1 /** Sessions task management
15 #include "bfu/dialog.h"
16 #include "cache/cache.h"
17 #include "dialogs/menu.h"
18 #include "dialogs/status.h"
19 #include "document/document.h"
20 #include "document/html/parser.h"
21 #include "document/refresh.h"
22 #include "document/view.h"
23 #include "intl/gettext/libintl.h"
24 #include "main/event.h"
25 #include "main/timer.h"
26 #include "network/connection.h"
27 #include "osdep/newwin.h"
28 #include "protocol/protocol.h"
29 #include "protocol/uri.h"
30 #include "terminal/terminal.h"
31 #include "terminal/window.h"
32 #include "session/download.h"
33 #include "session/location.h"
34 #include "session/session.h"
35 #include "session/task.h"
36 #include "viewer/text/view.h"
39 static void loading_callback(struct download
*, struct session
*);
42 free_task(struct session
*ses
)
44 assertm(ses
->task
.type
, "Session has no task");
45 if_assert_failed
return;
47 if (ses
->loading_uri
) {
48 done_uri(ses
->loading_uri
);
49 ses
->loading_uri
= NULL
;
51 ses
->task
.type
= TASK_NONE
;
52 mem_free_set(&ses
->task
.target
.frame
, NULL
);
56 abort_preloading(struct session
*ses
, int interrupt
)
58 if (!ses
->task
.type
) return;
60 cancel_download(&ses
->loading
, interrupt
);
68 enum cache_mode cache_mode
;
69 struct session_task session_task
;
73 ses_load(struct session
*ses
, struct uri
*uri
, unsigned char *target_frame
,
74 struct location
*target_location
, enum cache_mode cache_mode
,
75 enum task_type task_type
)
77 ses
->loading
.callback
= (download_callback_T
*) loading_callback
;
78 ses
->loading
.data
= ses
;
79 ses
->loading_uri
= uri
;
81 ses
->task
.type
= task_type
;
82 mem_free_set(&ses
->task
.target
.frame
, null_or_stracpy(target_frame
));
83 ses
->task
.target
.location
= target_location
;
85 load_uri(ses
->loading_uri
, ses
->referrer
, &ses
->loading
,
86 PRI_MAIN
, cache_mode
, -1);
92 struct task
*task
= task_
;
94 abort_preloading(task
->ses
, 0);
96 /* XXX: Make the session inherit the URI. */
97 ses_load(task
->ses
, task
->uri
, task
->session_task
.target
.frame
,
98 task
->session_task
.target
.location
, task
->cache_mode
,
99 task
->session_task
.type
);
105 struct task
*task
= task_
;
107 reload(task
->ses
, CACHE_MODE_NORMAL
);
111 /** Check if the URI is obfuscated (bug 382). The problem is said to occur when
112 * a URI designed to pass access a specific location with a supplied username,
113 * contains misleading chars prior to the @ symbol.
115 * An attacker can exploit this issue by supplying a malicious URI pointing to
116 * a page designed to mimic that of a trusted site, and tricking a victim who
117 * follows a link into believing they are actually at the trusted location.
119 * Only the user ID (and not also the password) is checked because only the
120 * user ID is displayed in the status bar. */
122 check_malicious_uri(struct uri
*uri
)
124 unsigned char *user
, *pos
;
127 assert(uri
->user
&& uri
->userlen
);
129 user
= pos
= memacpy(uri
->user
, uri
->userlen
);
132 decode_uri_for_display(user
);
135 int length
, trailing_dots
;
137 for (length
= 0; pos
[length
] != '\0'; length
++)
138 if (!(isalnum(pos
[length
]) || pos
[length
] == '.'))
141 /* Wind back so that the TLD part is checked correctly. */
142 for (trailing_dots
= 0; trailing_dots
< length
; trailing_dots
++)
143 if (!length
|| pos
[length
- trailing_dots
- 1] != '.')
146 /* Not perfect, but I am clueless as how to do better. Besides
147 * I don't really think it is an issue for ELinks. --jonas */
148 if (end_with_known_tld(pos
, length
- trailing_dots
) != -1) {
155 while (*pos
&& (!isalnum(*pos
) || *pos
== '.'))
165 ses_goto(struct session
*ses
, struct uri
*uri
, unsigned char *target_frame
,
166 struct location
*target_location
, enum cache_mode cache_mode
,
167 enum task_type task_type
, int redir
)
169 /* [gettext_accelerator_context(ses_goto)] */
171 int referrer_incomplete
= 0;
172 int malicious_uri
= 0;
173 int confirm_submit
= uri
->form
&& get_opt_bool("document.browse.forms"
174 ".confirm_submit", ses
);
175 unsigned char *m1
= NULL
, *message
= NULL
;
178 && ses
->doc_view
->document
179 && ses
->doc_view
->document
->refresh
) {
180 kill_document_refresh(ses
->doc_view
->document
->refresh
);
183 assertm(!ses
->loading_uri
, "Buggy URI reference counting");
185 /* Reset the redirect counter if this is not a redirect. */
187 ses
->redirect_cnt
= 0;
190 /* Figure out whether to confirm submit or not */
192 /* Only confirm submit if we are posting form data or a misleading URI
194 /* Note uri->post might be empty here but we are still supposely
195 * posting form data so this should be more correct. */
197 if (uri
->user
&& uri
->userlen
198 && get_opt_bool("document.browse.links.warn_malicious", ses
)
199 && check_malicious_uri(uri
)) {
203 } else if (uri
->form
) {
204 /* First check if the referring URI was incomplete. It
205 * indicates that the posted form data might be incomplete too.
208 struct cache_entry
*cached
;
210 cached
= find_in_cache(ses
->referrer
);
211 referrer_incomplete
= (cached
&& cached
->incomplete
);
214 if (referrer_incomplete
) {
217 } else if (get_validated_cache_entry(uri
, cache_mode
)) {
222 if (!confirm_submit
) {
223 ses_load(ses
, get_uri_reference(uri
), target_frame
,
224 target_location
, cache_mode
, task_type
);
228 task
= mem_alloc(sizeof(*task
));
232 task
->uri
= get_uri_reference(uri
);
233 task
->cache_mode
= cache_mode
;
234 task
->session_task
.type
= task_type
;
235 task
->session_task
.target
.frame
= null_or_stracpy(target_frame
);
236 task
->session_task
.target
.location
= target_location
;
239 unsigned char *host
= memacpy(uri
->host
, uri
->hostlen
);
240 unsigned char *user
= memacpy(uri
->user
, uri
->userlen
);
241 unsigned char *uristring
= get_uri_string(uri
, URI_PUBLIC
);
243 message
= msg_text(ses
->tab
->term
,
244 N_("The URL you are about to follow might be maliciously "
245 "crafted in order to confuse you. By following the URL "
246 "you will be connecting to host \"%s\" as user \"%s\".\n\n"
247 "Do you want to go to URL %s?"), host
, user
, uristring
);
251 mem_free_if(uristring
);
254 m1
= N_("Do you want to follow the redirect and post form data "
257 } else if (referrer_incomplete
) {
258 m1
= N_("The form data you are about to post might be incomplete.\n"
259 "Do you want to post to URL %s?");
261 } else if (task_type
== TASK_FORWARD
) {
262 m1
= N_("Do you want to post form data to URL %s?");
265 m1
= N_("Do you want to repost form data to URL %s?");
268 if (!message
&& m1
) {
269 unsigned char *uristring
= get_uri_string(uri
, URI_PUBLIC
);
271 message
= msg_text(ses
->tab
->term
, m1
, uristring
);
272 mem_free_if(uristring
);
275 msg_box(ses
->tab
->term
, getml(task
, (void *) NULL
), MSGBOX_FREE_TEXT
,
276 N_("Warning"), ALIGN_CENTER
,
279 MSG_BOX_BUTTON(N_("~Yes"), post_yes
, B_ENTER
),
280 MSG_BOX_BUTTON(N_("~No"), post_no
, B_ESC
));
284 /** If @a loaded_in_frame is set, this was called just to indicate a move inside
285 * a frameset, and we basically just reset the appropriate frame's view_state in
286 * that case. When clicking on a link inside a frame, the frame URI is somehow
287 * updated and added to the files-to-load queue, then ses_forward() is called
288 * with @a loaded_in_frame unset, duplicating the whole frameset's location,
289 * then later the file-to-load callback calls it for the particular frame with
290 * @a loaded_in_frame set. */
292 ses_forward(struct session
*ses
, int loaded_in_frame
)
294 struct location
*loc
= NULL
;
295 struct view_state
*vs
;
297 if (!loaded_in_frame
) {
299 mem_free_set(&ses
->search_word
, NULL
);
303 if (!loaded_in_frame
) {
304 loc
= mem_calloc(1, sizeof(*loc
));
305 if (!loc
) return NULL
;
306 copy_struct(&loc
->download
, &ses
->loading
);
309 if (ses
->task
.target
.frame
&& *ses
->task
.target
.frame
) {
312 assertm(have_location(ses
), "no location yet");
313 if_assert_failed
return NULL
;
315 if (!loaded_in_frame
) {
316 copy_location(loc
, cur_loc(ses
));
317 add_to_history(&ses
->history
, loc
);
320 frame
= ses_find_frame(ses
, ses
->task
.target
.frame
);
322 if (!loaded_in_frame
) {
323 del_from_history(&ses
->history
, loc
);
324 destroy_location(loc
);
326 mem_free_set(&ses
->task
.target
.frame
, NULL
);
331 if (!loaded_in_frame
) {
333 init_vs(vs
, ses
->loading_uri
, vs
->plain
);
336 vs
->uri
= get_uri_reference(ses
->loading_uri
);
338 /* vs->doc_view itself will get detached in
339 * render_document_frames(), but that's too
341 vs
->doc_view
->vs
= NULL
;
344 #ifdef CONFIG_ECMASCRIPT
345 vs
->ecmascript_fragile
= 1;
351 if_assert_failed
return NULL
;
353 init_list(loc
->frames
);
355 init_vs(vs
, ses
->loading_uri
, vs
->plain
);
356 add_to_history(&ses
->history
, loc
);
359 ses
->status
.visited
= 0;
361 /* This is another "branch" in the browsing, so throw away the current
362 * unhistory, we are venturing in another direction! */
363 if (ses
->task
.type
== TASK_FORWARD
)
364 clean_unhistory(&ses
->history
);
369 ses_imgmap(struct session
*ses
)
371 struct cache_entry
*cached
= find_in_cache(ses
->loading_uri
);
372 struct document_view
*doc_view
= current_frame(ses
);
373 struct fragment
*fragment
;
374 struct memory_list
*ml
;
375 struct menu_item
*menu
;
378 INTERNAL("can't find cache entry");
382 fragment
= get_cache_fragment(cached
);
383 if (!fragment
) return;
385 if (!doc_view
|| !doc_view
->document
) return;
387 if (get_image_map(cached
->head
, fragment
->data
,
388 fragment
->data
+ fragment
->length
,
389 &menu
, &ml
, ses
->loading_uri
,
390 &doc_view
->document
->options
,
391 ses
->task
.target
.frame
,
392 get_terminal_codepage(ses
->tab
->term
),
393 get_opt_codepage("document.codepage.assume", ses
),
394 get_opt_bool("document.codepage.force_assumed", ses
)))
397 add_empty_window(ses
->tab
->term
, (void (*)(void *)) freeml
, ml
);
398 do_menu(ses
->tab
->term
, menu
, ses
, 0);
408 do_redirect(struct session
*ses
, struct download
**download_p
, struct cache_entry
*cached
)
410 enum task_type task
= ses
->task
.type
;
412 if (task
== TASK_HISTORY
&& !have_location(ses
))
413 return DO_MOVE_DISPLAY
;
415 assertm(compare_uri(cached
->uri
, ses
->loading_uri
, URI_BASE
),
416 "Redirecting using bad base URI");
418 if (cached
->redirect
->protocol
== PROTOCOL_UNKNOWN
)
419 return DO_MOVE_ABORT
;
421 abort_loading(ses
, 0);
422 if (have_location(ses
))
423 *download_p
= &cur_loc(ses
)->download
;
427 set_session_referrer(ses
, cached
->uri
);
434 protocol_external_handler_T
*fn
;
435 struct uri
*uri
= cached
->redirect
;
437 fn
= get_protocol_external_handler(ses
->tab
->term
, uri
);
441 return DO_MOVE_ABORT
;
446 ses_goto(ses
, cached
->redirect
, ses
->task
.target
.frame
, NULL
,
447 CACHE_MODE_NORMAL
, task
, 1);
450 ses_goto(ses
, cached
->redirect
, NULL
, ses
->task
.target
.location
,
451 CACHE_MODE_NORMAL
, TASK_RELOAD
, 1);
454 ses_goto(ses
, cached
->redirect
, NULL
, NULL
,
455 ses
->reloadlevel
, TASK_RELOAD
, 1);
459 return DO_MOVE_DISPLAY
;
463 do_move(struct session
*ses
, struct download
**download_p
)
465 struct cache_entry
*cached
;
467 assert(download_p
&& *download_p
);
468 assertm(ses
->loading_uri
!= NULL
, "no ses->loading_uri");
469 if_assert_failed
return DO_MOVE_ABORT
;
471 if (ses
->loading_uri
->protocol
== PROTOCOL_UNKNOWN
)
472 return DO_MOVE_ABORT
;
474 /* Handling image map needs to scan the source of the loaded document
475 * so all of it has to be available. */
476 if (ses
->task
.type
== TASK_IMGMAP
&& is_in_progress_state((*download_p
)->state
))
477 return DO_MOVE_ABORT
;
479 cached
= (*download_p
)->cached
;
480 if (!cached
) return DO_MOVE_ABORT
;
482 if (cached
->redirect
&& ses
->redirect_cnt
++ < MAX_REDIRECTS
) {
483 enum do_move d
= do_redirect(ses
, download_p
, cached
);
485 if (d
!= DO_MOVE_DISPLAY
) return d
;
488 kill_timer(&ses
->display_timer
);
490 switch (ses
->task
.type
) {
494 if (setup_download_handler(ses
, &ses
->loading
, cached
, 0)) {
496 reload(ses
, CACHE_MODE_NORMAL
);
504 ses_history_move(ses
);
507 ses
->task
.target
.location
= cur_loc(ses
)->prev
;
508 ses_history_move(ses
);
513 if (is_in_progress_state((*download_p
)->state
)) {
514 if (have_location(ses
))
515 *download_p
= &cur_loc(ses
)->download
;
516 move_download(&ses
->loading
, *download_p
, PRI_MAIN
);
517 } else if (have_location(ses
)) {
518 cur_loc(ses
)->download
.state
= ses
->loading
.state
;
522 return DO_MOVE_DISPLAY
;
526 loading_callback(struct download
*download
, struct session
*ses
)
530 assertm(ses
->task
.type
, "loading_callback: no ses->task");
531 if_assert_failed
return;
533 d
= do_move(ses
, &download
);
534 if (!download
) return;
535 if (d
== DO_MOVE_DONE
) goto end
;
537 if (d
== DO_MOVE_DISPLAY
) {
538 download
->callback
= (download_callback_T
*) doc_loading_callback
;
542 if (is_in_result_state(download
->state
)) {
543 if (ses
->task
.type
) free_task(ses
);
544 if (d
== DO_MOVE_DISPLAY
) doc_loading_callback(download
, ses
);
547 if (is_in_result_state(download
->state
)
548 && !is_in_state(download
->state
, S_OK
)) {
549 print_error_dialog(ses
, download
->state
,
550 download
->conn
? download
->conn
->uri
: NULL
,
552 if (d
== DO_MOVE_ABORT
) reload(ses
, CACHE_MODE_NORMAL
);
556 check_questions_queue(ses
);
557 print_screen_status(ses
);
562 do_follow_url(struct session
*ses
, struct uri
*uri
, unsigned char *target
,
563 enum task_type task
, enum cache_mode cache_mode
, int do_referrer
)
565 struct uri
*referrer
= NULL
;
566 protocol_external_handler_T
*external_handler
;
569 print_error_dialog(ses
, connection_state(S_BAD_URL
), uri
, PRI_CANCEL
);
573 external_handler
= get_protocol_external_handler(ses
->tab
->term
, uri
);
574 if (external_handler
) {
575 external_handler(ses
, uri
);
580 struct document_view
*doc_view
= current_frame(ses
);
582 if (doc_view
&& doc_view
->document
)
583 referrer
= doc_view
->document
->uri
;
586 if (target
&& !strcmp(target
, "_blank")) {
587 int mode
= get_opt_int("document.browse.links.target_blank",
591 && !get_cmd_opt_bool("anonymous")
592 && can_open_in_new(ses
->tab
->term
)
593 && !get_cmd_opt_bool("no-connect")
594 && !get_cmd_opt_bool("no-home")) {
595 enum term_env_type env
= ses
->tab
->term
->environment
;
597 open_uri_in_new_window(ses
, uri
, referrer
, env
,
603 struct session
*new_ses
;
605 new_ses
= init_session(ses
, ses
->tab
->term
, uri
, (mode
== 2));
606 if (new_ses
) ses
= new_ses
;
610 ses
->reloadlevel
= cache_mode
;
612 if (ses
->task
.type
== task
) {
613 if (compare_uri(ses
->loading_uri
, uri
, 0)) {
614 /* We're already loading the URL. */
619 abort_loading(ses
, 0);
621 set_session_referrer(ses
, referrer
);
623 ses_goto(ses
, uri
, target
, NULL
, cache_mode
, task
, 0);
627 follow_url(struct session
*ses
, struct uri
*uri
, unsigned char *target
,
628 enum task_type task
, enum cache_mode cache_mode
, int referrer
)
630 #ifdef CONFIG_SCRIPTING
631 static int follow_url_event_id
= EVENT_NONE
;
632 unsigned char *uristring
= uri
? get_uri_string(uri
, URI_BASE
| URI_FRAGMENT
) : NULL
;
635 do_follow_url(ses
, uri
, target
, task
, cache_mode
, referrer
);
639 set_event_id(follow_url_event_id
, "follow-url");
640 trigger_event(follow_url_event_id
, &uristring
, ses
);
642 if (!uristring
|| !*uristring
) {
643 mem_free_if(uristring
);
647 /* FIXME: Compare if uristring and struri(uri) are equal */
648 /* FIXME: When uri->post will no longer be an encoded string (but
649 * hopefully some refcounted object) we will have to assign the post
650 * data object to the translated URI. */
651 uri
= get_translated_uri(uristring
, ses
->tab
->term
->cwd
);
655 do_follow_url(ses
, uri
, target
, task
, cache_mode
, referrer
);
657 #ifdef CONFIG_SCRIPTING
658 if (uri
) done_uri(uri
);
663 goto_uri(struct session
*ses
, struct uri
*uri
)
665 follow_url(ses
, uri
, NULL
, TASK_FORWARD
, CACHE_MODE_NORMAL
, 0);
669 goto_uri_frame(struct session
*ses
, struct uri
*uri
,
670 unsigned char *target
, enum cache_mode cache_mode
)
672 follow_url(ses
, uri
, target
, TASK_FORWARD
, cache_mode
, 1);
676 delayed_goto_uri_frame(void *data
)
678 struct delayed_open
*deo
= data
;
682 frame
= ses_find_frame(deo
->ses
, deo
->target
);
684 goto_uri_frame(deo
->ses
, deo
->uri
, frame
->name
, CACHE_MODE_NORMAL
);
686 goto_uri_frame(deo
->ses
, deo
->uri
, NULL
, CACHE_MODE_NORMAL
);
689 mem_free(deo
->target
);
695 map_selected(struct terminal
*term
, void *ld_
, void *ses_
)
697 struct link_def
*ld
= ld_
;
698 struct session
*ses
= ses_
;
699 struct uri
*uri
= get_uri(ld
->link
, 0);
701 goto_uri_frame(ses
, uri
, ld
->target
, CACHE_MODE_NORMAL
);
702 if (uri
) done_uri(uri
);
707 goto_url(struct session
*ses
, unsigned char *url
)
709 struct uri
*uri
= get_uri(url
, 0);
712 if (uri
) done_uri(uri
);
716 get_hooked_uri(unsigned char *uristring
, struct session
*ses
, unsigned char *cwd
)
720 #if defined(CONFIG_SCRIPTING) || defined(CONFIG_URI_REWRITE)
721 static int goto_url_event_id
= EVENT_NONE
;
723 uristring
= stracpy(uristring
);
724 if (!uristring
) return NULL
;
726 set_event_id(goto_url_event_id
, "goto-url");
727 trigger_event(goto_url_event_id
, &uristring
, ses
);
728 if (!uristring
) return NULL
;
731 uri
= *uristring
? get_translated_uri(uristring
, cwd
) : NULL
;
733 #if defined(CONFIG_SCRIPTING) || defined(CONFIG_URI_REWRITE)
740 goto_url_with_hook(struct session
*ses
, unsigned char *url
)
742 unsigned char *cwd
= ses
->tab
->term
->cwd
;
745 /* Bail out if passed empty string from goto-url dialog */
748 uri
= get_hooked_uri(url
, ses
, cwd
);
757 goto_url_home(struct session
*ses
)
759 unsigned char *homepage
= get_opt_str("ui.sessions.homepage", ses
);
761 if (!*homepage
) homepage
= getenv("WWW_HOME");
762 if (!homepage
|| !*homepage
) homepage
= WWW_HOME_URL
;
764 if (!homepage
|| !*homepage
) return 0;
766 goto_url_with_hook(ses
, homepage
);
770 /* TODO: Should there be goto_imgmap_reload() ? */
773 goto_imgmap(struct session
*ses
, struct uri
*uri
, unsigned char *target
)
775 follow_url(ses
, uri
, target
, TASK_IMGMAP
, CACHE_MODE_NORMAL
, 1);