Generator

This site is rendered from md files using a filter I developed.

2024-11-01

main.c

#include "main.h"

const char *HEADER_UserAgent	= "X-User-Agent";
const char *HEADER_BaseURL	  = "X-Base-URL";
const char *HEADER_RealIP	   = "X-Real-IP";
const char *HEADER_DocumentRoot = "X-Document-Root";

uint16_t	 _TCP_PORT		   = 8888;
unsigned int _CONNECTION_TOT_SEC = 10;
unsigned int _THREAD_POOL_SIZE   = 4;
unsigned int _CONNECTION_LIMIT   = 10000;

int is_horizontal_rule(const char* line) {
	int count = 0;
	char prev = '\0';
	while (*line) {
		if (*line != ' ' && *line != '-' && *line != '*' && *line != '_') {
			return 0;
		}
		if (*line == '-' || *line == '*' || *line == '_') {
			if (prev != ' ' && prev != '\0' && prev != *line) {
				return 0;
			}
			count++;
		}
		prev = *line;
		line++;
	}
	return count >= 3;
}

int ends_with(const char* str, const char* suffix) {
	size_t str_len = strlen(str);
	size_t suffix_len = strlen(suffix);
	return (str_len >= suffix_len) &&
		(strcasecmp(str + str_len - suffix_len, suffix) == 0);
}

void render_link_or_media(DynamicBuffer *h, const char* text, const char* url) {
	if (ends_with(url, ".mp4") || ends_with(url, ".webm") || ends_with(url, ".ogg")) {
		hprintf(h, "<video width=\"320\" height=\"240\" controls><source src=\"%s\" type=\"video/mp4\">"
		"Your browser does not support the video tag.</video>\n", url);
	} else if (ends_with(url, ".jpg") || ends_with(url, ".jpeg") || ends_with(url, ".png") || ends_with(url, ".gif")) {
		hprintf(h, "<img src=\"%s\" alt=\"%s\">", url, text);
	} else {
		hprintf(h, "<a href=\"%s\">%s</a>", url, text);
	}
}

enum TokenType {
	KEYWORD,
	IDENTIFIER,
	STRING,
	NUMBER,
	COMMENT,
	OPERATOR,
	PUNCTUATION,
	WHITESPACE
};
struct Token {
	enum TokenType type;
	char text[MAX_TOKEN_LENGTH];
	int indent;
};
static const char* keywords[] = {
	"auto", "break", "case", "char", "const", "continue", "default", "do",
	"double", "else", "enum", "extern", "float", "for", "goto", "if",
	"int", "long", "register", "return", "short", "signed", "sizeof", "static",
	"struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"
};
static const int num_keywords = sizeof(keywords) / sizeof(keywords[0]);

int is_keyword(const char* word) {
	for (int i = 0; i < num_keywords; i++) {
		if (strcmp(word, keywords[i]) == 0) {
			return 1;
		}
	}
	return 0;
}

void escape_html(DynamicBuffer *h, const char* src) {
	while (*src) {
		switch (*src) {
			case '&':  hprintf(h, "&amp;"); break;
			case '<':  hprintf(h, "&lt;"); break;
			case '>':  hprintf(h, "&gt;"); break;
			case '"':  hprintf(h, "&quot;"); break;
			case '\'': hprintf(h, "&#39;"); break;
			default:   hprintf(h, "%c", *src);
		}
		src++;
	}
}

const char* get_token_class_name(enum TokenType type) {
	switch (type) {
		case KEYWORD: return "keyword";
		case IDENTIFIER: return "identifier";
		case STRING: return "string";
		case NUMBER: return "number";
		case COMMENT: return "comment";
		case OPERATOR: return "operator";
		case PUNCTUATION: return "punctuation";
		default: return "";
	}
}

void print_highlighted_token(DynamicBuffer *h, struct Token token) {
	const char* class_name;
	switch (token.type) {
		case KEYWORD: class_name = "keyword"; break;
		case IDENTIFIER: class_name = "identifier"; break;
		case STRING: class_name = "string"; break;
		case NUMBER: class_name = "number"; break;
		case COMMENT: class_name = "comment"; break;
		case OPERATOR: class_name = "operator"; break;
		case PUNCTUATION: class_name = "punctuation"; break;
		case WHITESPACE:
			hprintf(h, "%s", token.text);
			return;
		default: class_name = ""; break;
	}
	hprintf(h, "<span class=\"%s\">%s</span>", class_name, token.text);
}

void escape_html_stream(DynamicBuffer *h, const char* src) {
	switch (*src) {
		case '&':  hprintf(h, "&amp;"); break;
		case '<':  hprintf(h, "&lt;"); break;
		case '>':  hprintf(h, "&gt;"); break;
		case '"':  hprintf(h, "&quot;"); break;
		case '\'': hprintf(h, "&#39;"); break;
		default:   hprintf(h, "%c", *src);
	}
}

void process_inline_markdown(DynamicBuffer *h, const char* line) {
	int in_bold = 0;
	int in_italic = 0;
	const char* ptr = line;

	while (*ptr) {
		if (*ptr == '*' || *ptr == '_') {
			if (*(ptr + 1) == *ptr) {  // Bold
				hprintf(h, in_bold ? "</strong>" : "<strong>");
				in_bold = !in_bold;
				ptr += 2;
			} else {  // Italic
				hprintf(h, in_italic ? "</em>" : "<em>");
				in_italic = !in_italic;
				ptr++;
			}
		} else if (*ptr == '!' && *(ptr + 1) == '[') {  // Image
			const char* alt_start = ptr + 2;
			const char* alt_end = strchr(alt_start, ']');
			if (alt_end && alt_end[1] == '(' && strchr(alt_end + 2, ')')) {
				const char* url_start = alt_end + 2;
				const char* url_end = strchr(url_start, ')');

				hprintf(h, "<img src=\"");
				while (url_start < url_end) {
					escape_html_stream(h, url_start);
					url_start++;
				}
				hprintf(h, "\" alt=\"");
				while (alt_start < alt_end) {
					escape_html_stream(h, alt_start);
					alt_start++;
				}
				hprintf(h, "\">");

				ptr = url_end + 1;
			} else {
				escape_html_stream(h, ptr);
				ptr++;
			}
		} else if (*ptr == '[') {  // Link
			const char* text_start = ptr + 1;
			const char* text_end = strchr(text_start, ']');
			if (text_end && text_end[1] == '(' && strchr(text_end + 2, ')')) {
				const char* url_start = text_end + 2;
				const char* url_end = strchr(url_start, ')');

				hprintf(h, "<a href=\"");
				while (url_start < url_end) {
					escape_html_stream(h, url_start);
					url_start++;
				}
				hprintf(h, "\">");
				while (text_start < text_end) {
					escape_html_stream(h, text_start);
					text_start++;
				}
				hprintf(h, "</a>");

				ptr = url_end + 1;
			} else {
				escape_html_stream(h, ptr);
				ptr++;
			}
		} else {
			escape_html_stream(h, ptr);
			ptr++;
		}
	}
	if (in_bold) { hprintf(h, "</strong>"); }
	if (in_italic) { hprintf(h, "</em>"); }
}

struct Token get_next_token_from_string(const char** code, int tab_size) {
	struct Token token;
	const char* ptr = *code;
	token.indent = 0;

	if (isspace(*ptr)) {
		token.type = WHITESPACE;
		int i = 0;
		while (isspace(*ptr) && i < MAX_TOKEN_LENGTH - 1) {
			if (*ptr == '\t') {
				token.indent += tab_size;
			} else {
				token.indent++;
			}
			token.text[i++] = *ptr++;
		}
		token.text[i] = '\0';
		*code = ptr;
		return token;
	}

	if (*ptr == '\0') {
		token.type = WHITESPACE;
		token.text[0] = '\0';
		*code = ptr;
		return token;
	}

	if (isalpha(*ptr) || *ptr == '_') {
		token.type = IDENTIFIER;
		int i = 0;
		do {
			token.text[i++] = *ptr++;
		} while ((isalnum(*ptr) || *ptr == '_') && i < MAX_TOKEN_LENGTH - 1);
		token.text[i] = '\0';

		if (is_keyword(token.text)) {
			token.type = KEYWORD;
		}
		*code = ptr;
		return token;
	}

	if (isdigit(*ptr)) {
		token.type = NUMBER;
		int i = 0;
		do {
			token.text[i++] = *ptr++;
		} while ((isdigit(*ptr) || *ptr == '.') && i < MAX_TOKEN_LENGTH - 1);
		token.text[i] = '\0';
		*code = ptr;
		return token;
	}

	if (*ptr == '"') {
		token.type = STRING;
		int i = 0;
		token.text[i++] = *ptr++;
		while (*ptr != '\0' && *ptr != '"' && *ptr != '\n' && i < MAX_TOKEN_LENGTH - 2) {
			if (*ptr == '\\' && *(ptr + 1) != '\0') {
				token.text[i++] = *ptr++;
				token.text[i++] = *ptr++;
			} else {
				token.text[i++] = *ptr++;
			}
		}
		if (*ptr == '"') {
			token.text[i++] = *ptr++;
		}
		token.text[i] = '\0';
		*code = ptr;
		return token;
	}

	if (*ptr == '/') {
		if (*(ptr + 1) == '/') {
			token.type = COMMENT;
			int i = 0;
			while (*ptr != '\0' && *ptr != '\n' && i < MAX_TOKEN_LENGTH - 1) {
				token.text[i++] = *ptr++;
			}
			token.text[i] = '\0';
			*code = ptr;
			return token;
		}
	}

	token.type = (strchr("+-*/%=<>!&|^~", *ptr) ? OPERATOR : PUNCTUATION);
	token.text[0] = *ptr++;
	token.text[1] = '\0';
	*code = ptr;
	return token;
}

int is_header(const char* line, int* level) {
	*level = 0;
	while (*line == '#') { (*level)++; line++; }
	return *level > 0 && *level <= 6 && *line == ' ';
}

void highlight_code(DynamicBuffer *h, const char* code, int tab_size) {
	struct Token token;
	const char* ptr = code;
	int newline = 1;

	while (*ptr) {
		token = get_next_token_from_string(&ptr, tab_size);

		if (newline && token.type != WHITESPACE) {
			hprintf(h, "<span class=\"line\">");
			newline = 0;
		}

		if (token.type == WHITESPACE) {
			if (strchr(token.text, '\n')) {
				if (!newline) {
					hprintf(h, "</span>");
				}
				escape_html(h, token.text);
				newline = 1;
			} else {
				escape_html(h, token.text);
			}
		} else {
			hprintf(h, "<span class=\"%s\">", get_token_class_name(token.type));
			escape_html(h, token.text);
			hprintf(h, "</span>");
		}

		if (*ptr == '\0' && !newline) {
			hprintf(h, "</span>");
		}
	}
}

void render_code_block(DynamicBuffer *h, FILE *fd) {
	DynamicBuffer code_buffer = {0};
	char line[1024];  // reasonable line buffer size
	int in_code_block = 1;
	int is_empty_block = 1;  // Flag to check if the block is empty

	// TODO add code detector
	hprintf(h, "<style>"
	".keyword { color: blue; }"
	".identifier { color: black; }"
	".string { color: green; }"
	".number { color: orange; }"
	".comment { color: gray; }"
	".operator { color: red; }"
	".punctuation { color: black; }"
	"</style>\n");

	hprintf(h, "<pre><code>");

	while (fgets(line, sizeof(line), fd) && in_code_block) {
		if (strncmp(line, "```", 3) == 0) {
			in_code_block = 0;
			break;
		}

		is_empty_block = 0;

		hprintf(&code_buffer, "%s", line);
	}

	if (!is_empty_block) {
		highlight_code(h, code_buffer.data, 4);  // Assume tab size of 4
	}

	hprintf(h, "</code></pre>\n");
	free(code_buffer.data);
}


void render_table_row(DynamicBuffer *h, const char* line, int is_header) {
	hprintf(h, "<tr>\n");
	char* token = strtok((char*)line, "|");
	while (token != NULL) {
		while (*token == ' ') token++; // Trim leading spaces
		char* end = token + strlen(token) - 1;
		while (end > token && *end == ' ') end--; // Trim trailing spaces
		*(end + 1) = '\0';
		if (is_header) {
			hprintf(h, "  <th>%s</th>\n", token);
		} else {
			hprintf(h, "  <td>%s</td>\n", token);
		}
		token = strtok(NULL, "|");
	}
	hprintf(h, "</tr>\n");
}

void render_line(DynamicBuffer *h, char* line, FILE* fd) {
	int header_level;
	if (is_header(line, &header_level)) {
		hprintf(h, "<h%d>", header_level);
		process_inline_markdown(h, line + header_level + 1);
		hprintf(h, "</h%d>\n", header_level);
	} else if (is_horizontal_rule(line)) {
		hprintf(h, "<hr>\n");
	} else if (strncmp(line, ">", 1) == 0) {  // Blockquote
		hprintf(h, "<blockquote><p>");
		process_inline_markdown(h, line + 1);
		hprintf(h, "</p></blockquote>\n");
	} else if (strncmp(line, "```", 3) == 0) {  // Code block
		render_code_block(h, fd);
	} else if (strncmp(line, "- ", 2) == 0 || strncmp(line, "* ", 2) == 0 || strncmp(line, "+ ", 2) == 0) {  // Unordered list
		static int in_list = 0;
		if (!in_list) {
			hprintf(h, "<ul>\n");
			in_list = 1;
		}
		hprintf(h, "<li>");
		process_inline_markdown(h, line + 2);
		hprintf(h, "</li>\n");
		// Check next line to see if we need to close the list
		char next_line[MAX_LINE_LENGTH];
		long pos = ftell(fd);
		if (fgets(next_line, sizeof(next_line), fd)) {
			if (!(strncmp(next_line, "- ", 2) == 0 || strncmp(next_line, "* ", 2) == 0 || strncmp(next_line, "+ ", 2) == 0)) {
				hprintf(h, "</ul>\n");
				in_list = 0;
			}
			fseek(fd, pos, SEEK_SET);  // Go back to previous position
		}
	} else if (isdigit(line[0]) && strncmp(line + 1, ". ", 2) == 0) {  // Ordered list
		static int in_ordered_list = 0;
		if (!in_ordered_list) {
			hprintf(h, "<ol>\n");
			in_ordered_list = 1;
		}
		hprintf(h, "<li>");
		process_inline_markdown(h, line + 3);
		hprintf(h, "</li>\n");
		// Check next line to see if we need to close the list
		char next_line[MAX_LINE_LENGTH];
		long pos = ftell(fd);
		if (fgets(next_line, sizeof(next_line), fd)) {
			if (!(isdigit(next_line[0]) && strncmp(next_line + 1, ". ", 2) == 0)) {
				hprintf(h, "</ol>\n");
				in_ordered_list = 0;
			}
			fseek(fd, pos, SEEK_SET);  // Go back to previous position
		}
	} else if (line[0] == '|') {  // Table
		render_table_row(h, line, 1);  // Assuming this is the header row
		// Handle table body rows
		char next_line[MAX_LINE_LENGTH];
		while (fgets(next_line, sizeof(next_line), fd) && next_line[0] == '|') {
			if (strstr(next_line, "---")) {
				continue;  // Skip separator row
			}
			render_table_row(h, next_line, 0);
		}
		fseek(fd, -strlen(next_line), SEEK_CUR);  // Go back one line
	} else if (strlen(line) == 0) {  // Empty line
		hprintf(h, "\n");
	} else {  // Regular paragraph
		hprintf(h, "<p>");
		process_inline_markdown(h, line);
		hprintf(h, "</p>\n");
	}
}


void md_content(DynamicBuffer* h, FILE* fd) {
	char line[MAX_CODE_BLOCK_SIZE];
	int in_paragraph = 0;
	int in_table = 0;
	while (fgets(line, sizeof(line), fd)) {
		line[strcspn(line, "\n")] = 0; // Remove newline character
		if (strlen(line) == 0) {
			if (in_paragraph) {
				hprintf(h, "</p>\n");
				in_paragraph = 0;
			}
			if (in_table) {
				hprintf(h, "</table>\n");
				in_table = 0;
			}
		} else if (line[0] == '|') {  // Table row
			if (!in_table) {
				hprintf(h, "<table>\n");
				in_table = 1;
				render_table_row(h, line, 1);  // Render header row
			} else if (strstr(line, "---")) {
				// Skip the separator row
			} else {
				render_table_row(h, line, 0);  // Render regular row
			}
		} else {
			if (!in_paragraph && !is_header(line, &(int){0}) && !is_horizontal_rule(line)) {
				hprintf(h, "<p>");
				in_paragraph = 1;
			}
			render_line(h, line, fd);
		}
	}
	if (in_paragraph) { hprintf(h, "</p>\n"); }
	if (in_table) { hprintf(h, "</table>\n"); }
}


char* find_format_hashtags(FILE* fd) {
	char line[MAX_LINE_LENGTH];
	char* formatted_tags = NULL;
	long file_size;

	fseek(fd, 0, SEEK_END);
	file_size = ftell(fd);

	if (file_size == 0) {
		return NULL;
	}

	long pos = -2;
	while (pos >= -file_size && fgetc(fd) != '\n') {
		fseek(fd, pos, SEEK_END);
		pos--;
	}

	if (fgets(line, sizeof(line), fd) == NULL) {
		return NULL;  // Failed to read the last line
	}

	line[strcspn(line, "\n")] = 0;

	formatted_tags = malloc(strlen(line) + 1);
	if (formatted_tags == NULL) {
		fprintf(stderr, "Memory allocation failed\n");
		return NULL;
	}
	formatted_tags[0] = '\0';

	char* token = strtok(line, " ");
	int first_tag = 1;

	while (token != NULL) {
		if (token[0] == '#') {
			// Remove '#' symbol
			char* tag = token + 1;

			// Replace underscore with space
			for (char* ch = tag; *ch; ch++) {
				if (*ch == '_') *ch = ' ';
			}

			if (!first_tag) {
				strcat(formatted_tags, ", ");
			}
			strcat(formatted_tags, tag);
			first_tag = 0;
		}
		token = strtok(NULL, " ");
	}

	// If no tags found, free memory and return NULL
	if (first_tag) {
		free(formatted_tags);
		return NULL;
	}

	return formatted_tags;
}


int bufferVprintf(DynamicBuffer* buffer, const char* format, va_list args) {
	va_list args_copy;
	va_copy(args_copy, args);
	int i = vsnprintf(NULL, 0, format, args_copy);
	va_end(args_copy);
	if (i < 0) {
		++buffer->corrupts;
		return 0;
	}
	size_t reqsz = buffer->size + i + 1;
	if (reqsz > buffer->capacity) {
		size_t new_capacity = ((reqsz - 1) / EXPANDBUFFER_CHUNK_SIZE + 1) * EXPANDBUFFER_CHUNK_SIZE;
		char* new_data = realloc(buffer->data, new_capacity);
		if (new_data) {
			buffer->data = new_data;
			buffer->capacity = new_capacity;
		} else {
			++buffer->corrupts;
			return 0;
		}
	}
	i = vsnprintf(buffer->data + buffer->size, buffer->capacity - buffer->size, format, args);
	if (i < 0) {
		++buffer->corrupts;
		return 0;
	}
	buffer->size += i;
	return i;
}

int hprintf(DynamicBuffer* buffer, const char* format, ...) {
	va_list args;
	va_start(args, format);
	int result = bufferVprintf(buffer, format, args);
	va_end(args);
	return result;
}

char* sanitize_url(const char* url) {
	if (url == NULL) return NULL;

	size_t len = strlen(url);
	char* sanitized = malloc(len + 1);
	if (sanitized == NULL) return NULL;

	size_t i = 0, j = 0;
	bool previous_slash = false;

	// Skip leading '.' or '..'
	while (url[i] == '.' || url[i] == '/') i++;

	for (; i < len; i++) {
		if (url[i] == '/' && previous_slash) {
			continue; // Skip consecutive slashes
		}

		if (isprint(url[i]) && url[i] != '\\') {
			sanitized[j++] = url[i];
			previous_slash = (url[i] == '/');
		}
	}

	sanitized[j] = '\0';

	// Check if the sanitized URL is empty or only contains '/'
	if (j == 0 || (j == 1 && sanitized[0] == '/')) {
		free(sanitized);
		return NULL;
	}

	return sanitized;
}


enum MHD_Result serve_file(struct MHD_Connection *connection, const char *url) {
	enum MHD_Result result = MHD_NO;
	char *real_path = NULL;
	DynamicBuffer *buffer = NULL;

	ReqData req = {
		.Bot = 0,
		.UserAgent = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, HEADER_UserAgent),
		.BaseURL   = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, HEADER_BaseURL),
		.RealIP	= MHD_lookup_connection_value(connection, MHD_HEADER_KIND, HEADER_RealIP),
		.DocRoot   = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, HEADER_DocumentRoot)
	};

	char* sanitized_url = sanitize_url(url);
	if (!sanitized_url) {
		goto cleanup; // URL sanitization failed
	}

	req.URL = sanitized_url;

	char full_path[PATH_MAX + 1]; // Use PATH_MAX from limits.h
	if (req.DocRoot) {
		int written = snprintf(full_path, sizeof(full_path), "%s/%s", req.DocRoot, sanitized_url);
		if (written < 0 || (size_t)written >= sizeof(full_path) - 1) {
			goto cleanup; // Path too long or encoding error
		}
	} else {
		int written = snprintf(full_path, sizeof(full_path), "./%s", sanitized_url);
		if (written < 0 || (size_t)written >= sizeof(full_path) - 1) {
			goto cleanup; // Path too long or encoding error
		}
	}
	full_path[sizeof(full_path) - 1] = '\0'; // Ensure null-termination

	errno = 0;
	char resolved_path[PATH_MAX + 1];
	if (realpath(full_path, resolved_path) == NULL) {
		goto cleanup;
	}

	if (req.DocRoot) {
		char resolved_docroot[PATH_MAX + 1];
		if (realpath(req.DocRoot, resolved_docroot) == NULL) {
			goto cleanup;
		}
		if (strncmp(resolved_path, resolved_docroot, strlen(resolved_docroot)) != 0) {
			goto cleanup; // Path is outside of allowed directory
		}
	}

	int written = snprintf(req.filename, sizeof(req.filename), "%s", resolved_path);
	if (written < 0 || (size_t)written >= sizeof(req.filename) - 1) {
		goto cleanup; // Path too long for req.filename or encoding error
	}
	req.filename[sizeof(req.filename) - 1] = '\0'; // Ensure null-termination

	buffer = malloc(sizeof(DynamicBuffer));
	if (!buffer) {
		goto cleanup;
	}

	buffer->data = malloc(EXPANDBUFFER_CHUNK_SIZE);
	if (!buffer->data) {
		free(buffer);
		buffer = NULL;
		goto cleanup;
	}

	buffer->capacity = EXPANDBUFFER_CHUNK_SIZE;
	buffer->size = 0;
	buffer->corrupts = 0;

	req.h = buffer;

	memset(&req.timeinfo, 0, sizeof(struct tm));
	if (stat(req.filename, &req.attr) != 0) {
		goto cleanup;
	}

	if (localtime_r(&req.attr.st_mtime, &req.timeinfo) == NULL) {
		goto cleanup;
	}

	generateHtml(&req);

	if ((buffer->corrupts == 0) && (buffer->size > 0)) {
		struct MHD_Response *response = MHD_create_response_from_buffer(
			buffer->size,
			buffer->data,
			MHD_RESPMEM_MUST_FREE
		);
		if (!response) {
			goto cleanup;
		}
		result = MHD_queue_response(connection, MHD_HTTP_OK, response);
		MHD_destroy_response(response);
	} else {
		struct MHD_Response *response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
		if (!response) {
			goto cleanup;
		}
		result = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response);
		MHD_destroy_response(response);
	}

cleanup:

	if (sanitized_url) { free(sanitized_url); }
	if (real_path) { free(real_path); }
	if (result != MHD_YES && buffer) {
		if (buffer->data) {
			free(buffer->data);
		}
		free(buffer);
	}
	return result;
}

static enum MHD_Result RequestProcess(void *cls,
	struct MHD_Connection *connection,
	const char *url,
	const char *method,
	const char *version,
	const char *upload_data,
	size_t *upload_data_size,
	void **req_cls
) {
	enum MHD_Result result = MHD_NO;
	if (strlen(url) >= MAX_URL_LENGTH) { return MHD_NO; }
	if (0 == strcmp(method, "GET")) {
		const char *str_redirect = "/redirect";
		if (strncmp(url, str_redirect, strlen(str_redirect)) == 0) {
			result = serve_static_redirect(connection);
		} else {
			result = serve_file(connection, url);
		}
	} else {
		fprintf(stderr, "Method! [%s]\n", method);
	}
	(void) cls; (void) url; (void) version; (void) upload_data;
	(void) upload_data_size; (void) req_cls; // mute warning
	return result;
}

int main (int argc, char *const *argv) {
	(void)argc; (void)argv;
	struct MHD_Daemon *d;
	struct sockaddr_in loopback_addr;

	memset(&loopback_addr, 0, sizeof(loopback_addr));
	loopback_addr.sin_family = AF_INET;
	loopback_addr.sin_port = htons(_TCP_PORT);
	loopback_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

	d = MHD_start_daemon(
		MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_EPOLL | MHD_USE_TURBO,
		_TCP_PORT,
		NULL, NULL, &RequestProcess, NULL,
		MHD_OPTION_SOCK_ADDR, (struct sockaddr *)(&loopback_addr),
		MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) _CONNECTION_TOT_SEC,
		MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) _THREAD_POOL_SIZE,
		MHD_OPTION_CONNECTION_LIMIT, (unsigned int) _CONNECTION_LIMIT,
		MHD_OPTION_END
	);

	if (d == NULL) {
		fprintf(stderr, "Failed to create MHDaemon\n");
		return 1;
	}

	char ip_str[INET_ADDRSTRLEN];
	inet_ntop(AF_INET, &loopback_addr.sin_addr, ip_str, sizeof(ip_str));
	printf("Listen on %s:%d Press ENTER to stop\n", ip_str, _TCP_PORT);
	int c;
	while ((c = getchar()) != '\n' && c != EOF) {
		// Consume any extra input
	}
	MHD_stop_daemon(d);
	return 0;
}

main.h

#ifndef MAIN_H_INCLUDED
#define MAIN_H_INCLUDED

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <libgen.h>
#include <sys/stat.h>
#include <dirent.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>
#include <pthread.h>
#include <microhttpd.h>
#include <arpa/inet.h>
#include <stdbool.h>

#define MAX_TOKEN_LENGTH 2048
#define MAX_CODE_BLOCK_SIZE 2048
#define MAX_LINE_LENGTH 2048
#define EXPANDBUFFER_CHUNK_SIZE 2048
#define MAX_PATH 256
#define MAX_URL_LENGTH 2048

typedef struct {
	char* data;
	size_t size;
	size_t capacity;
	size_t corrupts;
} DynamicBuffer;

typedef struct {
	uint64_t Bot;
	const char* DocRoot;
	const char* UserAgent;
	const char* BaseURL;
	const char* RealIP;
	char filename[1024];
	struct tm timeinfo;
	const char* URL;
	FILE* file;
	struct stat attr;
	DynamicBuffer *h;
} ReqData;

void md_content(DynamicBuffer* h, FILE* fd);
void render_link_or_media(DynamicBuffer *h, const char* text, const char* url);
void render_table_row(DynamicBuffer *h, const char* line, int is_header);
// void extract_md_metadata(FILE* fd, char** h1_title, char** description, char** formatted_tags);
// void print_time_datetime(DynamicBuffer* h, const struct tm *date);
// char* find_h1_title(FILE* fd);
char* find_format_hashtags(FILE* fd);



int hprintf(DynamicBuffer* buffer, const char* format, ...);
void generateHtml(ReqData* Req);

#define HTAG_BLOCK(fd, tag) \
	for(int _i = ({ hprintf(fd, "<%s>", tag); 0;}); \
		!_i; \
		_i = ({hprintf(fd, "</%s>\n", tag); 1;}))

#define HTAG_BLOCKA(fd, tag, attr) \
	for(int _i = ({hprintf(fd, "<%s %s>", tag, attr); 0;}); \
		!_i; \
		_i = ({hprintf(fd, "</%s>\n", tag); 1;}))

enum MHD_Result serve_static_access_denied(struct MHD_Connection *connection);
enum MHD_Result serve_static_jpeg(struct MHD_Connection *connection, const char *file_path);
enum MHD_Result serve_static_redirect(struct MHD_Connection *connection);


extern const char *HEADER_UserAgent;
extern const char *HEADER_BaseURL;
extern const char *HEADER_RealIP;
extern const char *HEADER_DocumentRoot;

#endif // MAIN_H_INCLUDED

site.c

#include "main.h"

static const char *Access_denied = "Access denied";
// Example:
// #define FPATH_TEMPLETE "./store/%ld/%ld.jpeg"
// static const char *redir_to_HomePage = "/";
// static const char *redir_to_whatsapp = "https://wa.me/00000000";
// static const char *redir_to_telegram = "https://t.me/telegram";
// static const char *redir_to_viber =	"viber://chat?number=00000000";
// static const char *redir_to_email = "mailto:[email protected]?subject=HELLO";

#include "../secret.h"

void render_menu_links(DynamicBuffer *h) {
	render_link_or_media(h, "Telegram",  "/redirect?messenger=telegram");
	render_link_or_media(h, "WhatsApp",  "/redirect?messenger=whatsapp");
	render_link_or_media(h, "Viber",	 "/redirect?messenger=viber");
	render_link_or_media(h, "Email",	 "/redirect?messenger=email");
	render_link_or_media(h, "LinkedIn",  redir_to_LinkedIn);
	render_link_or_media(h, "GitHub",	redir_to_GitHub);
	render_link_or_media(h, "Facebook",  redir_to_Facebook);
	render_link_or_media(h, "Instagram", redir_to_Instagram);
	render_link_or_media(h, "About",	 "/about");
}

#define MAX_BOTS 14

const char *bots[MAX_BOTS] = {
	"googlebot",	"telegrambot",  "twitterbot",
	"bingbot",	  "slurp",		"duckduckbot",
	"baiduspider",  "yandex",	   "sogou",
	"exabot",	   "facebot",	  "ia_archiver",
	"curl",		 "wget"
};

typedef enum {
	BOT_NONE		= 0,
	BOT_GOOGLE	  = 1 << 0,
	BOT_TELEGRAM	= 1 << 1,
	BOT_TWITTER	 = 1 << 2,
	BOT_BING		= 1 << 3,
	BOT_YAHOO	   = 1 << 4,
	BOT_DUCKDUCKGO  = 1 << 5,
	BOT_BAIDU	   = 1 << 6,
	BOT_YANDEX	  = 1 << 7,
	BOT_SOGOU	   = 1 << 8,
	BOT_EXABOT	  = 1 << 9,
	BOT_FACEBOOK	= 1 << 10,
	BOT_ARCHIVE	 = 1 << 11,
	BOT_CURL		= 1 << 12,
	BOT_WGET		= 1 << 13
} BotType;

uint64_t detect_bot_UA(const char *user_agent) {
	uint64_t result = 0;
	char *lUA = strdup(user_agent);
	for (int i = 0; lUA[i]; i++) { lUA[i] = tolower(lUA[i]); }
	for (int i = 0; i < MAX_BOTS; i++) {
		if (strstr(lUA, bots[i]) != NULL) {
			result |= (1ULL << i);
		}
	}
	free(lUA);
	return result;
}

const char *days_of_week_en[] = {
	"Sunday", "Monday", "Tuesday", "Wednesday",
	"Thursday", "Friday", "Saturday"
};

const char *months_en[] = {
	"January", "February", "March", "April",
	"May", "June", "July", "August", "September",
	"October", "November", "December"
};

void print_time_datetime(DynamicBuffer* h, const struct tm *date) {
	const char** days_of_week = days_of_week_en;
	const char** months = months_en;

	if (!h || !date || !days_of_week || !months) return;
	hprintf(h, "<time datetime=\"%d-%02d-%02d %02d:%02d\">%s %s %02d, %d</time>\n",
			date->tm_year + 1900, date->tm_mon + 1, date->tm_mday,
			date->tm_hour, date->tm_min,
			days_of_week[date->tm_wday],
			months[date->tm_mon],
			date->tm_mday, date->tm_year + 1900);
}


int file_access_with_extension(const char *filename, const char *extension) {
	if (filename == NULL || extension == NULL) { return 1; }
	char *new_path = (char *)malloc(strlen(filename) + strlen(extension) + 1);
	if (new_path == NULL) { return 1; }
	strcpy(new_path, filename);
	strcat(new_path, extension);
	int ret = access(new_path, F_OK);
	free(new_path);
	return ret; // 0 - OK
}


char* file_mkpath_with_extension(const char *filename, const char *extension) {
	if (filename == NULL || extension == NULL) { return NULL; }
	char *new_path = (char *)malloc(strlen(filename) + strlen(extension) + 1);
	if (new_path == NULL) { return NULL; }
	strcpy(new_path, filename);
	strcat(new_path, extension);
	if (access(new_path, F_OK) == 0) {
		return new_path;
	} else {
		free(new_path);
		return NULL;
	}
}

FILE* file_open_with_extension(const char *filename, const char *extension) {
	if (filename == NULL || extension == NULL) { return NULL; }
	char *new_path = (char *)malloc(strlen(filename) + strlen(extension) + 1);
	if (new_path == NULL) { return NULL; }
	strcpy(new_path, filename);
	strcat(new_path, extension);
	FILE *file = fopen(new_path, "r");
	free(new_path);
	return file;
}





void print_l_r_m_h(ReqData* Req, const char* rel, const char* size, const char* href) {
	if (!Req || !rel || !href) return;
	hprintf(Req->h, "<link rel=\"%s\" ", rel);
	if (size) {
		if (size[0]) {
			hprintf(Req->h, "media=\"%s\" ", size);
		}
	}
	if (href[0] == '\1') {
		hprintf(Req->h, "href=\"%s/%s\">\n", Req->BaseURL, href + 1);
	} else {
		hprintf(Req->h, "href=\"%s\">\n", href);
	}
}

void esprint(DynamicBuffer *h, const char* src) {
	size_t len = strlen(src);
	for (size_t i = 0; i < len; i++) {
		switch (src[i]) {
			case '&':  hprintf(h, "&amp;"); break;
			case '<':  hprintf(h, "&lt;"); break;
			case '>':  hprintf(h, "&gt;"); break;
			case '"':  hprintf(h, "&quot;"); break;
			case '\'': hprintf(h, "&#39;"); break;
			default:   hprintf(h, "%c", src[i]);
		}
	}
}

void printMetaTags(ReqData* Req,
	const char* title,
	const char* description,
	const char* keywords_custom,
	// const char* canonical_url,
	const char* author,
	const char* twitter_channel,
	const char* telegram_channel,
	const char* locale)
{
	hprintf(Req->h, "\n<meta charset=\"UTF-8\">\n");

	if (!Req->Bot) {
		hprintf(Req->h, "<meta name=\"viewport\" content=\"width=device-width, "
		"initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no\">\n");
	}

	if (title && title[0]) {
		hprintf(Req->h, "<title>");
		esprint(Req->h, title);
		hprintf(Req->h, "</title>\n");
		hprintf(Req->h, "<meta name=\"title\" content=\"");
		esprint(Req->h, title);
		hprintf(Req->h, "\">\n");
		hprintf(Req->h, "<meta name=\"twitter:title\" content=\"");
		esprint(Req->h, title);
		hprintf(Req->h, "\">\n");
	}

	if (description && description[0]) {
		hprintf(Req->h, "<meta name=\"description\" content=\"");
		esprint(Req->h, description);
		hprintf(Req->h, "\">\n");
		hprintf(Req->h, "<meta name=\"twitter:description\" content=\"");
		esprint(Req->h, description);
		hprintf(Req->h, "\">\n");
		hprintf(Req->h, "<meta property=\"og:description\" content=\"");
		esprint(Req->h, description);
		hprintf(Req->h, "\">\n");
	}

	hprintf(Req->h, "<meta name=\"keywords\" content=\"");
	esprint(Req->h, def_keywords_a);
	if (keywords_custom) {
		hprintf(Req->h, ", ");
		esprint(Req->h, keywords_custom);
	} else {
		if (Req->Bot) {
			esprint(Req->h, def_keywords_b);
		}
	}
	hprintf(Req->h, "\">\n");
	// <meta name="generator" content="generator.com" />
	// <meta name="application-name" content="generator.com" />
	// <meta property="fb:app_id" content="000000000" />
	// <meta name="apple-itunes-app" content="app-id=111111111111" />
	// <meta name="apple-mobile-web-app-title" content="XXXXX.com" />

	if (author && author[0]) {
		hprintf(Req->h, "<meta name=\"author\" content=\"");
		esprint(Req->h, author);
		hprintf(Req->h, "\">\n");

		hprintf(Req->h, "<meta property=\"article:author\" content=\"");
		esprint(Req->h, author);
		hprintf(Req->h, "\">\n");
	}


	// CSS files
	print_l_r_m_h(Req, "stylesheet", NULL, "\1css/default.css");
	print_l_r_m_h(Req, "stylesheet", "only screen and (max-width: 550px)", "\1css/mobile.css");
	print_l_r_m_h(Req, "stylesheet", "only screen and (min-width: 551px)", "\1css/desktop.css");

	// Favicon
	hprintf(Req->h, "<link rel=\"icon\" type=\"image/png\" href=\"%s\">\n", "/favicon.png");

	// Twitter Card
	hprintf(Req->h, "<meta name=\"twitter:card\" content=\"summary_large_image\">\n");
	if (twitter_channel && twitter_channel[0]) {
		hprintf(Req->h, "<meta name=\"twitter:site\" content=\"%s\">\n", twitter_channel);
	}

	if (file_access_with_extension(Req->filename, ".jpeg") == 0) {
		hprintf(Req->h, "<meta name=\"twitter:image\" content=\"%s/%s%s\">\n", Req->BaseURL, Req->URL, ".jpeg");
		hprintf(Req->h, "<meta property=\"og:image\" content=\"%s/%s%s\">\n",  Req->BaseURL, Req->URL, ".jpeg");
	}

	// Open Graph
	hprintf(Req->h, "<meta property=\"og:type\" content=\"website\">\n");
	hprintf(Req->h, "<meta property=\"og:title\" content=\"");
	esprint(Req->h, title);
	hprintf(Req->h, "\">\n");
	hprintf(Req->h, "<meta property=\"og:site_name\" content=\"");
	esprint(Req->h, def_title);
	hprintf(Req->h, "\">\n");



	// Req->BaseURL, Req->URL, ".jpeg");

	hprintf(Req->h, "<meta property=\"og:url\" content=\"%s/%s\">\n", Req->BaseURL, Req->URL);
	hprintf(Req->h, "<meta property=\"og:image:type\" content=\"image/jpeg\">\n");
	hprintf(Req->h, "<meta property=\"og:image:alt\" content=\"");
	esprint(Req->h, title);
	hprintf(Req->h, "\">\n");

	if (Req->Bot & BOT_TELEGRAM) {
		if (telegram_channel && telegram_channel[0]) {
			hprintf(Req->h, "<meta property=\"telegram:channel\" content=\"%s\">\n", telegram_channel);
		}
		hprintf(Req->h, "<meta property=\"tg:site_verification\" content=\"g7j8/rPFXfhyrq5q0QQV7EsYWv4=\">\n");
	}

	if (Req->timeinfo.tm_year) {
		char time_str[74];
		snprintf(time_str, sizeof(time_str), "%d-%02d-%02dT%02d:%02d:%02dZ", Req->timeinfo.tm_year + 1900,
				 Req->timeinfo.tm_mon + 1, Req->timeinfo.tm_mday, Req->timeinfo.tm_hour, Req->timeinfo.tm_min, Req->timeinfo.tm_sec);
		hprintf(Req->h, "<meta name=\"date\" content=\"%s\">\n"
		"<meta name=\"last-modified\" content=\"%s\">\n"
		"<meta property=\"article:published_time\" content=\"%s\">\n",
		time_str, time_str, time_str);
	}

	// Canonical
	hprintf(Req->h, "<link rel=\"canonical\" href=\"%s/%s\">\n", Req->BaseURL, Req->URL);

	if (locale && locale[0]) {
		hprintf(Req->h, "<meta property=\"og:locale\" content=\"%s\">\n", locale);
			hprintf(Req->h, "<link rel=\"alternate\" hreflang=\"%s\" href=\"%s/%s\">\n",
				locale, Req->BaseURL, Req->URL);
	}
	// Google hreflang tag
	hprintf(Req->h, "<link rel=\"alternate\" hreflang=\"x-default\" href=\"%s/%s\">\n", Req->BaseURL, Req->URL);
	// <meta name="google-site-verification" content="XXXXXXXXXXXXXXXXXXXXXX" />
}


void extract_md_metadata(FILE* fd, char** h1_title, char** description, char** formatted_tags) {
	char line[MAX_LINE_LENGTH];
	long file_size;
	int h1_found = 0;
	size_t desc_len = 0;

	*h1_title = NULL;
	*description = NULL;
	*formatted_tags = NULL;

	// Allocate memory for description
	*description = malloc(MAX_DESC_LEN + 1);
	if (*description == NULL) {
		fprintf(stderr, "Memory allocation failed for description\n");
		return;
	}
	(*description)[0] = '\0';

	// Get file size
	fseek(fd, 0, SEEK_END);
	file_size = ftell(fd);
	rewind(fd);

	if (file_size == 0) {
		return;  // File is empty
	}

	// Read the file line by line
	while (fgets(line, sizeof(line), fd)) {
		// Remove newline character
		line[strcspn(line, "\n")] = 0;

		// Check for h1 title if not found yet
		if (!h1_found && line[0] == '#' && line[1] == ' ') {
			char* start = line + 2;
			while (*start == ' ') start++;

			char* end = start + strlen(start) - 1;
			while (end > start && *end == ' ') end--;
			*(end + 1) = '\0';

			*h1_title = malloc(end - start + 2);
			if (*h1_title == NULL) {
				fprintf(stderr, "Memory allocation failed for h1_title\n");
				return;
			}
			// Safely copy and escape the h1 title
			char* dst = *h1_title;
			for (char* src = start; *src && dst - *h1_title < end - start + 1; src++) {
				switch (*src) {
					case '&':  strcpy(dst, "&amp;");  dst += 5; break;
					case '<':  strcpy(dst, "&lt;");   dst += 4; break;
					case '>':  strcpy(dst, "&gt;");   dst += 4; break;
					case '"':  strcpy(dst, "&quot;"); dst += 6; break;
					case '\'': strcpy(dst, "&#39;");  dst += 5; break;
					default:   *dst++ = *src;
				}
			}
			*dst = '\0';
			h1_found = 1;
		}
		// Process description
		else if (desc_len < MAX_DESC_LEN && line[0] != '#') {
			for (char* c = line; *c && desc_len < MAX_DESC_LEN; c++) {
				if (isprint(*c)) {
					switch (*c) {
						case '&':
							if (desc_len + 5 <= MAX_DESC_LEN) {
								memcpy(*description + desc_len, "&amp;", 5);
								desc_len += 5;
							}
							break;
						case '<':
							if (desc_len + 4 <= MAX_DESC_LEN) {
								memcpy(*description + desc_len, "&lt;", 4);
								desc_len += 4;
							}
							break;
						case '>':
							if (desc_len + 4 <= MAX_DESC_LEN) {
								memcpy(*description + desc_len, "&gt;", 4);
								desc_len += 4;
							}
							break;
						default:
							(*description)[desc_len++] = *c;
					}
				}
			}
			if (desc_len < MAX_DESC_LEN) {
				(*description)[desc_len++] = ' ';
			}
		}
	}

	(*description)[desc_len] = '\0';
	char* end = *description + strlen(*description) - 1;
	while (end > *description && *end == ' ') {
		*end = '\0';
		end--;
	}

	if (line[0] != '\0') {
		*formatted_tags = malloc(strlen(line) * 6 + 1);
		if (*formatted_tags == NULL) {
			fprintf(stderr, "Memory allocation failed for formatted_tags\n");
			return;
		}
		(*formatted_tags)[0] = '\0';

		char* token = strtok(line, " ");
		int first_tag = 1;

		while (token != NULL) {
			if (token[0] == '#') {
				char* tag = token + 1;

				if (!first_tag) {
					strcat(*formatted_tags, ", ");
				}

				char* dst = *formatted_tags + strlen(*formatted_tags);
				for (char* src = tag; *src; src++) {
					switch (*src) {
						case '&':  strcpy(dst, "&amp;");  dst += 5; break;
						case '<':  strcpy(dst, "&lt;");   dst += 4; break;
						case '>':  strcpy(dst, "&gt;");   dst += 4; break;
						case '"':  strcpy(dst, "&quot;"); dst += 6; break;
						case '\'': strcpy(dst, "&#39;");  dst += 5; break;
						case '_':  *dst++ = ' ';					break;
						default:   *dst++ = *src;
					}
				}
				*dst = '\0';
				first_tag = 0;
			}
			token = strtok(NULL, " ");
		}
		if (first_tag) {
			free(*formatted_tags);
			*formatted_tags = NULL;
		}
	}
}

// Helper function to remove leading and trailing spaces
char* trim(const char* str) {
	if (str == NULL) return NULL;
	while (isspace((unsigned char)*str)) str++;
	if (*str == 0) return strdup("");
	const char* end = str + strlen(str) - 1;
	while (end > str && isspace((unsigned char)*end)) end--;
	end++;
	size_t len = end - str;
	char* result = malloc(len + 1);
	if (result) {
		memcpy(result, str, len);
		result[len] = '\0';
	}
	return result;
}

char* concatenateStrings(const char* default_str, const char* section_name, const char* site_name) {
	char* result = NULL;
	size_t len = 0;
	char* trimmed_default = trim(default_str);
	char* trimmed_section = trim(section_name);
	char* trimmed_site = trim(site_name);
	int has_site_name	= (trimmed_site != NULL && trimmed_site[0] != '\0');
	int has_section_name = (trimmed_section != NULL && trimmed_section[0] != '\0');

	if (has_site_name) {
		if (has_section_name) {
			len = snprintf(NULL, 0, FORMAT_TEMPLATE, trimmed_section, trimmed_site) + 1;
			result = (char*)malloc(len);
			if (result) {
				snprintf(result, len, FORMAT_TEMPLATE, trimmed_section, trimmed_site);
			}
		} else {
			len = strlen(trimmed_site) + 1;
			result = strdup(trimmed_site);
		}
	} else if (has_section_name) {
		len = snprintf(NULL, 0, FORMAT_TEMPLATE, trimmed_section, trimmed_default) + 1;
		result = (char*)malloc(len);
		if (result) {
			snprintf(result, len, FORMAT_TEMPLATE, trimmed_section, trimmed_default);
		}
	} else {
		result = strdup(trimmed_default);
	}
	free(trimmed_default);
	free(trimmed_section);
	free(trimmed_site);
	return result;
}

void generateHtml(ReqData *Req) {
	Req->file = fopen(Req->filename, "r");
	if (!Req->file) { return; }
	Req->Bot = detect_bot_UA(Req->UserAgent);
	char *locale  = "en";
	char *author  = "LocalAdmin";
	char *twitter_channel  = NULL;
	char *telegram_channel = NULL;

	if (def_author && def_author[0]) author = (char *)def_author;
	if (def_twitter_channel  && def_twitter_channel[0])  twitter_channel  = (char *)def_twitter_channel;
	if (def_telegram_channel && def_telegram_channel[0]) telegram_channel = (char *)def_telegram_channel;

	char *tags_a = NULL;
	char *title_a = NULL;
	char *description_a = NULL;

	extract_md_metadata(Req->file, &title_a, &description_a, &tags_a);
	// if (tags_a != NULL)		 { printf("	   Tags: %s\n", tags_a); }
	// if (title_a != NULL)		{ printf(" Title (h1): %s\n", title_a); }
	// if (description_a != NULL)  { printf("Description: %s\n", description_a); }

	char	   *title = concatenateStrings("Home",		title_a,	   def_title);
	char *description = concatenateStrings("Description", description_a, def_abstract);

	hprintf(Req->h, "<!DOCTYPE html>\n");

	if (Req->Bot) {
		hprintf(Req->h, "<html>\n");
		HTAG_BLOCK(Req->h, "head") {
			printMetaTags(Req, title, description, tags_a,
				author, twitter_channel, telegram_channel, locale);
		}
	} else {
		hprintf(Req->h, "<html lang=\"%s\" dir=\"ltr\">\n", locale);
		HTAG_BLOCKA(Req->h, "head", "class=\"page\"") {
			printMetaTags(Req, title, description, tags_a,
				author, twitter_channel, telegram_channel, locale);
		}
	}

	HTAG_BLOCK(Req->h, "body") {
		if (Req->Bot & BOT_TELEGRAM) {
			HTAG_BLOCKA(Req->h, "div", "class=\"article\"") {
				HTAG_BLOCK(Req->h, "article class=\"article__content\"") {
					hprintf(Req->h, "<figure>");
					if (file_access_with_extension(Req->filename, ".jpeg") == 0) {
						hprintf(Req->h, "<img src=\"%s/%s%s\" />", Req->BaseURL, Req->URL, ".jpeg");
					}
					hprintf(Req->h, "<figcaption>");
					esprint(Req->h, title);
					hprintf(Req->h, "</figcaption></figure><p>");
					esprint(Req->h, description);
					hprintf(Req->h, "</p>\n");
				}
			}
		} else if (Req->Bot & BOT_FACEBOOK) {
			hprintf(Req->h, "<h1>");
			esprint(Req->h, title);
			hprintf(Req->h, "</h1>\n<p>");
			esprint(Req->h, description);
			hprintf(Req->h, "</p>");
			if (file_access_with_extension(Req->filename, ".jpeg") == 0) {
				hprintf(Req->h, "<img src=\"%s/%s%s\" alt=\"", Req->BaseURL, Req->URL, ".jpeg");
				esprint(Req->h, title);
				hprintf(Req->h, "\">\n");
			}
		} else if (Req->Bot & BOT_GOOGLE) {
			HTAG_BLOCKA(Req->h, "div", "itemscope itemtype=\"http://schema.org/Article\"") {
				hprintf(Req->h, "<h1 itemprop=\"headline\">");
				esprint(Req->h, title);
				hprintf(Req->h, "</h1>\n");
				hprintf(Req->h, "<meta itemprop=\"description\" content=\"");
				esprint(Req->h, description);
				hprintf(Req->h, "\">\n");
				if (file_access_with_extension(Req->filename, ".jpeg") == 0) {
					hprintf(Req->h, "<img src=\"%s/%s%s\" alt=\"", Req->BaseURL, Req->URL, ".jpeg");
					esprint(Req->h, title);
					hprintf(Req->h, "\">\n");
				}
				hprintf(Req->h, "<div itemprop=\"articleBody\">");
				esprint(Req->h, description);
				hprintf(Req->h, "</div>\n");
			}
		} else if (Req->Bot & BOT_TWITTER) {
			HTAG_BLOCKA(Req->h, "div", "class=\"tweet\"") {
				hprintf(Req->h, "<h1>");
				esprint(Req->h, title);
				hprintf(Req->h, "</h1>\n<p>");
				esprint(Req->h, description);
				hprintf(Req->h, "</p>");
				if (file_access_with_extension(Req->filename, ".jpeg") == 0) {
					hprintf(Req->h, "<img src=\"%s/%s%s\" alt=\"", Req->BaseURL, Req->URL, ".jpeg");
					esprint(Req->h, title);
					hprintf(Req->h, "\">\n");
				}
			}
		} else if (Req->Bot & BOT_BING) {
			HTAG_BLOCKA(Req->h, "div", "class=\"bing-snippet\"") {
				hprintf(Req->h, "<h1>");
				esprint(Req->h, title);
				hprintf(Req->h, "</h1>\n<p>");
				esprint(Req->h, description);
				hprintf(Req->h, "</p>");
				if (file_access_with_extension(Req->filename, ".jpeg") == 0) {
					hprintf(Req->h, "<img src=\"%s/%s%s\" alt=\"", Req->BaseURL, Req->URL, ".jpeg");
					esprint(Req->h, title);
					hprintf(Req->h, "\">\n");
				}
			}
		} else if (Req->Bot != 0) {
			// Common case for other bots
			HTAG_BLOCK(Req->h, "article") {
				hprintf(Req->h, "<h1>");	esprint(Req->h, title); hprintf(Req->h, "</h1>\n");
				if (file_access_with_extension(Req->filename, ".jpeg") == 0) {
					hprintf(Req->h, "<img src=\"%s/%s%s\" alt=\"", Req->BaseURL, Req->URL, ".jpeg");
					esprint(Req->h, title);
					hprintf(Req->h, "\">\n");
				}
				hprintf(Req->h, "<p>"); esprint(Req->h, description); hprintf(Req->h, "</p>\n");
			}
		} else {

			HTAG_BLOCKA(Req->h, "header", "id=\"site-header\"") {
				HTAG_BLOCKA(Req->h, "section", "id=\"top\"") {

					hprintf(Req->h, "<a href=\"%s\"><div id=\"logo\" style=\"background-image: url('%s%s');\"></div></a>\n",
							Req->BaseURL, Req->BaseURL, def_logofile );

					if (title_a && title_a[0]) {
						hprintf(Req->h, "<h1><a href=\"%s/%s\">%s</a></h1>\n", Req->BaseURL, Req->URL, title_a);
					} else {
						hprintf(Req->h, "<h1><a href=\"%s\">%s</a></h1>\n", Req->BaseURL, title);
					}

					// if (description_a && description_a[0]) {
					//	 hprintf(Req->h, "<p id=\"site-abstract\">%s</p>\n", description_a);
					// } else {
					hprintf(Req->h, "<p id=\"site-abstract\">%s</p>\n", def_abstract);
					// }


				}

				HTAG_BLOCKA(Req->h, "div", "class=\"menu\"") {
					HTAG_BLOCKA(Req->h, "div", "id=\"menu-section\"") {
						hprintf(Req->h,
								"<label for=\"show-menu\" class=\"show-menu\">%s</label>\n"
								"<input type=\"checkbox\" id=\"show-menu\" role=\"button\">\n",
								"Show Menu"); // TODO "Show Menu"
								HTAG_BLOCKA(Req->h, "nav", "id=\"topmenu\"") {
									render_menu_links(Req->h);
								}
					}
				}
			} // header END

			HTAG_BLOCK(Req->h, "content") {



				Req->file = fopen(Req->filename, "r");
				if (!Req->file) {
					HTAG_BLOCK(Req->h, "blockquote") { hprintf(Req->h, "<p>Filter can't open the specified file</p>"); }
				} else {
					HTAG_BLOCK(Req->h, "header") {
						HTAG_BLOCKA(Req->h, "div", "id=\"banner\"") {
							if (file_access_with_extension(Req->filename, ".jpeg") == 0) {
								hprintf(Req->h, "<picture class=\"banner\">\n"
								"<img src=\"%s/%s%s\" alt=\"Banner\"></picture>\n", Req->BaseURL, Req->URL, ".jpeg");
								hprintf(Req->h, "<p class=\"timestamp\"><span class=\"published\">Published: ");
								print_time_datetime(Req->h, &Req->timeinfo/*, days_of_week, months*/);
								hprintf(Req->h, "</span></p>\n");
							}
						}
					}
					hprintf(Req->h, "<p class=\"floatstop\"></p>");

					HTAG_BLOCKA(Req->h, "div", "id=\"content\"") {
						HTAG_BLOCKA(Req->h, "div", "id=\"main\"") {
							HTAG_BLOCK(Req->h, "main") {
								HTAG_BLOCKA(Req->h, "section", "id=\"intro\"") {
									md_content(Req->h, Req->file);
								}

								// HTAG_BLOCKA(Req->h, "section", "id=\"article-list\"") {
								//	 hprintf(Req->h,"<h2>Articles</h2>\n");
								//	 list_articles(Req->h);
								// }
							}
						}

						HTAG_BLOCKA(Req->h, "div", "id=\"sidebar\"") {
							FILE* sbf = file_open_with_extension(Req->filename, ".sb");
							if (sbf) { // open OK
								char line[256];
								while (fgets(line, sizeof(line), sbf)) {
									hprintf(Req->h, "%s", line);
								}
							} else { // not open
								if (file_access_with_extension(Req->filename, ".sb") == 0) {
									hprintf(Req->h, "<p>Filter can't open the specified file</p>");
								}
							}
							if (sbf) { fclose(sbf); }
							// hprintf(Req->h,"<h2>Tags</h2>");
							// HTAG_BLOCK(Req->h, "nav") {
								// hprintf(Req->h, "<a class=\"tag\" href=\"%s\">%s</a>\n", "url", "name");
								// hprintf(Req->h, "<a class=\"tag\" href=\"%s\">%s</a>\n", "url", "name");
								// hprintf(Req->h, "<a class=\"tag\" href=\"%s\">%s</a>\n", "url", "name");
							// }

							// hprintf(Req->h,"<p class=\"rss\">\n<a class=\"rss\" href=\"%s\">", "TXT");
							// hprintf(Req->h,"<img class=\"rss-logo\" src=\"%s/css/rss.svg\" alt=\"RSS logo\">", Req->BaseURL);
							// hprintf(Req->h,"<span class=\"rss-label\">%s</span></a>\n", "TXT");
						}
					}
					hprintf(Req->h,"<p class=\"floatstop\"></p>\n");
					fclose(Req->file); Req->file = NULL;
				}
			} // content END

			HTAG_BLOCK(Req->h, "footer") {
				print_l_r_m_h(Req, "stylesheet", NULL, "\1css/float.css");
				hprintf(Req->h, def_footer);
				if (Req->RealIP && Req->UserAgent) {
					hprintf(Req->h,"<p><strong>IP: </strong>%s<strong> UA: </strong>%s</p>\n", Req->RealIP, Req->UserAgent);
				}
			} // footer END

		}
	}  // </body>
	hprintf(Req->h, "</html>");
	if (tags_a) free(tags_a);			   // free mem
	if (title_a) free(title_a);			 // free mem
	if (description_a) free(description_a); // free mem
}

enum MHD_Result serve_static_access_denied(struct MHD_Connection *connection) {
	struct MHD_Response *response =
	MHD_create_response_from_buffer(
		strlen(Access_denied),
		(void*)Access_denied,
		MHD_RESPMEM_PERSISTENT);
	enum MHD_Result ret = MHD_queue_response(connection, MHD_HTTP_FORBIDDEN, response);
	MHD_destroy_response(response);
	return ret;
}

enum MHD_Result serve_static_jpeg(struct MHD_Connection *connection, const char *file_path) {
	if (!connection || !file_path) { return MHD_NO; }
	FILE *file = fopen(file_path, "rb");
	if (!file) {
		return serve_static_access_denied(connection);
	}
	fseek(file, 0, SEEK_END);
	size_t file_size = ftell(file);
	fseek(file, 0, SEEK_SET);
	char *buffer = malloc(file_size);
	if (!buffer) {
		fclose(file);
		return MHD_NO;
	}
	size_t bytes_read = fread(buffer, 1, file_size, file);
	fclose(file);
	if (bytes_read != file_size) {
		free(buffer);
		return MHD_NO;
	}
	struct MHD_Response *response = MHD_create_response_from_buffer(file_size, buffer, MHD_RESPMEM_MUST_FREE);
	if (!response) {
		free(buffer);
		return MHD_NO;
	}
	MHD_add_response_header(response, "Content-Type", "image/jpeg");
	enum MHD_Result ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
	MHD_destroy_response(response);
	return ret;
}

enum MHD_Result serve_static_redirect(struct MHD_Connection *connection) {
	const char *UA = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, HEADER_UserAgent);
	if (UA && (detect_bot_UA(UA) != 0)) {
		return serve_static_access_denied(connection);
	}

	const char *dir_id_str  = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "dir");
	const char *file_id_str = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "file");
	if (dir_id_str && file_id_str) {
		char *endptr;
		long dir_id = strtol(dir_id_str, &endptr, 10);
		if (*endptr != '\0' || dir_id < 0) { return MHD_NO; }
		long file_id = strtol(file_id_str, &endptr, 10);
		if (*endptr != '\0' || file_id < 0) { return MHD_NO; }
		char file_path[MAX_PATH];
		snprintf(file_path, sizeof(file_path), FPATH_TEMPLETE, dir_id, file_id);
		return serve_static_jpeg(connection, file_path);
	}

	const char *messenger = MHD_lookup_connection_value(connection, MHD_GET_ARGUMENT_KIND, "messenger");
	const char *redirect_url = redir_to_HomePage;
	if (messenger) {
		if (strcmp(messenger, "whatsapp") == 0) { redirect_url = redir_to_whatsapp; } else
		if (strcmp(messenger, "telegram") == 0) { redirect_url = redir_to_telegram; } else
		if (strcmp(messenger, "viber") == 0) { redirect_url = redir_to_viber; } else
		if (strcmp(messenger, "email") == 0) { redirect_url = redir_to_email; }
	}

	struct MHD_Response *response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
	MHD_add_response_header(response, "Location", redirect_url);
	int ret = MHD_queue_response(connection, MHD_HTTP_FOUND, response);
	MHD_destroy_response(response);
	return ret;
}

#if 0

const char* get_bot_name(uint64_t bot_flags) {
	for (int i = 0; i < MAX_BOTS; i++) {
		if (bot_flags & (1ULL << i)) {
			return bots[i];
		}
	}
	return "Unknown";
}

typedef struct {
	 char *class_art_type;
	 char *url;
	 char *title;
	 struct tm* date;
	 char* description;
	 char* read_txt;
} article_next;

article_next articles[] = { {
	.class_art_type = "article_type",
	.url = "url1",
	.title = "title1",
	.date = NULL,
	.description = "description1",
	.read_txt = "read_txt1",
}, {
	.class_art_type = "article_type",
	.url = "url2",
	.title = "title2",
	.date = NULL,
	.description = "description2",
	.read_txt = "read_txt2",
}, {
	NULL
}};

void list_articles(DynamicBuffer* h) {
	for (int i = 0; articles[i].class_art_type != NULL; i++) {
		HTAG_BLOCKA(h, "article", "class=\"ainlist\"") {
			hprintf(h, "<h3 class=\"%s\"><a href=\"%s\">%s</a></h3>", articles[i].class_art_type, articles[i].url, articles[i].title);
			if (articles[i].date == NULL) {
				time_t now;
				time(&now);
				articles[i].date = localtime(&now);
			}
			print_time_datetime(h, articles[i].date);
			hprintf(h, "<p class=\"abstract\">%s</p>", articles[i].description);
			// hprintf(h, "<div class=\"tags\"><img class=\"tag-icon\" src=\"%scss/tag.svg\" alt=\"Tags\"/>", "./");
			hprintf(h, "<p><a class=\"link-to-article\" href=\"%s\">%s</a></p>", articles[i].url, articles[i].read_txt);
		}
	}
}

#endif

nginx server section

	index index.md index.html index.htm;

	location ~ \.md$ {
		proxy_set_header X-User-Agent	$http_user_agent;
		proxy_set_header X-Document-Root $document_root;
		proxy_set_header X-Base-URL	  $scheme://$host;
		proxy_set_header X-Real-IP	   $remote_addr;
		proxy_pass http://127.0.0.1:8888;
	}

	location = /redirect {
		proxy_set_header X-User-Agent	$http_user_agent;
		proxy_set_header X-Document-Root $document_root;
		proxy_set_header X-Base-URL	  $scheme://$host;
		proxy_set_header X-Real-IP	   $remote_addr;
		proxy_pass http://127.0.0.1:8888;
	}

	location ~ /\. { deny all; }

#Generator #rendered #mdfilter #Markdown #Cprogramminglanguage #nginx #server