1 /* Sessions task management */
2 /* $Id: task.c,v 1.183 2005/09/06 14:16:48 witekfl Exp $ */
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
;
55 abort_preloading(struct session
*ses
, int interrupt
)
57 if (!ses
->task
.type
) return;
59 change_connection(&ses
->loading
, NULL
, PRI_CANCEL
, interrupt
);
67 enum cache_mode cache_mode
;
68 struct session_task session_task
;
72 post_yes(struct task
*task
)
74 struct session
*ses
= task
->ses
;
76 abort_preloading(task
->ses
, 0);
78 ses
->loading
.callback
= (download_callback_T
*) loading_callback
;
79 ses
->loading
.data
= task
->ses
;
80 ses
->loading_uri
= task
->uri
; /* XXX: Make the session inherit the URI. */
81 memcpy(&ses
->task
, &task
->session_task
, sizeof(ses
->task
));
83 load_uri(ses
->loading_uri
, ses
->referrer
, &ses
->loading
,
84 PRI_MAIN
, task
->cache_mode
, -1);
88 post_no(struct task
*task
)
90 reload(task
->ses
, CACHE_MODE_NORMAL
);
94 /* Check if the URI is obfuscated (bug 382). The problem is said to occur when
95 * a URI designed to pass access a specific location with a supplied username,
96 * contains misleading chars prior to the @ symbol.
98 * An attacker can exploit this issue by supplying a malicious URI pointing to
99 * a page designed to mimic that of a trusted site, and tricking a victim who
100 * follows a link into believing they are actually at the trusted location.
102 * Only the user ID (and not also the password) is checked because only the
103 * user ID is displayed in the status bar. */
105 check_malicious_uri(struct uri
*uri
)
107 unsigned char *user
, *pos
;
110 assert(uri
->user
&& uri
->userlen
);
112 user
= pos
= memacpy(uri
->user
, uri
->userlen
);
115 decode_uri_for_display(user
);
118 int length
, trailing_dots
;
120 for (length
= 0; pos
[length
] != '\0'; length
++)
121 if (!(isalnum(pos
[length
]) || pos
[length
] == '.'))
124 /* Wind back so that the TLD part is checked correctly. */
125 for (trailing_dots
= 0; trailing_dots
< length
; trailing_dots
++)
126 if (!length
|| pos
[length
- trailing_dots
- 1] != '.')
129 /* Not perfect, but I am clueless as how to do better. Besides
130 * I don't really think it is an issue for ELinks. --jonas */
131 if (end_with_known_tld(pos
, length
- trailing_dots
) != -1) {
138 while (*pos
&& (!isalnum(*pos
) || *pos
== '.'))
148 ses_goto(struct session
*ses
, struct uri
*uri
, unsigned char *target_frame
,
149 struct location
*target_location
, enum cache_mode cache_mode
,
150 enum task_type task_type
, int redir
)
153 int referrer_incomplete
= 0;
154 int malicious_uri
= 0;
155 int confirm_submit
= uri
->form
;
156 unsigned char *m1
= NULL
, *message
= NULL
;
159 && ses
->doc_view
->document
160 && ses
->doc_view
->document
->refresh
) {
161 kill_document_refresh(ses
->doc_view
->document
->refresh
);
164 assertm(!ses
->loading_uri
, "Buggy URI reference counting");
166 /* Reset the redirect counter if this is not a redirect. */
168 ses
->redirect_cnt
= 0;
171 /* Figure out whether to confirm submit or not */
173 /* Only confirm submit if we are posting form data or a misleading URI
175 /* Note uri->post might be empty here but we are still supposely
176 * posting form data so this should be more correct. */
178 if (uri
->user
&& uri
->userlen
179 && get_opt_bool("document.browse.links.warn_malicious")
180 && check_malicious_uri(uri
)) {
184 } else if (!uri
->form
) {
188 struct cache_entry
*cached
;
190 /* First check if the referring URI was incomplete. It
191 * indicates that the posted form data might be incomplete too.
194 cached
= find_in_cache(ses
->referrer
);
195 referrer_incomplete
= (cached
&& cached
->incomplete
);
198 if (!get_opt_bool("document.browse.forms.confirm_submit")
199 && !referrer_incomplete
) {
202 } else if (get_validated_cache_entry(uri
, cache_mode
)) {
207 if (!confirm_submit
) {
208 ses
->loading
.callback
= (download_callback_T
*) loading_callback
;
209 ses
->loading
.data
= ses
;
210 ses
->loading_uri
= get_uri_reference(uri
);
212 ses
->task
.type
= task_type
;
213 ses
->task
.target
.frame
= target_frame
;
214 ses
->task
.target
.location
= target_location
;
216 load_uri(ses
->loading_uri
, ses
->referrer
, &ses
->loading
,
217 PRI_MAIN
, cache_mode
, -1);
222 task
= mem_alloc(sizeof(*task
));
226 task
->uri
= get_uri_reference(uri
);
227 task
->cache_mode
= cache_mode
;
228 task
->session_task
.type
= task_type
;
229 task
->session_task
.target
.frame
= target_frame
;
230 task
->session_task
.target
.location
= target_location
;
233 unsigned char *host
= memacpy(uri
->host
, uri
->hostlen
);
234 unsigned char *user
= memacpy(uri
->user
, uri
->userlen
);
235 unsigned char *uristring
= get_uri_string(uri
, URI_PUBLIC
);
237 message
= msg_text(ses
->tab
->term
,
238 N_("The URL you are about to follow might be maliciously "
239 "crafted in order to confuse you. By following the URL "
240 "you will be connecting to host \"%s\" as user \"%s\".\n\n"
241 "Do you want to go to URL %s?"), host
, user
, uristring
);
245 mem_free_if(uristring
);
248 m1
= N_("Do you want to follow the redirect and post form data "
251 } else if (referrer_incomplete
) {
252 m1
= N_("The form data you are about to post might be incomplete.\n"
253 "Do you want to post to URL %s?");
255 } else if (task_type
== TASK_FORWARD
) {
256 m1
= N_("Do you want to post form data to URL %s?");
259 m1
= N_("Do you want to repost form data to URL %s?");
262 if (!message
&& m1
) {
263 unsigned char *uristring
= get_uri_string(uri
, URI_PUBLIC
);
265 message
= msg_text(ses
->tab
->term
, m1
, uristring
);
266 mem_free_if(uristring
);
269 msg_box(ses
->tab
->term
, getml(task
, NULL
), MSGBOX_FREE_TEXT
,
270 N_("Warning"), ALIGN_CENTER
,
273 N_("~Yes"), post_yes
, B_ENTER
,
274 N_("~No"), post_no
, B_ESC
);
278 /* If @loaded_in_frame is set, this was called just to indicate a move inside a
279 * frameset, and we basically just reset the appropriate frame's view_state in
280 * that case. When clicking on a link inside a frame, the frame URI is somehow
281 * updated and added to the files-to-load queue, then ses_forward() is called
282 * with @loaded_in_frame unset, duplicating the whole frameset's location, then
283 * later the file-to-load callback calls it for the particular frame with
284 * @loaded_in_frame set. */
286 ses_forward(struct session
*ses
, int loaded_in_frame
)
288 struct location
*loc
= NULL
;
289 struct view_state
*vs
;
291 if (!loaded_in_frame
) {
293 mem_free_set(&ses
->search_word
, NULL
);
297 if (!loaded_in_frame
) {
298 loc
= mem_calloc(1, sizeof(*loc
));
299 if (!loc
) return NULL
;
300 copy_struct(&loc
->download
, &ses
->loading
);
303 if (ses
->task
.target
.frame
&& *ses
->task
.target
.frame
) {
306 assertm(have_location(ses
), "no location yet");
307 if_assert_failed
return NULL
;
309 if (!loaded_in_frame
) {
310 copy_location(loc
, cur_loc(ses
));
311 add_to_history(&ses
->history
, loc
);
314 frame
= ses_find_frame(ses
, ses
->task
.target
.frame
);
316 if (!loaded_in_frame
) {
317 del_from_history(&ses
->history
, loc
);
318 destroy_location(loc
);
320 ses
->task
.target
.frame
= NULL
;
325 if (!loaded_in_frame
) {
327 init_vs(vs
, ses
->loading_uri
, vs
->plain
);
330 vs
->uri
= get_uri_reference(ses
->loading_uri
);
332 /* vs->doc_view itself will get detached in
333 * render_document_frames(), but that's too
335 vs
->doc_view
->vs
= NULL
;
338 #ifdef CONFIG_ECMASCRIPT
339 vs
->ecmascript_fragile
= 1;
345 if_assert_failed
return NULL
;
347 init_list(loc
->frames
);
349 init_vs(vs
, ses
->loading_uri
, vs
->plain
);
350 add_to_history(&ses
->history
, loc
);
353 ses
->status
.visited
= 0;
355 /* This is another "branch" in the browsing, so throw away the current
356 * unhistory, we are venturing in another direction! */
357 if (ses
->task
.type
== TASK_FORWARD
)
358 clean_unhistory(&ses
->history
);
363 ses_imgmap(struct session
*ses
)
365 struct cache_entry
*cached
= find_in_cache(ses
->loading_uri
);
366 struct document_view
*doc_view
= current_frame(ses
);
367 struct fragment
*fragment
;
368 struct memory_list
*ml
;
369 struct menu_item
*menu
;
372 INTERNAL("can't find cache entry");
376 fragment
= get_cache_fragment(cached
);
377 if (!fragment
) return;
379 if (!doc_view
|| !doc_view
->document
) return;
381 if (get_image_map(cached
->head
, fragment
->data
,
382 fragment
->data
+ fragment
->length
,
383 &menu
, &ml
, ses
->loading_uri
,
384 &doc_view
->document
->options
,
385 ses
->task
.target
.frame
,
386 get_opt_codepage_tree(ses
->tab
->term
->spec
, "charset"),
387 get_opt_codepage("document.codepage.assume"),
388 get_opt_bool("document.codepage.force_assumed")))
391 add_empty_window(ses
->tab
->term
, (void (*)(void *)) freeml
, ml
);
392 do_menu(ses
->tab
->term
, menu
, ses
, 0);
402 do_redirect(struct session
*ses
, struct download
**download_p
, struct cache_entry
*cached
)
404 enum task_type task
= ses
->task
.type
;
406 if (task
== TASK_HISTORY
&& !have_location(ses
))
407 return DO_MOVE_DISPLAY
;
409 assertm(compare_uri(cached
->uri
, ses
->loading_uri
, URI_BASE
),
410 "Redirecting using bad base URI");
412 if (cached
->redirect
->protocol
== PROTOCOL_UNKNOWN
)
413 return DO_MOVE_ABORT
;
415 abort_loading(ses
, 0);
416 if (have_location(ses
))
417 *download_p
= &cur_loc(ses
)->download
;
421 set_session_referrer(ses
, cached
->uri
);
428 protocol_external_handler_T
*fn
;
429 struct uri
*uri
= cached
->redirect
;
431 fn
= get_protocol_external_handler(ses
->tab
->term
, uri
);
435 return DO_MOVE_ABORT
;
440 ses_goto(ses
, cached
->redirect
, ses
->task
.target
.frame
, NULL
,
441 CACHE_MODE_NORMAL
, task
, 1);
444 ses_goto(ses
, cached
->redirect
, NULL
, ses
->task
.target
.location
,
445 CACHE_MODE_NORMAL
, TASK_RELOAD
, 1);
448 ses_goto(ses
, cached
->redirect
, NULL
, NULL
,
449 ses
->reloadlevel
, TASK_RELOAD
, 1);
453 return DO_MOVE_DISPLAY
;
457 do_move(struct session
*ses
, struct download
**download_p
)
459 struct cache_entry
*cached
;
461 assert(download_p
&& *download_p
);
462 assertm(ses
->loading_uri
, "no ses->loading_uri");
463 if_assert_failed
return DO_MOVE_ABORT
;
465 if (ses
->loading_uri
->protocol
== PROTOCOL_UNKNOWN
)
466 return DO_MOVE_ABORT
;
468 /* Handling image map needs to scan the source of the loaded document
469 * so all of it has to be available. */
470 if (ses
->task
.type
== TASK_IMGMAP
&& is_in_progress_state((*download_p
)->state
))
471 return DO_MOVE_ABORT
;
473 cached
= (*download_p
)->cached
;
474 if (!cached
) return DO_MOVE_ABORT
;
476 if (cached
->redirect
&& ses
->redirect_cnt
++ < MAX_REDIRECTS
) {
477 enum do_move d
= do_redirect(ses
, download_p
, cached
);
479 if (d
!= DO_MOVE_DISPLAY
) return d
;
482 kill_timer(&ses
->display_timer
);
484 switch (ses
->task
.type
) {
488 if (setup_download_handler(ses
, &ses
->loading
, cached
, 0)) {
490 reload(ses
, CACHE_MODE_NORMAL
);
498 ses_history_move(ses
);
501 ses
->task
.target
.location
= cur_loc(ses
)->prev
;
502 ses_history_move(ses
);
507 if (is_in_progress_state((*download_p
)->state
)) {
508 if (have_location(ses
))
509 *download_p
= &cur_loc(ses
)->download
;
510 change_connection(&ses
->loading
, *download_p
, PRI_MAIN
, 0);
511 } else if (have_location(ses
)) {
512 cur_loc(ses
)->download
.state
= ses
->loading
.state
;
516 return DO_MOVE_DISPLAY
;
520 loading_callback(struct download
*download
, struct session
*ses
)
524 assertm(ses
->task
.type
, "loading_callback: no ses->task");
525 if_assert_failed
return;
527 d
= do_move(ses
, &download
);
528 if (!download
) return;
529 if (d
== DO_MOVE_DONE
) goto end
;
531 if (d
== DO_MOVE_DISPLAY
) {
532 download
->callback
= (download_callback_T
*) doc_loading_callback
;
536 if (is_in_result_state(download
->state
)) {
537 if (ses
->task
.type
) free_task(ses
);
538 if (d
== DO_MOVE_DISPLAY
) doc_loading_callback(download
, ses
);
541 if (is_in_result_state(download
->state
) && download
->state
!= S_OK
) {
542 print_error_dialog(ses
, download
->state
, download
->conn
->uri
,
544 if (d
== DO_MOVE_ABORT
) reload(ses
, CACHE_MODE_NORMAL
);
548 check_questions_queue(ses
);
549 print_screen_status(ses
);
554 do_follow_url(struct session
*ses
, struct uri
*uri
, unsigned char *target
,
555 enum task_type task
, enum cache_mode cache_mode
, int do_referrer
)
557 struct uri
*referrer
= NULL
;
558 protocol_external_handler_T
*external_handler
;
561 print_error_dialog(ses
, S_BAD_URL
, uri
, PRI_CANCEL
);
565 external_handler
= get_protocol_external_handler(ses
->tab
->term
, uri
);
566 if (external_handler
) {
567 external_handler(ses
, uri
);
572 struct document_view
*doc_view
= current_frame(ses
);
574 if (doc_view
&& doc_view
->document
)
575 referrer
= doc_view
->document
->uri
;
578 if (target
&& !strcmp(target
, "_blank")) {
579 int mode
= get_opt_int("document.browse.links.target_blank");
582 && !get_cmd_opt_bool("anonymous")
583 && can_open_in_new(ses
->tab
->term
)
584 && !get_cmd_opt_bool("no-connect")
585 && !get_cmd_opt_bool("no-home")) {
586 enum term_env_type env
= ses
->tab
->term
->environment
;
588 open_uri_in_new_window(ses
, uri
, referrer
, env
,
594 struct session
*new_ses
;
596 new_ses
= init_session(ses
, ses
->tab
->term
, uri
, (mode
== 2));
597 if (new_ses
) ses
= new_ses
;
601 ses
->reloadlevel
= cache_mode
;
603 if (ses
->task
.type
== task
) {
604 if (compare_uri(ses
->loading_uri
, uri
, 0)) {
605 /* We're already loading the URL. */
610 abort_loading(ses
, 0);
612 set_session_referrer(ses
, referrer
);
614 ses_goto(ses
, uri
, target
, NULL
, cache_mode
, task
, 0);
618 follow_url(struct session
*ses
, struct uri
*uri
, unsigned char *target
,
619 enum task_type task
, enum cache_mode cache_mode
, int referrer
)
621 #ifdef CONFIG_SCRIPTING
622 static int follow_url_event_id
= EVENT_NONE
;
623 unsigned char *uristring
= uri
? get_uri_string(uri
, URI_BASE
| URI_FRAGMENT
) : NULL
;
626 do_follow_url(ses
, uri
, target
, task
, cache_mode
, referrer
);
630 set_event_id(follow_url_event_id
, "follow-url");
631 trigger_event(follow_url_event_id
, &uristring
, ses
);
633 if (!uristring
|| !*uristring
) {
634 mem_free_if(uristring
);
638 /* FIXME: Compare if uristring and struri(uri) are equal */
639 /* FIXME: When uri->post will no longer be an encoded string (but
640 * hopefully some refcounted object) we will have to assign the post
641 * data object to the translated URI. */
642 uri
= get_translated_uri(uristring
, ses
->tab
->term
->cwd
);
646 do_follow_url(ses
, uri
, target
, task
, cache_mode
, referrer
);
648 #ifdef CONFIG_SCRIPTING
649 if (uri
) done_uri(uri
);
654 goto_uri(struct session
*ses
, struct uri
*uri
)
656 follow_url(ses
, uri
, NULL
, TASK_FORWARD
, CACHE_MODE_NORMAL
, 0);
660 goto_uri_frame(struct session
*ses
, struct uri
*uri
,
661 unsigned char *target
, enum cache_mode cache_mode
)
663 follow_url(ses
, uri
, target
, TASK_FORWARD
, cache_mode
, 1);
668 map_selected(struct terminal
*term
, void *ld_
, void *ses_
)
670 struct link_def
*ld
= ld_
;
671 struct session
*ses
= ses_
;
672 struct uri
*uri
= get_uri(ld
->link
, 0);
674 goto_uri_frame(ses
, uri
, ld
->target
, CACHE_MODE_NORMAL
);
675 if (uri
) done_uri(uri
);
680 goto_url(struct session
*ses
, unsigned char *url
)
682 struct uri
*uri
= get_uri(url
, 0);
685 if (uri
) done_uri(uri
);
689 get_hooked_uri(unsigned char *uristring
, struct session
*ses
, unsigned char *cwd
)
693 #if defined(CONFIG_SCRIPTING) || defined(CONFIG_URI_REWRITE)
694 static int goto_url_event_id
= EVENT_NONE
;
696 uristring
= stracpy(uristring
);
697 if (!uristring
) return NULL
;
699 set_event_id(goto_url_event_id
, "goto-url");
700 trigger_event(goto_url_event_id
, &uristring
, ses
);
701 if (!uristring
) return NULL
;
704 uri
= *uristring
? get_translated_uri(uristring
, cwd
) : NULL
;
706 #if defined(CONFIG_SCRIPTING) || defined(CONFIG_URI_REWRITE)
713 goto_url_with_hook(struct session
*ses
, unsigned char *url
)
715 unsigned char *cwd
= ses
->tab
->term
->cwd
;
718 /* Bail out if passed empty string from goto-url dialog */
721 uri
= get_hooked_uri(url
, ses
, cwd
);
723 if (uri
) done_uri(uri
);
727 goto_url_home(struct session
*ses
)
729 unsigned char *homepage
= get_opt_str("ui.sessions.homepage");
731 if (!*homepage
) homepage
= getenv("WWW_HOME");
732 if (!homepage
|| !*homepage
) homepage
= WWW_HOME_URL
;
734 if (!homepage
|| !*homepage
) return 0;
736 goto_url_with_hook(ses
, homepage
);
740 /* TODO: Should there be goto_imgmap_reload() ? */
743 goto_imgmap(struct session
*ses
, struct uri
*uri
, unsigned char *target
)
745 follow_url(ses
, uri
, target
, TASK_IMGMAP
, CACHE_MODE_NORMAL
, 1);