|
|
@@ -11,15 +11,14 @@
|
|
|
|
#include <locale.h>
|
|
|
|
#include <locale.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
|
|
|
|
// === ПРОТОТИПЫ ФУНКЦИЙ ===
|
|
|
|
// === ПРОТОТИПЫ ===
|
|
|
|
void url_encode(const char *src, char *dst, size_t dst_size);
|
|
|
|
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 url_decode_enhanced(const char *src, char *dst, size_t dst_size);
|
|
|
|
void format_size(long long size, char* buffer);
|
|
|
|
void format_size(long long size, char* buffer);
|
|
|
|
void format_date(time_t mtime, char* buffer, size_t size);
|
|
|
|
void format_date(time_t mtime, char* buffer, size_t size);
|
|
|
|
void html_escape(const char *src, char *dst, size_t dst_size);
|
|
|
|
void html_escape(const char *src, char *dst, size_t dst_size);
|
|
|
|
|
|
|
|
|
|
|
|
// === КОНФИГУРАЦИЯ ===
|
|
|
|
// === КОНФИГ ===
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
typedef struct {
|
|
|
|
char base_path[1024];
|
|
|
|
char base_path[1024];
|
|
|
|
char template_path[1024];
|
|
|
|
char template_path[1024];
|
|
|
@@ -38,34 +37,27 @@ typedef struct {
|
|
|
|
|
|
|
|
|
|
|
|
config_t g_config;
|
|
|
|
config_t g_config;
|
|
|
|
|
|
|
|
|
|
|
|
// === ПАРАМЕТРЫ ЗАПРОСА (сортировка, поиск) ===
|
|
|
|
// === ПАРАМЕТРЫ ===
|
|
|
|
typedef struct {
|
|
|
|
typedef struct {
|
|
|
|
char sort_by[16]; // "name", "size", "date"
|
|
|
|
char sort_by[16];
|
|
|
|
int sort_order; // 1 = asc, -1 = desc
|
|
|
|
int sort_order;
|
|
|
|
char search[256];
|
|
|
|
char search[256];
|
|
|
|
int recursive; // 1 = рекурсивный поиск, 0 = только текущая папка
|
|
|
|
int recursive;
|
|
|
|
} query_params_t;
|
|
|
|
} query_params_t;
|
|
|
|
|
|
|
|
|
|
|
|
query_params_t g_params;
|
|
|
|
query_params_t g_params;
|
|
|
|
|
|
|
|
|
|
|
|
// === ПОИСК БЕЗ УЧЁТА РЕГИСТРА (поддерживает UTF-8) ===
|
|
|
|
// === УТИЛИТЫ ===
|
|
|
|
char* my_strcasestr(const char *haystack, const char *needle) {
|
|
|
|
char* my_strcasestr(const char *haystack, const char *needle) {
|
|
|
|
if (!haystack || !needle || *needle == '\0') return (char*)haystack;
|
|
|
|
if (!haystack || !needle || *needle == '\0') return (char*)haystack;
|
|
|
|
|
|
|
|
|
|
|
|
const char *h = haystack;
|
|
|
|
|
|
|
|
const char *n = needle;
|
|
|
|
|
|
|
|
size_t needle_len = strlen(needle);
|
|
|
|
size_t needle_len = strlen(needle);
|
|
|
|
|
|
|
|
while (*haystack) {
|
|
|
|
while (*h) {
|
|
|
|
if (strncasecmp(haystack, needle, needle_len) == 0) return (char*)haystack;
|
|
|
|
if (strncasecmp(h, n, needle_len) == 0) {
|
|
|
|
haystack++;
|
|
|
|
return (char*)h;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
h++;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === HTML ЭКРАНИРОВАНИЕ (защита от XSS) ===
|
|
|
|
|
|
|
|
void html_escape(const char *src, char *dst, size_t dst_size) {
|
|
|
|
void html_escape(const char *src, char *dst, size_t dst_size) {
|
|
|
|
if (!src || !dst || dst_size == 0) return;
|
|
|
|
if (!src || !dst || dst_size == 0) return;
|
|
|
|
char *p = dst;
|
|
|
|
char *p = dst;
|
|
|
@@ -83,19 +75,16 @@ void html_escape(const char *src, char *dst, size_t dst_size) {
|
|
|
|
*p = '\0';
|
|
|
|
*p = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Простой парсер INI (без зависимостей)
|
|
|
|
// === ПАРСЕР INI ===
|
|
|
|
int load_config(const char *path, config_t *cfg) {
|
|
|
|
int load_config(const char *path, config_t *cfg) {
|
|
|
|
FILE *f = fopen(path, "r");
|
|
|
|
FILE *f = fopen(path, "r");
|
|
|
|
if (!f) return -1;
|
|
|
|
if (!f) return -1;
|
|
|
|
|
|
|
|
|
|
|
|
char line[512];
|
|
|
|
char line[512];
|
|
|
|
char current_section[64] = "";
|
|
|
|
char current_section[64] = "";
|
|
|
|
|
|
|
|
|
|
|
|
while (fgets(line, sizeof(line), f)) {
|
|
|
|
while (fgets(line, sizeof(line), f)) {
|
|
|
|
char *p = line;
|
|
|
|
char *p = line;
|
|
|
|
while (*p == ' ' || *p == '\t') p++;
|
|
|
|
while (*p == ' ' || *p == '\t') p++;
|
|
|
|
if (*p == '\0' || *p == '#' || *p == ';') continue;
|
|
|
|
if (*p == '\0' || *p == '#' || *p == ';') continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (*p == '[') {
|
|
|
|
if (*p == '[') {
|
|
|
|
p++;
|
|
|
|
p++;
|
|
|
|
char *end = strchr(p, ']');
|
|
|
|
char *end = strchr(p, ']');
|
|
|
@@ -106,36 +95,29 @@ int load_config(const char *path, config_t *cfg) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *eq = strchr(p, '=');
|
|
|
|
char *eq = strchr(p, '=');
|
|
|
|
if (!eq) continue;
|
|
|
|
if (!eq) continue;
|
|
|
|
|
|
|
|
|
|
|
|
*eq = '\0';
|
|
|
|
*eq = '\0';
|
|
|
|
char *key = p;
|
|
|
|
char *key = p;
|
|
|
|
char *value = eq + 1;
|
|
|
|
char *value = eq + 1;
|
|
|
|
|
|
|
|
|
|
|
|
char *end_key = key + strlen(key) - 1;
|
|
|
|
char *end_key = key + strlen(key) - 1;
|
|
|
|
while (end_key > key && (*end_key == ' ' || *end_key == '\t')) {
|
|
|
|
while (end_key > key && (*end_key == ' ' || *end_key == '\t')) {
|
|
|
|
*end_key = '\0';
|
|
|
|
*end_key = '\0';
|
|
|
|
end_key--;
|
|
|
|
end_key--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while (*value == ' ' || *value == '\t') value++;
|
|
|
|
while (*value == ' ' || *value == '\t') value++;
|
|
|
|
char *end_val = value + strlen(value) - 1;
|
|
|
|
char *end_val = value + strlen(value) - 1;
|
|
|
|
while (end_val > value && (*end_val == ' ' || *end_val == '\t' || *end_val == '\r' || *end_val == '\n')) {
|
|
|
|
while (end_val > value && (*end_val == ' ' || *end_val == '\t' || *end_val == '\r' || *end_val == '\n')) {
|
|
|
|
*end_val = '\0';
|
|
|
|
*end_val = '\0';
|
|
|
|
end_val--;
|
|
|
|
end_val--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (strcmp(current_section, "paths") == 0) {
|
|
|
|
if (strcmp(current_section, "paths") == 0) {
|
|
|
|
if (strcmp(key, "base_path") == 0) strncpy(cfg->base_path, value, sizeof(cfg->base_path) - 1);
|
|
|
|
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, "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(key, "passwd_file") == 0) strncpy(cfg->passwd_file, value, sizeof(cfg->passwd_file) - 1);
|
|
|
|
}
|
|
|
|
} else if (strcmp(current_section, "limits") == 0) {
|
|
|
|
else if (strcmp(current_section, "limits") == 0) {
|
|
|
|
|
|
|
|
if (strcmp(key, "max_entries") == 0) cfg->max_entries = atoi(value);
|
|
|
|
if (strcmp(key, "max_entries") == 0) cfg->max_entries = atoi(value);
|
|
|
|
}
|
|
|
|
} else if (strcmp(current_section, "hidden") == 0) {
|
|
|
|
else if (strcmp(current_section, "hidden") == 0) {
|
|
|
|
|
|
|
|
if (strcmp(key, "dirs") == 0) {
|
|
|
|
if (strcmp(key, "dirs") == 0) {
|
|
|
|
char *token = strtok(value, ",");
|
|
|
|
char *token = strtok(value, ",");
|
|
|
|
cfg->hidden_dirs_count = 0;
|
|
|
|
cfg->hidden_dirs_count = 0;
|
|
|
@@ -146,8 +128,7 @@ int load_config(const char *path, config_t *cfg) {
|
|
|
|
cfg->hidden_dirs_count++;
|
|
|
|
cfg->hidden_dirs_count++;
|
|
|
|
token = strtok(NULL, ",");
|
|
|
|
token = strtok(NULL, ",");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (strcmp(key, "files") == 0) {
|
|
|
|
else if (strcmp(key, "files") == 0) {
|
|
|
|
|
|
|
|
char *token = strtok(value, ",");
|
|
|
|
char *token = strtok(value, ",");
|
|
|
|
cfg->hidden_files_count = 0;
|
|
|
|
cfg->hidden_files_count = 0;
|
|
|
|
while (token && cfg->hidden_files_count < 10) {
|
|
|
|
while (token && cfg->hidden_files_count < 10) {
|
|
|
@@ -158,8 +139,7 @@ int load_config(const char *path, config_t *cfg) {
|
|
|
|
token = strtok(NULL, ",");
|
|
|
|
token = strtok(NULL, ",");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (strcmp(current_section, "preview") == 0) {
|
|
|
|
else if (strcmp(current_section, "preview") == 0) {
|
|
|
|
|
|
|
|
if (strcmp(key, "image") == 0) strncpy(cfg->preview_image, value, sizeof(cfg->preview_image) - 1);
|
|
|
|
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, "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, "audio") == 0) strncpy(cfg->preview_audio, value, sizeof(cfg->preview_audio) - 1);
|
|
|
@@ -167,7 +147,6 @@ int load_config(const char *path, config_t *cfg) {
|
|
|
|
else if (strcmp(key, "pdf") == 0) strncpy(cfg->preview_pdf, value, sizeof(cfg->preview_pdf) - 1);
|
|
|
|
else if (strcmp(key, "pdf") == 0) strncpy(cfg->preview_pdf, value, sizeof(cfg->preview_pdf) - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
fclose(f);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -200,8 +179,7 @@ int is_string_in_list(const char *str, const char *list) {
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === ОСТАЛЬНОЙ КОД ===
|
|
|
|
// === ОСНОВНЫЕ СТРУКТУРЫ ===
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
typedef struct {
|
|
|
|
char name[256];
|
|
|
|
char name[256];
|
|
|
|
int is_dir;
|
|
|
|
int is_dir;
|
|
|
@@ -237,10 +215,9 @@ void buffer_append(buffer_t *buf, const char *str) {
|
|
|
|
buf->size += len;
|
|
|
|
buf->size += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void buffer_free(buffer_t *buf) {
|
|
|
|
void buffer_free(buffer_t *buf) { free(buf->data); }
|
|
|
|
free(buf->data);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// === БЕЗОПАСНОСТЬ ===
|
|
|
|
int is_safe_path(const char *path) {
|
|
|
|
int is_safe_path(const char *path) {
|
|
|
|
if (!path) return 0;
|
|
|
|
if (!path) return 0;
|
|
|
|
if (strstr(path, "..")) return 0;
|
|
|
|
if (strstr(path, "..")) return 0;
|
|
|
@@ -258,12 +235,11 @@ void safe_path_join(char *result, size_t result_size, const char *base, const ch
|
|
|
|
snprintf(result, result_size, "%s/%s", base, path);
|
|
|
|
snprintf(result, result_size, "%s/%s", base, path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// === ПАРОЛИ ===
|
|
|
|
int check_folder_password(const char *folder_name, const char *password) {
|
|
|
|
int check_folder_password(const char *folder_name, const char *password) {
|
|
|
|
if (!password || !folder_name || password[0] == '\0') return 0;
|
|
|
|
if (!password || !folder_name || password[0] == '\0') return 0;
|
|
|
|
|
|
|
|
|
|
|
|
FILE *f = fopen(g_config.passwd_file, "r");
|
|
|
|
FILE *f = fopen(g_config.passwd_file, "r");
|
|
|
|
if (!f) return 1;
|
|
|
|
if (!f) return 1;
|
|
|
|
|
|
|
|
|
|
|
|
char line[512];
|
|
|
|
char line[512];
|
|
|
|
while (fgets(line, sizeof(line), f)) {
|
|
|
|
while (fgets(line, sizeof(line), f)) {
|
|
|
|
line[strcspn(line, "\r\n")] = 0;
|
|
|
|
line[strcspn(line, "\r\n")] = 0;
|
|
|
@@ -272,7 +248,6 @@ int check_folder_password(const char *folder_name, const char *password) {
|
|
|
|
*colon = '\0';
|
|
|
|
*colon = '\0';
|
|
|
|
char *folder = line;
|
|
|
|
char *folder = line;
|
|
|
|
char *pass = colon + 1;
|
|
|
|
char *pass = colon + 1;
|
|
|
|
// Проверяем как полный путь, так и имя папки
|
|
|
|
|
|
|
|
const char *last_slash = strrchr(folder_name, '/');
|
|
|
|
const char *last_slash = strrchr(folder_name, '/');
|
|
|
|
const char *base_name = last_slash ? last_slash + 1 : folder_name;
|
|
|
|
const char *base_name = last_slash ? last_slash + 1 : folder_name;
|
|
|
|
if (strcmp(folder, folder_name) == 0 || strcmp(folder, base_name) == 0) {
|
|
|
|
if (strcmp(folder, folder_name) == 0 || strcmp(folder, base_name) == 0) {
|
|
|
@@ -286,20 +261,16 @@ int check_folder_password(const char *folder_name, const char *password) {
|
|
|
|
|
|
|
|
|
|
|
|
int is_folder_protected(const char *folder_path) {
|
|
|
|
int is_folder_protected(const char *folder_path) {
|
|
|
|
if (!folder_path || folder_path[0] == '\0') return 0;
|
|
|
|
if (!folder_path || folder_path[0] == '\0') return 0;
|
|
|
|
|
|
|
|
|
|
|
|
FILE *f = fopen(g_config.passwd_file, "r");
|
|
|
|
FILE *f = fopen(g_config.passwd_file, "r");
|
|
|
|
if (!f) return 0;
|
|
|
|
if (!f) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
char line[512];
|
|
|
|
char line[512];
|
|
|
|
while (fgets(line, sizeof(line), f)) {
|
|
|
|
while (fgets(line, sizeof(line), f)) {
|
|
|
|
line[strcspn(line, "\r\n")] = 0;
|
|
|
|
line[strcspn(line, "\r\n")] = 0;
|
|
|
|
char *colon = strchr(line, ':');
|
|
|
|
char *colon = strchr(line, ':');
|
|
|
|
if (!colon) continue;
|
|
|
|
if (!colon) continue;
|
|
|
|
*colon = '\0';
|
|
|
|
*colon = '\0';
|
|
|
|
// Получаем имя папки (последний сегмент пути)
|
|
|
|
|
|
|
|
const char *last_slash = strrchr(folder_path, '/');
|
|
|
|
const char *last_slash = strrchr(folder_path, '/');
|
|
|
|
const char *folder_name = last_slash ? last_slash + 1 : folder_path;
|
|
|
|
const char *folder_name = last_slash ? last_slash + 1 : folder_path;
|
|
|
|
// Проверяем как полный путь, так и имя папки
|
|
|
|
|
|
|
|
if (strcmp(line, folder_path) == 0 || strcmp(line, folder_name) == 0) {
|
|
|
|
if (strcmp(line, folder_path) == 0 || strcmp(line, folder_name) == 0) {
|
|
|
|
fclose(f);
|
|
|
|
fclose(f);
|
|
|
|
return 1;
|
|
|
|
return 1;
|
|
|
@@ -309,6 +280,7 @@ int is_folder_protected(const char *folder_path) {
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// === ФС ===
|
|
|
|
int count_files_in_dir(const char *path) {
|
|
|
|
int count_files_in_dir(const char *path) {
|
|
|
|
DIR *dir = opendir(path);
|
|
|
|
DIR *dir = opendir(path);
|
|
|
|
if (!dir) return 0;
|
|
|
|
if (!dir) return 0;
|
|
|
@@ -329,13 +301,12 @@ int count_files_in_dir(const char *path) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
long long get_dir_size(const char *path) {
|
|
|
|
long long get_dir_size(const char *path) {
|
|
|
|
DIR *dir;
|
|
|
|
DIR *dir = opendir(path);
|
|
|
|
|
|
|
|
if (!dir) return 0;
|
|
|
|
struct dirent *entry;
|
|
|
|
struct dirent *entry;
|
|
|
|
struct stat statbuf;
|
|
|
|
struct stat statbuf;
|
|
|
|
char fullpath[1024];
|
|
|
|
char fullpath[1024];
|
|
|
|
long long size = 0;
|
|
|
|
long long size = 0;
|
|
|
|
dir = opendir(path);
|
|
|
|
|
|
|
|
if (!dir) return 0;
|
|
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
|
|
|
snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);
|
|
|
|
snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);
|
|
|
@@ -376,21 +347,17 @@ int is_previewable(const char* filename) {
|
|
|
|
is_string_in_list(ext, g_config.preview_pdf));
|
|
|
|
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) {
|
|
|
|
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);
|
|
|
|
DIR *dir = opendir(base_path);
|
|
|
|
if (!dir) return;
|
|
|
|
if (!dir) return;
|
|
|
|
|
|
|
|
|
|
|
|
struct dirent *entry;
|
|
|
|
struct dirent *entry;
|
|
|
|
struct stat file_stat;
|
|
|
|
struct stat file_stat;
|
|
|
|
char full_path[1024];
|
|
|
|
char full_path[1024];
|
|
|
|
char display_subpath[1024];
|
|
|
|
char display_subpath[1024];
|
|
|
|
|
|
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL && *count < max_results) {
|
|
|
|
while ((entry = readdir(dir)) != NULL && *count < max_results) {
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
|
|
|
if (entry->d_name[0] == '.') continue;
|
|
|
|
if (entry->d_name[0] == '.') continue;
|
|
|
|
|
|
|
|
|
|
|
|
// Проверяем скрытые папки/файлы из конфига
|
|
|
|
|
|
|
|
int hidden = 0;
|
|
|
|
int hidden = 0;
|
|
|
|
for (int i = 0; i < g_config.hidden_dirs_count; i++) {
|
|
|
|
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; }
|
|
|
|
if (strcmp(entry->d_name, g_config.hidden_dirs[i]) == 0) { hidden = 1; break; }
|
|
|
@@ -399,25 +366,20 @@ void search_recursive(const char *base_path, const char *display_path, const cha
|
|
|
|
if (strcmp(entry->d_name, g_config.hidden_files[i]) == 0) { hidden = 1; break; }
|
|
|
|
if (strcmp(entry->d_name, g_config.hidden_files[i]) == 0) { hidden = 1; break; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hidden) continue;
|
|
|
|
if (hidden) continue;
|
|
|
|
|
|
|
|
|
|
|
|
snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name);
|
|
|
|
snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name);
|
|
|
|
if (display_path[0] == '\0') {
|
|
|
|
if (display_path[0] == '\0') {
|
|
|
|
snprintf(display_subpath, sizeof(display_subpath), "%s", entry->d_name);
|
|
|
|
snprintf(display_subpath, sizeof(display_subpath), "%s", entry->d_name);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
snprintf(display_subpath, sizeof(display_subpath), "%s/%s", display_path, entry->d_name);
|
|
|
|
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)) {
|
|
|
|
if (is_folder_protected(display_subpath) || is_folder_protected(entry->d_name)) {
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (stat(full_path, &file_stat) == 0) {
|
|
|
|
if (stat(full_path, &file_stat) == 0) {
|
|
|
|
if (my_strcasestr(entry->d_name, search_term) != NULL) {
|
|
|
|
if (my_strcasestr(entry->d_name, search_term) != NULL) {
|
|
|
|
entry_t *result = &results[*count];
|
|
|
|
entry_t *result = &results[*count];
|
|
|
|
strncpy(result->name, entry->d_name, sizeof(result->name) - 1);
|
|
|
|
strncpy(result->name, entry->d_name, sizeof(result->name) - 1);
|
|
|
|
result->name[sizeof(result->name) - 1] = '\0';
|
|
|
|
result->name[sizeof(result->name) - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
if (S_ISDIR(file_stat.st_mode)) {
|
|
|
|
if (S_ISDIR(file_stat.st_mode)) {
|
|
|
|
result->is_dir = 1;
|
|
|
|
result->is_dir = 1;
|
|
|
|
result->size = 0;
|
|
|
|
result->size = 0;
|
|
|
@@ -426,7 +388,6 @@ void search_recursive(const char *base_path, const char *display_path, const cha
|
|
|
|
result->mtime = file_stat.st_mtime;
|
|
|
|
result->mtime = file_stat.st_mtime;
|
|
|
|
strcpy(result->icon, "📁");
|
|
|
|
strcpy(result->icon, "📁");
|
|
|
|
result->is_protected = 0;
|
|
|
|
result->is_protected = 0;
|
|
|
|
|
|
|
|
|
|
|
|
url_encode(display_subpath, result->encoded_path, sizeof(result->encoded_path));
|
|
|
|
url_encode(display_subpath, result->encoded_path, sizeof(result->encoded_path));
|
|
|
|
strncpy(result->file_url, display_subpath, sizeof(result->file_url) - 1);
|
|
|
|
strncpy(result->file_url, display_subpath, sizeof(result->file_url) - 1);
|
|
|
|
(*count)++;
|
|
|
|
(*count)++;
|
|
|
@@ -438,13 +399,11 @@ void search_recursive(const char *base_path, const char *display_path, const cha
|
|
|
|
result->mtime = file_stat.st_mtime;
|
|
|
|
result->mtime = file_stat.st_mtime;
|
|
|
|
strcpy(result->icon, get_file_icon(entry->d_name));
|
|
|
|
strcpy(result->icon, get_file_icon(entry->d_name));
|
|
|
|
result->is_protected = 0;
|
|
|
|
result->is_protected = 0;
|
|
|
|
|
|
|
|
|
|
|
|
url_encode(display_subpath, result->encoded_path, sizeof(result->encoded_path));
|
|
|
|
url_encode(display_subpath, result->encoded_path, sizeof(result->encoded_path));
|
|
|
|
strncpy(result->file_url, display_subpath, sizeof(result->file_url) - 1);
|
|
|
|
strncpy(result->file_url, display_subpath, sizeof(result->file_url) - 1);
|
|
|
|
(*count)++;
|
|
|
|
(*count)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (S_ISDIR(file_stat.st_mode)) {
|
|
|
|
if (S_ISDIR(file_stat.st_mode)) {
|
|
|
|
search_recursive(full_path, display_subpath, search_term, results, count, max_results);
|
|
|
|
search_recursive(full_path, display_subpath, search_term, results, count, max_results);
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -454,15 +413,11 @@ void search_recursive(const char *base_path, const char *display_path, const cha
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === СОРТИРОВКА ===
|
|
|
|
// === СОРТИРОВКА ===
|
|
|
|
|
|
|
|
|
|
|
|
int compare_entries_sorted(const void *a, const void *b) {
|
|
|
|
int compare_entries_sorted(const void *a, const void *b) {
|
|
|
|
const entry_t *entryA = (const entry_t *)a;
|
|
|
|
const entry_t *entryA = (const entry_t *)a;
|
|
|
|
const entry_t *entryB = (const entry_t *)b;
|
|
|
|
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;
|
|
|
|
if (!entryA->is_dir && entryB->is_dir) return 1;
|
|
|
|
if (!entryA->is_dir && entryB->is_dir) return 1;
|
|
|
|
|
|
|
|
|
|
|
|
int result = 0;
|
|
|
|
int result = 0;
|
|
|
|
if (strcmp(g_params.sort_by, "size") == 0) {
|
|
|
|
if (strcmp(g_params.sort_by, "size") == 0) {
|
|
|
|
long long sizeA = entryA->is_dir ? entryA->dir_size : entryA->size;
|
|
|
|
long long sizeA = entryA->is_dir ? entryA->dir_size : entryA->size;
|
|
|
@@ -473,10 +428,10 @@ int compare_entries_sorted(const void *a, const void *b) {
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
result = strcasecmp(entryA->name, entryB->name);
|
|
|
|
result = strcasecmp(entryA->name, entryB->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result * g_params.sort_order;
|
|
|
|
return result * g_params.sort_order;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// === ФОРМАТИРОВАНИЕ ===
|
|
|
|
void format_size(long long size, char* buffer) {
|
|
|
|
void format_size(long long size, char* buffer) {
|
|
|
|
if (size < 1024) snprintf(buffer, 32, "%lld B", size);
|
|
|
|
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) snprintf(buffer, 32, "%.1f KB", size / 1024.0);
|
|
|
@@ -513,7 +468,6 @@ void url_decode_enhanced(const char *src, char *dst, size_t dst_size) {
|
|
|
|
size_t decoded_len = 0;
|
|
|
|
size_t decoded_len = 0;
|
|
|
|
while (*src && decoded_len < dst_size - 1) {
|
|
|
|
while (*src && decoded_len < dst_size - 1) {
|
|
|
|
if (*src == '%') {
|
|
|
|
if (*src == '%') {
|
|
|
|
// Проверяем, что есть ещё минимум 2 символа
|
|
|
|
|
|
|
|
if (src[1] && src[2] && isxdigit(src[1]) && isxdigit(src[2])) {
|
|
|
|
if (src[1] && src[2] && isxdigit(src[1]) && isxdigit(src[2])) {
|
|
|
|
char hex[3] = {src[1], src[2], '\0'};
|
|
|
|
char hex[3] = {src[1], src[2], '\0'};
|
|
|
|
unsigned char c = (unsigned char)strtol(hex, NULL, 16);
|
|
|
|
unsigned char c = (unsigned char)strtol(hex, NULL, 16);
|
|
|
@@ -526,7 +480,6 @@ void url_decode_enhanced(const char *src, char *dst, size_t dst_size) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
src += 3;
|
|
|
|
src += 3;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// Невалидный % — просто пропускаем
|
|
|
|
|
|
|
|
src++;
|
|
|
|
src++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (*src == '+') {
|
|
|
|
} else if (*src == '+') {
|
|
|
@@ -541,6 +494,7 @@ void url_decode_enhanced(const char *src, char *dst, size_t dst_size) {
|
|
|
|
*p = '\0';
|
|
|
|
*p = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// === ОТРИСОВКА ===
|
|
|
|
void print_breadcrumb(buffer_t *buf, const char *display_path) {
|
|
|
|
void print_breadcrumb(buffer_t *buf, const char *display_path) {
|
|
|
|
buffer_append(buf, "<div class=\"breadcrumb\">\n");
|
|
|
|
buffer_append(buf, "<div class=\"breadcrumb\">\n");
|
|
|
|
buffer_append(buf, " <a href=\"/cgi-bin/index.cgi\">🏠 Главная</a>");
|
|
|
|
buffer_append(buf, " <a href=\"/cgi-bin/index.cgi\">🏠 Главная</a>");
|
|
|
@@ -554,9 +508,9 @@ void print_breadcrumb(buffer_t *buf, const char *display_path) {
|
|
|
|
if (temp_path[0]) strcat(temp_path, "/");
|
|
|
|
if (temp_path[0]) strcat(temp_path, "/");
|
|
|
|
strcat(temp_path, token);
|
|
|
|
strcat(temp_path, token);
|
|
|
|
url_encode(temp_path, encoded, sizeof(encoded));
|
|
|
|
url_encode(temp_path, encoded, sizeof(encoded));
|
|
|
|
char link[4096];
|
|
|
|
|
|
|
|
char safe_token[512];
|
|
|
|
char safe_token[512];
|
|
|
|
html_escape(token, safe_token, sizeof(safe_token));
|
|
|
|
html_escape(token, safe_token, sizeof(safe_token));
|
|
|
|
|
|
|
|
char link[4096];
|
|
|
|
snprintf(link, sizeof(link), "<a href=\"/cgi-bin/index.cgi?path=%s\">%s</a>", encoded, safe_token);
|
|
|
|
snprintf(link, sizeof(link), "<a href=\"/cgi-bin/index.cgi?path=%s\">%s</a>", encoded, safe_token);
|
|
|
|
buffer_append(buf, link);
|
|
|
|
buffer_append(buf, link);
|
|
|
|
token = strtok(NULL, "/");
|
|
|
|
token = strtok(NULL, "/");
|
|
|
@@ -601,24 +555,19 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
struct dirent *entry;
|
|
|
|
struct dirent *entry;
|
|
|
|
struct stat file_stat;
|
|
|
|
struct stat file_stat;
|
|
|
|
char full_path[1024];
|
|
|
|
char full_path[1024];
|
|
|
|
|
|
|
|
|
|
|
|
entry_t *entries = malloc(sizeof(entry_t) * g_config.max_entries);
|
|
|
|
entry_t *entries = malloc(sizeof(entry_t) * g_config.max_entries);
|
|
|
|
if (!entries) {
|
|
|
|
if (!entries) {
|
|
|
|
buffer_append(buf, "<div class=\"error\">Ошибка выделения памяти</div>");
|
|
|
|
buffer_append(buf, "<div class=\"error\">Ошибка выделения памяти</div>");
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int entry_count = 0, dir_count = 0, file_count = 0;
|
|
|
|
int entry_count = 0;
|
|
|
|
|
|
|
|
int dir_count = 0, file_count = 0;
|
|
|
|
|
|
|
|
long long total_size = 0;
|
|
|
|
long long total_size = 0;
|
|
|
|
int is_recursive_search = 0;
|
|
|
|
int is_recursive_search = 0;
|
|
|
|
|
|
|
|
|
|
|
|
char folder_name[256] = "";
|
|
|
|
char folder_name[256] = "";
|
|
|
|
if (display_path && display_path[0]) {
|
|
|
|
if (display_path && display_path[0]) {
|
|
|
|
char *last_slash = strrchr(display_path, '/');
|
|
|
|
char *last_slash = strrchr(display_path, '/');
|
|
|
|
if (last_slash) strcpy(folder_name, last_slash + 1);
|
|
|
|
if (last_slash) strcpy(folder_name, last_slash + 1);
|
|
|
|
else strcpy(folder_name, display_path);
|
|
|
|
else strcpy(folder_name, display_path);
|
|
|
|
|
|
|
|
|
|
|
|
char *query_string = getenv("QUERY_STRING");
|
|
|
|
char *query_string = getenv("QUERY_STRING");
|
|
|
|
char *password = NULL;
|
|
|
|
char *password = NULL;
|
|
|
|
if (query_string) {
|
|
|
|
if (query_string) {
|
|
|
@@ -637,7 +586,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (is_folder_protected(folder_name)) {
|
|
|
|
if (is_folder_protected(folder_name)) {
|
|
|
|
if (!password || !check_folder_password(folder_name, password)) {
|
|
|
|
if (!password || !check_folder_password(folder_name, password)) {
|
|
|
|
print_password_form(buf, folder_name);
|
|
|
|
print_password_form(buf, folder_name);
|
|
|
@@ -646,12 +594,9 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === ЕСЛИ ЕСТЬ ПОИСК — ИСПОЛЬЗУЕМ РЕКУРСИВНЫЙ ОБХОД ===
|
|
|
|
|
|
|
|
if (g_params.search[0] != '\0') {
|
|
|
|
if (g_params.search[0] != '\0') {
|
|
|
|
int result_count = 0;
|
|
|
|
int result_count = 0;
|
|
|
|
search_recursive(g_config.base_path, "", g_params.search, entries, &result_count, g_config.max_entries);
|
|
|
|
search_recursive(g_config.base_path, "", g_params.search, entries, &result_count, g_config.max_entries);
|
|
|
|
|
|
|
|
|
|
|
|
if (result_count == 0) {
|
|
|
|
if (result_count == 0) {
|
|
|
|
buffer_append(buf, "<div class=\"empty-state\">\n");
|
|
|
|
buffer_append(buf, "<div class=\"empty-state\">\n");
|
|
|
|
buffer_append(buf, " <div class=\"icon\">🔍</div>\n");
|
|
|
|
buffer_append(buf, " <div class=\"icon\">🔍</div>\n");
|
|
|
@@ -665,10 +610,8 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
free(entries);
|
|
|
|
free(entries);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
entry_count = result_count;
|
|
|
|
entry_count = result_count;
|
|
|
|
is_recursive_search = 1;
|
|
|
|
is_recursive_search = 1;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
if (entries[i].is_dir) {
|
|
|
|
if (entries[i].is_dir) {
|
|
|
|
dir_count++;
|
|
|
|
dir_count++;
|
|
|
@@ -679,7 +622,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// === ОБЫЧНЫЙ ОБХОД ТЕКУЩЕЙ ПАПКИ ===
|
|
|
|
|
|
|
|
dir = opendir(base_path);
|
|
|
|
dir = opendir(base_path);
|
|
|
|
if (!dir) {
|
|
|
|
if (!dir) {
|
|
|
|
char alt_path[1024];
|
|
|
|
char alt_path[1024];
|
|
|
@@ -705,10 +647,8 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL && entry_count < g_config.max_entries) {
|
|
|
|
while ((entry = readdir(dir)) != NULL && entry_count < g_config.max_entries) {
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
|
|
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
|
|
|
|
|
|
|
|
|
|
|
int hidden = 0;
|
|
|
|
int hidden = 0;
|
|
|
|
for (int i = 0; i < g_config.hidden_dirs_count; i++) {
|
|
|
|
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; }
|
|
|
|
if (strcmp(entry->d_name, g_config.hidden_dirs[i]) == 0) { hidden = 1; break; }
|
|
|
@@ -718,12 +658,10 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (entry->d_name[0] == '.') hidden = 1;
|
|
|
|
if (entry->d_name[0] == '.') hidden = 1;
|
|
|
|
if (hidden) continue;
|
|
|
|
if (hidden) continue;
|
|
|
|
|
|
|
|
|
|
|
|
snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name);
|
|
|
|
snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name);
|
|
|
|
if (stat(full_path, &file_stat) == 0) {
|
|
|
|
if (stat(full_path, &file_stat) == 0) {
|
|
|
|
strncpy(entries[entry_count].name, entry->d_name, sizeof(entries[0].name) - 1);
|
|
|
|
strncpy(entries[entry_count].name, entry->d_name, sizeof(entries[0].name) - 1);
|
|
|
|
entries[entry_count].name[sizeof(entries[0].name) - 1] = '\0';
|
|
|
|
entries[entry_count].name[sizeof(entries[0].name) - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
if (S_ISDIR(file_stat.st_mode)) {
|
|
|
|
if (S_ISDIR(file_stat.st_mode)) {
|
|
|
|
entries[entry_count].is_dir = 1;
|
|
|
|
entries[entry_count].is_dir = 1;
|
|
|
|
entries[entry_count].size = 0;
|
|
|
|
entries[entry_count].size = 0;
|
|
|
@@ -731,7 +669,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
entries[entry_count].file_count = count_files_in_dir(full_path);
|
|
|
|
entries[entry_count].file_count = count_files_in_dir(full_path);
|
|
|
|
entries[entry_count].dir_size = get_dir_size(full_path);
|
|
|
|
entries[entry_count].dir_size = get_dir_size(full_path);
|
|
|
|
total_size += entries[entry_count].dir_size;
|
|
|
|
total_size += entries[entry_count].dir_size;
|
|
|
|
|
|
|
|
|
|
|
|
if (is_folder_protected(entry->d_name)) {
|
|
|
|
if (is_folder_protected(entry->d_name)) {
|
|
|
|
strcpy(entries[entry_count].icon, "🔒");
|
|
|
|
strcpy(entries[entry_count].icon, "🔒");
|
|
|
|
entries[entry_count].is_protected = 1;
|
|
|
|
entries[entry_count].is_protected = 1;
|
|
|
@@ -739,7 +676,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
strcpy(entries[entry_count].icon, "📁");
|
|
|
|
strcpy(entries[entry_count].icon, "📁");
|
|
|
|
entries[entry_count].is_protected = 0;
|
|
|
|
entries[entry_count].is_protected = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char new_path[2048];
|
|
|
|
char new_path[2048];
|
|
|
|
snprintf(new_path, sizeof(new_path), "%s%s%s", display_path, display_path[0] ? "/" : "", entries[entry_count].name);
|
|
|
|
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));
|
|
|
|
url_encode(new_path, entries[entry_count].encoded_path, sizeof(entries[0].encoded_path));
|
|
|
@@ -752,11 +688,9 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
entries[entry_count].dir_size = 0;
|
|
|
|
entries[entry_count].dir_size = 0;
|
|
|
|
strcpy(entries[entry_count].icon, get_file_icon(entry->d_name));
|
|
|
|
strcpy(entries[entry_count].icon, get_file_icon(entry->d_name));
|
|
|
|
entries[entry_count].is_protected = 0;
|
|
|
|
entries[entry_count].is_protected = 0;
|
|
|
|
|
|
|
|
|
|
|
|
char encoded_filename[1024];
|
|
|
|
char encoded_filename[1024];
|
|
|
|
url_encode(entry->d_name, encoded_filename, sizeof(encoded_filename));
|
|
|
|
url_encode(entry->d_name, encoded_filename, sizeof(encoded_filename));
|
|
|
|
snprintf(entries[entry_count].file_url, sizeof(entries[0].file_url), "%s%s%s", display_path, display_path[0] ? "/" : "", encoded_filename);
|
|
|
|
snprintf(entries[entry_count].file_url, sizeof(entries[0].file_url), "%s%s%s", display_path, display_path[0] ? "/" : "", encoded_filename);
|
|
|
|
|
|
|
|
|
|
|
|
total_size += file_stat.st_size;
|
|
|
|
total_size += file_stat.st_size;
|
|
|
|
file_count++;
|
|
|
|
file_count++;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
@@ -766,7 +700,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
closedir(dir);
|
|
|
|
|
|
|
|
|
|
|
|
if (entry_count == 0) {
|
|
|
|
if (entry_count == 0) {
|
|
|
|
buffer_append(buf, "<div class=\"empty-state\">\n");
|
|
|
|
buffer_append(buf, "<div class=\"empty-state\">\n");
|
|
|
|
buffer_append(buf, " <div class=\"icon\">📄</div>\n");
|
|
|
|
buffer_append(buf, " <div class=\"icon\">📄</div>\n");
|
|
|
@@ -777,15 +710,11 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qsort(entries, entry_count, sizeof(entry_t), compare_entries_sorted);
|
|
|
|
qsort(entries, entry_count, sizeof(entry_t), compare_entries_sorted);
|
|
|
|
|
|
|
|
|
|
|
|
// === ТУЛБАР (поиск + сортировка) ===
|
|
|
|
|
|
|
|
char encoded_path[1024];
|
|
|
|
char encoded_path[1024];
|
|
|
|
url_encode(display_path, encoded_path, sizeof(encoded_path));
|
|
|
|
url_encode(display_path, encoded_path, sizeof(encoded_path));
|
|
|
|
char safe_search[512];
|
|
|
|
char safe_search[512];
|
|
|
|
html_escape(g_params.search, safe_search, sizeof(safe_search));
|
|
|
|
html_escape(g_params.search, safe_search, sizeof(safe_search));
|
|
|
|
|
|
|
|
|
|
|
|
buffer_append(buf, "<div class=\"toolbar\">\n");
|
|
|
|
buffer_append(buf, "<div class=\"toolbar\">\n");
|
|
|
|
buffer_append(buf, " <div class=\"search-box\">\n");
|
|
|
|
buffer_append(buf, " <div class=\"search-box\">\n");
|
|
|
|
buffer_append(buf, " <form method=\"GET\" action=\"/cgi-bin/index.cgi\">\n");
|
|
|
|
buffer_append(buf, " <form method=\"GET\" action=\"/cgi-bin/index.cgi\">\n");
|
|
|
@@ -817,8 +746,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
buffer_append(buf, "\">📅 Дата</a>\n");
|
|
|
|
buffer_append(buf, "\">📅 Дата</a>\n");
|
|
|
|
buffer_append(buf, " </div>\n");
|
|
|
|
buffer_append(buf, " </div>\n");
|
|
|
|
buffer_append(buf, "</div>\n");
|
|
|
|
buffer_append(buf, "</div>\n");
|
|
|
|
|
|
|
|
|
|
|
|
// === ВЫВОД РЕЗУЛЬТАТОВ ===
|
|
|
|
|
|
|
|
if (is_recursive_search) {
|
|
|
|
if (is_recursive_search) {
|
|
|
|
buffer_append(buf, "<div style=\"font-size:0.85rem;color:var(--text-muted);margin-bottom:0.5rem;padding:0.25rem 0.5rem;\">");
|
|
|
|
buffer_append(buf, "<div style=\"font-size:0.85rem;color:var(--text-muted);margin-bottom:0.5rem;padding:0.25rem 0.5rem;\">");
|
|
|
|
buffer_append(buf, "🔍 Результаты поиска по всему сайту: ");
|
|
|
|
buffer_append(buf, "🔍 Результаты поиска по всему сайту: ");
|
|
|
@@ -827,24 +754,16 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
buffer_append(buf, tmp);
|
|
|
|
buffer_append(buf, tmp);
|
|
|
|
buffer_append(buf, " файлов/папок найдено</div>\n");
|
|
|
|
buffer_append(buf, " файлов/папок найдено</div>\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
buffer_append(buf, "<ul>\n");
|
|
|
|
buffer_append(buf, "<ul>\n");
|
|
|
|
|
|
|
|
|
|
|
|
// Вывод папок
|
|
|
|
// Вывод папок
|
|
|
|
int has_dirs = 0;
|
|
|
|
int has_dirs = 0;
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
for (int i = 0; i < entry_count; i++) if (entries[i].is_dir) { has_dirs = 1; break; }
|
|
|
|
if (entries[i].is_dir) { has_dirs = 1; break; }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (has_dirs) {
|
|
|
|
if (has_dirs) {
|
|
|
|
int display_dir_count = 0;
|
|
|
|
int display_dir_count = 0;
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
for (int i = 0; i < entry_count; i++) if (entries[i].is_dir) display_dir_count++;
|
|
|
|
if (entries[i].is_dir) display_dir_count++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
char section_title[128];
|
|
|
|
char section_title[128];
|
|
|
|
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📁 Папки (%d)</li>\n", display_dir_count);
|
|
|
|
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📁 Папки (%d)</li>\n", display_dir_count);
|
|
|
|
buffer_append(buf, section_title);
|
|
|
|
buffer_append(buf, section_title);
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
if (entries[i].is_dir) {
|
|
|
|
if (entries[i].is_dir) {
|
|
|
|
buffer_append(buf, " <li>\n");
|
|
|
|
buffer_append(buf, " <li>\n");
|
|
|
@@ -900,17 +819,20 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
html_escape(entries[i].name, safe_name4, sizeof(safe_name4));
|
|
|
|
html_escape(entries[i].name, safe_name4, sizeof(safe_name4));
|
|
|
|
buffer_append(buf, safe_name4);
|
|
|
|
buffer_append(buf, safe_name4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (entries[i].file_count > 0 && !is_recursive_search) {
|
|
|
|
|
|
|
|
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, "</span>\n");
|
|
|
|
buffer_append(buf, " </a>\n");
|
|
|
|
buffer_append(buf, " </a>\n");
|
|
|
|
buffer_append(buf, " <div class=\"file-controls\">\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, " <button class=\"copy-btn\" onclick=\"copyLink('/cgi-bin/index.cgi?path=");
|
|
|
|
buffer_append(buf, entries[i].encoded_path);
|
|
|
|
buffer_append(buf, entries[i].encoded_path);
|
|
|
|
buffer_append(buf, "')\" title=\"Копировать ссылку на папку\">🔗</button>\n");
|
|
|
|
buffer_append(buf, "')\" title=\"Копировать ссылку на папку\">🔗</button>\n");
|
|
|
|
|
|
|
|
if (entries[i].dir_size > 0) {
|
|
|
|
|
|
|
|
char size_str[32];
|
|
|
|
|
|
|
|
format_size(entries[i].dir_size, size_str);
|
|
|
|
|
|
|
|
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, " </div>\n");
|
|
|
|
buffer_append(buf, " </div>\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -918,39 +840,29 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Вывод файлов
|
|
|
|
// Вывод файлов
|
|
|
|
int has_files = 0;
|
|
|
|
int has_files = 0;
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
for (int i = 0; i < entry_count; i++) if (!entries[i].is_dir) { has_files = 1; break; }
|
|
|
|
if (!entries[i].is_dir) { has_files = 1; break; }
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (has_files) {
|
|
|
|
if (has_files) {
|
|
|
|
int display_file_count = 0;
|
|
|
|
int display_file_count = 0;
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
for (int i = 0; i < entry_count; i++) if (!entries[i].is_dir) display_file_count++;
|
|
|
|
if (!entries[i].is_dir) display_file_count++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
char section_title[128];
|
|
|
|
char section_title[128];
|
|
|
|
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📄 Файлы (%d)</li>\n", display_file_count);
|
|
|
|
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📄 Файлы (%d)</li>\n", display_file_count);
|
|
|
|
buffer_append(buf, section_title);
|
|
|
|
buffer_append(buf, section_title);
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
for (int i = 0; i < entry_count; i++) {
|
|
|
|
if (!entries[i].is_dir) {
|
|
|
|
if (!entries[i].is_dir) {
|
|
|
|
char size_str[32];
|
|
|
|
char size_str[32];
|
|
|
|
char date_str[64];
|
|
|
|
char date_str[64];
|
|
|
|
format_size(entries[i].size, size_str);
|
|
|
|
format_size(entries[i].size, size_str);
|
|
|
|
format_date(entries[i].mtime, date_str, sizeof(date_str));
|
|
|
|
format_date(entries[i].mtime, date_str, sizeof(date_str));
|
|
|
|
|
|
|
|
|
|
|
|
buffer_append(buf, " <li>\n");
|
|
|
|
buffer_append(buf, " <li>\n");
|
|
|
|
buffer_append(buf, " <div class=\"file-row\">\n");
|
|
|
|
buffer_append(buf, " <div class=\"file-row\">\n");
|
|
|
|
|
|
|
|
|
|
|
|
char file_link[2048];
|
|
|
|
char file_link[2048];
|
|
|
|
if (is_recursive_search) {
|
|
|
|
if (is_recursive_search) {
|
|
|
|
snprintf(file_link, sizeof(file_link), "/%s", entries[i].file_url);
|
|
|
|
snprintf(file_link, sizeof(file_link), "/%s", entries[i].file_url);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
snprintf(file_link, sizeof(file_link), "/%s", entries[i].file_url);
|
|
|
|
snprintf(file_link, sizeof(file_link), "/%s", entries[i].file_url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
buffer_append(buf, " <a class=\"file-link\" href=\"");
|
|
|
|
buffer_append(buf, " <a class=\"file-link\" href=\"");
|
|
|
|
buffer_append(buf, file_link);
|
|
|
|
buffer_append(buf, file_link);
|
|
|
|
buffer_append(buf, "\" download>\n");
|
|
|
|
buffer_append(buf, "\" download>\n");
|
|
|
@@ -969,7 +881,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buffer_append(buf, "</span>\n");
|
|
|
|
buffer_append(buf, "</span>\n");
|
|
|
|
buffer_append(buf, " </a>\n");
|
|
|
|
buffer_append(buf, " </a>\n");
|
|
|
|
|
|
|
|
|
|
|
|
buffer_append(buf, " <div class=\"file-controls\">\n");
|
|
|
|
buffer_append(buf, " <div class=\"file-controls\">\n");
|
|
|
|
if (is_previewable(entries[i].name)) {
|
|
|
|
if (is_previewable(entries[i].name)) {
|
|
|
|
buffer_append(buf, " <button class=\"preview-btn\" onclick=\"openPreview('");
|
|
|
|
buffer_append(buf, " <button class=\"preview-btn\" onclick=\"openPreview('");
|
|
|
@@ -982,7 +893,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
buffer_append(buf, " <button class=\"copy-btn\" onclick=\"copyLink('");
|
|
|
|
buffer_append(buf, " <button class=\"copy-btn\" onclick=\"copyLink('");
|
|
|
|
buffer_append(buf, file_link);
|
|
|
|
buffer_append(buf, file_link);
|
|
|
|
buffer_append(buf, "')\" title=\"Копировать ссылку на файл\">🔗</button>\n");
|
|
|
|
buffer_append(buf, "')\" title=\"Копировать ссылку на файл\">🔗</button>\n");
|
|
|
|
|
|
|
|
|
|
|
|
buffer_append(buf, " <span class=\"file-meta date\">");
|
|
|
|
buffer_append(buf, " <span class=\"file-meta date\">");
|
|
|
|
buffer_append(buf, date_str);
|
|
|
|
buffer_append(buf, date_str);
|
|
|
|
buffer_append(buf, "</span>\n");
|
|
|
|
buffer_append(buf, "</span>\n");
|
|
|
@@ -995,10 +905,7 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
buffer_append(buf, "</ul>\n");
|
|
|
|
buffer_append(buf, "</ul>\n");
|
|
|
|
|
|
|
|
|
|
|
|
// Статистика
|
|
|
|
|
|
|
|
if (is_recursive_search) {
|
|
|
|
if (is_recursive_search) {
|
|
|
|
char stats[256];
|
|
|
|
char stats[256];
|
|
|
|
snprintf(stats, sizeof(stats),
|
|
|
|
snprintf(stats, sizeof(stats),
|
|
|
@@ -1014,7 +921,6 @@ void print_content(buffer_t *buf, const char *base_path, const char *display_pat
|
|
|
|
dir_count, file_count, total_size_str);
|
|
|
|
dir_count, file_count, total_size_str);
|
|
|
|
buffer_append(buf, stats);
|
|
|
|
buffer_append(buf, stats);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
free(entries);
|
|
|
|
free(entries);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@@ -1024,10 +930,8 @@ void print_template(const char *base_path, const char *display_path) {
|
|
|
|
printf("Error: Cannot read template\n");
|
|
|
|
printf("Error: Cannot read template\n");
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
buffer_t output;
|
|
|
|
buffer_t output;
|
|
|
|
buffer_init(&output);
|
|
|
|
buffer_init(&output);
|
|
|
|
|
|
|
|
|
|
|
|
char line[4096];
|
|
|
|
char line[4096];
|
|
|
|
while (fgets(line, sizeof(line), file)) {
|
|
|
|
while (fgets(line, sizeof(line), file)) {
|
|
|
|
if (strstr(line, "<!--BREADCRUMB-->")) {
|
|
|
|
if (strstr(line, "<!--BREADCRUMB-->")) {
|
|
|
@@ -1039,7 +943,6 @@ void print_template(const char *base_path, const char *display_path) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fclose(file);
|
|
|
|
fclose(file);
|
|
|
|
|
|
|
|
|
|
|
|
fwrite(output.data, 1, output.size, stdout);
|
|
|
|
fwrite(output.data, 1, output.size, stdout);
|
|
|
|
buffer_free(&output);
|
|
|
|
buffer_free(&output);
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -1047,26 +950,19 @@ void print_template(const char *base_path, const char *display_path) {
|
|
|
|
int main() {
|
|
|
|
int main() {
|
|
|
|
setlocale(LC_ALL, "en_US.UTF-8");
|
|
|
|
setlocale(LC_ALL, "en_US.UTF-8");
|
|
|
|
setlocale(LC_CTYPE, "en_US.UTF-8");
|
|
|
|
setlocale(LC_CTYPE, "en_US.UTF-8");
|
|
|
|
|
|
|
|
|
|
|
|
// Загружаем конфиг
|
|
|
|
|
|
|
|
if (load_config("/home/romkazvo/www/cgi-bin/config.ini", &g_config) != 0) {
|
|
|
|
if (load_config("/home/romkazvo/www/cgi-bin/config.ini", &g_config) != 0) {
|
|
|
|
set_default_config(&g_config);
|
|
|
|
set_default_config(&g_config);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === ПАРСИМ ПАРАМЕТРЫ ЗАПРОСА ===
|
|
|
|
|
|
|
|
strcpy(g_params.sort_by, "name");
|
|
|
|
strcpy(g_params.sort_by, "name");
|
|
|
|
g_params.sort_order = 1;
|
|
|
|
g_params.sort_order = 1;
|
|
|
|
g_params.search[0] = '\0';
|
|
|
|
g_params.search[0] = '\0';
|
|
|
|
g_params.recursive = 0;
|
|
|
|
g_params.recursive = 0;
|
|
|
|
|
|
|
|
|
|
|
|
char *query_string = getenv("QUERY_STRING");
|
|
|
|
char *query_string = getenv("QUERY_STRING");
|
|
|
|
char display_path[1024] = "";
|
|
|
|
char display_path[1024] = "";
|
|
|
|
char safe_display_path[1024] = "";
|
|
|
|
char safe_display_path[1024] = "";
|
|
|
|
char base_path[1024];
|
|
|
|
char base_path[1024];
|
|
|
|
strcpy(base_path, g_config.base_path);
|
|
|
|
strcpy(base_path, g_config.base_path);
|
|
|
|
|
|
|
|
|
|
|
|
if (query_string) {
|
|
|
|
if (query_string) {
|
|
|
|
// === ПАРСИМ PATH ===
|
|
|
|
|
|
|
|
char *path_start = strstr(query_string, "path=");
|
|
|
|
char *path_start = strstr(query_string, "path=");
|
|
|
|
if (path_start) {
|
|
|
|
if (path_start) {
|
|
|
|
path_start += 5;
|
|
|
|
path_start += 5;
|
|
|
@@ -1087,8 +983,6 @@ int main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === ПАРСИМ SORT ===
|
|
|
|
|
|
|
|
char *sort_start = strstr(query_string, "sort=");
|
|
|
|
char *sort_start = strstr(query_string, "sort=");
|
|
|
|
if (sort_start) {
|
|
|
|
if (sort_start) {
|
|
|
|
sort_start += 5;
|
|
|
|
sort_start += 5;
|
|
|
@@ -1099,8 +993,6 @@ int main() {
|
|
|
|
g_params.sort_by[sort_len] = '\0';
|
|
|
|
g_params.sort_by[sort_len] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === ПАРСИМ SEARCH (отдельно от path!) ===
|
|
|
|
|
|
|
|
char *search_start = strstr(query_string, "search=");
|
|
|
|
char *search_start = strstr(query_string, "search=");
|
|
|
|
if (search_start) {
|
|
|
|
if (search_start) {
|
|
|
|
search_start += 7;
|
|
|
|
search_start += 7;
|
|
|
@@ -1116,8 +1008,6 @@ int main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// === ОБРАБОТКА ПАРОЛЯ ===
|
|
|
|
|
|
|
|
if (strstr(query_string, "password=")) {
|
|
|
|
if (strstr(query_string, "password=")) {
|
|
|
|
char *error = strstr(query_string, "error=1");
|
|
|
|
char *error = strstr(query_string, "error=1");
|
|
|
|
if (!error) {
|
|
|
|
if (!error) {
|
|
|
@@ -1165,9 +1055,7 @@ int main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
printf("Content-type: text/html; charset=utf-8\n\n");
|
|
|
|
printf("Content-type: text/html; charset=utf-8\n\n");
|
|
|
|
|
|
|
|
|
|
|
|
print_template(base_path, display_path);
|
|
|
|
print_template(base_path, display_path);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|