291 lines
6.7 KiB
C
291 lines
6.7 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
#include <strings.h>
|
|
#include <errno.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <linux/limits.h>
|
|
#include <linux/unistd.h>
|
|
#include <dirent.h>
|
|
|
|
#include "logger.h"
|
|
#include "templates.h"
|
|
#include "util.h"
|
|
|
|
#define FLG_USED 1
|
|
#define FLG_ISDIR 2
|
|
|
|
struct Entry {
|
|
char *name;
|
|
off_t size;
|
|
uint8_t flags;
|
|
};
|
|
|
|
const char* format_size(off_t s) {
|
|
static char buf[32];
|
|
const char *prefixes = " kMGTPEZY";
|
|
float fs = s;
|
|
|
|
int divisions = 0;
|
|
|
|
while(fs > 1024) {
|
|
fs /= 1024.0f;
|
|
divisions++;
|
|
}
|
|
|
|
if(divisions == 0) {
|
|
sprintf(buf, "%lu B", s);
|
|
} else {
|
|
sprintf(buf, "%.2f %ciB", fs, prefixes[divisions]);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static int compare_entries(const void *sp1,const void *sp2 ) {
|
|
const struct Entry *e1 = sp1;
|
|
const struct Entry *e2 = sp2;
|
|
|
|
return( strcasecmp(e1->name, e2->name) );
|
|
}
|
|
|
|
char* gen_html(const char *url, struct Entry *entries, size_t numentries) {
|
|
char *result;
|
|
size_t allocated;
|
|
size_t i;
|
|
char buf[256];
|
|
char fullpath[PATH_MAX];
|
|
char encName[2*PATH_MAX];
|
|
|
|
// allocate a buffer for the result string
|
|
allocated = strlen(HEADER1 HEADER2 FOOTER) + 64 * numentries + 4096;
|
|
LOG(LVL_DEBUG, "initially allocating result string - size: %lu bytes", allocated * sizeof(char));
|
|
result = malloc(allocated * sizeof(char));
|
|
result[0] = '\0';
|
|
|
|
// this is still safe
|
|
strcat(result, HEADER1);
|
|
|
|
sprintf(buf, "<title>Directory listing for %s</title>", url);
|
|
strcat(result, buf);
|
|
|
|
strcat(result, HEADER2);
|
|
|
|
sprintf(buf, "<h1>Directory listing for %s</h1>", url);
|
|
strcat(result, buf);
|
|
|
|
int isrootdir = strcmp(url, "/") == 0;
|
|
|
|
// Begin Navigation
|
|
strcat(result, "<p>Navigation: ");
|
|
|
|
// tokenize the URL: init
|
|
char *token, *urlclone;
|
|
|
|
urlclone = strdup(url);
|
|
token = strtok(urlclone, "/");
|
|
strcpy(fullpath, "/");
|
|
|
|
if(isrootdir) {
|
|
strcat(result, "/");
|
|
} else {
|
|
strcat(result, "<a href=\"/\">/</a>");
|
|
|
|
// create a link for each token in URL
|
|
do {
|
|
LOG(LVL_DUMP, "Found token of %s: %s", url, token);
|
|
|
|
strcat(fullpath, token);
|
|
strcat(fullpath, "/");
|
|
|
|
sprintf(buf, " <a href=\"%s\">%s</a> /", fullpath, token);
|
|
strcat(result, buf);
|
|
}
|
|
while((token = strtok(NULL, "/")) != NULL);
|
|
}
|
|
|
|
free(urlclone);
|
|
|
|
strcat(result, "</p>");
|
|
|
|
// begin listing subdirs
|
|
strcat(result, "<h2>Sub-directories</h2>");
|
|
strcat(result, "<ul>");
|
|
|
|
for(i = 0; i < numentries; i++) {
|
|
// skip all non-directories
|
|
if(!(entries[i].flags & FLG_ISDIR))
|
|
continue;
|
|
|
|
size_t entrylen = 32 + 2 * strlen(entries[i].name);
|
|
size_t resultlen = strlen(result);
|
|
|
|
if(allocated < resultlen + entrylen) {
|
|
allocated *= 2;
|
|
LOG(LVL_DEBUG, "reallocating result string (subdirs) - new size: %lu bytes", allocated * sizeof(char));
|
|
char *tmpresult = realloc(result, allocated * sizeof(char));
|
|
if(!tmpresult) {
|
|
LOG(LVL_ERR, "realloc failed for result string: %s", strerror(errno));
|
|
break;
|
|
}
|
|
|
|
result = tmpresult;
|
|
}
|
|
|
|
urlencode(entries[i].name, encName);
|
|
|
|
if(isrootdir) {
|
|
sprintf(buf, "<li><a href=\"/%s/\">%s/</a></li>\n",
|
|
encName, entries[i].name);
|
|
} else {
|
|
sprintf(buf, "<li><a href=\"%s/%s/\">%s/</a></li>\n",
|
|
url, encName, entries[i].name);
|
|
}
|
|
strcat(result, buf);
|
|
}
|
|
|
|
strcat(result, "</ul>");
|
|
|
|
// begin listing files
|
|
strcat(result, "<h2>Files</h2>");
|
|
strcat(result, "<ul>");
|
|
|
|
// here the listing entries are generated
|
|
for(i = 0; i < numentries; i++) {
|
|
// skip all directories
|
|
if(entries[i].flags & FLG_ISDIR)
|
|
continue;
|
|
|
|
size_t entrylen = 64 + 2 * strlen(entries[i].name);
|
|
size_t resultlen = strlen(result);
|
|
|
|
if(allocated < resultlen + entrylen) {
|
|
allocated *= 2;
|
|
LOG(LVL_DEBUG, "reallocating result string (files) - new size: %lu bytes", allocated * sizeof(char));
|
|
char *tmpresult = realloc(result, allocated * sizeof(char));
|
|
if(!tmpresult) {
|
|
LOG(LVL_ERR, "realloc failed for result string: %s", strerror(errno));
|
|
break;
|
|
}
|
|
|
|
result = tmpresult;
|
|
}
|
|
|
|
urlencode(entries[i].name, encName);
|
|
|
|
if(isrootdir) {
|
|
sprintf(buf, "<li><a href=\"/%s\">%s</a> [%s]</li>\n",
|
|
encName, entries[i].name, format_size(entries[i].size));
|
|
} else {
|
|
sprintf(buf, "<li><a href=\"%s/%s\">%s</a> [%s]</li>\n",
|
|
url, encName, entries[i].name, format_size(entries[i].size));
|
|
}
|
|
strcat(result, buf);
|
|
}
|
|
|
|
strcat(result, "</ul>");
|
|
strcat(result, FOOTER);
|
|
|
|
return result;
|
|
}
|
|
|
|
char* create_dirlisting(const char *url, const char *localpath) {
|
|
char *result;
|
|
struct Entry *entries;
|
|
|
|
size_t numentries, allocated;
|
|
size_t i;
|
|
|
|
DIR *dir;
|
|
struct dirent *de;
|
|
|
|
struct stat s;
|
|
|
|
char fullpath[PATH_MAX];
|
|
|
|
if((dir = opendir(localpath)) == NULL) {
|
|
LOG(LVL_ERR, "Cannot opendir %s: %s", localpath, strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
// allocate some entry pointers
|
|
allocated = 100;
|
|
numentries = 0;
|
|
|
|
entries = malloc(allocated * sizeof(struct Entry));
|
|
if(!entries) {
|
|
LOG(LVL_ERR, "malloc failed for directory entries: %s", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
// initialize all entry strings with NULL
|
|
for(i = 0; i < allocated; i++) {
|
|
entries[i].flags = 0;
|
|
}
|
|
|
|
while((de = readdir(dir)) != NULL) {
|
|
if(de->d_name[0] == '.') {
|
|
continue;
|
|
}
|
|
|
|
// check if entries array is full
|
|
if(numentries == allocated) {
|
|
// double the size of the entries array
|
|
LOG(LVL_DEBUG, "reallocating entry list - new size: %lu bytes", 2 * allocated * sizeof(struct Entry));
|
|
struct Entry *tmpentries = realloc(entries, 2 * allocated * sizeof(struct Entry));
|
|
if(!tmpentries) {
|
|
LOG(LVL_ERR, "realloc failed for directory entries: %s", strerror(errno));
|
|
free(entries);
|
|
return NULL;
|
|
}
|
|
|
|
entries = tmpentries;
|
|
|
|
// initialize the new entries
|
|
for(i = allocated; i < 2*allocated; i++) {
|
|
entries[i].flags = 0;
|
|
}
|
|
|
|
allocated *= 2;
|
|
}
|
|
|
|
snprintf(fullpath, PATH_MAX, "%s/%s", localpath, de->d_name);
|
|
|
|
if(stat(fullpath, &s) == -1) {
|
|
LOG(LVL_WARN, "Cannot stat %s while listing directories: %s",
|
|
fullpath, strerror(errno));
|
|
|
|
// stat failed -> ignore this entry
|
|
continue;
|
|
}
|
|
|
|
entries[numentries].name = strdup(de->d_name);
|
|
entries[numentries].flags |= FLG_USED;
|
|
entries[numentries].size = s.st_size;
|
|
|
|
if(S_ISDIR(s.st_mode)) {
|
|
entries[numentries].flags |= FLG_ISDIR;
|
|
}
|
|
|
|
numentries++;
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
qsort(entries, numentries, sizeof(struct Entry), compare_entries);
|
|
|
|
result = gen_html(url, entries, numentries);
|
|
|
|
for(i = 0; i < numentries; i++) {
|
|
if(entries[i].flags & FLG_USED) free(entries[i].name);
|
|
}
|
|
free(entries);
|
|
|
|
return result;
|
|
}
|
|
|