Esempio n. 1
0
void bookmarks_read( void )
{
  int eax;
  int backend_num = *(int*)(get_opt_( config_options, (unsigned char*)config_options ));
  struct bookmarks_backend *backend = bookmarks_backends[ *(int*)(get_opt_( config_options, (unsigned char*)config_options )) ];
  unsigned char *file_name;
  FILE *f;
  if ( bookmarks_backends[ *(int*)(get_opt_( config_options, (unsigned char*)config_options )) ] && backend->read && backend && eax( 0 ) && ( elinks_home == 0 || straconcat( elinks_home ) ) )
  {
    fopen64( (char*)straconcat( elinks_home ), "rb" );
    if ( elinks_home )
    {
      mem_free( (void*)straconcat( elinks_home ) );
      *ebp_28 = ebp_28;
    }
    if ( f->_flags )
    {
      backend->read( &f[0] );
      fclose( &f[0] );
      bookmarks_unset_dirty(  );
      loaded_backend_num = backend_num;
    }
  }
  return;
}
Esempio n. 2
0
void bookmarks_write( struct list_head_elinks *bookmarks_list )
{
  int backend_num = *(int*)(get_opt_( config_options, (unsigned char*)config_options ));
  struct bookmarks_backend *backend = bookmarks_backends[ *(int*)(get_opt_( config_options, (unsigned char*)config_options )) ];
  struct secure_save_info *ssi;
  unsigned char *file_name;
  if ( ( bookmarks_are_dirty(  ) || loaded_backend_num != backend_num ) && backend[0] && backend->write && elinks_home && backend )
  {
    eax( 1 );
    if ( file_name && straconcat( elinks_home ) )
    {
      mem_free( &file_name );
      if ( secure_open( straconcat( elinks_home ) ) )
      {
        backend->write( &ssi[0], &bookmarks_list[0] );
        if ( eax )
        {
          backend_num = backend_num;
        }
        else
        {
        }
      }
    }
  }
  return;
}
Esempio n. 3
0
File: mime.c Progetto: Efreak/elinks
/* Checks if application/x-<extension> has any handlers. */
static inline unsigned char *
check_extension_type(unsigned char *extension)
{
	/* Trim the extension so only last .<extension> is used. */
	unsigned char *trimmed = strrchr((const char *)extension, '.');
	struct mime_handler *handler;
	unsigned char *content_type;

	if (!trimmed)
		return NULL;

	content_type = straconcat("application/x-", trimmed + 1,
				  (unsigned char *) NULL);
	if (!content_type)
		return NULL;

	handler = get_mime_type_handler(content_type, 1);
	if (handler) {
		mem_free(handler);
		return content_type;
	}

	mem_free(content_type);
	return NULL;
}
Esempio n. 4
0
static void
build_edit_dialog(struct terminal *term, struct cookie *cookie)
{
#define EDIT_WIDGETS_COUNT 8
    /* [gettext_accelerator_context(.build_edit_dialog)] */
    struct dialog *dlg;
    unsigned char *name, *value, *domain, *expires, *secure;
    unsigned char *dlg_server;
    int length = 0;

    dlg = calloc_dialog(EDIT_WIDGETS_COUNT, MAX_STR_LEN * 5);
    if (!dlg) return;

    dlg->title = _("Edit", term);
    dlg->layouter = generic_dialog_layouter;
    dlg->udata = cookie;
    dlg->udata2 = NULL;

    name = get_dialog_offset(dlg, EDIT_WIDGETS_COUNT);
    value = name + MAX_STR_LEN;
    domain = value + MAX_STR_LEN;
    expires = domain + MAX_STR_LEN;
    secure = expires + MAX_STR_LEN;

    safe_strncpy(name, cookie->name, MAX_STR_LEN);
    safe_strncpy(value, cookie->value, MAX_STR_LEN);
    safe_strncpy(domain, cookie->domain, MAX_STR_LEN);
    /* Bug 923: Assumes time_t values fit in unsigned long.  */
    ulongcat(expires, &length, cookie->expires, MAX_STR_LEN, 0);
    length = 0;
    ulongcat(secure, &length, cookie->secure, MAX_STR_LEN, 0);

    dlg_server = cookie->server->host;
    dlg_server = straconcat(_("Server", term), ": ", dlg_server, "\n",
                            (unsigned char *) NULL);

    if (!dlg_server) {
        mem_free(dlg);
        return;
    }

    add_dlg_text(dlg, dlg_server, ALIGN_LEFT, 0);
    add_dlg_field_float(dlg, _("Name", term), 0, 0, set_cookie_name, MAX_STR_LEN, name, NULL);
    add_dlg_field_float(dlg, _("Value", term), 0, 0, set_cookie_value, MAX_STR_LEN, value, NULL);
    add_dlg_field_float(dlg, _("Domain", term), 0, 0, set_cookie_domain, MAX_STR_LEN, domain, NULL);
    add_dlg_field_float(dlg, _("Expires", term), 0, 0, set_cookie_expires, MAX_STR_LEN, expires, NULL);
    add_dlg_field_float(dlg, _("Secure", term), 0, 0, set_cookie_secure, MAX_STR_LEN, secure, NULL);

    add_dlg_button(dlg, _("~OK", term), B_ENTER, ok_dialog, NULL);
    add_dlg_button(dlg, _("~Cancel", term), B_ESC, cancel_dialog, NULL);

    add_dlg_end(dlg, EDIT_WIDGETS_COUNT);

    do_dialog(term, dlg, getml(dlg, (void *) dlg_server, (void *) NULL));
#undef EDIT_WIDGETS_COUNT
}
Esempio n. 5
0
static void
smjs_load_hooks(void)
{
	unsigned char *path;

	assert(smjs_ctx);

	if (elinks_home) {
		path = straconcat(elinks_home, SMJS_HOOKS_FILENAME, NULL);
	} else {
		path = stracpy(CONFDIR "/" SMJS_HOOKS_FILENAME);
	}

	if (file_exists(path))
		smjs_do_file(path);
	mem_free(path);
}
Esempio n. 6
0
static unsigned char *
init_data_protocol_header(struct cache_entry *cached,
			  unsigned char *type, int typelen)
{
	unsigned char *head;

	assert(typelen);

	type = memacpy(type, typelen);
	if (!type) return NULL;

	/* Set fake content type */
	head = straconcat((const unsigned char *)"\r\nContent-Type: ", type, "\r\n",
			  (unsigned char *) NULL);
	mem_free(type);
	if (!head) return NULL;

	mem_free_set(&cached->head, head);
	return head;
}
Esempio n. 7
0
void
init_ruby(struct module *module)
{
	unsigned char *path;

	/* Set up and initialize the interpreter. This function should be called
	 * before any other Ruby-related functions. */
	ruby_init();
	ruby_script("ELinks-ruby");
	ruby_init_loadpath();

	/* ``Trap'' debug prints from scripts. */
	rb_define_singleton_method(rb_stdout, "write", erb_module_message, 1);
	rb_define_global_function("p", erb_stdout_p, -1);

	/* Set up the ELinks module interface. */
	init_erb_module();

	if (elinks_home) {
		path = straconcat(elinks_home, RUBY_HOOKS_FILENAME,
				  (unsigned char *) NULL);

	} else {
		path = stracpy(CONFDIR STRING_DIR_SEP RUBY_HOOKS_FILENAME);
	}

	if (!path) return;

	if (file_can_read(path)) {
		int error;

		/* Load ~/.elinks/hooks.rb into the interpreter. */
		//rb_load_file(path);
		rb_load_protect(rb_str_new2(path), 0, &error);
		if (error)
			erb_report_error(NULL, error);
	}

	mem_free(path);
}
Esempio n. 8
0
/* Searches a word to find an email adress or an URI to add as a link. */
static inline struct link *
check_link_word(struct document *document, unsigned char *uri, int length,
		int x, int y)
{
	struct uri test;
	unsigned char *where = NULL;
	unsigned char *mailto = memchr(uri, '@', length);
	int keep = uri[length];
	struct link *new_link;

	assert(document);
	if_assert_failed return NULL;

	uri[length] = 0;

	if (mailto && mailto > uri && mailto - uri < length - 1) {
		where = straconcat("mailto:", uri, (unsigned char *) NULL);

	} else if (parse_uri(&test, uri) == URI_ERRNO_OK
		   && test.protocol != PROTOCOL_UNKNOWN
		   && (test.datalen || test.hostlen)) {
		where = memacpy(uri, length);
	}

	uri[length] = keep;

	if (!where) return NULL;

	/* We need to reparse the URI and normalize it so that the protocol and
	 * host part are converted to lowercase. */
	normalize_uri(NULL, where);

	new_link = add_document_link(document, where, length, x, y);

	if (!new_link) mem_free(where);

	return new_link;
}
Esempio n. 9
0
void show_http_error_document( struct session *ses, void *data )
{
  struct terminal *term = ses->tab->term;
  struct cache_entry *cached;
  struct cache_entry *cache = find_in_cache( &((int*)data)[1] );
  unsigned char *str;
  cached = find_in_cache( &((int*)data)[1] );
  if ( cached || get_cache_entry( &((int*)data)[1] ) )
  {
    if ( 0 != 72 )
    {
      if ( term && current_charset != get_terminal_codepage( term ) )
      {
        bind_textdomain_codeset( "elinks", get_cp_mime_name( get_terminal_codepage( term ) ) );
        current_charset = get_terminal_codepage( term );
      }
      gettext( "HTTP error %03d" );
    }
    if ( init_string( ebp_32 ) == 0 )
    {
      if ( asprintfa( (char*)gettext( "HTTP error %03d" ) ) )
        mem_free( (void*)asprintfa( (char*)gettext( "HTTP error %03d" ) ) );
    }
    else
    {
      add_format_to_string( (struct string*)asprintfa( (char*)gettext( "HTTP error %03d" ) ), asprintfa( (char*)gettext( "HTTP error %03d" ) ) );
      if ( 0 != 32 )
      {
        if ( term && current_charset != get_terminal_codepage( term ) )
        {
          bind_textdomain_codeset( "elinks", get_cp_mime_name( get_terminal_codepage( term ) ) );
          current_charset = get_terminal_codepage( term );
        }
        gettext( "  An error occurred on the server while fetching the document you\n  requested. However, the server did not send back any explanation of what\n  happened, so it is unknown what went wrong. Please contact the web\n  server administrator about this, if you believe that this error should\n  not occur since it is not a nice behaviour from the web server at all\n  and indicates that there is some much deeper problem with the web server\n  software.\n" );
      }
      add_format_to_string( ebp_32, gettext( "  An error occurred on the server while fetching the document you\n  requested. However, the server did not send back any explanation of what\n  happened, so it is unknown what went wrong. Please contact the web\n  server administrator about this, if you believe that this error should\n  not occur since it is not a nice behaviour from the web server at all\n  and indicates that there is some much deeper problem with the web server\n  software.\n" ) );
      add_format_to_string( "  &lt;/p&gt;\n  &lt;p&gt;\n  URI: &lt;a href=\"%s\"&gt;%s&lt;/a&gt;\n", *(int*)(((int*)data)[1]) );
      add_format_to_string( ebp_32, " &lt;/p&gt;\n &lt;hr /&gt;\n &lt;/body&gt;\n&lt;/html&gt;\n" );
      if ( asprintfa( (char*)gettext( "HTTP error %03d" ) ) )
        mem_free( (void*)asprintfa( (char*)gettext( "HTTP error %03d" ) ) );
      str[0] = ebp_32;
      if ( ebp_32 )
      {
        int gettext_codepage = get_terminal_codepage( term );
        if ( cached )
          delete_entry_content( &cache[0] );
        if ( cache->content_type )
          mem_free( (void*)cache->content_type );
        cache->content_type = stracpy( "text/html" );
        if ( cache->head )
          mem_free( (void*)cache->head );
        cache->head = straconcat( "\r\nContent-Type: text/html; charset=" );
        add_fragment( &cache[0], (long long)0, &str[0] );
        mem_free( &str[0] );
        draw_formatted( ses, 1 );
      }
    }
  }
  done_uri( &((int*)data)[1] );
  mem_free( &((int*)data)[0] );
  return;
}
Esempio n. 10
0
File: smb2.c Progetto: Efreak/elinks
static void
add_smb_dir_entry(struct directory_entry *entry, struct string *page,
	      int pathlen, unsigned char *dircolor)
{
	unsigned char *lnk = NULL;
	struct string html_encoded_name;
	struct string uri_encoded_name;

	if (!init_string(&html_encoded_name)) return;
	if (!init_string(&uri_encoded_name)) {
		done_string(&html_encoded_name);
		return;
	}

	encode_uri_string(&uri_encoded_name, entry->name + pathlen, -1, 1);
	add_html_to_string(&html_encoded_name, entry->name + pathlen,
			   strlen(entry->name) - pathlen);

	/* add_to_string(&fragment, &fragmentlen, "   "); */
	add_html_to_string(page, entry->attrib, strlen(entry->attrib));
	add_to_string(page, "<a href=\"");
	add_string_to_string(page, &uri_encoded_name);

	if (entry->attrib[0] == 'd') {
		add_char_to_string(page, '/');

#ifdef FS_UNIX_SOFTLINKS
	} else if (entry->attrib[0] == 'l') {
		struct stat st;
		unsigned char buf[MAX_STR_LEN];
		int readlen = readlink(entry->name, buf, MAX_STR_LEN);

		if (readlen > 0 && readlen != MAX_STR_LEN) {
			buf[readlen] = '\0';
			lnk = straconcat(" -> ", buf, (unsigned char *) NULL);
		}

		if (!stat(entry->name, &st) && S_ISDIR(st.st_mode))
			add_char_to_string(page, '/');
#endif
	}

	add_to_string(page, "\">");

	if (entry->attrib[0] == 'd' && *dircolor) {
		/* The <b> is for the case when use_document_colors is off. */
		string_concat(page, "<font color=\"", dircolor, "\"><b>",
			      (unsigned char *) NULL);
	}

	add_string_to_string(page, &html_encoded_name);
	done_string(&uri_encoded_name);
	done_string(&html_encoded_name);

	if (entry->attrib[0] == 'd' && *dircolor) {
		add_to_string(page, "</b></font>");
	}

	add_to_string(page, "</a>");
	if (lnk) {
		add_html_to_string(page, lnk, strlen(lnk));
		mem_free(lnk);
	}

	add_char_to_string(page, '\n');
}
Esempio n. 11
0
static void
html_img_do(unsigned char *a, unsigned char *object_src,
            struct html_context *html_context)
{
	int ismap, usemap = 0;
	int add_brackets = 0;
	unsigned char *src = NULL;
	unsigned char *label = NULL;
	unsigned char *usemap_attr;
	struct document_options *options = html_context->options;
	int display_style = options->image_link.display_style;

	/* Note about display_style:
	 * 0     means always display IMG
	 * 1     means always display filename
	 * 2     means display alt/title attribute if possible, IMG if not
	 * 3     means display alt/title attribute if possible, filename if not */

	usemap_attr = get_attr_val(a, (unsigned char *)"usemap", html_context->doc_cp);
	if (usemap_attr) {
		unsigned char *joined_urls = join_urls(html_context->base_href,
						       usemap_attr);
		unsigned char *map_url;

		mem_free(usemap_attr);
		if (!joined_urls) return;
		map_url = straconcat((unsigned char *)"MAP@", joined_urls,
				     (unsigned char *) NULL);
		mem_free(joined_urls);
		if (!map_url) return;

		html_stack_dup(html_context, ELEMENT_KILLABLE);
		mem_free_set(&format.link, map_url);
		format.form = NULL;
		format.style.attr |= AT_BOLD;
		usemap = 1;
 	}

	ismap = format.link
	        && has_attr(a, (unsigned char *)"ismap", html_context->doc_cp)
	        && !usemap;

	if (display_style == 2 || display_style == 3) {
		label = get_attr_val(a, (unsigned char *)"alt", html_context->doc_cp);
		if (!label)
			label = get_attr_val(a, (unsigned char *)"title", html_context->doc_cp);

		/* Little hack to preserve rendering of [   ], in directory listings,
		 * but we still want to drop extra spaces in alt or title attribute
		 * to limit display width on certain websites. --Zas */
		if (label && strlen((const char *)label) > 5) clr_spaces(label);
	}

	src = null_or_stracpy(object_src);
	if (!src) src = get_url_val(a, (unsigned char *)"src", html_context->doc_cp);
	if (!src) src = get_url_val(a, (unsigned char *)"dynsrc", html_context->doc_cp);

	/* If we have no label yet (no title or alt), so
	 * just use default ones, or image filename. */
	if (!label || !*label) {
		mem_free_set(&label, NULL);
		/* Do we want to display images with no alt/title and with no
		 * link on them ?
		 * If not, just exit now. */
		if (!options->images && !format.link) {
			mem_free_if(src);
			if (usemap) pop_html_element(html_context);
			return;
		}

		add_brackets = 1;

		if (usemap) {
			label = stracpy((const unsigned char *)"USEMAP");
		} else if (ismap) {
			label = stracpy((const unsigned char *)"ISMAP");
		} else {
			if (display_style == 3)
				label = get_image_filename_from_src(options->image_link.filename_maxlen, src);
		}

	} else {
		label = get_image_label(options->image_link.label_maxlen, label);
	}

	if (!label || !*label) {
		mem_free_set(&label, NULL);
		add_brackets = 1;
		if (display_style == 1)
			label = get_image_filename_from_src(options->image_link.filename_maxlen, src);
		if (!label || !*label)
			mem_free_set(&label, stracpy((const unsigned char *)"IMG"));
	}

	mem_free_set(&format.image, NULL);
	mem_free_set(&format.title, NULL);

	if (label) {
		int img_link_tag = options->image_link.tagging;

		if (img_link_tag && (img_link_tag == 2 || add_brackets)) {
			unsigned char *img_link_prefix = options->image_link.prefix;
			unsigned char *img_link_suffix = options->image_link.suffix;
			unsigned char *new_label = straconcat(img_link_prefix, label, img_link_suffix, (unsigned char *) NULL);

			if (new_label) mem_free_set(&label, new_label);
		}

		if (!options->image_link.show_any_as_links) {
			put_image_label(a, label, html_context);

		} else {
			if (src) {
				format.image = join_urls(html_context->base_href, src);
			}

			format.title = get_attr_val(a, (unsigned char *)"title", html_context->doc_cp);

			if (ismap) {
				unsigned char *new_link;

				html_stack_dup(html_context, ELEMENT_KILLABLE);
				new_link = straconcat(format.link, (unsigned char *)"?0,0", (unsigned char *) NULL);
				if (new_link)
					mem_free_set(&format.link, new_link);
			}

			put_image_label(a, label, html_context);

			if (ismap) pop_html_element(html_context);
			mem_free_set(&format.image, NULL);
			mem_free_set(&format.title, NULL);
		}

		mem_free(label);
	}

	mem_free_if(src);
	if (usemap) pop_html_element(html_context);
}
Esempio n. 12
0
void
textarea_edit(int op, struct terminal *term_, struct form_state *fs_,
	      struct document_view *doc_view_, struct link *link_)
{
	struct textarea_data *td = NULL;

	assert ((op == 0 || op == 1) && term_);
	if_assert_failed return;

	if (op == 0 && get_cmd_opt_bool("anonymous")) {
		info_box(term_, 0, N_("Error"), ALIGN_CENTER,
			 N_("You cannot launch an external"
			    " editor in the anonymous mode."));
		return;
	}

	if (op == 0) {
		unsigned char *ed;
		unsigned char *ex;

		assert(fs_ && doc_view_ && link_ && term_);

		td = init_textarea_data(term_, fs_, doc_view_, link_);
		if (!td)
			return;

		ed = get_opt_str("document.browse.forms.editor",
		                 doc_view_->session);
		if (!ed || !*ed) {
			ed = getenv("EDITOR");
			if (!ed || !*ed) ed = "vi";
		}

		ex = straconcat(ed, " ", td->fn, (unsigned char *) NULL);
		if (!ex) {
			unlink(td->fn);
			done_textarea_data(td);
			return;
		}

		td->term->textarea_data = td;

		exec_on_terminal(td->term, ex, "", TERM_EXEC_FG);
		mem_free(ex);

		return;

	} else if (op == 1) {
		struct string file;

		td = term_->textarea_data;
		term_->textarea_data = NULL;
		assert(td);

		if (!td->fs || !init_string(&file)
		    || !add_file_to_string(&file, td->fn)) {
			done_textarea_data(td);
			return;
		}

		if (file.length > td->fc_maxlength) {
			file.source[td->fc_maxlength] = '\0';
			/* Casting size_t fc_maxlength to unsigned int
			 * and formatting it with "%u" is safe,
			 * because fc_maxlength is smaller than
			 * file.length, which is an int.  */
			info_box(td->term, MSGBOX_FREE_TEXT, N_("Warning"),
			         ALIGN_CENTER,
			         msg_text(td->term,
				          N_("You have exceeded the textarea's"
				             " size limit: your input is %d"
					     " bytes, but the maximum is %u"
					     " bytes.\n\n"
					     "Your input has been truncated,"
					     " but you can still recover the"
					     " text that you entered from"
					     " this file: %s"), file.length,
				             (unsigned int) td->fc_maxlength, td->fn));
		} else {
			unlink(td->fn);
		}

		mem_free(td->fs->value);
		td->fs->value = file.source;
		td->fs->state = file.length;

		if (td->doc_view && td->link)
			draw_form_entry(td->term, td->doc_view, td->link);
	}

	done_textarea_data(td);
}
Esempio n. 13
0
/* To reduce redundant error handling code [calls to abort_connection()]
 * most of the function is build around conditions that will assign the error
 * code to @state if anything goes wrong. The rest of the function will then just
 * do the necessary cleanups. If all works out we end up with @state being S_OK
 * resulting in a cache entry being created with the fragment data generated by
 * either reading the file content or listing a directory. */
void
file_protocol_handler(struct connection *connection)
{
	unsigned char *redirect_location = NULL;
	struct string page, name;
	struct connection_state state;
	int set_dir_content_type = 0;

	if (get_cmd_opt_bool((const unsigned char *)"anonymous")) {
		if (strcmp((const char *)connection->uri->string, "file:///dev/stdin")
		    || isatty(STDIN_FILENO)) {
			abort_connection(connection,
					 connection_state(S_FILE_ANONYMOUS));
			return;
		}
	}

#ifdef CONFIG_CGI
	if (!execute_cgi(connection)) return;
#endif /* CONFIG_CGI */

	/* Treat /dev/stdin in special way */
	if (!strcmp((const char *)connection->uri->string, "file:///dev/stdin")) {
		int fd = open("/dev/stdin", O_RDONLY);

		if (fd == -1) {
			abort_connection(connection, connection_state(-errno));
			return;
		}
		set_nonblocking_fd(fd);
		if (!init_http_connection_info(connection, 1, 0, 1)) {
			abort_connection(connection, connection_state(S_OUT_OF_MEM));
			close(fd);
			return;
		}
		connection->socket->fd = fd;
		connection->data_socket->fd = -1;
		read_from_stdin(connection);
		return;
	}


	/* This function works on already simplified file-scheme URI pre-chewed
	 * by transform_file_url(). By now, the function contains no hostname
	 * part anymore, possibly relative path is converted to an absolute one
	 * and uri->data is just the final path to file/dir we should try to
	 * show. */

	if (!init_string(&name)
	    || !add_uri_to_string(&name, connection->uri, URI_PATH)) {
		done_string(&name);
		abort_connection(connection, connection_state(S_OUT_OF_MEM));
		return;
	}

	decode_uri_string(&name);

	/* In Win32, file_is_dir seems to always return 0 if the name
	 * ends with a directory separator.  */
	if ((name.length > 0 && dir_sep(name.source[name.length - 1]))
	    || file_is_dir(name.source)) {
		/* In order for global history and directory listing to
		 * function properly the directory url must end with a
		 * directory separator. */
		if (name.source[0] && !dir_sep(name.source[name.length - 1])) {
			redirect_location = (unsigned char *)STRING_DIR_SEP;
			state = connection_state(S_OK);
		} else {
			state = list_directory(connection, name.source, &page);
			set_dir_content_type = 1;
		}

	} else {
		state = read_encoded_file(&name, &page);
		/* FIXME: If state is now S_ENCODE_ERROR we should try loading
		 * the file undecoded. --jonas */
	}

	done_string(&name);

	if (is_in_state(state, S_OK)) {
		struct cache_entry *cached;

		/* Try to add fragment data to the connection cache if either
		 * file reading or directory listing worked out ok. */
		cached = connection->cached = get_cache_entry(connection->uri);
		if (!connection->cached) {
			if (!redirect_location) done_string(&page);
			state = connection_state(S_OUT_OF_MEM);

		} else if (redirect_location) {
			if (!redirect_cache(cached, redirect_location, 1, 0))
				state = connection_state(S_OUT_OF_MEM);

		} else {
			add_fragment(cached, 0, page.source, page.length);
			connection->from += page.length;

			if (!cached->head && set_dir_content_type) {
				unsigned char *head;

				/* If the system charset somehow
				 * changes after the directory listing
				 * has been generated, it should be
				 * parsed with the original charset.  */
				head = straconcat((const unsigned char *)"\r\nContent-Type: text/html; charset=",
						  get_cp_mime_name(get_cp_index((const unsigned char *)"System")),
						  "\r\n", (unsigned char *) NULL);

				/* Not so gracefully handle failed memory
				 * allocation. */
				if (!head)
					state = connection_state(S_OUT_OF_MEM);

				/* Setup directory listing for viewing. */
				mem_free_set(&cached->head, head);
			}

			done_string(&page);
		}
	}

	abort_connection(connection, state);
}