Beispiel #1
0
/* Timer callback for @periodic_save_timer.  As explained in @install_timer,
 * this function must erase the expired timer ID from all variables.  */
static void
periodic_save_handler(void *xxx)
{
	static int periodic_save_event_id = EVENT_NONE;
	milliseconds_T interval;

	if (get_cmd_opt_bool((const unsigned char *)"anonymous")) return;

	/* Don't trigger anything at startup */
	if (periodic_save_event_id == EVENT_NONE)
		set_event_id(periodic_save_event_id, (unsigned char *)"periodic-saving");
	else
		trigger_event(periodic_save_event_id);

	interval = sec_to_ms(get_opt_int((const unsigned char *)"infofiles.save_interval", NULL));
	if (!interval) {
		/* We should get here only if @periodic_save_handler
		 * is being called from @periodic_save_change_hook or
		 * @init_timer, rather than from the timer system.  */
		assert(periodic_save_timer == TIMER_ID_UNDEF);
		return;
	}

	install_timer(&periodic_save_timer, interval, periodic_save_handler, NULL);
	/* The expired timer ID has now been erased.  */
}
Beispiel #2
0
static int
periodic_save_change_hook(struct session *ses, struct option *current,
			  struct option *changed)
{
	if (get_cmd_opt_bool((const unsigned char *)"anonymous")) return 0;

	kill_timer(&periodic_save_timer);

	periodic_save_handler(NULL);

	return 0;
}
Beispiel #3
0
static enum parse_error
parse_bind(struct option *opt_tree, struct conf_parsing_state *state,
	   struct string *mirror, int is_system_conf)
{
	unsigned char *keymap, *keystroke, *action;
	enum parse_error err = ERROR_NONE;
	struct conf_parsing_pos before_error;

	skip_white(&state->pos);
	if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);

	/* Keymap */
	before_error = state->pos;
	keymap = option_types[OPT_STRING].read(NULL, &state->pos.look,
					       &state->pos.line);
	skip_white(&state->pos);
	if (!keymap || !*state->pos.look) {
		state->pos = before_error;
		return show_parse_error(state, ERROR_PARSE);
	}

	/* Keystroke */
	before_error = state->pos;
	keystroke = option_types[OPT_STRING].read(NULL, &state->pos.look,
						  &state->pos.line);
	skip_white(&state->pos);
	if (!keystroke || !*state->pos.look) {
		mem_free(keymap); mem_free_if(keystroke);
		state->pos = before_error;
		return show_parse_error(state, ERROR_PARSE);
	}

	/* Equal sign */
	skip_white(&state->pos);
	if (*state->pos.look != '=') {
		mem_free(keymap); mem_free(keystroke);
		return show_parse_error(state, ERROR_PARSE);
	}
	state->pos.look++; /* '=' */

	skip_white(&state->pos);
	if (!*state->pos.look) {
		mem_free(keymap); mem_free(keystroke);
		return show_parse_error(state, ERROR_PARSE);
	}

	/* Action */
	before_error = state->pos;
	action = option_types[OPT_STRING].read(NULL, &state->pos.look,
					       &state->pos.line);
	if (!action) {
		mem_free(keymap); mem_free(keystroke);
		state->pos = before_error;
		return show_parse_error(state, ERROR_PARSE);
	}

	if (!mirror) {
		/* loading a configuration file */
		/* We don't bother to bind() if -default-keys. */
		if (!get_cmd_opt_bool("default-keys")
		    && bind_do(keymap, keystroke, action, is_system_conf)) {
			/* bind_do() tried but failed. */
			err = show_parse_error(state, ERROR_VALUE);
		} else {
			err = ERROR_NONE;
		}
	} else if (is_system_conf) {
		/* scanning a file that will not be rewritten */
		/* TODO */
	} else {
		/* rewriting a configuration file */
		/* Mirror what we already have.  If the keystroke has
		 * been unbound, then act_str is simply "none" and
		 * this does not require special handling.  */
		unsigned char *act_str = bind_act(keymap, keystroke);

		if (act_str) {
			add_bytes_to_string(mirror, state->mirrored,
					    before_error.look - state->mirrored);
			add_to_string(mirror, act_str);
			mem_free(act_str);
			state->mirrored = state->pos.look;
		} else {
			err = show_parse_error(state, ERROR_VALUE);
		}
	}
	mem_free(keymap); mem_free(keystroke); mem_free(action);
	return err;
}
Beispiel #4
0
/* Many execution paths may lead to this code so it needs to take appropriate
 * precausions to stuff like doc_view and doc_view->vs being NULL. */
enum frame_event_status
do_action(struct session *ses, enum main_action action_id, int verbose)
{
	enum frame_event_status status = FRAME_EVENT_OK;
	struct terminal *term = ses->tab->term;
	struct document_view *doc_view = current_frame(ses);
	struct link *link = NULL;

	if (action_id == -1) goto unknown_action;

	if (doc_view && doc_view->vs) {
		if (action_prefix_is_link_number(KEYMAP_MAIN, action_id)
		    && !try_jump_to_link_number(ses, doc_view))
			goto ignore_action;

		link = get_current_link(doc_view);

	} else if (action_requires_view_state(KEYMAP_MAIN, action_id)) {
		goto ignore_action;
	}

	if (action_requires_location(KEYMAP_MAIN, action_id)
	    && !have_location(ses))
		return FRAME_EVENT_OK;

	if (action_requires_link(KEYMAP_MAIN, action_id)
	    && !link)
		goto ignore_action;

	if (action_requires_form(KEYMAP_MAIN, action_id)
	    && (!link || !link_is_form(link)))
		goto ignore_action;

	if (!action_is_anonymous_safe(KEYMAP_MAIN, action_id)
	    && get_cmd_opt_bool("anonymous"))
		goto ignore_action;

	/* Please keep in alphabetical order for now. Later we can sort by most
	 * used or something. */
	switch (action_id) {
		case ACT_MAIN_ABORT_CONNECTION:
			abort_loading(ses, 1);
			print_screen_status(ses);
			break;

		case ACT_MAIN_ADD_BOOKMARK:
#ifdef CONFIG_BOOKMARKS
			launch_bm_add_doc_dialog(term, NULL, ses);
#endif
			break;
		case ACT_MAIN_ADD_BOOKMARK_LINK:
#ifdef CONFIG_BOOKMARKS
			launch_bm_add_link_dialog(term, NULL, ses);
#endif
			break;
		case ACT_MAIN_ADD_BOOKMARK_TABS:
#ifdef CONFIG_BOOKMARKS
			bookmark_terminal_tabs_dialog(term);
#endif
			break;

		case ACT_MAIN_AUTH_MANAGER:
			auth_manager(ses);
			break;

		case ACT_MAIN_BACKSPACE_PREFIX:

			if (!ses->kbdprefix.repeat_count) break;

			set_kbd_repeat_count(ses,
			                     ses->kbdprefix.repeat_count / 10);

			/* Keep send_event from resetting repeat_count. */
			status = FRAME_EVENT_SESSION_DESTROYED;

			break;

		case ACT_MAIN_BOOKMARK_MANAGER:
#ifdef CONFIG_BOOKMARKS
			bookmark_manager(ses);
#endif
			break;

		case ACT_MAIN_CACHE_MANAGER:
			cache_manager(ses);
			break;

		case ACT_MAIN_CACHE_MINIMIZE:
			shrink_memory(1);
			break;

		case ACT_MAIN_COOKIES_LOAD:
#ifdef CONFIG_COOKIES
			if (!get_opt_bool("cookies.save", NULL)) break;
			load_cookies();
#endif
			break;

		case ACT_MAIN_COOKIE_MANAGER:
#ifdef CONFIG_COOKIES
			cookie_manager(ses);
#endif
			break;

		case ACT_MAIN_COPY_CLIPBOARD:
			status = copy_current_link_to_clipboard(ses, doc_view, 0);
			break;

		case ACT_MAIN_DOCUMENT_INFO:
			document_info_dialog(ses);
			break;

		case ACT_MAIN_DOWNLOAD_MANAGER:
			download_manager(ses);
			break;

		case ACT_MAIN_EXMODE:
#ifdef CONFIG_EXMODE
			exmode_start(ses);
#endif
			break;

		case ACT_MAIN_FILE_MENU:
			activate_bfu_technology(ses, 0);
			break;

		case ACT_MAIN_FIND_NEXT:
			status = find_next(ses, doc_view, 1);
			break;

		case ACT_MAIN_FIND_NEXT_BACK:
			status = find_next(ses, doc_view, -1);
			break;

		case ACT_MAIN_FORGET_CREDENTIALS:
			free_auth();
			shrink_memory(1); /* flush caches */
			break;

		case ACT_MAIN_FORMHIST_MANAGER:
#ifdef CONFIG_FORMHIST
			formhist_manager(ses);
#endif
			break;

		case ACT_MAIN_FRAME_EXTERNAL_COMMAND:
			status = pass_uri_to_command(ses, doc_view,
			                             PASS_URI_FRAME);
			break;

		case ACT_MAIN_FRAME_NEXT:
			next_frame(ses, 1);
			draw_formatted(ses, 0);
			break;

		case ACT_MAIN_FRAME_MAXIMIZE:
			status = set_frame(ses, doc_view, 0);
			break;

		case ACT_MAIN_FRAME_PREV:
			next_frame(ses, -1);
			draw_formatted(ses, 0);
			break;

		case ACT_MAIN_GOTO_URL:
			goto_url_action(ses, NULL);
			break;

		case ACT_MAIN_GOTO_URL_CURRENT:
			goto_url_action(ses, get_current_url);
			break;

		case ACT_MAIN_GOTO_URL_CURRENT_LINK:
			goto_url_action(ses, get_current_link_url);
			break;

		case ACT_MAIN_GOTO_URL_HOME:
			goto_url_home(ses);
			break;

		case ACT_MAIN_HEADER_INFO:
			protocol_header_dialog(ses);
			break;

		case ACT_MAIN_HISTORY_MANAGER:
#ifdef CONFIG_GLOBHIST
			history_manager(ses);
#endif
			break;

		case ACT_MAIN_HISTORY_MOVE_BACK:
		{
			int count = int_max(1, eat_kbd_repeat_count(ses));

			go_history_by_n(ses, -count);
			break;
		}
		case ACT_MAIN_HISTORY_MOVE_FORWARD:
		{
			int count = int_max(1, eat_kbd_repeat_count(ses));

			go_history_by_n(ses, count);
			break;
		}
		case ACT_MAIN_JUMP_TO_LINK:
			break;

		case ACT_MAIN_KEYBINDING_MANAGER:
			keybinding_manager(ses);
			break;

		case ACT_MAIN_KILL_BACKGROUNDED_CONNECTIONS:
			abort_background_connections();
			break;

		case ACT_MAIN_LINK_DIALOG:
			open_link_dialog(ses);
			break;

		case ACT_MAIN_LINK_DOWNLOAD:
		case ACT_MAIN_LINK_DOWNLOAD_IMAGE:
		case ACT_MAIN_LINK_DOWNLOAD_RESUME:
			status = download_link(ses, doc_view, action_id);
			break;

		case ACT_MAIN_LINK_EXTERNAL_COMMAND:
			status = pass_uri_to_command(ses, doc_view,
			                             PASS_URI_LINK);
			break;

		case ACT_MAIN_LINK_FOLLOW:
			status = enter(ses, doc_view, 0);
			break;

		case ACT_MAIN_LINK_FOLLOW_RELOAD:
			status = enter(ses, doc_view, 1);
			break;

		case ACT_MAIN_LINK_INFO:
			link_info_dialog(ses);
			break;
			
		case ACT_MAIN_LINK_MENU:
			link_menu(term, NULL, ses);
			break;

		case ACT_MAIN_LINK_FORM_MENU:
			link_form_menu(ses);
			break;

		case ACT_MAIN_LUA_CONSOLE:
#ifdef CONFIG_SCRIPTING_LUA
			trigger_event_name("dialog-lua-console", ses);
#endif
			break;

		case ACT_MAIN_MARK_SET:
#ifdef CONFIG_MARKS
			ses->kbdprefix.mark = KP_MARK_SET;
			status = FRAME_EVENT_REFRESH;
#endif
			break;

		case ACT_MAIN_MARK_GOTO:
#ifdef CONFIG_MARKS
			/* TODO: Show promptly a menu (or even listbox?)
			 * with all the marks. But the next letter must
			 * still choose a mark directly! --pasky */
			ses->kbdprefix.mark = KP_MARK_GOTO;
			status = FRAME_EVENT_REFRESH;
#endif
			break;

		case ACT_MAIN_MENU:
			activate_bfu_technology(ses, -1);
			break;

		case ACT_MAIN_MOVE_CURRENT_TOP:
			status = move_current_top(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_CURSOR_UP:
			status = move_cursor_up(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_CURSOR_DOWN:
			status = move_cursor_down(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_CURSOR_LEFT:
			status = move_cursor_left(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_CURSOR_RIGHT:
			status = move_cursor_right(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_CURSOR_LINE_START:
			status = move_cursor_line_start(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_HALF_PAGE_DOWN:
			status = move_half_page_down(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_HALF_PAGE_UP:
			status = move_half_page_up(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_LINK_DOWN:
			status = move_link_down(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_LINK_DOWN_LINE:
			status = move_link_down_line(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_LINK_LEFT:
			status = move_link_left(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_LINK_LEFT_LINE:
			status = move_link_prev_line(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_LINK_NEXT:
			status = move_link_next(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_LINK_PREV:
			status = move_link_prev(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_LINK_RIGHT:
			status = move_link_right(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_LINK_RIGHT_LINE:
			status = move_link_next_line(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_LINK_UP:
			status = move_link_up(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_LINK_UP_LINE:
			status = move_link_up_line(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_PAGE_DOWN:
			status = move_page_down(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_PAGE_UP:
			status = move_page_up(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_DOCUMENT_START:
			status = move_document_start(ses, doc_view);
			break;

		case ACT_MAIN_MOVE_DOCUMENT_END:
			status = move_document_end(ses, doc_view);
			break;

		case ACT_MAIN_OPEN_LINK_IN_NEW_TAB:
			open_current_link_in_new_tab(ses, 0);
			break;

		case ACT_MAIN_OPEN_LINK_IN_NEW_TAB_IN_BACKGROUND:
			open_current_link_in_new_tab(ses, 1);
			break;

		case ACT_MAIN_OPEN_LINK_IN_NEW_WINDOW:
			open_in_new_window(term, send_open_in_new_window, ses);
			break;

		case ACT_MAIN_OPEN_NEW_TAB:
			open_uri_in_new_tab(ses, NULL, 0, 1);
			break;

		case ACT_MAIN_OPEN_NEW_TAB_IN_BACKGROUND:
			open_uri_in_new_tab(ses, NULL, 1, 1);
			break;

		case ACT_MAIN_OPEN_NEW_WINDOW:
			open_in_new_window(term, send_open_new_window, ses);
			break;

		case ACT_MAIN_OPEN_OS_SHELL:
			exec_shell(term);
			break;

		case ACT_MAIN_OPTIONS_MANAGER:
			options_manager(ses);
			break;

		case ACT_MAIN_QUIT:
			exit_prog(ses, 1);
			break;

		case ACT_MAIN_REALLY_QUIT:
			exit_prog(ses, 0);
			break;

		case ACT_MAIN_REDRAW:
			redraw_terminal_cls(term);
			break;

		case ACT_MAIN_RELOAD:
			reload(ses, CACHE_MODE_INCREMENT);
			break;

		case ACT_MAIN_RERENDER:
			draw_formatted(ses, 2);
			break;

		case ACT_MAIN_RESET_FORM:
			status = reset_form(ses, doc_view, 0);
			break;

		case ACT_MAIN_RESOURCE_INFO:
			resource_info(term);
			break;

		case ACT_MAIN_SAVE_AS:
			status = save_as(ses, doc_view, 0);
			break;

		case ACT_MAIN_SAVE_FORMATTED:
			status = save_formatted_dlg(ses, doc_view, 0);
			break;

		case ACT_MAIN_SAVE_OPTIONS:
			write_config(term);
			break;

		case ACT_MAIN_SAVE_URL_AS:
			save_url_as(ses);
			break;

		case ACT_MAIN_SCROLL_DOWN:
			status = scroll_down(ses, doc_view);
			break;

		case ACT_MAIN_SCROLL_LEFT:
			status = scroll_left(ses, doc_view);
			break;

		case ACT_MAIN_SCROLL_RIGHT:
			status = scroll_right(ses, doc_view);
			break;

		case ACT_MAIN_SCROLL_UP:
			status = scroll_up(ses, doc_view);
			break;

		case ACT_MAIN_SEARCH:
			status = search_dlg(ses, doc_view, 1);
			break;

		case ACT_MAIN_SEARCH_BACK:
			status = search_dlg(ses, doc_view, -1);
			break;

		case ACT_MAIN_SEARCH_TYPEAHEAD:
		case ACT_MAIN_SEARCH_TYPEAHEAD_LINK:
		case ACT_MAIN_SEARCH_TYPEAHEAD_TEXT:
		case ACT_MAIN_SEARCH_TYPEAHEAD_TEXT_BACK:
			status = search_typeahead(ses, doc_view, action_id);
			break;

		case ACT_MAIN_SHOW_TERM_OPTIONS:
			terminal_options(term, NULL, ses);
			break;

		case ACT_MAIN_SUBMIT_FORM:
			status = submit_form(ses, doc_view, 0);
			break;

		case ACT_MAIN_SUBMIT_FORM_RELOAD:
			status = submit_form(ses, doc_view, 1);
			break;

		case ACT_MAIN_TAB_CLOSE:
			close_tab(term, ses);
			status = FRAME_EVENT_SESSION_DESTROYED;
			break;

		case ACT_MAIN_TAB_CLOSE_ALL_BUT_CURRENT:
			close_all_tabs_but_current(ses);
			break;

		case ACT_MAIN_TAB_EXTERNAL_COMMAND:
			status = pass_uri_to_command(ses, doc_view,
			                             PASS_URI_TAB);
			break;

		case ACT_MAIN_TAB_MOVE_LEFT:
			move_current_tab(ses, -1);
			break;

		case ACT_MAIN_TAB_MOVE_RIGHT:
			move_current_tab(ses, 1);
			break;

		case ACT_MAIN_TAB_MENU:
			assert(ses->tab == get_current_tab(term));

			if (ses->status.show_tabs_bar)
				tab_menu(ses, ses->tab->xpos,
					 term->height - 1
					  - ses->status.show_status_bar,
					 1);
			else
				tab_menu(ses, 0, 0, 0);

			break;

		case ACT_MAIN_TAB_NEXT:
			switch_current_tab(ses, 1);
			break;

		case ACT_MAIN_TAB_PREV:
			switch_current_tab(ses, -1);
			break;

		case ACT_MAIN_TERMINAL_RESIZE:
			resize_terminal_dialog(term);
			break;

		case ACT_MAIN_TOGGLE_CSS:
#ifdef CONFIG_CSS
			toggle_document_option(ses, "document.css.enable");
#endif
			break;

		case ACT_MAIN_TOGGLE_DISPLAY_IMAGES:
			toggle_document_option(ses, "document.browse.images.show_as_links");
			break;

		case ACT_MAIN_TOGGLE_DISPLAY_TABLES:
			toggle_document_option(ses, "document.html.display_tables");
			break;

		case ACT_MAIN_TOGGLE_DOCUMENT_COLORS:
			toggle_document_option(ses, "document.colors.use_document_colors");
			break;

		case ACT_MAIN_TOGGLE_HTML_PLAIN:
			toggle_plain_html(ses, ses->doc_view, 0);
			break;

		case ACT_MAIN_TOGGLE_MOUSE:
#ifdef CONFIG_MOUSE
			toggle_mouse();
#endif
			break;

		case ACT_MAIN_TOGGLE_NUMBERED_LINKS:
			toggle_document_option(ses, "document.browse.links.numbering");
			break;

		case ACT_MAIN_TOGGLE_PLAIN_COMPRESS_EMPTY_LINES:
			toggle_document_option(ses, "document.plain.compress_empty_lines");
			break;

		case ACT_MAIN_TOGGLE_WRAP_TEXT:
			toggle_wrap_text(ses, ses->doc_view, 0);
			break;

		case ACT_MAIN_VIEW_IMAGE:
			status = view_image(ses, doc_view, 0);
			break;

		case ACT_MAIN_SCRIPTING_FUNCTION:
		case ACT_MAIN_NONE:
		case MAIN_ACTIONS:
		default:
unknown_action:
			if (verbose) {
				INTERNAL("No action handling defined for '%s'.",
					 get_action_name(KEYMAP_MAIN, action_id));
			}

			status = FRAME_EVENT_IGNORED;
	}

ignore_action:
	/* XXX: At this point the session may have been destroyed */

	if (status != FRAME_EVENT_SESSION_DESTROYED
	    && ses->insert_mode == INSERT_MODE_ON
	    && link != get_current_link(doc_view))
		ses->insert_mode = INSERT_MODE_OFF;

	if (status == FRAME_EVENT_REFRESH && doc_view)
		refresh_view(ses, doc_view, 0);

	return status;
}
Beispiel #5
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);
}
Beispiel #6
0
void
connect_socket(struct socket *csocket, struct connection_state state)
{
	int sock = -1;
	struct connect_info *connect_info = csocket->connect_info;
	int i;
	int trno = connect_info->triedno;
	int only_local = get_cmd_opt_bool("localhost");
	int saved_errno = 0;
	int at_least_one_remote_ip = 0;
#ifdef CONFIG_IPV6
	int try_ipv6 = get_opt_bool("connection.try_ipv6", NULL);
#endif
	int try_ipv4 = get_opt_bool("connection.try_ipv4", NULL);
	/* We tried something but we failed in such a way that we would rather
	 * prefer the connection to retain the information about previous
	 * failures.  That is, we i.e. decided we are forbidden to even think
	 * about such a connection attempt.
	 * XXX: Unify with @local_only handling? --pasky */
	int silent_fail = 0;

	csocket->ops->set_state(csocket, state);

	/* Clear handlers, the connection to the previous RR really timed
	 * out and doesn't interest us anymore. */
	if (csocket->fd >= 0)
		close_socket(csocket);

	for (i = connect_info->triedno + 1; i < connect_info->addrno; i++) {
#ifdef CONFIG_IPV6
		struct sockaddr_in6 addr = *((struct sockaddr_in6 *) &connect_info->addr[i]);
		int family = addr.sin6_family;
#else
		struct sockaddr_in addr = *((struct sockaddr_in *) &connect_info->addr[i]);
		int family = addr.sin_family;
#endif
		int pf;
		int force_family = connect_info->ip_family;

		connect_info->triedno++;

		if (only_local) {
			int local = 0;
#ifdef CONFIG_IPV6
			if (family == AF_INET6)
				local = check_if_local_address6((struct sockaddr_in6 *) &addr);
			else
#endif
				local = check_if_local_address4((struct sockaddr_in *) &addr);

			/* This forbids connections to anything but local, if option is set. */
			if (!local) {
				at_least_one_remote_ip = 1;
				continue;
			}
		}

#ifdef CONFIG_IPV6
		if (family == AF_INET6) {
			if (!try_ipv6 || (force_family && force_family != 6)) {
				silent_fail = 1;
				continue;
			}
			pf = PF_INET6;

		} else
#endif
		if (family == AF_INET) {
			if (!try_ipv4 || (force_family && force_family != 4)) {
				silent_fail = 1;
				continue;
			}
			pf = PF_INET;

		} else {
			continue;
		}
		silent_fail = 0;

		sock = socket(pf, SOCK_STREAM, IPPROTO_TCP);
		if (sock == -1) {
			if (errno && !saved_errno) saved_errno = errno;
			continue;
		}

		if (set_nonblocking_fd(sock) < 0) {
			if (errno && !saved_errno) saved_errno = errno;
			close(sock);
			continue;
		}
		csocket->fd = sock;

#ifdef CONFIG_IPV6
		addr.sin6_port = htons(connect_info->port);
#else
		addr.sin_port = htons(connect_info->port);
#endif

		/* We can set csocket->protocol_family here even if the connection
		 * will fail, as we will use it only when it will be successfully
		 * established. At least I hope that noone else will want to do
		 * something else ;-). --pasky */
		/* And in fact we must set it early, because of EINPROGRESS.  */

#ifdef CONFIG_IPV6
		if (family == AF_INET6) {
			csocket->protocol_family = EL_PF_INET6;
			if (connect(sock, (struct sockaddr *) &addr,
					sizeof(struct sockaddr_in6)) == 0) {
				/* Success */
				complete_connect_socket(csocket, NULL, NULL);
				return;
			}
		} else
#endif
		{
			csocket->protocol_family = EL_PF_INET;
			if (connect(sock, (struct sockaddr *) &addr,
					sizeof(struct sockaddr_in)) == 0) {
				/* Success */
				complete_connect_socket(csocket, NULL, NULL);
				return;
			}
		}

		if (errno == EALREADY
#ifdef EWOULDBLOCK
		    || errno == EWOULDBLOCK
#endif
		    || errno == EINPROGRESS) {
			/* It will take some more time... */
			set_handlers(sock, NULL, (select_handler_T) connected,
				     (select_handler_T) dns_exception, csocket);
			csocket->ops->set_state(csocket, connection_state(S_CONN));
			return;
		}

		if (errno && !saved_errno) saved_errno = errno;

		close(sock);
	}

	assert(i >= connect_info->addrno);

	/* Tried everything, but it didn't help :(. */

	if (only_local && !saved_errno && at_least_one_remote_ip) {
		/* Yes we might hit a local address and fail in the process, but
		 * what matters is the last one because we do not know the
		 * previous one's errno, and the added complexity wouldn't
		 * really be worth it. */
		csocket->ops->done(csocket, connection_state(S_LOCAL_ONLY));
		return;
	}

	/* Retry reporting the errno state only if we already tried something
	 * new. Else use the S_DNS _progress_ state to make sure that no
	 * download callbacks will report any errors. */
	if (trno != connect_info->triedno && !silent_fail)
		state = connection_state_for_errno(errno);
	else if (trno == -1 && silent_fail)
		/* All failed. */
		state = connection_state(S_NO_FORCED_DNS);

	csocket->ops->retry(csocket, state);
}
Beispiel #7
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);
}