aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremias Stotter <jeremias@stotter.eu>2021-10-31 19:31:45 +0100
committerJeremias Stotter <jeremias@stotter.eu>2021-10-31 19:31:45 +0100
commit3880399fe667a309d3e6bcb2949df7f7240274cd (patch)
tree419955fddf1ef4ea5857e9308e75a6857d132211
parent5790b667440a278856d45c5af65568fba9f236ca (diff)
downloadJBlog-3880399fe667a309d3e6bcb2949df7f7240274cd.tar.gz
JBlog-3880399fe667a309d3e6bcb2949df7f7240274cd.tar.bz2
JBlog-3880399fe667a309d3e6bcb2949df7f7240274cd.zip
Indexes are now implemented
-rw-r--r--index.c123
-rw-r--r--index.h4
-rw-r--r--jblog.c141
-rw-r--r--jblog.h9
-rw-r--r--makefile4
-rw-r--r--md.c4
-rw-r--r--pattern.xhtml9
7 files changed, 249 insertions, 45 deletions
diff --git a/index.c b/index.c
new file mode 100644
index 0000000..f7e75ec
--- /dev/null
+++ b/index.c
@@ -0,0 +1,123 @@
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+
+#include "jblog.h"
+
+int create_index(char* path_name, int search_depth,
+ char* buffer, size_t buffer_size,
+ char* relative_path, char* type_list[],
+ int type_count, char* req_uri) {
+ DIR* index_dir = opendir(path_name);
+ if(index_dir == NULL) {
+ perror("Error opening directory for indexing");
+ return -1;
+ }
+
+ for(struct dirent* index_dirent = readdir(index_dir); index_dirent != NULL; index_dirent = readdir(index_dir)) {
+ // Find out if the file is a directory or a regular file
+ struct stat index_ent_stat = {0};
+ char index_ent_fullname[PATH_MAX] = "";
+ sprintf(index_ent_fullname, "%s/%s", path_name, index_dirent->d_name);
+ if(stat(index_ent_fullname, &index_ent_stat) != 0) {
+ fprintf(stderr, "stat error on file %s: %s\n", index_ent_fullname, strerror(errno));
+ continue;
+ }
+ if(*(index_dirent->d_name) == '.') {
+ continue;
+ }
+ if(S_ISREG(index_ent_stat.st_mode)) {
+ char* last_dot = strrchr(index_dirent->d_name, '.');
+ if(last_dot == NULL) {
+ continue;
+ }
+ if(strcmp(last_dot, ".jblog") != 0) {
+ continue;
+ }
+ // We have a regular file that is also a jblog file
+ int jblog_file = open(index_ent_fullname, O_RDONLY);
+ if(jblog_file < 0) {
+ fprintf(stderr, "error opening file %s: %s\n", index_ent_fullname, strerror(errno));
+ continue;
+ }
+ // read until, we don't need the content here
+ char* header = NULL;
+ size_t current_size = 1;
+ char current_char = '\0';
+ do {
+ if(read(jblog_file, &current_char, 1) < 1) {
+ break;
+ }
+ current_size ++;
+ header = realloc(header, current_size);
+ if(header == NULL) {
+ perror("realloc error");
+ close(jblog_file);
+ closedir(index_dir);
+ return -1;
+ }
+ *(header + current_size - 2) = current_char;
+ } while(memcmp(header + current_size - 5, "\n///", 4));
+ *(header + current_size -1) = '\0';
+ // Read in the values
+ char *title = NULL;
+ char *type = NULL;
+ char retrieve_values[2][32] = {
+ "Title",
+ "Type"
+ };
+ char **return_strings[2] = {
+ &title,
+ &type
+ };
+
+ retrieve_headvals(header, retrieve_values, return_strings, 2);
+
+ if(title == NULL || type == NULL) {
+ continue;
+ }
+
+ trim_whitespace(&type);
+
+ bool allowed_type = false;
+ for(int i = 0; i < type_count && !allowed_type; i++) {
+ if(strcmp(type, type_list[i]) == 0) {
+ allowed_type = true;
+ }
+ }
+ if(!allowed_type) {
+ continue;
+ }
+
+ size_t current_bufsize = strnlen(buffer, buffer_size);
+
+ snprintf(buffer + current_bufsize, buffer_size - current_bufsize - 1,
+ "<br/><a href=\"%s/../%s%s\"><span class=\"jb-indextitle\">%s</span><span class=\"jb-indextype\">%s</span></a>",
+ req_uri, relative_path == NULL ? "" : relative_path,
+ index_dirent->d_name, title, type);
+
+ free(header);
+ close(jblog_file);
+ } else if(S_ISDIR(index_ent_stat.st_mode)) {
+ if(search_depth > 0) {
+ size_t current_bufsize = strnlen(buffer, buffer_size);
+ char index_name[PATH_MAX] = "";
+ strcpy(index_name ,index_dirent->d_name);
+ strncat(index_name, "/", PATH_MAX);
+ create_index(index_ent_fullname, search_depth -1, buffer + current_bufsize, buffer_size - current_bufsize, index_name, type_list, type_count, req_uri);
+ }
+ }
+ }
+
+ closedir(index_dir);
+ return 0;
+}
diff --git a/index.h b/index.h
new file mode 100644
index 0000000..6e5fddd
--- /dev/null
+++ b/index.h
@@ -0,0 +1,4 @@
+int create_index(char* path_name, int search_depth,
+ char* buffer, size_t buffer_size,
+ char* relative_path, char* type_list[],
+ int type_count, char* req_uri);
diff --git a/jblog.c b/jblog.c
index 2d232c9..aab057e 100644
--- a/jblog.c
+++ b/jblog.c
@@ -32,11 +32,13 @@
#include <stdbool.h>
#include "md.h"
+#include "index.h"
// Call program like this
// jblog <UNIX socket> <html pattern> <directory with blog files>
#include "cache.h"
+#include "jblog.h"
#define HTML_ERROR(error_code) "<html><body><h1>Error " #error_code "</h1>\n<h2>JBLOG</h2></body></html>"
@@ -46,8 +48,6 @@
// This is the part of the pattern where the dynamic content is insertet
#define BLOGMARK "\n$BLOG$"
#define TITLEMARK "\n$TITLE$"
-// Actually we are not gonna have quite as much usable
-#define MAX_REPLY 8096
// These are used when differentiating between html and xhtml
#define XHTML_ID 0
@@ -73,6 +73,36 @@ char* trim_whitespace(char** str) {
return *str;
}
+void retrieve_headvals(char* head, char retrieve_values[][32], char** returned_strings[], int retrieve_ammount) {
+ head--;
+ do {
+ head++;
+ if(*head == '#') {
+ head = strchr(head, '\n');
+ continue;
+ }
+ char* value = strchr(head, ':');
+ if(value == NULL) {
+ break;
+ }
+ *value = '\0';
+ value++;
+ char* value_end = strchr(value, '\n');
+ // Check if the line is a comment
+ if(value_end != NULL) {
+ *value_end = '\0';
+ }
+ // Read all the values
+ for(int i = 0; i < retrieve_ammount; i++) {
+ if(strcmp(retrieve_values[i], head) == 0) {
+ *(returned_strings[i]) = trim_whitespace(&value);
+ break;
+ }
+ }
+ head = value_end;
+ } while(head != NULL);
+}
+
char* blog_head_pretitle;
char* blog_head_postitle;
char* blog_foot;
@@ -194,6 +224,16 @@ int main(int argc, char* argv[]) {
}
}
+int get_basepath(char* buffer, size_t buffer_size, char* input_path) {
+ char* last_slash = strrchr(input_path, '/');
+ if(last_slash != NULL) {
+ strncpy(buffer, input_path, buffer_size - 1);
+ buffer[last_slash - input_path] = '\0';
+ return 0;
+ }
+ return -1;
+}
+
// This gets the length of a netstring that starts at the current position of the stream
size_t get_next_netstr_len(int stream) {
size_t msg_len = 0;
@@ -325,7 +365,7 @@ int handle_request(int socket) {
}
// Maybe put the patterns in some sort of in memory cache
-char* read_jblog_file(char* file_name, int* error_code, char* title_buffer, size_t title_buffer_size) {
+char* read_jblog_file(char* file_name, int* error_code, char* title_buffer, size_t title_buffer_size, char* request_uri) {
int blog_fd = open(file_name, O_RDONLY);
if(blog_fd < 0) {
*error_code = 404;
@@ -367,39 +407,28 @@ char* read_jblog_file(char* file_name, int* error_code, char* title_buffer, size
char* author = NULL;
char* date_str = NULL;
char* type_str = NULL;
+ char* depth_str = NULL;
+ char* index_types = NULL;
// So we can still free() later;
char* original_blog_content = blog_content;
// blog_content now contains the head of the file, blog_text contains all the text
- blog_content--;
- do {
- blog_content++;
- if(*blog_content == '#') {
- blog_content = strchr(blog_content, '\n');
- continue;
- }
- char* value = strchr(blog_content, ':');
- if(value == NULL) {
- break;
- }
- *value = '\0';
- value++;
- char* value_end = strchr(value, '\n');
- // Check if the line is a comment
- if(value_end != NULL) {
- *value_end = '\0';
- }
- // Read all the values
- if(strcmp(blog_content, "Date") == 0) {
- date_str = trim_whitespace(&value);
- } else if(strcmp(blog_content, "Title") == 0) {
- title = trim_whitespace(&value);
- } else if(strcmp(blog_content, "Type") == 0) {
- type_str = trim_whitespace(&value);
- } else if(strcmp(blog_content, "Author") == 0) {
- author = trim_whitespace(&value);
- }
- blog_content = value_end;
- } while(blog_content != NULL);
+ char retrieve_values[6][32] = {
+ "Date",
+ "Title",
+ "Type",
+ "Author",
+ "Depth",
+ "DTypes"
+ };
+ char** returned_strings[6] = {
+ &date_str,
+ &title,
+ &type_str,
+ &author,
+ &depth_str,
+ &index_types
+ };
+ retrieve_headvals(blog_content, retrieve_values, returned_strings, 6);
char* html = malloc(MAX_REPLY + 1);
if(html == NULL) {
@@ -414,18 +443,56 @@ char* read_jblog_file(char* file_name, int* error_code, char* title_buffer, size
}
if(strcmp(type_str, "Entry") == 0) {
- char parsed_text[MAX_REPLY-128] = "\0";
+ char parsed_text[MAX_REPLY] = "\0";
if(parse_markdown(blog_text, parsed_text, MAX_REPLY) != 0) {
*error_code = 500;
return NULL;
}
- snprintf(html, MAX_REPLY + 1, "<div class=\"jblog\">"
+ snprintf(html, MAX_REPLY, "<div class=\"jblog\">"
"<h1>%s</h1>"
"%s"
"<h4>Written by %s</h4>"
"<h4>Published on %s</h4>"
"</div>", title, parsed_text, author, date_str);
- } else if(strcmp(type_str, "Index") == 0) {
+ } else if(strcmp(type_str, "Index") == 0) {
+ char index_text[MAX_REPLY] = "\0";
+ char path_name[PATH_MAX] = "\0";
+ if(get_basepath(path_name, PATH_MAX, file_name) != 0) {
+ *error_code = 500;
+ return NULL;
+ }
+ int depth = 0;
+ if(depth_str != NULL) {
+ depth = atoi(depth_str);
+ }
+ // Get the allowed Types
+ char** allow_types = NULL;
+ int allow_type_count = 0;
+ char* end_space = index_types;
+ for(int i = 0; end_space != NULL ; i++) {
+ index_types = trim_whitespace(&index_types);
+ end_space = strchr(index_types, ' ');
+ if(end_space != NULL)
+ *end_space = '\0';
+ allow_type_count++;
+ allow_types = realloc(allow_types, allow_type_count * sizeof(char*));
+ if(allow_types == NULL) {
+ *error_code = 500;
+ return NULL;
+ }
+ allow_types[i] = index_types;
+ index_types = end_space+1;
+ }
+
+ if(create_index(path_name, depth, index_text, MAX_REPLY, NULL, allow_types, allow_type_count, request_uri) != 0) {
+ *error_code = 500;
+ return NULL;
+ }
+ snprintf(html, MAX_REPLY, "<div class=\"jblog index\">"
+ "<h1>%s</h1>"
+ "%s"
+ "<h4>Written by %s</h4>"
+ "</div>", title, index_text, author);
}
strncpy(title_buffer, title, title_buffer_size-1);
@@ -481,7 +548,7 @@ int create_html(char* content, size_t content_length, char request_uri[1024], ch
int error_code = 0;
char title[128] = {'\0'};
- char* dyn_content = read_jblog_file(path, &error_code, title, 128);
+ char* dyn_content = read_jblog_file(canonicalized_path, &error_code, title, 128, request_uri);
if(dyn_content == NULL) {
sprintf(*buffer, HTML_ERROR(%d), error_code);
diff --git a/jblog.h b/jblog.h
new file mode 100644
index 0000000..acd3150
--- /dev/null
+++ b/jblog.h
@@ -0,0 +1,9 @@
+#ifndef JBLOG
+#define JBLOG
+
+// The maximum reply size
+// Actually we are not gonna have quite as much usable
+#define MAX_REPLY 8096
+void retrieve_headvals(char* head, char retrieve_values[][32], char** returned_strings[], int retrieve_ammount);
+char* trim_whitespace(char** str);
+#endif
diff --git a/makefile b/makefile
index fd95387..ab7628c 100644
--- a/makefile
+++ b/makefile
@@ -1,5 +1,5 @@
CC=gcc
CFLAGS=-O2 -std=c99 -Wall -g
-jblog: jblog.c md.o
- $(CC) $(CFLAGS) jblog.c md.o -o $@
+jblog: jblog.c md.o index.o
+ $(CC) $(CFLAGS) jblog.c md.o index.o -o $@
diff --git a/md.c b/md.c
index 73ddc9c..2eeb67d 100644
--- a/md.c
+++ b/md.c
@@ -58,8 +58,8 @@ size_t append(char* in_dest, char* in_src) {
// Prepend in_src to in_dst, then return length of new string
size_t prepend(char* in_dest, char* in_src) {
char prepended_string[LINE_MAX] = {'\0'};
- memcpy(prepended_string, in_src, LINE_MAX);
- strcat(prepended_string, in_dest);
+ strncpy(prepended_string, in_src, LINE_MAX - 1);
+ strncat(prepended_string, in_dest, LINE_MAX - 1);
memcpy(in_dest, prepended_string, LINE_MAX);
// for pure safety reasons I'll put a terminating \0 at the end of the destination string
in_dest[LINE_MAX-1] = '\0';
diff --git a/pattern.xhtml b/pattern.xhtml
index 7e71664..065fd41 100644
--- a/pattern.xhtml
+++ b/pattern.xhtml
@@ -1,9 +1,7 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
- <meta name='description' content='Software developed by me (Jeremias Stotter). From backup programs to cgit themes.'/>
- <meta name="keywords" content="C, Programming, cgit dark theme, backup, software"/>
- <title>Stotter Jeremias Development</title>
+$TITLE$
<style>
body {
font-family: sans-serif;
@@ -25,6 +23,9 @@
th {
text-align: left;
}
+ .jb-indextype {
+ float: right;
+ }
</style>
<link rel="icon" href="/favicon.ico"/>
</head>
@@ -35,7 +36,7 @@
<h1>Blog</h1>
</th>
<th width="100%" style="text-align: right">
- <img alt="logo" src="/logo100.png" />
+ <img alt="logo" src="https://jeremias.stotter.eu/logo100.png" />
</th>
</tr>
</table>
Jeremias Stotters git repositories generated by CGIT