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
) {
59 /* ses->task.target.frame must be freed in the
60 * destroy_session() => abort_loading() =>
61 * abort_preloading() call chain. Normally,
62 * free_task() called from here would do that,
63 * but if the task has no type, free_task()
64 * cannot be called because an assertion would
65 * fail. There are several functions that set
66 * ses->task.target.frame without ses->task.type,
67 * so apparently it is allowed. */
68 mem_free_set(&ses
->task
.target
.frame
, NULL
);
72 cancel_download(&ses
->loading
, interrupt
);
80 enum cache_mode cache_mode
;
81 struct session_task session_task
;
85 ses_load(struct session
*ses
, struct uri
*uri
, unsigned char *target_frame
,
86 struct location
*target_location
, enum cache_mode cache_mode
,
87 enum task_type task_type
)
89 ses
->loading
.callback
= (download_callback_T
*) loading_callback
;
90 ses
->loading
.data
= ses
;
91 ses
->loading_uri
= uri
;
93 ses
->task
.type
= task_type
;
94 mem_free_set(&ses
->task
.target
.frame
, null_or_stracpy(target_frame
));
95 ses
->task
.target
.location
= target_location
;
97 load_uri(ses
->loading_uri
, ses
->referrer
, &ses
->loading
,
98 PRI_MAIN
, cache_mode
, -1);
102 post_yes(void *task_
)
104 struct task
*task
= task_
;
106 abort_preloading(task
->ses
, 0);
108 /* XXX: Make the session inherit the URI. */
109 ses_load(task
->ses
, task
->uri
, task
->session_task
.target
.frame
,
110 task
->session_task
.target
.location
, task
->cache_mode
,
111 task
->session_task
.type
);
117 struct task
*task
= task_
;
119 reload(task
->ses
, CACHE_MODE_NORMAL
);
123 /** Check if the URI is obfuscated (bug 382). The problem is said to occur when
124 * a URI designed to pass access a specific location with a supplied username,
125 * contains misleading chars prior to the @ symbol.
127 * An attacker can exploit this issue by supplying a malicious URI pointing to
128 * a page designed to mimic that of a trusted site, and tricking a victim who
129 * follows a link into believing they are actually at the trusted location.
131 * Only the user ID (and not also the password) is checked because only the
132 * user ID is displayed in the status bar. */
134 check_malicious_uri(struct uri
*uri
)
136 unsigned char *user
, *pos
;
139 assert(uri
->user
&& uri
->userlen
);
141 user
= pos
= memacpy(uri
->user
, uri
->userlen
);
144 decode_uri_for_display(user
);
147 int length
, trailing_dots
;
149 for (length
= 0; pos
[length
] != '\0'; length
++)
150 if (!(isalnum(pos
[length
]) || pos
[length
] == '.'))
153 /* Wind back so that the TLD part is checked correctly. */
154 for (trailing_dots
= 0; trailing_dots
< length
; trailing_dots
++)
155 if (!length
|| pos
[length
- trailing_dots
- 1] != '.')
158 /* Not perfect, but I am clueless as how to do better. Besides
159 * I don't really think it is an issue for ELinks. --jonas */
160 if (end_with_known_tld(pos
, length
- trailing_dots
) != -1) {
167 while (*pos
&& (!isalnum(*pos
) || *pos
== '.'))
177 ses_goto(struct session
*ses
, struct uri
*uri
, unsigned char *target_frame
,
178 struct location
*target_location
, enum cache_mode cache_mode
,
179 enum task_type task_type
, int redir
)
181 /* [gettext_accelerator_context(ses_goto)] */
183 int referrer_incomplete
= 0;
184 int malicious_uri
= 0;
185 int confirm_submit
= uri
->form
&& get_opt_bool("document.browse.forms"
186 ".confirm_submit", ses
);
187 unsigned char *m1
= NULL
, *message
= NULL
;
188 struct memory_list
*mlist
= NULL
;
191 && ses
->doc_view
->document
192 && ses
->doc_view
->document
->refresh
) {
193 kill_document_refresh(ses
->doc_view
->document
->refresh
);
196 assertm(!ses
->loading_uri
, "Buggy URI reference counting");
198 /* Reset the redirect counter if this is not a redirect. */
200 ses
->redirect_cnt
= 0;
203 /* Figure out whether to confirm submit or not */
205 /* Only confirm submit if we are posting form data or a misleading URI
207 /* Note uri->post might be empty here but we are still supposely
208 * posting form data so this should be more correct. */
210 if (uri
->user
&& uri
->userlen
211 && get_opt_bool("document.browse.links.warn_malicious", ses
)
212 && check_malicious_uri(uri
)) {
216 } else if (uri
->form
) {
217 /* First check if the referring URI was incomplete. It
218 * indicates that the posted form data might be incomplete too.
221 struct cache_entry
*cached
;
223 cached
= find_in_cache(ses
->referrer
);
224 referrer_incomplete
= (cached
&& cached
->incomplete
);
227 if (referrer_incomplete
) {
230 } else if (get_validated_cache_entry(uri
, cache_mode
)) {
235 if (!confirm_submit
) {
236 ses_load(ses
, get_uri_reference(uri
), target_frame
,
237 target_location
, cache_mode
, task_type
);
241 task
= mem_alloc(sizeof(*task
));
245 task
->uri
= get_uri_reference(uri
);
246 task
->cache_mode
= cache_mode
;
247 task
->session_task
.type
= task_type
;
248 task
->session_task
.target
.frame
= null_or_stracpy(target_frame
);
249 task
->session_task
.target
.location
= target_location
;
252 unsigned char *host
= memacpy(uri
->host
, uri
->hostlen
);
253 unsigned char *user
= memacpy(uri
->user
, uri
->userlen
);
254 unsigned char *uristring
= get_uri_string(uri
, URI_PUBLIC
);
256 message
= msg_text(ses
->tab
->term
,
257 N_("The URL you are about to follow might be maliciously "
258 "crafted in order to confuse you. By following the URL "
259 "you will be connecting to host \"%s\" as user \"%s\".\n\n"
260 "Do you want to go to URL %s?"), host
, user
, uristring
);
264 mem_free_if(uristring
);
267 m1
= N_("Do you want to follow the redirect and post form data "
270 } else if (referrer_incomplete
) {
271 m1
= N_("The form data you are about to post might be incomplete.\n"
272 "Do you want to post to URL %s?");
274 } else if (task_type
== TASK_FORWARD
) {
275 m1
= N_("Do you want to post form data to URL %s?");
278 m1
= N_("Do you want to repost form data to URL %s?");
281 if (!message
&& m1
) {
282 unsigned char *uristring
= get_uri_string(uri
, URI_PUBLIC
);
284 message
= msg_text(ses
->tab
->term
, m1
, uristring
);
285 mem_free_if(uristring
);
288 add_to_ml(&mlist
, task
, (void *) NULL
);
289 if (task
->session_task
.target
.frame
)
290 add_to_ml(&mlist
, task
->session_task
.target
.frame
, (void *) NULL
);
291 msg_box(ses
->tab
->term
, mlist
, MSGBOX_FREE_TEXT
,
292 N_("Warning"), ALIGN_CENTER
,
295 MSG_BOX_BUTTON(N_("~Yes"), post_yes
, B_ENTER
),
296 MSG_BOX_BUTTON(N_("~No"), post_no
, B_ESC
));
300 /** If @a loaded_in_frame is set, this was called just to indicate a move inside
301 * a frameset, and we basically just reset the appropriate frame's view_state in
302 * that case. When clicking on a link inside a frame, the frame URI is somehow
303 * updated and added to the files-to-load queue, then ses_forward() is called
304 * with @a loaded_in_frame unset, duplicating the whole frameset's location,
305 * then later the file-to-load callback calls it for the particular frame with
306 * @a loaded_in_frame set. */
308 ses_forward(struct session
*ses
, int loaded_in_frame
)
310 struct location
*loc
= NULL
;
311 struct view_state
*vs
;
313 if (!loaded_in_frame
) {
315 mem_free_set(&ses
->search_word
, NULL
);
319 if (!loaded_in_frame
) {
320 loc
= mem_calloc(1, sizeof(*loc
));
321 if (!loc
) return NULL
;
322 copy_struct(&loc
->download
, &ses
->loading
);
325 if (ses
->task
.target
.frame
&& *ses
->task
.target
.frame
) {
328 assertm(have_location(ses
), "no location yet");
329 if_assert_failed
return NULL
;
331 if (!loaded_in_frame
) {
332 copy_location(loc
, cur_loc(ses
));
333 add_to_history(&ses
->history
, loc
);
336 frame
= ses_find_frame(ses
, ses
->task
.target
.frame
);
338 if (!loaded_in_frame
) {
339 del_from_history(&ses
->history
, loc
);
340 destroy_location(loc
);
342 mem_free_set(&ses
->task
.target
.frame
, NULL
);
347 if (!loaded_in_frame
) {
349 init_vs(vs
, ses
->loading_uri
, vs
->plain
);
352 vs
->uri
= get_uri_reference(ses
->loading_uri
);
354 /* vs->doc_view itself will get detached in
355 * render_document_frames(), but that's too
357 vs
->doc_view
->vs
= NULL
;
360 #ifdef CONFIG_ECMASCRIPT
361 vs
->ecmascript_fragile
= 1;
367 if_assert_failed
return NULL
;
369 init_list(loc
->frames
);
371 init_vs(vs
, ses
->loading_uri
, vs
->plain
);
372 add_to_history(&ses
->history
, loc
);
375 ses
->status
.visited
= 0;
377 /* This is another "branch" in the browsing, so throw away the current
378 * unhistory, we are venturing in another direction! */
379 if (ses
->task
.type
== TASK_FORWARD
)
380 clean_unhistory(&ses
->history
);
385 ses_imgmap(struct session
*ses
)
387 struct cache_entry
*cached
= find_in_cache(ses
->loading_uri
);
388 struct document_view
*doc_view
= current_frame(ses
);
389 struct fragment
*fragment
;
390 struct memory_list
*ml
;
391 struct menu_item
*menu
;
394 INTERNAL("can't find cache entry");
398 fragment
= get_cache_fragment(cached
);
399 if (!fragment
) return;
401 if (!doc_view
|| !doc_view
->document
) return;
403 if (get_image_map(cached
->head
, fragment
->data
,
404 fragment
->data
+ fragment
->length
,
405 &menu
, &ml
, ses
->loading_uri
,
406 &doc_view
->document
->options
,
407 ses
->task
.target
.frame
,
408 get_terminal_codepage(ses
->tab
->term
),
409 get_opt_codepage("document.codepage.assume", ses
),
410 get_opt_bool("document.codepage.force_assumed", ses
)))
413 add_empty_window(ses
->tab
->term
, (void (*)(void *)) freeml
, ml
);
414 do_menu(ses
->tab
->term
, menu
, ses
, 0);
424 do_redirect(struct session
*ses
, struct download
**download_p
, struct cache_entry
*cached
)
426 enum task_type task
= ses
->task
.type
;
428 if (task
== TASK_HISTORY
&& !have_location(ses
))
429 return DO_MOVE_DISPLAY
;
431 assertm(compare_uri(cached
->uri
, ses
->loading_uri
, URI_BASE
),
432 "Redirecting using bad base URI");
434 if (cached
->redirect
->protocol
== PROTOCOL_UNKNOWN
)
435 return DO_MOVE_ABORT
;
437 abort_loading(ses
, 0);
438 if (have_location(ses
))
439 *download_p
= &cur_loc(ses
)->download
;
443 set_session_referrer(ses
, cached
->uri
);
450 protocol_external_handler_T
*fn
;
451 struct uri
*uri
= cached
->redirect
;
453 fn
= get_protocol_external_handler(ses
->tab
->term
, uri
);
457 return DO_MOVE_ABORT
;
462 ses_goto(ses
, cached
->redirect
, ses
->task
.target
.frame
, NULL
,
463 CACHE_MODE_NORMAL
, task
, 1);
466 ses_goto(ses
, cached
->redirect
, NULL
, ses
->task
.target
.location
,
467 CACHE_MODE_NORMAL
, TASK_RELOAD
, 1);
470 ses_goto(ses
, cached
->redirect
, NULL
, NULL
,
471 ses
->reloadlevel
, TASK_RELOAD
, 1);
475 return DO_MOVE_DISPLAY
;
479 do_move(struct session
*ses
, struct download
**download_p
)
481 struct cache_entry
*cached
;
483 assert(download_p
&& *download_p
);
484 assertm(ses
->loading_uri
!= NULL
, "no ses->loading_uri");
485 if_assert_failed
return DO_MOVE_ABORT
;
487 if (ses
->loading_uri
->protocol
== PROTOCOL_UNKNOWN
)
488 return DO_MOVE_ABORT
;
490 /* Handling image map needs to scan the source of the loaded document
491 * so all of it has to be available. */
492 if (ses
->task
.type
== TASK_IMGMAP
&& is_in_progress_state((*download_p
)->state
))
493 return DO_MOVE_ABORT
;
495 cached
= (*download_p
)->cached
;
496 if (!cached
) return DO_MOVE_ABORT
;
498 if (cached
->redirect
&& ses
->redirect_cnt
++ < MAX_REDIRECTS
) {
499 enum do_move d
= do_redirect(ses
, download_p
, cached
);
501 if (d
!= DO_MOVE_DISPLAY
) return d
;
504 kill_timer(&ses
->display_timer
);
506 switch (ses
->task
.type
) {
510 if (setup_download_handler(ses
, &ses
->loading
, cached
, 0)) {
512 reload(ses
, CACHE_MODE_NORMAL
);
520 ses_history_move(ses
);
523 ses
->task
.target
.location
= cur_loc(ses
)->prev
;
524 ses_history_move(ses
);
529 if (is_in_progress_state((*download_p
)->state
)) {
530 if (have_location(ses
))
531 *download_p
= &cur_loc(ses
)->download
;
532 move_download(&ses
->loading
, *download_p
, PRI_MAIN
);
533 } else if (have_location(ses
)) {
534 cur_loc(ses
)->download
.state
= ses
->loading
.state
;
538 return DO_MOVE_DISPLAY
;
542 loading_callback(struct download
*download
, struct session
*ses
)
546 assertm(ses
->task
.type
, "loading_callback: no ses->task");
547 if_assert_failed
return;
549 d
= do_move(ses
, &download
);
550 if (!download
) return;
551 if (d
== DO_MOVE_DONE
) goto end
;
553 if (d
== DO_MOVE_DISPLAY
) {
554 download
->callback
= (download_callback_T
*) doc_loading_callback
;
555 download
->data
= ses
;
559 if (is_in_result_state(download
->state
)) {
560 if (ses
->task
.type
) free_task(ses
);
561 if (d
== DO_MOVE_DISPLAY
) doc_loading_callback(download
, ses
);
564 if (is_in_result_state(download
->state
)
565 && !is_in_state(download
->state
, S_OK
)) {
566 print_error_dialog(ses
, download
->state
,
567 download
->conn
? download
->conn
->uri
: NULL
,
569 if (d
== DO_MOVE_ABORT
) reload(ses
, CACHE_MODE_NORMAL
);
573 check_questions_queue(ses
);
574 print_screen_status(ses
);
579 do_follow_url(struct session
*ses
, struct uri
*uri
, unsigned char *target
,
580 enum task_type task
, enum cache_mode cache_mode
, int do_referrer
)
582 struct uri
*referrer
= NULL
;
583 protocol_external_handler_T
*external_handler
;
586 print_error_dialog(ses
, connection_state(S_BAD_URL
), uri
, PRI_CANCEL
);
590 external_handler
= get_protocol_external_handler(ses
->tab
->term
, uri
);
591 if (external_handler
) {
592 external_handler(ses
, uri
);
597 struct document_view
*doc_view
= current_frame(ses
);
599 if (doc_view
&& doc_view
->document
)
600 referrer
= doc_view
->document
->uri
;
603 if (target
&& !strcmp(target
, "_blank")) {
604 int mode
= get_opt_int("document.browse.links.target_blank",
608 && !get_cmd_opt_bool("anonymous")
609 && can_open_in_new(ses
->tab
->term
)
610 && !get_cmd_opt_bool("no-connect")
611 && !get_cmd_opt_bool("no-home")) {
612 enum term_env_type env
= ses
->tab
->term
->environment
;
614 open_uri_in_new_window(ses
, uri
, referrer
, env
,
620 struct session
*new_ses
;
622 new_ses
= init_session(ses
, ses
->tab
->term
, uri
, (mode
== 2));
623 if (new_ses
) ses
= new_ses
;
627 ses
->reloadlevel
= cache_mode
;
629 if (ses
->task
.type
== task
) {
630 if (compare_uri(ses
->loading_uri
, uri
, 0)) {
631 /* We're already loading the URL. */
636 abort_loading(ses
, 0);
638 set_session_referrer(ses
, referrer
);
640 ses_goto(ses
, uri
, target
, NULL
, cache_mode
, task
, 0);
644 follow_url(struct session
*ses
, struct uri
*uri
, unsigned char *target
,
645 enum task_type task
, enum cache_mode cache_mode
, int referrer
)
647 #ifdef CONFIG_SCRIPTING
648 static int follow_url_event_id
= EVENT_NONE
;
649 unsigned char *uristring
;
651 uristring
= uri
&& !uri
->post
? get_uri_string(uri
, URI_BASE
| URI_FRAGMENT
)
654 /* Do nothing if we do not have a URI or if it is a POST request
655 * because scripts can corrupt POST requests leading to bad
656 * things happening later on. */
658 do_follow_url(ses
, uri
, target
, task
, cache_mode
, referrer
);
662 set_event_id(follow_url_event_id
, "follow-url");
663 trigger_event(follow_url_event_id
, &uristring
, ses
);
665 if (!uristring
|| !*uristring
) {
666 mem_free_if(uristring
);
670 /* FIXME: Compare if uristring and struri(uri) are equal */
671 /* FIXME: When uri->post will no longer be an encoded string (but
672 * hopefully some refcounted object) we will have to assign the post
673 * data object to the translated URI. */
674 uri
= get_translated_uri(uristring
, ses
->tab
->term
->cwd
);
678 do_follow_url(ses
, uri
, target
, task
, cache_mode
, referrer
);
680 #ifdef CONFIG_SCRIPTING
681 if (uri
) done_uri(uri
);
686 goto_uri(struct session
*ses
, struct uri
*uri
)
688 follow_url(ses
, uri
, NULL
, TASK_FORWARD
, CACHE_MODE_NORMAL
, 0);
692 goto_uri_frame(struct session
*ses
, struct uri
*uri
,
693 unsigned char *target
, enum cache_mode cache_mode
)
695 follow_url(ses
, uri
, target
, TASK_FORWARD
, cache_mode
, 1);
699 delayed_goto_uri_frame(void *data
)
701 struct delayed_open
*deo
= data
;
705 frame
= deo
->target
? ses_find_frame(deo
->ses
, deo
->target
) : NULL
;
707 goto_uri_frame(deo
->ses
, deo
->uri
, frame
->name
, CACHE_MODE_NORMAL
);
709 goto_uri_frame(deo
->ses
, deo
->uri
, NULL
, CACHE_MODE_NORMAL
);
712 mem_free_if(deo
->target
);
718 map_selected(struct terminal
*term
, void *ld_
, void *ses_
)
720 struct link_def
*ld
= ld_
;
721 struct session
*ses
= ses_
;
722 struct uri
*uri
= get_uri(ld
->link
, 0);
724 goto_uri_frame(ses
, uri
, ld
->target
, CACHE_MODE_NORMAL
);
725 if (uri
) done_uri(uri
);
730 goto_url(struct session
*ses
, unsigned char *url
)
732 struct uri
*uri
= get_uri(url
, 0);
735 if (uri
) done_uri(uri
);
739 get_hooked_uri(unsigned char *uristring
, struct session
*ses
, unsigned char *cwd
)
743 #if defined(CONFIG_SCRIPTING) || defined(CONFIG_URI_REWRITE)
744 static int goto_url_event_id
= EVENT_NONE
;
746 uristring
= stracpy(uristring
);
747 if (!uristring
) return NULL
;
749 set_event_id(goto_url_event_id
, "goto-url");
750 trigger_event(goto_url_event_id
, &uristring
, ses
);
751 if (!uristring
) return NULL
;
754 uri
= *uristring
? get_translated_uri(uristring
, cwd
) : NULL
;
756 #if defined(CONFIG_SCRIPTING) || defined(CONFIG_URI_REWRITE)
763 goto_url_with_hook(struct session
*ses
, unsigned char *url
)
765 unsigned char *cwd
= ses
->tab
->term
->cwd
;
768 /* Bail out if passed empty string from goto-url dialog */
771 uri
= get_hooked_uri(url
, ses
, cwd
);
780 goto_url_home(struct session
*ses
)
782 unsigned char *homepage
= get_opt_str("ui.sessions.homepage", ses
);
784 if (!*homepage
) homepage
= getenv("WWW_HOME");
785 if (!homepage
|| !*homepage
) homepage
= WWW_HOME_URL
;
787 if (!homepage
|| !*homepage
) return 0;
789 goto_url_with_hook(ses
, homepage
);
793 /* TODO: Should there be goto_imgmap_reload() ? */
796 goto_imgmap(struct session
*ses
, struct uri
*uri
, unsigned char *target
)
798 follow_url(ses
, uri
, target
, TASK_IMGMAP
, CACHE_MODE_NORMAL
, 1);