/* * vim: sw=2 ts=2 expandtab * * Copyright (c) 2013-2019 Thomas Kolb * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #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, "%lli B", (long long)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; const size_t BUFSIZE = 5*PATH_MAX; char buf[BUFSIZE]; char fullpath[PATH_MAX]; char encName[3*PATH_MAX]; // urlencode grows string by factor 3 at most // 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'; result = safe_append(result, &allocated, HEADER1); snprintf(buf, BUFSIZE, "Directory listing for %s", url); result = safe_append(result, &allocated, buf); result = safe_append(result, &allocated, HEADER2); snprintf(buf, BUFSIZE, "

Directory listing for %s

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

Navigation: "); // tokenize the URL: init char *token, *urlclone; urlclone = strdup(url); token = strtok(urlclone, "/"); strcpy(fullpath, "/"); if(isrootdir) { result = safe_append(result, &allocated, "/"); } else { result = safe_append(result, &allocated, "/"); // create a link for each token in URL do { LOG(LVL_DUMP, "Found token of %s: %s", url, token); // TODO: check if this is safe strcat(fullpath, token); strcat(fullpath, "/"); snprintf(buf, BUFSIZE, " %s /", fullpath, token); result = safe_append(result, &allocated, buf); } while((token = strtok(NULL, "/")) != NULL); } free(urlclone); result = safe_append(result, &allocated, "

"); // begin listing subdirs result = safe_append(result, &allocated, "

Sub-directories

"); result = safe_append(result, &allocated, "
    "); for(i = 0; i < numentries; i++) { // skip all non-directories if(!(entries[i].flags & FLG_ISDIR)) continue; urlencode(entries[i].name, encName); if(isrootdir) { snprintf(buf, BUFSIZE, "
  • %s/
  • \n", encName, entries[i].name); } else { snprintf(buf, BUFSIZE, "
  • %s/
  • \n", url, encName, entries[i].name); } result = safe_append(result, &allocated, buf); } result = safe_append(result, &allocated, "
"); // begin listing files result = safe_append(result, &allocated, "

Files

"); result = safe_append(result, &allocated, "
    "); // here the listing entries are generated for(i = 0; i < numentries; i++) { // skip all directories if(entries[i].flags & FLG_ISDIR) continue; urlencode(entries[i].name, encName); if(isrootdir) { snprintf(buf, BUFSIZE, "
  • %s [%s]
  • \n", encName, entries[i].name, format_size(entries[i].size)); } else { snprintf(buf, BUFSIZE, "
  • %s [%s]
  • \n", url, encName, entries[i].name, format_size(entries[i].size)); } result = safe_append(result, &allocated, buf); } result = safe_append(result, &allocated, "
"); if(uploadEnabled) { result = safe_append(result, &allocated, "

Upload

"); result = safe_append(result, &allocated, "

Upload a file to this directory:

"); result = safe_append(result, &allocated, "

" "

" " " " " "
" "

" ); } result = safe_append(result, &allocated, FOOTER); LOG(LVL_DEBUG, "result string (files) usage: %lu/%lu bytes", strlen(result), allocated); 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; }