Files
Trashbox/index.c
2026-07-23 16:19:58 +08:00

809 lines
33 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// /home/romkazvo/www/cgi-bin/index.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <locale.h>
#include <time.h>
// === КОНФИГУРАЦИЯ ===
typedef struct {
char base_path[1024];
char template_path[1024];
char passwd_file[1024];
int max_entries;
char hidden_dirs[10][256];
int hidden_dirs_count;
char hidden_files[10][256];
int hidden_files_count;
char preview_image[512];
char preview_text[512];
char preview_audio[512];
char preview_video[512];
char preview_pdf[512];
} config_t;
config_t g_config;
// Простой парсер 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, ']');
if (end) {
*end = '\0';
strncpy(current_section, p, sizeof(current_section) - 1);
current_section[sizeof(current_section) - 1] = '\0';
}
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) {
if (strcmp(key, "max_entries") == 0) cfg->max_entries = atoi(value);
}
else if (strcmp(current_section, "hidden") == 0) {
if (strcmp(key, "dirs") == 0) {
char *token = strtok(value, ",");
cfg->hidden_dirs_count = 0;
while (token && cfg->hidden_dirs_count < 10) {
while (*token == ' ') token++;
strncpy(cfg->hidden_dirs[cfg->hidden_dirs_count], token, 255);
cfg->hidden_dirs[cfg->hidden_dirs_count][255] = '\0';
cfg->hidden_dirs_count++;
token = strtok(NULL, ",");
}
}
else if (strcmp(key, "files") == 0) {
char *token = strtok(value, ",");
cfg->hidden_files_count = 0;
while (token && cfg->hidden_files_count < 10) {
while (*token == ' ') token++;
strncpy(cfg->hidden_files[cfg->hidden_files_count], token, 255);
cfg->hidden_files[cfg->hidden_files_count][255] = '\0';
cfg->hidden_files_count++;
token = strtok(NULL, ",");
}
}
}
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);
else if (strcmp(key, "video") == 0) strncpy(cfg->preview_video, value, sizeof(cfg->preview_video) - 1);
else if (strcmp(key, "pdf") == 0) strncpy(cfg->preview_pdf, value, sizeof(cfg->preview_pdf) - 1);
}
}
fclose(f);
return 0;
}
void set_default_config(config_t *cfg) {
strcpy(cfg->base_path, "/home/romkazvo/www");
strcpy(cfg->template_path, "/home/romkazvo/www/cgi-bin/template.html");
strcpy(cfg->passwd_file, "/home/romkazvo/www/cgi-bin/.htpasswd");
cfg->max_entries = 1000;
cfg->hidden_dirs_count = 0;
cfg->hidden_files_count = 0;
strcpy(cfg->preview_image, "jpg,jpeg,png,gif,webp,bmp,svg,ico");
strcpy(cfg->preview_text, "txt,md,html,htm,css,js,json,xml,csv");
strcpy(cfg->preview_audio, "mp3,wav,ogg,flac,m4a,aac");
strcpy(cfg->preview_video, "mp4,webm,ogv,mov,avi,mkv");
strcpy(cfg->preview_pdf, "pdf");
}
int is_string_in_list(const char *str, const char *list) {
if (!list || !*list) return 0;
char temp[512];
strncpy(temp, list, sizeof(temp) - 1);
temp[sizeof(temp) - 1] = '\0';
char *token = strtok(temp, ",");
while (token) {
while (*token == ' ') token++;
if (strcasecmp(token, str) == 0) return 1;
token = strtok(NULL, ",");
}
return 0;
}
// === ОСТАЛЬНОЙ КОД (без изменений, только теперь используем g_config вместо #define) ===
typedef struct {
char name[256];
int is_dir;
long size;
char icon[8];
char encoded_path[1024];
char file_url[1024];
int is_protected;
time_t mtime;
int file_count;
long long dir_size;
} entry_t;
typedef struct {
char *data;
size_t size;
size_t capacity;
} buffer_t;
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);
}
int is_safe_path(const char *path) {
if (!path) return 0;
if (strstr(path, "..")) return 0;
if (strstr(path, "./")) return 0;
if (path[0] == '/') return 0;
if (strstr(path, "//")) return 0;
return 1;
}
void safe_path_join(char *result, size_t result_size, const char *base, const char *path) {
if (!is_safe_path(path)) {
snprintf(result, result_size, "%s", base);
return;
}
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;
char *colon = strchr(line, ':');
if (!colon) continue;
*colon = '\0';
char *folder = line;
char *pass = colon + 1;
if (strcmp(folder, folder_name) == 0) {
fclose(f);
return strcmp(pass, password) == 0;
}
}
fclose(f);
return 1;
}
int is_folder_protected(const char *folder_name) {
if (!folder_name || folder_name[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';
if (strcmp(line, folder_name) == 0) {
fclose(f);
return 1;
}
}
fclose(f);
return 0;
}
int count_files_in_dir(const char *path) {
DIR *dir = opendir(path);
if (!dir) return 0;
struct dirent *entry;
int count = 0;
while ((entry = readdir(dir)) != NULL) {
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_files_count; i++) {
if (strcmp(entry->d_name, g_config.hidden_files[i]) == 0) { hidden = 1; break; }
}
if (hidden) continue;
count++;
}
closedir(dir);
return count;
}
long long get_dir_size(const char *path) {
DIR *dir;
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);
if (stat(fullpath, &statbuf) == 0) {
if (S_ISDIR(statbuf.st_mode)) size += get_dir_size(fullpath);
else if (S_ISREG(statbuf.st_mode)) size += statbuf.st_size;
}
}
closedir(dir);
return size;
}
const char* get_file_icon(const char* filename) {
const char *ext = strrchr(filename, '.');
if (!ext) return "📄";
ext++;
if (is_string_in_list(ext, g_config.preview_image)) return "🖼️";
if (is_string_in_list(ext, g_config.preview_audio)) return "🎵";
if (is_string_in_list(ext, g_config.preview_video)) return "🎬";
if (strcasecmp(ext, "zip") == 0 || strcasecmp(ext, "rar") == 0 ||
strcasecmp(ext, "7z") == 0 || strcasecmp(ext, "tar") == 0 ||
strcasecmp(ext, "gz") == 0) return "📦";
if (strcasecmp(ext, "pdf") == 0) return "📕";
if (strcasecmp(ext, "doc") == 0 || strcasecmp(ext, "docx") == 0) return "📘";
if (strcasecmp(ext, "xls") == 0 || strcasecmp(ext, "xlsx") == 0) return "📗";
if (strcasecmp(ext, "txt") == 0) return "📝";
return "📄";
}
int is_previewable(const char* filename) {
const char *ext = strrchr(filename, '.');
if (!ext) return 0;
ext++;
return (is_string_in_list(ext, g_config.preview_image) ||
is_string_in_list(ext, g_config.preview_text) ||
is_string_in_list(ext, g_config.preview_audio) ||
is_string_in_list(ext, g_config.preview_video) ||
is_string_in_list(ext, g_config.preview_pdf));
}
int compare_entries(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;
return strcasecmp(entryA->name, entryB->name);
}
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) { *p++ = c; decoded_len++; }
else if (c >= 0x20 || c == 0x0A || c == 0x0D) { *p++ = c; decoded_len++; }
else { *p++ = '_'; decoded_len++; }
src += 3;
} else { *p++ = *src++; decoded_len++; }
} else if (*src == '+') {
*p++ = ' '; decoded_len++; src++;
} else {
*p++ = *src++; decoded_len++;
}
}
*p = '\0';
}
void print_breadcrumb(buffer_t *buf, const char *display_path) {
buffer_append(buf, "<div class=\"breadcrumb\">\n");
buffer_append(buf, " <a href=\"/cgi-bin/index.cgi\">🏠 Главная</a>");
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 link[4096];
snprintf(link, sizeof(link), "<a href=\"/cgi-bin/index.cgi?path=%s\">%s</a>", encoded, token);
buffer_append(buf, link);
token = strtok(NULL, "/");
}
free(path_copy);
}
buffer_append(buf, "\n</div>\n");
}
void print_password_form(buffer_t *buf, const char *folder_name) {
buffer_append(buf, "<div class=\"password-form-container\">\n");
buffer_append(buf, " <div class=\"password-form\">\n");
buffer_append(buf, " <div class=\"lock-icon\">🔒</div>\n");
buffer_append(buf, " <h2>Папка защищена паролем</h2>\n");
buffer_append(buf, " <p>Введите пароль для доступа к папке <strong>");
buffer_append(buf, folder_name);
buffer_append(buf, "</strong></p>\n");
buffer_append(buf, " <form method=\"GET\" action=\"/cgi-bin/index.cgi\">\n");
buffer_append(buf, " <input type=\"hidden\" name=\"path\" value=\"");
char encoded_path[1024];
url_encode(folder_name, encoded_path, sizeof(encoded_path));
buffer_append(buf, encoded_path);
buffer_append(buf, "\">\n");
buffer_append(buf, " <input type=\"password\" name=\"password\" placeholder=\"Введите пароль\" required>\n");
buffer_append(buf, " <button type=\"submit\">Войти</button>\n");
buffer_append(buf, " </form>\n");
buffer_append(buf, " <p class=\"error-msg\">");
char *query_string = getenv("QUERY_STRING");
if (query_string && strstr(query_string, "error=1")) {
buffer_append(buf, "❌ Неверный пароль! Попробуйте снова.");
}
buffer_append(buf, "</p>\n");
buffer_append(buf, " <a href=\"/cgi-bin/index.cgi\" class=\"back-link\">← Вернуться на главную</a>\n");
buffer_append(buf, " </div>\n");
buffer_append(buf, "</div>\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, "<div class=\"error\">Ошибка выделения памяти</div>");
return;
}
int entry_count = 0;
int dir_count = 0, file_count = 0;
long long total_size = 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 ? pass_end - pass_start : 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;
}
}
}
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, "<div class=\"empty-state\">\n");
buffer_append(buf, " <div class=\"icon\">📁</div>\n");
buffer_append(buf, " <h3>Ошибка открытия директории</h3>\n");
char error_msg[512];
snprintf(error_msg, sizeof(error_msg), " <p>Путь: %s</p>\n", base_path);
buffer_append(buf, error_msg);
snprintf(error_msg, sizeof(error_msg), " <p>Ошибка: %s</p>\n", strerror(errno));
buffer_append(buf, error_msg);
buffer_append(buf, " <p><a href=\"/cgi-bin/index.cgi\">← Вернуться на главную</a></p>\n");
buffer_append(buf, "</div>\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;
snprintf(entries[entry_count].file_url, sizeof(entries[0].file_url), "%s%s%s", display_path, display_path[0] ? "/" : "", entries[entry_count].name);
total_size += file_stat.st_size;
file_count++;
} else {
continue;
}
entry_count++;
}
}
closedir(dir);
if (entry_count == 0) {
buffer_append(buf, "<div class=\"empty-state\">\n");
buffer_append(buf, " <div class=\"icon\">📄</div>\n");
buffer_append(buf, " <h3>Здесь пусто</h3>\n");
buffer_append(buf, " <p>В этой директории нет файлов или папок</p>\n");
buffer_append(buf, "</div>\n");
free(entries);
return;
}
qsort(entries, entry_count, sizeof(entry_t), compare_entries);
buffer_append(buf, "<ul>\n");
if (dir_count > 0) {
char section_title[128];
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📁 Папки (%d)</li>\n", dir_count);
buffer_append(buf, section_title);
for (int i = 0; i < entry_count; i++) {
if (entries[i].is_dir) {
buffer_append(buf, " <li>\n");
if (entries[i].is_protected) {
buffer_append(buf, " <div class=\"file-row\">\n");
buffer_append(buf, " <a class=\"dir-link protected-folder\" href=\"javascript:void(0)\" onclick=\"showPasswordForm('");
buffer_append(buf, entries[i].encoded_path);
buffer_append(buf, "', '");
buffer_append(buf, entries[i].name);
buffer_append(buf, "')\">\n");
buffer_append(buf, " <span class=\"file-icon\">🔒</span>\n");
buffer_append(buf, " <span class=\"file-name\">");
buffer_append(buf, entries[i].name);
buffer_append(buf, " <span style=\"font-size:0.7rem;color:var(--text-muted);\">(защищена)</span></span>\n");
buffer_append(buf, " </a>\n");
buffer_append(buf, " <div class=\"file-controls\">\n");
buffer_append(buf, " <button class=\"preview-btn\" onclick=\"showPasswordForm('");
buffer_append(buf, entries[i].encoded_path);
buffer_append(buf, "', '");
buffer_append(buf, entries[i].name);
buffer_append(buf, "')\" title=\"Ввести пароль\">🔑</button>\n");
buffer_append(buf, " </div>\n");
buffer_append(buf, " </div>\n");
} else {
buffer_append(buf, " <div class=\"file-row\">\n");
char dir_link[512];
snprintf(dir_link, sizeof(dir_link),
" <a class=\"dir-link\" href=\"/cgi-bin/index.cgi?path=%s\">\n",
entries[i].encoded_path);
buffer_append(buf, dir_link);
buffer_append(buf, " <span class=\"file-icon\">");
buffer_append(buf, entries[i].icon);
buffer_append(buf, "</span>\n");
buffer_append(buf, " <span class=\"file-name\">");
buffer_append(buf, entries[i].name);
if (entries[i].file_count > 0) {
char count_str[32];
snprintf(count_str, sizeof(count_str), " (%d)", entries[i].file_count);
buffer_append(buf, count_str);
}
buffer_append(buf, "</span>\n");
buffer_append(buf, " </a>\n");
buffer_append(buf, " <div class=\"file-controls\">\n");
buffer_append(buf, " <button class=\"copy-btn\" onclick=\"copyLink('/cgi-bin/index.cgi?path=");
buffer_append(buf, entries[i].encoded_path);
buffer_append(buf, "')\" title=\"Копировать ссылку на папку\">🔗</button>\n");
buffer_append(buf, " </div>\n");
buffer_append(buf, " </div>\n");
}
buffer_append(buf, " </li>\n");
}
}
}
if (file_count > 0) {
char section_title[128];
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📄 Файлы (%d)</li>\n", 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, " <li>\n");
buffer_append(buf, " <div class=\"file-row\">\n");
buffer_append(buf, " <a class=\"file-link\" href=\"/");
buffer_append(buf, entries[i].file_url);
buffer_append(buf, "\" download>\n");
buffer_append(buf, " <span class=\"file-icon\">");
buffer_append(buf, entries[i].icon);
buffer_append(buf, "</span>\n");
buffer_append(buf, " <span class=\"file-name\">");
buffer_append(buf, entries[i].name);
buffer_append(buf, "</span>\n");
buffer_append(buf, " </a>\n");
buffer_append(buf, " <div class=\"file-controls\">\n");
if (is_previewable(entries[i].name)) {
buffer_append(buf, " <button class=\"preview-btn\" onclick=\"openPreview('/");
buffer_append(buf, entries[i].file_url);
buffer_append(buf, "')\" title=\"Открыть в браузере\">👁</button>\n");
}
buffer_append(buf, " <button class=\"copy-btn\" onclick=\"copyLink('/");
buffer_append(buf, entries[i].file_url);
buffer_append(buf, "')\" title=\"Копировать ссылку на файл\">🔗</button>\n");
buffer_append(buf, " <span class=\"file-meta date\">");
buffer_append(buf, date_str);
buffer_append(buf, "</span>\n");
buffer_append(buf, " <span class=\"file-meta size\">");
buffer_append(buf, size_str);
buffer_append(buf, "</span>\n");
buffer_append(buf, " </div>\n");
buffer_append(buf, " </div>\n");
buffer_append(buf, " </li>\n");
}
}
}
buffer_append(buf, "</ul>\n");
char total_size_str[32];
format_size(total_size, total_size_str);
char stats[256];
snprintf(stats, sizeof(stats),
"<div class=\"stats\">\n Папки: %d | Файлы: %d | Общий размер: %s\n</div>\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, "<!--BREADCRUMB-->")) {
print_breadcrumb(&output, display_path);
} else if (strstr(line, "<!--CONTENT-->")) {
print_content(&output, base_path, display_path);
} else {
buffer_append(&output, line);
}
}
fclose(file);
fwrite(output.data, 1, output.size, stdout);
buffer_free(&output);
}
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);
}
printf("Content-type: text/html; charset=utf-8\n\n");
char base_path[1024];
strcpy(base_path, g_config.base_path);
char display_path[1024] = "";
char safe_display_path[1024] = "";
char *query_string = getenv("QUERY_STRING");
if (query_string) {
char *path_start = strstr(query_string, "path=");
if (path_start) {
path_start += 5;
char *path_end = strchr(path_start, '&');
int path_len = path_end ? path_end - path_start : strlen(path_start);
if (path_len > 0 && path_len < sizeof(display_path) - 1) {
char encoded_path[1024];
strncpy(encoded_path, path_start, path_len);
encoded_path[path_len] = '\0';
url_decode_enhanced(encoded_path, display_path, sizeof(display_path));
if (!is_safe_path(display_path)) {
display_path[0] = '\0';
strcpy(base_path, g_config.base_path);
} else {
strncpy(safe_display_path, display_path, sizeof(safe_display_path) - 1);
safe_display_path[sizeof(safe_display_path) - 1] = '\0';
safe_path_join(base_path, sizeof(base_path), g_config.base_path, safe_display_path);
}
}
}
if (strstr(query_string, "password=")) {
char *error = strstr(query_string, "error=1");
if (!error) {
char folder_name[256] = "";
if (display_path[0]) {
char *last_slash = strrchr(display_path, '/');
if (last_slash) strcpy(folder_name, last_slash + 1);
else strcpy(folder_name, display_path);
if (is_folder_protected(folder_name)) {
char *pass_start = strstr(query_string, "password=");
if (pass_start) {
pass_start += 9;
char *pass_end = strchr(pass_start, '&');
int pass_len = pass_end ? pass_end - pass_start : 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));
if (decoded_pass[0] == '\0') {
char encoded_path[1024];
url_encode(display_path, encoded_path, sizeof(encoded_path));
printf("Location: /cgi-bin/index.cgi?path=%s&error=1\n\n", encoded_path);
return 0;
}
if (!check_folder_password(folder_name, decoded_pass)) {
char encoded_path[1024];
url_encode(display_path, encoded_path, sizeof(encoded_path));
printf("Location: /cgi-bin/index.cgi?path=%s&error=1\n\n", encoded_path);
return 0;
}
} else {
char encoded_path[1024];
url_encode(display_path, encoded_path, sizeof(encoded_path));
printf("Location: /cgi-bin/index.cgi?path=%s&error=1\n\n", encoded_path);
return 0;
}
}
}
}
}
}
}
print_template(base_path, display_path);
return 0;
}