summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2010-09-09 21:45:59 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2010-09-09 21:45:59 +0000
commit2077918805c175ecefeb25dd38f78d2d5b3a3ca2 (patch)
tree73fd0d27d042d97aa9fe67032697bd21f8deee9a
parentb9773d34bb523d3bac09f328900e60b02feea97b (diff)
downloadnetsurf-2077918805c175ecefeb25dd38f78d2d5b3a3ca2.tar.gz
netsurf-2077918805c175ecefeb25dd38f78d2d5b3a3ca2.tar.bz2
Merge branches/vince/netsurf-file-fetcher to trunk
r=jmb svn path=/trunk/netsurf/; revision=10750
-rw-r--r--Makefile.sources4
-rw-r--r--amiga/filetype.c5
-rw-r--r--content/content.c5
-rw-r--r--content/content_type.h1
-rw-r--r--content/dirlist.c46
-rw-r--r--content/dirlist.h8
-rw-r--r--content/fetch.c2
-rw-r--r--content/fetchers/fetch_curl.c64
-rw-r--r--content/fetchers/fetch_file.c617
-rw-r--r--content/fetchers/fetch_file.h28
-rw-r--r--render/box.c1
-rw-r--r--render/directory.c291
-rw-r--r--render/directory.h38
-rw-r--r--utils/config.h16
-rw-r--r--utils/url.c25
-rw-r--r--utils/url.h1
16 files changed, 733 insertions, 419 deletions
diff --git a/Makefile.sources b/Makefile.sources
index 29898f535..4a8a57e14 100644
--- a/Makefile.sources
+++ b/Makefile.sources
@@ -6,9 +6,9 @@
#
S_CONTENT := content.c dirlist.c fetch.c hlcache.c llcache.c urldb.c \
- fetchers/fetch_curl.c fetchers/fetch_data.c
+ fetchers/fetch_curl.c fetchers/fetch_data.c fetchers/fetch_file.c
S_CSS := css.c dump.c internal.c select.c utils.c
-S_RENDER := box.c box_construct.c box_normalise.c directory.c favicon.c \
+S_RENDER := box.c box_construct.c box_normalise.c favicon.c \
font.c form.c html.c html_interaction.c html_redraw.c \
hubbub_binding.c imagemap.c layout.c list.c table.c textplain.c
S_UTILS := base64.c filename.c hashtable.c http.c locale.c messages.c \
diff --git a/amiga/filetype.c b/amiga/filetype.c
index cb5411923..790a2404a 100644
--- a/amiga/filetype.c
+++ b/amiga/filetype.c
@@ -186,11 +186,6 @@ const char *ami_content_type_to_file_type(content_type type)
return "rosprite";
break;
#endif
-
- case CONTENT_DIRECTORY:
- return "drawer";
- break;
-
#if defined(WITH_NS_SVG) || defined(WITH_RSVG)
case CONTENT_SVG:
return "svg";
diff --git a/content/content.c b/content/content.c
index 3f81de3a9..5165c6b50 100644
--- a/content/content.c
+++ b/content/content.c
@@ -38,7 +38,6 @@
#include "image/bitmap.h"
#include "desktop/browser.h"
#include "desktop/options.h"
-#include "render/directory.h"
#include "render/html.h"
#include "render/textplain.h"
#ifdef WITH_JPEG
@@ -109,7 +108,6 @@ static const struct mime_entry mime_map[] = {
#ifdef WITH_BMP
{"application/x-ico", CONTENT_ICO},
#endif
- {"application/x-netsurf-directory", CONTENT_DIRECTORY},
#ifdef WITH_THEME_INSTALL
{"application/x-netsurf-theme", CONTENT_THEME},
#endif
@@ -344,9 +342,6 @@ static const struct handler_entry handler_map[] = {
plugin_open, plugin_close, plugin_clone,
true},
#endif
- {directory_create, 0, directory_convert,
- 0, directory_destroy, 0, 0, 0, 0, 0, 0, 0, directory_clone,
- true},
#ifdef WITH_THEME_INSTALL
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false},
#endif
diff --git a/content/content_type.h b/content/content_type.h
index b183a45e8..23cdc6203 100644
--- a/content/content_type.h
+++ b/content/content_type.h
@@ -59,7 +59,6 @@ typedef enum {
#ifdef WITH_PLUGIN
CONTENT_PLUGIN,
#endif
- CONTENT_DIRECTORY,
#ifdef WITH_THEME_INSTALL
CONTENT_THEME,
#endif
diff --git a/content/dirlist.c b/content/dirlist.c
index bbe120296..1f58154e2 100644
--- a/content/dirlist.c
+++ b/content/dirlist.c
@@ -51,9 +51,10 @@ static char* dirlist_filesize_unit(unsigned long bytesize);
* dirlist_generate_bottom()
*/
-const char* dirlist_generate_top(void)
+bool dirlist_generate_top(char *buffer, int buffer_length)
{
- return "<html>\n"
+ int error = snprintf(buffer, buffer_length,
+ "<html>\n"
"<head>\n"
"<style>\n"
"html, body { margin: 0; padding: 0; }\n"
@@ -61,7 +62,7 @@ const char* dirlist_generate_top(void)
"h1 { padding: 5mm; margin: 0; "
"border-bottom: 2px solid #bcf; }\n"
"p { padding: 2px 5mm; margin: 0; }\n"
- "div { display: table; width: 94%; margin: 5mm auto 0 auto; "
+ "div { display: table; width: 94%%; margin: 5mm auto 0 auto; "
"padding: 0; }\n"
"a, strong { display: table-row; margin: 0; padding: 0; }\n"
"a.odd { background-color: #bcf; }\n"
@@ -74,7 +75,14 @@ const char* dirlist_generate_top(void)
"a.dir > span.type { font-weight: bold; }\n"
"span.size { text-align: right; padding-right: 0.3em; }\n"
"span.size + span.size { text-align: left; "
- "padding-right: 0; }\n";
+ "padding-right: 0; }\n");
+ if (error < 0 || error >= buffer_length)
+ /* Error or buffer too small */
+ return false;
+ else
+ /* OK */
+ return true;
+
}
@@ -142,9 +150,14 @@ bool dirlist_generate_hide_columns(int flags, char *buffer, int buffer_length)
* dirlist_generate_bottom()
*/
-bool dirlist_generate_title(char *title, char *buffer, int buffer_length)
+bool dirlist_generate_title(const char *title, char *buffer, int buffer_length)
{
- int error = snprintf(buffer, buffer_length,
+ int error;
+
+ if (title == NULL)
+ title = "";
+
+ error = snprintf(buffer, buffer_length,
"</style>\n"
"<title>%s</title>\n"
"</head>\n"
@@ -245,7 +258,7 @@ bool dirlist_generate_headings(char *buffer, int buffer_length)
* \param directory whether this row is for a directory (or a file)
* \param url url for row entry
* \param name name of row entry
- * \param type MIME type of row entry
+ * \param mimetype MIME type of row entry
* \param size size of row entry. If negative, size is left blank
* \param date date row entry was last modified
* \param time time row entry was last modified
@@ -266,7 +279,7 @@ bool dirlist_generate_headings(char *buffer, int buffer_length)
*/
bool dirlist_generate_row(bool even, bool directory, char *url, char *name,
- char *type, long long size, char *date, char *time,
+ const char *mimetype, long long size, char *date, char *time,
char *buffer, int buffer_length)
{
const char *unit;
@@ -292,7 +305,7 @@ bool dirlist_generate_row(bool even, bool directory, char *url, char *name,
"<span class=\"time\">%s</span></a>\n",
url, even ? "even" : "odd",
directory ? "dir" : "file",
- name, type, size_string, unit, date, time);
+ name, mimetype, size_string, unit, date, time);
if (error < 0 || error >= buffer_length)
/* Error or buffer too small */
return false;
@@ -319,11 +332,18 @@ bool dirlist_generate_row(bool even, bool directory, char *url, char *name,
* dirlist_generate_bottom()
*/
-const char* dirlist_generate_bottom(void)
+bool dirlist_generate_bottom(char *buffer, int buffer_length)
{
- return "</div>\n"
- "</body>\n"
- "</html>\n";
+ int error = snprintf(buffer, buffer_length,
+ "</div>\n"
+ "</body>\n"
+ "</html>\n");
+ if (error < 0 || error >= buffer_length)
+ /* Error or buffer too small */
+ return false;
+ else
+ /* OK */
+ return true;
}
diff --git a/content/dirlist.h b/content/dirlist.h
index 9b369144e..7228ae231 100644
--- a/content/dirlist.h
+++ b/content/dirlist.h
@@ -33,15 +33,15 @@
#define DIRLIST_NO_DATE_COLUMN 1 << 3
#define DIRLIST_NO_TIME_COLUMN 1 << 4
-const char* dirlist_generate_top(void);
+bool dirlist_generate_top(char *buffer, int buffer_length);
bool dirlist_generate_hide_columns(int flags, char *buffer, int buffer_length);
-bool dirlist_generate_title(char *title, char *buffer, int buffer_length);
+bool dirlist_generate_title(const char *title, char *buffer, int buffer_length);
bool dirlist_generate_parent_link(char *parent, char *buffer,
int buffer_length);
bool dirlist_generate_headings(char *buffer, int buffer_length);
bool dirlist_generate_row(bool even, bool directory, char *url, char *name,
- char *type, long long size, char *date, char *time,
+ const char *mimetype, long long size, char *date, char *time,
char *buffer, int buffer_length);
-const char* dirlist_generate_bottom(void);
+bool dirlist_generate_bottom(char *buffer, int buffer_length);
#endif
diff --git a/content/fetch.c b/content/fetch.c
index b1caba0d6..4cd1ed31a 100644
--- a/content/fetch.c
+++ b/content/fetch.c
@@ -38,6 +38,7 @@
#include "content/fetch.h"
#include "content/fetchers/fetch_curl.h"
#include "content/fetchers/fetch_data.h"
+#include "content/fetchers/fetch_file.h"
#include "content/urldb.h"
#include "desktop/netsurf.h"
#include "desktop/options.h"
@@ -106,6 +107,7 @@ void fetch_init(void)
{
fetch_curl_register();
fetch_data_register();
+ fetch_file_register();
fetch_active = false;
}
diff --git a/content/fetchers/fetch_curl.c b/content/fetchers/fetch_curl.c
index e53c844f1..343ffd89f 100644
--- a/content/fetchers/fetch_curl.c
+++ b/content/fetchers/fetch_curl.c
@@ -217,7 +217,10 @@ void fetch_curl_register(void)
data = curl_version_info(CURLVERSION_NOW);
- for (i = 0; data->protocols[i]; i++)
+ for (i = 0; data->protocols[i]; i++) {
+ if (strcmp(data->protocols[i], "file") == 0)
+ continue; /* do not use curl for file: */
+
if (!fetch_add_fetcher(data->protocols[i],
fetch_curl_initialise,
fetch_curl_setup,
@@ -229,6 +232,7 @@ void fetch_curl_register(void)
LOG(("Unable to register cURL fetcher for %s",
data->protocols[i]));
}
+ }
return;
curl_easy_setopt_failed:
@@ -1155,64 +1159,6 @@ bool fetch_curl_process_headers(struct curl_fetch_info *f)
return true;
}
- /* find MIME type from filetype for local files */
- if (strncmp(f->url, FILE_SCHEME_PREFIX, FILE_SCHEME_PREFIX_LEN) == 0) {
- struct stat s;
- char *url_path = url_to_path(f->url);
-
- LOG(("Obtaining mime type for file %s", url_path));
-
- if (url_path != NULL && stat(url_path, &s) == 0) {
- /* file: URL and file exists */
- char header[64];
- const char *type;
-
- /* create etag */
- snprintf(header, sizeof header,
- "ETag: \"%10" PRId64 "\"",
- (int64_t) s.st_mtime);
- /* And send it to the header handler */
- fetch_send_callback(FETCH_HEADER, f->fetch_handle,
- header, strlen(header),
- FETCH_ERROR_NO_ERROR);
-
- /* create Content-Type */
- type = fetch_filetype(url_path);
- snprintf(header, sizeof header,
- "Content-Type: %s", type);
- /* Send it to the header handler */
- fetch_send_callback(FETCH_HEADER, f->fetch_handle,
- header, strlen(header),
- FETCH_ERROR_NO_ERROR);
-
- /* create Content-Length */
- snprintf(header, sizeof header,
- "Content-Length: %" PRId64,
- (int64_t) s.st_size);
- /* Send it to the header handler */
- fetch_send_callback(FETCH_HEADER, f->fetch_handle,
- header, strlen(header),
- FETCH_ERROR_NO_ERROR);
-
- /* don't set last modified time so as to ensure that
- * local files are revalidated at all times. */
-
- /* Report not modified, if appropriate */
- if (f->last_modified && f->file_etag &&
- f->last_modified > s.st_mtime &&
- f->file_etag == s.st_mtime) {
- fetch_send_callback(FETCH_NOTMODIFIED,
- f->fetch_handle, 0, 0,
- FETCH_ERROR_NO_ERROR);
- free(url_path);
- return true;
- }
- }
-
- if (url_path != NULL)
- free(url_path);
- }
-
if (f->abort)
return true;
diff --git a/content/fetchers/fetch_file.c b/content/fetchers/fetch_file.c
new file mode 100644
index 000000000..19270c70a
--- /dev/null
+++ b/content/fetchers/fetch_file.c
@@ -0,0 +1,617 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* file: URL handling. Based on the data fetcher by Rob Kendrick */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdarg.h>
+
+#include "utils/config.h"
+#include "content/dirlist.h"
+#include "content/fetch.h"
+#include "content/fetchers/fetch_file.h"
+#include "content/urldb.h"
+#include "desktop/netsurf.h"
+#include "desktop/options.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/url.h"
+#include "utils/utils.h"
+#include "utils/ring.h"
+
+/* Maximum size of read buffer */
+#define FETCH_FILE_MAX_BUF_SIZE (1024 * 1024)
+
+/** Context for a fetch */
+struct fetch_file_context {
+ struct fetch_file_context *r_next, *r_prev;
+
+ struct fetch *fetchh; /**< Handle for this fetch */
+
+ bool aborted; /**< Flag indicating fetch has been aborted */
+ bool locked; /**< Flag indicating entry is already entered */
+
+ char *url; /**< The full url the fetch refers to */
+ char *path; /**< The actual path to be used with open() */
+};
+
+static struct fetch_file_context *ring = NULL;
+
+/** issue fetch callbacks with locking */
+static inline bool fetch_file_send_callback(fetch_msg msg,
+ struct fetch_file_context *ctx, const void *data,
+ unsigned long size, fetch_error_code errorcode)
+{
+ ctx->locked = true;
+ fetch_send_callback(msg, ctx->fetchh, data, size, errorcode);
+ ctx->locked = false;
+
+ return ctx->aborted;
+}
+
+static bool fetch_file_send_header(struct fetch_file_context *ctx, const char *fmt, ...)
+{
+ char header[64];
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ vsnprintf(header, sizeof header, fmt, ap);
+
+ va_end(ap);
+
+ fetch_file_send_callback(FETCH_HEADER, ctx, header, strlen(header), FETCH_ERROR_NO_ERROR);
+
+ return ctx->aborted;
+}
+
+static bool fetch_file_send_time(struct fetch_file_context *ctx, const char *fmt, const time_t *val)
+{
+ char header[64];
+ struct tm btm;
+
+ gmtime_r(val, &btm);
+
+ strftime(header, sizeof header, fmt, &btm);
+
+ fetch_file_send_callback(FETCH_HEADER, ctx, header, strlen(header), FETCH_ERROR_NO_ERROR);
+
+ return ctx->aborted;
+}
+
+/** callback to initialise the file fetcher. */
+static bool fetch_file_initialise(const char *scheme)
+{
+ return true;
+}
+
+/** callback to initialise the file fetcher. */
+static void fetch_file_finalise(const char *scheme)
+{
+}
+
+/** callback to set up a file fetch context. */
+static void *
+fetch_file_setup(struct fetch *fetchh,
+ const char *url,
+ bool only_2xx,
+ const char *post_urlenc,
+ const struct fetch_multipart_data *post_multipart,
+ const char **headers)
+{
+ struct fetch_file_context *ctx;
+ char *path;
+ url_func_result res; /* result from url routines */
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ res = url_path(url, &path);
+ if (res != URL_FUNC_OK) {
+ free(ctx);
+ return NULL;
+ }
+
+ res = url_unescape(path, &ctx->path);
+ free(path);
+ if (res != URL_FUNC_OK) {
+ free(ctx);
+ return NULL;
+ }
+
+ ctx->url = strdup(url);
+ if (ctx->url == NULL) {
+ free(ctx->path);
+ free(ctx);
+ return NULL;
+ }
+
+ ctx->fetchh = fetchh;
+
+ RING_INSERT(ring, ctx);
+
+ return ctx;
+}
+
+/** callback to free a file fetch */
+static void fetch_file_free(void *ctx)
+{
+ struct fetch_file_context *c = ctx;
+ free(c->url);
+ free(c->path);
+ RING_REMOVE(ring, c);
+ free(ctx);
+}
+
+/** callback to start a file fetch */
+static bool fetch_file_start(void *ctx)
+{
+ return true;
+}
+
+/** callback to abort a file fetch */
+static void fetch_file_abort(void *ctx)
+{
+ struct fetch_file_context *c = ctx;
+
+ /* To avoid the poll loop having to deal with the fetch context
+ * disappearing from under it, we simply flag the abort here.
+ * The poll loop itself will perform the appropriate cleanup.
+ */
+ c->aborted = true;
+}
+
+static void fetch_file_process_error(struct fetch_file_context *ctx, int code)
+{
+ char buffer[1024];
+ const char *title;
+ char key[8];
+
+ /* content is going to return error code */
+ fetch_set_http_code(ctx->fetchh, code);
+
+ /* content type */
+ if (fetch_file_send_header(ctx, "Content-Type: text/html"))
+ goto fetch_file_process_error_aborted;
+
+ snprintf(key, sizeof key, "HTTP%03d", code);
+ title = messages_get(key);
+
+ snprintf(buffer, sizeof buffer, "<html><head><title>%s</title></head><body><h1>%s</h1><p>Error %d while fetching file %s</p></body></html>", title, title, code,ctx->url);
+
+ if (fetch_file_send_callback(FETCH_DATA, ctx, buffer, strlen(buffer), FETCH_ERROR_NO_ERROR))
+ goto fetch_file_process_error_aborted;
+
+ fetch_file_send_callback(FETCH_FINISHED, ctx, 0, 0, FETCH_ERROR_NO_ERROR);
+
+fetch_file_process_error_aborted:
+ return;
+}
+
+
+/** Process object as a regular file */
+static void fetch_file_process_plain(struct fetch_file_context *ctx,
+ int fd, struct stat *fdstat)
+{
+ char *buf;
+ size_t buf_size;
+
+ ssize_t tot_read = 0;
+ ssize_t res;
+
+ /* set buffer size */
+ buf_size = fdstat->st_size;
+ if (buf_size > FETCH_FILE_MAX_BUF_SIZE)
+ buf_size = FETCH_FILE_MAX_BUF_SIZE;
+
+ /* allocate the buffer storage */
+ buf = malloc(buf_size);
+ if (buf == NULL) {
+ fetch_file_send_callback(FETCH_ERROR, ctx,
+ "Unable to allocate memory for file data buffer",
+ 0, FETCH_ERROR_MEMORY);
+ return;
+ }
+
+ /* fetch is going to be successful */
+ fetch_set_http_code(ctx->fetchh, 200);
+
+ /* Any callback can result in the fetch being aborted.
+ * Therefore, we _must_ check for this after _every_ call to
+ * fetch_file_send_callback().
+ */
+
+ /* content type */
+ if (fetch_file_send_header(ctx, "Content-Type: %s", fetch_filetype(ctx->path)))
+ goto fetch_file_process_aborted;
+
+ /* content length */
+ if (fetch_file_send_header(ctx, "Content-Length: %zd", fdstat->st_size))
+ goto fetch_file_process_aborted;
+
+ /* Set Last modified header */
+ if (fetch_file_send_time(ctx, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT", &fdstat->st_mtime))
+ goto fetch_file_process_aborted;
+
+ /* create etag */
+ if (fetch_file_send_header(ctx, "ETag: \"%10" PRId64 "\"", (int64_t) fdstat->st_mtime))
+ goto fetch_file_process_aborted;
+
+ /* main data loop */
+ do {
+ res = read(fd, buf, buf_size);
+ if (res == -1) {
+ fetch_file_send_callback(FETCH_ERROR, ctx, "Error reading file", 0, FETCH_ERROR_PARTIAL_FILE);
+ goto fetch_file_process_aborted;
+ }
+
+ if (res == 0) {
+ fetch_file_send_callback(FETCH_ERROR, ctx, "Unexpected EOF reading file", 0, FETCH_ERROR_PARTIAL_FILE);
+ goto fetch_file_process_aborted;
+ }
+
+ tot_read += res;
+
+ if (fetch_file_send_callback(FETCH_DATA, ctx, buf, res, FETCH_ERROR_NO_ERROR))
+ break;
+
+ } while (tot_read < fdstat->st_size);
+
+ if (!ctx->aborted)
+ fetch_file_send_callback(FETCH_FINISHED, ctx, 0, 0, FETCH_ERROR_NO_ERROR);
+
+fetch_file_process_aborted:
+
+ close(fd);
+ free(buf);
+ return;
+}
+
+static char *gen_nice_title(char *path)
+{
+ char *nice_path, *cnv, *tmp;
+ char *title;
+ int title_length;
+
+ /* Convert path for display */
+ nice_path = malloc(strlen(path) * SLEN("&amp;") + 1);
+ if (nice_path == NULL) {
+ return NULL;
+ }
+
+ /* 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 if (*tmp == '&') {
+ *cnv++ = '&';
+ *cnv++ = 'a';
+ *cnv++ = 'm';
+ *cnv++ = 'p';
+ *cnv++ = ';';
+ } else {
+ *cnv++ = *tmp;
+ }
+ }
+ *cnv = '\0';
+
+ /* Construct a localised title string */
+ title_length = (cnv - nice_path) + strlen(messages_get("FileIndex"));
+ title = malloc(title_length + 1);
+
+ if (title == NULL) {
+ free(nice_path);
+ return NULL;
+ }
+
+ /* Set title to localised "Index of <nice_path>" */
+ snprintf(title, title_length, messages_get("FileIndex"), nice_path);
+
+ free(nice_path);
+
+ return title;
+}
+
+
+static void fetch_file_process_dir(struct fetch_file_context *ctx,
+ int fd, struct stat *fdstat)
+{
+ char buffer[1024]; /* Output buffer */
+ bool even = false; /* formatting flag */
+ char *title; /* pretty printed title */
+ url_func_result res; /* result from url routines */
+ char *up; /* url of parent */
+ bool compare; /* result of url compare */
+
+ DIR *scandir; /* handle for enumerating the directory */
+ struct dirent* ent; /* leaf directory entry */
+ struct stat ent_stat; /* stat result of leaf entry */
+ char datebuf[64]; /* buffer for date text */
+ char timebuf[64]; /* buffer for time text */
+ char urlpath[PATH_MAX]; /* buffer for leaf entry path */
+
+#if defined(HAVE_FDOPENDIR)
+ scandir = fdopendir(fd);
+#else
+ /* this poses the possibility of a race where the directory
+ * has been removed from the namespace or resources for more
+ * fd are now unavailable between the previous open() and this
+ * call.
+ */
+ close(fd);
+ scandir = opendir(ctx->path);
+#endif
+
+ if (scandir == NULL) {
+ fetch_file_process_error(ctx, 500);
+ return;
+ }
+
+ /* fetch is going to be successful */
+ fetch_set_http_code(ctx->fetchh, 200);
+
+ /* content type */
+ if (fetch_file_send_header(ctx, "Content-Type: text/html"))
+ goto fetch_file_process_dir_aborted;
+
+ /* directory listing top */
+ dirlist_generate_top(buffer, sizeof buffer);
+ if (fetch_file_send_callback(FETCH_DATA, ctx, buffer, strlen(buffer), FETCH_ERROR_NO_ERROR))
+ goto fetch_file_process_dir_aborted;
+
+ /* directory listing title */
+ title = gen_nice_title(ctx->path);
+ dirlist_generate_title(title, buffer, sizeof buffer);
+ free(title);
+ if (fetch_file_send_callback(FETCH_DATA, ctx, buffer, strlen(buffer), FETCH_ERROR_NO_ERROR))
+ goto fetch_file_process_dir_aborted;
+
+ /* Print parent directory link */
+ res = url_parent(ctx->url, &up);
+ if (res == URL_FUNC_OK) {
+ res = url_compare(ctx->url, up, false, &compare);
+ if ((res == URL_FUNC_OK) && !compare) {
+ dirlist_generate_parent_link(up, buffer, sizeof buffer);
+
+ fetch_file_send_callback(FETCH_DATA, ctx,
+ buffer,
+ strlen(buffer),
+ FETCH_ERROR_NO_ERROR);
+
+ }
+ free(up);
+
+ if (ctx->aborted)
+ goto fetch_file_process_dir_aborted;
+
+ }
+
+ /* directory list headings */
+ dirlist_generate_headings(buffer, sizeof buffer);
+ if (fetch_file_send_callback(FETCH_DATA, ctx, buffer, strlen(buffer), FETCH_ERROR_NO_ERROR))
+ goto fetch_file_process_dir_aborted;
+
+ while ((ent = readdir(scandir)) != NULL) {
+
+ if (ent->d_name[0] == '.')
+ continue;
+
+ snprintf(urlpath,
+ sizeof urlpath,
+ "%s%s%s",
+ ctx->path,
+ (ctx->path[strlen(ctx->path) - 1] == '/')?"":"/" ,
+ ent->d_name);
+
+ if (stat(urlpath, &ent_stat) != 0) {
+ ent_stat.st_mode = 0;
+ datebuf[0] = 0;
+ timebuf[0] = 0;
+ } else {
+ /* Get date in output format */
+ if (strftime((char *)&datebuf, sizeof datebuf,
+ "%a %d %b %Y",
+ localtime(&ent_stat.st_mtime)) == 0) {
+ strncpy(datebuf, "-", sizeof datebuf);
+ }
+
+ /* Get time in output format */
+ if (strftime((char *)&timebuf, sizeof timebuf,
+ "%H:%M",
+ localtime(&ent_stat.st_mtime)) == 0) {
+ strncpy(timebuf, "-", sizeof timebuf);
+ }
+ }
+
+ if (S_ISREG(ent_stat.st_mode)) {
+ /* regular file */
+ dirlist_generate_row(even,
+ false,
+ urlpath,
+ ent->d_name,
+ fetch_filetype(urlpath),
+ ent_stat.st_size,
+ datebuf, timebuf,
+ buffer, sizeof(buffer));
+ } else if (S_ISDIR(ent_stat.st_mode)) {
+ /* directory */
+ dirlist_generate_row(even,
+ true,
+ urlpath,
+ ent->d_name,
+ messages_get("FileDirectory"),
+ -1,
+ datebuf, timebuf,
+ buffer, sizeof(buffer));
+ } else {
+ /* something else */
+ dirlist_generate_row(even,
+ false,
+ urlpath,
+ ent->d_name,
+ "",
+ -1,
+ datebuf, timebuf,
+ buffer, sizeof(buffer));
+ }
+
+ if (fetch_file_send_callback(FETCH_DATA, ctx,
+ buffer,
+ strlen(buffer),
+ FETCH_ERROR_NO_ERROR))
+ goto fetch_file_process_dir_aborted;
+
+ even = !even;
+ }
+
+ /* directory listing bottom */
+ dirlist_generate_bottom(buffer, sizeof buffer);
+ if (fetch_file_send_callback(FETCH_DATA, ctx, buffer, strlen(buffer), FETCH_ERROR_NO_ERROR))
+ goto fetch_file_process_dir_aborted;
+
+
+ fetch_file_send_callback(FETCH_FINISHED, ctx, 0, 0, FETCH_ERROR_NO_ERROR);
+
+fetch_file_process_dir_aborted:
+
+ closedir(scandir);
+}
+
+
+/* process a file fetch */
+static void fetch_file_process(struct fetch_file_context *ctx)
+{
+ int fd; /**< The file descriptor of the object */
+ struct stat fdstat; /**< The objects stat */
+
+ fd = open(ctx->path, O_RDONLY);
+ if (fd < 0) {
+ /* process errors as appropriate */
+ switch (errno) {
+ case EACCES:
+ fetch_file_process_error(ctx, 403);
+ break;
+
+ case ENOENT:
+ fetch_file_process_error(ctx, 404);
+ break;
+
+ default:
+ fetch_file_process_error(ctx, 500);
+ break;
+ }
+ return;
+ }
+
+ if (fstat(fd, &fdstat) != 0) {
+ /* process errors as appropriate */
+ close(fd);
+ fetch_file_process_error(ctx, 500);
+ return;
+ }
+
+ if (S_ISDIR(fdstat.st_mode)) {
+ /* directory listing */
+ fetch_file_process_dir(ctx, fd, &fdstat);
+ return;
+ } else if (S_ISREG(fdstat.st_mode)) {
+ /* regular file */
+ fetch_file_process_plain(ctx, fd, &fdstat);
+ return;
+ } else {
+ /* unhandled type of file */
+ close(fd);
+ fetch_file_process_error(ctx, 501);
+ }
+
+ return;
+}
+
+/** callback to poll for additional file fetch contents */
+static void fetch_file_poll(const char *scheme)
+{
+ struct fetch_file_context *c, *next;
+
+ if (ring == NULL) return;
+
+ /* Iterate over ring, processing each pending fetch */
+ c = ring;
+ do {
+ /* Take a copy of the next pointer as we may destroy
+ * the ring item we're currently processing */
+ next = c->r_next;
+
+ /* Ignore fetches that have been flagged as locked.
+ * This allows safe re-entrant calls to this function.
+ * Re-entrancy can occur if, as a result of a callback,
+ * the interested party causes fetch_poll() to be called
+ * again.
+ */
+ if (c->locked == true) {
+ continue;
+ }
+
+ /* Only process non-aborted fetches */
+ if (!c->aborted) {
+ /* file fetches can be processed in one go */
+ fetch_file_process(c);
+ }
+
+
+ fetch_remove_from_queues(c->fetchh);
+ fetch_free(c->fetchh);
+
+ /* Advance to next ring entry, exiting if we've reached
+ * the start of the ring or the ring has become empty
+ */
+ } while ( (c = next) != ring && ring != NULL);
+}
+
+void fetch_file_register(void)
+{
+ fetch_add_fetcher("file",
+ fetch_file_initialise,
+ fetch_file_setup,
+ fetch_file_start,
+ fetch_file_abort,
+ fetch_file_free,
+ fetch_file_poll,
+ fetch_file_finalise);
+}
diff --git a/content/fetchers/fetch_file.h b/content/fetchers/fetch_file.h
new file mode 100644
index 000000000..d1621b9ba
--- /dev/null
+++ b/content/fetchers/fetch_file.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * file: URL method handler
+ */
+
+#ifndef NETSURF_CONTENT_FETCHERS_FETCH_FILE_H
+#define NETSURF_CONTENT_FETCHERS_FETCH_FILE_H
+
+void fetch_file_register(void);
+
+#endif
diff --git a/render/box.c b/render/box.c
index 439ae0353..6be898057 100644
--- a/render/box.c
+++ b/render/box.c
@@ -1169,7 +1169,6 @@ bool box_duplicate_main_tree(struct box *box, struct content *c, int *count)
#ifdef WITH_PLUGIN
content_get_type(box->object) == CONTENT_PLUGIN ||
#endif
- content_get_type(box->object) == CONTENT_DIRECTORY ||
#ifdef WITH_THEME_INSTALL
content_get_type(box->object) == CONTENT_THEME ||
#endif
diff --git a/render/directory.c b/render/directory.c
deleted file mode 100644
index cdffdd0f1..000000000
--- a/render/directory.c
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright 2006 Richard Wilson <info@tinct.net>
- * Copyright 2010 Chris Young <chris@unsatisfactorysoftware.co.uk>
- * Copyright 2010 Michael Drake <tlsa@netsurf-browser.org>
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-/** \file
- * Content for directory listings (implementation).
- */
-
-#include <sys/types.h>
-#include <dirent.h>
-#include <stdbool.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <time.h>
-#include "content/content_protected.h"
-#include "content/dirlist.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
-
-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 *) dirlist_generate_top(),
- strlen(dirlist_generate_top()));
-
- return true;
-}
-
-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, directory;
- char *up;
- struct stat filestat;
- char *filepath, *mimetype = NULL;
- int filepath_size;
- char *urlpath;
- int urlpath_size;
- char moddate[100];
- char modtime[100];
- long long filesize;
- bool extendedinfo, evenrow = false;
- char *title;
- int title_length;
-
- /* 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';
-
- /* Set which columns to suppress */
- dirlist_generate_hide_columns(0, buffer, MAX_LENGTH);
-
- binding_parse_chunk(c->data.html.parser_binding,
- (uint8_t *) buffer, strlen(buffer));
-
- /* Construct a localised title string */
- title_length = strlen(nice_path) + strlen(messages_get("FileIndex"));
- title = malloc(title_length);
-
- if(title == NULL) {
- msg_data.error = messages_get("NoMemory");
- content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
- return false;
- }
-
- /* Set title to localised "Index of <nice_path>" */
- snprintf(title, title_length, messages_get("FileIndex"), nice_path);
-
- /* Print document title and heading */
- dirlist_generate_title(title, buffer, MAX_LENGTH);
- free(nice_path);
- free(title);
-
- 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) {
- dirlist_generate_parent_link(up, buffer, MAX_LENGTH);
-
- binding_parse_chunk(c->data.html.parser_binding,
- (uint8_t *) buffer, strlen(buffer));
- }
- free(up);
- }
-
- /* Print directory contents table column headings */
- dirlist_generate_headings(buffer, MAX_LENGTH);
-
- 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;
- }
-
- if (S_ISDIR(filestat.st_mode))
- directory = true;
- else
- directory = false;
-
- urlpath_size = strlen(content__get_url(c)) +
- strlen(entry->d_name) + 2;
- urlpath = malloc(urlpath_size);
- if (urlpath != NULL) {
- strcpy(urlpath, content__get_url(c));
- if(urlpath[strlen(urlpath) - 1] != '/')
- strncat(urlpath, "/", urlpath_size);
- strncat(urlpath, entry->d_name, urlpath_size);
-
- 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 (directory) {
- mimetype = strdup((char*)messages_get(
- "FileDirectory"));
- filesize = -1;
- } else {
- mimetype = fetch_mimetype(filepath);
- filesize = (long long)
- filestat.st_size;
- }
- } else {
- strncpy(moddate, "", sizeof moddate);
- strncpy(modtime, "", sizeof modtime);
- filesize = -1;
- }
- /* Print row */
- dirlist_generate_row(evenrow, directory,
- urlpath, entry->d_name,
- mimetype ? mimetype : (char*)"",
- filesize,
- moddate, modtime,
- buffer, MAX_LENGTH);
-
- binding_parse_chunk(c->data.html.parser_binding,
- (uint8_t *) buffer, strlen(buffer));
-
- free(urlpath);
- }
-
- 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 *) dirlist_generate_bottom(),
- strlen(dirlist_generate_bottom()));
-
- 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;
-}
-
diff --git a/render/directory.h b/render/directory.h
deleted file mode 100644
index f16ad541b..000000000
--- a/render/directory.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2006 Richard Wilson <info@tinct.net>
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-/** \file
- * Content for directory listings (interface).
- *
- * These functions should in general be called via the content interface.
- */
-
-#ifndef _NETSURF_RENDER_DIRECTORY_H_
-#define _NETSURF_RENDER_DIRECTORY_H_
-
-#include <stdbool.h>
-#include "content/content_type.h"
-
-struct http_parameter;
-
-bool directory_create(struct content *c, const struct http_parameter *params);
-bool directory_convert(struct content *c);
-void directory_destroy(struct content *c);
-bool directory_clone(const struct content *old, struct content *new_content);
-
-#endif
diff --git a/utils/config.h b/utils/config.h
index 4a4fa069c..f618102a2 100644
--- a/utils/config.h
+++ b/utils/config.h
@@ -20,6 +20,7 @@
#define _NETSURF_UTILS_CONFIG_H_
#include <stddef.h>
+#include <dirent.h>
/* Try to detect which features the target OS supports */
@@ -37,6 +38,21 @@ char *strndup(const char *s, size_t n);
char *strcasestr(const char *haystack, const char *needle);
#endif
+/* fdopendir is actually present on most unix systems but unless
+ * _POSIX_C_SOURCE is set to 2008 it is not declared in the system
+ * headers. It is unavailable on RISC OS which requires fallback code
+ */
+#if (_POSIX_C_SOURCE - 0) >= 200809L
+#define HAVE_FDOPENDIR
+#else
+#if defined(riscos)
+#undef HAVE_FDOPENDIR
+#else
+#define HAVE_FDOPENDIR
+DIR *fdopendir(int fd);
+#endif
+#endif
+
/* For some reason, UnixLib defines this unconditionally.
* Assume we're using UnixLib if building for RISC OS. */
#if (defined(_GNU_SOURCE) || defined(riscos))
diff --git a/utils/url.c b/utils/url.c
index 5cb7305e8..cd731c97c 100644
--- a/utils/url.c
+++ b/utils/url.c
@@ -902,6 +902,31 @@ no_path:
return URL_FUNC_FAILED;
}
+/**
+ * Convert an escaped string to plain.
+ * \param result unescaped string owned by caller must be freed with free()
+ * \return URL_FUNC_OK on success
+ */
+url_func_result url_unescape(const char *str, char **result)
+{
+ char *curlstr;
+ char *retstr;
+
+ curlstr = curl_unescape(str, 0);
+ if (curlstr == NULL) {
+ return URL_FUNC_NOMEM;
+ }
+
+ retstr = strdup(curlstr);
+ curl_free(curlstr);
+
+ if (retstr == NULL) {
+ return URL_FUNC_NOMEM;
+ }
+
+ *result = retstr;
+ return URL_FUNC_OK;
+}
/**
* Escape a string suitable for inclusion in an URL.
diff --git a/utils/url.h b/utils/url.h
index e0e18fb17..8b5da77f2 100644
--- a/utils/url.h
+++ b/utils/url.h
@@ -53,6 +53,7 @@ url_func_result url_nice(const char *url, char **result,
bool remove_extensions);
url_func_result url_escape(const char *unescaped, size_t toskip,
bool sptoplus, const char *escexceptions, char **result);
+url_func_result url_unescape(const char *str, char **result);
url_func_result url_canonical_root(const char *url, char **result);
url_func_result url_parent(const char *url, char **result);
url_func_result url_plq(const char *url, char **result);