grafthistory: support curl
[elinks/elinks-j605.git] / src / session / session.c
blobb0c724afbc5e2c478faf8fc81701f35e81962931
1 /* Sessions managment - you'll find things here which you wouldn't expect */
3 /* stpcpy */
4 #ifndef _GNU_SOURCE
5 #define _GNU_SOURCE 1
6 #endif
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
16 #include "elinks.h"
18 #include "bfu/dialog.h"
19 #include "bookmarks/bookmarks.h"
20 #include "cache/cache.h"
21 #include "config/home.h"
22 #include "config/options.h"
23 #include "dialogs/menu.h"
24 #include "dialogs/status.h"
25 #include "document/html/frames.h"
26 #include "document/html/parser.h"
27 #include "document/html/renderer.h"
28 #include "document/refresh.h"
29 #include "document/view.h"
30 #include "globhist/globhist.h"
31 #include "intl/gettext/libintl.h"
32 #include "main/event.h"
33 #include "main/object.h"
34 #include "main/timer.h"
35 #include "network/connection.h"
36 #include "network/state.h"
37 #include "osdep/newwin.h"
38 #include "protocol/protocol.h"
39 #include "protocol/uri.h"
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_HEAD(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_HEAD(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 static void
148 session_info_timeout(int id)
150 struct session_info *info = get_session_info(id);
152 if (!info) return;
153 info->timer = TIMER_ID_UNDEF;
154 done_session_info(info);
158 add_session_info(struct session *ses, struct uri *uri, struct uri *referrer,
159 enum cache_mode cache_mode, enum task_type task)
161 struct session_info *info = mem_calloc(1, sizeof(*info));
163 if (!info) return -1;
165 info->id = session_info_id++;
166 /* I don't know what a reasonable start up time for a new instance is
167 * but it won't hurt to have a few seconds atleast. --jonas */
168 install_timer(&info->timer, (milliseconds_T) 10000,
169 (void (*)(void *)) session_info_timeout,
170 (void *) (long) info->id);
172 info->ses = ses;
173 info->task = task;
174 info->cache_mode = cache_mode;
176 if (uri) info->uri = get_uri_reference(uri);
177 if (referrer) info->referrer = get_uri_reference(referrer);
179 add_to_list(session_info, info);
181 return info->id;
184 static struct session *
185 init_saved_session(struct terminal *term, int id)
187 struct session_info *info = get_session_info(id);
188 struct session *ses;
190 if (!info) return NULL;
192 ses = init_session(info->ses, term, info->uri, 0);
194 if (!ses) {
195 done_session_info(info);
196 return ses;
199 /* Only do the ses_goto()-thing for target=_blank. */
200 if (info->uri && info->task != TASK_NONE) {
201 /* The init_session() call would have started the download but
202 * not with the requested cache mode etc. so interrupt that
203 * download . */
204 abort_loading(ses, 1);
206 ses->reloadlevel = info->cache_mode;
207 set_session_referrer(ses, info->referrer);
208 ses_goto(ses, info->uri, NULL, NULL, info->cache_mode,
209 info->task, 0);
212 done_session_info(info);
214 return ses;
217 static struct session *
218 get_master_session(void)
220 struct session *ses;
222 foreach (ses, sessions)
223 if (ses->tab->term->master) {
224 struct window *tab = get_current_tab(ses->tab->term);
226 return tab ? tab->data : NULL;
229 return NULL;
232 struct download *
233 get_current_download(struct session *ses)
235 struct download *download = NULL;
237 if (!ses) return NULL;
239 if (ses->task.type)
240 download = &ses->loading;
241 else if (have_location(ses))
242 download = &cur_loc(ses)->download;
244 if (download && download->state == S_OK) {
245 struct file_to_load *ftl;
247 foreach (ftl, ses->more_files)
248 if (file_to_load_is_active(ftl))
249 return &ftl->download;
252 /* Note that @download isn't necessarily NULL here,
253 * if @ses->more_files is empty. -- Miciah */
254 return download;
257 void
258 print_error_dialog(struct session *ses, enum connection_state state,
259 struct uri *uri, enum connection_priority priority)
261 struct string msg;
262 unsigned char *uristring;
264 /* Don't show error dialogs for missing CSS stylesheets */
265 if (priority == PRI_CSS
266 || !init_string(&msg))
267 return;
269 uristring = uri ? get_uri_string(uri, URI_PUBLIC) : NULL;
270 if (uristring) {
271 decode_uri_for_display(uristring);
272 add_format_to_string(&msg,
273 _("Unable to retrieve %s", ses->tab->term),
274 uristring);
275 mem_free(uristring);
276 add_to_string(&msg, ":\n\n");
279 add_to_string(&msg, get_state_message(state, ses->tab->term));
281 info_box(ses->tab->term, MSGBOX_FREE_TEXT,
282 N_("Error"), ALIGN_CENTER,
283 msg.source);
285 /* TODO: retry */
288 static void
289 abort_files_load(struct session *ses, int interrupt)
291 while (1) {
292 struct file_to_load *ftl;
293 int more = 0;
295 foreach (ftl, ses->more_files) {
296 if (!file_to_load_is_active(ftl))
297 continue;
299 more = 1;
300 change_connection(&ftl->download, NULL, PRI_CANCEL, interrupt);
303 if (!more) break;
307 void
308 free_files(struct session *ses)
310 struct file_to_load *ftl;
312 abort_files_load(ses, 0);
313 foreach (ftl, ses->more_files) {
314 if (ftl->cached) object_unlock(ftl->cached);
315 if (ftl->uri) done_uri(ftl->uri);
316 mem_free_if(ftl->target_frame);
318 free_list(ses->more_files);
324 static void request_frameset(struct session *, struct frameset_desc *, int);
326 static void
327 request_frame(struct session *ses, unsigned char *name,
328 struct uri *uri, int depth)
330 struct location *loc = cur_loc(ses);
331 struct frame *frame;
333 assertm(have_location(ses), "request_frame: no location");
334 if_assert_failed return;
336 foreach (frame, loc->frames) {
337 struct document_view *doc_view;
339 if (strcasecmp(frame->name, name))
340 continue;
342 foreach (doc_view, ses->scrn_frames) {
343 if (doc_view->vs == &frame->vs && doc_view->document->frame_desc) {
344 request_frameset(ses, doc_view->document->frame_desc, depth);
345 return;
349 request_additional_file(ses, name, frame->vs.uri, PRI_FRAME);
350 return;
353 frame = mem_calloc(1, sizeof(*frame));
354 if (!frame) return;
356 frame->name = stracpy(name);
357 if (!frame->name) {
358 mem_free(frame);
359 return;
362 init_vs(&frame->vs, uri, -1);
364 add_to_list(loc->frames, frame);
366 request_additional_file(ses, name, frame->vs.uri, PRI_FRAME);
369 static void
370 request_frameset(struct session *ses, struct frameset_desc *frameset_desc, int depth)
372 int i;
374 if (depth > HTML_MAX_FRAME_DEPTH) return;
376 depth++; /* Inheritation counter (recursion brake ;) */
378 for (i = 0; i < frameset_desc->n; i++) {
379 struct frame_desc *frame_desc = &frameset_desc->frame_desc[i];
381 if (frame_desc->subframe) {
382 request_frameset(ses, frame_desc->subframe, depth);
383 } else if (frame_desc->name && frame_desc->uri) {
384 request_frame(ses, frame_desc->name,
385 frame_desc->uri, depth);
390 #ifdef CONFIG_CSS
391 static inline void
392 load_css_imports(struct session *ses, struct document_view *doc_view)
394 struct document *document = doc_view->document;
395 struct uri *uri;
396 int index;
398 if (!document) return;
400 foreach_uri (uri, index, &document->css_imports) {
401 request_additional_file(ses, "", uri, PRI_CSS);
404 #else
405 #define load_css_imports(ses, doc_view)
406 #endif
408 #ifdef CONFIG_ECMASCRIPT
409 static inline void
410 load_ecmascript_imports(struct session *ses, struct document_view *doc_view)
412 struct document *document = doc_view->document;
413 struct uri *uri;
414 int index;
416 if (!document) return;
418 foreach_uri (uri, index, &document->ecmascript_imports) {
419 request_additional_file(ses, "", uri, /* XXX */ PRI_CSS);
422 #else
423 #define load_ecmascript_imports(ses, doc_view)
424 #endif
426 inline void
427 load_frames(struct session *ses, struct document_view *doc_view)
429 struct document *document = doc_view->document;
431 if (!document || !document->frame_desc) return;
432 request_frameset(ses, document->frame_desc, 0);
434 foreach (doc_view, ses->scrn_frames) {
435 load_css_imports(ses, doc_view);
436 load_ecmascript_imports(ses, doc_view);
440 void
441 display_timer(struct session *ses)
443 timeval_T start, stop, duration;
444 milliseconds_T t;
446 timeval_now(&start);
447 draw_formatted(ses, 3);
448 timeval_now(&stop);
449 timeval_sub(&duration, &start, &stop);
451 t = mult_ms(timeval_to_milliseconds(&duration), DISPLAY_TIME);
452 if (t < DISPLAY_TIME_MIN) t = DISPLAY_TIME_MIN;
453 install_timer(&ses->display_timer, t,
454 (void (*)(void *)) display_timer,
455 ses);
457 load_frames(ses, ses->doc_view);
458 load_css_imports(ses, ses->doc_view);
459 load_ecmascript_imports(ses, ses->doc_view);
460 process_file_requests(ses);
464 struct questions_entry {
465 LIST_HEAD(struct questions_entry);
467 void (*callback)(struct session *, void *);
468 void *data;
471 INIT_LIST_HEAD(questions_queue);
474 void
475 check_questions_queue(struct session *ses)
477 while (!list_empty(questions_queue)) {
478 struct questions_entry *q = questions_queue.next;
480 q->callback(ses, q->data);
481 del_from_list(q);
482 mem_free(q);
486 void
487 add_questions_entry(void (*callback)(struct session *, void *), void *data)
489 struct questions_entry *q = mem_alloc(sizeof(*q));
491 if (!q) return;
493 q->callback = callback;
494 q->data = data;
495 add_to_list(questions_queue, q);
498 #ifdef CONFIG_SCRIPTING
499 static void
500 maybe_pre_format_html(struct cache_entry *cached, struct session *ses)
502 struct fragment *fragment;
503 static int pre_format_html_event = EVENT_NONE;
505 if (!cached || cached->preformatted)
506 return;
508 fragment = get_cache_fragment(cached);
509 if (!fragment) return;
511 /* We cannot do anything if the data are fragmented. */
512 if (!list_is_singleton(cached->frag)) return;
514 set_event_id(pre_format_html_event, "pre-format-html");
515 trigger_event(pre_format_html_event, ses, cached);
517 /* XXX: Keep this after the trigger_event, because hooks might call
518 * normalize_cache_entry()! */
519 cached->preformatted = 1;
521 #endif
523 static int
524 check_incomplete_redirects(struct cache_entry *cached)
526 assert(cached);
528 cached = follow_cached_redirects(cached);
529 if (cached && !cached->redirect) {
530 /* XXX: This is not quite true, but does that difference
531 * matter here? */
532 return cached->incomplete;
535 return 0;
539 session_is_loading(struct session *ses)
541 struct download *download = get_current_download(ses);
543 if (!download) return 0;
545 if (!is_in_result_state(download->state))
546 return 1;
548 /* The validness of download->cached (especially the download struct in
549 * ses->loading) is hard to maintain so check before using it.
550 * Related to bug 559. */
551 if (download->cached
552 && cache_entry_is_valid(download->cached)
553 && check_incomplete_redirects(download->cached))
554 return 1;
556 return 0;
559 void
560 doc_loading_callback(struct download *download, struct session *ses)
562 int submit = 0;
564 if (is_in_result_state(download->state)) {
565 #ifdef CONFIG_SCRIPTING
566 maybe_pre_format_html(download->cached, ses);
567 #endif
568 kill_timer(&ses->display_timer);
570 draw_formatted(ses, 1);
572 if (get_cmd_opt_bool("auto-submit")) {
573 if (!list_empty(ses->doc_view->document->forms)) {
574 get_cmd_opt_bool("auto-submit") = 0;
575 submit = 1;
579 load_frames(ses, ses->doc_view);
580 load_css_imports(ses, ses->doc_view);
581 load_ecmascript_imports(ses, ses->doc_view);
582 process_file_requests(ses);
584 if (ses->doc_view
585 && ses->doc_view->document
586 && ses->doc_view->document->refresh
587 && get_opt_bool("document.browse.refresh")) {
588 start_document_refresh(ses->doc_view->document->refresh,
589 ses);
592 if (download->state != S_OK) {
593 print_error_dialog(ses, download->state,
594 ses->doc_view->document->uri,
595 download->pri);
598 } else if (is_in_transfering_state(download->state)
599 && ses->display_timer == TIMER_ID_UNDEF) {
600 display_timer(ses);
603 check_questions_queue(ses);
604 print_screen_status(ses);
606 #ifdef CONFIG_GLOBHIST
607 if (download->conn && download->pri != PRI_CSS) {
608 unsigned char *title = ses->doc_view->document->title;
609 struct uri *uri = download->conn->proxied_uri;
611 add_global_history_item(struri(uri), title, time(NULL));
613 #endif
615 if (submit) auto_submit_form(ses);
618 static void
619 file_loading_callback(struct download *download, struct file_to_load *ftl)
621 if (ftl->download.cached && ftl->cached != ftl->download.cached) {
622 if (ftl->cached) object_unlock(ftl->cached);
623 ftl->cached = ftl->download.cached;
624 object_lock(ftl->cached);
627 /* FIXME: We need to do content-type check here! However, we won't
628 * handle properly the "Choose action" dialog now :(. */
629 if (ftl->cached && !ftl->cached->redirect_get && download->pri != PRI_CSS) {
630 struct session *ses = ftl->ses;
631 struct uri *loading_uri = ses->loading_uri;
632 unsigned char *target_frame = ses->task.target.frame;
634 ses->loading_uri = ftl->uri;
635 ses->task.target.frame = ftl->target_frame;
636 setup_download_handler(ses, &ftl->download, ftl->cached, 1);
637 ses->loading_uri = loading_uri;
638 ses->task.target.frame = target_frame;
641 doc_loading_callback(download, ftl->ses);
644 static struct file_to_load *
645 request_additional_file(struct session *ses, unsigned char *name, struct uri *uri, int pri)
647 struct file_to_load *ftl;
649 if (uri->protocol == PROTOCOL_UNKNOWN) {
650 return NULL;
653 /* XXX: We cannot run the external handler here, because
654 * request_additional_file() is called many times for a single URL
655 * (normally the foreach() right below catches them all). Anyway,
656 * having <frame src="mailto:foo"> would be just weird, wouldn't it?
657 * --pasky */
658 if (get_protocol_external_handler(ses->tab->term, uri)) {
659 return NULL;
662 foreach (ftl, ses->more_files) {
663 if (compare_uri(ftl->uri, uri, URI_BASE)) {
664 if (ftl->pri > pri) {
665 ftl->pri = pri;
666 change_connection(&ftl->download, &ftl->download, pri, 0);
668 return NULL;
672 ftl = mem_calloc(1, sizeof(*ftl));
673 if (!ftl) return NULL;
675 ftl->uri = get_uri_reference(uri);
676 ftl->target_frame = stracpy(name);
677 ftl->download.callback = (download_callback_T *) file_loading_callback;
678 ftl->download.data = ftl;
679 ftl->pri = pri;
680 ftl->ses = ses;
682 add_to_list(ses->more_files, ftl);
684 return ftl;
687 static void
688 load_additional_file(struct file_to_load *ftl, struct document_view *doc_view,
689 enum cache_mode cache_mode)
691 struct uri *referrer = doc_view && doc_view->document
692 ? doc_view->document->uri : NULL;
694 load_uri(ftl->uri, referrer, &ftl->download, ftl->pri, cache_mode, -1);
697 void
698 process_file_requests(struct session *ses)
700 if (ses->status.processing_file_requests) return;
701 ses->status.processing_file_requests = 1;
703 while (1) {
704 struct file_to_load *ftl;
705 int more = 0;
707 foreach (ftl, ses->more_files) {
708 struct document_view *doc_view;
710 if (ftl->req_sent)
711 continue;
713 ftl->req_sent = 1;
715 doc_view = current_frame(ses);
716 load_additional_file(ftl, doc_view, CACHE_MODE_NORMAL);
717 more = 1;
720 if (!more) break;
723 ses->status.processing_file_requests = 0;
727 static void
728 dialog_goto_url_open(void *data)
730 dialog_goto_url((struct session *) data, NULL);
733 /* Returns 0 if the first session was not properly initialized and
734 * setup_session() should be called on the session as well. */
735 static int
736 setup_first_session(struct session *ses, struct uri *uri)
738 struct terminal *term = ses->tab->term;
740 if (!*get_opt_str("protocol.http.user_agent")) {
741 info_box(term, 0,
742 N_("Warning"), ALIGN_CENTER,
743 N_("You have an empty string in protocol.http.user_agent - "
744 "this was a default value in the past, substituted by "
745 "default ELinks User-Agent string. However, currently "
746 "this means that NO User-Agent HEADER "
747 "WILL BE SENT AT ALL - if this is really what you want, "
748 "set its value to \" \", otherwise please delete the line "
749 "with this setting from your configuration file (if you "
750 "have no idea what I'm talking about, just do this), so "
751 "that the correct default setting will be used. Apologies for "
752 "any inconvience caused."));
755 if (!get_opt_bool("config.saving_style_w")) {
756 struct option *opt = get_opt_rec(config_options, "config.saving_style_w");
757 opt->value.number = 1;
758 option_changed(ses, opt, opt);
759 if (get_opt_int("config.saving_style") != 3) {
760 info_box(term, 0,
761 N_("Warning"), ALIGN_CENTER,
762 N_("You have option config.saving_style set to "
763 "a de facto obsolete value. The configuration "
764 "saving algorithms of ELinks were changed from "
765 "the last time you upgraded ELinks. Now, only "
766 "those options which you actually changed are "
767 "saved to the configuration file, instead of "
768 "all the options. This simplifies our "
769 "situation greatly when we see that some option "
770 "has an inappropriate default value or we need to "
771 "change the semantics of some option in a subtle way. "
772 "Thus, we recommend you change the value of "
773 "config.saving_style option to 3 in order to get "
774 "the \"right\" behaviour. Apologies for any "
775 "inconvience caused."));
779 if (first_use) {
780 /* Only open the goto URL dialog if no URI was passed on the
781 * command line. */
782 void *handler = uri ? NULL : dialog_goto_url_open;
784 first_use = 0;
786 msg_box(term, NULL, 0,
787 N_("Welcome"), ALIGN_CENTER,
788 N_("Welcome to ELinks!\n\n"
789 "Press ESC for menu. Documentation is available in "
790 "Help menu."),
791 ses, 1,
792 N_("~OK"), handler, B_ENTER | B_ESC);
794 /* If there is no URI the goto dialog will pop up so there is
795 * no need to call setup_session(). */
796 if (!uri) return 1;
798 #ifdef CONFIG_BOOKMARKS
799 } else if (!uri && get_opt_bool("ui.sessions.auto_restore")) {
800 unsigned char *folder;
802 folder = get_opt_str("ui.sessions.auto_save_foldername");
803 open_bookmark_folder(ses, folder);
804 return 1;
805 #endif
808 /* If there's a URI to load we have to call */
809 return 0;
812 /* First load the current URI of the base session. In most cases it will just
813 * be fetched from the cache so that the new tab will not appear ``empty' while
814 * loading the real URI or showing the goto URL dialog. */
815 static void
816 setup_session(struct session *ses, struct uri *uri, struct session *base)
818 if (base && have_location(base)) {
819 goto_uri(ses, cur_loc(base)->vs.uri);
820 if (ses->doc_view && ses->doc_view->vs
821 && base->doc_view && base->doc_view->vs) {
822 struct view_state *vs = ses->doc_view->vs;
824 destroy_vs(vs, 1);
825 copy_vs(vs, base->doc_view->vs);
829 if (uri) {
830 goto_uri(ses, uri);
832 } else if (!goto_url_home(ses)) {
833 if (get_opt_bool("ui.startup_goto_dialog")) {
834 dialog_goto_url_open(ses);
839 struct session *
840 init_session(struct session *base_session, struct terminal *term,
841 struct uri *uri, int in_background)
843 struct session *ses = mem_calloc(1, sizeof(*ses));
845 if (!ses) return NULL;
847 ses->tab = init_tab(term, ses, tabwin_func);
848 if (!ses->tab) {
849 mem_free(ses);
850 return NULL;
853 create_history(&ses->history);
854 init_list(ses->scrn_frames);
855 init_list(ses->more_files);
856 init_list(ses->type_queries);
857 ses->task.type = TASK_NONE;
858 ses->display_timer = TIMER_ID_UNDEF;
860 #ifdef CONFIG_LEDS
861 init_led_panel(&ses->status.leds);
862 ses->status.ssl_led = register_led(ses, 0);
863 ses->status.insert_mode_led = register_led(ses, 1);
864 ses->status.ecmascript_led = register_led(ses, 2);
865 ses->status.popup_led = register_led(ses, 3);
866 #endif
867 ses->status.force_show_status_bar = -1;
868 ses->status.force_show_title_bar = -1;
870 add_to_list(sessions, ses);
872 /* Update the status -- most importantly the info about whether to the
873 * show the title, tab and status bar -- _before_ loading the URI so
874 * the document cache is not filled with useless documents if the
875 * content is already cached.
877 * For big document it also reduces memory usage quite a bit because
878 * (atleast that is my interpretation --jonas) the old document will
879 * have a chance to be released before rendering a new one. A few
880 * numbers when opening a new tab while viewing debians package list
881 * for unstable:
883 * 9307 jonas 15 0 34252 30m 5088 S 0.0 12.2 0:03.63 elinks-old
884 * 9305 jonas 15 0 17060 13m 5088 S 0.0 5.5 0:02.07 elinks-new
886 update_status();
888 /* Check if the newly inserted session is the only in the list and do
889 * the special setup for the first session, */
890 if (!list_is_singleton(sessions) || !setup_first_session(ses, uri)) {
891 setup_session(ses, uri, base_session);
894 if (!in_background)
895 switch_to_tab(term, get_tab_number(ses->tab), -1);
897 return ses;
900 static void
901 init_remote_session(struct session *ses, enum remote_session_flags *remote_ptr,
902 struct uri *uri)
904 enum remote_session_flags remote = *remote_ptr;
906 if (remote & SES_REMOTE_CURRENT_TAB) {
907 goto_uri(ses, uri);
908 /* Mask out the current tab flag */
909 *remote_ptr = remote & ~SES_REMOTE_CURRENT_TAB;
911 /* Remote session was masked out. Open all following URIs in
912 * new tabs, */
913 if (!*remote_ptr)
914 *remote_ptr = SES_REMOTE_NEW_TAB;
916 } else if (remote & SES_REMOTE_NEW_TAB) {
917 /* FIXME: This is not perfect. Doing multiple -remote
918 * with no URLs will make the goto dialogs
919 * inaccessible. Maybe we should not support this kind
920 * of thing or make the window focus detecting code
921 * more intelligent. --jonas */
922 open_uri_in_new_tab(ses, uri, 0, 1);
924 if (remote & SES_REMOTE_PROMPT_URL) {
925 dialog_goto_url_open(ses);
928 } else if (remote & SES_REMOTE_NEW_WINDOW) {
929 /* FIXME: It is quite rude because we just take the first
930 * possibility and should maybe make it possible to specify
931 * new-screen etc via -remote "openURL(..., new-*)" --jonas */
932 if (!can_open_in_new(ses->tab->term))
933 return;
935 open_uri_in_new_window(ses, uri, NULL,
936 ses->tab->term->environment,
937 CACHE_MODE_NORMAL, TASK_NONE);
939 } else if (remote & SES_REMOTE_ADD_BOOKMARK) {
940 #ifdef CONFIG_BOOKMARKS
941 if (!uri) return;
942 add_bookmark(NULL, 1, struri(uri), struri(uri));
943 #endif
945 } else if (remote & SES_REMOTE_INFO_BOX) {
946 unsigned char *text;
948 if (!uri) return;
950 text = memacpy(uri->data, uri->datalen);
951 if (!text) return;
953 info_box(ses->tab->term, MSGBOX_FREE_TEXT,
954 N_("Error"), ALIGN_CENTER,
955 text);
957 } else if (remote & SES_REMOTE_PROMPT_URL) {
958 dialog_goto_url_open(ses);
964 struct string *
965 encode_session_info(struct string *info, struct list_head *url_list)
967 struct string_list_item *url;
969 if (!init_string(info)) return NULL;
971 foreach (url, *url_list) {
972 struct string *str = &url->string;
974 add_bytes_to_string(info, str->source, str->length + 1);
977 return info;
980 /* Older elinks versions (up to and including 0.9.1) sends no magic variable and if
981 * this is detected we fallback to the old session info format. For this format
982 * the magic member of terminal_info hold the length of the URI string. The
983 * old format is handled by the default label in the switch.
985 * The new session info format supports extraction of multiple URIS from the
986 * terminal_info data member. The magic variable controls how to interpret
987 * the fields:
989 * INTERLINK_NORMAL_MAGIC means use the terminal_info session_info
990 * variable as an id for a saved session.
992 * INTERLINK_REMOTE_MAGIC means use the terminal_info session_info
993 * variable as the remote session flags. */
996 decode_session_info(struct terminal *term, struct terminal_info *info)
998 int len = info->length;
999 struct session *base_session = NULL;
1000 enum remote_session_flags remote = 0;
1001 unsigned char *str;
1003 switch (info->magic) {
1004 case INTERLINK_NORMAL_MAGIC:
1005 /* Lookup if there are any saved sessions that should be
1006 * resumed using the session_info as an id. The id is derived
1007 * from the value of -base-session command line option in the
1008 * connecting instance.
1010 * At the moment it is only used when opening instances in new
1011 * window to figure out how to initialize it when the new
1012 * instance connects to the master.
1014 * We don't need to handle it for the old format since new
1015 * instances connecting this way will always be of the same
1016 * origin as the master. */
1017 if (init_saved_session(term, info->session_info))
1018 return 1;
1019 break;
1021 case INTERLINK_REMOTE_MAGIC:
1022 /* This could mean some fatal bug but I am unsure if we can
1023 * actually assert it since people could pour all kinds of crap
1024 * down the socket. */
1025 if (!info->session_info) {
1026 INTERNAL("Remote magic with no remote flags");
1027 return 0;
1030 remote = info->session_info;
1032 /* If processing session info from a -remote instance we want
1033 * to hook up with the master so we can handle request for
1034 * stuff in current tab. */
1035 base_session = get_master_session();
1036 if (!base_session) return 0;
1038 break;
1040 default:
1041 /* The old format calculates the session_info and magic members
1042 * as part of the data that should be decoded so we have to
1043 * substract it to get the size of the actual URI data. */
1044 len -= sizeof(info->session_info) + sizeof(info->magic);
1046 /* Extract URI containing @magic bytes */
1047 if (info->magic <= 0 || info->magic > len)
1048 len = 0;
1049 else
1050 len = info->magic;
1053 if (len <= 0) {
1054 if (!remote)
1055 return !!init_session(base_session, term, NULL, 0);
1057 /* Even though there are no URIs we still have to
1058 * handle remote stuff. */
1059 init_remote_session(base_session, &remote, NULL);
1060 return 0;
1063 str = info->data;
1065 /* Extract multiple (possible) NUL terminated URIs */
1066 while (len > 0) {
1067 unsigned char *end = memchr(str, 0, len);
1068 int urilength = end ? end - str : len;
1069 struct uri *uri = NULL;
1070 unsigned char *uristring = memacpy(str, urilength);
1072 if (uristring) {
1073 uri = get_hooked_uri(uristring, base_session, term->cwd);
1074 mem_free(uristring);
1077 len -= urilength + 1;
1078 str += urilength + 1;
1080 if (remote) {
1081 if (!uri) continue;
1083 init_remote_session(base_session, &remote, uri);
1085 } else {
1086 int backgrounded = !list_empty(term->windows);
1087 int bad_url = !uri;
1088 struct session *ses;
1090 if (!uri)
1091 uri = get_uri("about:blank", 0);
1093 ses = init_session(base_session, term, uri, backgrounded);
1094 if (!ses) {
1095 /* End loop if initialization fails */
1096 len = 0;
1097 } else if (bad_url) {
1098 print_error_dialog(ses, S_BAD_URL, NULL, PRI_MAIN);
1103 if (uri) done_uri(uri);
1106 /* If we only initialized remote sessions or didn't manage to add any
1107 * new tabs return zero so the terminal will be destroyed ASAP. */
1108 return remote ? 0 : !list_empty(term->windows);
1112 void
1113 abort_loading(struct session *ses, int interrupt)
1115 if (have_location(ses)) {
1116 struct location *loc = cur_loc(ses);
1118 if (is_in_progress_state(loc->download.state))
1119 change_connection(&loc->download, NULL, PRI_CANCEL, interrupt);
1120 abort_files_load(ses, interrupt);
1122 abort_preloading(ses, interrupt);
1125 static void
1126 destroy_session(struct session *ses)
1128 struct document_view *doc_view;
1130 assert(ses);
1131 if_assert_failed return;
1133 destroy_downloads(ses);
1134 abort_loading(ses, 0);
1135 free_files(ses);
1136 if (ses->doc_view) {
1137 detach_formatted(ses->doc_view);
1138 mem_free(ses->doc_view);
1141 foreach (doc_view, ses->scrn_frames)
1142 detach_formatted(doc_view);
1144 free_list(ses->scrn_frames);
1146 destroy_history(&ses->history);
1147 set_session_referrer(ses, NULL);
1149 if (ses->loading_uri) done_uri(ses->loading_uri);
1151 kill_timer(&ses->display_timer);
1153 while (!list_empty(ses->type_queries))
1154 done_type_query(ses->type_queries.next);
1156 if (ses->download_uri) done_uri(ses->download_uri);
1157 mem_free_if(ses->search_word);
1158 mem_free_if(ses->last_search_word);
1159 mem_free_if(ses->status.last_title);
1160 del_from_list(ses);
1163 void
1164 reload(struct session *ses, enum cache_mode cache_mode)
1166 abort_loading(ses, 0);
1168 if (cache_mode == CACHE_MODE_INCREMENT) {
1169 cache_mode = CACHE_MODE_NEVER;
1170 if (ses->reloadlevel < CACHE_MODE_NEVER)
1171 cache_mode = ++ses->reloadlevel;
1172 } else {
1173 ses->reloadlevel = cache_mode;
1176 if (have_location(ses)) {
1177 struct location *loc = cur_loc(ses);
1178 struct file_to_load *ftl;
1179 struct document_view *doc_view = current_frame(ses);
1181 #ifdef CONFIG_ECMASCRIPT
1182 loc->vs.ecmascript_fragile = 1;
1183 #endif
1185 /* FIXME: When reloading use loading_callback and set up a
1186 * session task so that the reloading will work even when the
1187 * reloaded document contains redirects. This is needed atleast
1188 * when reloading HTTP auth document after the user has entered
1189 * credentials. */
1190 loc->download.data = ses;
1191 loc->download.callback = (download_callback_T *) doc_loading_callback;
1193 load_uri(loc->vs.uri, ses->referrer, &loc->download, PRI_MAIN, cache_mode, -1);
1195 foreach (ftl, ses->more_files) {
1196 if (file_to_load_is_active(ftl))
1197 continue;
1199 ftl->download.data = ftl;
1200 ftl->download.callback = (download_callback_T *) file_loading_callback;
1202 load_additional_file(ftl, doc_view, cache_mode);
1208 struct frame *
1209 ses_find_frame(struct session *ses, unsigned char *name)
1211 struct location *loc = cur_loc(ses);
1212 struct frame *frame;
1214 assertm(have_location(ses), "ses_request_frame: no location yet");
1215 if_assert_failed return NULL;
1217 foreachback (frame, loc->frames)
1218 if (!strcasecmp(frame->name, name))
1219 return frame;
1221 return NULL;
1224 void
1225 set_session_referrer(struct session *ses, struct uri *referrer)
1227 if (ses->referrer) done_uri(ses->referrer);
1228 ses->referrer = referrer ? get_uri_reference(referrer) : NULL;
1231 static void
1232 tabwin_func(struct window *tab, struct term_event *ev)
1234 struct session *ses = tab->data;
1236 switch (ev->ev) {
1237 case EVENT_ABORT:
1238 if (ses) destroy_session(ses);
1239 if (!list_empty(sessions)) update_status();
1240 break;
1241 case EVENT_INIT:
1242 case EVENT_RESIZE:
1243 tab->resize = 1;
1244 /* fall-through */
1245 case EVENT_REDRAW:
1246 if (!ses || ses->tab != get_current_tab(ses->tab->term))
1247 break;
1249 draw_formatted(ses, tab->resize);
1250 if (tab->resize) {
1251 load_frames(ses, ses->doc_view);
1252 process_file_requests(ses);
1253 tab->resize = 0;
1255 print_screen_status(ses);
1256 break;
1257 case EVENT_KBD:
1258 case EVENT_MOUSE:
1259 if (!ses) break;
1260 /* This check is related to bug 296 */
1261 assert(ses->tab == get_current_tab(ses->tab->term));
1262 send_event(ses, ev);
1263 break;
1268 * Gets the url being viewed by this session. Writes it into str.
1269 * A maximum of str_size bytes (including null) will be written.
1271 unsigned char *
1272 get_current_url(struct session *ses, unsigned char *str, size_t str_size)
1274 struct uri *uri;
1275 int length;
1277 assert(str && str_size > 0);
1279 uri = have_location(ses) ? cur_loc(ses)->vs.uri : ses->loading_uri;
1281 /* Not looking or loading anything */
1282 if (!uri) return NULL;
1284 /* Ensure that the url size is not greater than str_size.
1285 * We can't just happily strncpy(str, here, str_size)
1286 * because we have to stop at POST_CHAR, not only at NULL. */
1287 length = int_min(get_real_uri_length(uri), str_size - 1);
1289 return safe_strncpy(str, struri(uri), length + 1);
1293 * Gets the title of the page being viewed by this session. Writes it into str.
1294 * A maximum of str_size bytes (including null) will be written.
1296 unsigned char *
1297 get_current_title(struct session *ses, unsigned char *str, size_t str_size)
1299 struct document_view *doc_view = current_frame(ses);
1301 assert(str && str_size > 0);
1303 /* Ensure that the title is defined */
1304 /* TODO: Try globhist --jonas */
1305 if (doc_view && doc_view->document->title)
1306 return safe_strncpy(str, doc_view->document->title, str_size);
1308 return NULL;
1312 * Gets the url of the link currently selected. Writes it into str.
1313 * A maximum of str_size bytes (including null) will be written.
1315 unsigned char *
1316 get_current_link_url(struct session *ses, unsigned char *str, size_t str_size)
1318 struct link *link = get_current_session_link(ses);
1320 assert(str && str_size > 0);
1322 if (!link) return NULL;
1324 return safe_strncpy(str, link->where ? link->where : link->where_img, str_size);
1327 /* get_current_link_name: returns the name of the current link
1328 * (the text between <A> and </A>), str is a preallocated string,
1329 * str_size includes the null char. */
1330 unsigned char *
1331 get_current_link_name(struct session *ses, unsigned char *str, size_t str_size)
1333 struct link *link = get_current_session_link(ses);
1334 unsigned char *where, *name = NULL;
1336 assert(str && str_size > 0);
1338 if (!link) return NULL;
1340 where = link->where ? link->where : link->where_img;
1341 #ifdef CONFIG_GLOBHIST
1343 struct global_history_item *item;
1345 item = get_global_history_item(where);
1346 if (item) name = item->title;
1348 #endif
1349 if (!name) name = get_link_name(link);
1350 if (!name) name = where;
1352 return safe_strncpy(str, name, str_size);
1355 struct link *
1356 get_current_link_in_view(struct document_view *doc_view)
1358 struct link *link = get_current_link(doc_view);
1360 return link && !link_is_form(link) ? link : NULL;
1363 struct link *
1364 get_current_session_link(struct session *ses)
1366 return get_current_link_in_view(current_frame(ses));
1370 eat_kbd_repeat_count(struct session *ses)
1372 int count = ses->kbdprefix.repeat_count;
1374 ses->kbdprefix.repeat_count = 0;
1376 return count;