/* * Copyright 2006 Richard Wilson * Copyright 2010 Chris Young * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** \file * Content for directory listings (implementation). */ #include #include #include #include #include #include #include #include #include "content/content_protected.h" #include "content/fetch.h" #include "render/directory.h" #include "render/html.h" #include "utils/messages.h" #include "utils/url.h" #include "utils/utils.h" #define MAX_LENGTH 2048 static const char header[] = "\n" "\n" "\n" "\n"; static const char footer[] = "</div>\n</body>\n</html>\n"; static char sizeunits[][7] = {"Bytes", "kBytes", "MBytes", "GBytes"}; static int filesize_calculate(unsigned long *bytesize); static int filesize_value(unsigned long bytesize); static char* filesize_unit(unsigned long bytesize); bool directory_create(struct content *c, const struct http_parameter *params) { if (!html_create(c, params)) /* html_create() must have broadcast MSG_ERROR already, so we * don't need to. */ return false; binding_parse_chunk(c->data.html.parser_binding, (uint8_t *) header, sizeof(header) - 1); return true; } /** * Obtain display value and units for filesize after conversion to B/kB/MB/GB, * as appropriate. * * \param bytesize file size in bytes, updated to filesize in output units * \return number of times bytesize has been divided by 1024 */ int filesize_calculate(unsigned long *bytesize) { int i = 0; while (*bytesize > 1024 * 4) { *bytesize /= 1024; i++; if (i == 3) break; } return i; } /** * Obtain display value for filesize after conversion to B/kB/MB/GB, * as appropriate * * \param bytesize file size in bytes * \return Value to display for file size, in units given by filesize_unit() */ int filesize_value(unsigned long bytesize) { filesize_calculate(&bytesize); return (int)bytesize; } /** * Obtain display units for filesize after conversion to B/kB/MB/GB, * as appropriate * * \param bytesize file size in bytes * \return Units to display for file size, for value given by filesize_value() */ char* filesize_unit(unsigned long bytesize) { return sizeunits[filesize_calculate(&bytesize)]; } bool directory_convert(struct content *c) { char *path; DIR *parent; struct dirent *entry; union content_msg_data msg_data; char buffer[MAX_LENGTH]; char *nice_path, *cnv, *tmp; url_func_result res; bool compare; char *up; struct stat filestat; char *filepath, *mimetype = NULL; int filepath_size; char moddate[100]; char modtime[100]; bool extendedinfo, evenrow = false; /* Get directory path from URL */ path = url_to_path(content__get_url(c)); if (!path) { msg_data.error = messages_get("NoMemory"); content_broadcast(c, CONTENT_MSG_ERROR, msg_data); return false; } /* Convert path for display */ nice_path = malloc(strlen(path) * 4 + 1); if (!nice_path) { msg_data.error = messages_get("MiscErr"); content_broadcast(c, CONTENT_MSG_ERROR, msg_data); return false; } /* Escape special HTML characters */ for (cnv = nice_path, tmp = path; *tmp != '\0'; tmp++) { if (*tmp == '<') { *cnv++ = '&'; *cnv++ = 'l'; *cnv++ = 't'; *cnv++ = ';'; } else if (*tmp == '>') { *cnv++ = '&'; *cnv++ = 'g'; *cnv++ = 't'; *cnv++ = ';'; } else { *cnv++ = *tmp; } } *cnv = '\0'; /* Print document title and heading */ snprintf(buffer, sizeof(buffer), "Index of %s\n\n" "\n

Index of %s

\n", nice_path, nice_path); free(nice_path); binding_parse_chunk(c->data.html.parser_binding, (uint8_t *) buffer, strlen(buffer)); /* Print parent directory link */ res = url_parent(content__get_url(c), &up); if (res == URL_FUNC_OK) { res = url_compare(content__get_url(c), up, false, &compare); if ((res == URL_FUNC_OK) && !compare) { if (up[strlen(up) - 1] == '/') up[strlen(up) - 1] = '\0'; snprintf(buffer, sizeof(buffer), "

%s

", up, messages_get("FileParent")); binding_parse_chunk(c->data.html.parser_binding, (uint8_t *) buffer, strlen(buffer)); } free(up); } /* Print directory contents table column headings */ snprintf(buffer, sizeof(buffer), "
\n" "%s " "%s " "%s " "%s " "%s\n", messages_get("FileName"), messages_get("FileType"), messages_get("FileSize"), messages_get("FileDate"), messages_get("FileTime")); binding_parse_chunk(c->data.html.parser_binding, (uint8_t *) buffer, strlen(buffer)); if ((parent = opendir(path)) == NULL) { msg_data.error = messages_get("EmptyErr"); content_broadcast(c, CONTENT_MSG_ERROR, msg_data); return false; } /* Print a row for each item in the directory */ while ((entry = readdir(parent)) != NULL) { if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) /* Skip . and .. entries */ continue; filepath_size = strlen(path) + strlen(entry->d_name) + 2; filepath = malloc(filepath_size); if (filepath != NULL) { strcpy(filepath, path); if (path_add_part(filepath, filepath_size, entry->d_name) == false) { msg_data.error = messages_get("MiscErr"); content_broadcast(c, CONTENT_MSG_ERROR, msg_data); return false; } if (stat(filepath, &filestat) == 0) extendedinfo = true; else extendedinfo = false; } else { msg_data.error = messages_get("MiscErr"); content_broadcast(c, CONTENT_MSG_ERROR, msg_data); return false; } /* Start row and print item name */ snprintf(buffer, sizeof(buffer), "" "%s ", content__get_url(c), entry->d_name, evenrow ? "even" : "odd", S_ISDIR(filestat.st_mode) ? "dir" : "file", entry->d_name); binding_parse_chunk(c->data.html.parser_binding, (uint8_t *) buffer, strlen(buffer)); if (extendedinfo == true) { /* Get date in output format */ if (strftime((char *)&moddate, sizeof moddate, "%a %d %b %Y", localtime(&filestat.st_mtime)) == 0) strncpy(moddate, "-", sizeof moddate); /* Get time in output format */ if (strftime((char *)&modtime, sizeof modtime, "%H:%M", localtime(&filestat.st_mtime)) == 0) strncpy(modtime, "-", sizeof modtime); if (S_ISDIR(filestat.st_mode)) { /* Directory: Print type and date/time */ snprintf(buffer, sizeof(buffer), "%s " "" " " "%s " "%s\n", messages_get("FileDirectory"), moddate, modtime); } else { /* File: Print type, size, and date/time */ mimetype = fetch_mimetype(filepath); snprintf(buffer, sizeof(buffer), "%s " "%d" "%s " "%s " "%s\n", mimetype, filesize_value( (unsigned long)filestat.st_size), messages_get(filesize_unit( (unsigned long)filestat.st_size)), moddate, modtime); } } else { /* Not got info, print empty cells */ snprintf(buffer, sizeof(buffer), " " "" " " " " "\n"); } binding_parse_chunk(c->data.html.parser_binding, (uint8_t *) buffer, strlen(buffer)); if (evenrow == false) evenrow = true; else evenrow = false; if (mimetype != NULL) { free(mimetype); mimetype = NULL; } free(filepath); } closedir(parent); binding_parse_chunk(c->data.html.parser_binding, (uint8_t *) footer, sizeof(footer) - 1); c->type = CONTENT_HTML; return html_convert(c); } void directory_destroy(struct content *c) { /* This will only get called if the content is destroyed before * content_convert() is called. Simply force the type to HTML and * delegate the cleanup to html_destroy() */ c->type = CONTENT_HTML; html_destroy(c); return; } bool directory_clone(const struct content *old, struct content *new_content) { /* This will only get called if the content is cloned before * content_convert() is called. Simply replay creation. */ if (directory_create(new_content, NULL) == false) return false; return true; }