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 @@ + + + + + + Infinitum + + + + + \ No newline at end of file diff --git a/client/login/login.html b/client/login/login.html new file mode 100644 index 0000000..beea925 --- /dev/null +++ b/client/login/login.html @@ -0,0 +1,43 @@ + + + + + + + Přihlášení | Infinitum + + +
+
+
+
+

Přihlášení

+
+ +
+ +
+ +
+ +
+ +
+
+
+
+ + + diff --git a/client/login/style.css b/client/login/style.css new file mode 100644 index 0000000..543c366 --- /dev/null +++ b/client/login/style.css @@ -0,0 +1,66 @@ +@import url('https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,200&family=Source+Code+Pro:wght@300&display=swap'); + +body { + background-image: linear-gradient(120deg, #fccb90 0%, #d57eeb 100%); +} + +.form_border { + border: 1px solid rgb(92, 92, 92); + border-radius: 7px; + background-color: rgb(160, 161, 182); + width: 350px; + text-align: center; + height: 400px; + justify-content: space-around; + line-height: 200%; + font-family: 'DM Sans', sans-serif; + font-size: larger; + position: relative; +} + +.form_border > input { + width: 95%; + font-family: 'Source Code Pro', monospace; + outline: none; + font-size: larger; + border-radius: 5px; +} + +.form_border > input:focus { + background-color: rgb(235, 235, 235); + outline: none; +} + +.form_border > label { + width: 97%; + display: inline-block; + line-height: 200%; + text-align: left; +} + +.form_border > button { + font-family: 'DM Sans', sans-serif; + font-size: larger; + position: absolute; + bottom: 10px; + /*left: 117px;*/ + left: 0; + background-color: rgba(255, 190, 78, 0.7); + height: 12%; + font-weight: bold; + border: none; + width: 100%; +} + +.most_outer_form_border { + position: fixed; + left: 0; + top: 0; + width: 100vw; + height: 100vh; + display: flex; +} + +.outer_form_border { + margin: auto; +} diff --git a/client/main/index.html b/client/main/index.html new file mode 100644 index 0000000..fb859fc --- /dev/null +++ b/client/main/index.html @@ -0,0 +1,24 @@ + + + + + + + + Infinitum + + +
+
+ +
+
+ + +
+
+ + + + \ No newline at end of file diff --git a/client/main/script.js b/client/main/script.js new file mode 100644 index 0000000..d874e70 --- /dev/null +++ b/client/main/script.js @@ -0,0 +1,76 @@ +messages = [ + { + messageid: 98568, + timestamp: 1630000000000, + author: { + name: "Velecísař Empy", + id: 1, + }, + content: "Test message 01", + }, + { + messageid: 98868, + timestamp: 1640000000000, + author: { + name: "Velecísař Empy 2", + id: 2, + }, + content: "Test message 02", + }, + { + messageid: 96666, + timestamp: 1650000000000, + author: { + name: "Velecísař Empy", + id: 1, + }, + content: "Test message 03\n\n\n\n\nPokračování\ntest\na\na\n\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\na\n\na\na\na\na\na\na\na\na\na\n\na\na\na\na\na\na\na\na\na\n\na\na\na\na\na\na\na\na\na\n", + }, +]; + +const messagesDescending = messages.sort((a, b) => a.timestamp - b.timestamp); + +let htmlString = ""; + +const currentUser = { + name: "Velecísař Empy", + id: 1, +}; + +messagesDescending.forEach((obj) => { + var meAuthor = obj.author.id == currentUser.id; + var tempString = ""; + if (meAuthor) { + tempString = '
'; + } 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