Reintroduce the FreeBSD fixes that were lost in the backouts.
[xombrero.git] / about.c
blobdb7651050424c722edc4d783e6e40a827701f037
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, 2012 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>
8 * Copyright (c) 2012 Josh Rickmar <jrick@devio.us>
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 #include <xombrero.h>
26 * xombrero "protocol" (xtp)
27 * We use this for managing stuff like downloads and favorites. They
28 * make magical HTML pages in memory which have xxxt:// links in order
29 * to communicate with xombrero's internals. These links take the format:
30 * xxxt://class/session_key/action/arg
32 * Don't begin xtp class/actions as 0. atoi returns that on error.
34 * Typically we have not put addition of items in this framework, as
35 * adding items is either done via an ex-command or via a keybinding instead.
38 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
39 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
40 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
41 "td{overflow: hidden;" \
42 " padding: 2px 2px 2px 2px;" \
43 " border: 1px solid black;" \
44 " vertical-align:top;" \
45 " word-wrap: break-word}\n" \
46 "tr:hover{background: #ffff99}\n" \
47 "th{background-color: #cccccc;" \
48 " border: 1px solid black}\n" \
49 "table{width: 100%%;" \
50 " border: 1px black solid;" \
51 " border-collapse:collapse}\n" \
52 ".progress-outer{" \
53 "border: 1px solid black;" \
54 " height: 8px;" \
55 " width: 90%%}\n" \
56 ".progress-inner{float: left;" \
57 " height: 8px;" \
58 " background: green}\n" \
59 ".dlstatus{font-size: small;" \
60 " text-align: center}\n" \
61 "table#settings{background-color: #eee;"\
62 " border: 0px;" \
63 " margin: 15px;}\n" \
64 "table#settings td{border: 0px;}\n" \
65 "table#settings th{border: 0px;}\n" \
66 "table#settings tr{" \
67 " background: #f6f6f6;}\n" \
68 "table#settings tr:nth-child(even){" \
69 " background: #eeeeee;}\n" \
70 "table#settings tr#modified{" \
71 " background: #FFFFBA;}\n" \
72 "table#settings tr#modified:nth-child(even){" \
73 " background: #ffffA0;}\n" \
74 "</style>\n"
76 int js_show_wl(struct tab *, struct karg *);
77 int pl_show_wl(struct tab *, struct karg *);
78 int https_show_wl(struct tab *, struct karg *);
79 int xtp_page_set(struct tab *, struct karg *);
80 int xtp_page_rt(struct tab *, struct karg *);
81 int marco(struct tab *, struct karg *);
82 int startpage(struct tab *, struct karg *);
83 const char * marco_message(int *);
84 void update_cookie_tabs(struct tab *apart_from);
85 int about_webkit(struct tab *, struct karg *);
86 int allthethings(struct tab *, struct karg *);
88 struct about_type about_list[] = {
89 { XT_URI_ABOUT_ABOUT, xtp_page_ab },
90 { XT_URI_ABOUT_ALLTHETHINGS, allthethings },
91 { XT_URI_ABOUT_BLANK, blank },
92 { XT_URI_ABOUT_CERTS, ca_cmd },
93 { XT_URI_ABOUT_COOKIEWL, cookie_show_wl },
94 { XT_URI_ABOUT_COOKIEJAR, xtp_page_cl },
95 { XT_URI_ABOUT_DOWNLOADS, xtp_page_dl },
96 { XT_URI_ABOUT_FAVORITES, xtp_page_fl },
97 { XT_URI_ABOUT_HELP, help },
98 { XT_URI_ABOUT_HISTORY, xtp_page_hl },
99 { XT_URI_ABOUT_JSWL, js_show_wl },
100 { XT_URI_ABOUT_SET, xtp_page_set },
101 { XT_URI_ABOUT_STATS, stats },
102 { XT_URI_ABOUT_MARCO, marco },
103 { XT_URI_ABOUT_STARTPAGE, startpage },
104 { XT_URI_ABOUT_PLUGINWL, pl_show_wl },
105 { XT_URI_ABOUT_HTTPS, https_show_wl },
106 { XT_URI_ABOUT_WEBKIT, about_webkit },
107 { XT_URI_ABOUT_SEARCH, xtp_page_sl },
108 { XT_URI_ABOUT_RUNTIME, xtp_page_rt },
109 { XT_URI_ABOUT_SECVIOLATION, NULL },
112 struct search_type {
113 const char *name;
114 const char *url;
115 } search_list[] = {
116 { "Google (SSL)", "https://encrypted.google.com/search?q=%s&&client=xombrero" },
117 { "Bing", "http://www.bing.com/search?q=%s" },
118 { "Yahoo", "http://search.yahoo.com/search?p=%s" },
119 { "DuckDuckGo", "https://duckduckgo.com/?q=%s" },
120 { "DuckDuckGo (HTML)", "https://duckduckgo.com/html?q=%s" },
121 { "DuckDuckGo (Lite)", "https://duckduckgo.com/lite?q=%s" },
122 { "Ixquick", "https://ixquick.com/do/search?q=%s" },
123 { "Startpage", "https://startpage.com/do/search?q=%s" },
127 * Session IDs.
128 * We use these to prevent people putting xxxt:// URLs on
129 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
131 #define XT_XTP_SES_KEY_SZ 8
132 #define XT_XTP_SES_KEY_HEX_FMT \
133 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8
135 char *dl_session_key; /* downloads */
136 char *hl_session_key; /* history list */
137 char *cl_session_key; /* cookie list */
138 char *fl_session_key; /* favorites list */
139 char *sl_session_key; /* search */
140 char *ab_session_key; /* about */
141 char *sv_session_key; /* secviolation */
142 char *rt_session_key; /* set */
144 int updating_ab_tabs = 0;
145 int updating_fl_tabs = 0;
146 int updating_dl_tabs = 0;
147 int updating_hl_tabs = 0;
148 int updating_cl_tabs = 0;
149 int updating_sl_tabs = 0;
150 int updating_sv_tabs = 0;
151 int updating_set_tabs = 0;
152 struct download_list downloads;
154 size_t
155 about_list_size(void)
157 return (LENGTH(about_list));
160 gchar *
161 get_html_page(gchar *title, gchar *body, gchar *head, bool addstyles)
163 gchar *r;
165 r = g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
166 "<head>\n"
167 "<title>%s</title>\n"
168 "%s"
169 "%s"
170 "</head>\n"
171 "<body>\n"
172 "<h1>%s</h1>\n"
173 "%s\n</body>\n"
174 "</html>",
175 title,
176 addstyles ? XT_PAGE_STYLE : "",
177 head,
178 title,
179 body);
181 return (r);
185 * Display a web page from a HTML string in memory, rather than from a URL
187 void
188 load_webkit_string(struct tab *t, const char *str, gchar *title)
190 char file[PATH_MAX];
191 int i;
193 /* we set this to indicate we want to manually do navaction */
194 if (t->bfl)
195 t->item = webkit_web_back_forward_list_get_current_item(t->bfl);
197 t->xtp_meaning = XT_XTP_TAB_MEANING_NORMAL;
198 if (title) {
199 /* set t->xtp_meaning */
200 for (i = 0; i < LENGTH(about_list); i++)
201 if (!strcmp(title, about_list[i].name)) {
202 t->xtp_meaning = i;
203 break;
206 webkit_web_view_load_string(t->wv, str, NULL, encoding,
207 XT_XTP_STR);
208 #if GTK_CHECK_VERSION(2, 20, 0)
209 gtk_spinner_stop(GTK_SPINNER(t->spinner));
210 gtk_widget_hide(t->spinner);
211 #endif
212 snprintf(file, sizeof file, "%s" PS "%s", resource_dir, icons[0]);
213 xt_icon_from_file(t, file);
218 blank(struct tab *t, struct karg *args)
220 if (t == NULL)
221 show_oops(NULL, "blank invalid parameters");
223 load_webkit_string(t, "", XT_URI_ABOUT_BLANK);
225 return (0);
229 help(struct tab *t, struct karg *args)
231 char *page, *head, *body;
233 if (t == NULL)
234 show_oops(NULL, "help invalid parameters");
236 head = "<meta http-equiv=\"REFRESH\" content=\"0;"
237 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xombrero\">"
238 "</head>\n";
239 body = "xombrero man page <a href=\"http://opensource.conformal.com/"
240 "cgi-bin/man-cgi?xombrero\">http://opensource.conformal.com/"
241 "cgi-bin/man-cgi?xombrero</a>";
243 page = get_html_page(XT_NAME, body, head, FALSE);
245 load_webkit_string(t, page, XT_URI_ABOUT_HELP);
246 g_free(page);
248 return (0);
252 stats(struct tab *t, struct karg *args)
254 char *page, *body, *s, line[64 * 1024];
255 uint64_t line_count = 0;
256 FILE *r_cookie_f;
258 if (t == NULL)
259 show_oops(NULL, "stats invalid parameters");
261 line[0] = '\0';
262 if (save_rejected_cookies) {
263 if ((r_cookie_f = fopen(rc_fname, "r"))) {
264 for (;;) {
265 s = fgets(line, sizeof line, r_cookie_f);
266 if (s == NULL || feof(r_cookie_f) ||
267 ferror(r_cookie_f))
268 break;
269 line_count++;
271 fclose(r_cookie_f);
272 snprintf(line, sizeof line,
273 "<br/>Cookies blocked(*) total: %" PRIu64,
274 line_count);
275 } else
276 show_oops(t, "Can't open blocked cookies file: %s",
277 strerror(errno));
280 body = g_strdup_printf(
281 "Cookies blocked(*) this session: %" PRIu64
282 "%s"
283 "<p><small><b>*</b> results vary based on settings</small></p>",
284 blocked_cookies,
285 line);
287 page = get_html_page("Statistics", body, "", 0);
288 g_free(body);
290 load_webkit_string(t, page, XT_URI_ABOUT_STATS);
291 g_free(page);
293 return (0);
296 void
297 show_certs(struct tab *t, gnutls_x509_crt_t *certs,
298 size_t cert_count, char *title)
300 gnutls_datum_t cinfo;
301 char *tmp, *body;
302 int i;
304 body = g_strdup("");
306 for (i = 0; i < cert_count; i++) {
307 if (gnutls_x509_crt_print(certs[i], GNUTLS_CRT_PRINT_FULL,
308 &cinfo))
309 return;
311 tmp = body;
312 body = g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
313 body, i, cinfo.data);
314 gnutls_free(cinfo.data);
315 g_free(tmp);
318 tmp = get_html_page(title, body, "", 0);
319 g_free(body);
321 load_webkit_string(t, tmp, XT_URI_ABOUT_CERTS);
322 g_free(tmp);
326 ca_cmd(struct tab *t, struct karg *args)
328 FILE *f = NULL;
329 int rv = 1, certs = 0, certs_read;
330 struct stat sb;
331 gnutls_datum_t dt;
332 gnutls_x509_crt_t *c = NULL;
333 char *certs_buf = NULL, *s;
335 if ((f = fopen(ssl_ca_file, "r")) == NULL) {
336 show_oops(t, "Can't open CA file: %s", ssl_ca_file);
337 return (1);
340 if (fstat(fileno(f), &sb) == -1) {
341 show_oops(t, "Can't stat CA file: %s", ssl_ca_file);
342 goto done;
345 certs_buf = g_malloc(sb.st_size + 1);
346 if (fread(certs_buf, 1, sb.st_size, f) != sb.st_size) {
347 show_oops(t, "Can't read CA file: %s", strerror(errno));
348 goto done;
350 certs_buf[sb.st_size] = '\0';
352 s = certs_buf;
353 while ((s = strstr(s, "BEGIN CERTIFICATE"))) {
354 certs++;
355 s += strlen("BEGIN CERTIFICATE");
358 bzero(&dt, sizeof dt);
359 dt.data = (unsigned char *)certs_buf;
360 dt.size = sb.st_size;
361 c = g_malloc(sizeof(gnutls_x509_crt_t) * certs);
362 certs_read = gnutls_x509_crt_list_import(c, (unsigned int *)&certs, &dt,
363 GNUTLS_X509_FMT_PEM, 0);
364 if (certs_read <= 0) {
365 show_oops(t, "No cert(s) available");
366 goto done;
368 show_certs(t, c, certs_read, "Certificate Authority Certificates");
369 done:
370 if (c)
371 g_free(c);
372 if (certs_buf)
373 g_free(certs_buf);
374 if (f)
375 fclose(f);
377 return (rv);
381 cookie_show_wl(struct tab *t, struct karg *args)
383 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
384 wl_show(t, args, "Cookie White List", &c_wl);
386 return (0);
390 js_show_wl(struct tab *t, struct karg *args)
392 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
393 wl_show(t, args, "JavaScript White List", &js_wl);
395 return (0);
399 cookie_cmd(struct tab *t, struct karg *args)
401 if (args->i & XT_SHOW)
402 wl_show(t, args, "Cookie White List", &c_wl);
403 else if (args->i & XT_WL_TOGGLE) {
404 args->i |= XT_WL_RELOAD;
405 toggle_cwl(t, args);
406 } else if (args->i & XT_SAVE) {
407 args->i |= XT_WL_RELOAD;
408 wl_save(t, args, XT_WL_COOKIE);
409 } else if (args->i & XT_DELETE) {
410 remove_cookie_all();
411 update_cookie_tabs(NULL);
414 return (0);
418 js_cmd(struct tab *t, struct karg *args)
420 if (args->i & XT_SHOW)
421 wl_show(t, args, "JavaScript White List", &js_wl);
422 else if (args->i & XT_SAVE) {
423 args->i |= XT_WL_RELOAD;
424 wl_save(t, args, XT_WL_JAVASCRIPT);
425 } else if (args->i & XT_WL_TOGGLE) {
426 args->i |= XT_WL_RELOAD;
427 toggle_js(t, args);
428 } else if (args->i & XT_DELETE)
429 show_oops(t, "'js delete' currently unimplemented");
431 return (0);
435 pl_show_wl(struct tab *t, struct karg *args)
437 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
438 wl_show(t, args, "Plugin White List", &pl_wl);
440 return (0);
444 pl_cmd(struct tab *t, struct karg *args)
446 if (args->i & XT_SHOW)
447 wl_show(t, args, "Plugin White List", &pl_wl);
448 else if (args->i & XT_SAVE) {
449 args->i |= XT_WL_RELOAD;
450 wl_save(t, args, XT_WL_PLUGIN);
451 } else if (args->i & XT_WL_TOGGLE) {
452 args->i |= XT_WL_RELOAD;
453 toggle_pl(t, args);
454 } else if (args->i & XT_DELETE)
455 show_oops(t, "'plugin delete' currently unimplemented");
457 return (0);
461 https_show_wl(struct tab *t, struct karg *args)
463 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
464 wl_show(t, args, "HTTPS Force List", &force_https);
466 return (0);
470 https_cmd(struct tab *t, struct karg *args)
472 if (args->i & XT_SHOW)
473 wl_show(t, args, "HTTPS Force List", &force_https);
474 else if (args->i & XT_SAVE) {
475 args->i |= XT_WL_RELOAD;
476 wl_save(t, args, XT_WL_HTTPS);
477 } else if (args->i & XT_WL_TOGGLE) {
478 args->i |= XT_WL_RELOAD;
479 toggle_force_https(t, args);
480 } else if (args->i & XT_DELETE)
481 show_oops(t, "https delete' currently unimplemented");
483 return (0);
487 * cancel, remove, etc. downloads
489 void
490 xtp_handle_dl(struct tab *t, uint8_t cmd, int id, const char *query)
492 struct download find, *d = NULL;
493 #ifndef __MINGW32__
494 char *file = NULL;
495 const char *uri = NULL;
496 #endif
498 DNPRINTF(XT_D_DOWNLOAD, "download control: cmd %d, id %d\n", cmd, id);
500 /* some commands require a valid download id */
501 if (cmd != XT_XTP_DL_LIST) {
502 /* lookup download in question */
503 find.id = id;
504 d = RB_FIND(download_list, &downloads, &find);
506 if (d == NULL) {
507 show_oops(t, "%s: no such download", __func__);
508 return;
512 /* decide what to do */
513 switch (cmd) {
514 case XT_XTP_DL_START:
515 /* our downloads always needs to be
516 * restarted if called from here
518 download_start(t, d, XT_DL_RESTART);
519 break;
520 case XT_XTP_DL_CANCEL:
521 webkit_download_cancel(d->download);
522 g_object_unref(d->download);
523 RB_REMOVE(download_list, &downloads, d);
524 break;
525 case XT_XTP_DL_UNLINK:
526 #ifdef __MINGW32__
527 /* XXX uri's aren't handled properly on windows? */
528 unlink(webkit_download_get_destination_uri(d->download));
529 #else
530 uri = webkit_download_get_destination_uri(d->download);
531 if ((file = g_filename_from_uri(uri, NULL, NULL)) != NULL) {
532 unlink(file);
533 g_free(file);
535 #endif
536 /* FALLTHROUGH */
537 case XT_XTP_DL_REMOVE:
538 webkit_download_cancel(d->download); /* just incase */
539 g_object_unref(d->download);
540 RB_REMOVE(download_list, &downloads, d);
541 break;
542 case XT_XTP_DL_LIST:
543 /* Nothing */
544 break;
545 default:
546 show_oops(t, "%s: unknown command", __func__);
547 break;
549 xtp_page_dl(t, NULL);
552 void
553 xtp_handle_hl(struct tab *t, uint8_t cmd, int id, const char *query)
555 struct history *h, *next, *ht;
556 int i = 1;
558 switch (cmd) {
559 case XT_XTP_HL_REMOVE:
560 /* walk backwards, as listed in reverse */
561 for (h = RB_MAX(history_list, &hl); h != NULL; h = next) {
562 next = RB_PREV(history_list, &hl, h);
563 if (id == i) {
564 RB_REMOVE(history_list, &hl, h);
565 g_free((gpointer) h->title);
566 g_free((gpointer) h->uri);
567 g_free(h);
568 break;
570 i++;
572 break;
573 case XT_XTP_HL_REMOVE_ALL:
574 RB_FOREACH_SAFE(h, history_list, &hl, ht)
575 RB_REMOVE(history_list, &hl, h);
576 break;
577 case XT_XTP_HL_LIST:
578 /* Nothing - just xtp_page_hl() below */
579 break;
580 default:
581 show_oops(t, "%s: unknown command", __func__);
582 break;
585 xtp_page_hl(t, NULL);
588 /* remove a favorite */
589 void
590 remove_favorite(struct tab *t, int index)
592 char file[PATH_MAX], *title, *uri = NULL;
593 char *new_favs, *tmp;
594 FILE *f;
595 int i;
596 size_t len, lineno;
598 /* open favorites */
599 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
601 if ((f = fopen(file, "r")) == NULL) {
602 show_oops(t, "%s: can't open favorites: %s",
603 __func__, strerror(errno));
604 return;
607 /* build a string which will become the new favroites file */
608 new_favs = g_strdup("");
610 for (i = 1;;) {
611 if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
612 if (feof(f) || ferror(f))
613 break;
614 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
615 if (len == 0) {
616 free(title);
617 title = NULL;
618 continue;
621 if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) {
622 if (feof(f) || ferror(f)) {
623 show_oops(t, "%s: can't parse favorites %s",
624 __func__, strerror(errno));
625 goto clean;
629 /* as long as this isn't the one we are deleting add to file */
630 if (i != index) {
631 tmp = new_favs;
632 new_favs = g_strdup_printf("%s%s\n%s\n",
633 new_favs, title, uri);
634 g_free(tmp);
637 free(uri);
638 uri = NULL;
639 free(title);
640 title = NULL;
641 i++;
643 fclose(f);
645 /* write back new favorites file */
646 if ((f = fopen(file, "w")) == NULL) {
647 show_oops(t, "%s: can't open favorites: %s",
648 __func__, strerror(errno));
649 goto clean;
652 if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
653 show_oops(t, "%s: can't fwrite", __func__);
654 fclose(f);
656 clean:
657 if (uri)
658 free(uri);
659 if (title)
660 free(title);
662 g_free(new_favs);
666 add_favorite(struct tab *t, struct karg *args)
668 char file[PATH_MAX];
669 FILE *f;
670 char *line = NULL;
671 size_t urilen, linelen;
672 const gchar *uri, *title;
674 if (t == NULL)
675 return (1);
677 /* don't allow adding of xtp pages to favorites */
678 if (t->xtp_meaning != XT_XTP_TAB_MEANING_NORMAL) {
679 show_oops(t, "%s: can't add xtp pages to favorites", __func__);
680 return (1);
683 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
684 if ((f = fopen(file, "r+")) == NULL) {
685 show_oops(t, "Can't open favorites file: %s", strerror(errno));
686 return (1);
689 title = get_title(t, FALSE);
690 uri = get_uri(t);
692 if (title == NULL || uri == NULL) {
693 show_oops(t, "can't add page to favorites");
694 goto done;
697 urilen = strlen(uri);
699 for (;;) {
700 if ((line = fparseln(f, &linelen, NULL, NULL, 0)) == NULL)
701 if (feof(f) || ferror(f))
702 break;
704 if (linelen == urilen && !strcmp(line, uri))
705 goto done;
707 free(line);
708 line = NULL;
711 fprintf(f, "\n%s\n%s", title, uri);
712 done:
713 if (line)
714 free(line);
715 fclose(f);
717 update_favorite_tabs(NULL);
719 return (0);
722 char *
723 search_engine_add(char *body, const char *name, const char *url, int select)
725 char *b = body;
727 body = g_strdup_printf("%s<tr>"
728 "<td>%s</td>"
729 "<td>%s</td>"
730 "<td style='text-align: center'>"
731 "<a href='%s%d/%s/%d/%d'>[ Select ]</a></td>"
732 "</tr>\n",
733 body,
734 name,
735 url,
736 XT_XTP_STR, XT_XTP_SL, sl_session_key, XT_XTP_SL_SET, select);
737 g_free(b);
738 return (body);
741 void
742 xtp_handle_ab(struct tab *t, uint8_t cmd, int arg, const char *query)
744 char config[PATH_MAX];
745 char *cmdstr;
746 char **sv;
748 switch (cmd) {
749 case XT_XTP_AB_EDIT_CONF:
750 if (external_editor == NULL || strlen(external_editor) == 0) {
751 show_oops(t, "external_editor is unset");
752 break;
755 snprintf(config, sizeof config, "%s" PS ".%s", pwd->pw_dir,
756 XT_CONF_FILE);
757 sv = g_strsplit(external_editor, "<file>", -1);
758 cmdstr = g_strjoinv(config, sv);
759 g_strfreev(sv);
760 sv = g_strsplit_set(cmdstr, " \t", -1);
762 if (!g_spawn_async(NULL, sv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
763 NULL, NULL))
764 show_oops(t, "%s: could not spawn process", __func__);
766 g_strfreev(sv);
767 g_free(cmdstr);
768 break;
769 default:
770 show_oops(t, "%s, invalid about command", __func__);
771 break;
773 xtp_page_ab(t, NULL);
775 void
776 xtp_handle_fl(struct tab *t, uint8_t cmd, int arg, const char *query)
778 switch (cmd) {
779 case XT_XTP_FL_LIST:
780 /* nothing, just the below call to xtp_page_fl() */
781 break;
782 case XT_XTP_FL_REMOVE:
783 remove_favorite(t, arg);
784 break;
785 default:
786 show_oops(t, "%s: invalid favorites command", __func__);
787 break;
790 xtp_page_fl(t, NULL);
793 void
794 xtp_handle_cl(struct tab *t, uint8_t cmd, int arg, const char *query)
796 switch (cmd) {
797 case XT_XTP_CL_LIST:
798 /* nothing, just xtp_page_cl() */
799 break;
800 case XT_XTP_CL_REMOVE:
801 remove_cookie(arg);
802 break;
803 case XT_XTP_CL_REMOVE_DOMAIN:
804 remove_cookie_domain(arg);
805 break;
806 case XT_XTP_CL_REMOVE_ALL:
807 remove_cookie_all();
808 break;
809 default:
810 show_oops(t, "%s: unknown cookie xtp command", __func__);
811 break;
814 xtp_page_cl(t, NULL);
817 void
818 xtp_handle_sl(struct tab *t, uint8_t cmd, int arg, const char *query)
820 const char *search;
821 char *enc_search, *uri;
822 char **sv;
824 switch (cmd) {
825 case XT_XTP_SL_SET:
826 set_search_string((char *)search_list[arg].url);
827 if (save_runtime_setting("search_string", search_list[arg].url))
828 show_oops(t, "could not set search_string in runtime");
829 break;
830 default:
831 show_oops(t, "%s: unknown search xtp command", __func__);
832 break;
835 search = gtk_entry_get_text(GTK_ENTRY(t->search_entry)); /* static */
836 enc_search = soup_uri_encode(search, XT_RESERVED_CHARS);
837 sv = g_strsplit(search_string, "%s", 2);
838 uri = g_strjoinv(enc_search, sv);
839 load_uri(t, uri);
840 g_free(enc_search);
841 g_strfreev(sv);
842 g_free(uri);
845 void
846 xtp_handle_sv(struct tab *t, uint8_t cmd, int id, const char *query)
848 SoupURI *soupuri = NULL;
849 struct karg args = {0};
850 struct secviolation find, *sv;
851 struct sv_ignore *svi = NULL;
853 find.xtp_arg = id;
854 if ((sv = RB_FIND(secviolation_list, &svl, &find)) == NULL)
855 return;
857 args.ptr = (void *)sv->t;
858 args.s = sv->uri;
860 switch (cmd) {
861 case XT_XTP_SV_SHOW_CERT:
862 args.i = XT_SHOW;
863 cert_cmd(t, &args);
864 break;
865 case XT_XTP_SV_ALLOW_SESSION:
866 soupuri = soup_uri_new(sv->uri);
867 svi = malloc(sizeof(struct sv_ignore));
868 svi->domain = g_strdup(soupuri->host);
869 RB_INSERT(sv_ignore_list, &svil, svi);
870 load_uri(t, sv->uri);
871 focus_webview(t);
872 break;
873 case XT_XTP_SV_CACHE:
874 args.i = XT_CACHE;
875 cert_cmd(t, &args);
876 load_uri(t, sv->uri);
877 focus_webview(t);
878 break;
879 default:
880 show_oops(t, "%s: invalid secviolation command", __func__);
881 break;
884 g_free(sv->uri);
885 if (soupuri)
886 soup_uri_free(soupuri);
887 RB_REMOVE(secviolation_list, &svl, sv);
890 void
891 xtp_handle_rt(struct tab *t, uint8_t cmd, int id, const char *query)
893 struct set_reject *sr;
894 GHashTable *new_settings = NULL;
895 int modify;
896 char *val, *curval, *s;
897 int i = 0;
899 switch (cmd) {
900 case XT_XTP_RT_SAVE:
901 if (query == NULL)
902 break;
903 new_settings = soup_form_decode(query);
904 for (i = 0; i < get_settings_size(); ++i) {
905 if (!rs[i].activate)
906 continue;
907 sr = g_malloc(sizeof *sr);
908 val = (char *)g_hash_table_lookup(new_settings,
909 rs[i].name);
910 modify = 0;
911 switch (rs[i].type) {
912 case XT_S_INT: /* FALLTHROUGH */
913 case XT_S_BOOL:
914 if (atoi(val) != *rs[i].ival)
915 modify = 1;
916 break;
917 case XT_S_FLOAT:
918 if (atof(val) != *rs[i].fval)
919 modify = 1;
920 break;
921 case XT_S_STR:
922 s = (rs[i].sval == NULL || *rs[i].sval == NULL)
923 ? "" : *rs[i].sval;
924 if (rs[i].sval && g_strcmp0(val, s))
925 modify = 1;
926 else if (rs[i].s && rs[i].s->get) {
927 curval = rs[i].s->get(NULL);
928 if (g_strcmp0(val, curval))
929 modify = 1;
930 g_free(curval);
932 break;
933 case XT_S_INVALID: /* FALLTHROUGH */
934 default:
935 break;
937 if (rs[i].activate(val)) {
938 sr->name = g_strdup(rs[i].name);
939 sr->value = g_strdup(val);
940 TAILQ_INSERT_TAIL(&srl, sr, entry);
941 continue;
943 if (modify)
944 if (save_runtime_setting(rs[i].name, val))
945 show_oops(t, "error");
947 break;
948 default:
949 show_oops(t, "%s: invalid set command", __func__);
950 break;
953 if (new_settings)
954 g_hash_table_destroy(new_settings);
956 xtp_page_rt(t, NULL);
959 /* link an XTP class to it's session key and handler function */
960 struct xtp_despatch {
961 uint8_t xtp_class;
962 char **session_key;
963 void (*handle_func)(struct tab *, uint8_t, int,
964 const char *query);
967 struct xtp_despatch xtp_despatches[] = {
968 { XT_XTP_DL, &dl_session_key, xtp_handle_dl },
969 { XT_XTP_HL, &hl_session_key, xtp_handle_hl },
970 { XT_XTP_FL, &fl_session_key, xtp_handle_fl },
971 { XT_XTP_CL, &cl_session_key, xtp_handle_cl },
972 { XT_XTP_SL, &sl_session_key, xtp_handle_sl },
973 { XT_XTP_AB, &ab_session_key, xtp_handle_ab },
974 { XT_XTP_SV, &sv_session_key, xtp_handle_sv },
975 { XT_XTP_RT, &rt_session_key, xtp_handle_rt },
976 { XT_XTP_INVALID, NULL, NULL }
980 * generate a session key to secure xtp commands.
981 * pass in a ptr to the key in question and it will
982 * be modified in place.
984 void
985 generate_xtp_session_key(char **key)
987 uint8_t rand_bytes[XT_XTP_SES_KEY_SZ];
989 /* free old key */
990 if (*key)
991 g_free(*key);
993 /* make a new one */
994 arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ);
995 *key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT,
996 rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
997 rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]);
999 DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key);
1002 void
1003 xtp_generate_keys(void)
1005 /* generate session keys for xtp pages */
1006 generate_xtp_session_key(&dl_session_key);
1007 generate_xtp_session_key(&hl_session_key);
1008 generate_xtp_session_key(&cl_session_key);
1009 generate_xtp_session_key(&fl_session_key);
1010 generate_xtp_session_key(&ab_session_key);
1011 generate_xtp_session_key(&sv_session_key);
1015 * validate a xtp session key.
1016 * return (1) if OK
1019 validate_xtp_session_key(struct tab *t, char *trusted, char *untrusted)
1021 if (strcmp(trusted, untrusted) != 0) {
1022 show_oops(t, "%s: xtp session key mismatch possible spoof",
1023 __func__);
1024 return (0);
1027 return (1);
1031 * is the url xtp protocol? (xxxt://)
1032 * if so, parse and despatch correct bahvior
1035 parse_xtp_url(struct tab *t, const char *uri_str)
1037 SoupURI *uri = NULL;
1038 struct xtp_despatch *dsp, *dsp_match = NULL;
1039 int ret = FALSE;
1040 int class = 0;
1041 char **sv = NULL;
1044 * uri->host = class
1045 * sv[0] = session key
1046 * sv[1] = command
1047 * sv[2] = optional argument
1050 DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, url);
1052 if ((uri = soup_uri_new(uri_str)) == NULL)
1053 goto clean;
1054 if (strncmp(uri->scheme, XT_XTP_SCHEME, strlen(XT_XTP_SCHEME)))
1055 goto clean;
1056 if (uri->host == NULL || strlen(uri->host) == 0)
1057 goto clean;
1058 else
1059 class = atoi(uri->host);
1060 if ((sv = g_strsplit(uri->path + 1, "/", 3)) == NULL)
1061 goto clean;
1063 if (sv[0] == NULL || sv[1] == NULL)
1064 goto clean;
1066 dsp = xtp_despatches;
1067 class = atoi(uri->host);
1068 while (dsp->xtp_class) {
1069 if (dsp->xtp_class == class) {
1070 dsp_match = dsp;
1071 break;
1073 dsp++;
1076 /* did we find one atall? */
1077 if (dsp_match == NULL) {
1078 show_oops(t, "%s: no matching xtp despatch found", __func__);
1079 goto clean;
1082 /* check session key and call despatch function */
1083 if (validate_xtp_session_key(t, *(dsp_match->session_key), sv[0])) {
1084 ret = TRUE; /* all is well, this was a valid xtp request */
1085 if (sv[2])
1086 dsp_match->handle_func(t, atoi(sv[1]), atoi(sv[2]),
1087 uri->query);
1088 else
1089 dsp_match->handle_func(t, atoi(sv[1]), 0, uri->query);
1092 clean:
1093 if (uri)
1094 soup_uri_free(uri);
1095 if (sv)
1096 g_strfreev(sv);
1098 return (ret);
1102 * update all favorite tabs apart from one. Pass NULL if
1103 * you want to update all.
1105 void
1106 update_favorite_tabs(struct tab *apart_from)
1108 struct tab *t;
1109 if (!updating_fl_tabs) {
1110 updating_fl_tabs = 1; /* stop infinite recursion */
1111 TAILQ_FOREACH(t, &tabs, entry)
1112 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL)
1113 && (t != apart_from))
1114 xtp_page_fl(t, NULL);
1115 updating_fl_tabs = 0;
1120 * update all download tabs apart from one. Pass NULL if
1121 * you want to update all.
1123 void
1124 update_download_tabs(struct tab *apart_from)
1126 struct tab *t;
1127 if (!updating_dl_tabs) {
1128 updating_dl_tabs = 1; /* stop infinite recursion */
1129 TAILQ_FOREACH(t, &tabs, entry)
1130 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_DL)
1131 && (t != apart_from))
1132 xtp_page_dl(t, NULL);
1133 updating_dl_tabs = 0;
1138 * update all cookie tabs apart from one. Pass NULL if
1139 * you want to update all.
1141 void
1142 update_cookie_tabs(struct tab *apart_from)
1144 struct tab *t;
1145 if (!updating_cl_tabs) {
1146 updating_cl_tabs = 1; /* stop infinite recursion */
1147 TAILQ_FOREACH(t, &tabs, entry)
1148 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL)
1149 && (t != apart_from))
1150 xtp_page_cl(t, NULL);
1151 updating_cl_tabs = 0;
1156 * update all history tabs apart from one. Pass NULL if
1157 * you want to update all.
1159 void
1160 update_history_tabs(struct tab *apart_from)
1162 struct tab *t;
1164 if (!updating_hl_tabs) {
1165 updating_hl_tabs = 1; /* stop infinite recursion */
1166 TAILQ_FOREACH(t, &tabs, entry)
1167 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL)
1168 && (t != apart_from))
1169 xtp_page_hl(t, NULL);
1170 updating_hl_tabs = 0;
1175 * update all search tabs apart from one. Pass NULL if
1176 * you want to update all.
1178 void
1179 update_search_tabs(struct tab *apart_from)
1181 struct tab *t;
1183 if (!updating_sl_tabs) {
1184 updating_sl_tabs = 1; /* stop infinite recursion */
1185 TAILQ_FOREACH(t, &tabs, entry)
1186 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_SL)
1187 && (t != apart_from))
1188 xtp_page_sl(t, NULL);
1189 updating_sl_tabs = 0;
1194 * update all about tabs apart from one. Pass NULL if
1195 * you want to update all.
1197 void
1198 update_about_tabs(struct tab *apart_from)
1200 struct tab *t;
1202 if (!updating_ab_tabs) {
1203 updating_ab_tabs = 1; /* stop infinite recursion */
1204 TAILQ_FOREACH(t, &tabs, entry)
1205 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_AB)
1206 && (t != apart_from))
1207 xtp_page_ab(t, NULL);
1208 updating_ab_tabs = 0;
1213 * update all secviolation tabs apart from one. Pass NULL if
1214 * you want to update all.
1216 void
1217 update_secviolation_tabs(struct tab *apart_from)
1219 struct tab *t;
1221 if (!updating_sv_tabs) {
1222 updating_sv_tabs = 1; /* stop infinite recursion */
1223 TAILQ_FOREACH(t, &tabs, entry)
1224 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_SV)
1225 && (t != apart_from))
1226 xtp_page_sv(t, NULL);
1227 updating_sv_tabs = 0;
1232 xtp_page_ab(struct tab *t, struct karg *args)
1234 char *page, *body;
1236 if (t == NULL)
1237 show_oops(NULL, "about invalid parameters");
1240 * Generate a new session key for next page instance.
1241 * This only happens for the top level call to xtp_page_ab()
1242 * in which case updating_sl_tabs is 0.
1244 if (!updating_ab_tabs)
1245 generate_xtp_session_key(&ab_session_key);
1247 body = g_strdup_printf("<b>Version: %s</b>"
1248 #ifdef XOMBRERO_BUILDSTR
1249 "<br><b>Build: %s</b>"
1250 #endif
1251 "<br><b>WebKit: %d.%d.%d</b>"
1252 "<br><b>User Agent: %d.%d</b>"
1253 #ifdef WEBKITGTK_API_VERSION
1254 "<br><b>WebKit API: %.1f</b>"
1255 #endif
1256 "<br><b>Configuration: %s" PS "<a href='%s%d/%s/%d'>.%s</a>"
1257 " (remember to restart the browser after any changes)</b>"
1258 "<p>"
1259 "Authors:"
1260 "<ul>"
1261 "<li>Marco Peereboom &lt;marco@peereboom.us&gt;</li>"
1262 "<li>Stevan Andjelkovic &lt;stevan@student.chalmers.se&gt;</li>"
1263 "<li>Edd Barrett &lt;vext01@gmail.com&gt;</li>"
1264 "<li>Todd T. Fries &lt;todd@fries.net&gt;</li>"
1265 "<li>Raphael Graf &lt;r@undefined.ch&gt;</li>"
1266 "<li>Michal Mazurek &lt;akfaew@jasminek.net&gt;</li>"
1267 "<li>Josh Rickmar &lt;jrick@devio.us&gt;</li>"
1268 "</ul>"
1269 "Copyrights and licenses can be found on the xombrero "
1270 "<a href=\"http://opensource.conformal.com/wiki/xombrero\">website</a>"
1271 "</p>",
1272 #ifdef XOMBRERO_BUILDSTR
1273 version, XOMBRERO_BUILDSTR,
1274 #else
1275 version,
1276 #endif
1277 WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION,
1278 WEBKIT_USER_AGENT_MAJOR_VERSION, WEBKIT_USER_AGENT_MINOR_VERSION
1279 #ifdef WEBKITGTK_API_VERSION
1280 ,WEBKITGTK_API_VERSION
1281 #endif
1282 ,pwd->pw_dir,
1283 XT_XTP_STR,
1284 XT_XTP_AB,
1285 ab_session_key,
1286 XT_XTP_AB_EDIT_CONF,
1287 XT_CONF_FILE
1290 page = get_html_page("About", body, "", 0);
1291 g_free(body);
1293 load_webkit_string(t, page, XT_URI_ABOUT_ABOUT);
1295 update_about_tabs(t);
1297 g_free(page);
1299 return (0);
1302 /* show a list of favorites (bookmarks) */
1304 xtp_page_fl(struct tab *t, struct karg *args)
1306 char file[PATH_MAX];
1307 FILE *f;
1308 char *uri = NULL, *title = NULL;
1309 size_t len, lineno = 0;
1310 int i, failed = 0;
1311 char *body, *tmp, *page = NULL;
1312 const char delim[3] = {'\\', '\\', '\0'};
1314 DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
1316 if (t == NULL)
1317 warn("%s: bad param", __func__);
1319 /* new session key */
1320 if (!updating_fl_tabs)
1321 generate_xtp_session_key(&fl_session_key);
1323 /* open favorites */
1324 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
1325 if ((f = fopen(file, "r")) == NULL) {
1326 show_oops(t, "Can't open favorites file: %s", strerror(errno));
1327 return (1);
1330 /* body */
1331 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
1332 "<th style='width: 40px'>&#35;</th><th>Link</th>"
1333 "<th style='width: 40px'>Rm</th></tr>\n");
1335 for (i = 1;;) {
1336 if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
1337 break;
1338 if (strlen(title) == 0) {
1339 free(title);
1340 title = NULL;
1341 continue;
1344 if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
1345 if (feof(f) || ferror(f)) {
1346 show_oops(t, "favorites file corrupt");
1347 failed = 1;
1348 break;
1351 tmp = body;
1352 body = g_strdup_printf("%s<tr>"
1353 "<td>%d</td>"
1354 "<td><a href='%s'>%s</a></td>"
1355 "<td style='text-align: center'>"
1356 "<a href='%s%d/%s/%d/%d'>X</a></td>"
1357 "</tr>\n",
1358 body, i, uri, title,
1359 XT_XTP_STR, XT_XTP_FL, fl_session_key, XT_XTP_FL_REMOVE, i);
1361 g_free(tmp);
1363 free(uri);
1364 uri = NULL;
1365 free(title);
1366 title = NULL;
1367 i++;
1369 fclose(f);
1371 /* if none, say so */
1372 if (i == 1) {
1373 tmp = body;
1374 body = g_strdup_printf("%s<tr>"
1375 "<td colspan='3' style='text-align: center'>"
1376 "No favorites - To add one use the 'favadd' command."
1377 "</td></tr>", body);
1378 g_free(tmp);
1381 tmp = body;
1382 body = g_strdup_printf("%s</table>", body);
1383 g_free(tmp);
1385 if (uri)
1386 free(uri);
1387 if (title)
1388 free(title);
1390 /* render */
1391 if (!failed) {
1392 page = get_html_page("Favorites", body, "", 1);
1393 load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES);
1394 g_free(page);
1397 update_favorite_tabs(t);
1399 if (body)
1400 g_free(body);
1402 return (failed);
1406 * Return a new string with a download row (in html)
1407 * appended. Old string is freed.
1409 char *
1410 xtp_page_dl_row(struct tab *t, char *html, struct download *dl)
1413 WebKitDownloadStatus stat;
1414 const gchar *destination;
1415 char *status_html = NULL, *cmd_html = NULL, *new_html;
1416 gdouble progress;
1417 char cur_sz[FMT_SCALED_STRSIZE];
1418 char tot_sz[FMT_SCALED_STRSIZE];
1419 char *xtp_prefix;
1421 DNPRINTF(XT_D_DOWNLOAD, "%s: dl->id %d\n", __func__, dl->id);
1423 /* All actions wil take this form:
1424 * xxxt://class/seskey
1426 xtp_prefix = g_strdup_printf("%s%d/%s/",
1427 XT_XTP_STR, XT_XTP_DL, dl_session_key);
1429 stat = webkit_download_get_status(dl->download);
1431 switch (stat) {
1432 case WEBKIT_DOWNLOAD_STATUS_FINISHED:
1433 status_html = g_strdup_printf("Finished");
1434 cmd_html = g_strdup_printf(
1435 "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1436 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1437 XT_XTP_DL_UNLINK, dl->id);
1438 break;
1439 case WEBKIT_DOWNLOAD_STATUS_STARTED:
1440 /* gather size info */
1441 progress = 100 * webkit_download_get_progress(dl->download);
1443 fmt_scaled(
1444 webkit_download_get_current_size(dl->download), cur_sz);
1445 fmt_scaled(
1446 webkit_download_get_total_size(dl->download), tot_sz);
1448 status_html = g_strdup_printf(
1449 "<div style='width: 100%%' align='center'>"
1450 "<div class='progress-outer'>"
1451 "<div class='progress-inner' style='width: %.2f%%'>"
1452 "</div></div></div>"
1453 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
1454 progress, cur_sz, tot_sz, progress);
1456 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1457 xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
1459 break;
1460 /* LLL */
1461 case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
1462 status_html = g_strdup_printf("Cancelled");
1463 cmd_html = g_strdup_printf(
1464 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1465 xtp_prefix, XT_XTP_DL_START, dl->id,
1466 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1467 XT_XTP_DL_UNLINK, dl->id);
1468 break;
1469 case WEBKIT_DOWNLOAD_STATUS_ERROR:
1470 status_html = g_strdup_printf("Error!");
1471 cmd_html = g_strdup_printf(
1472 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1473 xtp_prefix, XT_XTP_DL_START, dl->id,
1474 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1475 XT_XTP_DL_UNLINK, dl->id);
1476 break;
1477 case WEBKIT_DOWNLOAD_STATUS_CREATED:
1478 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Start</a> / <a href='%s%d/%d'>Cancel</a>",
1479 xtp_prefix, XT_XTP_DL_START, dl->id, xtp_prefix,
1480 XT_XTP_DL_CANCEL, dl->id);
1481 status_html = g_strdup_printf("Created");
1482 break;
1483 default:
1484 show_oops(t, "%s: unknown download status", __func__);
1487 destination = webkit_download_get_destination_uri(dl->download);
1488 /* we might not have a destination set yet */
1489 if (!destination)
1490 destination = webkit_download_get_suggested_filename(dl->download);
1491 new_html = g_strdup_printf(
1492 "%s\n<tr><td>%s</td><td>%s</td>"
1493 "<td style='text-align:center'>%s</td></tr>\n",
1494 html, basename((char *)destination),
1495 status_html, cmd_html);
1496 g_free(html);
1498 if (status_html)
1499 g_free(status_html);
1501 if (cmd_html)
1502 g_free(cmd_html);
1504 g_free(xtp_prefix);
1506 return new_html;
1509 /* cookie management XTP page */
1511 xtp_page_cl(struct tab *t, struct karg *args)
1513 char *body, *page, *tmp;
1514 int i = 1; /* all ids start 1 */
1515 int domain_id = 0;
1516 GSList *sc, *pc, *pc_start;
1517 SoupCookie *c;
1518 char *type, *table_headers, *last_domain;
1520 DNPRINTF(XT_D_CMD, "%s", __func__);
1522 if (t == NULL) {
1523 show_oops(NULL, "%s invalid parameters", __func__);
1524 return (1);
1527 /* Generate a new session key */
1528 if (!updating_cl_tabs)
1529 generate_xtp_session_key(&cl_session_key);
1531 /* table headers */
1532 table_headers = g_strdup_printf("<table><tr>"
1533 "<th>Type</th>"
1534 "<th>Name</th>"
1535 "<th style='width:200px'>Value</th>"
1536 "<th>Path</th>"
1537 "<th>Expires</th>"
1538 "<th>Secure</th>"
1539 "<th>HTTP<br />only</th>"
1540 "<th style='width:40px'>Rm</th></tr>\n");
1542 sc = soup_cookie_jar_all_cookies(s_cookiejar);
1543 pc = soup_cookie_jar_all_cookies(p_cookiejar);
1544 pc_start = pc;
1546 body = g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
1547 "[ Remove All Cookies From All Domains ]</a></div>\n",
1548 XT_XTP_STR, XT_XTP_CL, cl_session_key, XT_XTP_CL_REMOVE_ALL);
1550 last_domain = g_strdup("");
1551 for (; sc; sc = sc->next) {
1552 c = sc->data;
1554 if (strcmp(last_domain, c->domain) != 0) {
1555 /* new domain */
1556 domain_id ++;
1557 g_free(last_domain);
1558 last_domain = g_strdup(c->domain);
1560 if (body != NULL) {
1561 tmp = body;
1562 body = g_strdup_printf("%s</table>"
1563 "<h2>%s</h2><div align=\"center\">"
1564 "<a href='%s%d/%s/%d/%d'>"
1565 "[ Remove All From This Domain ]"
1566 "</a></div>%s\n",
1567 body, c->domain,
1568 XT_XTP_STR, XT_XTP_CL, cl_session_key,
1569 XT_XTP_CL_REMOVE_DOMAIN, domain_id,
1570 table_headers);
1571 g_free(tmp);
1572 } else {
1573 /* first domain */
1574 body = g_strdup_printf("<h2>%s</h2>"
1575 "<div align=\"center\">"
1576 "<a href='%s%d/%s/%d/%d'>"
1577 "[ Remove All From This Domain ]</a></div>%s\n",
1578 c->domain, XT_XTP_STR, XT_XTP_CL,
1579 cl_session_key, XT_XTP_CL_REMOVE_DOMAIN,
1580 domain_id, table_headers);
1584 type = "Session";
1585 for (pc = pc_start; pc; pc = pc->next)
1586 if (soup_cookie_equal(pc->data, c)) {
1587 type = "Session + Persistent";
1588 break;
1591 tmp = body;
1592 body = g_strdup_printf(
1593 "%s\n<tr>"
1594 "<td>%s</td>"
1595 "<td style='word-wrap:normal'>%s</td>"
1596 "<td>"
1597 " <textarea rows='4'>%s</textarea>"
1598 "</td>"
1599 "<td>%s</td>"
1600 "<td>%s</td>"
1601 "<td>%d</td>"
1602 "<td>%d</td>"
1603 "<td style='text-align:center'>"
1604 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1605 body,
1606 type,
1607 c->name,
1608 c->value,
1609 c->path,
1610 c->expires ?
1611 soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
1612 c->secure,
1613 c->http_only,
1615 XT_XTP_STR,
1616 XT_XTP_CL,
1617 cl_session_key,
1618 XT_XTP_CL_REMOVE,
1622 g_free(tmp);
1623 i++;
1626 soup_cookies_free(sc);
1627 soup_cookies_free(pc);
1629 /* small message if there are none */
1630 if (i == 1) {
1631 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1632 "colspan='8'>No Cookies</td></tr>\n", table_headers);
1634 tmp = body;
1635 body = g_strdup_printf("%s</table>", body);
1636 g_free(tmp);
1638 page = get_html_page("Cookie Jar", body, "", TRUE);
1639 g_free(body);
1640 g_free(table_headers);
1641 g_free(last_domain);
1643 load_webkit_string(t, page, XT_URI_ABOUT_COOKIEJAR);
1644 update_cookie_tabs(t);
1646 g_free(page);
1648 return (0);
1652 xtp_page_hl(struct tab *t, struct karg *args)
1654 char *body, *page, *tmp;
1655 struct history *h;
1656 int i = 1; /* all ids start 1 */
1658 DNPRINTF(XT_D_CMD, "%s", __func__);
1660 if (t == NULL) {
1661 show_oops(NULL, "%s invalid parameters", __func__);
1662 return (1);
1665 /* Generate a new session key */
1666 if (!updating_hl_tabs)
1667 generate_xtp_session_key(&hl_session_key);
1669 /* body */
1670 body = g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
1671 "[ Remove All ]</a></div>"
1672 "<table style='table-layout:fixed'><tr>"
1673 "<th>URI</th><th>Title</th><th>Last visited</th>"
1674 "<th style='width: 40px'>Rm</th></tr>\n",
1675 XT_XTP_STR, XT_XTP_HL, hl_session_key, XT_XTP_HL_REMOVE_ALL);
1677 RB_FOREACH_REVERSE(h, history_list, &hl) {
1678 tmp = body;
1679 body = g_strdup_printf(
1680 "%s\n<tr>"
1681 "<td><a href='%s'>%s</a></td>"
1682 "<td>%s</td>"
1683 "<td>%s</td>"
1684 "<td style='text-align: center'>"
1685 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1686 body, h->uri, h->uri, h->title, ctime(&h->time),
1687 XT_XTP_STR, XT_XTP_HL, hl_session_key,
1688 XT_XTP_HL_REMOVE, i);
1690 g_free(tmp);
1691 i++;
1694 /* small message if there are none */
1695 if (i == 1) {
1696 tmp = body;
1697 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1698 "colspan='4'>No History</td></tr>\n", body);
1699 g_free(tmp);
1702 tmp = body;
1703 body = g_strdup_printf("%s</table>", body);
1704 g_free(tmp);
1706 page = get_html_page("History", body, "", TRUE);
1707 g_free(body);
1710 * update all history manager tabs as the xtp session
1711 * key has now changed. No need to update the current tab.
1712 * Already did that above.
1714 update_history_tabs(t);
1716 load_webkit_string(t, page, XT_URI_ABOUT_HISTORY);
1717 g_free(page);
1719 return (0);
1723 * Generate a web page detailing the status of any downloads
1726 xtp_page_dl(struct tab *t, struct karg *args)
1728 struct download *dl;
1729 char *body, *page, *tmp;
1730 char *ref;
1731 int n_dl = 1;
1733 DNPRINTF(XT_D_DOWNLOAD, "%s", __func__);
1735 if (t == NULL) {
1736 show_oops(NULL, "%s invalid parameters", __func__);
1737 return (1);
1741 * Generate a new session key for next page instance.
1742 * This only happens for the top level call to xtp_page_dl()
1743 * in which case updating_dl_tabs is 0.
1745 if (!updating_dl_tabs)
1746 generate_xtp_session_key(&dl_session_key);
1748 /* header - with refresh so as to update */
1749 if (refresh_interval >= 1)
1750 ref = g_strdup_printf(
1751 "<meta http-equiv='refresh' content='%u"
1752 ";url=%s%d/%s/%d' />\n",
1753 refresh_interval,
1754 XT_XTP_STR,
1755 XT_XTP_DL,
1756 dl_session_key,
1757 XT_XTP_DL_LIST);
1758 else
1759 ref = g_strdup("");
1761 body = g_strdup_printf("<div align='center'>"
1762 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
1763 "</p><table><tr><th style='width: 60%%'>"
1764 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
1765 XT_XTP_STR, XT_XTP_DL, dl_session_key, XT_XTP_DL_LIST);
1767 RB_FOREACH_REVERSE(dl, download_list, &downloads) {
1768 body = xtp_page_dl_row(t, body, dl);
1769 n_dl++;
1772 /* message if no downloads in list */
1773 if (n_dl == 1) {
1774 tmp = body;
1775 body = g_strdup_printf("%s\n<tr><td colspan='3'"
1776 " style='text-align: center'>"
1777 "No downloads</td></tr>\n", body);
1778 g_free(tmp);
1781 tmp = body;
1782 body = g_strdup_printf("%s</table></div>", body);
1783 g_free(tmp);
1785 page = get_html_page("Downloads", body, ref, 1);
1786 g_free(ref);
1787 g_free(body);
1790 * update all download manager tabs as the xtp session
1791 * key has now changed. No need to update the current tab.
1792 * Already did that above.
1794 update_download_tabs(t);
1796 load_webkit_string(t, page, XT_URI_ABOUT_DOWNLOADS);
1797 g_free(page);
1799 return (0);
1803 xtp_page_sl(struct tab *t, struct karg *args)
1805 int i;
1806 char *page, *body, *tmp;
1808 DNPRINTF(XT_D_SEARCH, "%s", __func__);
1811 * Generate a new session key for next page instance.
1812 * This only happens for the top level call to xtp_page_sl()
1813 * in which case updating_sl_tabs is 0.
1815 if (!updating_sl_tabs)
1816 generate_xtp_session_key(&sl_session_key);
1818 if (t == NULL) {
1819 show_oops(NULL, "%s invalid parameters", __func__);
1820 return (1);
1823 body = g_strdup_printf("<p>The xombrero authors will not choose a "
1824 "default search engine for you. What follows is a list of search "
1825 "engines (in no particular order) you may be interested in. "
1826 "To permanently choose a search engine, click [ Select ] to save "
1827 "<tt>search_string</tt> as a runtime setting, or set "
1828 "<tt>search_string</tt> to the appropriate URL in your xombrero "
1829 "configuration.</p>");
1831 tmp = body;
1832 body = g_strdup_printf("%s\n<table style='table-layout:fixed'><tr>"
1833 "<th style='width: 200px'>Name</th><th>URL</th>"
1834 "<th style='width: 100px'>Select</th></tr>\n", body);
1835 g_free(tmp);
1837 for (i = 0; i < (sizeof search_list / sizeof (struct search_type)); ++i)
1838 body = search_engine_add(body, search_list[i].name,
1839 search_list[i].url, i);
1841 tmp = body;
1842 body = g_strdup_printf("%s</table>", body);
1843 g_free(tmp);
1845 page = get_html_page("Choose a search engine", body, "", 1);
1846 g_free(body);
1849 * update all search tabs as the xtp session key has now changed. No
1850 * need to update the current tab. Already did that above.
1852 update_search_tabs(t);
1854 load_webkit_string(t, page, XT_URI_ABOUT_SEARCH);
1855 g_free(page);
1857 return (0);
1861 xtp_page_sv(struct tab *t, struct karg *args)
1863 SoupURI *soupuri;
1864 static int arg = 0;
1865 struct secviolation find, *sv;
1866 char *page, *body;
1868 if (t == NULL)
1869 show_oops(NULL, "secviolation invalid parameters");
1872 * Generate a new session key for next page instance.
1873 * This only happens for the top level call to xtp_page_ab()
1874 * in which case updating_sv_tabs = 0.
1876 if (!updating_sv_tabs)
1877 generate_xtp_session_key(&sv_session_key);
1879 if (args == NULL) {
1880 find.xtp_arg = t->xtp_arg;
1881 sv = RB_FIND(secviolation_list, &svl, &find);
1882 if (sv == NULL)
1883 return (-1);
1884 } else {
1885 sv = g_malloc(sizeof(struct secviolation));
1886 sv->xtp_arg = ++arg;
1887 t->xtp_arg = arg;
1888 sv->t = t;
1889 sv->uri = args->s;
1890 RB_INSERT(secviolation_list, &svl, sv);
1893 if (sv->uri == NULL || (soupuri = soup_uri_new(sv->uri)) == NULL)
1894 return (-1);
1896 body = g_strdup_printf(
1897 "The domain of the page you have tried to access, %s, has a "
1898 "different remote certificate then the local cached version from a "
1899 "previous visit. As a security precaution to help prevent against "
1900 "man-in-the-middle attacks, please choose one of the following "
1901 "actions to continue, or disable the <tt>warn_cert_changes</tt> "
1902 "setting in your xombrero configuration."
1903 "<p><b>Choose an action:"
1904 "<br><a href='%s%d/%s/%d/%d'>Show Certificate</a>"
1905 "<br><a href='%s%d/%s/%d/%d'>Allow for this Session</a>"
1906 "<br><a href='%s%d/%s/%d/%d'>Cache new certificate</a>",
1907 soupuri->host,
1908 XT_XTP_STR, XT_XTP_SV, sv_session_key, XT_XTP_SV_SHOW_CERT,
1909 sv->xtp_arg,
1910 XT_XTP_STR, XT_XTP_SV, sv_session_key, XT_XTP_SV_ALLOW_SESSION,
1911 sv->xtp_arg,
1912 XT_XTP_STR, XT_XTP_SV, sv_session_key, XT_XTP_SV_CACHE,
1913 sv->xtp_arg);
1915 page = get_html_page("Security Violation", body, "", 0);
1916 g_free(body);
1918 update_secviolation_tabs(t);
1920 load_webkit_string(t, page, XT_URI_ABOUT_SECVIOLATION);
1922 g_free(page);
1923 if (soupuri)
1924 soup_uri_free(soupuri);
1926 return (0);
1930 startpage(struct tab *t, struct karg *args)
1932 char *page, *body, *b;
1933 struct sp *s;
1935 if (t == NULL)
1936 show_oops(NULL, "startpage invalid parameters");
1938 body = g_strdup_printf("<b>Startup Exception(s):</b><p>");
1940 TAILQ_FOREACH(s, &spl, entry) {
1941 b = body;
1942 body = g_strdup_printf("%s%s<br>", body, s->line);
1943 g_free(b);
1946 page = get_html_page("Startup Exception", body, "", 0);
1947 g_free(body);
1949 load_webkit_string(t, page, XT_URI_ABOUT_STARTPAGE);
1950 g_free(page);
1952 return (0);
1955 void
1956 startpage_add(const char *fmt, ...)
1958 va_list ap;
1959 char *msg;
1960 struct sp *s;
1962 if (fmt == NULL)
1963 return;
1965 va_start(ap, fmt);
1966 if ((msg = g_strdup_vprintf(fmt, ap)) == NULL)
1967 errx(1, "startpage_add failed");
1968 va_end(ap);
1970 s = g_malloc0(sizeof *s);
1971 s->line = msg;
1973 TAILQ_INSERT_TAIL(&spl, s, entry);
1976 gchar *
1977 show_g_object_settings(GObject *o, char *str, int recurse)
1979 char *b, *body, *valstr;
1980 guint n_props = 0;
1981 int i;
1982 GParamSpec *pspec;
1983 const gchar *tname;
1984 GValue value;
1985 int typeno;
1986 const gchar *string;
1987 gboolean boolean;
1988 gfloat fp;
1989 gdouble fpd;
1990 gint number;
1991 guint unumber;
1992 int64_t number64;
1993 uint64_t unumber64;
1994 GObject *object;
1995 GParamSpec **proplist;
1996 char *tmpstr, *tmpsettings;
1998 if (!G_IS_OBJECT(o)) {
1999 fprintf(stderr, "%s is not a g_object\n", str);
2000 return g_strdup("");
2002 proplist = g_object_class_list_properties(
2003 G_OBJECT_GET_CLASS(o), &n_props);
2004 body = g_strdup_printf("%s: %3d settings\n", str, n_props);
2005 for (i=0; i < n_props; i++) {
2006 pspec = proplist[i];
2007 tname = G_OBJECT_TYPE_NAME(pspec);
2008 bzero(&value, sizeof value);
2009 valstr = NULL;
2011 if (!(pspec->flags & G_PARAM_READABLE))
2012 valstr = g_strdup_printf("not a readable property");
2013 else {
2014 g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
2015 g_object_get_property(G_OBJECT(o), pspec->name,
2016 &value);
2019 /* based on the type, recurse and display values */
2020 if (valstr == NULL) {
2021 typeno = G_TYPE_FUNDAMENTAL( G_VALUE_TYPE(&value) );
2022 switch ( typeno ) {
2023 case G_TYPE_ENUM:
2024 number = g_value_get_enum(&value);
2025 valstr = g_strdup_printf("%d", number);
2026 break;
2027 case G_TYPE_INT:
2028 number = g_value_get_int(&value);
2029 valstr = g_strdup_printf("%d", number);
2030 break;
2031 case G_TYPE_INT64:
2032 number64 = (int64_t)g_value_get_int64(&value);
2033 valstr = g_strdup_printf("%" PRIo64, number64);
2034 break;
2035 case G_TYPE_UINT:
2036 unumber = g_value_get_uint(&value);
2037 valstr = g_strdup_printf("%d", unumber);
2038 break;
2039 case G_TYPE_UINT64:
2040 unumber64 =
2041 (uint64_t)g_value_get_uint64(&value);
2042 valstr =
2043 g_strdup_printf("%" PRIu64, unumber64);
2044 break;
2045 case G_TYPE_FLAGS:
2046 unumber = g_value_get_flags(&value);
2047 valstr = g_strdup_printf("0x%x", unumber);
2048 break;
2049 case G_TYPE_BOOLEAN:
2050 boolean = g_value_get_boolean(&value);
2051 valstr = g_strdup_printf("%s",
2052 boolean ? "TRUE" : "FALSE");
2053 break;
2054 case G_TYPE_FLOAT:
2055 fp = g_value_get_float(&value);
2056 valstr = g_strdup_printf("%f", fp);
2057 break;
2058 case G_TYPE_DOUBLE:
2059 fpd = g_value_get_double(&value);
2060 valstr = g_strdup_printf("%f", fpd);
2061 break;
2062 case G_TYPE_STRING:
2063 string = g_value_get_string(&value);
2064 valstr = g_strdup_printf("\"%s\"",
2065 string);
2066 break;
2067 case G_TYPE_OBJECT:
2068 object = g_value_get_object(&value);
2069 if (object != NULL) {
2070 if (recurse) {
2071 tmpstr = g_strdup_printf("%s ",
2072 str);
2073 tmpsettings = show_g_object_settings(
2074 object, tmpstr, recurse);
2075 valstr = g_strdup_printf(
2076 "{\n%s%s }\n",
2077 tmpsettings, str);
2078 g_free(tmpstr);
2079 g_free(tmpsettings);
2080 } else {
2081 valstr = g_strdup_printf("<...>");
2083 } else {
2084 valstr = g_strdup_printf("NULL");
2086 break;
2087 default:
2088 valstr = g_strdup_printf(
2089 "type %s unhandled",
2090 tname);
2094 b = body;
2095 body = g_strdup_printf(
2096 "%s%s: %3d: flags=0x%08x, %-13s %s = %s\n",
2097 body, str, i, pspec->flags, tname, pspec->name,
2098 valstr);
2099 g_free(b);
2100 g_free(valstr);
2102 g_free(proplist);
2103 return (body);
2107 about_webkit(struct tab *t, struct karg *arg)
2109 char *page, *body, *settingstr;
2111 settingstr = show_g_object_settings(G_OBJECT(t->settings),
2112 "t->settings", 0);
2113 body = g_strdup_printf("<pre>%s</pre>\n", settingstr);
2114 g_free(settingstr);
2116 page = get_html_page("About Webkit", body, "", 0);
2117 g_free(body);
2119 load_webkit_string(t, page, XT_URI_ABOUT_WEBKIT);
2120 g_free(page);
2122 return (0);
2126 allthethings(struct tab *t, struct karg *arg)
2128 char *page, *body, *b, *settingstr;
2129 extern GtkWidget *main_window;
2131 body = show_g_object_settings(G_OBJECT(t->wv), "t->wv", 1);
2132 b = body;
2133 settingstr = show_g_object_settings(G_OBJECT(t->inspector),
2134 "t->inspector", 1);
2135 body = g_strdup_printf("%s%s", body, settingstr);
2136 g_free(b);
2137 g_free(settingstr);
2138 b = body;
2139 settingstr = show_g_object_settings(G_OBJECT(main_window),
2140 "main_window", 1);
2141 body = g_strdup_printf("%s%s", body, settingstr);
2142 g_free(b);
2143 g_free(settingstr);
2144 b = body;
2145 body = g_strdup_printf("<pre>%scan paste clipboard = %d\n</pre>", body,
2146 webkit_web_view_can_paste_clipboard(t->wv));
2147 g_free(b);
2149 page = get_html_page("About All The Things _o/", body, "", 0);
2150 g_free(body);
2152 load_webkit_string(t, page, XT_URI_ABOUT_ALLTHETHINGS);
2153 g_free(page);
2155 return (0);