/* * vim: sw=2 ts=2 expandtab * * "THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"): * Thomas Kolb wrote this file. As long as you retain this * notice you can do whatever you want with this stuff. If we meet some day, * and you think this stuff is worth it, you can buy me a pizza in return. * - Thomas Kolb */ #include #include #include #include #include #include #include #include #include #include #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, int uploadEnabled) { 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, "Directory listing for %s", url); strcat(result, buf); strcat(result, HEADER2); sprintf(buf, "

Directory listing for %s

", url); strcat(result, buf); int isrootdir = strcmp(url, "/") == 0; // Begin Navigation strcat(result, "

Navigation: "); // tokenize the URL: init char *token, *urlclone; urlclone = strdup(url); token = strtok(urlclone, "/"); strcpy(fullpath, "/"); if(isrootdir) { strcat(result, "/"); } else { strcat(result, "/"); // 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, " %s /", fullpath, token); strcat(result, buf); } while((token = strtok(NULL, "/")) != NULL); } free(urlclone); strcat(result, "

"); // begin listing subdirs strcat(result, "

Sub-directories

"); strcat(result, "
    "); 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, "
  • %s/
  • \n", encName, entries[i].name); } else { sprintf(buf, "
  • %s/
  • \n", url, encName, entries[i].name); } strcat(result, buf); } strcat(result, "
"); // begin listing files strcat(result, "

Files

"); strcat(result, "
    "); // 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, "
  • %s [%s]
  • \n", encName, entries[i].name, format_size(entries[i].size)); } else { sprintf(buf, "
  • %s [%s]
  • \n", url, encName, entries[i].name, format_size(entries[i].size)); } strcat(result, buf); } strcat(result, "
"); if(uploadEnabled) { strcat(result, "

Upload

"); strcat(result, "

Upload a file to this directory

"); } strcat(result, FOOTER); return result; } char* create_dirlisting(const char *url, const char *localpath, int uploadEnabled) { 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, uploadEnabled); for(i = 0; i < numentries; i++) { if(entries[i].flags & FLG_USED) free(entries[i].name); } free(entries); return result; }