// /home/romkazvo/www/cgi-bin/index.c // Основной файловый сервер #include #include #include #include #include #include #include #include #include #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 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, "
\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 link[4096]; snprintf(link, sizeof(link), "%s", encoded, token); buffer_append(buf, link); token = strtok(NULL, "/"); } free(path_copy); } buffer_append(buf, "\n
\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, "
\n"); buffer_append(buf, "
📁
\n"); buffer_append(buf, "

Ошибка открытия директории

\n"); char error_msg[512]; snprintf(error_msg, sizeof(error_msg), "

Путь: %s

\n", base_path); buffer_append(buf, error_msg); snprintf(error_msg, sizeof(error_msg), "

Ошибка: %s

\n", strerror(errno)); buffer_append(buf, error_msg); buffer_append(buf, "

← Вернуться на главную

\n"); buffer_append(buf, "
\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)); 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, "
\n"); buffer_append(buf, "
📄
\n"); buffer_append(buf, "

Здесь пусто

\n"); buffer_append(buf, "

В этой директории нет файлов или папок

\n"); buffer_append(buf, "
\n"); return; } qsort(entries, entry_count, sizeof(entry_t), compare_entries); buffer_append(buf, "\n"); 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); } 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, "")) { print_breadcrumb(&output, display_path); } else if (strstr(line, "")) { 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); } } } print_template(base_path, display_path); return 0; }