From 349ea7e91e7113842d9cbca10fea9dbcffcdb9b8 Mon Sep 17 00:00:00 2001 From: RomkaZVO Date: Fri, 24 Jul 2026 02:02:37 +0800 Subject: [PATCH] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 23 ++++++ index.c | 202 +++++++++++--------------------------------------- status.c | 1 - style.c | 5 +- style.css | 3 +- template.html | 4 +- 6 files changed, 75 insertions(+), 163 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8021b18 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +# Makefile для Trashbox CGI +CC = gcc +CFLAGS = -Wall -Wextra -O2 +TARGETS = index.cgi style.cgi status.cgi + +all: $(TARGETS) + +index.cgi: index.c + $(CC) $(CFLAGS) -o $@ $< + +style.cgi: style.c + $(CC) $(CFLAGS) -o $@ $< + +status.cgi: status.c + $(CC) $(CFLAGS) -o $@ $< + +clean: + rm -f $(TARGETS) + +install: all + chmod +x $(TARGETS) + +.PHONY: all clean install diff --git a/index.c b/index.c index 7578505..529f996 100644 --- a/index.c +++ b/index.c @@ -11,15 +11,14 @@ #include #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 format_size(long long size, char* buffer); void format_date(time_t mtime, char* buffer, size_t size); void html_escape(const char *src, char *dst, size_t dst_size); -// === КОНФИГУРАЦИЯ === - +// === КОНФИГ === typedef struct { char base_path[1024]; char template_path[1024]; @@ -38,34 +37,27 @@ typedef struct { config_t g_config; -// === ПАРАМЕТРЫ ЗАПРОСА (сортировка, поиск) === +// === ПАРАМЕТРЫ === typedef struct { - char sort_by[16]; // "name", "size", "date" - int sort_order; // 1 = asc, -1 = desc + char sort_by[16]; + int sort_order; char search[256]; - int recursive; // 1 = рекурсивный поиск, 0 = только текущая папка + int recursive; } query_params_t; query_params_t g_params; -// === ПОИСК БЕЗ УЧЁТА РЕГИСТРА (поддерживает UTF-8) === +// === УТИЛИТЫ === char* my_strcasestr(const char *haystack, const char *needle) { if (!haystack || !needle || *needle == '\0') return (char*)haystack; - - const char *h = haystack; - const char *n = needle; size_t needle_len = strlen(needle); - - while (*h) { - if (strncasecmp(h, n, needle_len) == 0) { - return (char*)h; - } - h++; + while (*haystack) { + if (strncasecmp(haystack, needle, needle_len) == 0) return (char*)haystack; + haystack++; } return NULL; } -// === HTML ЭКРАНИРОВАНИЕ (защита от XSS) === void html_escape(const char *src, char *dst, size_t dst_size) { if (!src || !dst || dst_size == 0) return; char *p = dst; @@ -83,19 +75,16 @@ void html_escape(const char *src, char *dst, size_t dst_size) { *p = '\0'; } -// Простой парсер INI (без зависимостей) +// === ПАРСЕР INI === int load_config(const char *path, config_t *cfg) { FILE *f = fopen(path, "r"); if (!f) return -1; - char line[512]; char current_section[64] = ""; - while (fgets(line, sizeof(line), f)) { char *p = line; while (*p == ' ' || *p == '\t') p++; if (*p == '\0' || *p == '#' || *p == ';') continue; - if (*p == '[') { p++; char *end = strchr(p, ']'); @@ -106,36 +95,29 @@ int load_config(const char *path, config_t *cfg) { } continue; } - char *eq = strchr(p, '='); if (!eq) continue; - *eq = '\0'; char *key = p; char *value = eq + 1; - char *end_key = key + strlen(key) - 1; while (end_key > key && (*end_key == ' ' || *end_key == '\t')) { *end_key = '\0'; end_key--; } - while (*value == ' ' || *value == '\t') value++; char *end_val = value + strlen(value) - 1; while (end_val > value && (*end_val == ' ' || *end_val == '\t' || *end_val == '\r' || *end_val == '\n')) { *end_val = '\0'; end_val--; } - if (strcmp(current_section, "paths") == 0) { if (strcmp(key, "base_path") == 0) strncpy(cfg->base_path, value, sizeof(cfg->base_path) - 1); else if (strcmp(key, "template_path") == 0) strncpy(cfg->template_path, value, sizeof(cfg->template_path) - 1); else if (strcmp(key, "passwd_file") == 0) strncpy(cfg->passwd_file, value, sizeof(cfg->passwd_file) - 1); - } - else if (strcmp(current_section, "limits") == 0) { + } else if (strcmp(current_section, "limits") == 0) { if (strcmp(key, "max_entries") == 0) cfg->max_entries = atoi(value); - } - else if (strcmp(current_section, "hidden") == 0) { + } else if (strcmp(current_section, "hidden") == 0) { if (strcmp(key, "dirs") == 0) { char *token = strtok(value, ","); cfg->hidden_dirs_count = 0; @@ -146,8 +128,7 @@ int load_config(const char *path, config_t *cfg) { cfg->hidden_dirs_count++; token = strtok(NULL, ","); } - } - else if (strcmp(key, "files") == 0) { + } else if (strcmp(key, "files") == 0) { char *token = strtok(value, ","); cfg->hidden_files_count = 0; while (token && cfg->hidden_files_count < 10) { @@ -158,8 +139,7 @@ int load_config(const char *path, config_t *cfg) { token = strtok(NULL, ","); } } - } - else if (strcmp(current_section, "preview") == 0) { + } else if (strcmp(current_section, "preview") == 0) { if (strcmp(key, "image") == 0) strncpy(cfg->preview_image, value, sizeof(cfg->preview_image) - 1); else if (strcmp(key, "text") == 0) strncpy(cfg->preview_text, value, sizeof(cfg->preview_text) - 1); else if (strcmp(key, "audio") == 0) strncpy(cfg->preview_audio, value, sizeof(cfg->preview_audio) - 1); @@ -167,7 +147,6 @@ int load_config(const char *path, config_t *cfg) { else if (strcmp(key, "pdf") == 0) strncpy(cfg->preview_pdf, value, sizeof(cfg->preview_pdf) - 1); } } - fclose(f); return 0; } @@ -200,8 +179,7 @@ int is_string_in_list(const char *str, const char *list) { return 0; } -// === ОСТАЛЬНОЙ КОД === - +// === ОСНОВНЫЕ СТРУКТУРЫ === typedef struct { char name[256]; int is_dir; @@ -237,10 +215,9 @@ void buffer_append(buffer_t *buf, const char *str) { buf->size += len; } -void buffer_free(buffer_t *buf) { - free(buf->data); -} +void buffer_free(buffer_t *buf) { free(buf->data); } +// === БЕЗОПАСНОСТЬ === int is_safe_path(const char *path) { if (!path) return 0; if (strstr(path, "..")) return 0; @@ -258,12 +235,11 @@ void safe_path_join(char *result, size_t result_size, const char *base, const ch snprintf(result, result_size, "%s/%s", base, path); } +// === ПАРОЛИ === int check_folder_password(const char *folder_name, const char *password) { if (!password || !folder_name || password[0] == '\0') return 0; - FILE *f = fopen(g_config.passwd_file, "r"); if (!f) return 1; - char line[512]; while (fgets(line, sizeof(line), f)) { line[strcspn(line, "\r\n")] = 0; @@ -272,7 +248,6 @@ int check_folder_password(const char *folder_name, const char *password) { *colon = '\0'; char *folder = line; char *pass = colon + 1; - // Проверяем как полный путь, так и имя папки const char *last_slash = strrchr(folder_name, '/'); const char *base_name = last_slash ? last_slash + 1 : folder_name; if (strcmp(folder, folder_name) == 0 || strcmp(folder, base_name) == 0) { @@ -286,20 +261,16 @@ int check_folder_password(const char *folder_name, const char *password) { int is_folder_protected(const char *folder_path) { if (!folder_path || folder_path[0] == '\0') return 0; - FILE *f = fopen(g_config.passwd_file, "r"); if (!f) return 0; - char line[512]; while (fgets(line, sizeof(line), f)) { line[strcspn(line, "\r\n")] = 0; char *colon = strchr(line, ':'); if (!colon) continue; *colon = '\0'; - // Получаем имя папки (последний сегмент пути) const char *last_slash = strrchr(folder_path, '/'); const char *folder_name = last_slash ? last_slash + 1 : folder_path; - // Проверяем как полный путь, так и имя папки if (strcmp(line, folder_path) == 0 || strcmp(line, folder_name) == 0) { fclose(f); return 1; @@ -309,6 +280,7 @@ int is_folder_protected(const char *folder_path) { return 0; } +// === ФС === int count_files_in_dir(const char *path) { DIR *dir = opendir(path); if (!dir) return 0; @@ -329,13 +301,12 @@ int count_files_in_dir(const char *path) { } long long get_dir_size(const char *path) { - DIR *dir; + DIR *dir = opendir(path); + if (!dir) return 0; struct dirent *entry; struct stat statbuf; char fullpath[1024]; long long size = 0; - dir = opendir(path); - if (!dir) return 0; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name); @@ -376,21 +347,17 @@ int is_previewable(const char* filename) { is_string_in_list(ext, g_config.preview_pdf)); } -// === РЕКУРСИВНЫЙ ПОИСК (игнорирует защищённые папки) === +// === РЕКУРСИВНЫЙ ПОИСК === void search_recursive(const char *base_path, const char *display_path, const char *search_term, entry_t *results, int *count, int max_results) { DIR *dir = opendir(base_path); if (!dir) return; - struct dirent *entry; struct stat file_stat; char full_path[1024]; char display_subpath[1024]; - while ((entry = readdir(dir)) != NULL && *count < max_results) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; if (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; } @@ -399,25 +366,20 @@ void search_recursive(const char *base_path, const char *display_path, const cha if (strcmp(entry->d_name, g_config.hidden_files[i]) == 0) { hidden = 1; break; } } if (hidden) continue; - snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name); if (display_path[0] == '\0') { snprintf(display_subpath, sizeof(display_subpath), "%s", entry->d_name); } else { snprintf(display_subpath, sizeof(display_subpath), "%s/%s", display_path, entry->d_name); } - - // Если папка защищена — не заходим и не показываем if (is_folder_protected(display_subpath) || is_folder_protected(entry->d_name)) { continue; } - if (stat(full_path, &file_stat) == 0) { if (my_strcasestr(entry->d_name, search_term) != NULL) { entry_t *result = &results[*count]; strncpy(result->name, entry->d_name, sizeof(result->name) - 1); result->name[sizeof(result->name) - 1] = '\0'; - if (S_ISDIR(file_stat.st_mode)) { result->is_dir = 1; result->size = 0; @@ -426,7 +388,6 @@ void search_recursive(const char *base_path, const char *display_path, const cha result->mtime = file_stat.st_mtime; strcpy(result->icon, "📁"); result->is_protected = 0; - url_encode(display_subpath, result->encoded_path, sizeof(result->encoded_path)); strncpy(result->file_url, display_subpath, sizeof(result->file_url) - 1); (*count)++; @@ -438,13 +399,11 @@ void search_recursive(const char *base_path, const char *display_path, const cha result->mtime = file_stat.st_mtime; strcpy(result->icon, get_file_icon(entry->d_name)); result->is_protected = 0; - url_encode(display_subpath, result->encoded_path, sizeof(result->encoded_path)); strncpy(result->file_url, display_subpath, sizeof(result->file_url) - 1); (*count)++; } } - if (S_ISDIR(file_stat.st_mode)) { search_recursive(full_path, display_subpath, search_term, results, count, max_results); } @@ -454,15 +413,11 @@ void search_recursive(const char *base_path, const char *display_path, const cha } // === СОРТИРОВКА === - int compare_entries_sorted(const void *a, const void *b) { const entry_t *entryA = (const entry_t *)a; const entry_t *entryB = (const entry_t *)b; - - // Папки всегда сверху if (entryA->is_dir && !entryB->is_dir) return -1; if (!entryA->is_dir && entryB->is_dir) return 1; - int result = 0; if (strcmp(g_params.sort_by, "size") == 0) { long long sizeA = entryA->is_dir ? entryA->dir_size : entryA->size; @@ -473,10 +428,10 @@ int compare_entries_sorted(const void *a, const void *b) { } else { result = strcasecmp(entryA->name, entryB->name); } - return result * g_params.sort_order; } +// === ФОРМАТИРОВАНИЕ === 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); @@ -513,7 +468,6 @@ void url_decode_enhanced(const char *src, char *dst, size_t dst_size) { size_t decoded_len = 0; while (*src && decoded_len < dst_size - 1) { if (*src == '%') { - // Проверяем, что есть ещё минимум 2 символа 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); @@ -526,7 +480,6 @@ void url_decode_enhanced(const char *src, char *dst, size_t dst_size) { } src += 3; } else { - // Невалидный % — просто пропускаем src++; } } else if (*src == '+') { @@ -541,6 +494,7 @@ void url_decode_enhanced(const char *src, char *dst, size_t dst_size) { *p = '\0'; } +// === ОТРИСОВКА === void print_breadcrumb(buffer_t *buf, const char *display_path) { buffer_append(buf, "
\n"); buffer_append(buf, " 🏠 Главная"); @@ -554,9 +508,9 @@ void print_breadcrumb(buffer_t *buf, const char *display_path) { if (temp_path[0]) strcat(temp_path, "/"); strcat(temp_path, token); url_encode(temp_path, encoded, sizeof(encoded)); - char link[4096]; 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, "/"); @@ -601,24 +555,19 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat 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; - int dir_count = 0, file_count = 0; + 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) { @@ -637,7 +586,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat } } } - if (is_folder_protected(folder_name)) { if (!password || !check_folder_password(folder_name, password)) { print_password_form(buf, folder_name); @@ -646,12 +594,9 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat } } } - - // === ЕСЛИ ЕСТЬ ПОИСК — ИСПОЛЬЗУЕМ РЕКУРСИВНЫЙ ОБХОД === 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"); @@ -665,10 +610,8 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat 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++; @@ -679,7 +622,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat } } } else { - // === ОБЫЧНЫЙ ОБХОД ТЕКУЩЕЙ ПАПКИ === dir = opendir(base_path); if (!dir) { char alt_path[1024]; @@ -705,10 +647,8 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat 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; } @@ -718,12 +658,10 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat } 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; @@ -731,7 +669,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat 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; @@ -739,7 +676,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat 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)); @@ -752,11 +688,9 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat 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 { @@ -766,7 +700,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat } } closedir(dir); - if (entry_count == 0) { buffer_append(buf, "
\n"); buffer_append(buf, "
📄
\n"); @@ -777,15 +710,11 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat 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"); buffer_append(buf, "
\n"); buffer_append(buf, "
\n"); @@ -817,8 +746,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat buffer_append(buf, "\">📅 Дата\n"); buffer_append(buf, "
\n"); buffer_append(buf, "
\n"); - - // === ВЫВОД РЕЗУЛЬТАТОВ === if (is_recursive_search) { buffer_append(buf, "
"); buffer_append(buf, "🔍 Результаты поиска по всему сайту: "); @@ -827,24 +754,16 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat buffer_append(buf, tmp); buffer_append(buf, " файлов/папок найдено
\n"); } - buffer_append(buf, "\n"); - - // Статистика if (is_recursive_search) { char stats[256]; - snprintf(stats, sizeof(stats), - "
\n Найдено: папок %d | файлов %d\n
\n", + 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", + snprintf(stats, sizeof(stats), + "
\n Папки: %d | Файлы: %d | Общий размер: %s\n
\n", dir_count, file_count, total_size_str); buffer_append(buf, stats); } - free(entries); } @@ -1024,10 +930,8 @@ void print_template(const char *base_path, const char *display_path) { 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, "")) { @@ -1039,7 +943,6 @@ void print_template(const char *base_path, const char *display_path) { } } fclose(file); - fwrite(output.data, 1, output.size, stdout); buffer_free(&output); } @@ -1047,26 +950,19 @@ void print_template(const char *base_path, const char *display_path) { int main() { setlocale(LC_ALL, "en_US.UTF-8"); setlocale(LC_CTYPE, "en_US.UTF-8"); - - // Загружаем конфиг if (load_config("/home/romkazvo/www/cgi-bin/config.ini", &g_config) != 0) { set_default_config(&g_config); } - - // === ПАРСИМ ПАРАМЕТРЫ ЗАПРОСА === strcpy(g_params.sort_by, "name"); g_params.sort_order = 1; g_params.search[0] = '\0'; g_params.recursive = 0; - char *query_string = getenv("QUERY_STRING"); char display_path[1024] = ""; char safe_display_path[1024] = ""; char base_path[1024]; strcpy(base_path, g_config.base_path); - if (query_string) { - // === ПАРСИМ PATH === char *path_start = strstr(query_string, "path="); if (path_start) { path_start += 5; @@ -1087,8 +983,6 @@ int main() { } } } - - // === ПАРСИМ SORT === char *sort_start = strstr(query_string, "sort="); if (sort_start) { sort_start += 5; @@ -1099,8 +993,6 @@ int main() { g_params.sort_by[sort_len] = '\0'; } } - - // === ПАРСИМ SEARCH (отдельно от path!) === char *search_start = strstr(query_string, "search="); if (search_start) { search_start += 7; @@ -1116,8 +1008,6 @@ int main() { } } } - - // === ОБРАБОТКА ПАРОЛЯ === if (strstr(query_string, "password=")) { char *error = strstr(query_string, "error=1"); if (!error) { @@ -1165,9 +1055,7 @@ int main() { } } } - printf("Content-type: text/html; charset=utf-8\n\n"); - print_template(base_path, display_path); return 0; } diff --git a/status.c b/status.c index b9bf4f1..8cfd9a9 100755 --- a/status.c +++ b/status.c @@ -1,5 +1,4 @@ // /home/romkazvo/www/cgi-bin/status.c -// Отрисовка статистики #include #include #include diff --git a/style.c b/style.c index 71db6d7..313b3ab 100755 --- a/style.c +++ b/style.c @@ -1,10 +1,10 @@ -// Подгрузка CSS #include #include #include #include int main() { + // Проверяем, существует ли файл стилей char css_path[1024] = "/home/romkazvo/www/cgi-bin/style.css"; struct stat st; @@ -15,8 +15,9 @@ int main() { return 1; } + // Отдаем CSS с правильными заголовками printf("Content-type: text/css\n"); - printf("Cache-Control: public, max-age=3600\n\n"); + printf("Cache-Control: public, max-age=3600\n\n"); // Кэшируем на 1 час FILE *css_file = fopen(css_path, "r"); if (!css_file) { diff --git a/style.css b/style.css index 29a4367..73aac06 100755 --- a/style.css +++ b/style.css @@ -227,7 +227,8 @@ li:hover { .file-meta { color: var(--text-muted); - font-size: 0.75rem; + font-size: 0.85rem; + font-weight: 600; white-space: nowrap; text-align: right; font-variant-numeric: tabular-nums; diff --git a/template.html b/template.html index c067eb2..61f607b 100644 --- a/template.html +++ b/template.html @@ -116,8 +116,8 @@