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)
73 #define XT_XTP_DL_UNLINK (4)
74 #define XT_XTP_DL_START (5)
76 /* XTP history actions */
77 #define XT_XTP_HL_LIST (1)
78 #define XT_XTP_HL_REMOVE (2)
80 /* XTP cookie actions */
81 #define XT_XTP_CL_LIST (1)
82 #define XT_XTP_CL_REMOVE (2)
83 #define XT_XTP_CL_REMOVE_DOMAIN (3)
85 /* XTP cookie actions */
86 #define XT_XTP_FL_LIST (1)
87 #define XT_XTP_FL_REMOVE (2)
89 int js_show_wl(struct tab
*, struct karg
*);
90 int pl_show_wl(struct tab
*, struct karg
*);
91 int set(struct tab
*, struct karg
*);
92 int marco(struct tab
*, struct karg
*);
93 int startpage(struct tab
*, struct karg
*);
94 const char * marco_message(int *);
96 struct about_type about_list
[] = {
97 { XT_URI_ABOUT_ABOUT
, about
},
98 { XT_URI_ABOUT_BLANK
, blank
},
99 { XT_URI_ABOUT_CERTS
, ca_cmd
},
100 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
101 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
102 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
103 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
104 { XT_URI_ABOUT_HELP
, help
},
105 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
106 { XT_URI_ABOUT_JSWL
, js_show_wl
},
107 { XT_URI_ABOUT_SET
, set
},
108 { XT_URI_ABOUT_STATS
, stats
},
109 { XT_URI_ABOUT_MARCO
, marco
},
110 { XT_URI_ABOUT_STARTPAGE
, startpage
},
111 { XT_URI_ABOUT_PLUGINWL
, pl_show_wl
},
116 * We use these to prevent people putting xxxt:// URLs on
117 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
119 #define XT_XTP_SES_KEY_SZ 8
120 #define XT_XTP_SES_KEY_HEX_FMT \
121 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
122 char *dl_session_key
; /* downloads */
123 char *hl_session_key
; /* history list */
124 char *cl_session_key
; /* cookie list */
125 char *fl_session_key
; /* favorites list */
127 int updating_fl_tabs
= 0;
128 int updating_dl_tabs
= 0;
129 int updating_hl_tabs
= 0;
130 int updating_cl_tabs
= 0;
131 struct download_list downloads
;
134 about_list_size(void)
136 return (LENGTH(about_list
));
140 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
144 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
146 "<title>%s</title>\n"
155 addstyles
? XT_PAGE_STYLE
: "",
164 * Display a web page from a HTML string in memory, rather than from a URL
167 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
172 /* we set this to indicate we want to manually do navaction */
174 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
176 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
178 /* set t->xtp_meaning */
179 for (i
= 0; i
< LENGTH(about_list
); i
++)
180 if (!strcmp(title
, about_list
[i
].name
)) {
185 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
187 #if GTK_CHECK_VERSION(2, 20, 0)
188 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
189 gtk_widget_hide(t
->spinner
);
191 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
192 xt_icon_from_file(t
, file
);
197 blank(struct tab
*t
, struct karg
*args
)
200 show_oops(NULL
, "blank invalid parameters");
202 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
208 about(struct tab
*t
, struct karg
*args
)
213 show_oops(NULL
, "about invalid parameters");
215 body
= g_strdup_printf("<b>Version: %s</b>"
216 #ifdef XXXTERM_BUILDSTR
217 "<br><b>Build: %s</b>"
222 "<li>Marco Peereboom <marco@peereboom.us></li>"
223 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
224 "<li>Edd Barrett <vext01@gmail.com></li>"
225 "<li>Todd T. Fries <todd@fries.net></li>"
226 "<li>Raphael Graf <r@undefined.ch></li>"
227 "<li>Michal Mazurek <akfaew@jasminek.net></li>"
229 "Copyrights and licenses can be found on the XXXTerm "
230 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
232 #ifdef XXXTERM_BUILDSTR
233 version
, XXXTERM_BUILDSTR
239 page
= get_html_page("About", body
, "", 0);
242 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
249 help(struct tab
*t
, struct karg
*args
)
251 char *page
, *head
, *body
;
254 show_oops(NULL
, "help invalid parameters");
256 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
257 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
259 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
260 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
261 "cgi-bin/man-cgi?xxxterm</a>";
263 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
265 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
272 stats(struct tab
*t
, struct karg
*args
)
274 char *page
, *body
, *s
, line
[64 * 1024];
275 long long unsigned int line_count
= 0;
279 show_oops(NULL
, "stats invalid parameters");
282 if (save_rejected_cookies
) {
283 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
285 s
= fgets(line
, sizeof line
, r_cookie_f
);
286 if (s
== NULL
|| feof(r_cookie_f
) ||
292 snprintf(line
, sizeof line
,
293 "<br/>Cookies blocked(*) total: %llu", line_count
);
295 show_oops(t
, "Can't open blocked cookies file: %s",
299 body
= g_strdup_printf(
300 "Cookies blocked(*) this session: %llu"
302 "<p><small><b>*</b> results vary based on settings</small></p>",
306 page
= get_html_page("Statistics", body
, "", 0);
309 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
316 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
317 size_t cert_count
, char *title
)
319 gnutls_datum_t cinfo
;
325 for (i
= 0; i
< cert_count
; i
++) {
326 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
331 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
332 body
, i
, cinfo
.data
);
333 gnutls_free(cinfo
.data
);
337 tmp
= get_html_page(title
, body
, "", 0);
340 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
345 ca_cmd(struct tab
*t
, struct karg
*args
)
348 int rv
= 1, certs
= 0, certs_read
;
351 gnutls_x509_crt_t
*c
= NULL
;
352 char *certs_buf
= NULL
, *s
;
354 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
355 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
359 if (fstat(fileno(f
), &sb
) == -1) {
360 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
364 certs_buf
= g_malloc(sb
.st_size
+ 1);
365 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
366 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
369 certs_buf
[sb
.st_size
] = '\0';
372 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
374 s
+= strlen("BEGIN CERTIFICATE");
377 bzero(&dt
, sizeof dt
);
378 dt
.data
= (unsigned char *)certs_buf
;
379 dt
.size
= sb
.st_size
;
380 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
381 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
382 GNUTLS_X509_FMT_PEM
, 0);
383 if (certs_read
<= 0) {
384 show_oops(t
, "No cert(s) available");
387 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
400 cookie_show_wl(struct tab
*t
, struct karg
*args
)
402 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
403 wl_show(t
, args
, "Cookie White List", &c_wl
);
409 js_show_wl(struct tab
*t
, struct karg
*args
)
411 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
412 wl_show(t
, args
, "JavaScript White List", &js_wl
);
418 cookie_cmd(struct tab
*t
, struct karg
*args
)
420 if (args
->i
& XT_SHOW
)
421 wl_show(t
, args
, "Cookie White List", &c_wl
);
422 else if (args
->i
& XT_WL_TOGGLE
) {
423 args
->i
|= XT_WL_RELOAD
;
425 } else if (args
->i
& XT_SAVE
) {
426 args
->i
|= XT_WL_RELOAD
;
427 wl_save(t
, args
, XT_WL_COOKIE
);
428 } else if (args
->i
& XT_DELETE
)
429 show_oops(t
, "'cookie delete' currently unimplemented");
435 js_cmd(struct tab
*t
, struct karg
*args
)
437 if (args
->i
& XT_SHOW
)
438 wl_show(t
, args
, "JavaScript White List", &js_wl
);
439 else if (args
->i
& XT_SAVE
) {
440 args
->i
|= XT_WL_RELOAD
;
441 wl_save(t
, args
, XT_WL_JAVASCRIPT
);
442 } else if (args
->i
& XT_WL_TOGGLE
) {
443 args
->i
|= XT_WL_RELOAD
;
445 } else if (args
->i
& XT_DELETE
)
446 show_oops(t
, "'js delete' currently unimplemented");
452 pl_show_wl(struct tab
*t
, struct karg
*args
)
454 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
455 wl_show(t
, args
, "Plugin White List", &pl_wl
);
461 pl_cmd(struct tab
*t
, struct karg
*args
)
463 if (args
->i
& XT_SHOW
)
464 wl_show(t
, args
, "Plugin White List", &pl_wl
);
465 else if (args
->i
& XT_SAVE
) {
466 args
->i
|= XT_WL_RELOAD
;
467 wl_save(t
, args
, XT_WL_PLUGIN
);
468 } else if (args
->i
& XT_WL_TOGGLE
) {
469 args
->i
|= XT_WL_RELOAD
;
471 } else if (args
->i
& XT_DELETE
)
472 show_oops(t
, "'plugin delete' currently unimplemented");
478 * cancel, remove, etc. downloads
481 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
483 struct download find
, *d
= NULL
;
485 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
487 /* some commands require a valid download id */
488 if (cmd
!= XT_XTP_DL_LIST
) {
489 /* lookup download in question */
491 d
= RB_FIND(download_list
, &downloads
, &find
);
494 show_oops(t
, "%s: no such download", __func__
);
499 /* decide what to do */
501 case XT_XTP_DL_START
:
502 /* our downloads always needs to be
503 * restarted if called from here
505 download_start(t
, d
, XT_DL_RESTART
);
507 case XT_XTP_DL_CANCEL
:
508 webkit_download_cancel(d
->download
);
509 g_object_unref(d
->download
);
510 RB_REMOVE(download_list
, &downloads
, d
);
512 case XT_XTP_DL_UNLINK
:
513 unlink(webkit_download_get_destination_uri(d
->download
) +
516 case XT_XTP_DL_REMOVE
:
517 webkit_download_cancel(d
->download
); /* just incase */
518 g_object_unref(d
->download
);
519 RB_REMOVE(download_list
, &downloads
, d
);
525 show_oops(t
, "%s: unknown command", __func__
);
528 xtp_page_dl(t
, NULL
);
532 * Actions on history, only does one thing for now, but
533 * we provide the function for future actions
536 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
538 struct history
*h
, *next
;
542 case XT_XTP_HL_REMOVE
:
543 /* walk backwards, as listed in reverse */
544 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
545 next
= RB_PREV(history_list
, &hl
, h
);
547 RB_REMOVE(history_list
, &hl
, h
);
548 g_free((gpointer
) h
->title
);
549 g_free((gpointer
) h
->uri
);
557 /* Nothing - just xtp_page_hl() below */
560 show_oops(t
, "%s: unknown command", __func__
);
564 xtp_page_hl(t
, NULL
);
567 /* remove a favorite */
569 remove_favorite(struct tab
*t
, int index
)
571 char file
[PATH_MAX
], *title
, *uri
= NULL
;
572 char *new_favs
, *tmp
;
578 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
580 if ((f
= fopen(file
, "r")) == NULL
) {
581 show_oops(t
, "%s: can't open favorites: %s",
582 __func__
, strerror(errno
));
586 /* build a string which will become the new favroites file */
587 new_favs
= g_strdup("");
590 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
591 if (feof(f
) || ferror(f
))
593 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
600 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
601 if (feof(f
) || ferror(f
)) {
602 show_oops(t
, "%s: can't parse favorites %s",
603 __func__
, strerror(errno
));
608 /* as long as this isn't the one we are deleting add to file */
611 new_favs
= g_strdup_printf("%s%s\n%s\n",
612 new_favs
, title
, uri
);
624 /* write back new favorites file */
625 if ((f
= fopen(file
, "w")) == NULL
) {
626 show_oops(t
, "%s: can't open favorites: %s",
627 __func__
, strerror(errno
));
631 if (fwrite(new_favs
, strlen(new_favs
), 1, f
) != 1)
632 show_oops(t
, "%s: can't fwrite", __func__
);
645 add_favorite(struct tab
*t
, struct karg
*args
)
650 size_t urilen
, linelen
;
651 const gchar
*uri
, *title
;
656 /* don't allow adding of xtp pages to favorites */
657 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
658 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
662 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
663 if ((f
= fopen(file
, "r+")) == NULL
) {
664 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
668 title
= get_title(t
, FALSE
);
671 if (title
== NULL
|| uri
== NULL
) {
672 show_oops(t
, "can't add page to favorites");
676 urilen
= strlen(uri
);
679 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
680 if (feof(f
) || ferror(f
))
683 if (linelen
== urilen
&& !strcmp(line
, uri
))
690 fprintf(f
, "\n%s\n%s", title
, uri
);
696 update_favorite_tabs(NULL
);
702 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
706 /* nothing, just the below call to xtp_page_fl() */
708 case XT_XTP_FL_REMOVE
:
709 remove_favorite(t
, arg
);
712 show_oops(t
, "%s: invalid favorites command", __func__
);
716 xtp_page_fl(t
, NULL
);
720 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
724 /* nothing, just xtp_page_cl() */
726 case XT_XTP_CL_REMOVE
:
729 case XT_XTP_CL_REMOVE_DOMAIN
:
730 remove_cookie_domain(arg
);
733 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
737 xtp_page_cl(t
, NULL
);
740 /* link an XTP class to it's session key and handler function */
741 struct xtp_despatch
{
744 void (*handle_func
)(struct tab
*, uint8_t, int);
747 struct xtp_despatch xtp_despatches
[] = {
748 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
749 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
750 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
751 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
752 { XT_XTP_INVALID
, NULL
, NULL
}
756 * generate a session key to secure xtp commands.
757 * pass in a ptr to the key in question and it will
758 * be modified in place.
761 generate_xtp_session_key(char **key
)
763 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
770 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
771 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
772 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
773 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
775 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
779 xtp_generate_keys(void)
781 /* generate session keys for xtp pages */
782 generate_xtp_session_key(&dl_session_key
);
783 generate_xtp_session_key(&hl_session_key
);
784 generate_xtp_session_key(&cl_session_key
);
785 generate_xtp_session_key(&fl_session_key
);
789 * validate a xtp session key.
793 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
795 if (strcmp(trusted
, untrusted
) != 0) {
796 show_oops(t
, "%s: xtp session key mismatch possible spoof",
805 * is the url xtp protocol? (xxxt://)
806 * if so, parse and despatch correct bahvior
809 parse_xtp_url(struct tab
*t
, const char *url
)
811 char *dup
= NULL
, *p
, *last
= NULL
;
812 uint8_t n_tokens
= 0;
813 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
814 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
819 * tokens array meaning:
821 * tokens[1] = session key
823 * tokens[3] = optional argument
826 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
828 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
831 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
833 /* split out the url */
834 for ((p
= strtok_r(dup
, "/", &last
)); p
;
835 (p
= strtok_r(NULL
, "/", &last
))) {
837 tokens
[n_tokens
++] = p
;
840 /* should be atleast three fields 'class/seskey/command/arg' */
844 dsp
= xtp_despatches
;
845 req_class
= atoi(tokens
[0]);
846 while (dsp
->xtp_class
) {
847 if (dsp
->xtp_class
== req_class
) {
854 /* did we find one atall? */
855 if (dsp_match
== NULL
) {
856 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
860 /* check session key and call despatch function */
861 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
862 ret
= TRUE
; /* all is well, this was a valid xtp request */
863 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
874 * update all favorite tabs apart from one. Pass NULL if
875 * you want to update all.
878 update_favorite_tabs(struct tab
*apart_from
)
881 if (!updating_fl_tabs
) {
882 updating_fl_tabs
= 1; /* stop infinite recursion */
883 TAILQ_FOREACH(t
, &tabs
, entry
)
884 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
885 && (t
!= apart_from
))
886 xtp_page_fl(t
, NULL
);
887 updating_fl_tabs
= 0;
892 * update all download tabs apart from one. Pass NULL if
893 * you want to update all.
896 update_download_tabs(struct tab
*apart_from
)
899 if (!updating_dl_tabs
) {
900 updating_dl_tabs
= 1; /* stop infinite recursion */
901 TAILQ_FOREACH(t
, &tabs
, entry
)
902 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
903 && (t
!= apart_from
))
904 xtp_page_dl(t
, NULL
);
905 updating_dl_tabs
= 0;
910 * update all cookie tabs apart from one. Pass NULL if
911 * you want to update all.
914 update_cookie_tabs(struct tab
*apart_from
)
917 if (!updating_cl_tabs
) {
918 updating_cl_tabs
= 1; /* stop infinite recursion */
919 TAILQ_FOREACH(t
, &tabs
, entry
)
920 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
921 && (t
!= apart_from
))
922 xtp_page_cl(t
, NULL
);
923 updating_cl_tabs
= 0;
928 * update all history tabs apart from one. Pass NULL if
929 * you want to update all.
932 update_history_tabs(struct tab
*apart_from
)
936 if (!updating_hl_tabs
) {
937 updating_hl_tabs
= 1; /* stop infinite recursion */
938 TAILQ_FOREACH(t
, &tabs
, entry
)
939 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
940 && (t
!= apart_from
))
941 xtp_page_hl(t
, NULL
);
942 updating_hl_tabs
= 0;
946 /* show a list of favorites (bookmarks) */
948 xtp_page_fl(struct tab
*t
, struct karg
*args
)
952 char *uri
= NULL
, *title
= NULL
;
953 size_t len
, lineno
= 0;
955 char *body
, *tmp
, *page
= NULL
;
956 const char delim
[3] = {'\\', '\\', '\0'};
958 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
961 warn("%s: bad param", __func__
);
963 /* new session key */
964 if (!updating_fl_tabs
)
965 generate_xtp_session_key(&fl_session_key
);
968 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
969 if ((f
= fopen(file
, "r")) == NULL
) {
970 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
975 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
976 "<th style='width: 40px'>#</th><th>Link</th>"
977 "<th style='width: 40px'>Rm</th></tr>\n");
980 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
982 if (strlen(title
) == 0) {
988 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
989 if (feof(f
) || ferror(f
)) {
990 show_oops(t
, "favorites file corrupt");
996 body
= g_strdup_printf("%s<tr>"
998 "<td><a href='%s'>%s</a></td>"
999 "<td style='text-align: center'>"
1000 "<a href='%s%d/%s/%d/%d'>X</a></td>"
1002 body
, i
, uri
, title
,
1003 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
1015 /* if none, say so */
1018 body
= g_strdup_printf("%s<tr>"
1019 "<td colspan='3' style='text-align: center'>"
1020 "No favorites - To add one use the 'favadd' command."
1021 "</td></tr>", body
);
1026 body
= g_strdup_printf("%s</table>", body
);
1036 page
= get_html_page("Favorites", body
, "", 1);
1037 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
1041 update_favorite_tabs(t
);
1050 * Return a new string with a download row (in html)
1051 * appended. Old string is freed.
1054 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
1057 WebKitDownloadStatus stat
;
1058 const gchar
*destination
;
1059 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
1061 char cur_sz
[FMT_SCALED_STRSIZE
];
1062 char tot_sz
[FMT_SCALED_STRSIZE
];
1065 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
1067 /* All actions wil take this form:
1068 * xxxt://class/seskey
1070 xtp_prefix
= g_strdup_printf("%s%d/%s/",
1071 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
1073 stat
= webkit_download_get_status(dl
->download
);
1076 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
1077 status_html
= g_strdup_printf("Finished");
1078 cmd_html
= g_strdup_printf(
1079 "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1080 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
, xtp_prefix
,
1081 XT_XTP_DL_UNLINK
, dl
->id
);
1083 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
1084 /* gather size info */
1085 progress
= 100 * webkit_download_get_progress(dl
->download
);
1088 webkit_download_get_current_size(dl
->download
), cur_sz
);
1090 webkit_download_get_total_size(dl
->download
), tot_sz
);
1092 status_html
= g_strdup_printf(
1093 "<div style='width: 100%%' align='center'>"
1094 "<div class='progress-outer'>"
1095 "<div class='progress-inner' style='width: %.2f%%'>"
1096 "</div></div></div>"
1097 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
1098 progress
, cur_sz
, tot_sz
, progress
);
1100 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1101 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
1105 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
1106 status_html
= g_strdup_printf("Cancelled");
1107 cmd_html
= g_strdup_printf(
1108 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1109 xtp_prefix
, XT_XTP_DL_START
, dl
->id
,
1110 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
, xtp_prefix
,
1111 XT_XTP_DL_UNLINK
, dl
->id
);
1113 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
1114 status_html
= g_strdup_printf("Error!");
1115 cmd_html
= g_strdup_printf(
1116 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1117 xtp_prefix
, XT_XTP_DL_START
, dl
->id
,
1118 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
, xtp_prefix
,
1119 XT_XTP_DL_UNLINK
, dl
->id
);
1121 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
1122 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Start</a> / <a href='%s%d/%d'>Cancel</a>",
1123 xtp_prefix
, XT_XTP_DL_START
, dl
->id
, xtp_prefix
,
1124 XT_XTP_DL_CANCEL
, dl
->id
);
1125 status_html
= g_strdup_printf("Created");
1128 show_oops(t
, "%s: unknown download status", __func__
);
1131 destination
= webkit_download_get_destination_uri(dl
->download
);
1132 /* we might not have a destination set yet */
1134 destination
= webkit_download_get_suggested_filename(dl
->download
);
1135 new_html
= g_strdup_printf(
1136 "%s\n<tr><td>%s</td><td>%s</td>"
1137 "<td style='text-align:center'>%s</td></tr>\n",
1138 html
, basename((char *)destination
),
1139 status_html
, cmd_html
);
1143 g_free(status_html
);
1153 /* cookie management XTP page */
1155 xtp_page_cl(struct tab
*t
, struct karg
*args
)
1157 char *body
, *page
, *tmp
;
1158 int i
= 1; /* all ids start 1 */
1160 GSList
*sc
, *pc
, *pc_start
;
1162 char *type
, *table_headers
, *last_domain
;
1164 DNPRINTF(XT_D_CMD
, "%s", __func__
);
1167 show_oops(NULL
, "%s invalid parameters", __func__
);
1171 /* Generate a new session key */
1172 if (!updating_cl_tabs
)
1173 generate_xtp_session_key(&cl_session_key
);
1176 table_headers
= g_strdup_printf("<table><tr>"
1179 "<th style='width:200px'>Value</th>"
1183 "<th>HTTP<br />only</th>"
1184 "<th style='width:40px'>Rm</th></tr>\n");
1186 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
1187 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
1191 last_domain
= strdup("");
1192 for (; sc
; sc
= sc
->next
) {
1195 if (strcmp(last_domain
, c
->domain
) != 0) {
1199 last_domain
= strdup(c
->domain
);
1203 body
= g_strdup_printf("%s</table>"
1205 "<a href='%s%d/%s/%d/%d'>remove all</a>%s\n",
1207 XT_XTP_STR
, XT_XTP_CL
,
1208 cl_session_key
, XT_XTP_CL_REMOVE_DOMAIN
, domain_id
,
1213 body
= g_strdup_printf("<h2>%s</h2>"
1214 "<a href='%s%d/%s/%d/%d'>remove all</a>%s\n",
1216 XT_XTP_STR
, XT_XTP_CL
,
1217 cl_session_key
, XT_XTP_CL_REMOVE_DOMAIN
, domain_id
,
1223 for (pc
= pc_start
; pc
; pc
= pc
->next
)
1224 if (soup_cookie_equal(pc
->data
, c
)) {
1225 type
= "Session + Persistent";
1230 body
= g_strdup_printf(
1233 "<td style='word-wrap:normal'>%s</td>"
1235 " <textarea rows='4'>%s</textarea>"
1241 "<td style='text-align:center'>"
1242 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1249 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
1264 soup_cookies_free(sc
);
1265 soup_cookies_free(pc
);
1267 /* small message if there are none */
1269 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
1270 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
1273 body
= g_strdup_printf("%s</table>", body
);
1276 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
1278 g_free(table_headers
);
1279 g_free(last_domain
);
1281 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
1282 update_cookie_tabs(t
);
1290 xtp_page_hl(struct tab
*t
, struct karg
*args
)
1292 char *body
, *page
, *tmp
;
1294 int i
= 1; /* all ids start 1 */
1296 DNPRINTF(XT_D_CMD
, "%s", __func__
);
1299 show_oops(NULL
, "%s invalid parameters", __func__
);
1303 /* Generate a new session key */
1304 if (!updating_hl_tabs
)
1305 generate_xtp_session_key(&hl_session_key
);
1308 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
1309 "<th>URI</th><th>Title</th><th>Last visited</th><th style='width: 40px'>Rm</th></tr>\n");
1311 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1313 body
= g_strdup_printf(
1315 "<td><a href='%s'>%s</a></td>"
1318 "<td style='text-align: center'>"
1319 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1320 body
, h
->uri
, h
->uri
, h
->title
, ctime(&h
->time
),
1321 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
1322 XT_XTP_HL_REMOVE
, i
);
1328 /* small message if there are none */
1331 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
1332 "colspan='3'>No History</td></tr>\n", body
);
1337 body
= g_strdup_printf("%s</table>", body
);
1340 page
= get_html_page("History", body
, "", TRUE
);
1344 * update all history manager tabs as the xtp session
1345 * key has now changed. No need to update the current tab.
1346 * Already did that above.
1348 update_history_tabs(t
);
1350 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
1357 * Generate a web page detailing the status of any downloads
1360 xtp_page_dl(struct tab
*t
, struct karg
*args
)
1362 struct download
*dl
;
1363 char *body
, *page
, *tmp
;
1367 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
1370 show_oops(NULL
, "%s invalid parameters", __func__
);
1375 * Generate a new session key for next page instance.
1376 * This only happens for the top level call to xtp_page_dl()
1377 * in which case updating_dl_tabs is 0.
1379 if (!updating_dl_tabs
)
1380 generate_xtp_session_key(&dl_session_key
);
1382 /* header - with refresh so as to update */
1383 if (refresh_interval
>= 1)
1384 ref
= g_strdup_printf(
1385 "<meta http-equiv='refresh' content='%u"
1386 ";url=%s%d/%s/%d' />\n",
1395 body
= g_strdup_printf("<div align='center'>"
1396 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
1397 "</p><table><tr><th style='width: 60%%'>"
1398 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
1399 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
1401 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
1402 body
= xtp_page_dl_row(t
, body
, dl
);
1406 /* message if no downloads in list */
1409 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
1410 " style='text-align: center'>"
1411 "No downloads</td></tr>\n", body
);
1416 body
= g_strdup_printf("%s</table></div>", body
);
1419 page
= get_html_page("Downloads", body
, ref
, 1);
1424 * update all download manager tabs as the xtp session
1425 * key has now changed. No need to update the current tab.
1426 * Already did that above.
1428 update_download_tabs(t
);
1430 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
1437 startpage(struct tab
*t
, struct karg
*args
)
1439 char *page
, *body
, *b
;
1443 show_oops(NULL
, "startpage invalid parameters");
1445 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
1447 TAILQ_FOREACH(s
, &spl
, entry
) {
1449 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
1453 page
= get_html_page("Startup Exception", body
, "", 0);
1456 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
1463 startpage_add(const char *fmt
, ...)
1473 if (vasprintf(&msg
, fmt
, ap
) == -1)
1474 errx(1, "startpage_add failed");
1477 s
= g_malloc0(sizeof *s
);
1480 TAILQ_INSERT_TAIL(&spl
, s
, entry
);