+#include "render.h"
+#include "config.h"
+#include "auth.h"
+#include "fs.h"
+#include "utils.h"
+
+extern query_params_t g_params;
+
+void buffer_init(buffer_t *buf) {
+ buf->capacity = 65536;
+ buf->data = malloc(buf->capacity);
+ buf->size = 0;
+}
+
+void buffer_append(buffer_t *buf, const char *str) {
+ size_t len = strlen(str);
+ if (buf->size + len >= buf->capacity) {
+ buf->capacity *= 2;
+ buf->data = realloc(buf->data, buf->capacity);
+ }
+ memcpy(buf->data + buf->size, str, len);
+ buf->size += len;
+}
+
+void buffer_free(buffer_t *buf) { free(buf->data); }
+
+void print_breadcrumb(buffer_t *buf, const char *display_path) {
+ buffer_append(buf, "\n");
+ buffer_append(buf, "
🏠 Главная ");
+ if (display_path && display_path[0]) {
+ char temp_path[1024] = "";
+ char encoded[2048];
+ char *path_copy = strdup(display_path);
+ char *token = strtok(path_copy, "/");
+ while (token) {
+ buffer_append(buf, " / ");
+ if (temp_path[0]) strcat(temp_path, "/");
+ strcat(temp_path, token);
+ url_encode(temp_path, encoded, sizeof(encoded));
+ char safe_token[512];
+ html_escape(token, safe_token, sizeof(safe_token));
+ char link[4096];
+ snprintf(link, sizeof(link), "
%s ", encoded, safe_token);
+ buffer_append(buf, link);
+ token = strtok(NULL, "/");
+ }
+ free(path_copy);
+ }
+ buffer_append(buf, "\n
\n");
+}
+
+void print_password_form(buffer_t *buf, const char *folder_name) {
+ buffer_append(buf, "\n");
+}
+
+void print_content(buffer_t *buf, const char *base_path, const char *display_path) {
+ DIR *dir;
+ struct dirent *entry;
+ struct stat file_stat;
+ char full_path[1024];
+ entry_t *entries = malloc(sizeof(entry_t) * g_config.max_entries);
+ if (!entries) {
+ buffer_append(buf, "Ошибка выделения памяти
");
+ return;
+ }
+ int entry_count = 0, dir_count = 0, file_count = 0;
+ long long total_size = 0;
+ int is_recursive_search = 0;
+ char folder_name[256] = "";
+ if (display_path && display_path[0]) {
+ char *last_slash = strrchr(display_path, '/');
+ if (last_slash) strcpy(folder_name, last_slash + 1);
+ else strcpy(folder_name, display_path);
+ char *query_string = getenv("QUERY_STRING");
+ char *password = NULL;
+ if (query_string) {
+ char *pass_start = strstr(query_string, "password=");
+ if (pass_start) {
+ pass_start += 9;
+ char *pass_end = strchr(pass_start, '&');
+ int pass_len = pass_end ? (int)(pass_end - pass_start) : (int)strlen(pass_start);
+ if (pass_len > 0 && pass_len < 256) {
+ char pass_buf[256];
+ strncpy(pass_buf, pass_start, pass_len);
+ pass_buf[pass_len] = '\0';
+ char decoded_pass[256];
+ url_decode_enhanced(pass_buf, decoded_pass, sizeof(decoded_pass));
+ password = decoded_pass;
+ }
+ }
+ }
+ if (is_folder_protected(folder_name)) {
+ if (!password || !check_folder_password(folder_name, password)) {
+ print_password_form(buf, folder_name);
+ free(entries);
+ return;
+ }
+ }
+ }
+ if (g_params.search[0] != '\0') {
+ int result_count = 0;
+ search_recursive(g_config.base_path, "", g_params.search, entries, &result_count, g_config.max_entries);
+ if (result_count == 0) {
+ buffer_append(buf, "\n");
+ buffer_append(buf, "
🔍
\n");
+ buffer_append(buf, "
Ничего не найдено \n");
+ buffer_append(buf, "
По запросу \"");
+ char safe_search[512];
+ html_escape(g_params.search, safe_search, sizeof(safe_search));
+ buffer_append(buf, safe_search);
+ buffer_append(buf, "\" ничего не найдено
\n");
+ buffer_append(buf, "
\n");
+ free(entries);
+ return;
+ }
+ entry_count = result_count;
+ is_recursive_search = 1;
+ for (int i = 0; i < entry_count; i++) {
+ if (entries[i].is_dir) {
+ dir_count++;
+ total_size += entries[i].dir_size;
+ } else {
+ file_count++;
+ total_size += entries[i].size;
+ }
+ }
+ } else {
+ dir = opendir(base_path);
+ if (!dir) {
+ char alt_path[1024];
+ snprintf(alt_path, sizeof(alt_path), "%s", g_config.base_path);
+ if (display_path[0]) {
+ char *encoded = strdup(display_path);
+ url_decode_enhanced(encoded, alt_path + strlen(alt_path), sizeof(alt_path) - strlen(alt_path));
+ free(encoded);
+ }
+ dir = opendir(alt_path);
+ if (!dir) {
+ buffer_append(buf, "\n");
+ buffer_append(buf, "
📁
\n");
+ buffer_append(buf, "
Ошибка открытия директории \n");
+ char error_msg[512];
+ snprintf(error_msg, sizeof(error_msg), "
Путь: %s
\n", base_path);
+ buffer_append(buf, error_msg);
+ snprintf(error_msg, sizeof(error_msg), "
Ошибка: %s
\n", strerror(errno));
+ buffer_append(buf, error_msg);
+ buffer_append(buf, "
← Вернуться на главную
\n");
+ buffer_append(buf, "
\n");
+ free(entries);
+ return;
+ }
+ }
+ while ((entry = readdir(dir)) != NULL && entry_count < g_config.max_entries) {
+ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
+ int hidden = 0;
+ for (int i = 0; i < g_config.hidden_dirs_count; i++) {
+ if (strcmp(entry->d_name, g_config.hidden_dirs[i]) == 0) { hidden = 1; break; }
+ }
+ for (int i = 0; i < g_config.hidden_files_count; i++) {
+ if (strcmp(entry->d_name, g_config.hidden_files[i]) == 0) { hidden = 1; break; }
+ }
+ if (entry->d_name[0] == '.') hidden = 1;
+ if (hidden) continue;
+ snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name);
+ if (stat(full_path, &file_stat) == 0) {
+ strncpy(entries[entry_count].name, entry->d_name, sizeof(entries[0].name) - 1);
+ entries[entry_count].name[sizeof(entries[0].name) - 1] = '\0';
+ if (S_ISDIR(file_stat.st_mode)) {
+ entries[entry_count].is_dir = 1;
+ entries[entry_count].size = 0;
+ entries[entry_count].mtime = file_stat.st_mtime;
+ entries[entry_count].file_count = count_files_in_dir(full_path);
+ entries[entry_count].dir_size = get_dir_size(full_path);
+ total_size += entries[entry_count].dir_size;
+ if (is_folder_protected(entry->d_name)) {
+ strcpy(entries[entry_count].icon, "🔒");
+ entries[entry_count].is_protected = 1;
+ } else {
+ strcpy(entries[entry_count].icon, "📁");
+ entries[entry_count].is_protected = 0;
+ }
+ char new_path[2048];
+ snprintf(new_path, sizeof(new_path), "%s%s%s", display_path, display_path[0] ? "/" : "", entries[entry_count].name);
+ url_encode(new_path, entries[entry_count].encoded_path, sizeof(entries[0].encoded_path));
+ dir_count++;
+ } else if (S_ISREG(file_stat.st_mode)) {
+ entries[entry_count].is_dir = 0;
+ entries[entry_count].size = file_stat.st_size;
+ entries[entry_count].mtime = file_stat.st_mtime;
+ entries[entry_count].file_count = 0;
+ entries[entry_count].dir_size = 0;
+ strcpy(entries[entry_count].icon, get_file_icon(entry->d_name));
+ entries[entry_count].is_protected = 0;
+ char encoded_filename[1024];
+ url_encode(entry->d_name, encoded_filename, sizeof(encoded_filename));
+ snprintf(entries[entry_count].file_url, sizeof(entries[0].file_url), "%s%s%s", display_path, display_path[0] ? "/" : "", encoded_filename);
+ total_size += file_stat.st_size;
+ file_count++;
+ } else {
+ continue;
+ }
+ entry_count++;
+ }
+ }
+ closedir(dir);
+ if (entry_count == 0) {
+ buffer_append(buf, "\n");
+ buffer_append(buf, "
📄
\n");
+ buffer_append(buf, "
Здесь пусто \n");
+ buffer_append(buf, "
В этой директории нет файлов или папок
\n");
+ buffer_append(buf, "
\n");
+ free(entries);
+ return;
+ }
+ }
+ qsort(entries, entry_count, sizeof(entry_t), compare_entries_sorted);
+ char encoded_path[1024];
+ url_encode(display_path, encoded_path, sizeof(encoded_path));
+ char safe_search[512];
+ html_escape(g_params.search, safe_search, sizeof(safe_search));
+ buffer_append(buf, "\n");
+ if (is_recursive_search) {
+ buffer_append(buf, "");
+ buffer_append(buf, "🔍 Результаты поиска по всему сайту: ");
+ char tmp[32];
+ snprintf(tmp, sizeof(tmp), "%d", entry_count);
+ buffer_append(buf, tmp);
+ buffer_append(buf, " файлов/папок найдено
\n");
+ }
+ buffer_append(buf, "\n");
+ int has_dirs = 0;
+ for (int i = 0; i < entry_count; i++) if (entries[i].is_dir) { has_dirs = 1; break; }
+ if (has_dirs) {
+ int display_dir_count = 0;
+ for (int i = 0; i < entry_count; i++) if (entries[i].is_dir) display_dir_count++;
+ char section_title[128];
+ snprintf(section_title, sizeof(section_title), " 📁 Папки (%d) \n", display_dir_count);
+ buffer_append(buf, section_title);
+ for (int i = 0; i < entry_count; i++) {
+ if (entries[i].is_dir) {
+ buffer_append(buf, " \n");
+ if (entries[i].is_protected) {
+ buffer_append(buf, " \n");
+ buffer_append(buf, "
\n");
+ buffer_append(buf, " 🔒 \n");
+ buffer_append(buf, " ");
+ if (is_recursive_search) {
+ char safe_url[512];
+ html_escape(entries[i].file_url, safe_url, sizeof(safe_url));
+ buffer_append(buf, safe_url);
+ } else {
+ char safe_name2[512];
+ html_escape(entries[i].name, safe_name2, sizeof(safe_name2));
+ buffer_append(buf, safe_name2);
+ }
+ buffer_append(buf, " (защищена) \n");
+ buffer_append(buf, " \n");
+ buffer_append(buf, "
\n");
+ buffer_append(buf, " 🔑 \n");
+ buffer_append(buf, "
\n");
+ buffer_append(buf, "
\n");
+ } else {
+ buffer_append(buf, " \n");
+ char dir_link[512];
+ snprintf(dir_link, sizeof(dir_link),
+ "
\n",
+ entries[i].encoded_path);
+ buffer_append(buf, dir_link);
+ buffer_append(buf, " ");
+ buffer_append(buf, entries[i].icon);
+ buffer_append(buf, " \n");
+ buffer_append(buf, " ");
+ if (is_recursive_search) {
+ char safe_url[512];
+ html_escape(entries[i].file_url, safe_url, sizeof(safe_url));
+ buffer_append(buf, safe_url);
+ } else {
+ char safe_name4[512];
+ html_escape(entries[i].name, safe_name4, sizeof(safe_name4));
+ buffer_append(buf, safe_name4);
+ }
+ buffer_append(buf, " \n");
+ buffer_append(buf, " \n");
+ buffer_append(buf, "
\n");
+ buffer_append(buf, " 🔗 \n");
+ if (entries[i].dir_size > 0) {
+ char size_str[32];
+ format_size(entries[i].dir_size, size_str);
+ buffer_append(buf, " ");
+ buffer_append(buf, size_str);
+ buffer_append(buf, " \n");
+ }
+ buffer_append(buf, "
\n");
+ buffer_append(buf, "
\n");
+ }
+ buffer_append(buf, " \n");
+ }
+ }
+ }
+ int has_files = 0;
+ for (int i = 0; i < entry_count; i++) if (!entries[i].is_dir) { has_files = 1; break; }
+ if (has_files) {
+ int display_file_count = 0;
+ for (int i = 0; i < entry_count; i++) if (!entries[i].is_dir) display_file_count++;
+ char section_title[128];
+ snprintf(section_title, sizeof(section_title), " 📄 Файлы (%d) \n", display_file_count);
+ buffer_append(buf, section_title);
+ for (int i = 0; i < entry_count; i++) {
+ if (!entries[i].is_dir) {
+ char size_str[32];
+ char date_str[64];
+ format_size(entries[i].size, size_str);
+ format_date(entries[i].mtime, date_str, sizeof(date_str));
+ buffer_append(buf, " \n");
+ buffer_append(buf, " \n");
+ char file_link[2048];
+ if (is_recursive_search) {
+ snprintf(file_link, sizeof(file_link), "/%s", entries[i].file_url);
+ } else {
+ snprintf(file_link, sizeof(file_link), "/%s", entries[i].file_url);
+ }
+ buffer_append(buf, "
\n");
+ buffer_append(buf, " ");
+ buffer_append(buf, entries[i].icon);
+ buffer_append(buf, " \n");
+ buffer_append(buf, " ");
+ if (is_recursive_search) {
+ char safe_url[512];
+ html_escape(entries[i].file_url, safe_url, sizeof(safe_url));
+ buffer_append(buf, safe_url);
+ } else {
+ char safe_name5[512];
+ html_escape(entries[i].name, safe_name5, sizeof(safe_name5));
+ buffer_append(buf, safe_name5);
+ }
+ buffer_append(buf, " \n");
+ buffer_append(buf, " \n");
+ buffer_append(buf, "
\n");
+ if (is_previewable(entries[i].name)) {
+ buffer_append(buf, " 👁 \n");
+ }
+ buffer_append(buf, " ⬇️ \n");
+ buffer_append(buf, " 🔗 \n");
+ buffer_append(buf, " ");
+ buffer_append(buf, date_str);
+ buffer_append(buf, " \n");
+ buffer_append(buf, " ");
+ buffer_append(buf, size_str);
+ buffer_append(buf, " \n");
+ buffer_append(buf, "
\n");
+ buffer_append(buf, "
\n");
+ buffer_append(buf, " \n");
+ }
+ }
+ }
+ buffer_append(buf, " \n");
+ if (is_recursive_search) {
+ char stats[256];
+ snprintf(stats, sizeof(stats),
+ "\n Найдено: папок %d | файлов %d\n
\n",
+ dir_count, file_count);
+ buffer_append(buf, stats);
+ } else {
+ char total_size_str[32];
+ format_size(total_size, total_size_str);
+ char stats[256];
+ snprintf(stats, sizeof(stats),
+ "\n Папки: %d | Файлы: %d | Общий размер: %s\n
\n",
+ dir_count, file_count, total_size_str);
+ buffer_append(buf, stats);
+ }
+ free(entries);
+}
+
+void print_template(const char *base_path, const char *display_path) {
+ FILE *file = fopen(g_config.template_path, "r");
+ if (!file) {
+ printf("Error: Cannot read template\n");
+ return;
+ }
+ buffer_t output;
+ buffer_init(&output);
+ char line[4096];
+ while (fgets(line, sizeof(line), file)) {
+ if (strstr(line, "")) {
+ print_breadcrumb(&output, display_path);
+ } else if (strstr(line, "")) {
+ print_content(&output, base_path, display_path);
+ } else {
+ buffer_append(&output, line);
+ }
+ }
+ fclose(file);
+ fwrite(output.data, 1, output.size, stdout);
+ buffer_free(&output);
+}
diff --git a/src/render.h b/src/render.h
new file mode 100644
index 0000000..12a0752
--- /dev/null
+++ b/src/render.h
@@ -0,0 +1,17 @@
+#ifndef RENDER_H
+#define RENDER_H
+
+#include "fs.h"
+
+typedef struct {
+ char *data;
+ size_t size;
+ size_t capacity;
+} buffer_t;
+
+void buffer_init(buffer_t *buf);
+void buffer_append(buffer_t *buf, const char *str);
+void buffer_free(buffer_t *buf);
+void print_template(const char *base_path, const char *display_path);
+
+#endif
diff --git a/src/render.o b/src/render.o
new file mode 100644
index 0000000..b5609ba
Binary files /dev/null and b/src/render.o differ
diff --git a/src/status.c b/src/status.c
new file mode 100644
index 0000000..a674bdc
--- /dev/null
+++ b/src/status.c
@@ -0,0 +1,52 @@
+#include
+#include
+#include
+#include
+
+long get_nginx_memory() {
+ long total_kb = 0;
+ char command[] = "ps -o rss= -C nginx 2>/dev/null";
+
+ FILE *ps = popen(command, "r");
+ if (!ps) return 0;
+
+ char line[64];
+ while (fgets(line, sizeof(line), ps)) {
+ long kb;
+ if (sscanf(line, "%ld", &kb) == 1) {
+ total_kb += kb;
+ }
+ }
+ pclose(ps);
+
+ return total_kb;
+}
+
+int get_process_memory(const char *process_name, long *memory_kb) {
+ char command[256];
+ snprintf(command, sizeof(command), "ps -o rss= -C %s 2>/dev/null | head -1", process_name);
+
+ FILE *ps = popen(command, "r");
+ if (!ps) return 0;
+
+ int result = fscanf(ps, "%ld", memory_kb);
+ pclose(ps);
+
+ return result == 1;
+}
+
+int main() {
+ printf("Content-type: text/plain; charset=utf-8\n\n");
+
+ long busybox_kb = 0, nginx_kb = 0;
+
+ get_process_memory("busybox", &busybox_kb);
+ nginx_kb = get_nginx_memory();
+
+ printf("BusyBox: %ld KB | Nginx: %ld MB | Всего: %.1f MB",
+ busybox_kb,
+ nginx_kb / 1024,
+ (busybox_kb + nginx_kb) / 1024.0);
+
+ return 0;
+}
diff --git a/src/style.c b/src/style.c
new file mode 100644
index 0000000..818e8c1
--- /dev/null
+++ b/src/style.c
@@ -0,0 +1,36 @@
+#include
+#include
+#include
+#include
+
+int main() {
+ char css_path[1024] = "/home/romkazvo/www/cgi-bin/style.css";
+ struct stat st;
+
+ if (stat(css_path, &st) != 0) {
+ printf("Status: 404 Not Found\n");
+ printf("Content-type: text/plain\n\n");
+ printf("CSS file not found\n");
+ return 1;
+ }
+
+ printf("Content-type: text/css\n");
+ printf("Cache-Control: public, max-age=3600\n\n");
+
+ FILE *css_file = fopen(css_path, "r");
+ if (!css_file) {
+ printf("Status: 500 Internal Server Error\n");
+ printf("Content-type: text/plain\n\n");
+ printf("Cannot open CSS file\n");
+ return 1;
+ }
+
+ char buffer[4096];
+ size_t bytes_read;
+ while ((bytes_read = fread(buffer, 1, sizeof(buffer), css_file)) > 0) {
+ fwrite(buffer, 1, bytes_read, stdout);
+ }
+
+ fclose(css_file);
+ return 0;
+}
diff --git a/src/utils.c b/src/utils.c
new file mode 100644
index 0000000..d1ee12d
--- /dev/null
+++ b/src/utils.c
@@ -0,0 +1,95 @@
+#include
+#include
+#include
+#include
+#include
+#include "utils.h"
+
+char* my_strcasestr(const char *haystack, const char *needle) {
+ if (!haystack || !needle || *needle == '\0') return (char*)haystack;
+ size_t needle_len = strlen(needle);
+ while (*haystack) {
+ if (strncasecmp(haystack, needle, needle_len) == 0) return (char*)haystack;
+ haystack++;
+ }
+ return NULL;
+}
+
+void html_escape(const char *src, char *dst, size_t dst_size) {
+ if (!src || !dst || dst_size == 0) return;
+ char *p = dst;
+ size_t i = 0;
+ while (*src && i < dst_size - 6) {
+ switch (*src) {
+ case '<': strcpy(p, "<"); p += 4; i += 4; break;
+ case '>': strcpy(p, ">"); p += 4; i += 4; break;
+ case '"': strcpy(p, """); p += 6; i += 6; break;
+ case '&': strcpy(p, "&"); p += 5; i += 5; break;
+ default: *p++ = *src; i++; break;
+ }
+ src++;
+ }
+ *p = '\0';
+}
+
+void format_size(long long size, char* buffer) {
+ if (size < 1024) snprintf(buffer, 32, "%lld B", size);
+ else if (size < 1024 * 1024) snprintf(buffer, 32, "%.1f KB", size / 1024.0);
+ else if (size < 1024 * 1024 * 1024) snprintf(buffer, 32, "%.1f MB", size / (1024.0 * 1024.0));
+ else snprintf(buffer, 32, "%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
+}
+
+void format_date(time_t mtime, char* buffer, size_t size) {
+ struct tm *tm_info = localtime(&mtime);
+ strftime(buffer, size, "%d.%m.%Y", tm_info);
+}
+
+void url_encode(const char *src, char *dst, size_t dst_size) {
+ static const char *hex = "0123456789ABCDEF";
+ char *p = dst;
+ size_t i = 0;
+ while (*src && i < dst_size - 3) {
+ unsigned char c = (unsigned char)*src;
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
+ (c >= '0' && c <= '9') || strchr("-_.~", c)) {
+ *p++ = c; i++;
+ } else if (c == ' ') {
+ *p++ = '%'; *p++ = '2'; *p++ = '0'; i += 3;
+ } else {
+ *p++ = '%'; *p++ = hex[(c >> 4) & 0xF]; *p++ = hex[c & 0xF]; i += 3;
+ }
+ src++;
+ }
+ *p = '\0';
+}
+
+void url_decode_enhanced(const char *src, char *dst, size_t dst_size) {
+ char *p = dst;
+ size_t decoded_len = 0;
+ while (*src && decoded_len < dst_size - 1) {
+ if (*src == '%') {
+ if (src[1] && src[2] && isxdigit(src[1]) && isxdigit(src[2])) {
+ char hex[3] = {src[1], src[2], '\0'};
+ unsigned char c = (unsigned char)strtol(hex, NULL, 16);
+ if (c >= 0x80 || c >= 0x20 || c == 0x0A || c == 0x0D) {
+ *p++ = c;
+ decoded_len++;
+ } else {
+ *p++ = '_';
+ decoded_len++;
+ }
+ src += 3;
+ } else {
+ src++;
+ }
+ } else if (*src == '+') {
+ *p++ = ' ';
+ decoded_len++;
+ src++;
+ } else {
+ *p++ = *src++;
+ decoded_len++;
+ }
+ }
+ *p = '\0';
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644
index 0000000..0301302
--- /dev/null
+++ b/src/utils.h
@@ -0,0 +1,13 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#include
+
+void url_encode(const char *src, char *dst, size_t dst_size);
+void url_decode_enhanced(const char *src, char *dst, size_t dst_size);
+void html_escape(const char *src, char *dst, size_t dst_size);
+char* my_strcasestr(const char *haystack, const char *needle);
+void format_size(long long size, char* buffer);
+void format_date(time_t mtime, char* buffer, size_t size);
+
+#endif
diff --git a/src/utils.o b/src/utils.o
new file mode 100644
index 0000000..a65a653
Binary files /dev/null and b/src/utils.o differ
diff --git a/status.cgi b/status.cgi
new file mode 100755
index 0000000..09ddee7
Binary files /dev/null and b/status.cgi differ
diff --git a/style.cgi b/style.cgi
new file mode 100755
index 0000000..833b3ae
Binary files /dev/null and b/style.cgi differ