fileshare/dirlisting.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;
}