1
/* Sessions task management */
2
/* $Id: task.c,v 1.146.2.8 2005/09/14 13:19:10 jonas Exp $ */
15
#include "bfu/dialog.h"
16
#include "cache/cache.h"
17
#include "dialogs/status.h"
18
#include "document/document.h"
19
#include "document/html/parser.h"
20
#include "document/refresh.h"
21
#include "document/view.h"
22
#include "intl/gettext/libintl.h"
23
#include "lowlevel/select.h"
24
#include "protocol/protocol.h"
25
#include "protocol/uri.h"
26
#include "terminal/terminal.h"
27
#include "terminal/window.h"
28
#include "sched/download.h"
29
#include "sched/event.h"
30
#include "sched/session.h"
31
#include "sched/task.h"
32
#include "viewer/text/view.h"
36
free_task(struct session *ses)
38
assertm(ses->task.type, "Session has no task");
39
if_assert_failed return;
41
if (ses->loading_uri) {
42
done_uri(ses->loading_uri);
43
ses->loading_uri = NULL;
45
ses->task.type = TASK_NONE;
49
abort_preloading(struct session *ses, int interrupt)
51
if (!ses->task.type) return;
53
change_connection(&ses->loading, NULL, PRI_CANCEL, interrupt);
61
enum cache_mode cache_mode;
63
unsigned char *target_frame;
64
struct location *target_location;
68
post_yes(struct task *task)
70
struct session *ses = task->ses;
72
abort_preloading(task->ses, 0);
74
ses->loading.callback = (void (*)(struct download *, void *)) loading_callback;
75
ses->loading.data = task->ses;
76
ses->loading_uri = task->uri; /* XXX: Make the session inherit the URI. */
78
ses->task.type = task->type;
79
ses->task.target_frame = task->target_frame;
80
ses->task.target_location = task->target_location;
82
load_uri(ses->loading_uri, ses->referrer, &ses->loading,
83
PRI_MAIN, task->cache_mode, -1);
87
post_no(struct task *task)
89
reload(task->ses, CACHE_MODE_NORMAL);
93
/* Check if the URI is obfuscated (bug 382). The problem is said to occur when
94
* a URI designed to pass access a specific location with a supplied username,
95
* contains misleading chars prior to the @ symbol.
97
* An attacker can exploit this issue by supplying a malicious URI pointing to
98
* a page designed to mimic that of a trusted site, and tricking a victim who
99
* follows a link into believing they are actually at the trusted location.
101
* Only the user ID (and not also the password) is checked because only the
102
* user ID is displayed in the status bar. */
104
check_malicious_uri(struct uri *uri)
106
unsigned char *user, *pos;
109
assert(uri->user && uri->userlen);
111
user = pos = memacpy(uri->user, uri->userlen);
114
decode_uri_for_display(user);
117
int length, trailing_dots;
119
for (length = 0; pos[length] != '\0'; length++)
120
if (!(isalnum(pos[length]) || pos[length] == '.'))
123
/* Wind back so that the TLD part is checked correctly. */
124
for (trailing_dots = 0; trailing_dots < length; trailing_dots++)
125
if (!length || pos[length - trailing_dots - 1] != '.')
128
/* Not perfect, but I am clueless as how to do better. Besides
129
* I don't really think it is an issue for ELinks. --jonas */
130
if (end_with_known_tld(pos, length - trailing_dots) != -1) {
137
while (*pos && (!isalnum(*pos) || *pos == '.'))
147
ses_goto(struct session *ses, struct uri *uri, unsigned char *target_frame,
148
struct location *target_location, enum cache_mode cache_mode,
149
enum task_type task_type, int redir)
151
struct task *task = NULL;
152
int referrer_incomplete = 0;
153
int malicious_uri = 0;
154
int confirm_submit = uri->form;
155
unsigned char *m1 = NULL, *message = NULL;
158
&& ses->doc_view->document
159
&& ses->doc_view->document->refresh) {
160
kill_document_refresh(ses->doc_view->document->refresh);
163
assertm(!ses->loading_uri, "Buggy URI reference counting");
165
/* Reset the redirect counter if this is not a redirect. */
167
ses->redirect_cnt = 0;
170
/* Figure out whether to confirm submit or not */
172
/* Only confirm submit if we are posting form data or a misleading URI
174
/* Note uri->post might be empty here but we are still supposely
175
* posting form data so this should be more correct. */
177
if (uri->user && uri->userlen
178
&& get_opt_bool("document.browse.links.warn_malicious")
179
&& check_malicious_uri(uri)) {
183
} else if (!uri->form) {
187
struct cache_entry *cached;
189
/* First check if the referring URI was incomplete. It
190
* indicates that the posted form data might be incomplete too.
193
cached = find_in_cache(ses->referrer);
194
referrer_incomplete = (cached && cached->incomplete);
197
if (!get_opt_bool("document.browse.forms.confirm_submit")
198
&& !referrer_incomplete) {
201
} else if (get_validated_cache_entry(uri, cache_mode)) {
206
if (!confirm_submit) {
207
ses->loading.callback = (void (*)(struct download *, void *)) loading_callback;
208
ses->loading.data = ses;
209
ses->loading_uri = get_uri_reference(uri);
211
ses->task.type = task_type;
212
ses->task.target_frame = target_frame;
213
ses->task.target_location = target_location;
215
load_uri(ses->loading_uri, ses->referrer, &ses->loading,
216
PRI_MAIN, cache_mode, -1);
221
task = mem_alloc(sizeof(*task));
225
task->uri = get_uri_reference(uri);
226
task->cache_mode = cache_mode;
227
task->type = task_type;
228
task->target_frame = target_frame;
229
task->target_location = target_location;
232
unsigned char *host = memacpy(uri->host, uri->hostlen);
233
unsigned char *user = memacpy(uri->user, uri->userlen);
234
unsigned char *uristring = get_uri_string(uri, URI_PUBLIC);
236
message = msg_text(ses->tab->term,
237
N_("The URL you are about to follow might be maliciously "
238
"crafted in order to confuse you. By following the URL "
239
"you will be connecting to host \"%s\" as user \"%s\".\n\n"
240
"Do you want to go to URL %s?"), host, user, uristring);
244
mem_free_if(uristring);
247
m1 = N_("Do you want to follow the redirect and post form data "
250
} else if (referrer_incomplete) {
251
m1 = N_("The form data you are about to post might be incomplete.\n"
252
"Do you want to post to URL %s?");
254
} else if (task_type == TASK_FORWARD) {
255
m1 = N_("Do you want to post form data to URL %s?");
258
m1 = N_("Do you want to repost form data to URL %s?");
261
if (!message && m1) {
262
unsigned char *uristring = get_uri_string(uri, URI_PUBLIC);
264
message = msg_text(ses->tab->term, m1, uristring);
265
mem_free_if(uristring);
268
msg_box(ses->tab->term, getml(task, NULL), MSGBOX_FREE_TEXT,
269
N_("Warning"), ALIGN_CENTER,
272
N_("~Yes"), post_yes, B_ENTER,
273
N_("~No"), post_no, B_ESC);
277
/* If @loaded_in_frame is set, this was called just to indicate a move inside a
278
* frameset, and we basically just reset the appropriate frame's view_state in
279
* that case. When clicking on a link inside a frame, the frame URI is somehow
280
* updated and added to the files-to-load queue, then ses_forward() is called
281
* with @loaded_in_frame unset, duplicating the whole frameset's location, then
282
* later the file-to-load callback calls it for the particular frame with
283
* @loaded_in_frame set. */
285
ses_forward(struct session *ses, int loaded_in_frame)
287
struct location *loc = NULL;
288
struct view_state *vs;
290
if (!loaded_in_frame) {
292
mem_free_set(&ses->search_word, NULL);
296
if (!loaded_in_frame) {
297
loc = mem_calloc(1, sizeof(*loc));
298
if (!loc) return NULL;
299
copy_struct(&loc->download, &ses->loading);
302
if (ses->task.target_frame && *ses->task.target_frame) {
305
assertm(have_location(ses), "no location yet");
306
if_assert_failed return NULL;
308
if (!loaded_in_frame) {
309
copy_location(loc, cur_loc(ses));
310
add_to_history(&ses->history, loc);
313
frame = ses_find_frame(ses, ses->task.target_frame);
315
if (!loaded_in_frame) {
316
del_from_history(&ses->history, loc);
317
destroy_location(loc);
319
ses->task.target_frame = NULL;
324
if (!loaded_in_frame) {
326
init_vs(vs, ses->loading_uri, vs->plain);
329
vs->uri = get_uri_reference(ses->loading_uri);
331
/* vs->doc_view itself will get detached in
332
* render_document_frames(), but that's too
334
vs->doc_view->vs = NULL;
337
#ifdef CONFIG_ECMASCRIPT
338
vs->ecmascript_fragile = 1;
344
if_assert_failed return NULL;
346
init_list(loc->frames);
348
init_vs(vs, ses->loading_uri, vs->plain);
349
add_to_history(&ses->history, loc);
352
ses->status.visited = 0;
354
/* This is another "branch" in the browsing, so throw away the current
355
* unhistory, we are venturing in another direction! */
356
if (ses->task.type == TASK_FORWARD)
357
clean_unhistory(&ses->history);
362
ses_imgmap(struct session *ses)
364
struct cache_entry *cached = find_in_cache(ses->loading_uri);
365
struct document_view *doc_view = current_frame(ses);
366
struct fragment *fragment;
367
struct memory_list *ml;
368
struct menu_item *menu;
371
INTERNAL("can't find cache entry");
375
fragment = get_cache_fragment(cached);
376
if (!fragment) return;
378
if (!doc_view || !doc_view->document) return;
379
global_doc_opts = &doc_view->document->options;
381
if (get_image_map(cached->head, fragment->data,
382
fragment->data + fragment->length,
383
&menu, &ml, ses->loading_uri, ses->task.target_frame,
384
get_opt_codepage_tree(ses->tab->term->spec, "charset"),
385
get_opt_codepage("document.codepage.assume"),
386
get_opt_bool("document.codepage.force_assumed")))
389
add_empty_window(ses->tab->term, (void (*)(void *)) freeml, ml);
390
do_menu(ses->tab->term, menu, ses, 0);
394
do_move(struct session *ses, struct download **download_p)
396
struct cache_entry *cached;
398
assert(download_p && *download_p);
399
assertm(ses->loading_uri, "no ses->loading_uri");
400
if_assert_failed return 0;
402
if (ses->loading_uri->protocol == PROTOCOL_UNKNOWN)
405
/* Handling image map needs to scan the source of the loaded document
406
* so all of it has to be available. */
407
if (ses->task.type == TASK_IMGMAP && is_in_progress_state((*download_p)->state))
410
cached = (*download_p)->cached;
411
if (!cached) return 0;
413
if (cached->redirect && ses->redirect_cnt++ < MAX_REDIRECTS) {
414
enum task_type task = ses->task.type;
416
if (task == TASK_HISTORY && !have_location(ses))
419
assertm(compare_uri(cached->uri, ses->loading_uri, URI_BASE),
420
"Redirecting using bad base URI");
422
if (cached->redirect->protocol == PROTOCOL_UNKNOWN)
425
abort_loading(ses, 0);
426
if (have_location(ses))
427
*download_p = &cur_loc(ses)->download;
431
set_session_referrer(ses, cached->uri);
438
protocol_external_handler *fn;
439
struct uri *uri = cached->redirect;
441
fn = get_protocol_external_handler(uri->protocol);
450
ses_goto(ses, cached->redirect, ses->task.target_frame, NULL,
451
CACHE_MODE_NORMAL, task, 1);
454
ses_goto(ses, cached->redirect, NULL, ses->task.target_location,
455
CACHE_MODE_NORMAL, TASK_RELOAD, 1);
458
ses_goto(ses, cached->redirect, NULL, NULL,
459
ses->reloadlevel, TASK_RELOAD, 1);
465
if (ses->display_timer != -1) {
466
kill_timer(ses->display_timer);
467
ses->display_timer = -1;
470
switch (ses->task.type) {
474
if (ses_chktype(ses, &ses->loading, cached, 0)) {
476
reload(ses, CACHE_MODE_NORMAL);
484
ses_history_move(ses);
487
ses->task.target_location = cur_loc(ses)->prev;
488
ses_history_move(ses);
493
if (is_in_progress_state((*download_p)->state)) {
494
if (have_location(ses))
495
*download_p = &cur_loc(ses)->download;
496
change_connection(&ses->loading, *download_p, PRI_MAIN, 0);
497
} else if (have_location(ses)) {
498
cur_loc(ses)->download.state = ses->loading.state;
506
loading_callback(struct download *download, struct session *ses)
510
assertm(ses->task.type, "loading_callback: no ses->task");
511
if_assert_failed return;
513
d = do_move(ses, &download);
514
if (!download) return;
515
if (d == 2) goto end;
518
download->callback = (void (*)(struct download *, void *)) doc_loading_callback;
522
if (is_in_result_state(download->state)) {
523
if (ses->task.type) free_task(ses);
524
if (d == 1) doc_loading_callback(download, ses);
527
if (is_in_result_state(download->state) && download->state != S_OK) {
528
struct uri *uri = download->conn ? download->conn->uri : NULL;
529
print_error_dialog(ses, download->state, uri, download->pri);
530
if (d == 0) reload(ses, CACHE_MODE_NORMAL);
534
check_questions_queue(ses);
535
print_screen_status(ses);
540
do_follow_url(struct session *ses, struct uri *uri, unsigned char *target,
541
enum task_type task, enum cache_mode cache_mode, int do_referrer)
543
struct uri *referrer = NULL;
544
protocol_external_handler *external_handler;
547
print_error_dialog(ses, S_BAD_URL, uri, PRI_CANCEL);
551
external_handler = get_protocol_external_handler(uri->protocol);
552
if (external_handler) {
553
external_handler(ses, uri);
557
if (target && !strcmp(target, "_blank")) {
558
int mode = get_opt_int("document.browse.links.target_blank");
561
struct session *new_ses;
563
new_ses = init_session(ses, ses->tab->term, uri, (mode == 2));
564
if (new_ses) ses = new_ses;
568
ses->reloadlevel = cache_mode;
570
if (ses->task.type == task) {
571
if (compare_uri(ses->loading_uri, uri, 0)) {
572
/* We're already loading the URL. */
577
abort_loading(ses, 0);
580
struct document_view *doc_view = current_frame(ses);
582
if (doc_view && doc_view->document)
583
referrer = doc_view->document->uri;
586
set_session_referrer(ses, referrer);
588
ses_goto(ses, uri, target, NULL, cache_mode, task, 0);
592
follow_url(struct session *ses, struct uri *uri, unsigned char *target,
593
enum task_type task, enum cache_mode cache_mode, int referrer)
595
#ifdef CONFIG_SCRIPTING
596
static int follow_url_event_id = EVENT_NONE;
597
unsigned char *uristring = uri ? get_uri_string(uri, URI_BASE | URI_FRAGMENT) : NULL;
600
do_follow_url(ses, uri, target, task, cache_mode, referrer);
604
set_event_id(follow_url_event_id, "follow-url");
605
trigger_event(follow_url_event_id, &uristring, ses);
607
if (!uristring || !*uristring) {
608
mem_free_if(uristring);
612
/* FIXME: Compare if uristring and struri(uri) are equal */
613
/* FIXME: When uri->post will no longer be an encoded string (but
614
* hopefully some refcounted object) we will have to assign the post
615
* data object to the translated URI. */
616
uri = get_translated_uri(uristring, ses->tab->term->cwd);
620
do_follow_url(ses, uri, target, task, cache_mode, referrer);
622
#ifdef CONFIG_SCRIPTING
623
if (uri) done_uri(uri);
628
goto_uri(struct session *ses, struct uri *uri)
630
follow_url(ses, uri, NULL, TASK_FORWARD, CACHE_MODE_NORMAL, 0);
634
goto_uri_frame(struct session *ses, struct uri *uri,
635
unsigned char *target, enum cache_mode cache_mode)
637
follow_url(ses, uri, target, TASK_FORWARD, cache_mode, 1);
642
map_selected(struct terminal *term, void *ld_, void *ses_)
644
struct link_def *ld = ld_;
645
struct session *ses = ses_;
646
struct uri *uri = get_uri(ld->link, 0);
648
goto_uri_frame(ses, uri, ld->target, CACHE_MODE_NORMAL);
649
if (uri) done_uri(uri);
654
goto_url(struct session *ses, unsigned char *url)
656
struct uri *uri = get_uri(url, 0);
659
if (uri) done_uri(uri);
663
get_hooked_uri(unsigned char *uristring, struct session *ses, unsigned char *cwd)
667
#if defined(CONFIG_SCRIPTING) || defined(CONFIG_URI_REWRITE)
668
static int goto_url_event_id = EVENT_NONE;
670
uristring = stracpy(uristring);
671
if (!uristring) return NULL;
673
set_event_id(goto_url_event_id, "goto-url");
674
trigger_event(goto_url_event_id, &uristring, ses);
675
if (!uristring) return NULL;
678
uri = *uristring ? get_translated_uri(uristring, cwd) : NULL;
680
#if defined(CONFIG_SCRIPTING) || defined(CONFIG_URI_REWRITE)
687
goto_url_with_hook(struct session *ses, unsigned char *url)
689
unsigned char *cwd = ses->tab->term->cwd;
692
/* Bail out if passed empty string from goto-url dialog */
695
uri = get_hooked_uri(url, ses, cwd);
697
if (uri) done_uri(uri);
701
goto_url_home(struct session *ses)
703
unsigned char *homepage = get_opt_str("ui.sessions.homepage");
705
if (!*homepage) homepage = getenv("WWW_HOME");
706
if (!homepage || !*homepage) homepage = WWW_HOME_URL;
708
if (!homepage || !*homepage) return 0;
710
goto_url_with_hook(ses, homepage);
714
/* TODO: Should there be goto_imgmap_reload() ? */
717
goto_imgmap(struct session *ses, struct uri *uri, unsigned char *target)
719
follow_url(ses, uri, target, TASK_IMGMAP, CACHE_MODE_NORMAL, 1);