From 2c96daefbcdc66dd19ac7c31ff2957435f15f6e3 Mon Sep 17 00:00:00 2001
From: Blboun3 <62328614+Blboun3@users.noreply.github.com>
Date: Tue, 16 Jan 2024 09:56:01 +0100
Subject: [PATCH] Merged server and client code into single repo
---
client/index.html | 11 ++
client/login/login.html | 43 ++++++++
client/login/style.css | 66 +++++++++++
client/main/index.html | 24 ++++
client/main/script.js | 76 +++++++++++++
client/main/style.css | 94 ++++++++++++++++
server/Makefile | 14 +++
server/httpd.c | 231 +++++++++++++++++++++++++++++++++++++++
server/httpd.h | 51 +++++++++
server/main.c | 176 +++++++++++++++++++++++++++++
server/public/404.html | 61 +++++++++++
server/public/index.html | 16 +++
server/public/robots.txt | 5 +
13 files changed, 868 insertions(+)
create mode 100644 client/index.html
create mode 100644 client/login/login.html
create mode 100644 client/login/style.css
create mode 100644 client/main/index.html
create mode 100644 client/main/script.js
create mode 100644 client/main/style.css
create mode 100644 server/Makefile
create mode 100644 server/httpd.c
create mode 100644 server/httpd.h
create mode 100644 server/main.c
create mode 100644 server/public/404.html
create mode 100644 server/public/index.html
create mode 100644 server/public/robots.txt
diff --git a/client/index.html b/client/index.html
new file mode 100644
index 0000000..53a05b6
--- /dev/null
+++ b/client/index.html
@@ -0,0 +1,11 @@
+
+
+
+ ';
+ } else {
+ tempString = '
';
+ }
+
+ tempString += "
"
+
+ // console.log(event.toLocaleString('cs-CZ', { weekday: "long", year: "numeric", month:"long", day: "numeric", era: "long", hour: "numeric", minute: "numeric", second: "numeric"}));
+ const locale = "cs-CZ";
+ const options = {
+ weekday: "long",
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ hour: "numeric",
+ minute: "numeric",
+ second: "numeric",
+ era: "long"
+ };
+
+
+ tempString += `
${ obj.author.name}
`;
+ tempString += "
"
+ tempString += `
${ new Date(obj.timestamp).toLocaleString(locale, options)}
`;
+ tempString += "
"
+ //htmlString += `
ID: ${obj.messageid}, Timestamp: ${obj.timestamp}, Author Name: ${obj.author.name}, Author ID: ${obj.author.id}, Content: ${obj.content}
`;
+ tempString += `
${obj.content.replaceAll("\n", "
")}
`;
+
+ tempString += "
";
+ htmlString += tempString;
+});
+
+$(".chatHier").html(htmlString);
diff --git a/client/main/style.css b/client/main/style.css
new file mode 100644
index 0000000..efc94fa
--- /dev/null
+++ b/client/main/style.css
@@ -0,0 +1,94 @@
+@import url("https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,200&family=Source+Code+Pro:wght@300&display=swap");
+/*
+font-family: 'Source Code Pro', monospace;
+font-family: 'DM Sans', sans-serif;
+*/
+
+body,
+html {
+ font-family: "Source Code Pro", monospace;
+ background-image: linear-gradient(120deg, #fccb90 0%, #d57eeb 100%);
+ height: 100%;
+ margin: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.cosiRandomCentered {
+ height: 100vh;
+ width: 60vw;
+ background-color: rgba(240, 240, 240, 0.55);
+ display: flex;
+ flex-direction: column;
+}
+
+.inputHier {
+ margin-top: auto;
+ background-color: #f0f0f0; /* Optional: Set a background color for better visibility */
+ padding: 10px; /* Optional: Adjust padding as needed */
+ width: 100%;
+ box-sizing: border-box;
+ /*grid-template-columns: 2fr 1fr;*/
+ display: flex;
+ justify-content: center;
+}
+
+.inputHier > textarea {
+ font-family: "Source Code Pro", monospace;
+ font-size: large;
+ /*width: 1024px;*/
+ width: 90%;
+ box-sizing: border-box;
+ resize: none;
+}
+
+.inputHier > button {
+ font-family: "Source Code Pro", monospace;
+ font-size: larger;
+ font-weight: 700;
+ height: 100%;
+ box-sizing: border-box;
+ margin-left: 10px;
+}
+
+.chatHier {
+ overflow: scroll;
+}
+
+.message {
+ border: #d57eeb solid 5px;
+ border-radius: 20px;
+ width: max-content;
+ padding: 5px;
+ margin: 3px;
+}
+
+.time {
+ font-size: x-small;
+}
+
+.author {
+ font-size: x-small;
+ font-weight: 600;
+}
+
+.firstLine {
+ display: flex;
+}
+
+.spacer {
+ width: 50px;
+}
+
+.left {
+ color: darkslategray;
+ background-color: rgba(30, 215, 250, 0.8);
+ margin-right: auto;
+}
+.right {
+ color: darkslategray;
+ background-color: rgba(250, 65, 30, 0.8);
+ margin-left: auto;
+}
\ No newline at end of file
diff --git a/server/Makefile b/server/Makefile
new file mode 100644
index 0000000..a4d31af
--- /dev/null
+++ b/server/Makefile
@@ -0,0 +1,14 @@
+all: server
+
+clean:
+ rm -rf *.o
+ @rm -rf server
+
+server: main.o httpd.o
+ gcc -o server $^ -lssl -lcrypto
+
+main.o: main.c httpd.h
+ gcc -c -o main.o main.c -lssl -lcrypto
+
+httpd.o: httpd.c httpd.h
+ gcc -c -o httpd.o httpd.c
\ No newline at end of file
diff --git a/server/httpd.c b/server/httpd.c
new file mode 100644
index 0000000..a7f0693
--- /dev/null
+++ b/server/httpd.c
@@ -0,0 +1,231 @@
+#include "httpd.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_CONNECTIONS 1000
+#define BUF_SIZE 65535
+#define QUEUE_SIZE 1000000
+
+static int listenfd;
+int *clients;
+static void start_server(const char *);
+static void respond(int);
+
+static char *buf;
+
+// Client request
+char *method, // "GET" or "POST"
+ *uri, // "/index.html" things before '?'
+ *qs, // "a=1&b=2" things after '?'
+ *prot, // "HTTP/1.1"
+ *payload; // for POST
+
+int payload_size;
+
+void serve_forever(const char *PORT) {
+ struct sockaddr_in clientaddr;
+ socklen_t addrlen;
+
+ int slot = 0;
+
+ printf("Server started %shttp://127.0.0.1:%s%s\n", "\033[92m", PORT,
+ "\033[0m");
+
+ // create shared memory for client slot array
+ clients = mmap(NULL, sizeof(*clients) * MAX_CONNECTIONS,
+ PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+
+ // Setting all elements to -1: signifies there is no client connected
+ int i;
+ for (i = 0; i < MAX_CONNECTIONS; i++)
+ clients[i] = -1;
+ start_server(PORT);
+
+ // Ignore SIGCHLD to avoid zombie threads
+ signal(SIGCHLD, SIG_IGN);
+
+ // ACCEPT connections
+ while (1) {
+ addrlen = sizeof(clientaddr);
+ clients[slot] = accept(listenfd, (struct sockaddr *)&clientaddr, &addrlen);
+
+ if (clients[slot] < 0) {
+ perror("accept() error");
+ exit(1);
+ } else {
+ if (fork() == 0) {
+ close(listenfd);
+ respond(slot);
+ close(clients[slot]);
+ clients[slot] = -1;
+ exit(0);
+ } else {
+ close(clients[slot]);
+ }
+ }
+
+ while (clients[slot] != -1)
+ slot = (slot + 1) % MAX_CONNECTIONS;
+ }
+}
+
+// start server
+void start_server(const char *port) {
+ struct addrinfo hints, *res, *p;
+
+ // getaddrinfo for host
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ if (getaddrinfo(NULL, port, &hints, &res) != 0) {
+ perror("getaddrinfo() error");
+ exit(1);
+ }
+ // socket and bind
+ for (p = res; p != NULL; p = p->ai_next) {
+ int option = 1;
+ listenfd = socket(p->ai_family, p->ai_socktype, 0);
+ setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
+ if (listenfd == -1)
+ continue;
+ if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
+ break;
+ }
+ if (p == NULL) {
+ perror("socket() or bind()");
+ exit(1);
+ }
+
+ freeaddrinfo(res);
+
+ // listen for incoming connections
+ if (listen(listenfd, QUEUE_SIZE) != 0) {
+ perror("listen() error");
+ exit(1);
+ }
+}
+
+// get request header by name
+char *request_header(const char *name) {
+ header_t *h = reqhdr;
+ while (h->name) {
+ if (strcmp(h->name, name) == 0)
+ return h->value;
+ h++;
+ }
+ return NULL;
+}
+
+// get all request headers
+header_t *request_headers(void) { return reqhdr; }
+
+// Handle escape characters (%xx)
+static void uri_unescape(char *uri) {
+ char chr = 0;
+ char *src = uri;
+ char *dst = uri;
+
+ // Skip inital non encoded character
+ while (*src && !isspace((int)(*src)) && (*src != '%'))
+ src++;
+
+ // Replace encoded characters with corresponding code.
+ dst = src;
+ while (*src && !isspace((int)(*src))) {
+ if (*src == '+')
+ chr = ' ';
+ else if ((*src == '%') && src[1] && src[2]) {
+ src++;
+ chr = ((*src & 0x0F) + 9 * (*src > '9')) * 16;
+ src++;
+ chr += ((*src & 0x0F) + 9 * (*src > '9'));
+ } else
+ chr = *src;
+ *dst++ = chr;
+ src++;
+ }
+ *dst = '\0';
+}
+
+// client connection
+void respond(int slot) {
+ int rcvd;
+
+ buf = malloc(BUF_SIZE);
+ rcvd = recv(clients[slot], buf, BUF_SIZE, 0);
+
+ if (rcvd < 0) // receive error
+ fprintf(stderr, ("recv() error\n"));
+ else if (rcvd == 0) // receive socket closed
+ fprintf(stderr, "Client disconnected upexpectedly.\n");
+ else // message received
+ {
+ buf[rcvd] = '\0';
+
+ method = strtok(buf, " \t\r\n");
+ uri = strtok(NULL, " \t");
+ prot = strtok(NULL, " \t\r\n");
+
+ uri_unescape(uri);
+
+ fprintf(stderr, "\x1b[32m + [%s] %s\x1b[0m\n", method, uri);
+
+ qs = strchr(uri, '?');
+
+ if (qs)
+ *qs++ = '\0'; // split URI
+ else
+ qs = uri - 1; // use an empty string
+
+ header_t *h = reqhdr;
+ char *t, *t2;
+ while (h < reqhdr + 16) {
+ char *key, *val;
+
+ key = strtok(NULL, "\r\n: \t");
+ if (!key)
+ break;
+
+ val = strtok(NULL, "\r\n");
+ while (*val && *val == ' ')
+ val++;
+
+ h->name = key;
+ h->value = val;
+ h++;
+ fprintf(stderr, "[H] %s: %s\n", key, val);
+ t = val + 1 + strlen(val);
+ if (t[1] == '\r' && t[2] == '\n')
+ break;
+ }
+ t = strtok(NULL, "\r\n");
+ t2 = request_header("Content-Length"); // and the related header if there is
+ payload = t;
+ payload_size = t2 ? atol(t2) : (rcvd - (t - buf));
+
+ // bind clientfd to stdout, making it easier to write
+ int clientfd = clients[slot];
+ dup2(clientfd, STDOUT_FILENO);
+ close(clientfd);
+
+ // call router
+ route();
+
+ // tidy up
+ fflush(stdout);
+ shutdown(STDOUT_FILENO, SHUT_WR);
+ close(STDOUT_FILENO);
+ }
+
+ free(buf);
+}
\ No newline at end of file
diff --git a/server/httpd.h b/server/httpd.h
new file mode 100644
index 0000000..6469ac3
--- /dev/null
+++ b/server/httpd.h
@@ -0,0 +1,51 @@
+#ifndef _HTTPD_H___
+#define _HTTPD_H___
+
+#include
+#include
+
+// Client request
+extern char *method, // "GET" or "POST"
+ *uri, // "/index.html" things before '?'
+ *qs, // "a=1&b=2" things after '?'
+ *prot, // "HTTP/1.1"
+ *payload; // for POST
+
+extern int payload_size;
+
+// Server control functions
+void serve_forever(const char *PORT);
+
+char *request_header(const char *name);
+
+typedef struct {
+ char *name, *value;
+} header_t;
+static header_t reqhdr[17] = {{"\0", "\0"}};
+header_t *request_headers(void);
+
+// user shall implement this function
+
+void route();
+
+// Response
+#define RESPONSE_PROTOCOL "HTTP/1.1"
+
+#define HTTP_200 printf("%s 200 OK\n\n", RESPONSE_PROTOCOL)
+#define HTTP_201 printf("%s 201 Created\n\n", RESPONSE_PROTOCOL)
+#define HTTP_404 printf("%s 404 Not found\n\n", RESPONSE_PROTOCOL)
+#define HTTP_500 printf("%s 500 Internal Server Error\n\n", RESPONSE_PROTOCOL)
+#define HTTP_301 printf("%s 301 Moved Permanently\n\n", RESPONSE_PROTOCOL)
+
+// some interesting macro for `route()`
+#define ROUTE_START() if (0) {
+#define ROUTE(METHOD, URI) \
+ } \
+ else if (strcmp(URI, uri) == 0 && strcmp(METHOD, method) == 0) {
+#define GET(URI) ROUTE("GET", URI)
+#define POST(URI) ROUTE("POST", URI)
+#define ROUTE_END() \
+ } \
+ else HTTP_500;
+
+#endif
\ No newline at end of file
diff --git a/server/main.c b/server/main.c
new file mode 100644
index 0000000..2102b80
--- /dev/null
+++ b/server/main.c
@@ -0,0 +1,176 @@
+#include "httpd.h"
+#include
+#include
+
+#define CHUNK_SIZE 1024 // read 1024 bytes at a time
+
+// Public directory settings
+#define PUBLIC_DIR "./public"
+#define INDEX_HTML "/index.html"
+#define NOT_FOUND_HTML "/404.html"
+
+typedef struct {
+ char *key;
+ char *value;
+} KeyValuePair;
+
+// Function to create key-value pair
+KeyValuePair* createKeyValuePair(const char *key, const char *value) {
+ KeyValuePair *pair = malloc(sizeof(KeyValuePair));
+ if (pair != NULL) {
+ pair->key = strdup(key);
+ pair->value = strdup(value);
+ }
+ return pair;
+}
+
+// Function to free memory occupied by a key-value pair
+void freeKeyValuePair(KeyValuePair *pair) {
+ if (pair != NULL) {
+ free(pair->key);
+ free(pair->value);
+ free(pair);
+ }
+}
+
+void sha512_hash_string(const char *input, char outputBuffer[129]) {
+ EVP_MD_CTX *mdctx;
+ const EVP_MD *md;
+ unsigned char hash[EVP_MAX_MD_SIZE];
+ unsigned int hashLen;
+
+ md = EVP_sha512();
+ mdctx = EVP_MD_CTX_new();
+
+ EVP_DigestInit_ex(mdctx, md, NULL);
+ EVP_DigestUpdate(mdctx, input, strlen(input));
+ EVP_DigestFinal_ex(mdctx, hash, &hashLen);
+
+ EVP_MD_CTX_free(mdctx);
+
+ for (int i = 0; i < hashLen; i++) {
+ sprintf(&outputBuffer[i * 2], "%02x", hash[i]);
+ }
+ outputBuffer[hashLen * 2] = '\0';
+}
+
+void parsePasswordPostData(const char *postData) {
+ char *dataCopy = strdup(postData); // Make a copy to avoid modifying the original string
+ char *token = strtok(dataCopy, "&");
+
+ while (token != NULL) {
+ char *keyValuePair = strdup(token);
+ char *key = strtok(keyValuePair, "=");
+ char *value = strtok(NULL, "=");
+
+ if (key != NULL && value != NULL) {
+ //printf("Key: %s, Value: %s\n", key, value);
+ // Here, you can store or process the key and value as needed
+ if(*key == 'p') {
+ char localHashOutput[129];
+ sha512_hash_string(value, localHashOutput);
+ //printf("Hashed");
+ printf("Input password is: %s\nHashed password is: %s\n", value, localHashOutput);
+ }
+ }
+
+ free(keyValuePair);
+ token = strtok(NULL, "&");
+ }
+
+ free(dataCopy);
+}
+
+
+int main(int c, char **v) {
+ char *port = c == 1 ? "8000" : v[1];
+ serve_forever(port);
+ return 0;
+}
+
+int file_exists(const char *file_name) {
+ struct stat buffer;
+ int exists;
+
+ exists = (stat(file_name, &buffer) == 0);
+
+ return exists;
+}
+
+int read_file(const char *file_name) {
+ char buf[CHUNK_SIZE];
+ FILE *file;
+ size_t nread;
+ int err = 1;
+
+ file = fopen(file_name, "r");
+
+ if (file) {
+ while ((nread = fread(buf, 1, sizeof buf, file)) > 0)
+ fwrite(buf, 1, nread, stdout);
+
+ err = ferror(file);
+ fclose(file);
+ }
+ return err;
+}
+
+void route() {
+ ROUTE_START()
+
+ GET("/") {
+ char index_html[20];
+ sprintf(index_html, "%s%s", PUBLIC_DIR, INDEX_HTML);
+
+ HTTP_200;
+ if (file_exists(index_html)) {
+ read_file(index_html);
+ } else {
+ printf("Hello! You are using %s\n\n", request_header("User-Agent"));
+ }
+ }
+
+ POST("/hash") {
+ HTTP_201;
+ if(payload_size > 0){
+ parsePasswordPostData(payload);
+ }
+ }
+
+ GET("/test") {
+ HTTP_200;
+ printf("List of request headers:\n\n");
+
+ header_t *h = request_headers();
+
+ while (h->name) {
+ printf("%s: %s\n", h->name, h->value);
+ h++;
+ }
+ }
+
+ POST("/") {
+ HTTP_201;
+ printf("Wow, seems that you POSTed %d bytes.\n", payload_size);
+ printf("Fetch the data using `payload` variable.\n");
+ if (payload_size > 0)
+ printf("Request body: %s", payload);
+ }
+
+ GET(uri) {
+ char file_name[255];
+ sprintf(file_name, "%s%s", PUBLIC_DIR, uri);
+
+ if (file_exists(file_name)) {
+ HTTP_200;
+ read_file(file_name);
+ } else {
+ HTTP_404;
+ sprintf(file_name, "%s%s", PUBLIC_DIR, NOT_FOUND_HTML);
+ if (file_exists(file_name))
+ read_file(file_name);
+ }
+ }
+
+ ROUTE_END()
+}
\ No newline at end of file
diff --git a/server/public/404.html b/server/public/404.html
new file mode 100644
index 0000000..048f9f0
--- /dev/null
+++ b/server/public/404.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Page Not Found
+
+
+
+
+
+ Page Not Found
+ Sorry, but the page you were trying to view does not exist.
+
+
+
\ No newline at end of file
diff --git a/server/public/index.html b/server/public/index.html
new file mode 100644
index 0000000..4ee6ca0
--- /dev/null
+++ b/server/public/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+ Pico HTTP server
+
+
+
+
+
+
+ Hello world!
+
+
+
\ No newline at end of file
diff --git a/server/public/robots.txt b/server/public/robots.txt
new file mode 100644
index 0000000..52fa3eb
--- /dev/null
+++ b/server/public/robots.txt
@@ -0,0 +1,5 @@
+# www.robotstxt.org/
+
+# Allow crawling of all content
+User-agent: *
+Disallow:
\ No newline at end of file