Prepare for release 1.9.0.
[xxxterm.git] / about.c
blob744a870907b2a2379759e4ec9584023d057d7ef2
1 /*
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.
22 #include "xxxterm.h"
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" \
51 ".progress-outer{" \
52 "border: 1px solid black;" \
53 " height: 8px;" \
54 " width: 90%%}\n" \
55 ".progress-inner{float: left;" \
56 " height: 8px;" \
57 " background: green}\n" \
58 ".dlstatus{font-size: small;" \
59 " text-align: center}\n" \
60 "</style>\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 },
112 * Session IDs.
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;
130 size_t
131 about_list_size(void)
133 return (LENGTH(about_list));
136 gchar *
137 get_html_page(gchar *title, gchar *body, gchar *head, bool addstyles)
139 gchar *r;
141 r = g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
142 "<head>\n"
143 "<title>%s</title>\n"
144 "%s"
145 "%s"
146 "</head>\n"
147 "<body>\n"
148 "<h1>%s</h1>\n"
149 "%s\n</body>\n"
150 "</html>",
151 title,
152 addstyles ? XT_PAGE_STYLE : "",
153 head,
154 title,
155 body);
157 return (r);
161 * Display a web page from a HTML string in memory, rather than from a URL
163 void
164 load_webkit_string(struct tab *t, const char *str, gchar *title)
166 char file[PATH_MAX];
167 int i;
169 /* we set this to indicate we want to manually do navaction */
170 if (t->bfl)
171 t->item = webkit_web_back_forward_list_get_current_item(t->bfl);
173 t->xtp_meaning = XT_XTP_TAB_MEANING_NORMAL;
174 if (title) {
175 /* set t->xtp_meaning */
176 for (i = 0; i < LENGTH(about_list); i++)
177 if (!strcmp(title, about_list[i].name)) {
178 t->xtp_meaning = i;
179 break;
182 webkit_web_view_load_string(t->wv, str, NULL, encoding,
183 "file://");
184 #if GTK_CHECK_VERSION(2, 20, 0)
185 gtk_spinner_stop(GTK_SPINNER(t->spinner));
186 gtk_widget_hide(t->spinner);
187 #endif
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)
196 if (t == NULL)
197 show_oops(NULL, "blank invalid parameters");
199 load_webkit_string(t, "", XT_URI_ABOUT_BLANK);
201 return (0);
205 about(struct tab *t, struct karg *args)
207 char *page, *body;
209 if (t == NULL)
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>"
215 #endif
216 "<p>"
217 "Authors:"
218 "<ul>"
219 "<li>Marco Peereboom &lt;marco@peereboom.us&gt;</li>"
220 "<li>Stevan Andjelkovic &lt;stevan@student.chalmers.se&gt;</li>"
221 "<li>Edd Barrett &lt;vext01@gmail.com&gt;</li>"
222 "<li>Todd T. Fries &lt;todd@fries.net&gt;</li>"
223 "<li>Raphael Graf &lt;r@undefined.ch&gt;</li>"
224 "<li>Michal Mazurek &lt;akfaew@jasminek.net&gt;</li>"
225 "</ul>"
226 "Copyrights and licenses can be found on the XXXTerm "
227 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
228 "</p>",
229 #ifdef XXXTERM_BUILDSTR
230 version, XXXTERM_BUILDSTR
231 #else
232 version
233 #endif
236 page = get_html_page("About", body, "", 0);
237 g_free(body);
239 load_webkit_string(t, page, XT_URI_ABOUT_ABOUT);
240 g_free(page);
242 return (0);
246 help(struct tab *t, struct karg *args)
248 char *page, *head, *body;
250 if (t == NULL)
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\">"
255 "</head>\n";
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);
263 g_free(page);
265 return (0);
269 stats(struct tab *t, struct karg *args)
271 char *page, *body, *s, line[64 * 1024];
272 long long unsigned int line_count = 0;
273 FILE *r_cookie_f;
275 if (t == NULL)
276 show_oops(NULL, "stats invalid parameters");
278 line[0] = '\0';
279 if (save_rejected_cookies) {
280 if ((r_cookie_f = fopen(rc_fname, "r"))) {
281 for (;;) {
282 s = fgets(line, sizeof line, r_cookie_f);
283 if (s == NULL || feof(r_cookie_f) ||
284 ferror(r_cookie_f))
285 break;
286 line_count++;
288 fclose(r_cookie_f);
289 snprintf(line, sizeof line,
290 "<br/>Cookies blocked(*) total: %llu", line_count);
291 } else
292 show_oops(t, "Can't open blocked cookies file: %s",
293 strerror(errno));
296 body = g_strdup_printf(
297 "Cookies blocked(*) this session: %llu"
298 "%s"
299 "<p><small><b>*</b> results vary based on settings</small></p>",
300 blocked_cookies,
301 line);
303 page = get_html_page("Statistics", body, "", 0);
304 g_free(body);
306 load_webkit_string(t, page, XT_URI_ABOUT_STATS);
307 g_free(page);
309 return (0);
312 void
313 show_certs(struct tab *t, gnutls_x509_crt_t *certs,
314 size_t cert_count, char *title)
316 gnutls_datum_t cinfo;
317 char *tmp, *body;
318 int i;
320 body = g_strdup("");
322 for (i = 0; i < cert_count; i++) {
323 if (gnutls_x509_crt_print(certs[i], GNUTLS_CRT_PRINT_FULL,
324 &cinfo))
325 return;
327 tmp = body;
328 body = g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
329 body, i, cinfo.data);
330 gnutls_free(cinfo.data);
331 g_free(tmp);
334 tmp = get_html_page(title, body, "", 0);
335 g_free(body);
337 load_webkit_string(t, tmp, XT_URI_ABOUT_CERTS);
338 g_free(tmp);
342 ca_cmd(struct tab *t, struct karg *args)
344 FILE *f = NULL;
345 int rv = 1, certs = 0, certs_read;
346 struct stat sb;
347 gnutls_datum_t dt;
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);
353 return (1);
356 if (fstat(fileno(f), &sb) == -1) {
357 show_oops(t, "Can't stat CA file: %s", ssl_ca_file);
358 goto done;
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));
364 goto done;
366 certs_buf[sb.st_size] = '\0';
368 s = certs_buf;
369 while ((s = strstr(s, "BEGIN CERTIFICATE"))) {
370 certs++;
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");
382 goto done;
384 show_certs(t, c, certs_read, "Certificate Authority Certificates");
385 done:
386 if (c)
387 g_free(c);
388 if (certs_buf)
389 g_free(certs_buf);
390 if (f)
391 fclose(f);
393 return (rv);
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);
402 return (0);
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);
411 return (0);
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;
421 toggle_cwl(t, args);
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");
428 return (0);
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;
441 toggle_js(t, args);
442 } else if (args->i & XT_DELETE)
443 show_oops(t, "'js delete' currently unimplemented");
445 return (0);
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);
454 return (0);
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;
467 toggle_pl(t, args);
468 } else if (args->i & XT_DELETE)
469 show_oops(t, "'plugin delete' currently unimplemented");
471 return (0);
475 * cancel, remove, etc. downloads
477 void
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 */
487 find.id = id;
488 d = RB_FIND(download_list, &downloads, &find);
490 if (d == NULL) {
491 show_oops(t, "%s: no such download", __func__);
492 return;
496 /* decide what to do */
497 switch (cmd) {
498 case XT_XTP_DL_CANCEL:
499 webkit_download_cancel(d->download);
500 break;
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);
505 break;
506 case XT_XTP_DL_LIST:
507 /* Nothing */
508 break;
509 default:
510 show_oops(t, "%s: unknown command", __func__);
511 break;
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
520 void
521 xtp_handle_hl(struct tab *t, uint8_t cmd, int id)
523 struct history *h, *next;
524 int i = 1;
526 switch (cmd) {
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);
531 if (id == i) {
532 RB_REMOVE(history_list, &hl, h);
533 g_free((gpointer) h->title);
534 g_free((gpointer) h->uri);
535 g_free(h);
536 break;
538 i++;
540 break;
541 case XT_XTP_HL_LIST:
542 /* Nothing - just xtp_page_hl() below */
543 break;
544 default:
545 show_oops(t, "%s: unknown command", __func__);
546 break;
549 xtp_page_hl(t, NULL);
552 /* remove a favorite */
553 void
554 remove_favorite(struct tab *t, int index)
556 char file[PATH_MAX], *title, *uri = NULL;
557 char *new_favs, *tmp;
558 FILE *f;
559 int i;
560 size_t len, lineno;
562 /* open favorites */
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));
568 return;
571 /* build a string which will become the new favroites file */
572 new_favs = g_strdup("");
574 for (i = 1;;) {
575 if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
576 if (feof(f) || ferror(f))
577 break;
578 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
579 if (len == 0) {
580 free(title);
581 title = NULL;
582 continue;
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));
589 goto clean;
593 /* as long as this isn't the one we are deleting add to file */
594 if (i != index) {
595 tmp = new_favs;
596 new_favs = g_strdup_printf("%s%s\n%s\n",
597 new_favs, title, uri);
598 g_free(tmp);
601 free(uri);
602 uri = NULL;
603 free(title);
604 title = NULL;
605 i++;
607 fclose(f);
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));
613 goto clean;
616 if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
617 show_oops(t, "%s: can't fwrite"); /* shut gcc up */
618 fclose(f);
620 clean:
621 if (uri)
622 free(uri);
623 if (title)
624 free(title);
626 g_free(new_favs);
630 add_favorite(struct tab *t, struct karg *args)
632 char file[PATH_MAX];
633 FILE *f;
634 char *line = NULL;
635 size_t urilen, linelen;
636 const gchar *uri, *title;
638 if (t == NULL)
639 return (1);
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__);
644 return (1);
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));
650 return (1);
653 title = get_title(t, FALSE);
654 uri = get_uri(t);
656 if (title == NULL || uri == NULL) {
657 show_oops(t, "can't add page to favorites");
658 goto done;
661 urilen = strlen(uri);
663 for (;;) {
664 if ((line = fparseln(f, &linelen, NULL, NULL, 0)) == NULL)
665 if (feof(f) || ferror(f))
666 break;
668 if (linelen == urilen && !strcmp(line, uri))
669 goto done;
671 free(line);
672 line = NULL;
675 fprintf(f, "\n%s\n%s", title, uri);
676 done:
677 if (line)
678 free(line);
679 fclose(f);
681 update_favorite_tabs(NULL);
683 return (0);
686 void
687 xtp_handle_fl(struct tab *t, uint8_t cmd, int arg)
689 switch (cmd) {
690 case XT_XTP_FL_LIST:
691 /* nothing, just the below call to xtp_page_fl() */
692 break;
693 case XT_XTP_FL_REMOVE:
694 remove_favorite(t, arg);
695 break;
696 default:
697 show_oops(t, "%s: invalid favorites command", __func__);
698 break;
701 xtp_page_fl(t, NULL);
704 void
705 xtp_handle_cl(struct tab *t, uint8_t cmd, int arg)
707 switch (cmd) {
708 case XT_XTP_CL_LIST:
709 /* nothing, just xtp_page_cl() */
710 break;
711 case XT_XTP_CL_REMOVE:
712 remove_cookie(arg);
713 break;
714 default:
715 show_oops(t, "%s: unknown cookie xtp command", __func__);
716 break;
719 xtp_page_cl(t, NULL);
722 /* link an XTP class to it's session key and handler function */
723 struct xtp_despatch {
724 uint8_t xtp_class;
725 char **session_key;
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.
742 void
743 generate_xtp_session_key(char **key)
745 uint8_t rand_bytes[XT_XTP_SES_KEY_SZ];
747 /* free old key */
748 if (*key)
749 g_free(*key);
751 /* make a new one */
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);
760 void
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.
772 * return (1) if OK
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",
779 __func__);
780 return (0);
783 return (1);
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;
797 uint8_t req_class;
798 int ret = FALSE;
801 * tokens array meaning:
802 * tokens[0] = class
803 * tokens[1] = session key
804 * tokens[2] = action
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)))
811 goto clean;
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))) {
818 if (n_tokens < 4)
819 tokens[n_tokens++] = p;
822 /* should be atleast three fields 'class/seskey/command/arg' */
823 if (n_tokens < 3)
824 goto clean;
826 dsp = xtp_despatches;
827 req_class = atoi(tokens[0]);
828 while (dsp->xtp_class) {
829 if (dsp->xtp_class == req_class) {
830 dsp_match = dsp;
831 break;
833 dsp++;
836 /* did we find one atall? */
837 if (dsp_match == NULL) {
838 show_oops(t, "%s: no matching xtp despatch found", __func__);
839 goto clean;
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]));
848 clean:
849 if (dup)
850 g_free(dup);
852 return (ret);
856 * update all favorite tabs apart from one. Pass NULL if
857 * you want to update all.
859 void
860 update_favorite_tabs(struct tab *apart_from)
862 struct tab *t;
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.
877 void
878 update_download_tabs(struct tab *apart_from)
880 struct tab *t;
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.
895 void
896 update_cookie_tabs(struct tab *apart_from)
898 struct tab *t;
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.
913 void
914 update_history_tabs(struct tab *apart_from)
916 struct tab *t;
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)
932 char file[PATH_MAX];
933 FILE *f;
934 char *uri = NULL, *title = NULL;
935 size_t len, lineno = 0;
936 int i, failed = 0;
937 char *body, *tmp, *page = NULL;
938 const char delim[3] = {'\\', '\\', '\0'};
940 DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
942 if (t == NULL)
943 warn("%s: bad param", __func__);
945 /* new session key */
946 if (!updating_fl_tabs)
947 generate_xtp_session_key(&fl_session_key);
949 /* open favorites */
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));
953 return (1);
956 /* body */
957 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
958 "<th style='width: 40px'>&#35;</th><th>Link</th>"
959 "<th style='width: 40px'>Rm</th></tr>\n");
961 for (i = 1;;) {
962 if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
963 break;
964 if (strlen(title) == 0 || title[0] == '#') {
965 free(title);
966 title = NULL;
967 continue;
970 if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
971 if (feof(f) || ferror(f)) {
972 show_oops(t, "favorites file corrupt");
973 failed = 1;
974 break;
977 tmp = body;
978 body = g_strdup_printf("%s<tr>"
979 "<td>%d</td>"
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>"
983 "</tr>\n",
984 body, i, uri, title,
985 XT_XTP_STR, XT_XTP_FL, fl_session_key, XT_XTP_FL_REMOVE, i);
987 g_free(tmp);
989 free(uri);
990 uri = NULL;
991 free(title);
992 title = NULL;
993 i++;
995 fclose(f);
997 /* if none, say so */
998 if (i == 1) {
999 tmp = body;
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);
1004 g_free(tmp);
1007 tmp = body;
1008 body = g_strdup_printf("%s</table>", body);
1009 g_free(tmp);
1011 if (uri)
1012 free(uri);
1013 if (title)
1014 free(title);
1016 /* render */
1017 if (!failed) {
1018 page = get_html_page("Favorites", body, "", 1);
1019 load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES);
1020 g_free(page);
1023 update_favorite_tabs(t);
1025 if (body)
1026 g_free(body);
1028 return (failed);
1032 * Return a new string with a download row (in html)
1033 * appended. Old string is freed.
1035 char *
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;
1041 gdouble progress;
1042 char cur_sz[FMT_SCALED_STRSIZE];
1043 char tot_sz[FMT_SCALED_STRSIZE];
1044 char *xtp_prefix;
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);
1056 switch (stat) {
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);
1062 break;
1063 case WEBKIT_DOWNLOAD_STATUS_STARTED:
1064 /* gather size info */
1065 progress = 100 * webkit_download_get_progress(dl->download);
1067 fmt_scaled(
1068 webkit_download_get_current_size(dl->download), cur_sz);
1069 fmt_scaled(
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);
1083 break;
1084 /* LLL */
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);
1089 break;
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);
1094 break;
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");
1099 break;
1100 default:
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);
1109 g_free(html);
1111 if (status_html)
1112 g_free(status_html);
1114 if (cmd_html)
1115 g_free(cmd_html);
1117 g_free(xtp_prefix);
1119 return new_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;
1129 SoupCookie *c;
1130 char *type, *table_headers, *last_domain;
1132 DNPRINTF(XT_D_CMD, "%s", __func__);
1134 if (t == NULL) {
1135 show_oops(NULL, "%s invalid parameters", __func__);
1136 return (1);
1139 /* Generate a new session key */
1140 if (!updating_cl_tabs)
1141 generate_xtp_session_key(&cl_session_key);
1143 /* table headers */
1144 table_headers = g_strdup_printf("<table><tr>"
1145 "<th>Type</th>"
1146 "<th>Name</th>"
1147 "<th style='width:200px'>Value</th>"
1148 "<th>Path</th>"
1149 "<th>Expires</th>"
1150 "<th>Secure</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);
1156 pc_start = pc;
1158 body = NULL;
1159 last_domain = strdup("");
1160 for (; sc; sc = sc->next) {
1161 c = sc->data;
1163 if (strcmp(last_domain, c->domain) != 0) {
1164 /* new domain */
1165 free(last_domain);
1166 last_domain = strdup(c->domain);
1168 if (body != NULL) {
1169 tmp = body;
1170 body = g_strdup_printf("%s</table>"
1171 "<h2>%s</h2>%s\n",
1172 body, c->domain, table_headers);
1173 g_free(tmp);
1174 } else {
1175 /* first domain */
1176 body = g_strdup_printf("<h2>%s</h2>%s\n",
1177 c->domain, table_headers);
1181 type = "Session";
1182 for (pc = pc_start; pc; pc = pc->next)
1183 if (soup_cookie_equal(pc->data, c)) {
1184 type = "Session + Persistent";
1185 break;
1188 tmp = body;
1189 body = g_strdup_printf(
1190 "%s\n<tr>"
1191 "<td>%s</td>"
1192 "<td style='word-wrap:normal'>%s</td>"
1193 "<td>"
1194 " <textarea rows='4'>%s</textarea>"
1195 "</td>"
1196 "<td>%s</td>"
1197 "<td>%s</td>"
1198 "<td>%d</td>"
1199 "<td>%d</td>"
1200 "<td style='text-align:center'>"
1201 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1202 body,
1203 type,
1204 c->name,
1205 c->value,
1206 c->path,
1207 c->expires ?
1208 soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
1209 c->secure,
1210 c->http_only,
1212 XT_XTP_STR,
1213 XT_XTP_CL,
1214 cl_session_key,
1215 XT_XTP_CL_REMOVE,
1219 g_free(tmp);
1220 i++;
1223 soup_cookies_free(sc);
1224 soup_cookies_free(pc);
1226 /* small message if there are none */
1227 if (i == 1) {
1228 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1229 "colspan='8'>No Cookies</td></tr>\n", table_headers);
1231 tmp = body;
1232 body = g_strdup_printf("%s</table>", body);
1233 g_free(tmp);
1235 page = get_html_page("Cookie Jar", body, "", TRUE);
1236 g_free(body);
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);
1243 g_free(page);
1245 return (0);
1249 xtp_page_hl(struct tab *t, struct karg *args)
1251 char *body, *page, *tmp;
1252 struct history *h;
1253 int i = 1; /* all ids start 1 */
1255 DNPRINTF(XT_D_CMD, "%s", __func__);
1257 if (t == NULL) {
1258 show_oops(NULL, "%s invalid parameters", __func__);
1259 return (1);
1262 /* Generate a new session key */
1263 if (!updating_hl_tabs)
1264 generate_xtp_session_key(&hl_session_key);
1266 /* body */
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) {
1271 tmp = body;
1272 body = g_strdup_printf(
1273 "%s\n<tr>"
1274 "<td><a href='%s'>%s</a></td>"
1275 "<td>%s</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);
1282 g_free(tmp);
1283 i++;
1286 /* small message if there are none */
1287 if (i == 1) {
1288 tmp = body;
1289 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1290 "colspan='3'>No History</td></tr>\n", body);
1291 g_free(tmp);
1294 tmp = body;
1295 body = g_strdup_printf("%s</table>", body);
1296 g_free(tmp);
1298 page = get_html_page("History", body, "", TRUE);
1299 g_free(body);
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);
1309 g_free(page);
1311 return (0);
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;
1322 char *ref;
1323 int n_dl = 1;
1325 DNPRINTF(XT_D_DOWNLOAD, "%s", __func__);
1327 if (t == NULL) {
1328 show_oops(NULL, "%s invalid parameters", __func__);
1329 return (1);
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",
1345 refresh_interval,
1346 XT_XTP_STR,
1347 XT_XTP_DL,
1348 dl_session_key,
1349 XT_XTP_DL_LIST);
1350 else
1351 ref = g_strdup("");
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);
1361 n_dl++;
1364 /* message if no downloads in list */
1365 if (n_dl == 1) {
1366 tmp = body;
1367 body = g_strdup_printf("%s\n<tr><td colspan='3'"
1368 " style='text-align: center'>"
1369 "No downloads</td></tr>\n", body);
1370 g_free(tmp);
1373 tmp = body;
1374 body = g_strdup_printf("%s</table></div>", body);
1375 g_free(tmp);
1377 page = get_html_page("Downloads", body, ref, 1);
1378 g_free(ref);
1379 g_free(body);
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);
1389 g_free(page);
1391 return (0);
1395 startpage(struct tab *t, struct karg *args)
1397 char *page, *body, *b;
1398 struct sp *s;
1400 if (t == NULL)
1401 show_oops(NULL, "startpage invalid parameters");
1403 body = g_strdup_printf("<b>Startup Exception(s):</b><p>");
1405 TAILQ_FOREACH(s, &spl, entry) {
1406 b = body;
1407 body = g_strdup_printf("%s%s<br>", body, s->line);
1408 g_free(b);
1411 page = get_html_page("Startup Exception", body, "", 0);
1412 g_free(body);
1414 load_webkit_string(t, page, XT_URI_ABOUT_STARTPAGE);
1415 g_free(page);
1417 return (0);
1420 void
1421 startpage_add(const char *fmt, ...)
1423 va_list ap;
1424 char *msg;
1425 struct sp *s;
1427 if (fmt == NULL)
1428 return;
1430 va_start(ap, fmt);
1431 if (vasprintf(&msg, fmt, ap) == -1)
1432 errx(1, "startpage_add failed");
1433 va_end(ap);
1435 s = g_malloc0(sizeof *s);
1436 s->line = msg;
1438 TAILQ_INSERT_TAIL(&spl, s, entry);