Files
Trashbox/index.c

497 lines
18 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
typedef struct {
char name[256];
int is_dir;
long size;
char icon[8];
char encoded_path[1024];
char file_url[1024];
} 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);
}
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;
// PDF
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_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;
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;
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;
strcpy(entries[entry_count].icon, "📁");
// Предварительно кодируем путь для папок
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));
// Предварительно формируем URL для файлов
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");
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");
}
}
}
// Вывод файлов с предварительно сформированными URL
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);
// Один быстрый вывод вместо множества printf
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);
}
}
}
print_template(base_path, display_path);
return 0;
}