Retry only for https protocol
[elinks.git] / src / session / session.c
blob6dd9764bec99c0d151a8b8175328b9154336b915
1 /** Sessions managment - you'll find things here which you wouldn't expect
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
12 #include "elinks.h"
14 #include "bfu/dialog.h"
15 #include "bookmarks/bookmarks.h"
16 #include "cache/cache.h"
17 #include "config/home.h"
18 #include "config/options.h"
19 #include "dialogs/menu.h"
20 #include "dialogs/status.h"
21 #include "document/document.h"
22 #include "document/html/frames.h"
23 #include "document/refresh.h"
24 #include "document/renderer.h"
25 #include "document/view.h"
26 #include "globhist/globhist.h"
27 #include "intl/gettext/libintl.h"
28 #include "main/event.h"
29 #include "main/object.h"
30 #include "main/timer.h"
31 #include "network/connection.h"
32 #include "network/state.h"
33 #include "osdep/newwin.h"
34 #include "protocol/http/blacklist.h"
35 #include "protocol/protocol.h"
36 #include "protocol/uri.h"
37 #ifdef CONFIG_SCRIPTING_SPIDERMONKEY
38 # include "scripting/smjs/smjs.h"
39 #endif
40 #include "session/download.h"
41 #include "session/history.h"
42 #include "session/location.h"
43 #include "session/session.h"
44 #include "session/task.h"
45 #include "terminal/tab.h"
46 #include "terminal/terminal.h"
47 #include "terminal/window.h"
48 #include "util/conv.h"
49 #include "util/error.h"
50 #include "util/memlist.h"
51 #include "util/memory.h"
52 #include "util/string.h"
53 #include "util/time.h"
54 #include "viewer/text/draw.h"
55 #include "viewer/text/form.h"
56 #include "viewer/text/link.h"
57 #include "viewer/text/view.h"
60 struct file_to_load {
61 LIST_HEAD(struct file_to_load);
63 struct session *ses;
64 unsigned int req_sent:1;
65 int pri;
66 struct cache_entry *cached;
67 unsigned char *target_frame;
68 struct uri *uri;
69 struct download download;
72 /** This structure and related functions are used to maintain information
73 * for instances opened in new windows. We store all related session info like
74 * URI and base session to clone from so that when the new instance connects
75 * we can look up this information. In case of failure the session information
76 * has a timeout */
77 struct session_info {
78 LIST_HEAD(struct session_info);
80 int id;
81 timer_id_T timer;
82 struct session *ses;
83 struct uri *uri;
84 struct uri *referrer;
85 enum task_type task;
86 enum cache_mode cache_mode;
89 #define file_to_load_is_active(ftl) ((ftl)->req_sent && is_in_progress_state((ftl)->download.state))
92 INIT_LIST_OF(struct session, sessions);
94 enum remote_session_flags remote_session_flags;
97 static struct file_to_load *request_additional_file(struct session *,
98 unsigned char *,
99 struct uri *, int);
101 static window_handler_T tabwin_func;
104 static INIT_LIST_OF(struct session_info, session_info);
105 static int session_info_id = 1;
107 static struct session_info *
108 get_session_info(int id)
110 struct session_info *info;
112 foreach (info, session_info) {
113 struct session *ses;
115 if (info->id != id) continue;
117 /* Make sure the info->ses session is still with us. */
118 foreach (ses, sessions)
119 if (ses == info->ses)
120 return info;
122 info->ses = NULL;
123 return info;
126 return NULL;
129 static void
130 done_session_info(struct session_info *info)
132 del_from_list(info);
133 kill_timer(&info->timer);
135 if (info->uri) done_uri(info->uri);
136 if (info->referrer) done_uri(info->referrer);
137 mem_free(info);
140 void
141 done_saved_session_info(void)
143 while (!list_empty(session_info))
144 done_session_info(session_info.next);
147 /** Timer callback for session_info.timer. As explained in install_timer(),
148 * this function must erase the expired timer ID from all variables. */
149 static void
150 session_info_timeout(int id)
152 struct session_info *info = get_session_info(id);
154 if (!info) return;
155 info->timer = TIMER_ID_UNDEF;
156 /* The expired timer ID has now been erased. */
157 done_session_info(info);
161 add_session_info(struct session *ses, struct uri *uri, struct uri *referrer,
162 enum cache_mode cache_mode, enum task_type task)
164 struct session_info *info = mem_calloc(1, sizeof(*info));
166 if (!info) return -1;
168 info->id = session_info_id++;
169 /* I don't know what a reasonable start up time for a new instance is
170 * but it won't hurt to have a few seconds atleast. --jonas */
171 install_timer(&info->timer, (milliseconds_T) 10000,
172 (void (*)(void *)) session_info_timeout,
173 (void *) (long) info->id);
175 info->ses = ses;
176 info->task = task;
177 info->cache_mode = cache_mode;
179 if (uri) info->uri = get_uri_reference(uri);
180 if (referrer) info->referrer = get_uri_reference(referrer);
182 add_to_list(session_info, info);
184 return info->id;
187 static struct session *
188 init_saved_session(struct terminal *term, int id)
190 struct session_info *info = get_session_info(id);
191 struct session *ses;
193 if (!info) return NULL;
195 ses = init_session(info->ses, term, info->uri, 0);
197 if (!ses) {
198 done_session_info(info);
199 return ses;
202 /* Only do the ses_goto()-thing for target=_blank. */
203 if (info->uri && info->task != TASK_NONE) {
204 /* The init_session() call would have started the download but
205 * not with the requested cache mode etc. so interrupt that
206 * download . */
207 abort_loading(ses, 1);
209 ses->reloadlevel = info->cache_mode;
210 set_session_referrer(ses, info->referrer);
211 ses_goto(ses, info->uri, NULL, NULL, info->cache_mode,
212 info->task, 0);
215 done_session_info(info);
217 return ses;
220 static struct session *
221 get_master_session(void)
223 struct session *ses;
225 foreach (ses, sessions)
226 if (ses->tab->term->master) {
227 struct window *current_tab = get_current_tab(ses->tab->term);
229 return current_tab ? current_tab->data : NULL;
232 return NULL;
235 /** @relates session */
236 struct download *
237 get_current_download(struct session *ses)
239 struct download *download = NULL;
241 if (!ses) return NULL;
243 if (ses->task.type)
244 download = &ses->loading;
245 else if (have_location(ses))
246 download = &cur_loc(ses)->download;
248 if (download && is_in_state(download->state, S_OK)) {
249 struct file_to_load *ftl;
251 foreach (ftl, ses->more_files)
252 if (file_to_load_is_active(ftl))
253 return &ftl->download;
256 /* Note that @download isn't necessarily NULL here,
257 * if @ses->more_files is empty. -- Miciah */
258 return download;
261 static void
262 done_retry_connection_without_verification(void *data)
264 struct delayed_open *deo = (struct delayed_open *)data;
266 if (deo) {
267 done_uri(deo->uri);
268 mem_free(deo);
272 static void
273 retry_connection_without_verification(void *data)
275 struct delayed_open *deo = (struct delayed_open *)data;
277 if (deo) {
278 if (deo->uri->hostlen) {
279 add_blacklist_entry(deo->uri, SERVER_BLACKLIST_NO_CERT_VERIFY);
281 goto_uri(deo->ses, deo->uri);
282 done_uri(deo->uri);
283 mem_free(deo);
287 void
288 print_error_dialog(struct session *ses, struct connection_state state,
289 struct uri *uri, enum connection_priority priority)
291 struct string msg;
292 unsigned char *uristring;
294 /* Don't show error dialogs for missing CSS stylesheets */
295 if (priority == PRI_CSS
296 || !init_string(&msg))
297 return;
299 uristring = uri ? get_uri_string(uri, URI_PUBLIC) : NULL;
300 if (uristring) {
301 #ifdef CONFIG_UTF8
302 if (ses->tab->term->utf8_cp)
303 decode_uri(uristring);
304 else
305 #endif /* CONFIG_UTF8 */
306 decode_uri_for_display(uristring);
307 add_format_to_string(&msg,
308 _("Unable to retrieve %s", ses->tab->term),
309 uristring);
310 mem_free(uristring);
311 add_to_string(&msg, ":\n\n");
314 add_to_string(&msg, get_state_message(state, ses->tab->term));
316 if (!uri || uri->protocol != PROTOCOL_HTTPS) {
317 info_box(ses->tab->term, MSGBOX_FREE_TEXT,
318 N_("Error"), ALIGN_CENTER,
319 msg.source);
320 } else {
321 struct delayed_open *deo = mem_calloc(1, sizeof(*deo));
323 if (!deo) return;
325 add_to_string(&msg, "\n\n");
326 add_to_string(&msg, N_("Retry without verification?"));
327 deo->ses = ses;
328 deo->uri = get_uri_reference(uri);
330 msg_box(ses->tab->term, NULL, MSGBOX_FREE_TEXT,
331 N_("Error"), ALIGN_CENTER,
332 msg.source,
333 deo, 2,
334 MSG_BOX_BUTTON(N_("~Yes"), retry_connection_without_verification, B_ENTER),
335 MSG_BOX_BUTTON(N_("~No"), done_retry_connection_without_verification, B_ESC));
338 /* TODO: retry */
341 static void
342 abort_files_load(struct session *ses, int interrupt)
344 while (1) {
345 struct file_to_load *ftl;
346 int more = 0;
348 foreach (ftl, ses->more_files) {
349 if (!file_to_load_is_active(ftl))
350 continue;
352 more = 1;
353 cancel_download(&ftl->download, interrupt);
356 if (!more) break;
360 void
361 free_files(struct session *ses)
363 struct file_to_load *ftl;
365 abort_files_load(ses, 0);
366 foreach (ftl, ses->more_files) {
367 if (ftl->cached) object_unlock(ftl->cached);
368 if (ftl->uri) done_uri(ftl->uri);
369 mem_free_if(ftl->target_frame);
371 free_list(ses->more_files);
377 static void request_frameset(struct session *, struct frameset_desc *, int);
379 static void
380 request_frame(struct session *ses, unsigned char *name,
381 struct uri *uri, int depth)
383 struct location *loc = cur_loc(ses);
384 struct frame *frame;
386 assertm(have_location(ses), "request_frame: no location");
387 if_assert_failed return;
389 foreach (frame, loc->frames) {
390 struct document_view *doc_view;
392 if (c_strcasecmp(frame->name, name))
393 continue;
395 foreach (doc_view, ses->scrn_frames) {
396 if (doc_view->vs == &frame->vs && doc_view->document->frame_desc) {
397 request_frameset(ses, doc_view->document->frame_desc, depth);
398 return;
402 request_additional_file(ses, name, frame->vs.uri, PRI_FRAME);
403 return;
406 frame = mem_calloc(1, sizeof(*frame));
407 if (!frame) return;
409 frame->name = stracpy(name);
410 if (!frame->name) {
411 mem_free(frame);
412 return;
415 init_vs(&frame->vs, uri, -1);
417 add_to_list(loc->frames, frame);
419 request_additional_file(ses, name, frame->vs.uri, PRI_FRAME);
422 static void
423 request_frameset(struct session *ses, struct frameset_desc *frameset_desc, int depth)
425 int i;
427 if (depth > HTML_MAX_FRAME_DEPTH) return;
429 depth++; /* Inheritation counter (recursion brake ;) */
431 for (i = 0; i < frameset_desc->n; i++) {
432 struct frame_desc *frame_desc = &frameset_desc->frame_desc[i];
434 if (frame_desc->subframe) {
435 request_frameset(ses, frame_desc->subframe, depth);
436 } else if (frame_desc->name && frame_desc->uri) {
437 request_frame(ses, frame_desc->name,
438 frame_desc->uri, depth);
443 #ifdef CONFIG_CSS
444 static inline void
445 load_css_imports(struct session *ses, struct document_view *doc_view)
447 struct document *document = doc_view->document;
448 struct uri *uri;
449 int index;
451 if (!document) return;
453 foreach_uri (uri, index, &document->css_imports) {
454 request_additional_file(ses, "", uri, PRI_CSS);
457 #else
458 #define load_css_imports(ses, doc_view)
459 #endif
461 #ifdef CONFIG_ECMASCRIPT
462 static inline void
463 load_ecmascript_imports(struct session *ses, struct document_view *doc_view)
465 struct document *document = doc_view->document;
466 struct uri *uri;
467 int index;
469 if (!document) return;
471 foreach_uri (uri, index, &document->ecmascript_imports) {
472 request_additional_file(ses, "", uri, /* XXX */ PRI_CSS);
475 #else
476 #define load_ecmascript_imports(ses, doc_view)
477 #endif
479 NONSTATIC_INLINE void
480 load_frames(struct session *ses, struct document_view *doc_view)
482 struct document *document = doc_view->document;
484 if (!document || !document->frame_desc) return;
485 request_frameset(ses, document->frame_desc, 0);
487 foreach (doc_view, ses->scrn_frames) {
488 load_css_imports(ses, doc_view);
489 load_ecmascript_imports(ses, doc_view);
493 /** Timer callback for session.display_timer. As explained in install_timer(),
494 * this function must erase the expired timer ID from all variables. */
495 void
496 display_timer(struct session *ses)
498 timeval_T start, stop, duration;
499 milliseconds_T t;
501 timeval_now(&start);
502 draw_formatted(ses, 3);
503 timeval_now(&stop);
504 timeval_sub(&duration, &start, &stop);
506 t = mult_ms(timeval_to_milliseconds(&duration), DISPLAY_TIME);
507 if (t < DISPLAY_TIME_MIN) t = DISPLAY_TIME_MIN;
508 install_timer(&ses->display_timer, t,
509 (void (*)(void *)) display_timer,
510 ses);
511 /* The expired timer ID has now been erased. */
513 load_frames(ses, ses->doc_view);
514 load_css_imports(ses, ses->doc_view);
515 load_ecmascript_imports(ses, ses->doc_view);
516 process_file_requests(ses);
520 struct questions_entry {
521 LIST_HEAD(struct questions_entry);
523 void (*callback)(struct session *, void *);
524 void *data;
527 INIT_LIST_OF(struct questions_entry, questions_queue);
530 void
531 check_questions_queue(struct session *ses)
533 while (!list_empty(questions_queue)) {
534 struct questions_entry *q = questions_queue.next;
536 q->callback(ses, q->data);
537 del_from_list(q);
538 mem_free(q);
542 void
543 add_questions_entry(void (*callback)(struct session *, void *), void *data)
545 struct questions_entry *q = mem_alloc(sizeof(*q));
547 if (!q) return;
549 q->callback = callback;
550 q->data = data;
551 add_to_list(questions_queue, q);
554 #ifdef CONFIG_SCRIPTING
555 static void
556 maybe_pre_format_html(struct cache_entry *cached, struct session *ses)
558 struct fragment *fragment;
559 static int pre_format_html_event = EVENT_NONE;
561 if (!cached || cached->preformatted)
562 return;
564 /* The script called from here may indirectly call
565 * garbage_collection(). If the refcount of the cache entry
566 * were 0, it could then be freed, and the
567 * cached->preformatted assignment at the end of this function
568 * would crash. Normally, the document has a reference to the
569 * cache entry, and that suffices. However, if the cache
570 * entry was loaded to satisfy e.g. USEMAP="imgmap.html#map",
571 * then cached->object.refcount == 0 here, and must be
572 * incremented.
574 * cached->object.refcount == 0 is safe while the cache entry
575 * is being loaded, because garbage_collection() calls
576 * is_entry_used(), which checks whether any connection is
577 * using the cache entry. But loading has ended before this
578 * point. */
579 object_lock(cached);
581 fragment = get_cache_fragment(cached);
582 if (!fragment) goto unlock_and_return;
584 /* We cannot do anything if the data are fragmented. */
585 if (!list_is_singleton(cached->frag)) goto unlock_and_return;
587 set_event_id(pre_format_html_event, "pre-format-html");
588 trigger_event(pre_format_html_event, ses, cached);
590 /* XXX: Keep this after the trigger_event, because hooks might call
591 * normalize_cache_entry()! */
592 cached->preformatted = 1;
594 unlock_and_return:
595 object_unlock(cached);
597 #endif
599 static int
600 check_incomplete_redirects(struct cache_entry *cached)
602 assert(cached);
604 cached = follow_cached_redirects(cached);
605 if (cached && !cached->redirect) {
606 /* XXX: This is not quite true, but does that difference
607 * matter here? */
608 return cached->incomplete;
611 return 0;
615 session_is_loading(struct session *ses)
617 struct download *download = get_current_download(ses);
619 if (!download) return 0;
621 if (!is_in_result_state(download->state))
622 return 1;
624 /* The validness of download->cached (especially the download struct in
625 * ses->loading) is hard to maintain so check before using it.
626 * Related to bug 559. */
627 if (download->cached
628 && cache_entry_is_valid(download->cached)
629 && check_incomplete_redirects(download->cached))
630 return 1;
632 return 0;
635 void
636 doc_loading_callback(struct download *download, struct session *ses)
638 int submit = 0;
640 if (is_in_result_state(download->state)) {
641 #ifdef CONFIG_SCRIPTING
642 maybe_pre_format_html(download->cached, ses);
643 #endif
644 kill_timer(&ses->display_timer);
646 draw_formatted(ses, 1);
648 if (get_cmd_opt_bool("auto-submit")) {
649 if (!list_empty(ses->doc_view->document->forms)) {
650 get_cmd_opt_bool("auto-submit") = 0;
651 submit = 1;
655 load_frames(ses, ses->doc_view);
656 load_css_imports(ses, ses->doc_view);
657 load_ecmascript_imports(ses, ses->doc_view);
658 process_file_requests(ses);
660 start_document_refreshes(ses);
662 if (!is_in_state(download->state, S_OK)) {
663 print_error_dialog(ses, download->state,
664 ses->doc_view->document->uri,
665 download->pri);
668 } else if (is_in_transfering_state(download->state)
669 && ses->display_timer == TIMER_ID_UNDEF) {
670 display_timer(ses);
673 check_questions_queue(ses);
674 print_screen_status(ses);
676 #ifdef CONFIG_GLOBHIST
677 if (download->pri != PRI_CSS) {
678 unsigned char *title = ses->doc_view->document->title;
679 struct uri *uri;
681 if (download->conn)
682 uri = download->conn->proxied_uri;
683 else if (download->cached)
684 uri = download->cached->uri;
685 else
686 uri = NULL;
688 if (uri)
689 add_global_history_item(struri(uri), title, time(NULL));
691 #endif
693 if (submit) auto_submit_form(ses);
696 static void
697 file_loading_callback(struct download *download, struct file_to_load *ftl)
699 if (ftl->download.cached && ftl->cached != ftl->download.cached) {
700 if (ftl->cached) object_unlock(ftl->cached);
701 ftl->cached = ftl->download.cached;
702 object_lock(ftl->cached);
705 /* FIXME: We need to do content-type check here! However, we won't
706 * handle properly the "Choose action" dialog now :(. */
707 if (ftl->cached && !ftl->cached->redirect_get && download->pri != PRI_CSS) {
708 struct session *ses = ftl->ses;
709 struct uri *loading_uri = ses->loading_uri;
710 unsigned char *target_frame = null_or_stracpy(ses->task.target.frame);
712 ses->loading_uri = ftl->uri;
713 mem_free_set(&ses->task.target.frame, null_or_stracpy(ftl->target_frame));
714 setup_download_handler(ses, &ftl->download, ftl->cached, 1);
715 ses->loading_uri = loading_uri;
716 mem_free_set(&ses->task.target.frame, target_frame);
719 doc_loading_callback(download, ftl->ses);
722 static struct file_to_load *
723 request_additional_file(struct session *ses, unsigned char *name, struct uri *uri, int pri)
725 struct file_to_load *ftl;
727 if (uri->protocol == PROTOCOL_UNKNOWN) {
728 return NULL;
731 /* XXX: We cannot run the external handler here, because
732 * request_additional_file() is called many times for a single URL
733 * (normally the foreach() right below catches them all). Anyway,
734 * having <frame src="mailto:foo"> would be just weird, wouldn't it?
735 * --pasky */
736 if (get_protocol_external_handler(ses->tab->term, uri)) {
737 return NULL;
740 foreach (ftl, ses->more_files) {
741 if (compare_uri(ftl->uri, uri, URI_BASE)) {
742 if (ftl->pri > pri) {
743 ftl->pri = pri;
744 move_download(&ftl->download, &ftl->download, pri);
746 return NULL;
750 ftl = mem_calloc(1, sizeof(*ftl));
751 if (!ftl) return NULL;
753 ftl->uri = get_uri_reference(uri);
754 ftl->target_frame = stracpy(name);
755 ftl->download.callback = (download_callback_T *) file_loading_callback;
756 ftl->download.data = ftl;
757 ftl->pri = pri;
758 ftl->ses = ses;
760 add_to_list(ses->more_files, ftl);
762 return ftl;
765 static void
766 load_additional_file(struct file_to_load *ftl, enum cache_mode cache_mode)
768 struct document_view *doc_view = current_frame(ftl->ses);
769 struct uri *referrer = doc_view && doc_view->document
770 ? doc_view->document->uri : NULL;
772 load_uri(ftl->uri, referrer, &ftl->download, ftl->pri, cache_mode, -1);
775 void
776 process_file_requests(struct session *ses)
778 if (ses->status.processing_file_requests) return;
779 ses->status.processing_file_requests = 1;
781 while (1) {
782 struct file_to_load *ftl;
783 int more = 0;
785 foreach (ftl, ses->more_files) {
786 if (ftl->req_sent)
787 continue;
789 ftl->req_sent = 1;
791 load_additional_file(ftl, CACHE_MODE_NORMAL);
792 more = 1;
795 if (!more) break;
798 ses->status.processing_file_requests = 0;
802 static void
803 dialog_goto_url_open(void *data)
805 dialog_goto_url((struct session *) data, NULL);
808 /** @returns 0 if the first session was not properly initialized and
809 * setup_session() should be called on the session as well. */
810 static int
811 setup_first_session(struct session *ses, struct uri *uri)
813 /* [gettext_accelerator_context(setup_first_session)] */
814 struct terminal *term = ses->tab->term;
816 if (!*get_opt_str("protocol.http.user_agent", NULL)) {
817 info_box(term, 0,
818 N_("Warning"), ALIGN_CENTER,
819 N_("You have an empty string in protocol.http.user_agent - "
820 "this was a default value in the past, substituted by "
821 "default ELinks User-Agent string. However, currently "
822 "this means that NO User-Agent HEADER "
823 "WILL BE SENT AT ALL - if this is really what you want, "
824 "set its value to \" \", otherwise please delete the line "
825 "with this setting from your configuration file (if you "
826 "have no idea what I'm talking about, just do this), so "
827 "that the correct default setting will be used. Apologies for "
828 "any inconvience caused."));
831 if (!get_opt_bool("config.saving_style_w", NULL)) {
832 struct option *opt = get_opt_rec(config_options, "config.saving_style_w");
833 opt->value.number = 1;
834 option_changed(ses, opt);
835 if (get_opt_int("config.saving_style", NULL) != 3) {
836 info_box(term, 0,
837 N_("Warning"), ALIGN_CENTER,
838 N_("You have option config.saving_style set to "
839 "a de facto obsolete value. The configuration "
840 "saving algorithms of ELinks were changed from "
841 "the last time you upgraded ELinks. Now, only "
842 "those options which you actually changed are "
843 "saved to the configuration file, instead of "
844 "all the options. This simplifies our "
845 "situation greatly when we see that some option "
846 "has an inappropriate default value or we need to "
847 "change the semantics of some option in a subtle way. "
848 "Thus, we recommend you change the value of "
849 "config.saving_style option to 3 in order to get "
850 "the \"right\" behaviour. Apologies for any "
851 "inconvience caused."));
855 if (first_use) {
856 /* Only open the goto URL dialog if no URI was passed on the
857 * command line. */
858 void *handler = uri ? NULL : dialog_goto_url_open;
860 first_use = 0;
862 msg_box(term, NULL, 0,
863 N_("Welcome"), ALIGN_CENTER,
864 N_("Welcome to ELinks!\n\n"
865 "Press ESC for menu. Documentation is available in "
866 "Help menu."),
867 ses, 1,
868 MSG_BOX_BUTTON(N_("~OK"), handler, B_ENTER | B_ESC));
870 /* If there is no URI the goto dialog will pop up so there is
871 * no need to call setup_session(). */
872 if (!uri) return 1;
874 #ifdef CONFIG_BOOKMARKS
875 } else if (!uri && get_opt_bool("ui.sessions.auto_restore", NULL)) {
876 unsigned char *folder; /* UTF-8 */
878 folder = get_auto_save_bookmark_foldername_utf8();
879 if (folder) {
880 open_bookmark_folder(ses, folder);
881 mem_free(folder);
883 return 1;
884 #endif
887 /* If there's a URI to load we have to call */
888 return 0;
891 /** First load the current URI of the base session. In most cases it will just
892 * be fetched from the cache so that the new tab will not appear ``empty' while
893 * loading the real URI or showing the goto URL dialog. */
894 static void
895 setup_session(struct session *ses, struct uri *uri, struct session *base)
897 if (base && have_location(base)) {
898 ses_load(ses, get_uri_reference(cur_loc(base)->vs.uri),
899 NULL, NULL, CACHE_MODE_ALWAYS, TASK_FORWARD);
900 if (ses->doc_view && ses->doc_view->vs
901 && base->doc_view && base->doc_view->vs) {
902 struct view_state *vs = ses->doc_view->vs;
904 destroy_vs(vs, 1);
905 copy_vs(vs, base->doc_view->vs);
907 ses->doc_view->vs = vs;
911 if (uri) {
912 goto_uri(ses, uri);
914 } else if (!goto_url_home(ses)) {
915 if (get_opt_bool("ui.startup_goto_dialog", NULL)) {
916 dialog_goto_url_open(ses);
921 /** @relates session */
922 struct session *
923 init_session(struct session *base_session, struct terminal *term,
924 struct uri *uri, int in_background)
926 struct session *ses = mem_calloc(1, sizeof(*ses));
928 if (!ses) return NULL;
930 ses->tab = init_tab(term, ses, tabwin_func);
931 if (!ses->tab) {
932 mem_free(ses);
933 return NULL;
936 ses->option = copy_option(config_options,
937 CO_SHALLOW | CO_NO_LISTBOX_ITEM);
938 create_history(&ses->history);
939 init_list(ses->scrn_frames);
940 init_list(ses->more_files);
941 init_list(ses->type_queries);
942 ses->task.type = TASK_NONE;
943 ses->display_timer = TIMER_ID_UNDEF;
945 #ifdef CONFIG_LEDS
946 init_led_panel(&ses->status.leds);
947 ses->status.ssl_led = register_led(ses, 0);
948 ses->status.insert_mode_led = register_led(ses, 1);
949 ses->status.ecmascript_led = register_led(ses, 2);
950 ses->status.popup_led = register_led(ses, 3);
952 ses->status.download_led = register_led(ses, 5);
953 #endif
954 ses->status.force_show_status_bar = -1;
955 ses->status.force_show_title_bar = -1;
957 add_to_list(sessions, ses);
959 /* Update the status -- most importantly the info about whether to the
960 * show the title, tab and status bar -- _before_ loading the URI so
961 * the document cache is not filled with useless documents if the
962 * content is already cached.
964 * For big document it also reduces memory usage quite a bit because
965 * (atleast that is my interpretation --jonas) the old document will
966 * have a chance to be released before rendering a new one. A few
967 * numbers when opening a new tab while viewing debians package list
968 * for unstable:
970 * 9307 jonas 15 0 34252 30m 5088 S 0.0 12.2 0:03.63 elinks-old
971 * 9305 jonas 15 0 17060 13m 5088 S 0.0 5.5 0:02.07 elinks-new
973 update_status();
975 /* Check if the newly inserted session is the only in the list and do
976 * the special setup for the first session, */
977 if (!list_is_singleton(sessions) || !setup_first_session(ses, uri)) {
978 setup_session(ses, uri, base_session);
981 if (!in_background)
982 switch_to_tab(term, get_tab_number(ses->tab), -1);
984 if (!term->main_menu) activate_bfu_technology(ses, -1);
985 return ses;
988 static void
989 init_remote_session(struct session *ses, enum remote_session_flags *remote_ptr,
990 struct uri *uri)
992 enum remote_session_flags remote = *remote_ptr;
994 if (remote & SES_REMOTE_CURRENT_TAB) {
995 goto_uri(ses, uri);
996 /* Mask out the current tab flag */
997 *remote_ptr = remote & ~SES_REMOTE_CURRENT_TAB;
999 /* Remote session was masked out. Open all following URIs in
1000 * new tabs, */
1001 if (!*remote_ptr)
1002 *remote_ptr = SES_REMOTE_NEW_TAB;
1004 } else if (remote & SES_REMOTE_NEW_TAB) {
1005 /* FIXME: This is not perfect. Doing multiple -remote
1006 * with no URLs will make the goto dialogs
1007 * inaccessible. Maybe we should not support this kind
1008 * of thing or make the window focus detecting code
1009 * more intelligent. --jonas */
1010 open_uri_in_new_tab(ses, uri, 0, 0);
1012 if (remote & SES_REMOTE_PROMPT_URL) {
1013 dialog_goto_url_open(ses);
1016 } else if (remote & SES_REMOTE_NEW_WINDOW) {
1017 /* FIXME: It is quite rude because we just take the first
1018 * possibility and should maybe make it possible to specify
1019 * new-screen etc via -remote "openURL(..., new-*)" --jonas */
1020 if (!can_open_in_new(ses->tab->term))
1021 return;
1023 open_uri_in_new_window(ses, uri, NULL,
1024 ses->tab->term->environment,
1025 CACHE_MODE_NORMAL, TASK_NONE);
1027 } else if (remote & SES_REMOTE_ADD_BOOKMARK) {
1028 #ifdef CONFIG_BOOKMARKS
1029 int uri_cp;
1031 if (!uri) return;
1032 /** @todo Bug 1066: What is the encoding of struri()?
1033 * This code currently assumes the system charset.
1034 * It might be best to keep URIs in plain ASCII and
1035 * then have a function that reversibly converts them
1036 * to IRIs for display in a given encoding. */
1037 uri_cp = get_cp_index("System");
1038 add_bookmark_cp(NULL, 1, uri_cp, struri(uri), struri(uri));
1039 #endif
1041 } else if (remote & SES_REMOTE_INFO_BOX) {
1042 unsigned char *text;
1044 if (!uri) return;
1046 text = memacpy(uri->data, uri->datalen);
1047 if (!text) return;
1049 info_box(ses->tab->term, MSGBOX_FREE_TEXT,
1050 N_("Error"), ALIGN_CENTER,
1051 text);
1053 } else if (remote & SES_REMOTE_PROMPT_URL) {
1054 dialog_goto_url_open(ses);
1056 } else if (remote & SES_REMOTE_RELOAD) {
1057 reload(ses, CACHE_MODE_FORCE_RELOAD);
1063 struct string *
1064 encode_session_info(struct string *info,
1065 LIST_OF(struct string_list_item) *url_list)
1067 struct string_list_item *url;
1069 if (!init_string(info)) return NULL;
1071 foreach (url, *url_list) {
1072 struct string *str = &url->string;
1074 add_bytes_to_string(info, str->source, str->length + 1);
1077 return info;
1080 /** Older elinks versions (up to and including 0.9.1) sends no magic variable and if
1081 * this is detected we fallback to the old session info format. For this format
1082 * the magic member of terminal_info hold the length of the URI string. The
1083 * old format is handled by the default label in the switch.
1085 * The new session info format supports extraction of multiple URIS from the
1086 * terminal_info data member. The magic variable controls how to interpret
1087 * the fields:
1089 * - INTERLINK_NORMAL_MAGIC means use the terminal_info session_info
1090 * variable as an id for a saved session.
1092 * - INTERLINK_REMOTE_MAGIC means use the terminal_info session_info
1093 * variable as the remote session flags. */
1095 decode_session_info(struct terminal *term, struct terminal_info *info)
1097 int len = info->length;
1098 struct session *base_session = NULL;
1099 enum remote_session_flags remote = 0;
1100 unsigned char *str;
1102 switch (info->magic) {
1103 case INTERLINK_NORMAL_MAGIC:
1104 /* Lookup if there are any saved sessions that should be
1105 * resumed using the session_info as an id. The id is derived
1106 * from the value of -base-session command line option in the
1107 * connecting instance.
1109 * At the moment it is only used when opening instances in new
1110 * window to figure out how to initialize it when the new
1111 * instance connects to the master.
1113 * We don't need to handle it for the old format since new
1114 * instances connecting this way will always be of the same
1115 * origin as the master. */
1116 if (init_saved_session(term, info->session_info))
1117 return 1;
1118 break;
1120 case INTERLINK_REMOTE_MAGIC:
1121 /* This could mean some fatal bug but I am unsure if we can
1122 * actually assert it since people could pour all kinds of crap
1123 * down the socket. */
1124 if (!info->session_info) {
1125 INTERNAL("Remote magic with no remote flags");
1126 return 0;
1129 remote = info->session_info;
1131 /* If processing session info from a -remote instance we want
1132 * to hook up with the master so we can handle request for
1133 * stuff in current tab. */
1134 base_session = get_master_session();
1135 if (!base_session) return 0;
1137 break;
1139 default:
1140 /* The old format calculates the session_info and magic members
1141 * as part of the data that should be decoded so we have to
1142 * substract it to get the size of the actual URI data. */
1143 len -= sizeof(info->session_info) + sizeof(info->magic);
1145 /* Extract URI containing @magic bytes */
1146 if (info->magic <= 0 || info->magic > len)
1147 len = 0;
1148 else
1149 len = info->magic;
1152 if (len <= 0) {
1153 if (!remote)
1154 return !!init_session(base_session, term, NULL, 0);
1156 /* Even though there are no URIs we still have to
1157 * handle remote stuff. */
1158 init_remote_session(base_session, &remote, NULL);
1159 return 0;
1162 str = info->data;
1164 /* Extract multiple (possible) NUL terminated URIs */
1165 while (len > 0) {
1166 unsigned char *end = memchr(str, 0, len);
1167 int urilength = end ? end - str : len;
1168 struct uri *uri = NULL;
1169 unsigned char *uristring = memacpy(str, urilength);
1171 if (uristring) {
1172 uri = get_hooked_uri(uristring, base_session, term->cwd);
1173 mem_free(uristring);
1176 len -= urilength + 1;
1177 str += urilength + 1;
1179 if (remote) {
1180 if (!uri) continue;
1182 init_remote_session(base_session, &remote, uri);
1184 } else {
1185 int backgrounded = !list_empty(term->windows);
1186 int bad_url = !uri;
1187 struct session *ses;
1189 if (!uri)
1190 uri = get_uri("about:blank", 0);
1192 ses = init_session(base_session, term, uri, backgrounded);
1193 if (!ses) {
1194 /* End loop if initialization fails */
1195 len = 0;
1196 } else if (bad_url) {
1197 print_error_dialog(ses, connection_state(S_BAD_URL),
1198 NULL, PRI_MAIN);
1203 if (uri) done_uri(uri);
1206 /* If we only initialized remote sessions or didn't manage to add any
1207 * new tabs return zero so the terminal will be destroyed ASAP. */
1208 return remote ? 0 : !list_empty(term->windows);
1212 void
1213 abort_loading(struct session *ses, int interrupt)
1215 if (have_location(ses)) {
1216 struct location *loc = cur_loc(ses);
1218 cancel_download(&loc->download, interrupt);
1219 abort_files_load(ses, interrupt);
1221 abort_preloading(ses, interrupt);
1224 static void
1225 destroy_session(struct session *ses)
1227 struct document_view *doc_view;
1229 assert(ses);
1230 if_assert_failed return;
1232 #ifdef CONFIG_SCRIPTING_SPIDERMONKEY
1233 smjs_detach_session_object(ses);
1234 #endif
1235 destroy_downloads(ses);
1236 abort_loading(ses, 0);
1237 free_files(ses);
1238 if (ses->doc_view) {
1239 detach_formatted(ses->doc_view);
1240 mem_free(ses->doc_view);
1243 foreach (doc_view, ses->scrn_frames)
1244 detach_formatted(doc_view);
1246 free_list(ses->scrn_frames);
1248 destroy_history(&ses->history);
1249 set_session_referrer(ses, NULL);
1251 if (ses->loading_uri) done_uri(ses->loading_uri);
1253 kill_timer(&ses->display_timer);
1255 while (!list_empty(ses->type_queries))
1256 done_type_query(ses->type_queries.next);
1258 if (ses->download_uri) done_uri(ses->download_uri);
1259 mem_free_if(ses->search_word);
1260 mem_free_if(ses->last_search_word);
1261 mem_free_if(ses->status.last_title);
1262 #ifdef CONFIG_ECMASCRIPT
1263 mem_free_if(ses->status.window_status);
1264 #endif
1265 if (ses->option) {
1266 delete_option(ses->option);
1267 ses->option = NULL;
1269 del_from_list(ses);
1272 void
1273 reload(struct session *ses, enum cache_mode cache_mode)
1275 reload_frame(ses, NULL, cache_mode);
1278 void
1279 reload_frame(struct session *ses, unsigned char *name,
1280 enum cache_mode cache_mode)
1282 abort_loading(ses, 0);
1284 if (cache_mode == CACHE_MODE_INCREMENT) {
1285 cache_mode = CACHE_MODE_NEVER;
1286 if (ses->reloadlevel < CACHE_MODE_NEVER)
1287 cache_mode = ++ses->reloadlevel;
1288 } else {
1289 ses->reloadlevel = cache_mode;
1292 if (have_location(ses)) {
1293 struct location *loc = cur_loc(ses);
1294 struct file_to_load *ftl;
1296 #ifdef CONFIG_ECMASCRIPT
1297 loc->vs.ecmascript_fragile = 1;
1298 #endif
1300 /* FIXME: When reloading use loading_callback and set up a
1301 * session task so that the reloading will work even when the
1302 * reloaded document contains redirects. This is needed atleast
1303 * when reloading HTTP auth document after the user has entered
1304 * credentials. */
1305 loc->download.data = ses;
1306 loc->download.callback = (download_callback_T *) doc_loading_callback;
1308 mem_free_set(&ses->task.target.frame, null_or_stracpy(name));
1310 load_uri(loc->vs.uri, ses->referrer, &loc->download, PRI_MAIN, cache_mode, -1);
1312 foreach (ftl, ses->more_files) {
1313 if (file_to_load_is_active(ftl))
1314 continue;
1316 ftl->download.data = ftl;
1317 ftl->download.callback = (download_callback_T *) file_loading_callback;
1319 load_additional_file(ftl, cache_mode);
1325 struct frame *
1326 ses_find_frame(struct session *ses, unsigned char *name)
1328 struct location *loc = cur_loc(ses);
1329 struct frame *frame;
1331 assertm(have_location(ses), "ses_request_frame: no location yet");
1332 if_assert_failed return NULL;
1334 foreachback (frame, loc->frames)
1335 if (!c_strcasecmp(frame->name, name))
1336 return frame;
1338 return NULL;
1341 void
1342 set_session_referrer(struct session *ses, struct uri *referrer)
1344 if (ses->referrer) done_uri(ses->referrer);
1345 ses->referrer = referrer ? get_uri_reference(referrer) : NULL;
1348 static void
1349 tabwin_func(struct window *tab, struct term_event *ev)
1351 struct session *ses = tab->data;
1353 switch (ev->ev) {
1354 case EVENT_ABORT:
1355 if (ses) destroy_session(ses);
1356 if (!list_empty(sessions)) update_status();
1357 break;
1358 case EVENT_INIT:
1359 case EVENT_RESIZE:
1360 tab->resize = 1;
1361 /* fall-through */
1362 case EVENT_REDRAW:
1363 if (!ses || ses->tab != get_current_tab(ses->tab->term))
1364 break;
1366 draw_formatted(ses, tab->resize);
1367 if (tab->resize) {
1368 load_frames(ses, ses->doc_view);
1369 process_file_requests(ses);
1370 tab->resize = 0;
1372 print_screen_status(ses);
1373 break;
1374 case EVENT_KBD:
1375 case EVENT_MOUSE:
1376 if (!ses) break;
1377 /* This check is related to bug 296 */
1378 assert(ses->tab == get_current_tab(ses->tab->term));
1379 send_event(ses, ev);
1380 break;
1385 * Gets the url being viewed by this session. Writes it into @a str.
1386 * A maximum of @a str_size bytes (including null) will be written.
1387 * @relates session
1389 unsigned char *
1390 get_current_url(struct session *ses, unsigned char *str, size_t str_size)
1392 struct uri *uri;
1393 int length;
1395 assert(str && str_size > 0);
1397 uri = have_location(ses) ? cur_loc(ses)->vs.uri : ses->loading_uri;
1399 /* Not looking or loading anything */
1400 if (!uri) return NULL;
1402 /* Ensure that the url size is not greater than str_size.
1403 * We can't just happily strncpy(str, here, str_size)
1404 * because we have to stop at POST_CHAR, not only at NULL. */
1405 length = int_min(get_real_uri_length(uri), str_size - 1);
1407 return safe_strncpy(str, struri(uri), length + 1);
1411 * Gets the title of the page being viewed by this session. Writes it into
1412 * @a str. A maximum of @a str_size bytes (including null) will be written.
1413 * @relates session
1415 unsigned char *
1416 get_current_title(struct session *ses, unsigned char *str, size_t str_size)
1418 struct document_view *doc_view = current_frame(ses);
1420 assert(str && str_size > 0);
1422 /* Ensure that the title is defined */
1423 /* TODO: Try globhist --jonas */
1424 if (doc_view && doc_view->document->title)
1425 return safe_strncpy(str, doc_view->document->title, str_size);
1427 return NULL;
1431 * Gets the url of the link currently selected. Writes it into @a str.
1432 * A maximum of @a str_size bytes (including null) will be written.
1433 * @relates session
1435 unsigned char *
1436 get_current_link_url(struct session *ses, unsigned char *str, size_t str_size)
1438 struct link *link = get_current_session_link(ses);
1440 assert(str && str_size > 0);
1442 if (!link) return NULL;
1444 return safe_strncpy(str, link->where ? link->where : link->where_img, str_size);
1447 /** get_current_link_name: returns the name of the current link
1448 * (the text between @<A> and @</A>), @a str is a preallocated string,
1449 * @a str_size includes the null char.
1450 * @relates session */
1451 unsigned char *
1452 get_current_link_name(struct session *ses, unsigned char *str, size_t str_size)
1454 struct link *link = get_current_session_link(ses);
1455 unsigned char *where, *name = NULL;
1457 assert(str && str_size > 0);
1459 if (!link) return NULL;
1461 where = link->where ? link->where : link->where_img;
1462 #ifdef CONFIG_GLOBHIST
1464 struct global_history_item *item;
1466 item = get_global_history_item(where);
1467 if (item) name = item->title;
1469 #endif
1470 if (!name) name = get_link_name(link);
1471 if (!name) name = where;
1473 return safe_strncpy(str, name, str_size);
1476 struct link *
1477 get_current_link_in_view(struct document_view *doc_view)
1479 struct link *link = get_current_link(doc_view);
1481 return link && !link_is_form(link) ? link : NULL;
1484 /** @relates session */
1485 struct link *
1486 get_current_session_link(struct session *ses)
1488 return get_current_link_in_view(current_frame(ses));
1491 /** @relates session */
1493 eat_kbd_repeat_count(struct session *ses)
1495 int count = ses->kbdprefix.repeat_count;
1497 set_kbd_repeat_count(ses, 0);
1499 /* Clear status bar when prefix is eaten (bug 930) */
1500 print_screen_status(ses);
1501 return count;
1504 /** @relates session */
1506 set_kbd_repeat_count(struct session *ses, int new_count)
1508 int old_count = ses->kbdprefix.repeat_count;
1510 if (new_count == old_count)
1511 return old_count;
1513 ses->kbdprefix.repeat_count = new_count;
1515 /* Update the status bar. */
1516 print_screen_status(ses);
1518 /* Clear the old link highlighting. */
1519 draw_formatted(ses, 0);
1521 if (new_count != 0) {
1522 struct document_view *doc_view = current_frame(ses);
1524 highlight_links_with_prefixes_that_start_with_n(ses->tab->term,
1525 doc_view,
1526 new_count);
1529 return new_count;