система модулей
This commit is contained in:
18
Makefile
18
Makefile
@@ -1,21 +1,23 @@
|
||||
# Makefile для Trashbox CGI
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -O2
|
||||
CFLAGS = -Wall -Wextra -O2 -I./src
|
||||
TARGETS = index.cgi style.cgi status.cgi
|
||||
|
||||
SRCS_INDEX = src/main.c src/config.c src/auth.c src/fs.c src/render.c src/utils.c
|
||||
OBJS_INDEX = $(SRCS_INDEX:.c=.o)
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
index.cgi: index.c
|
||||
index.cgi: $(OBJS_INDEX)
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
style.cgi: src/style.c
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
style.cgi: style.c
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
status.cgi: status.c
|
||||
status.cgi: src/status.c
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(TARGETS)
|
||||
rm -f $(OBJS_INDEX) $(TARGETS)
|
||||
|
||||
install: all
|
||||
chmod +x $(TARGETS)
|
||||
|
||||
@@ -9,10 +9,13 @@ sudo apt install nginx busybox gcc
|
||||
```bash
|
||||
mkdir -p /home/romkazvo/www/cgi-bin
|
||||
cd /home/romkazvo/www/cgi-bin
|
||||
git clone https://git.mashup.su/RomkaZVO/Trashbox.git
|
||||
```
|
||||
### Компиляция CGI
|
||||
```bash
|
||||
sudo sh build.sh
|
||||
cd src
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
### Конфигурация Nginx
|
||||
Файл конфигурации `/etc/nginx/sites-available/trashbox`:<br>
|
||||
@@ -28,4 +31,4 @@ busybox httpd -p 127.0.0.1:8050 -h /home/romkazvo/www
|
||||
cat > /home/romkazvo/www/cgi-bin/.htpasswd << EOF
|
||||
secret:mysecretpassword
|
||||
EOF
|
||||
```
|
||||
```
|
||||
|
||||
52
src/auth.c
Normal file
52
src/auth.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "auth.h"
|
||||
#include "config.h"
|
||||
|
||||
int check_folder_password(const char *folder_name, const char *password) {
|
||||
if (!password || !folder_name || password[0] == '\0') return 0;
|
||||
|
||||
FILE *f = fopen(g_config.passwd_file, "r");
|
||||
if (!f) 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;
|
||||
const char *last_slash = strrchr(folder_name, '/');
|
||||
const char *base_name = last_slash ? last_slash + 1 : folder_name;
|
||||
if (strcmp(folder, folder_name) == 0 || strcmp(folder, base_name) == 0) {
|
||||
fclose(f);
|
||||
return strcmp(pass, password) == 0;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int is_folder_protected(const char *folder_path) {
|
||||
if (!folder_path || folder_path[0] == '\0') return 0;
|
||||
|
||||
FILE *f = fopen(g_config.passwd_file, "r");
|
||||
if (!f) return 0;
|
||||
|
||||
char line[512];
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
line[strcspn(line, "\r\n")] = 0;
|
||||
char *colon = strchr(line, ':');
|
||||
if (!colon) continue;
|
||||
*colon = '\0';
|
||||
const char *last_slash = strrchr(folder_path, '/');
|
||||
const char *folder_name = last_slash ? last_slash + 1 : folder_path;
|
||||
if (strcmp(line, folder_path) == 0 || strcmp(line, folder_name) == 0) {
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
7
src/auth.h
Normal file
7
src/auth.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef AUTH_H
|
||||
#define AUTH_H
|
||||
|
||||
int is_folder_protected(const char *folder_path);
|
||||
int check_folder_password(const char *folder_name, const char *password);
|
||||
|
||||
#endif
|
||||
BIN
src/auth.o
Normal file
BIN
src/auth.o
Normal file
Binary file not shown.
121
src/config.c
Normal file
121
src/config.c
Normal file
@@ -0,0 +1,121 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
|
||||
config_t g_config;
|
||||
|
||||
int load_config(const char *path) {
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) return -1;
|
||||
|
||||
char line[512];
|
||||
char current_section[64] = "";
|
||||
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
char *p = line;
|
||||
while (*p == ' ' || *p == '\t') p++;
|
||||
if (*p == '\0' || *p == '#' || *p == ';') continue;
|
||||
|
||||
if (*p == '[') {
|
||||
p++;
|
||||
char *end = strchr(p, ']');
|
||||
if (end) {
|
||||
*end = '\0';
|
||||
strncpy(current_section, p, sizeof(current_section) - 1);
|
||||
current_section[sizeof(current_section) - 1] = '\0';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
char *eq = strchr(p, '=');
|
||||
if (!eq) continue;
|
||||
*eq = '\0';
|
||||
char *key = p;
|
||||
char *value = eq + 1;
|
||||
|
||||
char *end_key = key + strlen(key) - 1;
|
||||
while (end_key > key && (*end_key == ' ' || *end_key == '\t')) {
|
||||
*end_key = '\0';
|
||||
end_key--;
|
||||
}
|
||||
|
||||
while (*value == ' ' || *value == '\t') value++;
|
||||
char *end_val = value + strlen(value) - 1;
|
||||
while (end_val > value && (*end_val == ' ' || *end_val == '\t' || *end_val == '\r' || *end_val == '\n')) {
|
||||
*end_val = '\0';
|
||||
end_val--;
|
||||
}
|
||||
|
||||
if (strcmp(current_section, "paths") == 0) {
|
||||
if (strcmp(key, "base_path") == 0) strncpy(g_config.base_path, value, sizeof(g_config.base_path) - 1);
|
||||
else if (strcmp(key, "template_path") == 0) strncpy(g_config.template_path, value, sizeof(g_config.template_path) - 1);
|
||||
else if (strcmp(key, "passwd_file") == 0) strncpy(g_config.passwd_file, value, sizeof(g_config.passwd_file) - 1);
|
||||
}
|
||||
else if (strcmp(current_section, "limits") == 0) {
|
||||
if (strcmp(key, "max_entries") == 0) g_config.max_entries = atoi(value);
|
||||
}
|
||||
else if (strcmp(current_section, "hidden") == 0) {
|
||||
if (strcmp(key, "dirs") == 0) {
|
||||
char *token = strtok(value, ",");
|
||||
g_config.hidden_dirs_count = 0;
|
||||
while (token && g_config.hidden_dirs_count < 10) {
|
||||
while (*token == ' ') token++;
|
||||
strncpy(g_config.hidden_dirs[g_config.hidden_dirs_count], token, 255);
|
||||
g_config.hidden_dirs[g_config.hidden_dirs_count][255] = '\0';
|
||||
g_config.hidden_dirs_count++;
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
}
|
||||
else if (strcmp(key, "files") == 0) {
|
||||
char *token = strtok(value, ",");
|
||||
g_config.hidden_files_count = 0;
|
||||
while (token && g_config.hidden_files_count < 10) {
|
||||
while (*token == ' ') token++;
|
||||
strncpy(g_config.hidden_files[g_config.hidden_files_count], token, 255);
|
||||
g_config.hidden_files[g_config.hidden_files_count][255] = '\0';
|
||||
g_config.hidden_files_count++;
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcmp(current_section, "preview") == 0) {
|
||||
if (strcmp(key, "image") == 0) strncpy(g_config.preview_image, value, sizeof(g_config.preview_image) - 1);
|
||||
else if (strcmp(key, "text") == 0) strncpy(g_config.preview_text, value, sizeof(g_config.preview_text) - 1);
|
||||
else if (strcmp(key, "audio") == 0) strncpy(g_config.preview_audio, value, sizeof(g_config.preview_audio) - 1);
|
||||
else if (strcmp(key, "video") == 0) strncpy(g_config.preview_video, value, sizeof(g_config.preview_video) - 1);
|
||||
else if (strcmp(key, "pdf") == 0) strncpy(g_config.preview_pdf, value, sizeof(g_config.preview_pdf) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_default_config(void) {
|
||||
strcpy(g_config.base_path, "/home/romkazvo/www");
|
||||
strcpy(g_config.template_path, "/home/romkazvo/www/cgi-bin/template.html");
|
||||
strcpy(g_config.passwd_file, "/home/romkazvo/www/cgi-bin/.htpasswd");
|
||||
g_config.max_entries = 1000;
|
||||
g_config.hidden_dirs_count = 0;
|
||||
g_config.hidden_files_count = 0;
|
||||
strcpy(g_config.preview_image, "jpg,jpeg,png,gif,webp,bmp,svg,ico");
|
||||
strcpy(g_config.preview_text, "txt,md,html,htm,css,js,json,xml,csv");
|
||||
strcpy(g_config.preview_audio, "mp3,wav,ogg,flac,m4a,aac");
|
||||
strcpy(g_config.preview_video, "mp4,webm,ogv,mov,avi,mkv");
|
||||
strcpy(g_config.preview_pdf, "pdf");
|
||||
}
|
||||
|
||||
int is_string_in_list(const char *str, const char *list) {
|
||||
if (!list || !*list) return 0;
|
||||
char temp[512];
|
||||
strncpy(temp, list, sizeof(temp) - 1);
|
||||
temp[sizeof(temp) - 1] = '\0';
|
||||
char *token = strtok(temp, ",");
|
||||
while (token) {
|
||||
while (*token == ' ') token++;
|
||||
if (strcasecmp(token, str) == 0) return 1;
|
||||
token = strtok(NULL, ",");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
26
src/config.h
Normal file
26
src/config.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
typedef struct {
|
||||
char base_path[1024];
|
||||
char template_path[1024];
|
||||
char passwd_file[1024];
|
||||
int max_entries;
|
||||
char hidden_dirs[10][256];
|
||||
int hidden_dirs_count;
|
||||
char hidden_files[10][256];
|
||||
int hidden_files_count;
|
||||
char preview_image[512];
|
||||
char preview_text[512];
|
||||
char preview_audio[512];
|
||||
char preview_video[512];
|
||||
char preview_pdf[512];
|
||||
} config_t;
|
||||
|
||||
extern config_t g_config;
|
||||
|
||||
int load_config(const char *path);
|
||||
void set_default_config(void);
|
||||
int is_string_in_list(const char *str, const char *list);
|
||||
|
||||
#endif
|
||||
BIN
src/config.o
Normal file
BIN
src/config.o
Normal file
Binary file not shown.
177
src/fs.c
Normal file
177
src/fs.c
Normal file
@@ -0,0 +1,177 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include "fs.h"
|
||||
#include "config.h"
|
||||
#include "auth.h"
|
||||
#include "utils.h"
|
||||
|
||||
query_params_t g_params;
|
||||
|
||||
int is_safe_path(const char *path) {
|
||||
if (!path) return 0;
|
||||
if (strstr(path, "..")) return 0;
|
||||
if (strstr(path, "./")) return 0;
|
||||
if (path[0] == '/') return 0;
|
||||
if (strstr(path, "//")) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void safe_path_join(char *result, size_t result_size, const char *base, const char *path) {
|
||||
if (!is_safe_path(path)) {
|
||||
snprintf(result, result_size, "%s", base);
|
||||
return;
|
||||
}
|
||||
snprintf(result, result_size, "%s/%s", base, path);
|
||||
}
|
||||
|
||||
int count_files_in_dir(const char *path) {
|
||||
DIR *dir = opendir(path);
|
||||
if (!dir) return 0;
|
||||
struct dirent *entry;
|
||||
int count = 0;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
if (entry->d_name[0] == '.') continue;
|
||||
int hidden = 0;
|
||||
for (int i = 0; i < g_config.hidden_files_count; i++) {
|
||||
if (strcmp(entry->d_name, g_config.hidden_files[i]) == 0) { hidden = 1; break; }
|
||||
}
|
||||
if (hidden) continue;
|
||||
count++;
|
||||
}
|
||||
closedir(dir);
|
||||
return count;
|
||||
}
|
||||
|
||||
long long get_dir_size(const char *path) {
|
||||
DIR *dir = opendir(path);
|
||||
if (!dir) return 0;
|
||||
struct dirent *entry;
|
||||
struct stat statbuf;
|
||||
char fullpath[1024];
|
||||
long long size = 0;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name);
|
||||
if (stat(fullpath, &statbuf) == 0) {
|
||||
if (S_ISDIR(statbuf.st_mode)) size += get_dir_size(fullpath);
|
||||
else if (S_ISREG(statbuf.st_mode)) size += statbuf.st_size;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return size;
|
||||
}
|
||||
|
||||
const char* get_file_icon(const char* filename) {
|
||||
const char *ext = strrchr(filename, '.');
|
||||
if (!ext) return "📄";
|
||||
ext++;
|
||||
if (is_string_in_list(ext, g_config.preview_image)) return "🖼️";
|
||||
if (is_string_in_list(ext, g_config.preview_audio)) return "🎵";
|
||||
if (is_string_in_list(ext, g_config.preview_video)) return "🎬";
|
||||
if (strcasecmp(ext, "zip") == 0 || strcasecmp(ext, "rar") == 0 ||
|
||||
strcasecmp(ext, "7z") == 0 || strcasecmp(ext, "tar") == 0 ||
|
||||
strcasecmp(ext, "gz") == 0) return "📦";
|
||||
if (strcasecmp(ext, "pdf") == 0) return "📕";
|
||||
if (strcasecmp(ext, "doc") == 0 || strcasecmp(ext, "docx") == 0) return "📘";
|
||||
if (strcasecmp(ext, "xls") == 0 || strcasecmp(ext, "xlsx") == 0) return "📗";
|
||||
if (strcasecmp(ext, "txt") == 0) return "📝";
|
||||
return "📄";
|
||||
}
|
||||
|
||||
int is_previewable(const char* filename) {
|
||||
const char *ext = strrchr(filename, '.');
|
||||
if (!ext) return 0;
|
||||
ext++;
|
||||
return (is_string_in_list(ext, g_config.preview_image) ||
|
||||
is_string_in_list(ext, g_config.preview_text) ||
|
||||
is_string_in_list(ext, g_config.preview_audio) ||
|
||||
is_string_in_list(ext, g_config.preview_video) ||
|
||||
is_string_in_list(ext, g_config.preview_pdf));
|
||||
}
|
||||
|
||||
void search_recursive(const char *base_path, const char *display_path, const char *search_term, entry_t *results, int *count, int max_results) {
|
||||
DIR *dir = opendir(base_path);
|
||||
if (!dir) return;
|
||||
struct dirent *entry;
|
||||
struct stat file_stat;
|
||||
char full_path[1024];
|
||||
char display_subpath[1024];
|
||||
while ((entry = readdir(dir)) != NULL && *count < max_results) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
if (entry->d_name[0] == '.') continue;
|
||||
int hidden = 0;
|
||||
for (int i = 0; i < g_config.hidden_dirs_count; i++) {
|
||||
if (strcmp(entry->d_name, g_config.hidden_dirs[i]) == 0) { hidden = 1; break; }
|
||||
}
|
||||
for (int i = 0; i < g_config.hidden_files_count; i++) {
|
||||
if (strcmp(entry->d_name, g_config.hidden_files[i]) == 0) { hidden = 1; break; }
|
||||
}
|
||||
if (hidden) continue;
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name);
|
||||
if (display_path[0] == '\0') {
|
||||
snprintf(display_subpath, sizeof(display_subpath), "%s", entry->d_name);
|
||||
} else {
|
||||
snprintf(display_subpath, sizeof(display_subpath), "%s/%s", display_path, entry->d_name);
|
||||
}
|
||||
if (is_folder_protected(display_subpath) || is_folder_protected(entry->d_name)) {
|
||||
continue;
|
||||
}
|
||||
if (stat(full_path, &file_stat) == 0) {
|
||||
if (my_strcasestr(entry->d_name, search_term) != NULL) {
|
||||
entry_t *result = &results[*count];
|
||||
strncpy(result->name, entry->d_name, sizeof(result->name) - 1);
|
||||
result->name[sizeof(result->name) - 1] = '\0';
|
||||
if (S_ISDIR(file_stat.st_mode)) {
|
||||
result->is_dir = 1;
|
||||
result->size = 0;
|
||||
result->dir_size = get_dir_size(full_path);
|
||||
result->file_count = count_files_in_dir(full_path);
|
||||
result->mtime = file_stat.st_mtime;
|
||||
strcpy(result->icon, "📁");
|
||||
result->is_protected = 0;
|
||||
url_encode(display_subpath, result->encoded_path, sizeof(result->encoded_path));
|
||||
strncpy(result->file_url, display_subpath, sizeof(result->file_url) - 1);
|
||||
(*count)++;
|
||||
} else if (S_ISREG(file_stat.st_mode)) {
|
||||
result->is_dir = 0;
|
||||
result->size = file_stat.st_size;
|
||||
result->dir_size = 0;
|
||||
result->file_count = 0;
|
||||
result->mtime = file_stat.st_mtime;
|
||||
strcpy(result->icon, get_file_icon(entry->d_name));
|
||||
result->is_protected = 0;
|
||||
url_encode(display_subpath, result->encoded_path, sizeof(result->encoded_path));
|
||||
strncpy(result->file_url, display_subpath, sizeof(result->file_url) - 1);
|
||||
(*count)++;
|
||||
}
|
||||
}
|
||||
if (S_ISDIR(file_stat.st_mode)) {
|
||||
search_recursive(full_path, display_subpath, search_term, results, count, max_results);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
int compare_entries_sorted(const void *a, const void *b) {
|
||||
const entry_t *entryA = (const entry_t *)a;
|
||||
const entry_t *entryB = (const entry_t *)b;
|
||||
if (entryA->is_dir && !entryB->is_dir) return -1;
|
||||
if (!entryA->is_dir && entryB->is_dir) return 1;
|
||||
int result = 0;
|
||||
if (strcmp(g_params.sort_by, "size") == 0) {
|
||||
long long sizeA = entryA->is_dir ? entryA->dir_size : entryA->size;
|
||||
long long sizeB = entryB->is_dir ? entryB->dir_size : entryB->size;
|
||||
result = (sizeA > sizeB) - (sizeA < sizeB);
|
||||
} else if (strcmp(g_params.sort_by, "date") == 0) {
|
||||
result = (entryA->mtime > entryB->mtime) - (entryA->mtime < entryB->mtime);
|
||||
} else {
|
||||
result = strcasecmp(entryA->name, entryB->name);
|
||||
}
|
||||
return result * g_params.sort_order;
|
||||
}
|
||||
37
src/fs.h
Normal file
37
src/fs.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef FS_H
|
||||
#define FS_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
typedef struct {
|
||||
char name[256];
|
||||
int is_dir;
|
||||
long size;
|
||||
char icon[8];
|
||||
char encoded_path[1024];
|
||||
char file_url[1024];
|
||||
int is_protected;
|
||||
time_t mtime;
|
||||
int file_count;
|
||||
long long dir_size;
|
||||
} entry_t;
|
||||
|
||||
typedef struct {
|
||||
char sort_by[16];
|
||||
int sort_order;
|
||||
char search[256];
|
||||
int recursive;
|
||||
} query_params_t;
|
||||
|
||||
extern query_params_t g_params;
|
||||
|
||||
int is_safe_path(const char *path);
|
||||
void safe_path_join(char *result, size_t result_size, const char *base, const char *path);
|
||||
int count_files_in_dir(const char *path);
|
||||
long long get_dir_size(const char *path);
|
||||
const char* get_file_icon(const char* filename);
|
||||
int is_previewable(const char* filename);
|
||||
void search_recursive(const char *base_path, const char *display_path, const char *search_term, entry_t *results, int *count, int max_results);
|
||||
int compare_entries_sorted(const void *a, const void *b);
|
||||
|
||||
#endif
|
||||
130
src/main.c
Normal file
130
src/main.c
Normal file
@@ -0,0 +1,130 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <locale.h>
|
||||
#include "config.h"
|
||||
#include "auth.h"
|
||||
#include "fs.h"
|
||||
#include "render.h"
|
||||
#include "utils.h"
|
||||
|
||||
int main() {
|
||||
setlocale(LC_ALL, "en_US.UTF-8");
|
||||
setlocale(LC_CTYPE, "en_US.UTF-8");
|
||||
|
||||
if (load_config("/home/romkazvo/www/cgi-bin/config.ini") != 0) {
|
||||
set_default_config();
|
||||
}
|
||||
|
||||
strcpy(g_params.sort_by, "name");
|
||||
g_params.sort_order = 1;
|
||||
g_params.search[0] = '\0';
|
||||
g_params.recursive = 0;
|
||||
|
||||
char *query_string = getenv("QUERY_STRING");
|
||||
char display_path[1024] = "";
|
||||
char safe_display_path[1024] = "";
|
||||
char base_path[1024];
|
||||
strcpy(base_path, g_config.base_path);
|
||||
|
||||
if (query_string) {
|
||||
char *path_start = strstr(query_string, "path=");
|
||||
if (path_start) {
|
||||
path_start += 5;
|
||||
char *path_end = strchr(path_start, '&');
|
||||
int path_len = path_end ? (int)(path_end - path_start) : (int)strlen(path_start);
|
||||
if (path_len > 0 && path_len < (int)sizeof(display_path) - 1) {
|
||||
char encoded_path[1024];
|
||||
strncpy(encoded_path, path_start, path_len);
|
||||
encoded_path[path_len] = '\0';
|
||||
url_decode_enhanced(encoded_path, display_path, sizeof(display_path));
|
||||
if (!is_safe_path(display_path)) {
|
||||
display_path[0] = '\0';
|
||||
strcpy(base_path, g_config.base_path);
|
||||
} else {
|
||||
strncpy(safe_display_path, display_path, sizeof(safe_display_path) - 1);
|
||||
safe_display_path[sizeof(safe_display_path) - 1] = '\0';
|
||||
safe_path_join(base_path, sizeof(base_path), g_config.base_path, safe_display_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *sort_start = strstr(query_string, "sort=");
|
||||
if (sort_start) {
|
||||
sort_start += 5;
|
||||
char *sort_end = strchr(sort_start, '&');
|
||||
int sort_len = sort_end ? (int)(sort_end - sort_start) : (int)strlen(sort_start);
|
||||
if (sort_len > 0 && sort_len < (int)sizeof(g_params.sort_by)) {
|
||||
strncpy(g_params.sort_by, sort_start, sort_len);
|
||||
g_params.sort_by[sort_len] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
char *search_start = strstr(query_string, "search=");
|
||||
if (search_start) {
|
||||
search_start += 7;
|
||||
char *search_end = strchr(search_start, '&');
|
||||
int search_len = search_end ? (int)(search_end - search_start) : (int)strlen(search_start);
|
||||
if (search_len > 0 && search_len < (int)sizeof(g_params.search) - 1) {
|
||||
char encoded_search[256];
|
||||
strncpy(encoded_search, search_start, search_len);
|
||||
encoded_search[search_len] = '\0';
|
||||
url_decode_enhanced(encoded_search, g_params.search, sizeof(g_params.search));
|
||||
if (g_params.search[0] != '\0') {
|
||||
g_params.recursive = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 ? (int)(pass_end - pass_start) : (int)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 (decoded_pass[0] == '\0') {
|
||||
char encoded_path[1024];
|
||||
url_encode(display_path, encoded_path, sizeof(encoded_path));
|
||||
printf("Status: 302 Found\r\n");
|
||||
printf("Location: /cgi-bin/index.cgi?path=%s&error=1\r\n\r\n", encoded_path);
|
||||
return 0;
|
||||
}
|
||||
if (!check_folder_password(folder_name, decoded_pass)) {
|
||||
char encoded_path[1024];
|
||||
url_encode(display_path, encoded_path, sizeof(encoded_path));
|
||||
printf("Status: 302 Found\r\n");
|
||||
printf("Location: /cgi-bin/index.cgi?path=%s&error=1\r\n\r\n", encoded_path);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
char encoded_path[1024];
|
||||
url_encode(display_path, encoded_path, sizeof(encoded_path));
|
||||
printf("Status: 302 Found\r\n");
|
||||
printf("Location: /cgi-bin/index.cgi?path=%s&error=1\r\n\r\n", encoded_path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("Content-type: text/html; charset=utf-8\n\n");
|
||||
print_template(base_path, display_path);
|
||||
return 0;
|
||||
}
|
||||
BIN
src/main.o
Normal file
BIN
src/main.o
Normal file
Binary file not shown.
482
src/render.c
Normal file
482
src/render.c
Normal file
@@ -0,0 +1,482 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include "render.h"
|
||||
#include "config.h"
|
||||
#include "auth.h"
|
||||
#include "fs.h"
|
||||
#include "utils.h"
|
||||
|
||||
extern query_params_t g_params;
|
||||
|
||||
void buffer_init(buffer_t *buf) {
|
||||
buf->capacity = 65536;
|
||||
buf->data = malloc(buf->capacity);
|
||||
buf->size = 0;
|
||||
}
|
||||
|
||||
void buffer_append(buffer_t *buf, const char *str) {
|
||||
size_t len = strlen(str);
|
||||
if (buf->size + len >= buf->capacity) {
|
||||
buf->capacity *= 2;
|
||||
buf->data = realloc(buf->data, buf->capacity);
|
||||
}
|
||||
memcpy(buf->data + buf->size, str, len);
|
||||
buf->size += len;
|
||||
}
|
||||
|
||||
void buffer_free(buffer_t *buf) { free(buf->data); }
|
||||
|
||||
void 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 safe_token[512];
|
||||
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);
|
||||
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>");
|
||||
char safe_name[512];
|
||||
html_escape(folder_name, safe_name, sizeof(safe_name));
|
||||
buffer_append(buf, safe_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 = malloc(sizeof(entry_t) * g_config.max_entries);
|
||||
if (!entries) {
|
||||
buffer_append(buf, "<div class=\"error\">Ошибка выделения памяти</div>");
|
||||
return;
|
||||
}
|
||||
int entry_count = 0, dir_count = 0, file_count = 0;
|
||||
long long total_size = 0;
|
||||
int is_recursive_search = 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 ? (int)(pass_end - pass_start) : (int)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));
|
||||
password = decoded_pass;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_folder_protected(folder_name)) {
|
||||
if (!password || !check_folder_password(folder_name, password)) {
|
||||
print_password_form(buf, folder_name);
|
||||
free(entries);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (g_params.search[0] != '\0') {
|
||||
int result_count = 0;
|
||||
search_recursive(g_config.base_path, "", g_params.search, entries, &result_count, g_config.max_entries);
|
||||
if (result_count == 0) {
|
||||
buffer_append(buf, "<div class=\"empty-state\">\n");
|
||||
buffer_append(buf, " <div class=\"icon\">🔍</div>\n");
|
||||
buffer_append(buf, " <h3>Ничего не найдено</h3>\n");
|
||||
buffer_append(buf, " <p>По запросу \"");
|
||||
char safe_search[512];
|
||||
html_escape(g_params.search, safe_search, sizeof(safe_search));
|
||||
buffer_append(buf, safe_search);
|
||||
buffer_append(buf, "\" ничего не найдено</p>\n");
|
||||
buffer_append(buf, "</div>\n");
|
||||
free(entries);
|
||||
return;
|
||||
}
|
||||
entry_count = result_count;
|
||||
is_recursive_search = 1;
|
||||
for (int i = 0; i < entry_count; i++) {
|
||||
if (entries[i].is_dir) {
|
||||
dir_count++;
|
||||
total_size += entries[i].dir_size;
|
||||
} else {
|
||||
file_count++;
|
||||
total_size += entries[i].size;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dir = opendir(base_path);
|
||||
if (!dir) {
|
||||
char alt_path[1024];
|
||||
snprintf(alt_path, sizeof(alt_path), "%s", g_config.base_path);
|
||||
if (display_path[0]) {
|
||||
char *encoded = strdup(display_path);
|
||||
url_decode_enhanced(encoded, alt_path + strlen(alt_path), sizeof(alt_path) - strlen(alt_path));
|
||||
free(encoded);
|
||||
}
|
||||
dir = opendir(alt_path);
|
||||
if (!dir) {
|
||||
buffer_append(buf, "<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");
|
||||
free(entries);
|
||||
return;
|
||||
}
|
||||
}
|
||||
while ((entry = readdir(dir)) != NULL && entry_count < g_config.max_entries) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
|
||||
int hidden = 0;
|
||||
for (int i = 0; i < g_config.hidden_dirs_count; i++) {
|
||||
if (strcmp(entry->d_name, g_config.hidden_dirs[i]) == 0) { hidden = 1; break; }
|
||||
}
|
||||
for (int i = 0; i < g_config.hidden_files_count; i++) {
|
||||
if (strcmp(entry->d_name, g_config.hidden_files[i]) == 0) { hidden = 1; break; }
|
||||
}
|
||||
if (entry->d_name[0] == '.') hidden = 1;
|
||||
if (hidden) continue;
|
||||
snprintf(full_path, sizeof(full_path), "%s/%s", base_path, entry->d_name);
|
||||
if (stat(full_path, &file_stat) == 0) {
|
||||
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;
|
||||
entries[entry_count].mtime = file_stat.st_mtime;
|
||||
entries[entry_count].file_count = count_files_in_dir(full_path);
|
||||
entries[entry_count].dir_size = get_dir_size(full_path);
|
||||
total_size += entries[entry_count].dir_size;
|
||||
if (is_folder_protected(entry->d_name)) {
|
||||
strcpy(entries[entry_count].icon, "🔒");
|
||||
entries[entry_count].is_protected = 1;
|
||||
} else {
|
||||
strcpy(entries[entry_count].icon, "📁");
|
||||
entries[entry_count].is_protected = 0;
|
||||
}
|
||||
char new_path[2048];
|
||||
snprintf(new_path, sizeof(new_path), "%s%s%s", display_path, display_path[0] ? "/" : "", entries[entry_count].name);
|
||||
url_encode(new_path, entries[entry_count].encoded_path, sizeof(entries[0].encoded_path));
|
||||
dir_count++;
|
||||
} else if (S_ISREG(file_stat.st_mode)) {
|
||||
entries[entry_count].is_dir = 0;
|
||||
entries[entry_count].size = file_stat.st_size;
|
||||
entries[entry_count].mtime = file_stat.st_mtime;
|
||||
entries[entry_count].file_count = 0;
|
||||
entries[entry_count].dir_size = 0;
|
||||
strcpy(entries[entry_count].icon, get_file_icon(entry->d_name));
|
||||
entries[entry_count].is_protected = 0;
|
||||
char encoded_filename[1024];
|
||||
url_encode(entry->d_name, encoded_filename, sizeof(encoded_filename));
|
||||
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;
|
||||
file_count++;
|
||||
} 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");
|
||||
free(entries);
|
||||
return;
|
||||
}
|
||||
}
|
||||
qsort(entries, entry_count, sizeof(entry_t), compare_entries_sorted);
|
||||
char encoded_path[1024];
|
||||
url_encode(display_path, encoded_path, sizeof(encoded_path));
|
||||
char safe_search[512];
|
||||
html_escape(g_params.search, safe_search, sizeof(safe_search));
|
||||
buffer_append(buf, "<div class=\"toolbar\">\n");
|
||||
buffer_append(buf, " <div class=\"search-box\">\n");
|
||||
buffer_append(buf, " <form method=\"GET\" action=\"/cgi-bin/index.cgi\">\n");
|
||||
buffer_append(buf, " <input type=\"hidden\" name=\"path\" value=\"");
|
||||
buffer_append(buf, encoded_path);
|
||||
buffer_append(buf, "\">\n");
|
||||
buffer_append(buf, " <input type=\"text\" name=\"search\" placeholder=\"🔍 Поиск по всему сайту...\" value=\"");
|
||||
buffer_append(buf, safe_search);
|
||||
buffer_append(buf, "\">\n");
|
||||
buffer_append(buf, " <button type=\"submit\">Найти</button>\n");
|
||||
buffer_append(buf, " </form>\n");
|
||||
buffer_append(buf, " </div>\n");
|
||||
buffer_append(buf, " <div class=\"sort-buttons\">\n");
|
||||
buffer_append(buf, " <span class=\"sort-label\">Сортировка:</span>\n");
|
||||
buffer_append(buf, " <a href=\"/cgi-bin/index.cgi?path=");
|
||||
buffer_append(buf, encoded_path);
|
||||
buffer_append(buf, "&sort=name\" class=\"sort-btn");
|
||||
if (strcmp(g_params.sort_by, "name") == 0) buffer_append(buf, " active");
|
||||
buffer_append(buf, "\">📝 Имя</a>\n");
|
||||
buffer_append(buf, " <a href=\"/cgi-bin/index.cgi?path=");
|
||||
buffer_append(buf, encoded_path);
|
||||
buffer_append(buf, "&sort=size\" class=\"sort-btn");
|
||||
if (strcmp(g_params.sort_by, "size") == 0) buffer_append(buf, " active");
|
||||
buffer_append(buf, "\">📊 Размер</a>\n");
|
||||
buffer_append(buf, " <a href=\"/cgi-bin/index.cgi?path=");
|
||||
buffer_append(buf, encoded_path);
|
||||
buffer_append(buf, "&sort=date\" class=\"sort-btn");
|
||||
if (strcmp(g_params.sort_by, "date") == 0) buffer_append(buf, " active");
|
||||
buffer_append(buf, "\">📅 Дата</a>\n");
|
||||
buffer_append(buf, " </div>\n");
|
||||
buffer_append(buf, "</div>\n");
|
||||
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, "🔍 Результаты поиска по всему сайту: ");
|
||||
char tmp[32];
|
||||
snprintf(tmp, sizeof(tmp), "%d", entry_count);
|
||||
buffer_append(buf, tmp);
|
||||
buffer_append(buf, " файлов/папок найдено</div>\n");
|
||||
}
|
||||
buffer_append(buf, "<ul>\n");
|
||||
int has_dirs = 0;
|
||||
for (int i = 0; i < entry_count; i++) if (entries[i].is_dir) { has_dirs = 1; break; }
|
||||
if (has_dirs) {
|
||||
int display_dir_count = 0;
|
||||
for (int i = 0; i < entry_count; i++) if (entries[i].is_dir) display_dir_count++;
|
||||
char section_title[128];
|
||||
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📁 Папки (%d)</li>\n", display_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, "', '");
|
||||
char safe_name[512];
|
||||
html_escape(entries[i].name, safe_name, sizeof(safe_name));
|
||||
buffer_append(buf, safe_name);
|
||||
buffer_append(buf, "')\">\n");
|
||||
buffer_append(buf, " <span class=\"file-icon\">🔒</span>\n");
|
||||
buffer_append(buf, " <span class=\"file-name\">");
|
||||
if (is_recursive_search) {
|
||||
char safe_url[512];
|
||||
html_escape(entries[i].file_url, safe_url, sizeof(safe_url));
|
||||
buffer_append(buf, safe_url);
|
||||
} else {
|
||||
char safe_name2[512];
|
||||
html_escape(entries[i].name, safe_name2, sizeof(safe_name2));
|
||||
buffer_append(buf, safe_name2);
|
||||
}
|
||||
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, "', '");
|
||||
char safe_name3[512];
|
||||
html_escape(entries[i].name, safe_name3, sizeof(safe_name3));
|
||||
buffer_append(buf, safe_name3);
|
||||
buffer_append(buf, "')\" title=\"Ввести пароль\">🔑</button>\n");
|
||||
buffer_append(buf, " </div>\n");
|
||||
buffer_append(buf, " </div>\n");
|
||||
} else {
|
||||
buffer_append(buf, " <div class=\"file-row\">\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\">");
|
||||
if (is_recursive_search) {
|
||||
char safe_url[512];
|
||||
html_escape(entries[i].file_url, safe_url, sizeof(safe_url));
|
||||
buffer_append(buf, safe_url);
|
||||
} else {
|
||||
char safe_name4[512];
|
||||
html_escape(entries[i].name, safe_name4, sizeof(safe_name4));
|
||||
buffer_append(buf, safe_name4);
|
||||
}
|
||||
buffer_append(buf, "</span>\n");
|
||||
buffer_append(buf, " </a>\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, entries[i].encoded_path);
|
||||
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, " </li>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
int has_files = 0;
|
||||
for (int i = 0; i < entry_count; i++) if (!entries[i].is_dir) { has_files = 1; break; }
|
||||
if (has_files) {
|
||||
int display_file_count = 0;
|
||||
for (int i = 0; i < entry_count; i++) if (!entries[i].is_dir) display_file_count++;
|
||||
char section_title[128];
|
||||
snprintf(section_title, sizeof(section_title), " <li class=\"section-title\">📄 Файлы (%d)</li>\n", display_file_count);
|
||||
buffer_append(buf, section_title);
|
||||
for (int i = 0; i < entry_count; i++) {
|
||||
if (!entries[i].is_dir) {
|
||||
char size_str[32];
|
||||
char date_str[64];
|
||||
format_size(entries[i].size, size_str);
|
||||
format_date(entries[i].mtime, date_str, sizeof(date_str));
|
||||
buffer_append(buf, " <li>\n");
|
||||
buffer_append(buf, " <div class=\"file-row\">\n");
|
||||
char file_link[2048];
|
||||
if (is_recursive_search) {
|
||||
snprintf(file_link, sizeof(file_link), "/%s", entries[i].file_url);
|
||||
} else {
|
||||
snprintf(file_link, sizeof(file_link), "/%s", entries[i].file_url);
|
||||
}
|
||||
buffer_append(buf, " <a class=\"file-link\" href=\"");
|
||||
buffer_append(buf, file_link);
|
||||
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\">");
|
||||
if (is_recursive_search) {
|
||||
char safe_url[512];
|
||||
html_escape(entries[i].file_url, safe_url, sizeof(safe_url));
|
||||
buffer_append(buf, safe_url);
|
||||
} else {
|
||||
char safe_name5[512];
|
||||
html_escape(entries[i].name, safe_name5, sizeof(safe_name5));
|
||||
buffer_append(buf, safe_name5);
|
||||
}
|
||||
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, file_link);
|
||||
buffer_append(buf, "')\" title=\"Открыть в браузере\">👁</button>\n");
|
||||
}
|
||||
buffer_append(buf, " <button class=\"download-btn\" onclick=\"downloadFile('");
|
||||
buffer_append(buf, file_link);
|
||||
buffer_append(buf, "')\" title=\"Скачать файл\">⬇️</button>\n");
|
||||
buffer_append(buf, " <button class=\"copy-btn\" onclick=\"copyLink('");
|
||||
buffer_append(buf, file_link);
|
||||
buffer_append(buf, "')\" title=\"Копировать ссылку на файл\">🔗</button>\n");
|
||||
buffer_append(buf, " <span class=\"file-meta date\">");
|
||||
buffer_append(buf, date_str);
|
||||
buffer_append(buf, "</span>\n");
|
||||
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, " </li>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer_append(buf, "</ul>\n");
|
||||
if (is_recursive_search) {
|
||||
char stats[256];
|
||||
snprintf(stats, sizeof(stats),
|
||||
"<div class=\"stats\">\n Найдено: папок %d | файлов %d\n</div>\n",
|
||||
dir_count, file_count);
|
||||
buffer_append(buf, stats);
|
||||
} else {
|
||||
char total_size_str[32];
|
||||
format_size(total_size, total_size_str);
|
||||
char stats[256];
|
||||
snprintf(stats, sizeof(stats),
|
||||
"<div class=\"stats\">\n Папки: %d | Файлы: %d | Общий размер: %s\n</div>\n",
|
||||
dir_count, file_count, total_size_str);
|
||||
buffer_append(buf, stats);
|
||||
}
|
||||
free(entries);
|
||||
}
|
||||
|
||||
void print_template(const char *base_path, const char *display_path) {
|
||||
FILE *file = fopen(g_config.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);
|
||||
}
|
||||
17
src/render.h
Normal file
17
src/render.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef RENDER_H
|
||||
#define RENDER_H
|
||||
|
||||
#include "fs.h"
|
||||
|
||||
typedef struct {
|
||||
char *data;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
} buffer_t;
|
||||
|
||||
void buffer_init(buffer_t *buf);
|
||||
void buffer_append(buffer_t *buf, const char *str);
|
||||
void buffer_free(buffer_t *buf);
|
||||
void print_template(const char *base_path, const char *display_path);
|
||||
|
||||
#endif
|
||||
BIN
src/render.o
Normal file
BIN
src/render.o
Normal file
Binary file not shown.
52
src/status.c
Normal file
52
src/status.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
long get_nginx_memory() {
|
||||
long total_kb = 0;
|
||||
char command[] = "ps -o rss= -C nginx 2>/dev/null";
|
||||
|
||||
FILE *ps = popen(command, "r");
|
||||
if (!ps) return 0;
|
||||
|
||||
char line[64];
|
||||
while (fgets(line, sizeof(line), ps)) {
|
||||
long kb;
|
||||
if (sscanf(line, "%ld", &kb) == 1) {
|
||||
total_kb += kb;
|
||||
}
|
||||
}
|
||||
pclose(ps);
|
||||
|
||||
return total_kb;
|
||||
}
|
||||
|
||||
int get_process_memory(const char *process_name, long *memory_kb) {
|
||||
char command[256];
|
||||
snprintf(command, sizeof(command), "ps -o rss= -C %s 2>/dev/null | head -1", process_name);
|
||||
|
||||
FILE *ps = popen(command, "r");
|
||||
if (!ps) return 0;
|
||||
|
||||
int result = fscanf(ps, "%ld", memory_kb);
|
||||
pclose(ps);
|
||||
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("Content-type: text/plain; charset=utf-8\n\n");
|
||||
|
||||
long busybox_kb = 0, nginx_kb = 0;
|
||||
|
||||
get_process_memory("busybox", &busybox_kb);
|
||||
nginx_kb = get_nginx_memory();
|
||||
|
||||
printf("BusyBox: %ld KB | Nginx: %ld MB | Всего: %.1f MB",
|
||||
busybox_kb,
|
||||
nginx_kb / 1024,
|
||||
(busybox_kb + nginx_kb) / 1024.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
src/style.c
Normal file
36
src/style.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int main() {
|
||||
char css_path[1024] = "/home/romkazvo/www/cgi-bin/style.css";
|
||||
struct stat st;
|
||||
|
||||
if (stat(css_path, &st) != 0) {
|
||||
printf("Status: 404 Not Found\n");
|
||||
printf("Content-type: text/plain\n\n");
|
||||
printf("CSS file not found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Content-type: text/css\n");
|
||||
printf("Cache-Control: public, max-age=3600\n\n");
|
||||
|
||||
FILE *css_file = fopen(css_path, "r");
|
||||
if (!css_file) {
|
||||
printf("Status: 500 Internal Server Error\n");
|
||||
printf("Content-type: text/plain\n\n");
|
||||
printf("Cannot open CSS file\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
char buffer[4096];
|
||||
size_t bytes_read;
|
||||
while ((bytes_read = fread(buffer, 1, sizeof(buffer), css_file)) > 0) {
|
||||
fwrite(buffer, 1, bytes_read, stdout);
|
||||
}
|
||||
|
||||
fclose(css_file);
|
||||
return 0;
|
||||
}
|
||||
95
src/utils.c
Normal file
95
src/utils.c
Normal file
@@ -0,0 +1,95 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include "utils.h"
|
||||
|
||||
char* my_strcasestr(const char *haystack, const char *needle) {
|
||||
if (!haystack || !needle || *needle == '\0') return (char*)haystack;
|
||||
size_t needle_len = strlen(needle);
|
||||
while (*haystack) {
|
||||
if (strncasecmp(haystack, needle, needle_len) == 0) return (char*)haystack;
|
||||
haystack++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void html_escape(const char *src, char *dst, size_t dst_size) {
|
||||
if (!src || !dst || dst_size == 0) return;
|
||||
char *p = dst;
|
||||
size_t i = 0;
|
||||
while (*src && i < dst_size - 6) {
|
||||
switch (*src) {
|
||||
case '<': strcpy(p, "<"); p += 4; i += 4; break;
|
||||
case '>': strcpy(p, ">"); p += 4; i += 4; break;
|
||||
case '"': strcpy(p, """); p += 6; i += 6; break;
|
||||
case '&': strcpy(p, "&"); p += 5; i += 5; break;
|
||||
default: *p++ = *src; i++; break;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
void format_size(long long size, char* buffer) {
|
||||
if (size < 1024) snprintf(buffer, 32, "%lld B", size);
|
||||
else if (size < 1024 * 1024) snprintf(buffer, 32, "%.1f KB", size / 1024.0);
|
||||
else if (size < 1024 * 1024 * 1024) snprintf(buffer, 32, "%.1f MB", size / (1024.0 * 1024.0));
|
||||
else snprintf(buffer, 32, "%.1f GB", size / (1024.0 * 1024.0 * 1024.0));
|
||||
}
|
||||
|
||||
void format_date(time_t mtime, char* buffer, size_t size) {
|
||||
struct tm *tm_info = localtime(&mtime);
|
||||
strftime(buffer, size, "%d.%m.%Y", tm_info);
|
||||
}
|
||||
|
||||
void url_encode(const char *src, char *dst, size_t dst_size) {
|
||||
static const char *hex = "0123456789ABCDEF";
|
||||
char *p = dst;
|
||||
size_t i = 0;
|
||||
while (*src && i < dst_size - 3) {
|
||||
unsigned char c = (unsigned char)*src;
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') || strchr("-_.~", c)) {
|
||||
*p++ = c; i++;
|
||||
} else if (c == ' ') {
|
||||
*p++ = '%'; *p++ = '2'; *p++ = '0'; i += 3;
|
||||
} else {
|
||||
*p++ = '%'; *p++ = hex[(c >> 4) & 0xF]; *p++ = hex[c & 0xF]; i += 3;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
void url_decode_enhanced(const char *src, char *dst, size_t dst_size) {
|
||||
char *p = dst;
|
||||
size_t decoded_len = 0;
|
||||
while (*src && decoded_len < dst_size - 1) {
|
||||
if (*src == '%') {
|
||||
if (src[1] && src[2] && isxdigit(src[1]) && isxdigit(src[2])) {
|
||||
char hex[3] = {src[1], src[2], '\0'};
|
||||
unsigned char c = (unsigned char)strtol(hex, NULL, 16);
|
||||
if (c >= 0x80 || c >= 0x20 || c == 0x0A || c == 0x0D) {
|
||||
*p++ = c;
|
||||
decoded_len++;
|
||||
} else {
|
||||
*p++ = '_';
|
||||
decoded_len++;
|
||||
}
|
||||
src += 3;
|
||||
} else {
|
||||
src++;
|
||||
}
|
||||
} else if (*src == '+') {
|
||||
*p++ = ' ';
|
||||
decoded_len++;
|
||||
src++;
|
||||
} else {
|
||||
*p++ = *src++;
|
||||
decoded_len++;
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
}
|
||||
13
src/utils.h
Normal file
13
src/utils.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
void url_encode(const char *src, char *dst, size_t dst_size);
|
||||
void url_decode_enhanced(const char *src, char *dst, size_t dst_size);
|
||||
void html_escape(const char *src, char *dst, size_t dst_size);
|
||||
char* my_strcasestr(const char *haystack, const char *needle);
|
||||
void format_size(long long size, char* buffer);
|
||||
void format_date(time_t mtime, char* buffer, size_t size);
|
||||
|
||||
#endif
|
||||
BIN
src/utils.o
Normal file
BIN
src/utils.o
Normal file
Binary file not shown.
BIN
status.cgi
Executable file
BIN
status.cgi
Executable file
Binary file not shown.
Reference in New Issue
Block a user