aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremias Stotter <jeremias@stotter.eu>2021-11-20 21:10:21 +0100
committerJeremias Stotter <jeremias@stotter.eu>2021-11-20 21:10:21 +0100
commit12f1ee8d844e95f834986e60c95755763f014275 (patch)
tree0350b6218da7f93b41d7ff06be8478ac34129f63
parent8c718c270953639f025fd81fef8f085a105cb8ae (diff)
downloadJBlog-12f1ee8d844e95f834986e60c95755763f014275.tar.gz
JBlog-12f1ee8d844e95f834986e60c95755763f014275.tar.bz2
JBlog-12f1ee8d844e95f834986e60c95755763f014275.zip
File uploading works now
You can now upload pictures!
-rw-r--r--admin.c287
-rw-r--r--admin.h2
-rw-r--r--index.c2
-rw-r--r--jblog.c31
-rw-r--r--md.c16
5 files changed, 235 insertions, 103 deletions
diff --git a/admin.c b/admin.c
index da002af..7ef6b05 100644
--- a/admin.c
+++ b/admin.c
@@ -31,41 +31,43 @@
#include "md.h"
#define ADMIN_HTML " \
-<table class=\"admin-table\"> \
- <tr><h1>Save / Publish</h1><br/></tr>\
- <tr class=\"stretch_tr\"> \
- <td> \
- <form class=\"jb-adm-form\" method=\"POST\" target=\"result_html\"> \
- <label for=\"release_date\">Release Date:</label> <input type=\"date\" id=\"release_date\" name=\"release_date\"/> <br/> \
- <label for=\"release_time\">Release Time:</label> <input type=\"time\" id=\"release_time\" name=\"release_time\"/> <br/> \
- <label for=\"author\">Author:</label> <input type=\"text\" id=\"author\" name=\"author\"/> <br/> \
- <label for=\"title\">Title:</label> <input type=\"text\" id=\"title\" name=\"title\"/> <br/> \
- <label for=\"md-input\">Markdown:</label><br/> \
- <textarea name=\"md-input\"></textarea><br/> \
- <input type=\"submit\" value=\"Process\" name=\"sub\"/> \
- <input type=\"submit\" value=\"Save\" name=\"sub\"/> \
- <input type=\"submit\" value=\"Publish\" name=\"sub\"/> <br/> \
- <label for=\"publish_to\">Publish/Save Path:</label> <input type=\"text\" id=\"publish_to\" name=\"publish_to\"/> <br/> \
- </form> \
- </td> \
- <td> \
- <iframe class=\"jb-adm-result\" id=\"result_html\" name=\"result_html\"></iframe> \
- </td> \
- </tr> \
- <tr><h1>Folder / File management</h1></tr> \
- <tr> \
- <td> \
- <form class=\"jb-adm-form\" method=\"POST\" target=\"folder_list\"> \
- <label for=\"folder_name\">File / Folder Name:</label> <input type=\"text\" id=\"folder_name\" name=\"folder_name\" autocomplete=\"off\"/> <br/> \
- <input type=\"submit\" value=\"Delete\" name=\"sub\"/> \
- <input type=\"submit\" value=\"Create Folder\" name=\"sub\"/> \
- <input type=\"submit\" value=\"Fetch Files\" name=\"sub\"/> \
- </form> \
- </td> \
- <td> \
- <iframe class=\"jb-adm-result-folder\" id=\"folder_list\" name=\"folder_list\"></iframe> \
- </td> \
- </tr> \
+<table class=\"admin-table\"> \n\
+ <tr><h1>Save / Publish</h1><br/></tr> \n\
+ <tr class=\"stretch_tr\"> \n\
+ <td> \n\
+ <form class=\"jb-adm-form\" method=\"POST\" target=\"result_html\" enctype=\"multipart/form-data\"> \n\
+ <label for=\"release_date\">Release Date:</label> <input type=\"date\" id=\"release_date\" name=\"release_date\"/> <br/> \n\
+ <label for=\"release_time\">Release Time:</label> <input type=\"time\" id=\"release_time\" name=\"release_time\"/> <br/> \n\
+ <label for=\"author\">Author:</label> <input type=\"text\" id=\"author\" name=\"author\"/> <br/> \n\
+ <label for=\"title\">Title:</label> <input type=\"text\" id=\"title\" name=\"title\"/> <br/> \n\
+ <label for=\"md-input\">Markdown:</label><br/> \n\
+ <textarea name=\"md-input\"></textarea><br/> \n\
+ <input type=\"submit\" value=\"Process\" name=\"sub\"/> \n\
+ <input type=\"submit\" value=\"Save\" name=\"sub\"/> \n\
+ <input type=\"submit\" value=\"Publish\" name=\"sub\"/> <br/> \n\
+ <label for=\"publish_to\">Publish/Save Path:</label> <input type=\"text\" id=\"publish_to\" name=\"publish_to\"/> <br/> \n\
+ </form> \n\
+ </td> \n\
+ <td> \n\
+ <iframe class=\"jb-adm-result\" id=\"result_html\" name=\"result_html\"></iframe> \n\
+ </td> \n\
+ </tr> \n\
+ <tr><h1>Folder / File management</h1></tr> \n\
+ <tr> \n\
+ <td> \n\
+ <form class=\"jb-adm-form\" method=\"POST\" target=\"folder_list\" enctype=\"multipart/form-data\"> \n\
+ <label for=\"folder_name\">File / Folder Name:</label> <input type=\"text\" id=\"folder_name\" name=\"folder_name\" autocomplete=\"off\"/> <br/> \n\
+ <label for=\"file_upload\">Upload file</label> <input type=\"file\" if=\"file_upload\" name=\"file_upload\"/> <br/>\n\
+ <input type=\"submit\" value=\"Delete\" name=\"sub\"/> \n\
+ <input type=\"submit\" value=\"Create Folder\" name=\"sub\"/> \n\
+ <input type=\"submit\" value=\"Fetch Files\" name=\"sub\"/> \n\
+ <input type=\"submit\" value=\"Upload\" name=\"sub\" /> \n\
+ </form> \n\
+ </td> \n\
+ <td> \n\
+ <iframe class=\"jb-adm-result-folder\" id=\"folder_list\" name=\"folder_list\"></iframe> \n\
+ </td> \n\
+ </tr> \n\
</table>"
#define JBLOG_FILEPATTERN "Date: %s\n\
@@ -83,6 +85,7 @@ Type: Entry\n\
#define ADM_FOLDER_DEL
#define ADM_FOLDER_CREA
+/* I think I don't need this anymore as multipart encoding does not urlencode anything
// Can be done inplace
void urldecode(char* src, char* dest) {
if(src == NULL) {
@@ -108,6 +111,7 @@ void urldecode(char* src, char* dest) {
*d = '\0';
return;
}
+*/
int create_admin(char* buffer, size_t buffer_size, char* adm_save_dir) {
strncpy(buffer, ADMIN_HTML, buffer_size-1);
@@ -130,67 +134,160 @@ void sanitize_path(char* input) {
}
}
+struct multipart_return {
+ char* filename;
+ char* value;
+ size_t value_size;
+};
+
+// Returns the number of values found
+// I honestly have no idea how this works
+//
+// You know when you code for an hour and have no idea anymore how you did anything, well this is that
+int read_multipart(char read_names[][32], int read_count, struct multipart_return* read_return_pointers, char* content, size_t content_size) {
+ char* multipart_delim = content;
+ char* line_end = strchr(content, '\r');
+ if(line_end == NULL) {
+ return 0;
+ }
+ *line_end = '\0';
+ // plus 2 because stupid \r\n mime encoding \:<
+ char* position = line_end + 2;
+ int read_fields = 0;
+ for( ; read_fields < read_count && position != NULL;) {
+ // Finding the next delimiter becomes a bit tricky because we are dealing with binary data here!
+ char* next_delim = NULL;
+ char* temppos = position;
+ while(temppos <= content + content_size && temppos != NULL) {
+ if(strncmp(temppos, multipart_delim, strlen(multipart_delim)) == 0) {
+ next_delim = temppos;
+ break;
+ }
+ temppos++;
+ temppos = memchr(temppos, multipart_delim[0], content_size - (temppos - content));
+ }
+
+ // Content of any files starts with an empty line, so we can search for \r\n\r\n
+ char* file_content = strstr(position, "\r\n\r\n");
+ if(file_content != NULL) {
+ *file_content = '\0';
+ file_content += 4;
+ }
+ char* first_line_end = strchr(position, '\r');
+ if(first_line_end != NULL) {
+ *first_line_end = '\0';
+ }
+ // The first line is made up like this:
+ // property: value; property: value; ...
+ char* property = position;
+ char* semicolon = strchr(property, ';');
+ char* filename = NULL;
+ char* name = NULL;
+ do {
+ char* next_pos = semicolon + 1;
+ // seperate all the things
+ if(semicolon != NULL) {
+ *semicolon = '\0';
+
+ semicolon = strchr(semicolon + 1, ';');
+ }
+ if(*property == ' ') {
+ property++;
+ }
+
+ if(strncmp(property, "name=\"", 6) == 0) {
+ name = property + 6;
+ char* strdelim = strchr(name, '\"');
+ if(strdelim != NULL) {
+ *strdelim = '\0';
+ }
+ }
+
+ if(strncmp(property, "filename=\"", 10) == 0) {
+ filename = property + 10;
+ char* strdelim = strchr(filename, '\"');
+ if(strdelim != NULL) {
+ *strdelim = '\0';
+ }
+ }
+
+ property = next_pos;
+ } while(property != NULL + 1);
+
+ if(name == NULL) {
+ break;
+ }
+ for(int i = 0; i < read_count; i++) {
+ if(strcmp(read_names[i], name) == 0) {
+ read_fields++;
+ read_return_pointers[i].filename = filename;
+ read_return_pointers[i].value = file_content;
+ read_return_pointers[i].value_size = next_delim - file_content;
+ break;
+ }
+ }
+ if(file_content != NULL) {
+ char* last_newline = strchr(file_content, '\r');
+ if(last_newline != NULL) {
+ *last_newline = '\0';
+ }
+ }
+ if(next_delim != NULL) {
+ position = next_delim + strlen(multipart_delim) + 2;
+ *(next_delim) = '\0';
+ } else {
+ position = NULL;
+ }
+ }
+ return read_fields;
+}
+
// This handles post request received
// Return 0 if the result should be beautified
-int post_admin(char* buffer, size_t buffer_size, char* req_content, char* adm_save_dir) {
- char* md_input = NULL;
+int post_admin(char* buffer, size_t buffer_size, char* req_content, size_t req_content_size, char* adm_save_dir) {
char* sub_str = NULL;
+ char* md_input = NULL;
char* date_str = NULL;
char* time_str = NULL;
char* author = NULL;
char* title = NULL;
char* publish_to = NULL;
char* folder_name = NULL;
+ char* file_upload = NULL;
+ char* file_upload_name = NULL;
+ size_t file_upload_size = 0;
- char* start = NULL;
- char* end = NULL;
-
- for(start = req_content, end = strchr(start, '&');;
- start = end + 1, end = strchr(start, '&'))
{
- // Seperate by =
- char* value = strchr(start, '=');
- if(value == NULL) {
- continue;
- }
- *value = '\0';
- value++;
- // Check if we habe sub or md_input
- if(strcmp(start, "md-input") == 0) {
- md_input = value;
- } else if(strcmp(start, "sub") == 0) {
- sub_str = value;
- } else if(strcmp(start, "release_date") == 0) {
- date_str = value;
- } else if(strcmp(start, "release_time") == 0) {
- time_str = value;
- } else if(strcmp(start, "author") == 0) {
- author = value;
- } else if(strcmp(start, "title") == 0) {
- title = value;
- } else if(strcmp(start, "publish_to") == 0) {
- publish_to = value;
- } else if(strcmp(start, "folder_name") == 0) {
- folder_name = value;
- }
+ char read_names[9][32] = {
+ "sub",
+ "md-input",
+ "release_date",
+ "release_time",
+ "author",
+ "title",
+ "publish_to",
+ "folder_name",
+ "file_upload"
+ };
+ struct multipart_return read_multiparts[9] = {{0}};
- if(end == NULL) {
- break;
- } else {
- *end = '\0';
- }
+ read_multipart(read_names, 9, read_multiparts, req_content, req_content_size);
+
+ sub_str = read_multiparts[0].value;
+ md_input = read_multiparts[1].value;
+ date_str = read_multiparts[2].value;
+ time_str = read_multiparts[3].value;
+ author = read_multiparts[4].value;
+ title = read_multiparts[5].value;
+ publish_to = read_multiparts[6].value;
+ folder_name = read_multiparts[7].value;
+ file_upload = read_multiparts[8].value;
+ file_upload_name = read_multiparts[8].filename;
+ file_upload_size = read_multiparts[8].value_size;
}
- // urldecode the relevant values
- urldecode(md_input, md_input);
- urldecode(author, author);
- urldecode(title, title);
- urldecode(time_str, time_str);
- urldecode(publish_to, publish_to);
- urldecode(folder_name, folder_name);
- urldecode(sub_str, sub_str);
- if(md_input && sub_str && date_str && time_str && author && title) {
+ if(md_input && sub_str && date_str && time_str && author && title && publish_to) {
sanitize_path(publish_to);
if(strcmp(sub_str, "Process") == 0) {
// Process a file
@@ -239,8 +336,10 @@ int post_admin(char* buffer, size_t buffer_size, char* req_content, char* adm_sa
return 1;
}
}
-
- if(folder_name != NULL) {
+
+ printf("%s %s %s\n", folder_name, sub_str, folder_name);
+
+ if(folder_name && sub_str && folder_name) {
sanitize_path(folder_name);
char folder_path[PATH_MAX] = "";
snprintf(folder_path, PATH_MAX, "%s%s", base_path, folder_name);
@@ -286,9 +385,29 @@ int post_admin(char* buffer, size_t buffer_size, char* req_content, char* adm_sa
}
closedir(fetch_dir);
return 1;
+ } else if(strcmp(sub_str, "Upload") == 0) {
+ if(file_upload_name == NULL || file_upload == NULL || strlen(file_upload) == 0) {
+ snprintf(buffer, buffer_size, "<p><b>No file to upload</b></p>");
+ return 1;
+ }
+ sanitize_path(file_upload_name);
+ char new_filename[PATH_MAX] = "";
+ snprintf(new_filename, PATH_MAX, "%s%s", folder_path, file_upload_name);
+ int new_file = open(new_filename, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if(new_file < 0) {
+ snprintf(buffer, buffer_size, "<p><b>Error opening new file \"%s\": %s</b></p>", new_filename, strerror(errno));
+ return 1;
+ }
+ if(write(new_file, file_upload, file_upload_size) != file_upload_size) {
+ snprintf(buffer, buffer_size, "<p><b>The file %s couldn't be fully uploaded, please delete the file and retry!</b></p>", new_filename);
+ } else {
+ snprintf(buffer, buffer_size, "<p><b>The file %s was successfully uploaded</b></p>", new_filename);
+ }
+ close(new_file);
+ return 1;
}
}
- perror("Couldn't read all the values from the form!");
+ fprintf(stderr, "Couldn't read all the values from the form successfully!\n");
return -1;
}
diff --git a/admin.h b/admin.h
index ad89960..d01c8e8 100644
--- a/admin.h
+++ b/admin.h
@@ -18,5 +18,5 @@
#ifndef JADMIN
#define JADMIN
int create_admin(char* buffer, size_t buffer_size, char* adm_save_dir);
-int post_admin(char* buffer, size_t buffer_size, char* req_content, char* adm_save_dir);
+int post_admin(char* buffer, size_t buffer_size, char* req_content, size_t req_content_size, char* adm_save_dir);
#endif
diff --git a/index.c b/index.c
index a750eb6..81c328e 100644
--- a/index.c
+++ b/index.c
@@ -191,7 +191,7 @@ int create_index(char* path_name, int search_depth,
if(search_depth > 0) {
char index_name[PATH_MAX] = "";
strcpy(index_name ,index_dirent->d_name);
- strncat(index_name, "/", PATH_MAX);
+ strncat(index_name, "/", PATH_MAX - 1);
create_index(index_ent_fullname, search_depth -1, NULL, 0, index_name, type_list, type_count, req_uri, &timesort_pointer, &timesort_count);
}
}
diff --git a/jblog.c b/jblog.c
index 1ebb447..57ba9f6 100644
--- a/jblog.c
+++ b/jblog.c
@@ -16,6 +16,7 @@
// along with ┬┤JBlog┬┤. If not, see <http://www.gnu.org/licenses/>.
#define _POSIX_C_SOURCE 200809L
+#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
@@ -30,6 +31,7 @@
#include <signal.h>
#include <string.h>
#include <stdbool.h>
+#include <limits.h>
#include "md.h"
#include "index.h"
@@ -54,6 +56,8 @@
#define XHTML_ID 0
#define HTML_ID 1
+#define MAX_UPLOAD 134217700
+
int sockfd;
char* blog_head_pretitle;
char* blog_head_postitle;
@@ -124,7 +128,7 @@ int seperate_string(char* input, char* seperator, char** out_pre, char** out_pos
}
int handle_request(int socket);
-int create_html(char* content, size_t content_length, char request_uri[1024], char request_method[64], char** buffer, size_t* reply_size, bool ignore_cache, char* req_content);
+int create_html(char* content, size_t content_length, char request_uri[1024], char request_method[64], char** buffer, size_t* reply_size, bool ignore_cache, char* req_content, size_t req_content_size);
int main(int argc, char* argv[]) {
// Signal handling
@@ -332,13 +336,22 @@ int handle_request(int socket) {
if(content_length > 0) {
lseek(socket, 1, SEEK_CUR);
printf("There is content of size %ld\n", content_length);
- content = calloc(content_length + 1, 1);
+ if(content_length > MAX_UPLOAD) {
+ fprintf(stderr, "Error: Upload too big\n");
+ return -1;
+ }
+ content = malloc(content_length + 1);
if(content == NULL) {
perror("malloc error");
return -1;
}
- if(read(socket, content, content_length) < content_length) {
- fprintf(stderr, "WARNING: message read from socket was shortet than expected\n");
+ size_t read_bytes = 0;
+ for(int read_bytes_iter = 1; read_bytes_iter > 0; read_bytes += read_bytes_iter) {
+ read_bytes_iter = read(socket, content + read_bytes, content_length - read_bytes);
+ }
+ if(read_bytes != content_length) {
+ fprintf(stderr, "WARNING: message read from socket was shortet than expected (Got %ld ; expected %ld)\n", read_bytes, content_length);
+ return -1;
}
}
// If the request ends with /! we ignore entries already in the cache
@@ -353,7 +366,7 @@ int handle_request(int socket) {
char* html;
char http_response[MAX_REPLY] = "";
size_t reply_size = 0;
- int status_code = create_html(content, content_length, request_uri, request_method, &html, &reply_size, ignore_cache, content);
+ int status_code = create_html(content, content_length, request_uri, request_method, &html, &reply_size, ignore_cache, content, content_length);
char pattern_media_type[128] = "";
@@ -396,7 +409,7 @@ int process_blog_text(char* buffer, size_t buffer_size, char* blog_text, char* t
return 0;
}
-int read_jblog_file(char* buffer, int buffer_size, char* file_name, int* error_code, char* request_uri, int request_method, char* req_content) {
+int read_jblog_file(char* buffer, int buffer_size, char* file_name, int* error_code, char* request_uri, int request_method, char* req_content, size_t req_content_size) {
int blog_fd = open(file_name, O_RDONLY);
if(blog_fd < 0) {
*error_code = 404;
@@ -536,7 +549,7 @@ int read_jblog_file(char* buffer, int buffer_size, char* file_name, int* error_c
return -1;
}
} else if(request_method == REQ_POST) {
- int post_result = post_admin(html, MAX_REPLY, req_content, adm_save_dir);
+ int post_result = post_admin(html, MAX_REPLY, req_content, req_content_size,adm_save_dir);
if(post_result > 0) {
wrap_html = false;
} else if(post_result < 0) {
@@ -568,7 +581,7 @@ int read_jblog_file(char* buffer, int buffer_size, char* file_name, int* error_c
// This will create the html in buffer and return a http status code
// It will also alocate the buffer itself
// The buffer should only be freed by the caller in case the return isn't in the 200-299 success range
-int create_html(char* content, size_t content_length, char request_uri[1024], char request_method[64], char** buffer, size_t* reply_size, bool ignore_cache, char* req_content) {
+int create_html(char* content, size_t content_length, char request_uri[1024], char request_method[64], char** buffer, size_t* reply_size, bool ignore_cache, char* req_content, size_t req_content_size) {
// See if the request method is supported
int req_method;
if(strcmp(request_method, "GET") == 0) {
@@ -625,7 +638,7 @@ int create_html(char* content, size_t content_length, char request_uri[1024], ch
}
int error_code = 0;
- *reply_size = read_jblog_file(*buffer, MAX_REPLY, canonicalized_path, &error_code, request_uri, req_method, req_content);
+ *reply_size = read_jblog_file(*buffer, MAX_REPLY, canonicalized_path, &error_code, request_uri, req_method, req_content, req_content_size);
// The response was too big
if(*reply_size >= MAX_REPLY) {
diff --git a/md.c b/md.c
index 4ebb75a..635b02b 100644
--- a/md.c
+++ b/md.c
@@ -395,10 +395,10 @@ int parse_markdown(char* input, char* buffer, size_t buffer_size) {
case '`':
{
if(in_mono) {
- strncat(format_line_buffer, "</code>", LINE_MAX);
+ strncat(format_line_buffer, "</code>", LINE_MAX - 1);
in_mono = false;
} else {
- strncat(format_line_buffer, "<code>", LINE_MAX);
+ strncat(format_line_buffer, "<code>", LINE_MAX - 1);
in_mono = true;
}
}
@@ -408,16 +408,16 @@ int parse_markdown(char* input, char* buffer, size_t buffer_size) {
{
// Look ahead, we only want to cut if there are two tildes
if(*(line_position + 1) != '~') {
- strncat(format_line_buffer, "~", LINE_MAX);
+ strncat(format_line_buffer, "~", LINE_MAX - 1);
break;
}
if(in_cut) {
// Close
- strncat(format_line_buffer, "</s>", LINE_MAX);
+ strncat(format_line_buffer, "</s>", LINE_MAX - 1);
in_cut = false;
} else {
in_cut = true;
- strncat(format_line_buffer, "<s>", LINE_MAX);
+ strncat(format_line_buffer, "<s>", LINE_MAX - 1);
}
line_position++;
}
@@ -433,7 +433,7 @@ int parse_markdown(char* input, char* buffer, size_t buffer_size) {
//<a href=""></a>\0
char* link_html = calloc(link_loctxt_len + 17, 1);
if(link_html == NULL || closing_rnd_bracket == NULL) {
- strncat(format_line_buffer, "[", LINE_MAX);
+ strncat(format_line_buffer, "[", LINE_MAX - 1);
if(link_html != NULL) free(link_html);
if(link_text != NULL) free(link_text);
if(link_loc != NULL) free(link_loc);
@@ -456,7 +456,7 @@ int parse_markdown(char* input, char* buffer, size_t buffer_size) {
char* img_src = NULL;
size_t img_altsrc_len = 0;
if(*(line_position + 1) == '\0') {
- strncat(format_line_buffer, "!", LINE_MAX);
+ strncat(format_line_buffer, "!", LINE_MAX - 1);
break;
}
char* closing_rnd_bracket = get_link_components(line_position + 1, &img_alt, &img_src, &img_altsrc_len);
@@ -464,7 +464,7 @@ int parse_markdown(char* input, char* buffer, size_t buffer_size) {
//<img alt="" src=""/>\0
char* img_html = calloc(img_altsrc_len + 22, 1);
if(img_html == NULL || closing_rnd_bracket == NULL) {
- strncat(format_line_buffer, "!", LINE_MAX);
+ strncat(format_line_buffer, "!", LINE_MAX - 1);
if(img_html != NULL) free(img_html);
if(img_alt != NULL) free(img_alt);
if(img_src != NULL) free(img_src);
Jeremias Stotters git repositories generated by CGIT