Files
Trashbox/index.c

689 lines
26 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// /home/romkazvo/www/cgi-bin/index.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <locale.h>
#define MAX_ENTRIES 1000
#define TEMPLATE_PATH "/home/romkazvo/www/cgi-bin/template.html"
#define OUTPUT_BUFFER_SIZE 65536
#define PASSWD_FILE "/home/romkazvo/www/cgi-bin/.htpasswd"
typedef struct {
char name[256];
int is_dir;
long size;
char icon[8];
char encoded_path[1024];
char file_url[1024];
int is_protected;
} entry_t;
// Быстрый буферизированный вывод
typedef struct {
char *data;
size_t size;
size_t capacity;
} buffer_t;
void buffer_init(buffer_t *buf) {
buf->capacity = OUTPUT_BUFFER_SIZE;
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_append_size(buffer_t *buf, const char *str, size_t len) {
if (buf->size + len >= buf->capacity) {
buf->capacity *= 2;
buf->data = realloc(buf->data, buf->capacity);
}
memcpy(buf->data + buf->size, str, len);
buf->size += len;
}
void buffer_free(buffer_t *buf) {
free(buf->data);
}
// Функция проверки пароля для папки
int check_folder_password(const char *folder_name, const char *password) {
FILE *f = fopen(PASSWD_FILE, "r");
if (!f) return 1; // Если нет файла с паролями - доступ разрешен
char line[512];
while (fgets(line, sizeof(line), f)) {
// Убираем перевод строки
line[strcspn(line, "\r\n")] = 0;
char *colon = strchr(line, ':');
if (!colon) continue;
*colon = '\0';
char *folder = line;
char *pass = colon + 1;
if (strcmp(folder, folder_name) == 0) {
fclose(f);
return strcmp(pass, password) == 0;
}
}
fclose(f);
return 1; // Папка не найдена в списке - доступ разрешен
}
// Проверяет, защищена ли папка паролем
int is_folder_protected(const char *folder_name) {
FILE *f = fopen(PASSWD_FILE, "r");
if (!f) return 0;
char line[512];
while (fgets(line, sizeof(line), f)) {
line[strcspn(line, "\r\n")] = 0;
char *colon = strchr(line, ':');
if (!colon) continue;
*colon = '\0';
if (strcmp(line, folder_name) == 0) {
fclose(f);
return 1;
}
}
fclose(f);
return 0;
}
const char* get_file_icon(const char* filename) {
const char *ext = strrchr(filename, '.');
if (!ext) return "📄";
ext++;
if (strcasecmp(ext, "jpg") == 0 || strcasecmp(ext, "jpeg") == 0 ||
strcasecmp(ext, "png") == 0 || strcasecmp(ext, "gif") == 0 ||
strcasecmp(ext, "webp") == 0 || strcasecmp(ext, "bmp") == 0)
return "🖼️";
if (strcasecmp(ext, "mp3") == 0 || strcasecmp(ext, "wav") == 0 ||
strcasecmp(ext, "flac") == 0 || strcasecmp(ext, "ogg") == 0)
return "🎵";
if (strcasecmp(ext, "mp4") == 0 || strcasecmp(ext, "avi") == 0 ||
strcasecmp(ext, "mkv") == 0 || strcasecmp(ext, "mov") == 0)
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++;
if (strcasecmp(ext, "jpg") == 0 || strcasecmp(ext, "jpeg") == 0 ||
strcasecmp(ext, "png") == 0 || strcasecmp(ext, "gif") == 0 ||
strcasecmp(ext, "webp") == 0 || strcasecmp(ext, "bmp") == 0 ||
strcasecmp(ext, "svg") == 0 || strcasecmp(ext, "ico") == 0)
return 1;
if (strcasecmp(ext, "txt") == 0 || strcasecmp(ext, "md") == 0 ||
strcasecmp(ext, "html") == 0 || strcasecmp(ext, "htm") == 0 ||
strcasecmp(ext, "css") == 0 || strcasecmp(ext, "js") == 0 ||
strcasecmp(ext, "json") == 0 || strcasecmp(ext, "xml") == 0 ||
strcasecmp(ext, "csv") == 0)
return 1;
if (strcasecmp(ext, "pdf") == 0)
return 1;
if (strcasecmp(ext, "mp3") == 0 || strcasecmp(ext, "wav") == 0 ||
strcasecmp(ext, "ogg") == 0 || strcasecmp(ext, "flac") == 0 ||
strcasecmp(ext, "m4a") == 0 || strcasecmp(ext, "aac") == 0)
return 1;
if (strcasecmp(ext, "mp4") == 0 || strcasecmp(ext, "webm") == 0 ||
strcasecmp(ext, "ogv") == 0 || strcasecmp(ext, "mov") == 0 ||
strcasecmp(ext, "avi") == 0 || strcasecmp(ext, "mkv") == 0)
return 1;
return 0;
}
int compare_entries(const void *a, const void *b) {
const entry_t *entryA = (const entry_t *)a;
const entry_t *entryB = (const entry_t *)b;
if (entryA->is_dir && !entryB->is_dir) return -1;
if (!entryA->is_dir && entryB->is_dir) return 1;
return strcasecmp(entryA->name, entryB->name);
}
void format_size(long size, char* buffer) {
if (size < 1024) {
snprintf(buffer, 32, "%ld 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 url_encode(const char *src, char *dst, size_t dst_size) {
static const char *hex = "0123456789ABCDEF";
char *p = dst;
size_t i = 0;
while (*src && i < dst_size - 3) {
unsigned char c = (unsigned char)*src;
if ((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
strchr("-_.~", c)) {
*p++ = c;
i++;
}
else if (c == ' ') {
*p++ = '%';
*p++ = '2';
*p++ = '0';
i += 3;
}
else {
*p++ = '%';
*p++ = hex[(c >> 4) & 0xF];
*p++ = hex[c & 0xF];
i += 3;
}
src++;
}
*p = '\0';
}
void url_decode_enhanced(const char *src, char *dst, size_t dst_size) {
char *p = dst;
size_t decoded_len = 0;
while (*src && decoded_len < dst_size - 1) {
if (*src == '%') {
if (src[1] && src[2] && isxdigit(src[1]) && isxdigit(src[2])) {
char hex[3] = {src[1], src[2], '\0'};
unsigned char c = (unsigned char)strtol(hex, NULL, 16);
if (c >= 0x80) {
*p++ = c;
decoded_len++;
} else if (c >= 0x20 || c == 0x0A || c == 0x0D) {
*p++ = c;
decoded_len++;
} else {
*p++ = '_';
decoded_len++;
}
src += 3;
} else {
*p++ = *src++;
decoded_len++;
}
} else if (*src == '+') {
*p++ = ' ';
decoded_len++;
src++;
} else {
*p++ = *src++;
decoded_len++;
}
}
*p = '\0';
}
void safe_path_join(char *result, size_t result_size, const char *base, const char *path) {
snprintf(result, result_size, "%s/%s", base, path);
}
void print_breadcrumb(buffer_t *buf, const char *display_path) {
buffer_append(buf, "<div class=\"breadcrumb\">\n");
buffer_append(buf, " <a href=\"/cgi-bin/index.cgi\">🏠 Главная</a>");
if (display_path && display_path[0]) {
char temp_path[1024] = "";
char encoded[2048];
char *path_copy = strdup(display_path);
char *token = strtok(path_copy, "/");
while (token) {
buffer_append(buf, " / ");
if (temp_path[0]) strcat(temp_path, "/");
strcat(temp_path, token);
url_encode(temp_path, encoded, sizeof(encoded));
char link[4096];
snprintf(link, sizeof(link), "<a href=\"/cgi-bin/index.cgi?path=%s\">%s</a>", encoded, token);
buffer_append(buf, link);
token = strtok(NULL, "/");
}
free(path_copy);
}
buffer_append(buf, "\n</div>\n");
}
void print_password_form(buffer_t *buf, const char *folder_name) {
buffer_append(buf, "<div class=\"password-form-container\">\n");
buffer_append(buf, " <div class=\"password-form\">\n");
buffer_append(buf, " <div class=\"lock-icon\">🔒</div>\n");
buffer_append(buf, " <h2>Папка защищена паролем</h2>\n");
buffer_append(buf, " <p>Введите пароль для доступа к папке <strong>");
buffer_append(buf, folder_name);
buffer_append(buf, "</strong></p>\n");
buffer_append(buf, " <form method=\"GET\" action=\"/cgi-bin/index.cgi\">\n");
buffer_append(buf, " <input type=\"hidden\" name=\"path\" value=\"");
char encoded_path[1024];
url_encode(folder_name, encoded_path, sizeof(encoded_path));
buffer_append(buf, encoded_path);
buffer_append(buf, "\">\n");
buffer_append(buf, " <input type=\"password\" name=\"password\" placeholder=\"Введите пароль\" required>\n");
buffer_append(buf, " <button type=\"submit\">Войти</button>\n");
buffer_append(buf, " </form>\n");
buffer_append(buf, " <p class=\"error-msg\">");
// Проверяем, была ли ошибка ввода пароля
char *query_string = getenv("QUERY_STRING");
if (query_string && strstr(query_string, "error=1")) {
buffer_append(buf, "❌ Неверный пароль! Попробуйте снова.");
}
buffer_append(buf, "</p>\n");
buffer_append(buf, " <a href=\"/cgi-bin/index.cgi\" class=\"back-link\">← Вернуться на главную</a>\n");
buffer_append(buf, " </div>\n");
buffer_append(buf, "</div>\n");
}
void print_content(buffer_t *buf, const char *base_path, const char *display_path) {
DIR *dir;
struct dirent *entry;
struct stat file_stat;
char full_path[1024];
entry_t entries[MAX_ENTRIES];
int entry_count = 0;
int dir_count = 0, file_count = 0;
long long total_size = 0;
// Проверяем, не защищена ли текущая папка паролем
char folder_name[256] = "";
if (display_path && display_path[0]) {
char *last_slash = strrchr(display_path, '/');
if (last_slash) {
strcpy(folder_name, last_slash + 1);
} else {
strcpy(folder_name, display_path);
}
// Проверяем пароль
char *query_string = getenv("QUERY_STRING");
char *password = NULL;
if (query_string) {
char *pass_start = strstr(query_string, "password=");
if (pass_start) {
pass_start += 9;
char *pass_end = strchr(pass_start, '&');
int pass_len = pass_end ? pass_end - pass_start : strlen(pass_start);
if (pass_len > 0 && pass_len < 256) {
char pass_buf[256];
strncpy(pass_buf, pass_start, pass_len);
pass_buf[pass_len] = '\0';
// URL декодируем пароль
char decoded_pass[256];
url_decode_enhanced(pass_buf, decoded_pass, sizeof(decoded_pass));
password = decoded_pass;
}
}
}
if (is_folder_protected(folder_name)) {
if (!password || !check_folder_password(folder_name, password)) {
// Показываем форму ввода пароля
print_password_form(buf, folder_name);
return;
}
}
}
dir = opendir(base_path);
if (!dir) {
char alt_path[1024];
snprintf(alt_path, sizeof(alt_path), "/home/romkazvo/www");
if (display_path[0]) {
char *encoded = strdup(display_path);
url_decode_enhanced(encoded, alt_path + strlen(alt_path),
sizeof(alt_path) - strlen(alt_path));
free(encoded);
}
dir = opendir(alt_path);
if (!dir) {
buffer_append(buf, "<div class=\"empty-state\">\n");
buffer_append(buf, " <div class=\"icon\">📁</div>\n");
buffer_append(buf, " <h3>Ошибка открытия директории</h3>\n");
char error_msg[512];
snprintf(error_msg, sizeof(error_msg), " <p>Путь: %s</p>\n", base_path);
buffer_append(buf, error_msg);
snprintf(error_msg, sizeof(error_msg), " <p>Ошибка: %s</p>\n", strerror(errno));
buffer_append(buf, error_msg);
buffer_append(buf, " <p><a href=\"/cgi-bin/index.cgi\">← Вернуться на главную</a></p>\n");
buffer_append(buf, "</div>\n");
return;
}
}
// Предварительная обработка записей
while ((entry = readdir(dir)) != NULL && entry_count < MAX_ENTRIES) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
if (strcmp(entry->d_name, "cgi-bin") == 0)
continue;
// Скрываем файл с паролями (на всякий случай, если кто-то сможет получить доступ)
if (strcmp(entry->d_name, ".htpasswd") == 0)
continue;
if (strcmp(entry->d_name, ".htaccess") == 0)
continue;
// Скрываем все файлы, начинающиеся с точки
if (entry->d_name[0] == '.')
continue;
snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name);
if (stat(full_path, &file_stat) == 0) {
strncpy(entries[entry_count].name, entry->d_name, sizeof(entries[0].name) - 1);
entries[entry_count].name[sizeof(entries[0].name) - 1] = '\0';
if (S_ISDIR(file_stat.st_mode)) {
entries[entry_count].is_dir = 1;
entries[entry_count].size = 0;
// Проверяем, защищена ли папка
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;
strcpy(entries[entry_count].icon, get_file_icon(entry->d_name));
entries[entry_count].is_protected = 0;
snprintf(entries[entry_count].file_url, sizeof(entries[0].file_url),
"%s%s%s", display_path, display_path[0] ? "/" : "", entries[entry_count].name);
file_count++;
total_size += file_stat.st_size;
} else {
continue;
}
entry_count++;
}
}
closedir(dir);
if (entry_count == 0) {
buffer_append(buf, "<div class=\"empty-state\">\n");
buffer_append(buf, " <div class=\"icon\">📄</div>\n");
buffer_append(buf, " <h3>Здесь пусто</h3>\n");
buffer_append(buf, " <p>В этой директории нет файлов или папок</p>\n");
buffer_append(buf, "</div>\n");
return;
}
qsort(entries, entry_count, sizeof(entry_t), compare_entries);
buffer_append(buf, "<ul>\n");
// Вывод папок
if (dir_count > 0) {
char section_title[128];
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📁 Папки (%d)</li>\n", dir_count);
buffer_append(buf, section_title);
for (int i = 0; i < entry_count; i++) {
if (entries[i].is_dir) {
buffer_append(buf, " <li>\n");
if (entries[i].is_protected) {
// Защищенная папка
buffer_append(buf, " <div class=\"file-row\">\n");
buffer_append(buf, " <a class=\"dir-link protected-folder\" href=\"javascript:void(0)\" onclick=\"showPasswordForm('");
buffer_append(buf, entries[i].encoded_path);
buffer_append(buf, "', '");
buffer_append(buf, entries[i].name);
buffer_append(buf, "')\">\n");
buffer_append(buf, " <span class=\"file-icon\">🔒</span>\n");
buffer_append(buf, " <span class=\"file-name\">");
buffer_append(buf, entries[i].name);
buffer_append(buf, " <span style=\"font-size:0.7rem;color:var(--text-muted);\">(защищена)</span></span>\n");
buffer_append(buf, " </a>\n");
buffer_append(buf, " <div class=\"file-controls\">\n");
buffer_append(buf, " <button class=\"preview-btn\" onclick=\"showPasswordForm('");
buffer_append(buf, entries[i].encoded_path);
buffer_append(buf, "', '");
buffer_append(buf, entries[i].name);
buffer_append(buf, "')\" title=\"Ввести пароль\">🔑</button>\n");
buffer_append(buf, " </div>\n");
buffer_append(buf, " </div>\n");
} else {
// Обычная папка
char dir_link[512];
snprintf(dir_link, sizeof(dir_link),
" <a class=\"dir-link\" href=\"/cgi-bin/index.cgi?path=%s\">\n",
entries[i].encoded_path);
buffer_append(buf, dir_link);
buffer_append(buf, " <span class=\"file-icon\">");
buffer_append(buf, entries[i].icon);
buffer_append(buf, "</span>\n");
buffer_append(buf, " <span class=\"file-name\">");
buffer_append(buf, entries[i].name);
buffer_append(buf, "</span>\n");
buffer_append(buf, " </a>\n");
}
buffer_append(buf, " </li>\n");
}
}
}
// Вывод файлов
if (file_count > 0) {
char section_title[128];
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📄 Файлы (%d)</li>\n", file_count);
buffer_append(buf, section_title);
for (int i = 0; i < entry_count; i++) {
if (!entries[i].is_dir) {
char size_str[32];
format_size(entries[i].size, size_str);
buffer_append(buf, " <li>\n");
buffer_append(buf, " <div class=\"file-row\">\n");
buffer_append(buf, " <a class=\"file-link\" href=\"/");
buffer_append(buf, entries[i].file_url);
buffer_append(buf, "\" download>\n");
buffer_append(buf, " <span class=\"file-icon\">");
buffer_append(buf, entries[i].icon);
buffer_append(buf, "</span>\n");
buffer_append(buf, " <span class=\"file-name\">");
buffer_append(buf, entries[i].name);
buffer_append(buf, "</span>\n");
buffer_append(buf, " </a>\n");
buffer_append(buf, " <div class=\"file-controls\">\n");
if (is_previewable(entries[i].name)) {
buffer_append(buf, " <button class=\"preview-btn\" onclick=\"openPreview('/");
buffer_append(buf, entries[i].file_url);
buffer_append(buf, "')\" title=\"Открыть в браузере\">👁</button>\n");
}
buffer_append(buf, " <span class=\"size\">");
buffer_append(buf, size_str);
buffer_append(buf, "</span>\n");
buffer_append(buf, " </div>\n");
buffer_append(buf, " </div>\n");
buffer_append(buf, " </li>\n");
}
}
}
buffer_append(buf, "</ul>\n");
// Статистика
char total_size_str[32];
format_size(total_size, total_size_str);
char stats[256];
snprintf(stats, sizeof(stats),
"<div class=\"stats\">\n Папки: %d | Файлы: %d | Общий размер: %s\n</div>\n",
dir_count, file_count, total_size_str);
buffer_append(buf, stats);
}
void print_template(const char *base_path, const char *display_path) {
FILE *file = fopen(TEMPLATE_PATH, "r");
if (!file) {
printf("Error: Cannot read template\n");
return;
}
buffer_t output;
buffer_init(&output);
char line[4096];
while (fgets(line, sizeof(line), file)) {
if (strstr(line, "<!--BREADCRUMB-->")) {
print_breadcrumb(&output, display_path);
} else if (strstr(line, "<!--CONTENT-->")) {
print_content(&output, base_path, display_path);
} else {
buffer_append(&output, line);
}
}
fclose(file);
fwrite(output.data, 1, output.size, stdout);
buffer_free(&output);
}
int main() {
setlocale(LC_ALL, "en_US.UTF-8");
setlocale(LC_CTYPE, "en_US.UTF-8");
printf("Content-type: text/html; charset=utf-8\n\n");
char base_path[1024] = "/home/romkazvo/www";
char display_path[1024] = "";
char safe_display_path[1024] = "";
char *query_string = getenv("QUERY_STRING");
if (query_string) {
char *path_start = strstr(query_string, "path=");
if (path_start) {
path_start += 5;
char *path_end = strchr(path_start, '&');
int path_len = path_end ? path_end - path_start : strlen(path_start);
if (path_len > 0 && path_len < sizeof(display_path) - 1) {
char encoded_path[1024];
strncpy(encoded_path, path_start, path_len);
encoded_path[path_len] = '\0';
url_decode_enhanced(encoded_path, display_path, sizeof(display_path));
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), "/home/romkazvo/www", safe_display_path);
}
}
// Обработка неправильного пароля
if (strstr(query_string, "password=")) {
char *error = strstr(query_string, "error=1");
if (!error) {
// Проверяем пароль для текущей папки
char folder_name[256] = "";
if (display_path[0]) {
char *last_slash = strrchr(display_path, '/');
if (last_slash) {
strcpy(folder_name, last_slash + 1);
} else {
strcpy(folder_name, display_path);
}
if (is_folder_protected(folder_name)) {
char *pass_start = strstr(query_string, "password=");
if (pass_start) {
pass_start += 9;
char *pass_end = strchr(pass_start, '&');
int pass_len = pass_end ? pass_end - pass_start : strlen(pass_start);
if (pass_len > 0 && pass_len < 256) {
char pass_buf[256];
strncpy(pass_buf, pass_start, pass_len);
pass_buf[pass_len] = '\0';
char decoded_pass[256];
url_decode_enhanced(pass_buf, decoded_pass, sizeof(decoded_pass));
if (!check_folder_password(folder_name, decoded_pass)) {
// Неверный пароль - редирект с error=1
char encoded_path[1024];
url_encode(display_path, encoded_path, sizeof(encoded_path));
printf("Location: /cgi-bin/index.cgi?path=%s&error=1\n\n", encoded_path);
return 0;
}
}
}
}
}
}
}
}
print_template(base_path, display_path);
return 0;
}