/* 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. */ }
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; }
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; }
/* 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; }
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); }
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); }
/* 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); }