diff --git a/.htpasswd b/.htpasswd deleted file mode 100644 index 0096a88..0000000 --- a/.htpasswd +++ /dev/null @@ -1 +0,0 @@ -private:testpass diff --git a/403.html b/403.html deleted file mode 100644 index 1ef3cf5..0000000 --- a/403.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - Trashbox - 403 Доступ запрещён - - - - -
-
403
-

Доступ запрещён

-

Эй, дружок-пирожок, сюда нельзя

- Вернуться на главную -
- - - diff --git a/404.html b/404.html deleted file mode 100644 index dabe65f..0000000 --- a/404.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - Trashbox - 404 Страница не найдена - - - - -
-
404
-

Страница не найдена

-

Эй, дружок-пирожок, тобой выбрана неправильная дверь

- Вернуться на главную -
- - diff --git a/Makefile b/Makefile deleted file mode 100644 index e22f56c..0000000 --- a/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -CC = gcc -CFLAGS = -Wall -Wextra -O2 -I./src -TARGETS = index.cgi style.cgi status.cgi - -SRCS_INDEX = src/main.c src/config.c src/auth.c src/fs.c src/render.c src/utils.c -OBJS_INDEX = $(SRCS_INDEX:.c=.o) - -all: $(TARGETS) - -index.cgi: $(OBJS_INDEX) - $(CC) $(CFLAGS) -o $@ $^ - -style.cgi: src/style.c - $(CC) $(CFLAGS) -o $@ $< - -status.cgi: src/status.c - $(CC) $(CFLAGS) -o $@ $< - -clean: - rm -f $(OBJS_INDEX) $(TARGETS) - -install: all - chmod +x $(TARGETS) - -.PHONY: all clean install diff --git a/busybox_nginx_reverse.txt b/busybox_nginx_reverse.txt deleted file mode 100644 index bc0cc00..0000000 --- a/busybox_nginx_reverse.txt +++ /dev/null @@ -1,86 +0,0 @@ -server { - listen 443 ssl; - http2 on; - server_name home.mashup.su www.home.mashup.su; - ssl_certificate /etc/letsencrypt/live/home.mashup.su/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/home.mashup.su/privkey.pem; - - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; - - root /home/romkazvo/www; - - # === КАСТОМНЫЕ СТРАНИЦЫ ОШИБОК === - error_page 404 =404 /404.html; - location = /404.html { - root /home/romkazvo/www; - allow all; - } - - error_page 403 =403 /403.html; - location = /403.html { - root /home/romkazvo/www; - allow all; - } - - # === СТАТИКА === - location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|webp)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - try_files $uri =404; - } - - # === СТРИМ /gachi === - location = /gachi { - proxy_pass http://127.0.0.1:8001/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - proxy_cache off; - proxy_read_timeout 3600; - proxy_send_timeout 3600; - add_header X-Stream-Name "Gachi Stream" always; - add_header Access-Control-Allow-Origin "*" always; - add_header Access-Control-Allow-Methods "GET, OPTIONS, HEAD" always; - add_header Access-Control-Allow-Headers "Range, Accept-Encoding" always; - } - - # === CGI (с перехватом ошибок) === - location /cgi-bin/ { - proxy_pass http://127.0.0.1:8050; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_http_version 1.1; - proxy_buffering off; - proxy_cache off; - proxy_read_timeout 300s; - proxy_send_timeout 300s; - proxy_intercept_errors on; - } - - # === ВСЁ ОСТАЛЬНОЕ === - location / { - proxy_pass http://127.0.0.1:8050; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_http_version 1.1; - proxy_buffering off; - proxy_cache off; - proxy_read_timeout 300s; - proxy_send_timeout 300s; - proxy_intercept_errors on; - } -} - -# HTTP → HTTPS -server { - listen 80; - server_name home.mashup.su www.home.mashup.su; - return 301 https://$server_name$request_uri; -} diff --git a/config.ini b/config.ini deleted file mode 100755 index b1086b2..0000000 --- a/config.ini +++ /dev/null @@ -1,30 +0,0 @@ -# Trashbox CGI config - -[paths] -base_path = /home/romkazvo/www -template_path = /home/romkazvo/www/cgi-bin/template.html -passwd_file = /home/romkazvo/www/cgi-bin/.htpasswd - -[limits] -max_entries = 1000 - -[hidden] -dirs = cgi-bin,.git,.svn -files = .htpasswd,.htaccess,.gitignore, robots.txt, 404.html, 403.html - -[preview] -image = jpg,jpeg,png,gif,webp,bmp,svg,ico -text = txt,md,html,htm,css,js,json,xml,csv -audio = mp3,wav,ogg,flac,m4a,aac -video = mp4,webm,ogv,mov,avi,mkv -pdf = pdf - -[security] -cookie_name = folder_auth -cookie_lifetime = 3600 -max_attempts = 5 -block_time = 900 - -[logging] -enable = 1 -log_file = /home/romkazvo/www/cgi-bin/logs/access.log diff --git a/src/auth.c b/src/auth.c deleted file mode 100644 index eba8a51..0000000 --- a/src/auth.c +++ /dev/null @@ -1,261 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "auth.h" -#include "config.h" -#include "utils.h" - -#define TOKEN_DIR "/tmp/trashbox_tokens" -#define TOKEN_SALT "TrashBoxSecretSalt2024" - -// ============================================ -// ПРОВЕРКА ПАРОЛЯ -// ============================================ - -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) { - fprintf(stderr, "DEBUG: cannot open passwd file: %s\n", g_config.passwd_file); - 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'; - 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) { - fclose(f); - return strcmp(pass, password) == 0; - } - } - fclose(f); - return 0; -} - -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; - } - } - fclose(f); - return 0; -} - -// ============================================ -// ЛИМИТ ПОПЫТОК -// ============================================ - -void get_attempts_file(char *buf, size_t size, const char *folder_name, const char *ip) { - char safe_folder[256]; - strncpy(safe_folder, folder_name, sizeof(safe_folder) - 1); - safe_folder[sizeof(safe_folder) - 1] = '\0'; - for (char *p = safe_folder; *p; p++) { - if (*p == '/' || *p == '\\' || *p == '.') *p = '_'; - } - snprintf(buf, size, "/tmp/trashbox_attempts_%s_%s", safe_folder, ip); -} - -int check_attempts(const char *folder_name, const char *ip) { - if (!folder_name || !ip) return 0; - - char attempts_file[256]; - get_attempts_file(attempts_file, sizeof(attempts_file), folder_name, ip); - - FILE *f = fopen(attempts_file, "r"); - if (!f) return 0; - - int attempts; - time_t first_attempt_time; - - if (fscanf(f, "%d %ld", &attempts, &first_attempt_time) != 2) { - fclose(f); - return 0; - } - fclose(f); - - time_t now = time(NULL); - if (now - first_attempt_time > g_config.block_time) { - unlink(attempts_file); - return 0; - } - - return attempts >= g_config.max_attempts; -} - -void add_attempt(const char *folder_name, const char *ip) { - if (!folder_name || !ip) return; - - char attempts_file[256]; - get_attempts_file(attempts_file, sizeof(attempts_file), folder_name, ip); - - int attempts = 0; - time_t first_attempt_time = time(NULL); - - FILE *f = fopen(attempts_file, "r"); - if (f) { - fscanf(f, "%d %ld", &attempts, &first_attempt_time); - fclose(f); - attempts++; - } else { - attempts = 1; - } - - f = fopen(attempts_file, "w"); - if (f) { - fprintf(f, "%d %ld\n", attempts, first_attempt_time); - fclose(f); - } -} - -void clear_attempts(const char *folder_name, const char *ip) { - if (!folder_name || !ip) return; - - char attempts_file[256]; - get_attempts_file(attempts_file, sizeof(attempts_file), folder_name, ip); - unlink(attempts_file); -} - -// ============================================ -// ТОКЕНЫ -// ============================================ - -char* generate_token(const char *path) { - // ============================================ - // ФИНАЛЬНЫЙ ФИКС: очищаем путь от любых нежелательных символов - // ============================================ - char clean_path[1024]; - strncpy(clean_path, path, sizeof(clean_path) - 1); - clean_path[sizeof(clean_path) - 1] = '\0'; - - // Удаляем всё после '%' и другие нежелательные символы - char *p = clean_path; - while (*p) { - if (*p == '%' || *p == '\n' || *p == '\r') { - *p = '\0'; - break; - } - p++; - } - - // Если путь пустой — используем "default" - if (clean_path[0] == '\0') { - strcpy(clean_path, "default"); - } - - mkdir(TOKEN_DIR, 0700); - - time_t now = time(NULL); - char input[1024]; - snprintf(input, sizeof(input), "%ld_%s_%s", now, clean_path, TOKEN_SALT); - - unsigned long hash = 0; - for (int i = 0; input[i]; i++) { - hash = hash * 31 + input[i]; - } - - char token[256]; - snprintf(token, sizeof(token), "%lx_%ld", hash, now); - - char token_file[512]; - snprintf(token_file, sizeof(token_file), "%s/%s", TOKEN_DIR, token); - - FILE *f = fopen(token_file, "w"); - if (!f) return NULL; - fprintf(f, "%s", clean_path); - fclose(f); - - return strdup(token); -} - -int check_token(const char *token, char *path, size_t path_size) { - if (!token || !token[0]) return 0; - - char token_file[512]; - snprintf(token_file, sizeof(token_file), "%s/%s", TOKEN_DIR, token); - - FILE *f = fopen(token_file, "r"); - if (!f) return 0; - - char stored_path[1024]; - if (fgets(stored_path, sizeof(stored_path), f) == NULL) { - fclose(f); - return 0; - } - fclose(f); - - stored_path[strcspn(stored_path, "\n")] = '\0'; - - // Обрезаем % в конце - char *pp = stored_path; - while (*pp) { - if (*pp == '%' || *pp == '\n' || *pp == '\r') { - *pp = '\0'; - break; - } - pp++; - } - - struct stat st; - if (stat(token_file, &st) != 0) return 0; - - time_t now = time(NULL); - if (now - st.st_mtime > 3600) { - unlink(token_file); - return 0; - } - - strncpy(path, stored_path, path_size - 1); - path[path_size - 1] = '\0'; - - return 1; -} - -void cleanup_old_tokens(void) { - DIR *dir = opendir(TOKEN_DIR); - if (!dir) return; - - struct dirent *entry; - time_t now = time(NULL); - - while ((entry = readdir(dir)) != NULL) { - if (entry->d_name[0] == '.') continue; - - char token_file[512]; - snprintf(token_file, sizeof(token_file), "%s/%s", TOKEN_DIR, entry->d_name); - - struct stat st; - if (stat(token_file, &st) == 0) { - if (now - st.st_mtime > 3600) { - unlink(token_file); - } - } - } - closedir(dir); -} \ No newline at end of file diff --git a/src/auth.h b/src/auth.h deleted file mode 100644 index c5a1b91..0000000 --- a/src/auth.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef AUTH_H -#define AUTH_H - -int is_folder_protected(const char *folder_path); -int check_folder_password(const char *folder_name, const char *password); -int check_attempts(const char *folder_name, const char *ip); -void add_attempt(const char *folder_name, const char *ip); -void clear_attempts(const char *folder_name, const char *ip); - -char* generate_token(const char *path); -int check_token(const char *token, char *path, size_t path_size); -void cleanup_old_tokens(void); - -#endif diff --git a/src/check_token.c b/src/check_token.c deleted file mode 100644 index 5522524..0000000 --- a/src/check_token.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define TOKEN_DIR "/tmp/trashbox_tokens" - -int main() { - char *query_string = getenv("QUERY_STRING"); - if (!query_string) { - printf("Content-type: text/plain\n\ninvalid"); - return 0; - } - - char *token_start = strstr(query_string, "token="); - if (!token_start) { - printf("Content-type: text/plain\n\ninvalid"); - return 0; - } - - token_start += 6; - char *token_end = strchr(token_start, '&'); - int token_len = token_end ? (int)(token_end - token_start) : (int)strlen(token_start); - if (token_len <= 0 || token_len >= 256) { - printf("Content-type: text/plain\n\ninvalid"); - return 0; - } - - char token[256]; - strncpy(token, token_start, token_len); - token[token_len] = '\0'; - - char token_file[512]; - snprintf(token_file, sizeof(token_file), "%s/%s", TOKEN_DIR, token); - - struct stat st; - if (stat(token_file, &st) != 0) { - printf("Content-type: text/plain\n\ninvalid"); - return 0; - } - - time_t now = time(NULL); - if (now - st.st_mtime > 3600) { - unlink(token_file); - printf("Content-type: text/plain\n\ninvalid"); - return 0; - } - - printf("Content-type: text/plain\n\nvalid"); - return 0; -} diff --git a/src/config.c b/src/config.c deleted file mode 100644 index 97f6861..0000000 --- a/src/config.c +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include -#include -#include "config.h" - -config_t g_config; - -int load_config(const char *path) { - 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(g_config.base_path, value, sizeof(g_config.base_path) - 1); - else if (strcmp(key, "template_path") == 0) strncpy(g_config.template_path, value, sizeof(g_config.template_path) - 1); - else if (strcmp(key, "passwd_file") == 0) strncpy(g_config.passwd_file, value, sizeof(g_config.passwd_file) - 1); - } - else if (strcmp(current_section, "limits") == 0) { - if (strcmp(key, "max_entries") == 0) g_config.max_entries = atoi(value); - } - else if (strcmp(current_section, "hidden") == 0) { - if (strcmp(key, "dirs") == 0) { - char *token = strtok(value, ","); - g_config.hidden_dirs_count = 0; - while (token && g_config.hidden_dirs_count < 10) { - while (*token == ' ') token++; - strncpy(g_config.hidden_dirs[g_config.hidden_dirs_count], token, 255); - g_config.hidden_dirs[g_config.hidden_dirs_count][255] = '\0'; - g_config.hidden_dirs_count++; - token = strtok(NULL, ","); - } - } - else if (strcmp(key, "files") == 0) { - char *token = strtok(value, ","); - g_config.hidden_files_count = 0; - while (token && g_config.hidden_files_count < 10) { - while (*token == ' ') token++; - strncpy(g_config.hidden_files[g_config.hidden_files_count], token, 255); - g_config.hidden_files[g_config.hidden_files_count][255] = '\0'; - g_config.hidden_files_count++; - token = strtok(NULL, ","); - } - } - } - else if (strcmp(current_section, "preview") == 0) { - if (strcmp(key, "image") == 0) strncpy(g_config.preview_image, value, sizeof(g_config.preview_image) - 1); - else if (strcmp(key, "text") == 0) strncpy(g_config.preview_text, value, sizeof(g_config.preview_text) - 1); - else if (strcmp(key, "audio") == 0) strncpy(g_config.preview_audio, value, sizeof(g_config.preview_audio) - 1); - else if (strcmp(key, "video") == 0) strncpy(g_config.preview_video, value, sizeof(g_config.preview_video) - 1); - else if (strcmp(key, "pdf") == 0) strncpy(g_config.preview_pdf, value, sizeof(g_config.preview_pdf) - 1); - } - else if (strcmp(current_section, "security") == 0) { - if (strcmp(key, "max_attempts") == 0) g_config.max_attempts = atoi(value); - else if (strcmp(key, "block_time") == 0) g_config.block_time = atoi(value); - } - else if (strcmp(current_section, "logging") == 0) { - if (strcmp(key, "enable") == 0) { - g_config.enable_access_log = atoi(value); - } else if (strcmp(key, "log_file") == 0) { - strncpy(g_config.log_file, value, sizeof(g_config.log_file) - 1); - g_config.log_file[sizeof(g_config.log_file) - 1] = '\0'; - } - } - } - - fclose(f); - return 0; -} - -void set_default_config(void) { - strcpy(g_config.base_path, "/home/romkazvo/www"); - strcpy(g_config.template_path, "/home/romkazvo/www/cgi-bin/template.html"); - strcpy(g_config.passwd_file, "/home/romkazvo/www/cgi-bin/.htpasswd"); - g_config.max_entries = 1000; - g_config.hidden_dirs_count = 0; - g_config.hidden_files_count = 0; - strcpy(g_config.preview_image, "jpg,jpeg,png,gif,webp,bmp,svg,ico"); - strcpy(g_config.preview_text, "txt,md,html,htm,css,js,json,xml,csv"); - strcpy(g_config.preview_audio, "mp3,wav,ogg,flac,m4a,aac"); - strcpy(g_config.preview_video, "mp4,webm,ogv,mov,avi,mkv"); - strcpy(g_config.preview_pdf, "pdf"); - - g_config.max_attempts = 5; - g_config.block_time = 900; - - strcpy(g_config.log_file, "/home/romkazvo/www/cgi-bin/logs/access.log"); - g_config.enable_access_log = 1; -} - -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; -} diff --git a/src/config.h b/src/config.h deleted file mode 100644 index 3a07b9a..0000000 --- a/src/config.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_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]; - - int max_attempts; - int block_time; - - char log_file[1024]; - int enable_access_log; -} config_t; - -extern config_t g_config; - -int load_config(const char *path); -void set_default_config(void); -int is_string_in_list(const char *str, const char *list); - -#endif diff --git a/src/fs.c b/src/fs.c deleted file mode 100644 index 0971555..0000000 --- a/src/fs.c +++ /dev/null @@ -1,179 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "fs.h" -#include "config.h" -#include "auth.h" -#include "utils.h" - -query_params_t g_params; - -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 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 = opendir(path); - if (!dir) return 0; - struct dirent *entry; - struct stat statbuf; - char fullpath[1024]; - long long size = 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)); -} - -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; } - } - 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; - 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]; - snprintf(result->name, sizeof(result->name), "%s", entry->d_name); - if (S_ISDIR(file_stat.st_mode)) { - result->is_dir = 1; - result->size = 0; - result->dir_size = get_dir_size(full_path); - result->file_count = count_files_in_dir(full_path); - result->mtime = file_stat.st_mtime; - strcpy(result->icon, "📁"); - result->is_protected = 0; - url_encode(display_subpath, result->encoded_path, sizeof(result->encoded_path)); - snprintf(result->file_url, sizeof(result->file_url), "%s", display_subpath); - (*count)++; - } else if (S_ISREG(file_stat.st_mode)) { - result->is_dir = 0; - result->size = file_stat.st_size; - result->dir_size = 0; - result->file_count = 0; - 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)); - snprintf(result->file_url, sizeof(result->file_url), "%s", display_subpath); - (*count)++; - } - } - if (S_ISDIR(file_stat.st_mode)) { - search_recursive(full_path, display_subpath, search_term, results, count, max_results); - } - } - } - closedir(dir); -} - -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; - long long sizeB = entryB->is_dir ? entryB->dir_size : entryB->size; - result = (sizeA > sizeB) - (sizeA < sizeB); - } else if (strcmp(g_params.sort_by, "date") == 0) { - result = (entryA->mtime > entryB->mtime) - (entryA->mtime < entryB->mtime); - } else { - result = strcasecmp(entryA->name, entryB->name); - } - - return result * g_params.sort_order; -} diff --git a/src/fs.h b/src/fs.h deleted file mode 100644 index 1fb901c..0000000 --- a/src/fs.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef FS_H -#define FS_H - -#include - -typedef struct { - char name[256]; - int is_dir; - long size; - char icon[8]; - char encoded_path[1024]; - char file_url[2048]; - int is_protected; - time_t mtime; - int file_count; - long long dir_size; -} entry_t; - -typedef struct { - char sort_by[16]; - int sort_order; - char search[256]; - int recursive; - char view[16]; -} query_params_t; - -extern query_params_t g_params; - -int is_safe_path(const char *path); -void safe_path_join(char *result, size_t result_size, const char *base, const char *path); -int count_files_in_dir(const char *path); -long long get_dir_size(const char *path); -const char* get_file_icon(const char* filename); -int is_previewable(const char* filename); -void search_recursive(const char *base_path, const char *display_path, const char *search_term, entry_t *results, int *count, int max_results); -int compare_entries_sorted(const void *a, const void *b); - -#endif diff --git a/src/main.c b/src/main.c deleted file mode 100644 index f90d953..0000000 --- a/src/main.c +++ /dev/null @@ -1,245 +0,0 @@ -#include -#include -#include -#include -#include -#include "config.h" -#include "auth.h" -#include "fs.h" -#include "render.h" -#include "utils.h" - -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") != 0) { - set_default_config(); - } - - strcpy(g_params.sort_by, "name"); - g_params.sort_order = 1; - g_params.search[0] = '\0'; - g_params.recursive = 0; - strcpy(g_params.view, "list"); - - char *method = getenv("REQUEST_METHOD"); - char *query_string = getenv("QUERY_STRING"); - char *remote_addr = getenv("REMOTE_ADDR"); - if (!remote_addr) remote_addr = "unknown"; - - char display_path[1024] = ""; - char safe_display_path[1024] = ""; - char base_path[1024]; - strcpy(base_path, g_config.base_path); - - // ============================================ - // ОБРАБОТКА POST (пароль через форму) - // ============================================ - if (method && strcmp(method, "POST") == 0) { - char *content_length_str = getenv("CONTENT_LENGTH"); - if (content_length_str) { - int content_length = atoi(content_length_str); - if (content_length > 0 && content_length < 65536) { - char *post_data = malloc(content_length + 1); - if (post_data) { - int read_len = fread(post_data, 1, content_length, stdin); - post_data[read_len] = '\0'; - - char post_path[1024] = ""; - char post_password[256] = ""; - - char *p = post_data; - while (*p) { - if (strncmp(p, "path=", 5) == 0) { - p += 5; - char *end = strchr(p, '&'); - int len = end ? (int)(end - p) : (int)strlen(p); - if (len > 0 && len < (int)sizeof(post_path) - 1) { - char encoded[1024]; - strncpy(encoded, p, len); - encoded[len] = '\0'; - url_decode_enhanced(encoded, post_path, sizeof(post_path)); - } - p += len; - if (*p == '&') p++; - } else if (strncmp(p, "password=", 9) == 0) { - p += 9; - char *end = strchr(p, '&'); - int len = end ? (int)(end - p) : (int)strlen(p); - if (len > 0 && len < (int)sizeof(post_password) - 1) { - char encoded[256]; - strncpy(encoded, p, len); - encoded[len] = '\0'; - url_decode_enhanced(encoded, post_password, sizeof(post_password)); - } - p += len; - if (*p == '&') p++; - } else { - p++; - } - } - - free(post_data); - - fprintf(stderr, "DEBUG POST: path='%s', password='%s'\n", post_path, post_password); - - if (post_path[0] && post_password[0]) { - if (check_attempts(post_path, remote_addr)) { - char encoded_path[1024]; - url_encode(post_path, encoded_path, sizeof(encoded_path)); - printf("Status: 302 Found\r\n"); - printf("Location: /cgi-bin/index.cgi?path=%s&error=blocked\r\n\r\n", encoded_path); - return 0; - } - - const char *last_slash = strrchr(post_path, '/'); - const char *folder_name = last_slash ? last_slash + 1 : post_path; - - fprintf(stderr, "DEBUG: checking folder='%s'\n", folder_name); - - if (check_folder_password(folder_name, post_password)) { - fprintf(stderr, "DEBUG: password OK!\n"); - clear_attempts(post_path, remote_addr); - - char encoded_path[1024]; - url_encode(post_path, encoded_path, sizeof(encoded_path)); - - char clean_path[1024]; - strncpy(clean_path, post_path, sizeof(clean_path) - 1); - clean_path[sizeof(clean_path) - 1] = '\0'; - - char *pp = clean_path; - while (*pp) { - if (*pp == '%') { - *pp = '\0'; - break; - } - pp++; - } - - fprintf(stderr, "DEBUG: clean_path before token = '%s'\n", clean_path); - - char *token = generate_token(clean_path); - if (token) { - fprintf(stderr, "DEBUG: token generated: %s\n", token); - // ============================================ - // РЕДИРЕКТ С ТОКЕНОМ - // ============================================ - printf("Status: 302 Found\r\n"); - printf("Location: /cgi-bin/index.cgi?path=%s&token=%s\r\n", encoded_path, token); - printf("\r\n"); - free(token); - return 0; - } else { - fprintf(stderr, "DEBUG: token generation failed\n"); - printf("Status: 302 Found\r\n"); - printf("Location: /cgi-bin/index.cgi?path=%s\r\n", encoded_path); - printf("\r\n"); - return 0; - } - } else { - fprintf(stderr, "DEBUG: password FAILED!\n"); - add_attempt(post_path, remote_addr); - - char encoded_path[1024]; - url_encode(post_path, encoded_path, sizeof(encoded_path)); - - printf("Status: 302 Found\r\n"); - printf("Location: /cgi-bin/index.cgi?path=%s&error=1\r\n", encoded_path); - printf("\r\n"); - return 0; - } - } else { - fprintf(stderr, "DEBUG: path or password empty!\n"); - } - } - } - } - } - - // ============================================ - // ОБРАБОТКА GET - // ============================================ - 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 ? (int)(path_end - path_start) : (int)strlen(path_start); - if (path_len > 0 && path_len < (int)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); - } - } - } - - char *sort_start = strstr(query_string, "sort="); - if (sort_start) { - sort_start += 5; - char *sort_end = strchr(sort_start, '&'); - int sort_len = sort_end ? (int)(sort_end - sort_start) : (int)strlen(sort_start); - if (sort_len > 0 && sort_len < (int)sizeof(g_params.sort_by)) { - strncpy(g_params.sort_by, sort_start, sort_len); - g_params.sort_by[sort_len] = '\0'; - } - } - - char *order_start = strstr(query_string, "order="); - if (order_start) { - order_start += 6; - if (*order_start == '1') { - g_params.sort_order = 1; - } else if (*order_start == '-') { - g_params.sort_order = -1; - } - } - - char *view_start = strstr(query_string, "view="); - if (view_start) { - view_start += 5; - char *view_end = strchr(view_start, '&'); - int view_len = view_end ? (int)(view_end - view_start) : (int)strlen(view_start); - if (view_len > 0 && view_len < (int)sizeof(g_params.view) - 1) { - strncpy(g_params.view, view_start, view_len); - g_params.view[view_len] = '\0'; - } - } - - char *search_start = strstr(query_string, "search="); - if (search_start) { - search_start += 7; - char *search_end = strchr(search_start, '&'); - int search_len = search_end ? (int)(search_end - search_start) : (int)strlen(search_start); - if (search_len > 0 && search_len < (int)sizeof(g_params.search) - 1) { - char encoded_search[256]; - strncpy(encoded_search, search_start, search_len); - encoded_search[search_len] = '\0'; - url_decode_enhanced(encoded_search, g_params.search, sizeof(g_params.search)); - if (g_params.search[0] != '\0') { - g_params.recursive = 1; - } - } - } - } - - static time_t last_cleanup = 0; - time_t now = time(NULL); - if (now - last_cleanup > 3600) { - cleanup_old_tokens(); - last_cleanup = now; - } - - printf("Content-type: text/html; charset=utf-8\n\n"); - print_template(base_path, display_path); - return 0; -} \ No newline at end of file diff --git a/src/render.c b/src/render.c deleted file mode 100644 index 644576f..0000000 --- a/src/render.c +++ /dev/null @@ -1,657 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#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 get_token_part(char *buf, size_t size) { - buf[0] = '\0'; - char *query_string = getenv("QUERY_STRING"); - if (!query_string) return; - - char *token_start = strstr(query_string, "token="); - if (!token_start) return; - - token_start += 6; - char *token_end = strchr(token_start, '&'); - int token_len = token_end ? (int)(token_end - token_start) : (int)strlen(token_start); - if (token_len > 0 && token_len < (int)size - 1) { - strncpy(buf, token_start, token_len); - buf[token_len] = '\0'; - } -} - -void print_breadcrumb(buffer_t *buf, const char *display_path) { - char token_part[256] = ""; - get_token_part(token_part, sizeof(token_part)); - - 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)); - - buffer_append(buf, ""); - buffer_append(buf, safe_token); - buffer_append(buf, ""); - - token = strtok(NULL, "/"); - } - free(path_copy); - } - buffer_append(buf, "\n
\n"); -} - -void print_password_form(buffer_t *buf, const char *folder_name) { - char *remote_addr = getenv("REMOTE_ADDR"); - if (!remote_addr) remote_addr = "unknown"; - - if (check_attempts(folder_name, remote_addr)) { - buffer_append(buf, "
\n"); - buffer_append(buf, "
\n"); - buffer_append(buf, "
🚫
\n"); - buffer_append(buf, "

Доступ заблокирован

\n"); - buffer_append(buf, "

Слишком много неудачных попыток. Подождите 15 минут.

\n"); - buffer_append(buf, " ← Вернуться на главную\n"); - buffer_append(buf, "
\n"); - buffer_append(buf, "
\n"); - return; - } - - char encoded_path[1024]; - url_encode(folder_name, encoded_path, sizeof(encoded_path)); - - buffer_append(buf, "
\n"); - buffer_append(buf, "
\n"); - buffer_append(buf, "
🔒
\n"); - buffer_append(buf, "

Папка защищена паролем

\n"); - buffer_append(buf, "

Введите пароль для доступа к папке "); - char safe_name[512]; - html_escape(folder_name, safe_name, sizeof(safe_name)); - buffer_append(buf, safe_name); - buffer_append(buf, "

\n"); - buffer_append(buf, "
\n"); - buffer_append(buf, " \n"); - buffer_append(buf, " \n"); - buffer_append(buf, " \n"); - buffer_append(buf, "
\n"); - buffer_append(buf, "

"); - - char *query_string = getenv("QUERY_STRING"); - if (query_string && strstr(query_string, "error=1")) { - buffer_append(buf, "❌ Неверный пароль! Попробуйте снова."); - } - buffer_append(buf, "

\n"); - buffer_append(buf, " ← Вернуться на главную\n"); - buffer_append(buf, "
\n"); - buffer_append(buf, "
\n"); -} - -void print_content(buffer_t *buf, const char *base_path, const char *display_path) { - fprintf(stderr, "DEBUG: print_content START, display_path='%s'\n", display_path ? display_path : "(null)"); - - // ============================================ - // ОБЪЯВЛЯЕМ entries В НАЧАЛЕ ФУНКЦИИ - // ============================================ - DIR *dir; - struct dirent *entry; - struct stat file_stat; - char full_path[1024]; - entry_t *entries = NULL; - int entry_count = 0, dir_count = 0, file_count = 0; - long long total_size = 0; - int is_recursive_search = 0; - char *remote_addr = getenv("REMOTE_ADDR"); - if (!remote_addr) remote_addr = "unknown"; - - char *query_string = getenv("QUERY_STRING"); - int is_root = 0; - if (!query_string || !strstr(query_string, "path=")) { - fprintf(stderr, "DEBUG: no path=, rendering root\n"); - is_root = 1; - } - - char token_part[256] = ""; - get_token_part(token_part, sizeof(token_part)); - fprintf(stderr, "DEBUG: token_part='%s'\n", token_part); - - int has_valid_token = 0; - - if (!is_root && query_string) { - char *token_start = strstr(query_string, "token="); - if (token_start) { - token_start += 6; - char *token_end = strchr(token_start, '&'); - int token_len = token_end ? (int)(token_end - token_start) : (int)strlen(token_start); - if (token_len > 0 && token_len < 256) { - char token[256]; - strncpy(token, token_start, token_len); - token[token_len] = '\0'; - - char token_path[1024]; - if (check_token(token, token_path, sizeof(token_path))) { - if (strcmp(token_path, display_path) == 0) { - has_valid_token = 1; - fprintf(stderr, "DEBUG: valid token for '%s'\n", display_path); - } - } - } - } - } - - if (!is_root && !has_valid_token && display_path && display_path[0]) { - char *last_slash = strrchr(display_path, '/'); - char *folder_name = last_slash ? last_slash + 1 : (char*)display_path; - - if (query_string && strstr(query_string, "error=1")) { - fprintf(stderr, "DEBUG: error=1, showing password form\n"); - print_password_form(buf, folder_name); - return; - } - - if (is_folder_protected(folder_name)) { - fprintf(stderr, "DEBUG: no valid token, showing password form\n"); - // Добавляем скрипт очистки localStorage - buffer_append(buf, "\n"); - print_password_form(buf, folder_name); - return; - } - } - - // ============================================ - // ВЫДЕЛЯЕМ ПАМЯТЬ ДЛЯ entries - // ============================================ - entries = malloc(sizeof(entry_t) * g_config.max_entries); - if (!entries) { - buffer_append(buf, "
Ошибка выделения памяти
"); - 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"); - char *display_base = strstr(base_path, "/www/"); - if (!display_base) display_base = (char*)base_path; - buffer_append(buf, "

Ошибка открытия директории: "); - buffer_append(buf, display_base); - buffer_append(buf, "

\n"); - buffer_append(buf, "

Папка не найдена

\n"); - 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) { - snprintf(entries[entry_count].name, sizeof(entries[0].name), "%s", entry->d_name); - 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)); - char temp_url[2048]; - snprintf(temp_url, sizeof(temp_url), "%s%s%s", display_path, display_path[0] ? "/" : "", encoded_filename); - snprintf(entries[entry_count].file_url, sizeof(entries[0].file_url), "%s", temp_url); - 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)); - - int next_order = g_params.sort_order * -1; - char order_str[4]; - snprintf(order_str, sizeof(order_str), "%d", next_order); - - char active_name[64] = ""; - char active_size[64] = ""; - char active_date[64] = ""; - if (strcmp(g_params.sort_by, "name") == 0) { strcpy(active_name, " active"); } - else if (strcmp(g_params.sort_by, "size") == 0) { strcpy(active_size, " active"); } - else if (strcmp(g_params.sort_by, "date") == 0) { strcpy(active_date, " active"); } - - char arrow[8] = ""; - if (g_params.sort_order == 1) strcpy(arrow, "🔽"); - else strcpy(arrow, "🔼"); - - char link_with_token[512]; - if (token_part[0]) { - snprintf(link_with_token, sizeof(link_with_token), "&%s", token_part); - } else { - link_with_token[0] = '\0'; - } - - buffer_append(buf, "
\n"); - buffer_append(buf, "
\n"); - buffer_append(buf, "
\n"); - buffer_append(buf, " \n"); - if (token_part[0]) { - buffer_append(buf, " \n"); - } - buffer_append(buf, " \n"); - buffer_append(buf, " \n"); - buffer_append(buf, "
\n"); - buffer_append(buf, "
\n"); - buffer_append(buf, " \n"); - 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"); - 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); - fprintf(stderr, "DEBUG: print_content END\n"); -} - -void print_template(const char *base_path, const char *display_path) { - FILE *file = fopen(g_config.template_path, "r"); - if (!file) { - fprintf(stderr, "DEBUG: cannot open template: %s\n", g_config.template_path); - printf("Error: Cannot read template\n"); - return; - } - fprintf(stderr, "DEBUG: template opened successfully\n"); - - 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); - - fprintf(stderr, "DEBUG: output size = %zu bytes\n", output.size); - if (output.size == 0) { - fprintf(stderr, "DEBUG: WARNING! output is empty!\n"); - buffer_free(&output); - return; - } - fwrite(output.data, 1, output.size, stdout); - fflush(stdout); - fprintf(stderr, "DEBUG: fwrite done, flushed\n"); - - buffer_free(&output); -} diff --git a/src/render.h b/src/render.h deleted file mode 100644 index 0b38b6b..0000000 --- a/src/render.h +++ /dev/null @@ -1,18 +0,0 @@ -#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); -void render_folder(buffer_t *buf, const char *base_path, const char *display_path); - -#endif diff --git a/src/status.c b/src/status.c deleted file mode 100644 index 42a82ba..0000000 --- a/src/status.c +++ /dev/null @@ -1,97 +0,0 @@ -// /home/romkazvo/www/cgi-bin/src/status.c -#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; -} - -float get_cpu_load() { - FILE *fp = fopen("/proc/loadavg", "r"); - if (!fp) return 0.0f; - - float load; - fscanf(fp, "%f", &load); - fclose(fp); - - return load; -} - -void get_memory_usage(long *used_mb, long *total_mb) { - FILE *fp = fopen("/proc/meminfo", "r"); - if (!fp) { - *used_mb = 0; - *total_mb = 0; - return; - } - - char line[128]; - long total = 0, available = 0; - - while (fgets(line, sizeof(line), fp)) { - if (sscanf(line, "MemTotal: %ld kB", &total) == 1) { - // сохраняем - } else if (sscanf(line, "MemAvailable: %ld kB", &available) == 1) { - break; - } - } - fclose(fp); - - *total_mb = total / 1024; - *used_mb = (total - available) / 1024; -} - -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(); - - double busybox_mb = busybox_kb / 1024.0; - double nginx_mb = nginx_kb / 1024.0; - double total_mb = busybox_mb + nginx_mb; - - float cpu_load = get_cpu_load(); - - long used_mb = 0, total_sys_mb = 0; - get_memory_usage(&used_mb, &total_sys_mb); - - // Две строки - printf("BusyBox: %.1f MB | Nginx: %.1f MB | Всего: %.1f MB\n", - busybox_mb, nginx_mb, total_mb); - printf("CPU Load: %.2f | Память: %ld / %ld MB (%.0f%%)", - cpu_load, used_mb, total_sys_mb, total_sys_mb > 0 ? (float)used_mb / total_sys_mb * 100 : 0); - - return 0; -} diff --git a/src/style.c b/src/style.c deleted file mode 100644 index 818e8c1..0000000 --- a/src/style.c +++ /dev/null @@ -1,36 +0,0 @@ -#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 deleted file mode 100644 index b7caffe..0000000 --- a/src/utils.c +++ /dev/null @@ -1,98 +0,0 @@ -#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'; -} \ No newline at end of file diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 0301302..0000000 --- a/src/utils.h +++ /dev/null @@ -1,13 +0,0 @@ -#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/style.css b/style.css deleted file mode 100755 index 98a550f..0000000 --- a/style.css +++ /dev/null @@ -1,1387 +0,0 @@ -/* ============================================ - БАЗА - ============================================ */ -:root { - --bg-primary: #f8f9fa; - --bg-secondary: #ffffff; - --bg-header: linear-gradient(135deg, #2d3436, #636e72); - --text-primary: #2d3436; - --text-secondary: #636e72; - --text-muted: #b2bec3; - --border-color: #dfe6e9; - --border-light: #f1f2f6; - --accent-blue: #0984e3; - --accent-orange: #e17055; - --accent-green: #00b894; - --hover-bg: #f1f2f6; - --shadow: 0 4px 12px rgba(0,0,0,0.04); - --radius: 12px; -} - -.dark-theme { - --bg-primary: #1e1e1e; - --bg-secondary: #2d2d2d; - --bg-header: linear-gradient(135deg, #1e1e1e, #2d2d2d); - --text-primary: #e0e0e0; - --text-secondary: #b0b0b0; - --text-muted: #6c6c6c; - --border-color: #3d3d3d; - --border-light: #333333; - --accent-blue: #74b9ff; - --accent-orange: #fdcb6e; - --accent-green: #55efc4; - --hover-bg: #3d3d3d; - --shadow: 0 4px 12px rgba(0,0,0,0.2); - --radius: 12px; -} - -/* ============================================ - ТЕЛО - ============================================ */ -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - background: var(--bg-primary); - color: var(--text-primary); - line-height: 1.6; - min-height: 100vh; - transition: background 0.25s ease, color 0.25s ease; -} - -body.loaded * { - transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; -} - -/* ============================================ - КОНТЕЙНЕР - ============================================ */ -.container { - max-width: 100%; - margin: 0 auto; - background: var(--bg-secondary); - min-width: 320px; - min-height: 100vh; - box-shadow: var(--shadow); - transition: background 0.25s ease, box-shadow 0.25s ease; -} - -@media (min-width: 768px) { - .container { - max-width: 1100px; - margin: 16px auto; - min-height: auto; - border-radius: var(--radius); - overflow: hidden; - box-shadow: var(--shadow); - } -} - -/* ============================================ - ШАПКА - ============================================ */ -.header { - background: var(--bg-header); - color: white; - padding: 2rem 1.5rem; - text-align: center; - position: relative; -} - -.header h1 { - font-size: 2rem; - font-weight: 700; - margin: 0; - letter-spacing: -0.5px; -} - -.header .subtitle { - opacity: 0.85; - font-size: 1.05rem; - margin-top: 0.25rem; - font-weight: 400; -} - -/* ============================================ - ОПИСАНИЕ (НЕ УВЕЛИЧИВАЕМ) - ============================================ */ -.site-description { - text-align: center; - padding: 0.75rem 1.5rem; - font-size: 1rem; - color: var(--text-secondary); - border-bottom: 1px solid var(--border-color); - background: var(--bg-primary); - font-weight: 400; - letter-spacing: 0.2px; -} - -/* ============================================ - ХЛЕБНЫЕ КРОШКИ - ============================================ */ -.breadcrumb { - background: var(--bg-primary); - padding: 0.6rem 1.25rem; - border-bottom: 1px solid var(--border-color); - font-size: 0.85rem; - overflow-x: auto; - white-space: nowrap; - -webkit-overflow-scrolling: touch; - color: var(--text-secondary); - display: flex; - align-items: center; - gap: 0.3rem; - flex-wrap: nowrap; -} - -.breadcrumb a { - color: var(--accent-blue); - text-decoration: none; - font-weight: 500; - transition: color 0.15s ease; - padding: 0.1rem 0.2rem; -} - -.breadcrumb a:hover { - color: var(--accent-orange); - text-decoration: underline; -} - -.breadcrumb .separator { - color: var(--text-muted); - user-select: none; - font-weight: 300; - margin: 0 0.1rem; -} - -/* ============================================ - ТУЛБАР - ============================================ */ -.toolbar { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 0.5rem 1rem; - padding: 0.6rem 1.25rem; - background: var(--bg-secondary); - border-bottom: 1px solid var(--border-light); - transition: background 0.2s ease; -} - -/* Поиск */ -.search-box { - flex: 1 1 220px; - min-width: 150px; -} - -.search-box form { - display: flex; - gap: 0.4rem; - align-items: center; -} - -.search-box input[type="text"] { - flex: 1; - padding: 0.5rem 1rem; - border: 1.5px solid var(--border-color); - border-radius: 50px; - background: var(--bg-primary); - color: var(--text-primary); - font-size: 0.85rem; - transition: border-color 0.2s ease, box-shadow 0.2s ease; - outline: none; - min-width: 80px; - height: 38px; - box-sizing: border-box; -} - -.search-box input[type="text"]:focus { - border-color: var(--accent-blue); - box-shadow: 0 0 0 3px rgba(9, 132, 227, 0.12); -} - -.search-box button { - padding: 0 1.2rem; - background: var(--accent-blue); - color: white; - border: none; - border-radius: 50px; - cursor: pointer; - font-weight: 600; - font-size: 0.85rem; - transition: background 0.2s ease, transform 0.15s ease; - white-space: nowrap; - height: 38px; - display: inline-flex; - align-items: center; - justify-content: center; - box-sizing: border-box; -} - -.search-box button:hover { - background: var(--accent-orange); - transform: scale(1.02); -} - -/* Группы настроек */ -.settings-group { - display: flex; - align-items: center; - gap: 0.25rem; - flex-wrap: wrap; -} - -.settings-label { - color: var(--text-muted); - font-weight: 500; - font-size: 0.7rem; - text-transform: uppercase; - letter-spacing: 0.3px; - margin-right: 0.15rem; -} - -/* Разделитель между группами */ -.settings-divider { - width: 1px; - height: 30px; - background: var(--border-color); - margin: 0 0.25rem; -} - -@media (max-width: 768px) { - .settings-divider { - display: none; - } -} - -/* Кнопки вида и сортировки */ -.view-btn, -.sort-btn { - padding: 0 0.9rem; - background: transparent; - border: 1.5px solid var(--border-color); - border-radius: 50px; - color: var(--text-secondary); - font-size: 0.78rem; - cursor: pointer; - transition: all 0.15s ease; - font-weight: 500; - text-decoration: none; - display: inline-flex; - align-items: center; - justify-content: center; - height: 38px; - line-height: 1; - gap: 0.2rem; - box-sizing: border-box; - white-space: nowrap; -} - -.view-btn:hover, -.sort-btn:hover { - background: var(--hover-bg); - border-color: var(--accent-blue); - color: var(--text-primary); -} - -.view-btn.active, -.sort-btn.active { - background: var(--accent-blue); - color: white !important; - border-color: var(--accent-blue); -} - -.view-btn .icon, -.sort-btn .icon { - font-size: 0.85rem; - line-height: 1; -} - -/* Скрываем старый settings-row */ -.settings-row { - display: none !important; -} - -/* ============================================ - СПИСОК ФАЙЛОВ - ============================================ */ -ul { - list-style: none; - padding: 0; - margin: 0; -} - -li { - padding: 0.6rem 1.25rem; - border-bottom: 1px solid var(--border-light); - display: flex; - align-items: center; - flex-wrap: nowrap; - transition: background 0.15s ease; -} - -li:hover { - background: var(--hover-bg); -} - -li:last-child { - border-bottom: none; -} - -li.section-title { - color: var(--text-secondary); - font-size: 0.8rem; - font-weight: 600; - padding: 0.5rem 1.25rem; - border-bottom: 2px solid var(--border-color); - background: var(--bg-primary); - text-transform: uppercase; - letter-spacing: 0.5px; - margin-top: 0.5rem; -} - -li.section-title:first-of-type { - margin-top: 0; -} - -/* ============================================ - ФАЙЛОВАЯ СТРОКА — ФИНАЛЬНЫЙ ФИКС - ============================================ */ -.file-row { - display: flex; - align-items: center; - justify-content: space-between; - flex-grow: 1; - min-width: 0; - gap: 0.75rem; - flex-wrap: nowrap; - width: 100%; -} - -.file-link, -.dir-link { - display: flex; - align-items: center; - flex: 1 1 auto; - min-width: 0; - gap: 0.6rem; - overflow: hidden; - text-decoration: none; - color: var(--text-primary); -} - -.file-link .file-name { - font-weight: 500; -} - -.dir-link .file-name { - font-weight: 600; -} - -.file-link:hover .file-name { - color: var(--accent-blue); -} - -.dir-link:hover .file-name { - color: var(--accent-orange); -} - -.file-icon { - font-size: 1.3rem; - flex-shrink: 0; - width: 28px; - min-width: 28px; - max-width: 28px; - text-align: center; - line-height: 1; -} - -.file-name { - flex: 1 1 auto; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.92rem; - transition: color 0.15s ease; -} - -.file-controls { - display: flex; - align-items: center; - gap: 0.3rem; - flex-shrink: 0; - flex-wrap: nowrap; - margin-left: auto; -} - -.file-meta { - color: var(--text-muted); - font-size: 0.75rem; - font-weight: 500; - white-space: nowrap; - text-align: right; - font-variant-numeric: tabular-nums; - flex-shrink: 0; -} - -.file-meta.date { - min-width: 80px; - width: 80px; -} - -.file-meta.size { - min-width: 60px; - width: 60px; - text-align: right; - flex-shrink: 0; -} - -/* Кнопки — фиксированный размер */ -.preview-btn, -.copy-btn, -.download-btn { - background: transparent; - border: 1px solid var(--border-color); - border-radius: 6px; - width: 32px; - height: 32px; - min-width: 32px; - min-height: 32px; - max-width: 32px; - max-height: 32px; - cursor: pointer; - font-size: 0.8rem; - display: inline-flex; - align-items: center; - justify-content: center; - transition: all 0.15s ease; - opacity: 0.6; - color: var(--text-secondary); - padding: 0; - box-sizing: border-box; - flex-shrink: 0; -} - -.preview-btn:hover, -.copy-btn:hover, -.download-btn:hover { - opacity: 1; - transform: scale(1.05); -} - -.preview-btn:hover { - background: var(--accent-blue); - color: white; - border-color: var(--accent-blue); -} - -.copy-btn:hover { - background: var(--accent-blue); - color: white; - border-color: var(--accent-blue); -} - -.download-btn:hover { - background: var(--accent-green); - color: white; - border-color: var(--accent-green); -} - -/* ============================================ - СТАТИСТИКА - ============================================ */ -.stats { - text-align: center; - color: var(--text-secondary); - margin-top: 2rem; - padding: 1rem 1.25rem; - border-top: 1px solid var(--border-color); - font-size: 0.9rem; - background: var(--bg-primary); -} - -.empty-state { - text-align: center; - padding: 3rem 1rem; - color: var(--text-secondary); -} - -.empty-state .icon { - font-size: 3rem; - margin-bottom: 1rem; - opacity: 0.5; -} - -/* ============================================ - ПАРОЛЬНАЯ ФОРМА - ============================================ */ -.password-form-container { - display: flex; - justify-content: center; - align-items: center; - min-height: 400px; - padding: 2rem; -} - -.password-form { - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 2rem; - max-width: 400px; - width: 100%; - text-align: center; - box-shadow: 0 4px 20px rgba(0,0,0,0.1); -} - -.password-form .lock-icon { - font-size: 3rem; - margin-bottom: 1rem; - display: block; -} - -.password-form h2 { - font-size: 1.3rem; - margin-bottom: 0.5rem; - color: var(--text-primary); -} - -.password-form p { - color: var(--text-secondary); - margin-bottom: 1.5rem; - font-size: 0.95rem; -} - -.password-form p strong { - color: var(--text-primary); - word-break: break-all; -} - -.password-form form { - display: flex; - flex-direction: column; - gap: 0.75rem; -} - -.password-form input[type="password"] { - padding: 0.75rem 1rem; - border: 2px solid var(--border-color); - border-radius: 8px; - font-size: 1rem; - background: var(--bg-primary); - color: var(--text-primary); - transition: border-color 0.2s ease; - width: 100%; - box-sizing: border-box; -} - -.password-form input[type="password"]:focus { - outline: none; - border-color: var(--accent-blue); -} - -.password-form button { - padding: 0.75rem; - background: var(--accent-blue); - color: white; - border: none; - border-radius: 8px; - font-size: 1rem; - font-weight: 600; - cursor: pointer; - transition: background 0.2s ease; -} - -.password-form button:hover { - background: var(--accent-green); -} - -.password-form .error-msg { - color: #dc3545; - font-size: 0.9rem; - min-height: 1.5rem; - margin: 0.25rem 0; -} - -.password-form .back-link { - display: inline-block; - margin-top: 0.75rem; - color: var(--text-secondary); - text-decoration: none; - font-size: 0.9rem; -} - -.password-form .back-link:hover { - color: var(--accent-blue); - text-decoration: underline; -} - -.protected-folder { - position: relative; -} - -/* ============================================ - МОДАЛКА - ============================================ */ -.preview-modal { - display: none; - position: fixed; - z-index: 2000; - left: 0; - top: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(5px); - -webkit-backdrop-filter: blur(5px); - overflow: auto; -} - -.preview-modal.open { - display: flex !important; - align-items: center; - justify-content: center; -} - -.preview-content { - background: var(--bg-secondary); - border-radius: 12px; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); - display: flex; - flex-direction: column; - overflow: hidden; - max-width: 90vw; - max-height: 90vh; - width: auto; - height: auto; - min-width: 320px; - min-height: 200px; -} - -.preview-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0.75rem 1rem; - border-bottom: 1px solid var(--border-color); - background: var(--bg-primary); - flex-shrink: 0; - gap: 1rem; -} - -.preview-title { - font-weight: 600; - color: var(--text-primary); - margin: 0; - flex-grow: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.95rem; -} - -.close-preview { - background: transparent; - color: var(--text-secondary); - border: none; - border-radius: 6px; - width: 34px; - height: 34px; - cursor: pointer; - font-size: 1.4rem; - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.15s ease; -} - -.close-preview:hover { - background: #dc3545; - color: white; -} - -.preview-iframe { - flex-grow: 1; - border: none; - background: var(--bg-secondary); - min-height: 0; - width: 100%; - height: 100%; -} - -.preview-image-container { - display: flex; - align-items: center; - justify-content: center; - flex-grow: 1; - padding: 1rem; - background: var(--bg-primary); - overflow: hidden; - width: 100%; - height: 100%; -} - -.preview-image { - max-width: 100%; - max-height: 100%; - width: auto; - height: auto; - object-fit: contain; - border-radius: 4px; - box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); - display: block; -} - -/* Форма пароля в модалке */ -#passwordFormContainer { - display: flex; - justify-content: center; - align-items: center; - padding: 2rem; - flex-grow: 1; - min-height: 300px; -} - -#passwordFormContainer > div { - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 2rem; - max-width: 400px; - width: 100%; - text-align: center; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); -} - -/* ============================================ - ФУТЕР — НЕ УВЕЛИЧИВАЕМ - ============================================ */ -.footer { - background: var(--bg-primary); - border-top: 1px solid var(--border-color); - padding: 1.5rem 1rem; - text-align: center; - color: var(--text-secondary); - font-size: 0.85rem; -} - -.footer a { - color: var(--accent-blue); - text-decoration: none; - font-weight: 500; - transition: opacity 0.15s ease; -} - -.footer a:hover { - opacity: 0.7; -} - -/* ============================================ - ТЕМА И СКРОЛЛ - ============================================ */ -.theme-toggle { - position: fixed; - top: 12px; - right: 12px; - background: rgba(255,255,255,0.15); - border: 1px solid rgba(255,255,255,0.2); - border-radius: 50%; - width: 48px; - height: 48px; - font-size: 1.3rem; - cursor: pointer; - z-index: 1000; - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s ease; - color: white; -} - -.dark-theme .theme-toggle { - background: rgba(0,0,0,0.3); - border-color: rgba(255,255,255,0.1); - color: var(--text-primary); -} - -.theme-toggle:hover { - transform: scale(1.1); - background: rgba(255,255,255,0.25); -} - -.dark-theme .theme-toggle:hover { - background: rgba(0,0,0,0.4); -} - -.scroll-top { - position: fixed; - bottom: 20px; - right: 20px; - width: 48px; - height: 48px; - background: var(--accent-blue); - color: white; - border: none; - border-radius: 50%; - font-size: 1.3rem; - cursor: pointer; - opacity: 0; - visibility: hidden; - transition: all 0.3s ease; - z-index: 1000; - box-shadow: 0 2px 12px rgba(0,0,0,0.2); - display: flex; - align-items: center; - justify-content: center; -} - -.scroll-top.visible { - opacity: 1; - visibility: visible; -} - -.scroll-top:hover { - background: var(--accent-green); - transform: translateY(-2px); - box-shadow: 0 4px 20px rgba(0,0,0,0.3); -} - -/* ============================================ - УВЕДОМЛЕНИЕ - ============================================ */ -#copyNotification { - position: fixed; - bottom: 80px; - left: 50%; - transform: translateX(-50%); - background: var(--accent-green); - color: white; - padding: 12px 24px; - border-radius: 8px; - font-weight: 600; - box-shadow: 0 4px 12px rgba(0,0,0,0.3); - z-index: 3000; - transition: opacity 0.3s; - opacity: 1; -} - -/* ============================================ - АДАПТИВ — ПЛАНШЕТЫ - ============================================ */ -@media (max-width: 768px) { - .header { - padding: 1.25rem 1rem; - } - .header h1 { - font-size: 1.5rem; - } - - .toolbar { - flex-direction: column; - align-items: stretch; - gap: 0.4rem; - padding: 0.6rem 1rem; - } - - .search-box { - flex: 1 1 100%; - min-width: 0; - } - - .search-box form { - flex-wrap: nowrap; - } - - .search-box input[type="text"] { - min-width: 60px; - font-size: 0.8rem; - padding: 0.4rem 0.8rem; - height: 34px; - } - - .search-box button { - font-size: 0.8rem; - padding: 0 0.9rem; - height: 34px; - } - - .settings-group { - flex-wrap: wrap; - justify-content: center; - min-height: 34px; - align-items: center; - } - - .view-btn, - .sort-btn { - font-size: 0.75rem; - padding: 0 0.7rem; - height: 34px; - } - - .settings-divider { - height: 26px; - } - - .file-controls { - gap: 0.2rem; - } - - .preview-btn, - .copy-btn, - .download-btn { - width: 28px; - height: 28px; - min-width: 28px; - min-height: 28px; - max-width: 28px; - max-height: 28px; - font-size: 0.7rem; - } - - .file-icon { - font-size: 1.2rem; - width: 24px; - } - - .file-name { - font-size: 0.85rem; - } - - li { - padding: 0.5rem 0.75rem; - } - - .file-meta.date { - min-width: 60px; - width: 60px; - } - - .file-meta.size { - min-width: 50px; - width: 50px; - } - - .breadcrumb { - padding: 0.5rem 0.75rem; - font-size: 0.8rem; - } - - .preview-content { - max-width: 96vw; - max-height: 92vh; - min-width: auto; - border-radius: 8px; - } - - #passwordFormContainer { - padding: 1.5rem; - min-height: 250px; - } - - #passwordFormContainer > div { - padding: 1.5rem; - } -} - -/* ============================================ - АДАПТИВ — МОБИЛКИ (ВСЁ УВЕЛИЧЕНО) - ============================================ */ -@media (max-width: 480px) { - .header { - padding: 1.25rem 0.75rem; - } - .header h1 { - font-size: 1.6rem; - } - .header .subtitle { - font-size: 1rem; - } - - .toolbar { - flex-direction: column; - align-items: stretch; - gap: 0.6rem; - padding: 0.75rem 0.75rem; - } - - .search-box { - width: 100%; - min-width: 0; - } - - .search-box form { - flex-wrap: nowrap; - width: 100%; - gap: 0.5rem; - } - - .search-box input[type="text"] { - font-size: 0.9rem; - padding: 0.6rem 0.8rem; - min-width: 60px; - height: 44px; - flex: 1; - } - - .search-box button { - font-size: 0.9rem; - padding: 0 1rem; - height: 44px; - flex-shrink: 0; - } - - .settings-group { - gap: 0.3rem; - flex-wrap: wrap; - justify-content: center; - min-height: 40px; - align-items: center; - } - - .settings-label { - font-size: 0.7rem; - flex-shrink: 0; - } - - .view-btn, - .sort-btn { - font-size: 0.8rem; - padding: 0 0.8rem; - height: 38px; - border-width: 1.5px; - flex-shrink: 0; - } - - .view-btn .icon, - .sort-btn .icon { - font-size: 0.85rem; - } - - /* ===== ФАЙЛОВАЯ СТРОКА — ФИКС ===== */ - .file-row { - gap: 0.3rem !important; - flex-wrap: nowrap !important; - } - - .file-link, - .dir-link { - gap: 0.3rem !important; - flex: 1 1 auto !important; - min-width: 0 !important; - } - - .file-icon { - font-size: 1.4rem !important; - width: 32px !important; - min-width: 32px !important; - flex-shrink: 0 !important; - } - - .file-name { - font-size: 0.95rem !important; - flex: 1 1 auto !important; - min-width: 0 !important; - overflow: hidden !important; - text-overflow: ellipsis !important; - white-space: nowrap !important; - } - - .file-controls { - gap: 0.3rem !important; - flex-shrink: 0 !important; - margin-left: auto !important; - } - - .file-meta.size { - font-size: 0.75rem !important; - min-width: 50px !important; - width: auto !important; - flex-shrink: 0 !important; - text-align: right !important; - } - - .file-meta.date { - display: none !important; - } - - .preview-btn, - .copy-btn, - .download-btn { - width: 38px !important; - height: 38px !important; - min-width: 38px !important; - min-height: 38px !important; - max-width: 38px !important; - max-height: 38px !important; - font-size: 0.9rem !important; - flex-shrink: 0 !important; - padding: 0 !important; - border-width: 1.5px !important; - margin: 0 !important; - border-radius: 8px !important; - } - - li { - padding: 0.6rem 0.6rem !important; - min-height: 56px !important; - } - - li.section-title { - font-size: 0.85rem; - padding: 0.5rem 0.6rem; - } - - /* ===== ХЛЕБНЫЕ КРОШКИ ===== */ - .breadcrumb { - padding: 0.6rem 0.75rem; - font-size: 0.9rem; - gap: 0.3rem; - } - .breadcrumb a { - padding: 0.2rem 0.3rem; - } - - /* ===== СТАТИСТИКА ===== */ - .stats { - font-size: 0.85rem; - padding: 0.75rem 0.6rem; - } - - /* ===== ТЕМА И СКРОЛЛ ===== */ - .theme-toggle { - top: 8px; - right: 8px; - width: 48px; - height: 48px; - font-size: 1.3rem; - } - - .scroll-top { - bottom: 20px; - right: 20px; - width: 48px; - height: 48px; - font-size: 1.3rem; - } - - /* ===== ФУТЕР (НЕ УВЕЛИЧИВАЕМ) ===== */ - .footer { - padding: 1rem 0.75rem; - font-size: 0.8rem; - } - - /* ===== МОДАЛКА ===== */ - .preview-content { - max-width: 98vw; - max-height: 95vh; - border-radius: 8px; - min-height: 150px; - } - - .preview-header { - padding: 0.6rem 0.8rem; - } - - .preview-title { - font-size: 0.9rem; - } - - .close-preview { - width: 34px; - height: 34px; - font-size: 1.2rem; - } - - .preview-image-container { - padding: 0.5rem; - } - - #passwordFormContainer { - padding: 1rem; - min-height: 200px; - } - - #passwordFormContainer > div { - padding: 1.5rem 1rem; - border-radius: 8px; - } - - /* ===== ФОРМА ПАРОЛЯ ===== */ - .password-form { - padding: 1.5rem 1rem; - } - .password-form .lock-icon { - font-size: 2.5rem; - } - .password-form h2 { - font-size: 1.2rem; - } - .password-form p { - font-size: 0.9rem; - } - .password-form input[type="password"] { - font-size: 1rem; - padding: 0.7rem 0.8rem; - height: 48px; - } - .password-form button { - font-size: 1rem; - padding: 0.7rem; - height: 48px; - } -} - -/* ============================================ - АДАПТИВ — ОЧЕНЬ МАЛЕНЬКИЕ ЭКРАНЫ - ============================================ */ -@media (max-width: 360px) { - .header h1 { - font-size: 1.3rem; - } - - .search-box input[type="text"] { - font-size: 0.8rem; - height: 40px; - padding: 0.4rem 0.6rem; - } - .search-box button { - font-size: 0.8rem; - height: 40px; - padding: 0 0.7rem; - } - - .view-btn, - .sort-btn { - font-size: 0.7rem; - padding: 0 0.5rem; - height: 34px; - } - - .file-icon { - font-size: 1.2rem !important; - width: 28px !important; - min-width: 28px !important; - } - - .file-name { - font-size: 0.85rem !important; - } - - .preview-btn, - .copy-btn, - .download-btn { - width: 34px !important; - height: 34px !important; - min-width: 34px !important; - min-height: 34px !important; - max-width: 34px !important; - max-height: 34px !important; - font-size: 0.8rem !important; - border-radius: 6px !important; - } - - .file-meta.size { - font-size: 0.65rem !important; - min-width: 40px !important; - } - - li { - padding: 0.4rem 0.4rem !important; - min-height: 48px !important; - } - - .file-row { - gap: 0.3rem !important; - } - - .theme-toggle { - width: 40px; - height: 40px; - font-size: 1.1rem; - top: 6px; - right: 6px; - } - - .scroll-top { - width: 40px; - height: 40px; - font-size: 1.1rem; - bottom: 16px; - right: 16px; - } - - .password-form input[type="password"] { - height: 42px; - font-size: 0.9rem; - } - .password-form button { - height: 42px; - font-size: 0.9rem; - } -} - -/* ===== ФАЙЛЫ — МОБИЛКИ ===== */ -.file-row { - gap: 0.3rem !important; -} - -.file-link, -.dir-link { - gap: 0.3rem !important; - flex: 1 1 auto !important; -} - -.file-icon { - font-size: 1.4rem !important; - width: 32px !important; - min-width: 32px !important; - max-width: 32px !important; - flex-shrink: 0 !important; -} - -.file-name { - font-size: 0.95rem !important; - flex: 1 1 auto !important; - min-width: 0 !important; -} - -.file-link .file-name { - font-weight: 500 !important; -} - -.dir-link .file-name { - font-weight: 600 !important; -} - -.file-controls { - gap: 0.3rem !important; - flex-shrink: 0 !important; - margin-left: auto !important; -} - -.file-meta.size { - font-size: 0.75rem !important; - min-width: 50px !important; - width: auto !important; - flex-shrink: 0 !important; -} - -.file-meta.date { - display: none !important; -} - -.preview-btn, -.copy-btn, -.download-btn { - width: 38px !important; - height: 38px !important; - min-width: 38px !important; - min-height: 38px !important; - max-width: 38px !important; - max-height: 38px !important; - font-size: 0.9rem !important; - flex-shrink: 0 !important; -} \ No newline at end of file diff --git a/template.html b/template.html deleted file mode 100644 index 8d225ae..0000000 --- a/template.html +++ /dev/null @@ -1,546 +0,0 @@ - - - - - - Trashbox - личная помоечка - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- Trashbox -
-

Trashbox

-
- Склад всякой всячины -
-
-
-
-
- Личный уголок для обмена файлами. Заливаю сюда то, чем хочу поделиться, без всяких лимитов и сторонних сервисов. -
-
- - -
-
- -
- - -
- Загрузка статистики... -
- - -
- - - -
-
-
-

Предпросмотр файла

- -
- -
-
- - - - \ No newline at end of file