enum uri_errno parse_uri(struct uri *uri, unsigned char *uristring) { unsigned char *prefix_end, *host_end; #ifdef CONFIG_IPV6 unsigned char *lbracket, *rbracket; #endif assertm(uristring != NULL, "No uri to parse."); memset(uri, 0, sizeof(*uri)); /* Nothing to do for an empty url. */ if_assert_failed return 0; if (!*uristring) return URI_ERRNO_EMPTY; uri->string = uristring; uri->protocollen = get_protocol_length(uristring); /* Invalid */ if (!uri->protocollen) return URI_ERRNO_INVALID_PROTOCOL; /* Figure out whether the protocol is known */ uri->protocol = get_protocol(struri(uri), uri->protocollen); prefix_end = uristring + uri->protocollen; /* ':' */ /* Check if there's a digit after the protocol name. */ if (isdigit(*prefix_end)) { uri->ip_family = uristring[uri->protocollen] - '0'; prefix_end++; } if (*prefix_end != ':') return URI_ERRNO_INVALID_PROTOCOL; prefix_end++; /* Skip slashes */ if (prefix_end[0] == '/' && prefix_end[1] == '/') { if (prefix_end[2] == '/' && get_protocol_need_slash_after_host(uri->protocol)) return URI_ERRNO_TOO_MANY_SLASHES; prefix_end += 2; } else if (get_protocol_need_slashes(uri->protocol)) { return URI_ERRNO_NO_SLASHES; } if (get_protocol_free_syntax(uri->protocol)) { uri->data = prefix_end; uri->datalen = strlen(prefix_end); return URI_ERRNO_OK; } else if (uri->protocol == PROTOCOL_FILE) { int datalen = strcspn(prefix_end, "#" POST_CHAR_S); unsigned char *frag_or_post = prefix_end + datalen; /* Extract the fragment part. */ if (datalen >= 0) { if (*frag_or_post == '#') { uri->fragment = frag_or_post + 1; uri->fragmentlen = strcspn(uri->fragment, POST_CHAR_S); frag_or_post = uri->fragment + uri->fragmentlen; } if (*frag_or_post == POST_CHAR) { uri->post = frag_or_post + 1; } } else { datalen = strlen(prefix_end); } /* A bit of a special case, but using the "normal" host * parsing seems a bit scary at this point. (see bug 107). */ if (datalen > 9 && !c_strncasecmp(prefix_end, "localhost/", 10)) { prefix_end += 9; datalen -= 9; } uri->data = prefix_end; uri->datalen = datalen; return URI_ERRNO_OK; } /* Isolate host */ #ifdef CONFIG_IPV6 /* Get brackets enclosing IPv6 address */ lbracket = strchr((const char *)prefix_end, '['); if (lbracket) { rbracket = strchr((const char *)lbracket, ']'); /* [address] is handled only inside of hostname part (surprisingly). */ if (rbracket && rbracket < prefix_end + strcspn(prefix_end, "/")) uri->ipv6 = 1; else lbracket = rbracket = NULL; } else { rbracket = NULL; } #endif /* Possibly skip auth part */ host_end = prefix_end + strcspn(prefix_end, "@"); if (prefix_end + strcspn(prefix_end, "/") > host_end && *host_end) { /* we have auth info here */ unsigned char *user_end; /* Allow '@' in the password component */ while (strcspn(host_end + 1, "@") < strcspn(host_end + 1, "/?")) host_end = host_end + 1 + strcspn(host_end + 1, "@"); user_end = strchr((const char *)prefix_end, ':'); if (!user_end || user_end > host_end) { uri->user = prefix_end; uri->userlen = host_end - prefix_end; } else { uri->user = prefix_end; uri->userlen = user_end - prefix_end; uri->password = user_end + 1; uri->passwordlen = host_end - user_end - 1; } prefix_end = host_end + 1; } #ifdef CONFIG_IPV6 if (uri->ipv6) host_end = rbracket + strcspn(rbracket, ":/?"); else #endif host_end = prefix_end + strcspn(prefix_end, ":/?"); #ifdef CONFIG_IPV6 if (uri->ipv6) { int addrlen = rbracket - lbracket - 1; /* Check for valid length. * addrlen >= sizeof(hostbuf) is theorically impossible * but i keep the test in case of... Safer, imho --Zas */ assertm(addrlen >= 0 && addrlen < NI_MAXHOST, "parse_uri(): addrlen value is bad (%d) for URL '%s'. " "Problems are likely to be encountered. Please report " "this, it is a security bug!", addrlen, uristring); if_assert_failed return URI_ERRNO_IPV6_SECURITY; uri->host = lbracket + 1; uri->hostlen = addrlen; } else
static void S_ADECL(check_handle)(void *P) { // ARRAY_LOG("checking %p",P); if(P && !HashPtr_Find(&S_ADECL(ABARRAY_tracker),P)) assertm(0,"unknown handle %p passed to Array",P); }
void draw_textarea(struct terminal *term, struct form_state *fs, struct document_view *doc_view, struct link *link) { struct line_info *line, *linex; struct form_control *fc; struct box *box; int vx, vy; int sl, ye; int x, y; assert(term && doc_view && doc_view->document && doc_view->vs && link); if_assert_failed return; #ifdef CONFIG_UTF8 if (term->utf8_cp) { draw_textarea_utf8(term, fs, doc_view, link); return; } #endif /* CONFIG_UTF8 */ fc = get_link_form_control(link); assertm(fc != NULL, "link %d has no form control", (int) (link - doc_view->document->links)); if_assert_failed return; box = &doc_view->box; vx = doc_view->vs->x; vy = doc_view->vs->y; if (!link->npoints) return; #ifdef CONFIG_UTF8 area_cursor(fc, fs, 0); #else area_cursor(fc, fs); #endif /* CONFIG_UTF8 */ linex = format_text(fs->value, fc->cols, fc->wrap, 0); if (!linex) return; line = linex; sl = fs->vypos; while (line->start != -1 && sl) sl--, line++; x = link->points[0].x + box->x - vx; y = link->points[0].y + box->y - vy; ye = y + fc->rows; for (; line->start != -1 && y < ye; line++, y++) { int i; if (!row_is_in_box(box, y)) continue; for (i = 0; i < fc->cols; i++) { unsigned char data; int xi = x + i; if (!col_is_in_box(box, xi)) continue; if (i >= -fs->vpos && i + fs->vpos < line->end - line->start) data = fs->value[line->start + i + fs->vpos]; else data = '_'; draw_char_data(term, xi, y, data); } } for (; y < ye; y++) { int i; if (!row_is_in_box(box, y)) continue; for (i = 0; i < fc->cols; i++) { int xi = x + i; if (col_is_in_box(box, xi)) draw_char_data(term, xi, y, '_'); } } mem_free(linex); }
void ses_goto(struct session *ses, struct uri *uri, unsigned char *target_frame, struct location *target_location, enum cache_mode cache_mode, enum task_type task_type, int redir) { /* [gettext_accelerator_context(ses_goto)] */ struct task *task; int referrer_incomplete = 0; int malicious_uri = 0; int confirm_submit = uri->form && get_opt_bool("document.browse.forms" ".confirm_submit", ses); unsigned char *m1 = NULL, *message = NULL; struct memory_list *mlist = NULL; if (ses->doc_view && ses->doc_view->document && ses->doc_view->document->refresh) { kill_document_refresh(ses->doc_view->document->refresh); } assertm(!ses->loading_uri, "Buggy URI reference counting"); /* Reset the redirect counter if this is not a redirect. */ if (!redir) { ses->redirect_cnt = 0; } /* Figure out whether to confirm submit or not */ /* Only confirm submit if we are posting form data or a misleading URI * was detected. */ /* Note uri->post might be empty here but we are still supposely * posting form data so this should be more correct. */ if (uri->user && uri->userlen && get_opt_bool("document.browse.links.warn_malicious", ses) && check_malicious_uri(uri)) { malicious_uri = 1; confirm_submit = 1; } else if (uri->form) { /* First check if the referring URI was incomplete. It * indicates that the posted form data might be incomplete too. * See bug 460. */ if (ses->referrer) { struct cache_entry *cached; cached = find_in_cache(ses->referrer); referrer_incomplete = (cached && cached->incomplete); } if (referrer_incomplete) { confirm_submit = 1; } else if (get_validated_cache_entry(uri, cache_mode)) { confirm_submit = 0; } } if (!confirm_submit) { ses_load(ses, get_uri_reference(uri), target_frame, target_location, cache_mode, task_type); return; } task = mem_alloc(sizeof(*task)); if (!task) return; task->ses = ses; task->uri = get_uri_reference(uri); task->cache_mode = cache_mode; task->session_task.type = task_type; task->session_task.target.frame = null_or_stracpy(target_frame); task->session_task.target.location = target_location; if (malicious_uri) { unsigned char *host = memacpy(uri->host, uri->hostlen); unsigned char *user = memacpy(uri->user, uri->userlen); unsigned char *uristring = get_uri_string(uri, URI_PUBLIC); message = msg_text(ses->tab->term, N_("The URL you are about to follow might be maliciously " "crafted in order to confuse you. By following the URL " "you will be connecting to host \"%s\" as user \"%s\".\n\n" "Do you want to go to URL %s?"), host, user, uristring); mem_free_if(host); mem_free_if(user); mem_free_if(uristring); } else if (redir) { m1 = N_("Do you want to follow the redirect and post form data " "to URL %s?"); } else if (referrer_incomplete) { m1 = N_("The form data you are about to post might be incomplete.\n" "Do you want to post to URL %s?"); } else if (task_type == TASK_FORWARD) { m1 = N_("Do you want to post form data to URL %s?"); } else { m1 = N_("Do you want to repost form data to URL %s?"); } if (!message && m1) { unsigned char *uristring = get_uri_string(uri, URI_PUBLIC); message = msg_text(ses->tab->term, m1, uristring); mem_free_if(uristring); } add_to_ml(&mlist, task, (void *) NULL); if (task->session_task.target.frame) add_to_ml(&mlist, task->session_task.target.frame, (void *) NULL); msg_box(ses->tab->term, mlist, MSGBOX_FREE_TEXT, N_("Warning"), ALIGN_CENTER, message, task, 2, MSG_BOX_BUTTON(N_("~Yes"), post_yes, B_ENTER), MSG_BOX_BUTTON(N_("~No"), post_no, B_ESC)); }
static void draw_textarea_utf8(struct terminal *term, struct form_state *fs, struct document_view *doc_view, struct link *link) { struct line_info *line, *linex; struct form_control *fc; struct box *box; int vx, vy; int sl, ye; int x, xbase, y; assert(term && doc_view && doc_view->document && doc_view->vs && link); if_assert_failed return; fc = get_link_form_control(link); assertm(fc != NULL, "link %d has no form control", (int) (link - doc_view->document->links)); if_assert_failed return; box = &doc_view->box; vx = doc_view->vs->x; vy = doc_view->vs->y; if (!link->npoints) return; area_cursor(fc, fs, 1); linex = format_textutf8(fs->value, fc->cols, fc->wrap, 0); if (!linex) return; line = linex; sl = fs->vypos; while (line->start != -1 && sl) sl--, line++; xbase = link->points[0].x + box->x - vx; y = link->points[0].y + box->y - vy; ye = y + fc->rows; for (; line->start != -1 && y < ye; line++, y++) { int i; unsigned char *text, *end; text = fs->value + line->start; end = fs->value + line->end; text += utf8_cells2bytes(text, fs->vpos, end); if (!row_is_in_box(box, y)) continue; for (i = 0, x = xbase; i < fc->cols; i++, x++) { unicode_val_T data; if (i >= -fs->vpos && text < end) { /* utf8_to_unicode will increment text. */ data = utf8_to_unicode(&text, end); } else data = '_'; if (col_is_in_box(box, x)) { int cell = unicode_to_cell(data); if (cell == 2) { draw_char_data(term, x++, y, data); i++; data = UCS_NO_CHAR; } draw_char_data(term, x, y, data); } } } for (; y < ye; y++) { int i; if (!row_is_in_box(box, y)) continue; for (i = 0, x = xbase; i < fc->cols; i++, x++) { if (col_is_in_box(box, x)) draw_char_data(term, x, y, '_'); } } mem_free(linex); }
static void read_select(struct socket *socket) { struct read_buffer *rb = socket->read_buffer; ssize_t rd; assertm(rb != NULL, "read socket has no buffer"); if_assert_failed { socket->ops->done(socket, connection_state(S_INTERNAL)); return; } /* We are making some progress, therefore reset the timeout; we do this * for read_select() to avoid that the periodic calls to user handlers * has to do it. */ socket->ops->set_timeout(socket, connection_state(0)); if (!socket->duplex) clear_handlers(socket->fd); if (!rb->freespace) { int size = RD_SIZE(rb, rb->length); rb = mem_realloc(rb, size); if (!rb) { socket->ops->done(socket, connection_state(S_OUT_OF_MEM)); return; } rb->freespace = size - sizeof(*rb) - rb->length; assert(rb->freespace > 0); socket->read_buffer = rb; } #ifdef CONFIG_SSL if (socket->ssl) { rd = ssl_read(socket, rb->data + rb->length, rb->freespace); } else #endif { rd = generic_read(socket, rb->data + rb->length, rb->freespace); } switch (rd) { #ifdef CONFIG_SSL case SOCKET_SSL_WANT_READ: read_from_socket(socket, rb, connection_state(S_TRANS), rb->done); break; #endif case SOCKET_CANT_READ: if (socket->state != SOCKET_RETRY_ONCLOSE) { socket->state = SOCKET_CLOSED; rb->done(socket, rb); break; } socket->ops->retry(socket, connection_state(S_CANT_READ)); break; case SOCKET_SYSCALL_ERROR: socket->ops->retry(socket, connection_state_for_errno(errno)); break; case SOCKET_INTERNAL_ERROR: /* The global errno variable is used for passing * internal connection_state error value. */ socket->ops->done(socket, connection_state(errno)); break; default: debug_transfer_log(rb->data + rb->length, rd); rb->length += rd; rb->freespace -= rd; assert(rb->freespace >= 0); rb->done(socket, rb); } }
static void write_select(struct socket *socket) { struct write_buffer *wb = socket->write_buffer; int wr; assertm(wb != NULL, "write socket has no buffer"); if_assert_failed { socket->ops->done(socket, connection_state(S_INTERNAL)); return; } /* We are making some progress, therefore reset the timeout; ie. when * uploading large files the time needed for all the data to be sent can * easily exceed the timeout. */ socket->ops->set_timeout(socket, connection_state(0)); #if 0 printf("ws: %d\n",wb->length-wb->pos); for (wr = wb->pos; wr < wb->length; wr++) printf("%c", wb->data[wr]); printf("-\n"); #endif #ifdef CONFIG_SSL if (socket->ssl) { wr = ssl_write(socket, wb->data + wb->pos, wb->length - wb->pos); } else #endif { assert(wb->length - wb->pos > 0); wr = generic_write(socket, wb->data + wb->pos, wb->length - wb->pos); } switch (wr) { case SOCKET_CANT_WRITE: socket->ops->retry(socket, connection_state(S_CANT_WRITE)); break; case SOCKET_SYSCALL_ERROR: socket->ops->retry(socket, connection_state_for_errno(errno)); break; case SOCKET_INTERNAL_ERROR: /* The global errno variable is used for passing * internal connection_state error value. */ socket->ops->done(socket, connection_state(errno)); break; default: if (wr < 0) break; /*printf("wr: %d\n", wr);*/ wb->pos += wr; if (wb->pos == wb->length) { socket_write_T done = wb->done; if (!socket->duplex) { clear_handlers(socket->fd); } else { select_handler_T read_handler; select_handler_T error_handler; read_handler = get_handler(socket->fd, SELECT_HANDLER_READ); error_handler = read_handler ? (select_handler_T) exception : NULL; set_handlers(socket->fd, read_handler, NULL, error_handler, socket); } mem_free_set(&socket->write_buffer, NULL); done(socket); } } }
/** Parse one event from itrm_in.queue and append to itrm_out.queue. * @pre On entry, @a *itrm must not be blocked. * @returns the number of bytes removed from itrm->in.queue; at least 0. * @post If this function leaves the queue not full, it also reenables * reading from itrm->in.std. (Because it does not add to the queue, * it never need disable reading.) */ static int process_queue(struct itrm *itrm) { struct interlink_event ev; int el = 0; if (!itrm->in.queue.len) goto return_without_event; assert(!itrm->blocked); if_assert_failed return 0; /* unlike goto, don't enable reading */ set_kbd_interlink_event(&ev, KBD_UNDEF, KBD_MOD_NONE); #ifdef DEBUG_ITRM_QUEUE { int i; /* Dump current queue in a readable form to stderr. */ for (i = 0; i < itrm->in.queue.len; i++) if (itrm->in.queue.data[i] == ASCII_ESC) fprintf(stderr, "ESC "); else if (isprint(itrm->in.queue.data[i]) && !isspace(itrm->in.queue.data[i])) fprintf(stderr, "%c ", itrm->in.queue.data[i]); else fprintf(stderr, "0x%02x ", itrm->in.queue.data[i]); fprintf(stderr, "\n"); fflush(stderr); } #endif /* DEBUG_ITRM_QUEUE */ /* el == -1 means itrm->in.queue appears to be the beginning of an * escape sequence but it is not yet complete. Set a timer; * if it times out, then assume it wasn't an escape sequence * after all. * el == 0 means this function has not yet figured out what the data * in itrm->in.queue is, but some possibilities remain. * One of them will be chosen before returning. * el > 0 means some bytes were successfully parsed from the beginning * of itrm->in.queue and should now be removed from there. * However, this does not always imply an event will be queued. */ /* ELinks should also recognize U+009B CONTROL SEQUENCE INTRODUCER * as meaning the same as ESC 0x5B, and U+008F SINGLE SHIFT THREE as * meaning the same as ESC 0x4F, but those cannot yet be implemented * because of bug 777: the UTF-8 decoder is run too late. */ if (itrm->in.queue.data[0] == ASCII_ESC) { if (itrm->in.queue.len < 2) { el = -1; } else if (itrm->in.queue.data[1] == 0x5B /* CSI */) { el = decode_terminal_escape_sequence(itrm, &ev); } else if (itrm->in.queue.data[1] == 0x4F /* SS3 */) { el = decode_terminal_application_key(itrm, &ev); } else if (itrm->in.queue.data[1] == ASCII_ESC) { /* ESC ESC can be either Alt-Esc or the * beginning of e.g. ESC ESC 0x5B 0x41, * which we should parse as Esc Up. */ if (itrm->in.queue.len < 3) { /* Need more data to figure it out. */ el = -1; } else if (itrm->in.queue.data[2] == 0x5B || itrm->in.queue.data[2] == 0x4F) { /* The first ESC appears to be followed * by an escape sequence. Treat it as * a standalone Esc. */ el = 1; set_kbd_event(itrm, &ev, itrm->in.queue.data[0], KBD_MOD_NONE); } else { /* The second ESC of ESC ESC is not the * beginning of any known escape sequence. * This must be Alt-Esc, then. */ el = 2; set_kbd_event(itrm, &ev, itrm->in.queue.data[1], KBD_MOD_ALT); } } if (el == 0) { /* Begins with ESC, but none of the above */ el = 2; set_kbd_event(itrm, &ev, itrm->in.queue.data[1], KBD_MOD_ALT); } } else if (itrm->in.queue.data[0] == 0) { static const struct term_event_keyboard os2xtd[256] = { #include "terminal/key.inc" }; if (itrm->in.queue.len < 2) el = -1; else { el = 2; set_kbd_interlink_event(&ev, os2xtd[itrm->in.queue.data[1]].key, os2xtd[itrm->in.queue.data[1]].modifier); } } if (el == 0) { el = 1; set_kbd_event(itrm, &ev, itrm->in.queue.data[0], itrm->bracketed_pasting ? KBD_MOD_PASTE : KBD_MOD_NONE); } /* The call to decode_terminal_escape_sequence() might have changed the * keyboard event to a mouse event. */ if (ev.ev == EVENT_MOUSE || ev.info.keyboard.key != KBD_UNDEF) { itrm_queue_event(itrm, (char *) &ev, sizeof(ev)); itrm->bracketed_pasting = (ev.ev == EVENT_KBD && (ev.info.keyboard.modifier & KBD_MOD_PASTE)); } return_without_event: if (el == -1) { install_timer(&itrm->timer, ESC_TIMEOUT, (void (*)(void *)) kbd_timeout, itrm); return 0; } else { assertm(itrm->in.queue.len >= el, "event queue underflow"); if_assert_failed { itrm->in.queue.len = el; } itrm->in.queue.len -= el; if (itrm->in.queue.len) memmove(itrm->in.queue.data, itrm->in.queue.data + el, itrm->in.queue.len); if (itrm->in.queue.len < ITRM_IN_QUEUE_SIZE) handle_itrm_stdin(itrm); return el; } }
//---------------------------------------- // receive helper. // this really could use a dose of win32 // optimizations. I should figure out how // to schedule and defer receives... // @todo -AB: get this optimized for async reads :09/24/08 //---------------------------------------- BOOL sock_recv(SOCKET sock, char **hrecv_frame, BOOL *closed) { int total_read = 0; if(closed) *closed = FALSE; if(sock == INVALID_SOCKET || !hrecv_frame) return FALSE; // apparently there is no way to know how much data is available to read... // I find this very hard to believe #if 0 { long cur = ftell(sock); long n_avil; int cur_size = achr_size(hrecv_frame); int n_read; assertm(0==fseek(sock,0,SEEK_END),"failed to seek."); n_avil = ftell(sock); assertm(0==fseek(sock,cur,SEEK_CUR)); achr_setsize(hrecv_frame,cur_size+n_avil); n_read = recv(c->sock,(*hrecv_frame)+cur_size,n_avil,0); if(n_read<0) { if(last_error() != EWOULDBLOCK) return FALSE; } else if(n_read == 0) // closed on other end { if(closed) *closed = TRUE; } return TRUE; } #endif for(;;) { int n_read; int cur_size = achr_size(hrecv_frame); achr_setsize(hrecv_frame,cur_size+1024); n_read = recv(sock,(*hrecv_frame)+cur_size,1024,0); if(n_read<0) { if(last_error() != EWOULDBLOCK) return FALSE; else { achr_setsize(hrecv_frame,cur_size); break; // out of data to read. } } else if(n_read == 0) // closed on other end { if(closed) *closed = TRUE; achr_setsize(hrecv_frame,cur_size); break; } achr_setsize(hrecv_frame,cur_size+n_read); total_read += n_read; } return TRUE; }