fix page titles starting with a #, from raphael
[xombrero.git] / about.c
blob530e6614baa79aa9e87e42136e69b13b58d0dddf
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)
73 #define XT_XTP_DL_UNLINK (4)
75 /* XTP history actions */
76 #define XT_XTP_HL_LIST (1)
77 #define XT_XTP_HL_REMOVE (2)
79 /* XTP cookie actions */
80 #define XT_XTP_CL_LIST (1)
81 #define XT_XTP_CL_REMOVE (2)
82 #define XT_XTP_CL_REMOVE_DOMAIN (3)
84 /* XTP cookie actions */
85 #define XT_XTP_FL_LIST (1)
86 #define XT_XTP_FL_REMOVE (2)
88 int js_show_wl(struct tab *, struct karg *);
89 int pl_show_wl(struct tab *, struct karg *);
90 int set(struct tab *, struct karg *);
91 int marco(struct tab *, struct karg *);
92 int startpage(struct tab *, struct karg *);
93 const char * marco_message(int *);
95 struct about_type about_list[] = {
96 { XT_URI_ABOUT_ABOUT, about },
97 { XT_URI_ABOUT_BLANK, blank },
98 { XT_URI_ABOUT_CERTS, ca_cmd },
99 { XT_URI_ABOUT_COOKIEWL, cookie_show_wl },
100 { XT_URI_ABOUT_COOKIEJAR, xtp_page_cl },
101 { XT_URI_ABOUT_DOWNLOADS, xtp_page_dl },
102 { XT_URI_ABOUT_FAVORITES, xtp_page_fl },
103 { XT_URI_ABOUT_HELP, help },
104 { XT_URI_ABOUT_HISTORY, xtp_page_hl },
105 { XT_URI_ABOUT_JSWL, js_show_wl },
106 { XT_URI_ABOUT_SET, set },
107 { XT_URI_ABOUT_STATS, stats },
108 { XT_URI_ABOUT_MARCO, marco },
109 { XT_URI_ABOUT_STARTPAGE, startpage },
110 { XT_URI_ABOUT_PLUGINWL, pl_show_wl },
114 * Session IDs.
115 * We use these to prevent people putting xxxt:// URLs on
116 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
118 #define XT_XTP_SES_KEY_SZ 8
119 #define XT_XTP_SES_KEY_HEX_FMT \
120 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
121 char *dl_session_key; /* downloads */
122 char *hl_session_key; /* history list */
123 char *cl_session_key; /* cookie list */
124 char *fl_session_key; /* favorites list */
126 int updating_fl_tabs = 0;
127 int updating_dl_tabs = 0;
128 int updating_hl_tabs = 0;
129 int updating_cl_tabs = 0;
130 struct download_list downloads;
132 size_t
133 about_list_size(void)
135 return (LENGTH(about_list));
138 gchar *
139 get_html_page(gchar *title, gchar *body, gchar *head, bool addstyles)
141 gchar *r;
143 r = g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
144 "<head>\n"
145 "<title>%s</title>\n"
146 "%s"
147 "%s"
148 "</head>\n"
149 "<body>\n"
150 "<h1>%s</h1>\n"
151 "%s\n</body>\n"
152 "</html>",
153 title,
154 addstyles ? XT_PAGE_STYLE : "",
155 head,
156 title,
157 body);
159 return (r);
163 * Display a web page from a HTML string in memory, rather than from a URL
165 void
166 load_webkit_string(struct tab *t, const char *str, gchar *title)
168 char file[PATH_MAX];
169 int i;
171 /* we set this to indicate we want to manually do navaction */
172 if (t->bfl)
173 t->item = webkit_web_back_forward_list_get_current_item(t->bfl);
175 t->xtp_meaning = XT_XTP_TAB_MEANING_NORMAL;
176 if (title) {
177 /* set t->xtp_meaning */
178 for (i = 0; i < LENGTH(about_list); i++)
179 if (!strcmp(title, about_list[i].name)) {
180 t->xtp_meaning = i;
181 break;
184 webkit_web_view_load_string(t->wv, str, NULL, encoding,
185 "file://");
186 #if GTK_CHECK_VERSION(2, 20, 0)
187 gtk_spinner_stop(GTK_SPINNER(t->spinner));
188 gtk_widget_hide(t->spinner);
189 #endif
190 snprintf(file, sizeof file, "%s/%s", resource_dir, icons[0]);
191 xt_icon_from_file(t, file);
196 blank(struct tab *t, struct karg *args)
198 if (t == NULL)
199 show_oops(NULL, "blank invalid parameters");
201 load_webkit_string(t, "", XT_URI_ABOUT_BLANK);
203 return (0);
207 about(struct tab *t, struct karg *args)
209 char *page, *body;
211 if (t == NULL)
212 show_oops(NULL, "about invalid parameters");
214 body = g_strdup_printf("<b>Version: %s</b>"
215 #ifdef XXXTERM_BUILDSTR
216 "<br><b>Build: %s</b>"
217 #endif
218 "<p>"
219 "Authors:"
220 "<ul>"
221 "<li>Marco Peereboom &lt;marco@peereboom.us&gt;</li>"
222 "<li>Stevan Andjelkovic &lt;stevan@student.chalmers.se&gt;</li>"
223 "<li>Edd Barrett &lt;vext01@gmail.com&gt;</li>"
224 "<li>Todd T. Fries &lt;todd@fries.net&gt;</li>"
225 "<li>Raphael Graf &lt;r@undefined.ch&gt;</li>"
226 "<li>Michal Mazurek &lt;akfaew@jasminek.net&gt;</li>"
227 "</ul>"
228 "Copyrights and licenses can be found on the XXXTerm "
229 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
230 "</p>",
231 #ifdef XXXTERM_BUILDSTR
232 version, XXXTERM_BUILDSTR
233 #else
234 version
235 #endif
238 page = get_html_page("About", body, "", 0);
239 g_free(body);
241 load_webkit_string(t, page, XT_URI_ABOUT_ABOUT);
242 g_free(page);
244 return (0);
248 help(struct tab *t, struct karg *args)
250 char *page, *head, *body;
252 if (t == NULL)
253 show_oops(NULL, "help invalid parameters");
255 head = "<meta http-equiv=\"REFRESH\" content=\"0;"
256 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
257 "</head>\n";
258 body = "XXXTerm man page <a href=\"http://opensource.conformal.com/"
259 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
260 "cgi-bin/man-cgi?xxxterm</a>";
262 page = get_html_page(XT_NAME, body, head, FALSE);
264 load_webkit_string(t, page, XT_URI_ABOUT_HELP);
265 g_free(page);
267 return (0);
271 stats(struct tab *t, struct karg *args)
273 char *page, *body, *s, line[64 * 1024];
274 long long unsigned int line_count = 0;
275 FILE *r_cookie_f;
277 if (t == NULL)
278 show_oops(NULL, "stats invalid parameters");
280 line[0] = '\0';
281 if (save_rejected_cookies) {
282 if ((r_cookie_f = fopen(rc_fname, "r"))) {
283 for (;;) {
284 s = fgets(line, sizeof line, r_cookie_f);
285 if (s == NULL || feof(r_cookie_f) ||
286 ferror(r_cookie_f))
287 break;
288 line_count++;
290 fclose(r_cookie_f);
291 snprintf(line, sizeof line,
292 "<br/>Cookies blocked(*) total: %llu", line_count);
293 } else
294 show_oops(t, "Can't open blocked cookies file: %s",
295 strerror(errno));
298 body = g_strdup_printf(
299 "Cookies blocked(*) this session: %llu"
300 "%s"
301 "<p><small><b>*</b> results vary based on settings</small></p>",
302 blocked_cookies,
303 line);
305 page = get_html_page("Statistics", body, "", 0);
306 g_free(body);
308 load_webkit_string(t, page, XT_URI_ABOUT_STATS);
309 g_free(page);
311 return (0);
314 void
315 show_certs(struct tab *t, gnutls_x509_crt_t *certs,
316 size_t cert_count, char *title)
318 gnutls_datum_t cinfo;
319 char *tmp, *body;
320 int i;
322 body = g_strdup("");
324 for (i = 0; i < cert_count; i++) {
325 if (gnutls_x509_crt_print(certs[i], GNUTLS_CRT_PRINT_FULL,
326 &cinfo))
327 return;
329 tmp = body;
330 body = g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
331 body, i, cinfo.data);
332 gnutls_free(cinfo.data);
333 g_free(tmp);
336 tmp = get_html_page(title, body, "", 0);
337 g_free(body);
339 load_webkit_string(t, tmp, XT_URI_ABOUT_CERTS);
340 g_free(tmp);
344 ca_cmd(struct tab *t, struct karg *args)
346 FILE *f = NULL;
347 int rv = 1, certs = 0, certs_read;
348 struct stat sb;
349 gnutls_datum_t dt;
350 gnutls_x509_crt_t *c = NULL;
351 char *certs_buf = NULL, *s;
353 if ((f = fopen(ssl_ca_file, "r")) == NULL) {
354 show_oops(t, "Can't open CA file: %s", ssl_ca_file);
355 return (1);
358 if (fstat(fileno(f), &sb) == -1) {
359 show_oops(t, "Can't stat CA file: %s", ssl_ca_file);
360 goto done;
363 certs_buf = g_malloc(sb.st_size + 1);
364 if (fread(certs_buf, 1, sb.st_size, f) != sb.st_size) {
365 show_oops(t, "Can't read CA file: %s", strerror(errno));
366 goto done;
368 certs_buf[sb.st_size] = '\0';
370 s = certs_buf;
371 while ((s = strstr(s, "BEGIN CERTIFICATE"))) {
372 certs++;
373 s += strlen("BEGIN CERTIFICATE");
376 bzero(&dt, sizeof dt);
377 dt.data = (unsigned char *)certs_buf;
378 dt.size = sb.st_size;
379 c = g_malloc(sizeof(gnutls_x509_crt_t) * certs);
380 certs_read = gnutls_x509_crt_list_import(c, (unsigned int *)&certs, &dt,
381 GNUTLS_X509_FMT_PEM, 0);
382 if (certs_read <= 0) {
383 show_oops(t, "No cert(s) available");
384 goto done;
386 show_certs(t, c, certs_read, "Certificate Authority Certificates");
387 done:
388 if (c)
389 g_free(c);
390 if (certs_buf)
391 g_free(certs_buf);
392 if (f)
393 fclose(f);
395 return (rv);
399 cookie_show_wl(struct tab *t, struct karg *args)
401 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
402 wl_show(t, args, "Cookie White List", &c_wl);
404 return (0);
408 js_show_wl(struct tab *t, struct karg *args)
410 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
411 wl_show(t, args, "JavaScript White List", &js_wl);
413 return (0);
417 cookie_cmd(struct tab *t, struct karg *args)
419 if (args->i & XT_SHOW)
420 wl_show(t, args, "Cookie White List", &c_wl);
421 else if (args->i & XT_WL_TOGGLE) {
422 args->i |= XT_WL_RELOAD;
423 toggle_cwl(t, args);
424 } else if (args->i & XT_SAVE) {
425 args->i |= XT_WL_RELOAD;
426 wl_save(t, args, XT_WL_COOKIE);
427 } else if (args->i & XT_DELETE)
428 show_oops(t, "'cookie delete' currently unimplemented");
430 return (0);
434 js_cmd(struct tab *t, struct karg *args)
436 if (args->i & XT_SHOW)
437 wl_show(t, args, "JavaScript White List", &js_wl);
438 else if (args->i & XT_SAVE) {
439 args->i |= XT_WL_RELOAD;
440 wl_save(t, args, XT_WL_JAVASCRIPT);
441 } else if (args->i & XT_WL_TOGGLE) {
442 args->i |= XT_WL_RELOAD;
443 toggle_js(t, args);
444 } else if (args->i & XT_DELETE)
445 show_oops(t, "'js delete' currently unimplemented");
447 return (0);
451 pl_show_wl(struct tab *t, struct karg *args)
453 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
454 wl_show(t, args, "Plugin White List", &pl_wl);
456 return (0);
460 pl_cmd(struct tab *t, struct karg *args)
462 if (args->i & XT_SHOW)
463 wl_show(t, args, "Plugin White List", &pl_wl);
464 else if (args->i & XT_SAVE) {
465 args->i |= XT_WL_RELOAD;
466 wl_save(t, args, XT_WL_PLUGIN);
467 } else if (args->i & XT_WL_TOGGLE) {
468 args->i |= XT_WL_RELOAD;
469 toggle_pl(t, args);
470 } else if (args->i & XT_DELETE)
471 show_oops(t, "'plugin delete' currently unimplemented");
473 return (0);
477 * cancel, remove, etc. downloads
479 void
480 xtp_handle_dl(struct tab *t, uint8_t cmd, int id)
482 struct download find, *d = NULL;
484 DNPRINTF(XT_D_DOWNLOAD, "download control: cmd %d, id %d\n", cmd, id);
486 /* some commands require a valid download id */
487 if (cmd != XT_XTP_DL_LIST) {
488 /* lookup download in question */
489 find.id = id;
490 d = RB_FIND(download_list, &downloads, &find);
492 if (d == NULL) {
493 show_oops(t, "%s: no such download", __func__);
494 return;
498 /* decide what to do */
499 switch (cmd) {
500 case XT_XTP_DL_CANCEL:
501 webkit_download_cancel(d->download);
502 g_object_unref(d->download);
503 break;
504 case XT_XTP_DL_UNLINK:
505 unlink(webkit_download_get_destination_uri(d->download) +
506 strlen("file://"));
507 /* FALLTHROUGH */
508 case XT_XTP_DL_REMOVE:
509 webkit_download_cancel(d->download); /* just incase */
510 g_object_unref(d->download);
511 RB_REMOVE(download_list, &downloads, d);
512 break;
513 case XT_XTP_DL_LIST:
514 /* Nothing */
515 break;
516 default:
517 show_oops(t, "%s: unknown command", __func__);
518 break;
520 xtp_page_dl(t, NULL);
524 * Actions on history, only does one thing for now, but
525 * we provide the function for future actions
527 void
528 xtp_handle_hl(struct tab *t, uint8_t cmd, int id)
530 struct history *h, *next;
531 int i = 1;
533 switch (cmd) {
534 case XT_XTP_HL_REMOVE:
535 /* walk backwards, as listed in reverse */
536 for (h = RB_MAX(history_list, &hl); h != NULL; h = next) {
537 next = RB_PREV(history_list, &hl, h);
538 if (id == i) {
539 RB_REMOVE(history_list, &hl, h);
540 g_free((gpointer) h->title);
541 g_free((gpointer) h->uri);
542 g_free(h);
543 break;
545 i++;
547 break;
548 case XT_XTP_HL_LIST:
549 /* Nothing - just xtp_page_hl() below */
550 break;
551 default:
552 show_oops(t, "%s: unknown command", __func__);
553 break;
556 xtp_page_hl(t, NULL);
559 /* remove a favorite */
560 void
561 remove_favorite(struct tab *t, int index)
563 char file[PATH_MAX], *title, *uri = NULL;
564 char *new_favs, *tmp;
565 FILE *f;
566 int i;
567 size_t len, lineno;
569 /* open favorites */
570 snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE);
572 if ((f = fopen(file, "r")) == NULL) {
573 show_oops(t, "%s: can't open favorites: %s",
574 __func__, strerror(errno));
575 return;
578 /* build a string which will become the new favroites file */
579 new_favs = g_strdup("");
581 for (i = 1;;) {
582 if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
583 if (feof(f) || ferror(f))
584 break;
585 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
586 if (len == 0) {
587 free(title);
588 title = NULL;
589 continue;
592 if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) {
593 if (feof(f) || ferror(f)) {
594 show_oops(t, "%s: can't parse favorites %s",
595 __func__, strerror(errno));
596 goto clean;
600 /* as long as this isn't the one we are deleting add to file */
601 if (i != index) {
602 tmp = new_favs;
603 new_favs = g_strdup_printf("%s%s\n%s\n",
604 new_favs, title, uri);
605 g_free(tmp);
608 free(uri);
609 uri = NULL;
610 free(title);
611 title = NULL;
612 i++;
614 fclose(f);
616 /* write back new favorites file */
617 if ((f = fopen(file, "w")) == NULL) {
618 show_oops(t, "%s: can't open favorites: %s",
619 __func__, strerror(errno));
620 goto clean;
623 if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
624 show_oops(t, "%s: can't fwrite", __func__);
625 fclose(f);
627 clean:
628 if (uri)
629 free(uri);
630 if (title)
631 free(title);
633 g_free(new_favs);
637 add_favorite(struct tab *t, struct karg *args)
639 char file[PATH_MAX];
640 FILE *f;
641 char *line = NULL;
642 size_t urilen, linelen;
643 const gchar *uri, *title;
645 if (t == NULL)
646 return (1);
648 /* don't allow adding of xtp pages to favorites */
649 if (t->xtp_meaning != XT_XTP_TAB_MEANING_NORMAL) {
650 show_oops(t, "%s: can't add xtp pages to favorites", __func__);
651 return (1);
654 snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE);
655 if ((f = fopen(file, "r+")) == NULL) {
656 show_oops(t, "Can't open favorites file: %s", strerror(errno));
657 return (1);
660 title = get_title(t, FALSE);
661 uri = get_uri(t);
663 if (title == NULL || uri == NULL) {
664 show_oops(t, "can't add page to favorites");
665 goto done;
668 urilen = strlen(uri);
670 for (;;) {
671 if ((line = fparseln(f, &linelen, NULL, NULL, 0)) == NULL)
672 if (feof(f) || ferror(f))
673 break;
675 if (linelen == urilen && !strcmp(line, uri))
676 goto done;
678 free(line);
679 line = NULL;
682 fprintf(f, "\n%s\n%s", title, uri);
683 done:
684 if (line)
685 free(line);
686 fclose(f);
688 update_favorite_tabs(NULL);
690 return (0);
693 void
694 xtp_handle_fl(struct tab *t, uint8_t cmd, int arg)
696 switch (cmd) {
697 case XT_XTP_FL_LIST:
698 /* nothing, just the below call to xtp_page_fl() */
699 break;
700 case XT_XTP_FL_REMOVE:
701 remove_favorite(t, arg);
702 break;
703 default:
704 show_oops(t, "%s: invalid favorites command", __func__);
705 break;
708 xtp_page_fl(t, NULL);
711 void
712 xtp_handle_cl(struct tab *t, uint8_t cmd, int arg)
714 switch (cmd) {
715 case XT_XTP_CL_LIST:
716 /* nothing, just xtp_page_cl() */
717 break;
718 case XT_XTP_CL_REMOVE:
719 remove_cookie(arg);
720 break;
721 case XT_XTP_CL_REMOVE_DOMAIN:
722 remove_cookie_domain(arg);
723 break;
724 default:
725 show_oops(t, "%s: unknown cookie xtp command", __func__);
726 break;
729 xtp_page_cl(t, NULL);
732 /* link an XTP class to it's session key and handler function */
733 struct xtp_despatch {
734 uint8_t xtp_class;
735 char **session_key;
736 void (*handle_func)(struct tab *, uint8_t, int);
739 struct xtp_despatch xtp_despatches[] = {
740 { XT_XTP_DL, &dl_session_key, xtp_handle_dl },
741 { XT_XTP_HL, &hl_session_key, xtp_handle_hl },
742 { XT_XTP_FL, &fl_session_key, xtp_handle_fl },
743 { XT_XTP_CL, &cl_session_key, xtp_handle_cl },
744 { XT_XTP_INVALID, NULL, NULL }
748 * generate a session key to secure xtp commands.
749 * pass in a ptr to the key in question and it will
750 * be modified in place.
752 void
753 generate_xtp_session_key(char **key)
755 uint8_t rand_bytes[XT_XTP_SES_KEY_SZ];
757 /* free old key */
758 if (*key)
759 g_free(*key);
761 /* make a new one */
762 arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ);
763 *key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT,
764 rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
765 rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]);
767 DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key);
770 void
771 xtp_generate_keys(void)
773 /* generate session keys for xtp pages */
774 generate_xtp_session_key(&dl_session_key);
775 generate_xtp_session_key(&hl_session_key);
776 generate_xtp_session_key(&cl_session_key);
777 generate_xtp_session_key(&fl_session_key);
781 * validate a xtp session key.
782 * return (1) if OK
785 validate_xtp_session_key(struct tab *t, char *trusted, char *untrusted)
787 if (strcmp(trusted, untrusted) != 0) {
788 show_oops(t, "%s: xtp session key mismatch possible spoof",
789 __func__);
790 return (0);
793 return (1);
797 * is the url xtp protocol? (xxxt://)
798 * if so, parse and despatch correct bahvior
801 parse_xtp_url(struct tab *t, const char *url)
803 char *dup = NULL, *p, *last = NULL;
804 uint8_t n_tokens = 0;
805 char *tokens[4] = {NULL, NULL, NULL, ""};
806 struct xtp_despatch *dsp, *dsp_match = NULL;
807 uint8_t req_class;
808 int ret = FALSE;
811 * tokens array meaning:
812 * tokens[0] = class
813 * tokens[1] = session key
814 * tokens[2] = action
815 * tokens[3] = optional argument
818 DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, url);
820 if (strncmp(url, XT_XTP_STR, strlen(XT_XTP_STR)))
821 goto clean;
823 dup = g_strdup(url + strlen(XT_XTP_STR));
825 /* split out the url */
826 for ((p = strtok_r(dup, "/", &last)); p;
827 (p = strtok_r(NULL, "/", &last))) {
828 if (n_tokens < 4)
829 tokens[n_tokens++] = p;
832 /* should be atleast three fields 'class/seskey/command/arg' */
833 if (n_tokens < 3)
834 goto clean;
836 dsp = xtp_despatches;
837 req_class = atoi(tokens[0]);
838 while (dsp->xtp_class) {
839 if (dsp->xtp_class == req_class) {
840 dsp_match = dsp;
841 break;
843 dsp++;
846 /* did we find one atall? */
847 if (dsp_match == NULL) {
848 show_oops(t, "%s: no matching xtp despatch found", __func__);
849 goto clean;
852 /* check session key and call despatch function */
853 if (validate_xtp_session_key(t, *(dsp_match->session_key), tokens[1])) {
854 ret = TRUE; /* all is well, this was a valid xtp request */
855 dsp_match->handle_func(t, atoi(tokens[2]), atoi(tokens[3]));
858 clean:
859 if (dup)
860 g_free(dup);
862 return (ret);
866 * update all favorite tabs apart from one. Pass NULL if
867 * you want to update all.
869 void
870 update_favorite_tabs(struct tab *apart_from)
872 struct tab *t;
873 if (!updating_fl_tabs) {
874 updating_fl_tabs = 1; /* stop infinite recursion */
875 TAILQ_FOREACH(t, &tabs, entry)
876 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL)
877 && (t != apart_from))
878 xtp_page_fl(t, NULL);
879 updating_fl_tabs = 0;
884 * update all download tabs apart from one. Pass NULL if
885 * you want to update all.
887 void
888 update_download_tabs(struct tab *apart_from)
890 struct tab *t;
891 if (!updating_dl_tabs) {
892 updating_dl_tabs = 1; /* stop infinite recursion */
893 TAILQ_FOREACH(t, &tabs, entry)
894 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_DL)
895 && (t != apart_from))
896 xtp_page_dl(t, NULL);
897 updating_dl_tabs = 0;
902 * update all cookie tabs apart from one. Pass NULL if
903 * you want to update all.
905 void
906 update_cookie_tabs(struct tab *apart_from)
908 struct tab *t;
909 if (!updating_cl_tabs) {
910 updating_cl_tabs = 1; /* stop infinite recursion */
911 TAILQ_FOREACH(t, &tabs, entry)
912 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL)
913 && (t != apart_from))
914 xtp_page_cl(t, NULL);
915 updating_cl_tabs = 0;
920 * update all history tabs apart from one. Pass NULL if
921 * you want to update all.
923 void
924 update_history_tabs(struct tab *apart_from)
926 struct tab *t;
928 if (!updating_hl_tabs) {
929 updating_hl_tabs = 1; /* stop infinite recursion */
930 TAILQ_FOREACH(t, &tabs, entry)
931 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL)
932 && (t != apart_from))
933 xtp_page_hl(t, NULL);
934 updating_hl_tabs = 0;
938 /* show a list of favorites (bookmarks) */
940 xtp_page_fl(struct tab *t, struct karg *args)
942 char file[PATH_MAX];
943 FILE *f;
944 char *uri = NULL, *title = NULL;
945 size_t len, lineno = 0;
946 int i, failed = 0;
947 char *body, *tmp, *page = NULL;
948 const char delim[3] = {'\\', '\\', '\0'};
950 DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
952 if (t == NULL)
953 warn("%s: bad param", __func__);
955 /* new session key */
956 if (!updating_fl_tabs)
957 generate_xtp_session_key(&fl_session_key);
959 /* open favorites */
960 snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE);
961 if ((f = fopen(file, "r")) == NULL) {
962 show_oops(t, "Can't open favorites file: %s", strerror(errno));
963 return (1);
966 /* body */
967 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
968 "<th style='width: 40px'>&#35;</th><th>Link</th>"
969 "<th style='width: 40px'>Rm</th></tr>\n");
971 for (i = 1;;) {
972 if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
973 break;
974 if (strlen(title) == 0) {
975 free(title);
976 title = NULL;
977 continue;
980 if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
981 if (feof(f) || ferror(f)) {
982 show_oops(t, "favorites file corrupt");
983 failed = 1;
984 break;
987 tmp = body;
988 body = g_strdup_printf("%s<tr>"
989 "<td>%d</td>"
990 "<td><a href='%s'>%s</a></td>"
991 "<td style='text-align: center'>"
992 "<a href='%s%d/%s/%d/%d'>X</a></td>"
993 "</tr>\n",
994 body, i, uri, title,
995 XT_XTP_STR, XT_XTP_FL, fl_session_key, XT_XTP_FL_REMOVE, i);
997 g_free(tmp);
999 free(uri);
1000 uri = NULL;
1001 free(title);
1002 title = NULL;
1003 i++;
1005 fclose(f);
1007 /* if none, say so */
1008 if (i == 1) {
1009 tmp = body;
1010 body = g_strdup_printf("%s<tr>"
1011 "<td colspan='3' style='text-align: center'>"
1012 "No favorites - To add one use the 'favadd' command."
1013 "</td></tr>", body);
1014 g_free(tmp);
1017 tmp = body;
1018 body = g_strdup_printf("%s</table>", body);
1019 g_free(tmp);
1021 if (uri)
1022 free(uri);
1023 if (title)
1024 free(title);
1026 /* render */
1027 if (!failed) {
1028 page = get_html_page("Favorites", body, "", 1);
1029 load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES);
1030 g_free(page);
1033 update_favorite_tabs(t);
1035 if (body)
1036 g_free(body);
1038 return (failed);
1042 * Return a new string with a download row (in html)
1043 * appended. Old string is freed.
1045 char *
1046 xtp_page_dl_row(struct tab *t, char *html, struct download *dl)
1049 WebKitDownloadStatus stat;
1050 char *status_html = NULL, *cmd_html = NULL, *new_html;
1051 gdouble progress;
1052 char cur_sz[FMT_SCALED_STRSIZE];
1053 char tot_sz[FMT_SCALED_STRSIZE];
1054 char *xtp_prefix;
1056 DNPRINTF(XT_D_DOWNLOAD, "%s: dl->id %d\n", __func__, dl->id);
1058 /* All actions wil take this form:
1059 * xxxt://class/seskey
1061 xtp_prefix = g_strdup_printf("%s%d/%s/",
1062 XT_XTP_STR, XT_XTP_DL, dl_session_key);
1064 stat = webkit_download_get_status(dl->download);
1066 switch (stat) {
1067 case WEBKIT_DOWNLOAD_STATUS_FINISHED:
1068 status_html = g_strdup_printf("Finished");
1069 cmd_html = g_strdup_printf(
1070 "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1071 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1072 XT_XTP_DL_UNLINK, dl->id);
1073 break;
1074 case WEBKIT_DOWNLOAD_STATUS_STARTED:
1075 /* gather size info */
1076 progress = 100 * webkit_download_get_progress(dl->download);
1078 fmt_scaled(
1079 webkit_download_get_current_size(dl->download), cur_sz);
1080 fmt_scaled(
1081 webkit_download_get_total_size(dl->download), tot_sz);
1083 status_html = g_strdup_printf(
1084 "<div style='width: 100%%' align='center'>"
1085 "<div class='progress-outer'>"
1086 "<div class='progress-inner' style='width: %.2f%%'>"
1087 "</div></div></div>"
1088 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
1089 progress, cur_sz, tot_sz, progress);
1091 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1092 xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
1094 break;
1095 /* LLL */
1096 case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
1097 status_html = g_strdup_printf("Cancelled");
1098 cmd_html = g_strdup_printf(
1099 "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1100 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1101 XT_XTP_DL_UNLINK, dl->id);
1102 break;
1103 case WEBKIT_DOWNLOAD_STATUS_ERROR:
1104 status_html = g_strdup_printf("Error!");
1105 cmd_html = g_strdup_printf(
1106 "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1107 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1108 XT_XTP_DL_UNLINK, dl->id);
1109 break;
1110 case WEBKIT_DOWNLOAD_STATUS_CREATED:
1111 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1112 xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
1113 status_html = g_strdup_printf("Starting");
1114 break;
1115 default:
1116 show_oops(t, "%s: unknown download status", __func__);
1119 new_html = g_strdup_printf(
1120 "%s\n<tr><td>%s</td><td>%s</td>"
1121 "<td style='text-align:center'>%s</td></tr>\n",
1122 html, basename((char *)webkit_download_get_destination_uri(dl->download)),
1123 status_html, cmd_html);
1124 g_free(html);
1126 if (status_html)
1127 g_free(status_html);
1129 if (cmd_html)
1130 g_free(cmd_html);
1132 g_free(xtp_prefix);
1134 return new_html;
1137 /* cookie management XTP page */
1139 xtp_page_cl(struct tab *t, struct karg *args)
1141 char *body, *page, *tmp;
1142 int i = 1; /* all ids start 1 */
1143 int domain_id = 0;
1144 GSList *sc, *pc, *pc_start;
1145 SoupCookie *c;
1146 char *type, *table_headers, *last_domain;
1148 DNPRINTF(XT_D_CMD, "%s", __func__);
1150 if (t == NULL) {
1151 show_oops(NULL, "%s invalid parameters", __func__);
1152 return (1);
1155 /* Generate a new session key */
1156 if (!updating_cl_tabs)
1157 generate_xtp_session_key(&cl_session_key);
1159 /* table headers */
1160 table_headers = g_strdup_printf("<table><tr>"
1161 "<th>Type</th>"
1162 "<th>Name</th>"
1163 "<th style='width:200px'>Value</th>"
1164 "<th>Path</th>"
1165 "<th>Expires</th>"
1166 "<th>Secure</th>"
1167 "<th>HTTP<br />only</th>"
1168 "<th style='width:40px'>Rm</th></tr>\n");
1170 sc = soup_cookie_jar_all_cookies(s_cookiejar);
1171 pc = soup_cookie_jar_all_cookies(p_cookiejar);
1172 pc_start = pc;
1174 body = NULL;
1175 last_domain = strdup("");
1176 for (; sc; sc = sc->next) {
1177 c = sc->data;
1179 if (strcmp(last_domain, c->domain) != 0) {
1180 /* new domain */
1181 domain_id ++;
1182 free(last_domain);
1183 last_domain = strdup(c->domain);
1185 if (body != NULL) {
1186 tmp = body;
1187 body = g_strdup_printf("%s</table>"
1188 "<h2>%s</h2>"
1189 "<a href='%s%d/%s/%d/%d'>remove all</a>%s\n",
1190 body, c->domain,
1191 XT_XTP_STR, XT_XTP_CL,
1192 cl_session_key, XT_XTP_CL_REMOVE_DOMAIN, domain_id,
1193 table_headers);
1194 g_free(tmp);
1195 } else {
1196 /* first domain */
1197 body = g_strdup_printf("<h2>%s</h2>"
1198 "<a href='%s%d/%s/%d/%d'>remove all</a>%s\n",
1199 c->domain,
1200 XT_XTP_STR, XT_XTP_CL,
1201 cl_session_key, XT_XTP_CL_REMOVE_DOMAIN, domain_id,
1202 table_headers);
1206 type = "Session";
1207 for (pc = pc_start; pc; pc = pc->next)
1208 if (soup_cookie_equal(pc->data, c)) {
1209 type = "Session + Persistent";
1210 break;
1213 tmp = body;
1214 body = g_strdup_printf(
1215 "%s\n<tr>"
1216 "<td>%s</td>"
1217 "<td style='word-wrap:normal'>%s</td>"
1218 "<td>"
1219 " <textarea rows='4'>%s</textarea>"
1220 "</td>"
1221 "<td>%s</td>"
1222 "<td>%s</td>"
1223 "<td>%d</td>"
1224 "<td>%d</td>"
1225 "<td style='text-align:center'>"
1226 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1227 body,
1228 type,
1229 c->name,
1230 c->value,
1231 c->path,
1232 c->expires ?
1233 soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
1234 c->secure,
1235 c->http_only,
1237 XT_XTP_STR,
1238 XT_XTP_CL,
1239 cl_session_key,
1240 XT_XTP_CL_REMOVE,
1244 g_free(tmp);
1245 i++;
1248 soup_cookies_free(sc);
1249 soup_cookies_free(pc);
1251 /* small message if there are none */
1252 if (i == 1) {
1253 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1254 "colspan='8'>No Cookies</td></tr>\n", table_headers);
1256 tmp = body;
1257 body = g_strdup_printf("%s</table>", body);
1258 g_free(tmp);
1260 page = get_html_page("Cookie Jar", body, "", TRUE);
1261 g_free(body);
1262 g_free(table_headers);
1263 g_free(last_domain);
1265 load_webkit_string(t, page, XT_URI_ABOUT_COOKIEJAR);
1266 update_cookie_tabs(t);
1268 g_free(page);
1270 return (0);
1274 xtp_page_hl(struct tab *t, struct karg *args)
1276 char *body, *page, *tmp;
1277 struct history *h;
1278 int i = 1; /* all ids start 1 */
1280 DNPRINTF(XT_D_CMD, "%s", __func__);
1282 if (t == NULL) {
1283 show_oops(NULL, "%s invalid parameters", __func__);
1284 return (1);
1287 /* Generate a new session key */
1288 if (!updating_hl_tabs)
1289 generate_xtp_session_key(&hl_session_key);
1291 /* body */
1292 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
1293 "<th>URI</th><th>Title</th><th>Last visited</th><th style='width: 40px'>Rm</th></tr>\n");
1295 RB_FOREACH_REVERSE(h, history_list, &hl) {
1296 tmp = body;
1297 body = g_strdup_printf(
1298 "%s\n<tr>"
1299 "<td><a href='%s'>%s</a></td>"
1300 "<td>%s</td>"
1301 "<td>%s</td>"
1302 "<td style='text-align: center'>"
1303 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1304 body, h->uri, h->uri, h->title, ctime(&h->time),
1305 XT_XTP_STR, XT_XTP_HL, hl_session_key,
1306 XT_XTP_HL_REMOVE, i);
1308 g_free(tmp);
1309 i++;
1312 /* small message if there are none */
1313 if (i == 1) {
1314 tmp = body;
1315 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1316 "colspan='3'>No History</td></tr>\n", body);
1317 g_free(tmp);
1320 tmp = body;
1321 body = g_strdup_printf("%s</table>", body);
1322 g_free(tmp);
1324 page = get_html_page("History", body, "", TRUE);
1325 g_free(body);
1328 * update all history manager tabs as the xtp session
1329 * key has now changed. No need to update the current tab.
1330 * Already did that above.
1332 update_history_tabs(t);
1334 load_webkit_string(t, page, XT_URI_ABOUT_HISTORY);
1335 g_free(page);
1337 return (0);
1341 * Generate a web page detailing the status of any downloads
1344 xtp_page_dl(struct tab *t, struct karg *args)
1346 struct download *dl;
1347 char *body, *page, *tmp;
1348 char *ref;
1349 int n_dl = 1;
1351 DNPRINTF(XT_D_DOWNLOAD, "%s", __func__);
1353 if (t == NULL) {
1354 show_oops(NULL, "%s invalid parameters", __func__);
1355 return (1);
1359 * Generate a new session key for next page instance.
1360 * This only happens for the top level call to xtp_page_dl()
1361 * in which case updating_dl_tabs is 0.
1363 if (!updating_dl_tabs)
1364 generate_xtp_session_key(&dl_session_key);
1366 /* header - with refresh so as to update */
1367 if (refresh_interval >= 1)
1368 ref = g_strdup_printf(
1369 "<meta http-equiv='refresh' content='%u"
1370 ";url=%s%d/%s/%d' />\n",
1371 refresh_interval,
1372 XT_XTP_STR,
1373 XT_XTP_DL,
1374 dl_session_key,
1375 XT_XTP_DL_LIST);
1376 else
1377 ref = g_strdup("");
1379 body = g_strdup_printf("<div align='center'>"
1380 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
1381 "</p><table><tr><th style='width: 60%%'>"
1382 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
1383 XT_XTP_STR, XT_XTP_DL, dl_session_key, XT_XTP_DL_LIST);
1385 RB_FOREACH_REVERSE(dl, download_list, &downloads) {
1386 body = xtp_page_dl_row(t, body, dl);
1387 n_dl++;
1390 /* message if no downloads in list */
1391 if (n_dl == 1) {
1392 tmp = body;
1393 body = g_strdup_printf("%s\n<tr><td colspan='3'"
1394 " style='text-align: center'>"
1395 "No downloads</td></tr>\n", body);
1396 g_free(tmp);
1399 tmp = body;
1400 body = g_strdup_printf("%s</table></div>", body);
1401 g_free(tmp);
1403 page = get_html_page("Downloads", body, ref, 1);
1404 g_free(ref);
1405 g_free(body);
1408 * update all download manager tabs as the xtp session
1409 * key has now changed. No need to update the current tab.
1410 * Already did that above.
1412 update_download_tabs(t);
1414 load_webkit_string(t, page, XT_URI_ABOUT_DOWNLOADS);
1415 g_free(page);
1417 return (0);
1421 startpage(struct tab *t, struct karg *args)
1423 char *page, *body, *b;
1424 struct sp *s;
1426 if (t == NULL)
1427 show_oops(NULL, "startpage invalid parameters");
1429 body = g_strdup_printf("<b>Startup Exception(s):</b><p>");
1431 TAILQ_FOREACH(s, &spl, entry) {
1432 b = body;
1433 body = g_strdup_printf("%s%s<br>", body, s->line);
1434 g_free(b);
1437 page = get_html_page("Startup Exception", body, "", 0);
1438 g_free(body);
1440 load_webkit_string(t, page, XT_URI_ABOUT_STARTPAGE);
1441 g_free(page);
1443 return (0);
1446 void
1447 startpage_add(const char *fmt, ...)
1449 va_list ap;
1450 char *msg;
1451 struct sp *s;
1453 if (fmt == NULL)
1454 return;
1456 va_start(ap, fmt);
1457 if (vasprintf(&msg, fmt, ap) == -1)
1458 errx(1, "startpage_add failed");
1459 va_end(ap);
1461 s = g_malloc0(sizeof *s);
1462 s->line = msg;
1464 TAILQ_INSERT_TAIL(&spl, s, entry);