2 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
4 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
5 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
6 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
7 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * xxxterm "protocol" (xtp)
26 * We use this for managing stuff like downloads and favorites. They
27 * make magical HTML pages in memory which have xxxt:// links in order
28 * to communicate with xxxterm's internals. These links take the format:
29 * xxxt://class/session_key/action/arg
31 * Don't begin xtp class/actions as 0. atoi returns that on error.
33 * Typically we have not put addition of items in this framework, as
34 * adding items is either done via an ex-command or via a keybinding instead.
37 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
38 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
39 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
40 "td{overflow: hidden;" \
41 " padding: 2px 2px 2px 2px;" \
42 " border: 1px solid black;" \
43 " vertical-align:top;" \
44 " word-wrap: break-word}\n" \
45 "tr:hover{background: #ffff99}\n" \
46 "th{background-color: #cccccc;" \
47 " border: 1px solid black}\n" \
48 "table{width: 100%%;" \
49 " border: 1px black solid;" \
50 " border-collapse:collapse}\n" \
52 "border: 1px solid black;" \
55 ".progress-inner{float: left;" \
57 " background: green}\n" \
58 ".dlstatus{font-size: small;" \
59 " text-align: center}\n" \
62 /* XTP classes (xxxt://<class>) */
63 #define XT_XTP_INVALID (0) /* invalid */
64 #define XT_XTP_DL (1) /* downloads */
65 #define XT_XTP_HL (2) /* history */
66 #define XT_XTP_CL (3) /* cookies */
67 #define XT_XTP_FL (4) /* favorites */
69 /* XTP download actions */
70 #define XT_XTP_DL_LIST (1)
71 #define XT_XTP_DL_CANCEL (2)
72 #define XT_XTP_DL_REMOVE (3)
74 /* XTP history actions */
75 #define XT_XTP_HL_LIST (1)
76 #define XT_XTP_HL_REMOVE (2)
78 /* XTP cookie actions */
79 #define XT_XTP_CL_LIST (1)
80 #define XT_XTP_CL_REMOVE (2)
82 /* XTP cookie actions */
83 #define XT_XTP_FL_LIST (1)
84 #define XT_XTP_FL_REMOVE (2)
86 int js_show_wl(struct tab
*, struct karg
*);
87 int pl_show_wl(struct tab
*, struct karg
*);
88 int set(struct tab
*, struct karg
*);
89 int marco(struct tab
*, struct karg
*);
90 int startpage(struct tab
*, struct karg
*);
91 const char * marco_message(int *);
93 struct about_type about_list
[] = {
94 { XT_URI_ABOUT_ABOUT
, about
},
95 { XT_URI_ABOUT_BLANK
, blank
},
96 { XT_URI_ABOUT_CERTS
, ca_cmd
},
97 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
98 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
99 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
100 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
101 { XT_URI_ABOUT_HELP
, help
},
102 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
103 { XT_URI_ABOUT_JSWL
, js_show_wl
},
104 { XT_URI_ABOUT_SET
, set
},
105 { XT_URI_ABOUT_STATS
, stats
},
106 { XT_URI_ABOUT_MARCO
, marco
},
107 { XT_URI_ABOUT_STARTPAGE
, startpage
},
108 { XT_URI_ABOUT_PLUGINWL
, pl_show_wl
},
113 * We use these to prevent people putting xxxt:// URLs on
114 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
116 #define XT_XTP_SES_KEY_SZ 8
117 #define XT_XTP_SES_KEY_HEX_FMT \
118 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
119 char *dl_session_key
; /* downloads */
120 char *hl_session_key
; /* history list */
121 char *cl_session_key
; /* cookie list */
122 char *fl_session_key
; /* favorites list */
124 int updating_fl_tabs
= 0;
125 int updating_dl_tabs
= 0;
126 int updating_hl_tabs
= 0;
127 int updating_cl_tabs
= 0;
128 struct download_list downloads
;
131 about_list_size(void)
133 return (LENGTH(about_list
));
137 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
141 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
143 "<title>%s</title>\n"
152 addstyles
? XT_PAGE_STYLE
: "",
161 * Display a web page from a HTML string in memory, rather than from a URL
164 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
169 /* we set this to indicate we want to manually do navaction */
171 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
173 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
175 /* set t->xtp_meaning */
176 for (i
= 0; i
< LENGTH(about_list
); i
++)
177 if (!strcmp(title
, about_list
[i
].name
)) {
182 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
184 #if GTK_CHECK_VERSION(2, 20, 0)
185 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
186 gtk_widget_hide(t
->spinner
);
188 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
189 xt_icon_from_file(t
, file
);
194 blank(struct tab
*t
, struct karg
*args
)
197 show_oops(NULL
, "blank invalid parameters");
199 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
205 about(struct tab
*t
, struct karg
*args
)
210 show_oops(NULL
, "about invalid parameters");
212 body
= g_strdup_printf("<b>Version: %s</b>"
213 #ifdef XXXTERM_BUILDSTR
214 "<br><b>Build: %s</b>"
219 "<li>Marco Peereboom <marco@peereboom.us></li>"
220 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
221 "<li>Edd Barrett <vext01@gmail.com></li>"
222 "<li>Todd T. Fries <todd@fries.net></li>"
223 "<li>Raphael Graf <r@undefined.ch></li>"
224 "<li>Michal Mazurek <akfaew@jasminek.net></li>"
226 "Copyrights and licenses can be found on the XXXTerm "
227 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
229 #ifdef XXXTERM_BUILDSTR
230 version
, XXXTERM_BUILDSTR
236 page
= get_html_page("About", body
, "", 0);
239 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
246 help(struct tab
*t
, struct karg
*args
)
248 char *page
, *head
, *body
;
251 show_oops(NULL
, "help invalid parameters");
253 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
254 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
256 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
257 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
258 "cgi-bin/man-cgi?xxxterm</a>";
260 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
262 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
269 stats(struct tab
*t
, struct karg
*args
)
271 char *page
, *body
, *s
, line
[64 * 1024];
272 long long unsigned int line_count
= 0;
276 show_oops(NULL
, "stats invalid parameters");
279 if (save_rejected_cookies
) {
280 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
282 s
= fgets(line
, sizeof line
, r_cookie_f
);
283 if (s
== NULL
|| feof(r_cookie_f
) ||
289 snprintf(line
, sizeof line
,
290 "<br/>Cookies blocked(*) total: %llu", line_count
);
292 show_oops(t
, "Can't open blocked cookies file: %s",
296 body
= g_strdup_printf(
297 "Cookies blocked(*) this session: %llu"
299 "<p><small><b>*</b> results vary based on settings</small></p>",
303 page
= get_html_page("Statistics", body
, "", 0);
306 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
313 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
314 size_t cert_count
, char *title
)
316 gnutls_datum_t cinfo
;
322 for (i
= 0; i
< cert_count
; i
++) {
323 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
328 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
329 body
, i
, cinfo
.data
);
330 gnutls_free(cinfo
.data
);
334 tmp
= get_html_page(title
, body
, "", 0);
337 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
342 ca_cmd(struct tab
*t
, struct karg
*args
)
345 int rv
= 1, certs
= 0, certs_read
;
348 gnutls_x509_crt_t
*c
= NULL
;
349 char *certs_buf
= NULL
, *s
;
351 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
352 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
356 if (fstat(fileno(f
), &sb
) == -1) {
357 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
361 certs_buf
= g_malloc(sb
.st_size
+ 1);
362 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
363 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
366 certs_buf
[sb
.st_size
] = '\0';
369 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
371 s
+= strlen("BEGIN CERTIFICATE");
374 bzero(&dt
, sizeof dt
);
375 dt
.data
= (unsigned char *)certs_buf
;
376 dt
.size
= sb
.st_size
;
377 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
378 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
379 GNUTLS_X509_FMT_PEM
, 0);
380 if (certs_read
<= 0) {
381 show_oops(t
, "No cert(s) available");
384 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
397 cookie_show_wl(struct tab
*t
, struct karg
*args
)
399 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
400 wl_show(t
, args
, "Cookie White List", &c_wl
);
406 js_show_wl(struct tab
*t
, struct karg
*args
)
408 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
409 wl_show(t
, args
, "JavaScript White List", &js_wl
);
415 cookie_cmd(struct tab
*t
, struct karg
*args
)
417 if (args
->i
& XT_SHOW
)
418 wl_show(t
, args
, "Cookie White List", &c_wl
);
419 else if (args
->i
& XT_WL_TOGGLE
) {
420 args
->i
|= XT_WL_RELOAD
;
422 } else if (args
->i
& XT_SAVE
) {
423 args
->i
|= XT_WL_RELOAD
;
424 wl_save(t
, args
, XT_WL_COOKIE
);
425 } else if (args
->i
& XT_DELETE
)
426 show_oops(t
, "'cookie delete' currently unimplemented");
432 js_cmd(struct tab
*t
, struct karg
*args
)
434 if (args
->i
& XT_SHOW
)
435 wl_show(t
, args
, "JavaScript White List", &js_wl
);
436 else if (args
->i
& XT_SAVE
) {
437 args
->i
|= XT_WL_RELOAD
;
438 wl_save(t
, args
, XT_WL_JAVASCRIPT
);
439 } else if (args
->i
& XT_WL_TOGGLE
) {
440 args
->i
|= XT_WL_RELOAD
;
442 } else if (args
->i
& XT_DELETE
)
443 show_oops(t
, "'js delete' currently unimplemented");
449 pl_show_wl(struct tab
*t
, struct karg
*args
)
451 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
452 wl_show(t
, args
, "Plugin White List", &pl_wl
);
458 pl_cmd(struct tab
*t
, struct karg
*args
)
460 if (args
->i
& XT_SHOW
)
461 wl_show(t
, args
, "Plugin White List", &pl_wl
);
462 else if (args
->i
& XT_SAVE
) {
463 args
->i
|= XT_WL_RELOAD
;
464 wl_save(t
, args
, XT_WL_PLUGIN
);
465 } else if (args
->i
& XT_WL_TOGGLE
) {
466 args
->i
|= XT_WL_RELOAD
;
468 } else if (args
->i
& XT_DELETE
)
469 show_oops(t
, "'plugin delete' currently unimplemented");
475 * cancel, remove, etc. downloads
478 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
480 struct download find
, *d
= NULL
;
482 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
484 /* some commands require a valid download id */
485 if (cmd
!= XT_XTP_DL_LIST
) {
486 /* lookup download in question */
488 d
= RB_FIND(download_list
, &downloads
, &find
);
491 show_oops(t
, "%s: no such download", __func__
);
496 /* decide what to do */
498 case XT_XTP_DL_CANCEL
:
499 webkit_download_cancel(d
->download
);
501 case XT_XTP_DL_REMOVE
:
502 webkit_download_cancel(d
->download
); /* just incase */
503 g_object_unref(d
->download
);
504 RB_REMOVE(download_list
, &downloads
, d
);
510 show_oops(t
, "%s: unknown command", __func__
);
513 xtp_page_dl(t
, NULL
);
517 * Actions on history, only does one thing for now, but
518 * we provide the function for future actions
521 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
523 struct history
*h
, *next
;
527 case XT_XTP_HL_REMOVE
:
528 /* walk backwards, as listed in reverse */
529 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
530 next
= RB_PREV(history_list
, &hl
, h
);
532 RB_REMOVE(history_list
, &hl
, h
);
533 g_free((gpointer
) h
->title
);
534 g_free((gpointer
) h
->uri
);
542 /* Nothing - just xtp_page_hl() below */
545 show_oops(t
, "%s: unknown command", __func__
);
549 xtp_page_hl(t
, NULL
);
552 /* remove a favorite */
554 remove_favorite(struct tab
*t
, int index
)
556 char file
[PATH_MAX
], *title
, *uri
= NULL
;
557 char *new_favs
, *tmp
;
563 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
565 if ((f
= fopen(file
, "r")) == NULL
) {
566 show_oops(t
, "%s: can't open favorites: %s",
567 __func__
, strerror(errno
));
571 /* build a string which will become the new favroites file */
572 new_favs
= g_strdup("");
575 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
576 if (feof(f
) || ferror(f
))
578 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
585 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
586 if (feof(f
) || ferror(f
)) {
587 show_oops(t
, "%s: can't parse favorites %s",
588 __func__
, strerror(errno
));
593 /* as long as this isn't the one we are deleting add to file */
596 new_favs
= g_strdup_printf("%s%s\n%s\n",
597 new_favs
, title
, uri
);
609 /* write back new favorites file */
610 if ((f
= fopen(file
, "w")) == NULL
) {
611 show_oops(t
, "%s: can't open favorites: %s",
612 __func__
, strerror(errno
));
616 if (fwrite(new_favs
, strlen(new_favs
), 1, f
) != 1)
617 show_oops(t
, "%s: can't fwrite"); /* shut gcc up */
630 add_favorite(struct tab
*t
, struct karg
*args
)
635 size_t urilen
, linelen
;
636 const gchar
*uri
, *title
;
641 /* don't allow adding of xtp pages to favorites */
642 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
643 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
647 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
648 if ((f
= fopen(file
, "r+")) == NULL
) {
649 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
653 title
= get_title(t
, FALSE
);
656 if (title
== NULL
|| uri
== NULL
) {
657 show_oops(t
, "can't add page to favorites");
661 urilen
= strlen(uri
);
664 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
665 if (feof(f
) || ferror(f
))
668 if (linelen
== urilen
&& !strcmp(line
, uri
))
675 fprintf(f
, "\n%s\n%s", title
, uri
);
681 update_favorite_tabs(NULL
);
687 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
691 /* nothing, just the below call to xtp_page_fl() */
693 case XT_XTP_FL_REMOVE
:
694 remove_favorite(t
, arg
);
697 show_oops(t
, "%s: invalid favorites command", __func__
);
701 xtp_page_fl(t
, NULL
);
705 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
709 /* nothing, just xtp_page_cl() */
711 case XT_XTP_CL_REMOVE
:
715 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
719 xtp_page_cl(t
, NULL
);
722 /* link an XTP class to it's session key and handler function */
723 struct xtp_despatch
{
726 void (*handle_func
)(struct tab
*, uint8_t, int);
729 struct xtp_despatch xtp_despatches
[] = {
730 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
731 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
732 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
733 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
734 { XT_XTP_INVALID
, NULL
, NULL
}
738 * generate a session key to secure xtp commands.
739 * pass in a ptr to the key in question and it will
740 * be modified in place.
743 generate_xtp_session_key(char **key
)
745 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
752 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
753 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
754 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
755 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
757 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
761 xtp_generate_keys(void)
763 /* generate session keys for xtp pages */
764 generate_xtp_session_key(&dl_session_key
);
765 generate_xtp_session_key(&hl_session_key
);
766 generate_xtp_session_key(&cl_session_key
);
767 generate_xtp_session_key(&fl_session_key
);
771 * validate a xtp session key.
775 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
777 if (strcmp(trusted
, untrusted
) != 0) {
778 show_oops(t
, "%s: xtp session key mismatch possible spoof",
787 * is the url xtp protocol? (xxxt://)
788 * if so, parse and despatch correct bahvior
791 parse_xtp_url(struct tab
*t
, const char *url
)
793 char *dup
= NULL
, *p
, *last
= NULL
;
794 uint8_t n_tokens
= 0;
795 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
796 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
801 * tokens array meaning:
803 * tokens[1] = session key
805 * tokens[3] = optional argument
808 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
810 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
813 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
815 /* split out the url */
816 for ((p
= strtok_r(dup
, "/", &last
)); p
;
817 (p
= strtok_r(NULL
, "/", &last
))) {
819 tokens
[n_tokens
++] = p
;
822 /* should be atleast three fields 'class/seskey/command/arg' */
826 dsp
= xtp_despatches
;
827 req_class
= atoi(tokens
[0]);
828 while (dsp
->xtp_class
) {
829 if (dsp
->xtp_class
== req_class
) {
836 /* did we find one atall? */
837 if (dsp_match
== NULL
) {
838 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
842 /* check session key and call despatch function */
843 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
844 ret
= TRUE
; /* all is well, this was a valid xtp request */
845 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
856 * update all favorite tabs apart from one. Pass NULL if
857 * you want to update all.
860 update_favorite_tabs(struct tab
*apart_from
)
863 if (!updating_fl_tabs
) {
864 updating_fl_tabs
= 1; /* stop infinite recursion */
865 TAILQ_FOREACH(t
, &tabs
, entry
)
866 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
867 && (t
!= apart_from
))
868 xtp_page_fl(t
, NULL
);
869 updating_fl_tabs
= 0;
874 * update all download tabs apart from one. Pass NULL if
875 * you want to update all.
878 update_download_tabs(struct tab
*apart_from
)
881 if (!updating_dl_tabs
) {
882 updating_dl_tabs
= 1; /* stop infinite recursion */
883 TAILQ_FOREACH(t
, &tabs
, entry
)
884 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
885 && (t
!= apart_from
))
886 xtp_page_dl(t
, NULL
);
887 updating_dl_tabs
= 0;
892 * update all cookie tabs apart from one. Pass NULL if
893 * you want to update all.
896 update_cookie_tabs(struct tab
*apart_from
)
899 if (!updating_cl_tabs
) {
900 updating_cl_tabs
= 1; /* stop infinite recursion */
901 TAILQ_FOREACH(t
, &tabs
, entry
)
902 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
903 && (t
!= apart_from
))
904 xtp_page_cl(t
, NULL
);
905 updating_cl_tabs
= 0;
910 * update all history tabs apart from one. Pass NULL if
911 * you want to update all.
914 update_history_tabs(struct tab
*apart_from
)
918 if (!updating_hl_tabs
) {
919 updating_hl_tabs
= 1; /* stop infinite recursion */
920 TAILQ_FOREACH(t
, &tabs
, entry
)
921 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
922 && (t
!= apart_from
))
923 xtp_page_hl(t
, NULL
);
924 updating_hl_tabs
= 0;
928 /* show a list of favorites (bookmarks) */
930 xtp_page_fl(struct tab
*t
, struct karg
*args
)
934 char *uri
= NULL
, *title
= NULL
;
935 size_t len
, lineno
= 0;
937 char *body
, *tmp
, *page
= NULL
;
938 const char delim
[3] = {'\\', '\\', '\0'};
940 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
943 warn("%s: bad param", __func__
);
945 /* new session key */
946 if (!updating_fl_tabs
)
947 generate_xtp_session_key(&fl_session_key
);
950 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
951 if ((f
= fopen(file
, "r")) == NULL
) {
952 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
957 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
958 "<th style='width: 40px'>#</th><th>Link</th>"
959 "<th style='width: 40px'>Rm</th></tr>\n");
962 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
964 if (strlen(title
) == 0 || title
[0] == '#') {
970 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
971 if (feof(f
) || ferror(f
)) {
972 show_oops(t
, "favorites file corrupt");
978 body
= g_strdup_printf("%s<tr>"
980 "<td><a href='%s'>%s</a></td>"
981 "<td style='text-align: center'>"
982 "<a href='%s%d/%s/%d/%d'>X</a></td>"
985 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
997 /* if none, say so */
1000 body
= g_strdup_printf("%s<tr>"
1001 "<td colspan='3' style='text-align: center'>"
1002 "No favorites - To add one use the 'favadd' command."
1003 "</td></tr>", body
);
1008 body
= g_strdup_printf("%s</table>", body
);
1018 page
= get_html_page("Favorites", body
, "", 1);
1019 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
1023 update_favorite_tabs(t
);
1032 * Return a new string with a download row (in html)
1033 * appended. Old string is freed.
1036 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
1039 WebKitDownloadStatus stat
;
1040 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
1042 char cur_sz
[FMT_SCALED_STRSIZE
];
1043 char tot_sz
[FMT_SCALED_STRSIZE
];
1046 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
1048 /* All actions wil take this form:
1049 * xxxt://class/seskey
1051 xtp_prefix
= g_strdup_printf("%s%d/%s/",
1052 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
1054 stat
= webkit_download_get_status(dl
->download
);
1057 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
1058 status_html
= g_strdup_printf("Finished");
1059 cmd_html
= g_strdup_printf(
1060 "<a href='%s%d/%d'>Remove</a>",
1061 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
1063 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
1064 /* gather size info */
1065 progress
= 100 * webkit_download_get_progress(dl
->download
);
1068 webkit_download_get_current_size(dl
->download
), cur_sz
);
1070 webkit_download_get_total_size(dl
->download
), tot_sz
);
1072 status_html
= g_strdup_printf(
1073 "<div style='width: 100%%' align='center'>"
1074 "<div class='progress-outer'>"
1075 "<div class='progress-inner' style='width: %.2f%%'>"
1076 "</div></div></div>"
1077 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
1078 progress
, cur_sz
, tot_sz
, progress
);
1080 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1081 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
1085 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
1086 status_html
= g_strdup_printf("Cancelled");
1087 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
1088 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
1090 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
1091 status_html
= g_strdup_printf("Error!");
1092 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
1093 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
1095 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
1096 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1097 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
1098 status_html
= g_strdup_printf("Starting");
1101 show_oops(t
, "%s: unknown download status", __func__
);
1104 new_html
= g_strdup_printf(
1105 "%s\n<tr><td>%s</td><td>%s</td>"
1106 "<td style='text-align:center'>%s</td></tr>\n",
1107 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
1108 status_html
, cmd_html
);
1112 g_free(status_html
);
1122 /* cookie management XTP page */
1124 xtp_page_cl(struct tab
*t
, struct karg
*args
)
1126 char *body
, *page
, *tmp
;
1127 int i
= 1; /* all ids start 1 */
1128 GSList
*sc
, *pc
, *pc_start
;
1130 char *type
, *table_headers
, *last_domain
;
1132 DNPRINTF(XT_D_CMD
, "%s", __func__
);
1135 show_oops(NULL
, "%s invalid parameters", __func__
);
1139 /* Generate a new session key */
1140 if (!updating_cl_tabs
)
1141 generate_xtp_session_key(&cl_session_key
);
1144 table_headers
= g_strdup_printf("<table><tr>"
1147 "<th style='width:200px'>Value</th>"
1151 "<th>HTTP<br />only</th>"
1152 "<th style='width:40px'>Rm</th></tr>\n");
1154 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
1155 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
1159 last_domain
= strdup("");
1160 for (; sc
; sc
= sc
->next
) {
1163 if (strcmp(last_domain
, c
->domain
) != 0) {
1166 last_domain
= strdup(c
->domain
);
1170 body
= g_strdup_printf("%s</table>"
1172 body
, c
->domain
, table_headers
);
1176 body
= g_strdup_printf("<h2>%s</h2>%s\n",
1177 c
->domain
, table_headers
);
1182 for (pc
= pc_start
; pc
; pc
= pc
->next
)
1183 if (soup_cookie_equal(pc
->data
, c
)) {
1184 type
= "Session + Persistent";
1189 body
= g_strdup_printf(
1192 "<td style='word-wrap:normal'>%s</td>"
1194 " <textarea rows='4'>%s</textarea>"
1200 "<td style='text-align:center'>"
1201 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1208 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
1223 soup_cookies_free(sc
);
1224 soup_cookies_free(pc
);
1226 /* small message if there are none */
1228 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
1229 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
1232 body
= g_strdup_printf("%s</table>", body
);
1235 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
1237 g_free(table_headers
);
1238 g_free(last_domain
);
1240 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
1241 update_cookie_tabs(t
);
1249 xtp_page_hl(struct tab
*t
, struct karg
*args
)
1251 char *body
, *page
, *tmp
;
1253 int i
= 1; /* all ids start 1 */
1255 DNPRINTF(XT_D_CMD
, "%s", __func__
);
1258 show_oops(NULL
, "%s invalid parameters", __func__
);
1262 /* Generate a new session key */
1263 if (!updating_hl_tabs
)
1264 generate_xtp_session_key(&hl_session_key
);
1267 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
1268 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
1270 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1272 body
= g_strdup_printf(
1274 "<td><a href='%s'>%s</a></td>"
1276 "<td style='text-align: center'>"
1277 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1278 body
, h
->uri
, h
->uri
, h
->title
,
1279 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
1280 XT_XTP_HL_REMOVE
, i
);
1286 /* small message if there are none */
1289 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
1290 "colspan='3'>No History</td></tr>\n", body
);
1295 body
= g_strdup_printf("%s</table>", body
);
1298 page
= get_html_page("History", body
, "", TRUE
);
1302 * update all history manager tabs as the xtp session
1303 * key has now changed. No need to update the current tab.
1304 * Already did that above.
1306 update_history_tabs(t
);
1308 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
1315 * Generate a web page detailing the status of any downloads
1318 xtp_page_dl(struct tab
*t
, struct karg
*args
)
1320 struct download
*dl
;
1321 char *body
, *page
, *tmp
;
1325 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
1328 show_oops(NULL
, "%s invalid parameters", __func__
);
1333 * Generate a new session key for next page instance.
1334 * This only happens for the top level call to xtp_page_dl()
1335 * in which case updating_dl_tabs is 0.
1337 if (!updating_dl_tabs
)
1338 generate_xtp_session_key(&dl_session_key
);
1340 /* header - with refresh so as to update */
1341 if (refresh_interval
>= 1)
1342 ref
= g_strdup_printf(
1343 "<meta http-equiv='refresh' content='%u"
1344 ";url=%s%d/%s/%d' />\n",
1353 body
= g_strdup_printf("<div align='center'>"
1354 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
1355 "</p><table><tr><th style='width: 60%%'>"
1356 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
1357 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
1359 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
1360 body
= xtp_page_dl_row(t
, body
, dl
);
1364 /* message if no downloads in list */
1367 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
1368 " style='text-align: center'>"
1369 "No downloads</td></tr>\n", body
);
1374 body
= g_strdup_printf("%s</table></div>", body
);
1377 page
= get_html_page("Downloads", body
, ref
, 1);
1382 * update all download manager tabs as the xtp session
1383 * key has now changed. No need to update the current tab.
1384 * Already did that above.
1386 update_download_tabs(t
);
1388 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
1395 startpage(struct tab
*t
, struct karg
*args
)
1397 char *page
, *body
, *b
;
1401 show_oops(NULL
, "startpage invalid parameters");
1403 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
1405 TAILQ_FOREACH(s
, &spl
, entry
) {
1407 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
1411 page
= get_html_page("Startup Exception", body
, "", 0);
1414 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
1421 startpage_add(const char *fmt
, ...)
1431 if (vasprintf(&msg
, fmt
, ap
) == -1)
1432 errx(1, "startpage_add failed");
1435 s
= g_malloc0(sizeof *s
);
1438 TAILQ_INSERT_TAIL(&spl
, s
, entry
);