Strip whitespace for config inputs
[xombrero.git] / about.c
blob655de9a8935340b24619f7e49cc079a2a3bf2441
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 *);
89 * If you change the index of any of these, correct the
90 * XT_XTP_TAB_MEANING_* macros in xombrero.h!
92 struct about_type about_list[] = {
93 { XT_URI_ABOUT_ABOUT, xtp_page_ab },
94 { XT_URI_ABOUT_ALLTHETHINGS, allthethings },
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, xtp_page_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 },
109 { XT_URI_ABOUT_HTTPS, https_show_wl },
110 { XT_URI_ABOUT_WEBKIT, about_webkit },
111 { XT_URI_ABOUT_SEARCH, xtp_page_sl },
112 { XT_URI_ABOUT_RUNTIME, xtp_page_rt },
113 { XT_URI_ABOUT_SECVIOLATION, NULL },
116 struct search_type {
117 const char *name;
118 const char *url;
119 } search_list[] = {
120 { "Google (SSL)", "https://encrypted.google.com/search?q=%s" },
121 { "Bing", "http://www.bing.com/search?q=%s" },
122 { "Yahoo", "http://search.yahoo.com/search?p=%s" },
123 { "DuckDuckGo", "https://duckduckgo.com/?q=%s" },
124 { "DuckDuckGo (HTML)", "https://duckduckgo.com/html?q=%s" },
125 { "DuckDuckGo (Lite)", "https://duckduckgo.com/lite?q=%s" },
126 { "Ixquick", "https://ixquick.com/do/search?q=%s" },
127 { "Startpage", "https://startpage.com/do/search?q=%s" },
131 * Session IDs.
132 * We use these to prevent people putting xxxt:// URLs on
133 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
135 #define XT_XTP_SES_KEY_SZ 8
136 #define XT_XTP_SES_KEY_HEX_FMT \
137 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8
139 int updating_fl_tabs = 0;
140 int updating_dl_tabs = 0;
141 int updating_hl_tabs = 0;
142 int updating_cl_tabs = 0;
143 int updating_sl_tabs = 0;
144 int updating_sv_tabs = 0;
145 int updating_set_tabs = 0;
146 struct download_list downloads;
148 size_t
149 about_list_size(void)
151 return (LENGTH(about_list));
154 gchar *
155 get_html_page(gchar *title, gchar *body, gchar *head, bool addstyles)
157 gchar *r;
159 r = g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
160 "<head>\n"
161 "<title>%s</title>\n"
162 "%s"
163 "%s"
164 "</head>\n"
165 "<body>\n"
166 "<h1>%s</h1>\n"
167 "%s\n</body>\n"
168 "</html>",
169 title,
170 addstyles ? XT_PAGE_STYLE : "",
171 head,
172 title,
173 body);
175 return (r);
179 * Display a web page from a HTML string in memory, rather than from a URL
181 void
182 load_webkit_string(struct tab *t, const char *str, gchar *title, int nohist)
184 char file[PATH_MAX];
185 int i;
187 if (g_signal_handler_is_connected(t->wv, t->progress_handle))
188 g_signal_handler_disconnect(t->wv, t->progress_handle);
190 /* we set this to indicate we want to manually do navaction */
191 if (t->bfl && !nohist) {
192 t->item = webkit_web_back_forward_list_get_current_item(t->bfl);
193 if (t->item)
194 g_object_ref(t->item);
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);
216 if (t->xtp_meaning == XT_XTP_TAB_MEANING_NORMAL &&
217 t->session_key != NULL) {
218 g_free(t->session_key);
219 t->session_key = NULL;
222 t->progress_handle = g_signal_connect(t->wv,
223 "notify::progress", G_CALLBACK(webview_progress_changed_cb), t);
227 blank(struct tab *t, struct karg *args)
229 if (t == NULL)
230 show_oops(NULL, "blank invalid parameters");
232 load_webkit_string(t, "", XT_URI_ABOUT_BLANK, 0);
234 return (0);
238 help(struct tab *t, struct karg *args)
240 char *page, *head, *body;
242 if (t == NULL)
243 show_oops(NULL, "help invalid parameters");
245 head = "<meta http-equiv=\"REFRESH\" content=\"0;"
246 "url=https://opensource.conformal.com/cgi-bin/man-cgi?xombrero\">"
247 "</head>\n";
248 body = "xombrero man page <a href=\"https://opensource.conformal.com/"
249 "cgi-bin/man-cgi?xombrero\">https://opensource.conformal.com/"
250 "cgi-bin/man-cgi?xombrero</a>";
252 page = get_html_page(XT_NAME, body, head, FALSE);
254 load_webkit_string(t, page, XT_URI_ABOUT_HELP, 0);
255 g_free(page);
257 return (0);
261 stats(struct tab *t, struct karg *args)
263 char *page, *body, *s, line[64 * 1024];
264 uint64_t line_count = 0;
265 FILE *r_cookie_f;
267 if (t == NULL)
268 show_oops(NULL, "stats invalid parameters");
270 line[0] = '\0';
271 if (save_rejected_cookies) {
272 if ((r_cookie_f = fopen(rc_fname, "r"))) {
273 for (;;) {
274 s = fgets(line, sizeof line, r_cookie_f);
275 if (s == NULL || feof(r_cookie_f) ||
276 ferror(r_cookie_f))
277 break;
278 line_count++;
280 fclose(r_cookie_f);
281 snprintf(line, sizeof line,
282 "<br/>Cookies blocked(*) total: %" PRIu64,
283 line_count);
284 } else
285 show_oops(t, "Can't open blocked cookies file: %s",
286 strerror(errno));
289 body = g_strdup_printf(
290 "Cookies blocked(*) this session: %" PRIu64
291 "%s"
292 "<p><small><b>*</b> results vary based on settings</small></p>",
293 blocked_cookies,
294 line);
296 page = get_html_page("Statistics", body, "", 0);
297 g_free(body);
299 load_webkit_string(t, page, XT_URI_ABOUT_STATS, 0);
300 g_free(page);
302 return (0);
305 void
306 show_certs(struct tab *t, gnutls_x509_crt_t *certs,
307 size_t cert_count, char *title)
309 gnutls_datum_t *cinfo;
310 char *tmp, *body;
311 int i;
313 body = g_strdup("");
315 for (i = 0; i < cert_count; i++) {
316 cinfo = gnutls_malloc(sizeof *cinfo);
317 if (gnutls_x509_crt_print(certs[i], GNUTLS_CRT_PRINT_FULL,
318 cinfo)) {
319 gnutls_free(cinfo);
320 g_free(body);
321 return;
324 tmp = body;
325 body = g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
326 body, i, cinfo->data);
327 gnutls_free(cinfo);
328 g_free(tmp);
331 tmp = get_html_page(title, body, "", 0);
332 g_free(body);
334 load_webkit_string(t, tmp, XT_URI_ABOUT_CERTS, 0);
335 g_free(tmp);
339 ca_cmd(struct tab *t, struct karg *args)
341 FILE *f = NULL;
342 int rv = 1, certs_read;
343 unsigned int certs = 0;
344 struct stat sb;
345 gnutls_datum_t dt;
346 gnutls_x509_crt_t *c = NULL;
347 char *certs_buf = NULL, *s;
349 if ((f = fopen(ssl_ca_file, "r")) == NULL) {
350 show_oops(t, "Can't open CA file: %s", ssl_ca_file);
351 return (1);
354 if (fstat(fileno(f), &sb) == -1) {
355 show_oops(t, "Can't stat CA file: %s", ssl_ca_file);
356 goto done;
359 certs_buf = g_malloc(sb.st_size + 1);
360 if (fread(certs_buf, 1, sb.st_size, f) != sb.st_size) {
361 show_oops(t, "Can't read CA file: %s", strerror(errno));
362 goto done;
364 certs_buf[sb.st_size] = '\0';
366 s = certs_buf;
367 while ((s = strstr(s, "BEGIN CERTIFICATE"))) {
368 certs++;
369 s += strlen("BEGIN CERTIFICATE");
372 bzero(&dt, sizeof dt);
373 dt.data = (unsigned char *)certs_buf;
374 dt.size = sb.st_size;
375 c = gnutls_malloc(sizeof(*c) * certs);
376 certs_read = gnutls_x509_crt_list_import(c, &certs, &dt,
377 GNUTLS_X509_FMT_PEM, 0);
378 if (certs_read <= 0) {
379 show_oops(t, "No cert(s) available");
380 goto done;
382 show_certs(t, c, certs_read, "Certificate Authority Certificates");
383 done:
384 if (c)
385 gnutls_free(c);
386 if (certs_buf)
387 g_free(certs_buf);
388 if (f)
389 fclose(f);
391 return (rv);
395 cookie_show_wl(struct tab *t, struct karg *args)
397 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
398 wl_show(t, args, "Cookie White List", &c_wl);
400 return (0);
404 js_show_wl(struct tab *t, struct karg *args)
406 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
407 wl_show(t, args, "JavaScript White List", &js_wl);
409 return (0);
413 cookie_cmd(struct tab *t, struct karg *args)
415 if (args->i & XT_SHOW)
416 wl_show(t, args, "Cookie White List", &c_wl);
417 else if (args->i & XT_WL_TOGGLE) {
418 args->i |= XT_WL_RELOAD;
419 toggle_cwl(t, args);
420 } else if (args->i & XT_SAVE) {
421 args->i |= XT_WL_RELOAD;
422 wl_save(t, args, XT_WL_COOKIE);
423 } else if (args->i & XT_DELETE) {
424 remove_cookie_all();
425 update_cookie_tabs(NULL);
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 https_show_wl(struct tab *t, struct karg *args)
477 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
478 wl_show(t, args, "HTTPS Force List", &force_https);
480 return (0);
484 https_cmd(struct tab *t, struct karg *args)
486 if (args->i & XT_SHOW)
487 wl_show(t, args, "HTTPS Force List", &force_https);
488 else if (args->i & XT_SAVE) {
489 args->i |= XT_WL_RELOAD;
490 wl_save(t, args, XT_WL_HTTPS);
491 } else if (args->i & XT_WL_TOGGLE) {
492 args->i |= XT_WL_RELOAD;
493 toggle_force_https(t, args);
494 } else if (args->i & XT_DELETE)
495 show_oops(t, "https delete' currently unimplemented");
497 return (0);
501 * cancel, remove, etc. downloads
503 void
504 xtp_handle_dl(struct tab *t, uint8_t cmd, int id, const char *query)
506 struct download find, *d = NULL;
507 #ifndef __MINGW32__
508 char *file = NULL;
509 const char *uri = NULL;
510 #endif
512 DNPRINTF(XT_D_DOWNLOAD, "download control: cmd %d, id %d\n", cmd, id);
514 /* some commands require a valid download id */
515 if (cmd != XT_XTP_DL_LIST) {
516 /* lookup download in question */
517 find.id = id;
518 d = RB_FIND(download_list, &downloads, &find);
520 if (d == NULL) {
521 show_oops(t, "%s: no such download", __func__);
522 return;
526 /* decide what to do */
527 switch (cmd) {
528 case XT_XTP_DL_START:
529 /* our downloads always needs to be
530 * restarted if called from here
532 download_start(t, d, XT_DL_RESTART);
533 break;
534 case XT_XTP_DL_CANCEL:
535 webkit_download_cancel(d->download);
536 g_object_unref(d->download);
537 RB_REMOVE(download_list, &downloads, d);
538 break;
539 case XT_XTP_DL_UNLINK:
540 #ifdef __MINGW32__
541 /* XXX uri's aren't handled properly on windows? */
542 unlink(webkit_download_get_destination_uri(d->download));
543 #else
544 uri = webkit_download_get_destination_uri(d->download);
545 if ((file = g_filename_from_uri(uri, NULL, NULL)) != NULL) {
546 unlink(file);
547 g_free(file);
549 #endif
550 /* FALLTHROUGH */
551 case XT_XTP_DL_REMOVE:
552 webkit_download_cancel(d->download); /* just incase */
553 g_object_unref(d->download);
554 RB_REMOVE(download_list, &downloads, d);
555 break;
556 case XT_XTP_DL_LIST:
557 /* Nothing */
558 break;
559 default:
560 show_oops(t, "%s: unknown command", __func__);
561 break;
563 xtp_page_dl(t, NULL);
566 void
567 xtp_handle_hl(struct tab *t, uint8_t cmd, int id, const char *query)
569 struct history *h, *next, *ht;
570 int i = 1;
572 switch (cmd) {
573 case XT_XTP_HL_REMOVE:
574 /* walk backwards, as listed in reverse */
575 for (h = RB_MAX(history_list, &hl); h != NULL; h = next) {
576 next = RB_PREV(history_list, &hl, h);
577 if (id == i) {
578 RB_REMOVE(history_list, &hl, h);
579 g_free((gpointer) h->title);
580 g_free((gpointer) h->uri);
581 g_free(h);
582 break;
584 i++;
586 break;
587 case XT_XTP_HL_REMOVE_ALL:
588 RB_FOREACH_SAFE(h, history_list, &hl, ht)
589 RB_REMOVE(history_list, &hl, h);
590 break;
591 case XT_XTP_HL_LIST:
592 /* Nothing - just xtp_page_hl() below */
593 break;
594 default:
595 show_oops(t, "%s: unknown command", __func__);
596 break;
599 xtp_page_hl(t, NULL);
602 /* remove a favorite */
603 void
604 remove_favorite(struct tab *t, int index)
606 char file[PATH_MAX], *title, *uri = NULL;
607 char *new_favs, *tmp;
608 FILE *f;
609 int i;
610 size_t len, lineno;
612 /* open favorites */
613 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
615 if ((f = fopen(file, "r")) == NULL) {
616 show_oops(t, "%s: can't open favorites: %s",
617 __func__, strerror(errno));
618 return;
621 /* build a string which will become the new favorites file */
622 new_favs = g_strdup("");
624 for (i = 1;;) {
625 if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
626 if (feof(f) || ferror(f))
627 break;
628 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
629 if (len == 0) {
630 free(title);
631 title = NULL;
632 continue;
635 if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) {
636 if (feof(f) || ferror(f)) {
637 show_oops(t, "%s: can't parse favorites %s",
638 __func__, strerror(errno));
639 goto clean;
643 /* as long as this isn't the one we are deleting add to file */
644 if (i != index) {
645 tmp = new_favs;
646 new_favs = g_strdup_printf("%s%s\n%s\n",
647 new_favs, title, uri);
648 g_free(tmp);
651 free(uri);
652 uri = NULL;
653 free(title);
654 title = NULL;
655 i++;
657 fclose(f);
659 /* write back new favorites file */
660 if ((f = fopen(file, "w")) == NULL) {
661 show_oops(t, "%s: can't open favorites: %s",
662 __func__, strerror(errno));
663 goto clean;
666 if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
667 show_oops(t, "%s: can't fwrite", __func__);
668 fclose(f);
670 clean:
671 if (uri)
672 free(uri);
673 if (title)
674 free(title);
676 g_free(new_favs);
680 add_favorite(struct tab *t, struct karg *args)
682 char file[PATH_MAX];
683 FILE *f;
684 char *line = NULL;
685 size_t urilen, linelen;
686 gchar *argtitle = NULL;
687 const gchar *uri, *title;
689 if (t == NULL)
690 return (1);
692 /* don't allow adding of xtp pages to favorites */
693 if (t->xtp_meaning != XT_XTP_TAB_MEANING_NORMAL) {
694 show_oops(t, "%s: can't add xtp pages to favorites", __func__);
695 return (1);
698 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
699 if ((f = fopen(file, "r+")) == NULL) {
700 show_oops(t, "Can't open favorites file: %s", strerror(errno));
701 return (1);
704 if (args->s && strlen(g_strstrip(args->s)))
705 argtitle = html_escape(g_strstrip(args->s));
707 title = argtitle ? argtitle : get_title(t, FALSE);
708 uri = get_uri(t);
710 if (title == NULL || uri == NULL) {
711 show_oops(t, "can't add page to favorites");
712 goto done;
715 urilen = strlen(uri);
717 for (;;) {
718 if ((line = fparseln(f, &linelen, NULL, NULL, 0)) == NULL) {
719 if (feof(f))
720 break;
721 else {
722 show_oops(t, "Error reading favorites file: %s",
723 strerror(errno));
724 goto done;
728 if (linelen == urilen && !strcmp(line, uri))
729 goto done;
731 free(line);
732 line = NULL;
735 fprintf(f, "\n%s\n%s", title, uri);
736 done:
737 if (argtitle)
738 g_free(argtitle);
739 if (line)
740 free(line);
741 fclose(f);
743 update_favorite_tabs(NULL);
745 return (0);
748 char *
749 search_engine_add(char *body, const char *name, const char *url,
750 const char *key, int select)
752 char *b = body;
754 body = g_strdup_printf("%s<tr>"
755 "<td>%s</td>"
756 "<td>%s</td>"
757 "<td style='text-align: center'>"
758 "<a href='%s%d/%s/%d/%d'>[ Select ]</a></td>"
759 "</tr>\n",
760 body,
761 name,
762 url,
763 XT_XTP_STR, XT_XTP_SL, key, XT_XTP_SL_SET, select);
764 g_free(b);
765 return (body);
768 void
769 xtp_handle_ab(struct tab *t, uint8_t cmd, int arg, const char *query)
771 char config[PATH_MAX];
772 char *cmdstr;
773 char **sv;
775 switch (cmd) {
776 case XT_XTP_AB_EDIT_CONF:
777 if (external_editor == NULL || strlen(external_editor) == 0) {
778 show_oops(t, "external_editor is unset");
779 break;
782 snprintf(config, sizeof config, "%s" PS ".%s", pwd->pw_dir,
783 XT_CONF_FILE);
784 sv = g_strsplit(external_editor, "<file>", -1);
785 cmdstr = g_strjoinv(config, sv);
786 g_strfreev(sv);
787 sv = g_strsplit_set(cmdstr, " \t", -1);
789 if (!g_spawn_async(NULL, sv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
790 NULL, NULL))
791 show_oops(t, "%s: could not spawn process", __func__);
793 g_strfreev(sv);
794 g_free(cmdstr);
795 break;
796 default:
797 show_oops(t, "%s, invalid about command", __func__);
798 break;
800 xtp_page_ab(t, NULL);
802 void
803 xtp_handle_fl(struct tab *t, uint8_t cmd, int arg, const char *query)
805 struct karg args = {0};
807 switch (cmd) {
808 case XT_XTP_FL_LIST:
809 /* nothing, just the below call to xtp_page_fl() */
810 break;
811 case XT_XTP_FL_REMOVE:
812 remove_favorite(t, arg);
813 args.i = XT_DELETE;
814 break;
815 default:
816 show_oops(t, "%s: invalid favorites command", __func__);
817 break;
820 xtp_page_fl(t, &args);
823 void
824 xtp_handle_cl(struct tab *t, uint8_t cmd, int arg, const char *query)
826 switch (cmd) {
827 case XT_XTP_CL_LIST:
828 /* nothing, just xtp_page_cl() */
829 break;
830 case XT_XTP_CL_REMOVE:
831 remove_cookie(arg);
832 break;
833 case XT_XTP_CL_REMOVE_DOMAIN:
834 remove_cookie_domain(arg);
835 break;
836 case XT_XTP_CL_REMOVE_ALL:
837 remove_cookie_all();
838 break;
839 default:
840 show_oops(t, "%s: unknown cookie xtp command", __func__);
841 break;
844 xtp_page_cl(t, NULL);
847 void
848 xtp_handle_sl(struct tab *t, uint8_t cmd, int arg, const char *query)
850 const char *search;
851 char *enc_search, *uri;
852 char **sv;
854 switch (cmd) {
855 case XT_XTP_SL_SET:
856 set_search_string((char *)search_list[arg].url);
857 if (save_runtime_setting("search_string", search_list[arg].url))
858 show_oops(t, "could not set search_string in runtime");
859 break;
860 default:
861 show_oops(t, "%s: unknown search xtp command", __func__);
862 break;
865 search = gtk_entry_get_text(GTK_ENTRY(t->search_entry)); /* static */
866 enc_search = soup_uri_encode(search, XT_RESERVED_CHARS);
867 sv = g_strsplit(search_string, "%s", 2);
868 uri = g_strjoinv(enc_search, sv);
869 load_uri(t, uri);
870 g_free(enc_search);
871 g_strfreev(sv);
872 g_free(uri);
875 void
876 xtp_handle_sv(struct tab *t, uint8_t cmd, int id, const char *query)
878 SoupURI *soupuri = NULL;
879 struct karg args = {0};
880 struct secviolation find, *sv;
882 find.xtp_arg = id;
883 if ((sv = RB_FIND(secviolation_list, &svl, &find)) == NULL)
884 return;
886 args.ptr = (void *)sv->t;
887 args.s = sv->uri;
889 switch (cmd) {
890 case XT_XTP_SV_SHOW_NEW_CERT:
891 args.i = XT_SHOW;
892 if (cert_cmd(t, &args)) {
893 xtp_page_sv(t, &args);
894 return;
896 break;
897 case XT_XTP_SV_SHOW_CACHED_CERT:
898 args.i = XT_CACHE | XT_SHOW;
899 if (cert_cmd(t, &args)) {
900 xtp_page_sv(t, &args);
901 return;
903 break;
904 case XT_XTP_SV_ALLOW_SESSION:
905 soupuri = soup_uri_new(sv->uri);
906 wl_add(soupuri->host, &svil, 0);
907 load_uri(t, sv->uri);
908 focus_webview(t);
909 break;
910 case XT_XTP_SV_CACHE:
911 args.i = XT_CACHE;
912 if (cert_cmd(t, &args)) {
913 xtp_page_sv(t, &args);
914 return;
916 load_uri(t, sv->uri);
917 focus_webview(t);
918 break;
919 default:
920 show_oops(t, "%s: invalid secviolation command", __func__);
921 break;
924 g_free(sv->uri);
925 if (soupuri)
926 soup_uri_free(soupuri);
927 RB_REMOVE(secviolation_list, &svl, sv);
930 void
931 xtp_handle_rt(struct tab *t, uint8_t cmd, int id, const char *query)
933 struct set_reject *sr;
934 GHashTable *new_settings = NULL;
935 int modify;
936 char *val, *curval, *s;
937 int i = 0;
939 switch (cmd) {
940 case XT_XTP_RT_SAVE:
941 if (query == NULL)
942 break;
943 new_settings = soup_form_decode(query);
944 for (i = 0; i < get_settings_size(); ++i) {
945 if (!rs[i].activate)
946 continue;
947 val = (char *)g_hash_table_lookup(new_settings,
948 rs[i].name);
949 modify = 0;
950 switch (rs[i].type) {
951 case XT_S_INT: /* FALLTHROUGH */
952 case XT_S_BOOL:
953 if (atoi(val) != *rs[i].ival)
954 modify = 1;
955 break;
956 case XT_S_FLOAT:
957 if (atof(val) < (*rs[i].fval - 0.0001) ||
958 atof(val) > (*rs[i].fval + 0.0001))
959 modify = 1;
960 break;
961 case XT_S_STR:
962 s = (rs[i].sval == NULL || *rs[i].sval == NULL)
963 ? "" : *rs[i].sval;
964 if (rs[i].sval && g_strcmp0(val, s))
965 modify = 1;
966 else if (rs[i].s && rs[i].s->get) {
967 curval = rs[i].s->get(NULL);
968 if (g_strcmp0(val, curval))
969 modify = 1;
970 g_free(curval);
972 break;
973 case XT_S_INVALID: /* FALLTHROUGH */
974 default:
975 break;
977 if (rs[i].activate(val)) {
978 sr = g_malloc(sizeof *sr);
979 sr->name = g_strdup(rs[i].name);
980 sr->value = g_strdup(val);
981 TAILQ_INSERT_TAIL(&srl, sr, entry);
982 continue;
984 if (modify)
985 if (save_runtime_setting(rs[i].name, val))
986 show_oops(t, "error");
988 break;
989 default:
990 show_oops(t, "%s: invalid set command", __func__);
991 break;
994 if (new_settings)
995 g_hash_table_destroy(new_settings);
997 xtp_page_rt(t, NULL);
1000 /* link an XTP class to it's session key and handler function */
1001 struct xtp_despatch {
1002 uint8_t xtp_class;
1003 void (*handle_func)(struct tab *, uint8_t, int,
1004 const char *query);
1007 struct xtp_despatch xtp_despatches[] = {
1008 { XT_XTP_DL, xtp_handle_dl },
1009 { XT_XTP_HL, xtp_handle_hl },
1010 { XT_XTP_FL, xtp_handle_fl },
1011 { XT_XTP_CL, xtp_handle_cl },
1012 { XT_XTP_SL, xtp_handle_sl },
1013 { XT_XTP_AB, xtp_handle_ab },
1014 { XT_XTP_SV, xtp_handle_sv },
1015 { XT_XTP_RT, xtp_handle_rt },
1016 { XT_XTP_INVALID, NULL }
1020 * generate a session key to secure xtp commands.
1021 * pass in a ptr to the key in question and it will
1022 * be modified in place.
1024 void
1025 generate_xtp_session_key(char **key)
1027 uint8_t rand_bytes[XT_XTP_SES_KEY_SZ];
1029 if (key == NULL)
1030 return;
1032 /* free old key */
1033 if (*key != NULL)
1034 g_free(*key);
1036 /* make a new one */
1037 arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ);
1038 *key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT,
1039 rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
1040 rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]);
1042 DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key);
1046 * validate a xtp session key.
1047 * return (1) if OK
1050 validate_xtp_session_key(struct tab *t, char *key)
1052 if (t == NULL || t->session_key == NULL || key == NULL)
1053 return (0);
1055 if (strcmp(t->session_key, key) != 0) {
1056 show_oops(t, "%s: xtp session key mismatch possible spoof",
1057 __func__);
1058 return (0);
1061 return (1);
1065 * is the url xtp protocol? (xxxt://)
1066 * if so, parse and despatch correct bahvior
1069 parse_xtp_url(struct tab *t, const char *uri_str)
1071 SoupURI *uri = NULL;
1072 struct xtp_despatch *dsp, *dsp_match = NULL;
1073 int ret = FALSE;
1074 int class = 0;
1075 char **sv = NULL;
1078 * uri->host = class
1079 * sv[0] = session key
1080 * sv[1] = command
1081 * sv[2] = optional argument
1084 DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, uri_str);
1086 if ((uri = soup_uri_new(uri_str)) == NULL)
1087 goto clean;
1088 if (strncmp(uri->scheme, XT_XTP_SCHEME, strlen(XT_XTP_SCHEME)))
1089 goto clean;
1090 if (uri->host == NULL || strlen(uri->host) == 0)
1091 goto clean;
1092 if ((sv = g_strsplit(uri->path + 1, "/", 3)) == NULL)
1093 goto clean;
1095 if (sv[0] == NULL || sv[1] == NULL)
1096 goto clean;
1098 dsp = xtp_despatches;
1099 class = atoi(uri->host);
1100 while (dsp->xtp_class) {
1101 if (dsp->xtp_class == class) {
1102 dsp_match = dsp;
1103 break;
1105 dsp++;
1108 /* did we find one atall? */
1109 if (dsp_match == NULL) {
1110 show_oops(t, "%s: no matching xtp despatch found", __func__);
1111 goto clean;
1114 /* check session key and call despatch function */
1115 if (validate_xtp_session_key(t, sv[0])) {
1116 ret = TRUE; /* all is well, this was a valid xtp request */
1117 if (sv[2])
1118 dsp_match->handle_func(t, atoi(sv[1]), atoi(sv[2]),
1119 uri->query);
1120 else
1121 dsp_match->handle_func(t, atoi(sv[1]), 0, uri->query);
1124 clean:
1125 if (uri)
1126 soup_uri_free(uri);
1127 if (sv)
1128 g_strfreev(sv);
1130 return (ret);
1134 * update all favorite tabs apart from one. Pass NULL if
1135 * you want to update all.
1137 void
1138 update_favorite_tabs(struct tab *apart_from)
1140 struct tab *t;
1142 if (!updating_fl_tabs) {
1143 updating_fl_tabs = 1; /* stop infinite recursion */
1144 TAILQ_FOREACH(t, &tabs, entry)
1145 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL)
1146 && (t != apart_from))
1147 xtp_page_fl(t, NULL);
1148 updating_fl_tabs = 0;
1153 * update all download tabs apart from one. Pass NULL if
1154 * you want to update all.
1156 void
1157 update_download_tabs(struct tab *apart_from)
1159 struct tab *t;
1161 if (!updating_dl_tabs) {
1162 updating_dl_tabs = 1; /* stop infinite recursion */
1163 TAILQ_FOREACH(t, &tabs, entry)
1164 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_DL)
1165 && (t != apart_from))
1166 xtp_page_dl(t, NULL);
1167 updating_dl_tabs = 0;
1172 * update all cookie tabs apart from one. Pass NULL if
1173 * you want to update all.
1175 void
1176 update_cookie_tabs(struct tab *apart_from)
1178 struct tab *t;
1180 if (!updating_cl_tabs) {
1181 updating_cl_tabs = 1; /* stop infinite recursion */
1182 TAILQ_FOREACH(t, &tabs, entry)
1183 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL)
1184 && (t != apart_from))
1185 xtp_page_cl(t, NULL);
1186 updating_cl_tabs = 0;
1191 * update all history tabs apart from one. Pass NULL if
1192 * you want to update all.
1194 void
1195 update_history_tabs(struct tab *apart_from)
1197 struct tab *t;
1199 if (!updating_hl_tabs) {
1200 updating_hl_tabs = 1; /* stop infinite recursion */
1201 TAILQ_FOREACH(t, &tabs, entry)
1202 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL)
1203 && (t != apart_from))
1204 xtp_page_hl(t, NULL);
1205 updating_hl_tabs = 0;
1210 * update all search tabs apart from one. Pass NULL if
1211 * you want to update all.
1213 void
1214 update_search_tabs(struct tab *apart_from)
1216 struct tab *t;
1218 if (!updating_sl_tabs) {
1219 updating_sl_tabs = 1; /* stop infinite recursion */
1220 TAILQ_FOREACH(t, &tabs, entry)
1221 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_SL)
1222 && (t != apart_from))
1223 xtp_page_sl(t, NULL);
1224 updating_sl_tabs = 0;
1229 xtp_page_ab(struct tab *t, struct karg *args)
1231 char *page, *body;
1233 if (t == NULL) {
1234 show_oops(NULL, "about invalid parameters");
1235 return (-1);
1238 generate_xtp_session_key(&t->session_key);
1240 body = g_strdup_printf("<b>Version: %s</b>"
1241 #ifdef XOMBRERO_BUILDSTR
1242 "<br><b>Build: %s</b>"
1243 #endif
1244 "<br><b>WebKit: %d.%d.%d</b>"
1245 "<br><b>User Agent: %d.%d</b>"
1246 #ifdef WEBKITGTK_API_VERSION
1247 "<br><b>WebKit API: %.1f</b>"
1248 #endif
1249 "<br><b>Configuration: %s" PS "<a href='%s%d/%s/%d'>.%s</a>"
1250 " (remember to restart the browser after any changes)</b>"
1251 "<p>"
1252 "Authors:"
1253 "<ul>"
1254 "<li>Marco Peereboom &lt;marco@peereboom.us&gt;</li>"
1255 "<li>Stevan Andjelkovic &lt;stevan@student.chalmers.se&gt;</li>"
1256 "<li>Edd Barrett &lt;vext01@gmail.com&gt;</li>"
1257 "<li>Todd T. Fries &lt;todd@fries.net&gt;</li>"
1258 "<li>Raphael Graf &lt;r@undefined.ch&gt;</li>"
1259 "<li>Michal Mazurek &lt;akfaew@jasminek.net&gt;</li>"
1260 "<li>Josh Rickmar &lt;jrick@devio.us&gt;</li>"
1261 "<li>David Hill &lt;dhill@mindcry.org&gt;</li>"
1262 "</ul>"
1263 "Copyrights and licenses can be found on the xombrero "
1264 "<a href=\"https://opensource.conformal.com/wiki/xombrero\">website</a>"
1265 "</p>",
1266 #ifdef XOMBRERO_BUILDSTR
1267 version, XOMBRERO_BUILDSTR,
1268 #else
1269 version,
1270 #endif
1271 WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION,
1272 WEBKIT_USER_AGENT_MAJOR_VERSION, WEBKIT_USER_AGENT_MINOR_VERSION
1273 #ifdef WEBKITGTK_API_VERSION
1274 ,WEBKITGTK_API_VERSION
1275 #endif
1276 ,pwd->pw_dir,
1277 XT_XTP_STR,
1278 XT_XTP_AB,
1279 t->session_key ? t->session_key : "",
1280 XT_XTP_AB_EDIT_CONF,
1281 XT_CONF_FILE
1284 page = get_html_page("About", body, "", 0);
1285 g_free(body);
1287 load_webkit_string(t, page, XT_URI_ABOUT_ABOUT, 0);
1289 g_free(page);
1291 return (0);
1294 /* show a list of favorites (bookmarks) */
1296 xtp_page_fl(struct tab *t, struct karg *args)
1298 char file[PATH_MAX];
1299 FILE *f;
1300 char *uri = NULL, *title = NULL;
1301 size_t len, lineno = 0;
1302 int i, failed = 0;
1303 char *body, *tmp, *page = NULL;
1304 const char delim[3] = {'\\', '\\', '\0'};
1306 DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
1308 if (t == NULL) {
1309 show_oops(NULL, "%s: bad param", __func__);
1310 return (-1);
1313 generate_xtp_session_key(&t->session_key);
1315 /* open favorites */
1316 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
1317 if ((f = fopen(file, "r")) == NULL) {
1318 show_oops(t, "Can't open favorites file: %s", strerror(errno));
1319 return (1);
1322 /* body */
1323 if (args && args->i & XT_DELETE)
1324 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
1325 "<th style='width: 40px'>&#35;</th><th>Link</th>"
1326 "<th style='width: 40px'>Rm</th></tr>\n");
1327 else
1328 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
1329 "<th style='width: 40px'>&#35;</th><th>Link</th></tr>\n");
1331 for (i = 1;;) {
1332 if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
1333 break;
1334 if (strlen(title) == 0) {
1335 free(title);
1336 title = NULL;
1337 continue;
1340 if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
1341 if (feof(f) || ferror(f)) {
1342 show_oops(t, "favorites file corrupt");
1343 failed = 1;
1344 break;
1347 tmp = body;
1348 if (args && args->i & XT_DELETE)
1349 body = g_strdup_printf("%s<tr>"
1350 "<td>%d</td>"
1351 "<td><a href='%s'>%s</a></td>"
1352 "<td style='text-align: center'>"
1353 "<a href='%s%d/%s/%d/%d'>X</a></td>"
1354 "</tr>\n",
1355 body, i, uri, title,
1356 XT_XTP_STR, XT_XTP_FL,
1357 t->session_key ? t->session_key : "",
1358 XT_XTP_FL_REMOVE, i);
1359 else
1360 body = g_strdup_printf("%s<tr>"
1361 "<td>%d</td>"
1362 "<td><a href='%s'>%s</a></td>"
1363 "</tr>\n",
1364 body, i, uri, title);
1365 g_free(tmp);
1367 free(uri);
1368 uri = NULL;
1369 free(title);
1370 title = NULL;
1371 i++;
1373 fclose(f);
1375 /* if none, say so */
1376 if (i == 1) {
1377 tmp = body;
1378 body = g_strdup_printf("%s<tr>"
1379 "<td colspan='%d' style='text-align: center'>"
1380 "No favorites - To add one use the 'favadd' command."
1381 "</td></tr>", body, (args && args->i & XT_DELETE) ? 3 : 2);
1382 g_free(tmp);
1385 tmp = body;
1386 body = g_strdup_printf("%s</table>", body);
1387 g_free(tmp);
1389 if (uri)
1390 free(uri);
1391 if (title)
1392 free(title);
1394 /* render */
1395 if (!failed) {
1396 page = get_html_page("Favorites", body, "", 1);
1397 load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES, 0);
1398 g_free(page);
1401 update_favorite_tabs(t);
1403 if (body)
1404 g_free(body);
1406 return (failed);
1410 * Return a new string with a download row (in html)
1411 * appended. Old string is freed.
1413 char *
1414 xtp_page_dl_row(struct tab *t, char *html, struct download *dl)
1417 WebKitDownloadStatus stat;
1418 const gchar *destination;
1419 gchar *d;
1420 char *status_html = NULL, *cmd_html = NULL, *new_html;
1421 gdouble progress;
1422 char cur_sz[FMT_SCALED_STRSIZE];
1423 char tot_sz[FMT_SCALED_STRSIZE];
1424 char *xtp_prefix;
1426 DNPRINTF(XT_D_DOWNLOAD, "%s: dl->id %d\n", __func__, dl->id);
1428 /* All actions wil take this form:
1429 * xxxt://class/seskey
1431 xtp_prefix = g_strdup_printf("%s%d/%s/",
1432 XT_XTP_STR, XT_XTP_DL, t->session_key);
1434 stat = webkit_download_get_status(dl->download);
1436 switch (stat) {
1437 case WEBKIT_DOWNLOAD_STATUS_FINISHED:
1438 status_html = g_strdup_printf("Finished");
1439 cmd_html = g_strdup_printf(
1440 "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1441 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1442 XT_XTP_DL_UNLINK, dl->id);
1443 break;
1444 case WEBKIT_DOWNLOAD_STATUS_STARTED:
1445 /* gather size info */
1446 progress = 100 * webkit_download_get_progress(dl->download);
1448 fmt_scaled(
1449 webkit_download_get_current_size(dl->download), cur_sz);
1450 fmt_scaled(
1451 webkit_download_get_total_size(dl->download), tot_sz);
1453 status_html = g_strdup_printf(
1454 "<div style='width: 100%%' align='center'>"
1455 "<div class='progress-outer'>"
1456 "<div class='progress-inner' style='width: %.2f%%'>"
1457 "</div></div></div>"
1458 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
1459 progress, cur_sz, tot_sz, progress);
1461 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1462 xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
1464 break;
1465 /* LLL */
1466 case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
1467 status_html = g_strdup_printf("Cancelled");
1468 cmd_html = g_strdup_printf(
1469 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1470 xtp_prefix, XT_XTP_DL_START, dl->id,
1471 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1472 XT_XTP_DL_UNLINK, dl->id);
1473 break;
1474 case WEBKIT_DOWNLOAD_STATUS_ERROR:
1475 status_html = g_strdup_printf("Error!");
1476 cmd_html = g_strdup_printf(
1477 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1478 xtp_prefix, XT_XTP_DL_START, dl->id,
1479 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1480 XT_XTP_DL_UNLINK, dl->id);
1481 break;
1482 case WEBKIT_DOWNLOAD_STATUS_CREATED:
1483 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Start</a> / <a href='%s%d/%d'>Cancel</a>",
1484 xtp_prefix, XT_XTP_DL_START, dl->id, xtp_prefix,
1485 XT_XTP_DL_CANCEL, dl->id);
1486 status_html = g_strdup_printf("Created");
1487 break;
1488 default:
1489 show_oops(t, "%s: unknown download status", __func__);
1492 destination = webkit_download_get_destination_uri(dl->download);
1493 /* we might not have a destination set yet */
1494 if (!destination)
1495 destination = webkit_download_get_suggested_filename(dl->download);
1496 d = g_strdup(destination); /* copy for basename */
1497 new_html = g_strdup_printf(
1498 "%s\n<tr><td>%s</td><td>%s</td>"
1499 "<td style='text-align:center'>%s</td></tr>\n",
1500 html, basename(d), status_html, cmd_html);
1501 g_free(d);
1502 g_free(html);
1504 if (status_html)
1505 g_free(status_html);
1507 if (cmd_html)
1508 g_free(cmd_html);
1510 g_free(xtp_prefix);
1512 return new_html;
1515 /* cookie management XTP page */
1517 xtp_page_cl(struct tab *t, struct karg *args)
1519 char *body, *page, *tmp;
1520 int i = 1; /* all ids start 1 */
1521 int domain_id = 0;
1522 GSList *sc, *pc, *pc_start;
1523 SoupCookie *c;
1524 char *type, *table_headers, *last_domain;
1526 DNPRINTF(XT_D_CMD, "%s", __func__);
1528 if (t == NULL) {
1529 show_oops(NULL, "%s invalid parameters", __func__);
1530 return (1);
1533 generate_xtp_session_key(&t->session_key);
1535 /* table headers */
1536 table_headers = g_strdup_printf("<table><tr>"
1537 "<th>Type</th>"
1538 "<th>Name</th>"
1539 "<th style='width:200px'>Value</th>"
1540 "<th>Path</th>"
1541 "<th>Expires</th>"
1542 "<th>Secure</th>"
1543 "<th>HTTP<br />only</th>"
1544 "<th style='width:40px'>Rm</th></tr>\n");
1546 sc = soup_cookie_jar_all_cookies(s_cookiejar);
1547 pc = soup_cookie_jar_all_cookies(p_cookiejar);
1548 pc_start = pc;
1550 body = g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
1551 "[ Remove All Cookies From All Domains ]</a></div>\n",
1552 XT_XTP_STR, XT_XTP_CL, t->session_key, XT_XTP_CL_REMOVE_ALL);
1554 last_domain = g_strdup("");
1555 for (; sc; sc = sc->next) {
1556 c = sc->data;
1558 if (strcmp(last_domain, c->domain) != 0) {
1559 /* new domain */
1560 domain_id ++;
1561 g_free(last_domain);
1562 last_domain = g_strdup(c->domain);
1564 if (body != NULL) {
1565 tmp = body;
1566 body = g_strdup_printf("%s</table>"
1567 "<h2>%s</h2><div align=\"center\">"
1568 "<a href='%s%d/%s/%d/%d'>"
1569 "[ Remove All From This Domain ]"
1570 "</a></div>%s\n",
1571 body, c->domain,
1572 XT_XTP_STR, XT_XTP_CL, t->session_key,
1573 XT_XTP_CL_REMOVE_DOMAIN, domain_id,
1574 table_headers);
1575 g_free(tmp);
1576 } else {
1577 /* first domain */
1578 body = g_strdup_printf("<h2>%s</h2>"
1579 "<div align=\"center\">"
1580 "<a href='%s%d/%s/%d/%d'>"
1581 "[ Remove All From This Domain ]</a></div>%s\n",
1582 c->domain, XT_XTP_STR, XT_XTP_CL,
1583 t->session_key, XT_XTP_CL_REMOVE_DOMAIN,
1584 domain_id, table_headers);
1588 type = "Session";
1589 for (pc = pc_start; pc; pc = pc->next)
1590 if (soup_cookie_equal(pc->data, c)) {
1591 type = "Session + Persistent";
1592 break;
1595 tmp = body;
1596 body = g_strdup_printf(
1597 "%s\n<tr>"
1598 "<td>%s</td>"
1599 "<td style='word-wrap:normal'>%s</td>"
1600 "<td>"
1601 " <textarea rows='4'>%s</textarea>"
1602 "</td>"
1603 "<td>%s</td>"
1604 "<td>%s</td>"
1605 "<td>%d</td>"
1606 "<td>%d</td>"
1607 "<td style='text-align:center'>"
1608 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1609 body,
1610 type,
1611 c->name,
1612 c->value,
1613 c->path,
1614 c->expires ?
1615 soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
1616 c->secure,
1617 c->http_only,
1619 XT_XTP_STR,
1620 XT_XTP_CL,
1621 t->session_key,
1622 XT_XTP_CL_REMOVE,
1626 g_free(tmp);
1627 i++;
1630 soup_cookies_free(sc);
1631 soup_cookies_free(pc);
1633 /* small message if there are none */
1634 if (i == 1) {
1635 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1636 "colspan='8'>No Cookies</td></tr>\n", table_headers);
1638 tmp = body;
1639 body = g_strdup_printf("%s</table>", body);
1640 g_free(tmp);
1642 page = get_html_page("Cookie Jar", body, "", TRUE);
1643 g_free(body);
1644 g_free(table_headers);
1645 g_free(last_domain);
1647 load_webkit_string(t, page, XT_URI_ABOUT_COOKIEJAR, 0);
1648 update_cookie_tabs(t);
1650 g_free(page);
1652 return (0);
1656 xtp_page_hl(struct tab *t, struct karg *args)
1658 char *body, *page, *tmp;
1659 struct history *h;
1660 int i = 1; /* all ids start 1 */
1662 DNPRINTF(XT_D_CMD, "%s", __func__);
1664 if (t == NULL) {
1665 show_oops(NULL, "%s invalid parameters", __func__);
1666 return (1);
1669 generate_xtp_session_key(&t->session_key);
1671 /* body */
1672 body = g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
1673 "[ Remove All ]</a></div>"
1674 "<table style='table-layout:fixed'><tr>"
1675 "<th>URI</th><th>Title</th><th>Last visited</th>"
1676 "<th style='width: 40px'>Rm</th></tr>\n",
1677 XT_XTP_STR, XT_XTP_HL, t->session_key, XT_XTP_HL_REMOVE_ALL);
1679 RB_FOREACH_REVERSE(h, history_list, &hl) {
1680 tmp = body;
1681 body = g_strdup_printf(
1682 "%s\n<tr>"
1683 "<td><a href='%s'>%s</a></td>"
1684 "<td>%s</td>"
1685 "<td>%s</td>"
1686 "<td style='text-align: center'>"
1687 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1688 body, h->uri, h->uri, h->title, ctime(&h->time),
1689 XT_XTP_STR, XT_XTP_HL, t->session_key,
1690 XT_XTP_HL_REMOVE, i);
1692 g_free(tmp);
1693 i++;
1696 /* small message if there are none */
1697 if (i == 1) {
1698 tmp = body;
1699 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1700 "colspan='4'>No History</td></tr>\n", body);
1701 g_free(tmp);
1704 tmp = body;
1705 body = g_strdup_printf("%s</table>", body);
1706 g_free(tmp);
1708 page = get_html_page("History", body, "", TRUE);
1709 g_free(body);
1712 * update all history manager tabs as the xtp session
1713 * key has now changed. No need to update the current tab.
1714 * Already did that above.
1716 update_history_tabs(t);
1718 load_webkit_string(t, page, XT_URI_ABOUT_HISTORY, 0);
1719 g_free(page);
1721 return (0);
1725 * Generate a web page detailing the status of any downloads
1728 xtp_page_dl(struct tab *t, struct karg *args)
1730 struct download *dl;
1731 char *body, *page, *tmp;
1732 char *ref;
1733 int n_dl = 1;
1735 DNPRINTF(XT_D_DOWNLOAD, "%s", __func__);
1737 if (t == NULL) {
1738 show_oops(NULL, "%s invalid parameters", __func__);
1739 return (1);
1742 generate_xtp_session_key(&t->session_key);
1744 /* header - with refresh so as to update */
1745 if (refresh_interval >= 1)
1746 ref = g_strdup_printf(
1747 "<meta http-equiv='refresh' content='%u"
1748 ";url=%s%d/%s/%d' />\n",
1749 refresh_interval,
1750 XT_XTP_STR,
1751 XT_XTP_DL,
1752 t->session_key,
1753 XT_XTP_DL_LIST);
1754 else
1755 ref = g_strdup("");
1757 body = g_strdup_printf("<div align='center'>"
1758 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
1759 "</p><table><tr><th style='width: 60%%'>"
1760 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
1761 XT_XTP_STR, XT_XTP_DL, t->session_key, XT_XTP_DL_LIST);
1763 RB_FOREACH_REVERSE(dl, download_list, &downloads) {
1764 body = xtp_page_dl_row(t, body, dl);
1765 n_dl++;
1768 /* message if no downloads in list */
1769 if (n_dl == 1) {
1770 tmp = body;
1771 body = g_strdup_printf("%s\n<tr><td colspan='3'"
1772 " style='text-align: center'>"
1773 "No downloads</td></tr>\n", body);
1774 g_free(tmp);
1777 tmp = body;
1778 body = g_strdup_printf("%s</table></div>", body);
1779 g_free(tmp);
1781 page = get_html_page("Downloads", body, ref, 1);
1782 g_free(ref);
1783 g_free(body);
1786 * update all download manager tabs as the xtp session
1787 * key has now changed. No need to update the current tab.
1788 * Already did that above.
1790 update_download_tabs(t);
1792 load_webkit_string(t, page, XT_URI_ABOUT_DOWNLOADS, 0);
1793 g_free(page);
1795 return (0);
1799 xtp_page_sl(struct tab *t, struct karg *args)
1801 int i;
1802 char *page, *body, *tmp;
1804 DNPRINTF(XT_D_SEARCH, "%s", __func__);
1806 generate_xtp_session_key(&t->session_key);
1808 if (t == NULL) {
1809 show_oops(NULL, "%s invalid parameters", __func__);
1810 return (1);
1813 body = g_strdup_printf("<p>The xombrero authors will not choose a "
1814 "default search engine for you. What follows is a list of search "
1815 "engines (in no particular order) you may be interested in. "
1816 "To permanently choose a search engine, click [ Select ] to save "
1817 "<tt>search_string</tt> as a runtime setting, or set "
1818 "<tt>search_string</tt> to the appropriate URL in your xombrero "
1819 "configuration.</p>");
1821 tmp = body;
1822 body = g_strdup_printf("%s\n<table style='table-layout:fixed'><tr>"
1823 "<th style='width: 200px'>Name</th><th>URL</th>"
1824 "<th style='width: 100px'>Select</th></tr>\n", body);
1825 g_free(tmp);
1827 for (i = 0; i < (sizeof search_list / sizeof (struct search_type)); ++i)
1828 body = search_engine_add(body, search_list[i].name,
1829 search_list[i].url, t->session_key, i);
1831 tmp = body;
1832 body = g_strdup_printf("%s</table>", body);
1833 g_free(tmp);
1835 page = get_html_page("Choose a search engine", body, "", 1);
1836 g_free(body);
1839 * update all search tabs as the xtp session key has now changed. No
1840 * need to update the current tab. Already did that above.
1842 update_search_tabs(t);
1844 load_webkit_string(t, page, XT_URI_ABOUT_SEARCH, 0);
1845 g_free(page);
1847 return (0);
1851 xtp_page_sv(struct tab *t, struct karg *args)
1853 SoupURI *soupuri;
1854 static int arg = 0;
1855 struct secviolation find, *sv;
1856 char *page, *body;
1858 if (t == NULL) {
1859 show_oops(NULL, "secviolation invalid parameters");
1860 return (-1);
1863 generate_xtp_session_key(&t->session_key);
1865 if (args == NULL) {
1866 find.xtp_arg = t->xtp_arg;
1867 sv = RB_FIND(secviolation_list, &svl, &find);
1868 if (sv == NULL)
1869 return (-1);
1870 } else {
1871 sv = g_malloc(sizeof(struct secviolation));
1872 sv->xtp_arg = ++arg;
1873 t->xtp_arg = arg;
1874 sv->t = t;
1875 sv->uri = args->s;
1876 RB_INSERT(secviolation_list, &svl, sv);
1879 if (sv->uri == NULL || (soupuri = soup_uri_new(sv->uri)) == NULL)
1880 return (-1);
1882 body = g_strdup_printf(
1883 "<p><b>You tried to access %s</b>."
1884 "<p><b>The site's security certificate has been modified.</b>"
1885 "<p>The domain of the page you have tried to access, <b>%s</b>, "
1886 "has a different remote certificate then the local cached version "
1887 "from a previous visit. As a security precaution to help prevent "
1888 "against man-in-the-middle attacks, please choose one of the "
1889 "following actions to continue, or disable the "
1890 "<tt>warn_cert_changes</tt> setting in your xombrero "
1891 "configuration."
1892 "<p><b>Choose an action:"
1893 "<br><a href='%s%d/%s/%d/%d'>Allow for this session</a>"
1894 "<br><a href='%s%d/%s/%d/%d'>Cache new certificate</a>"
1895 "<br><a href='%s%d/%s/%d/%d'>Show cached certificate</a>"
1896 "<br><a href='%s%d/%s/%d/%d'>Show new certificate</a>",
1897 sv->uri,
1898 soupuri->host,
1899 XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_ALLOW_SESSION,
1900 sv->xtp_arg,
1901 XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_CACHE,
1902 sv->xtp_arg,
1903 XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_SHOW_CACHED_CERT,
1904 sv->xtp_arg,
1905 XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_SHOW_NEW_CERT,
1906 sv->xtp_arg);
1908 page = get_html_page("Security Violation", body, "", 0);
1909 g_free(body);
1911 load_webkit_string(t, page, XT_URI_ABOUT_SECVIOLATION, 1);
1913 g_free(page);
1914 if (soupuri)
1915 soup_uri_free(soupuri);
1917 return (0);
1921 startpage(struct tab *t, struct karg *args)
1923 char *page, *body, *b;
1924 struct sp *s;
1926 if (t == NULL)
1927 show_oops(NULL, "startpage invalid parameters");
1929 body = g_strdup_printf("<b>Startup Exception(s):</b><p>");
1931 TAILQ_FOREACH(s, &spl, entry) {
1932 b = body;
1933 body = g_strdup_printf("%s%s<br>", body, s->line);
1934 g_free(b);
1937 page = get_html_page("Startup Exception", body, "", 0);
1938 g_free(body);
1940 load_webkit_string(t, page, XT_URI_ABOUT_STARTPAGE, 0);
1941 g_free(page);
1943 return (0);
1946 void
1947 startpage_add(const char *fmt, ...)
1949 va_list ap;
1950 char *msg;
1951 struct sp *s;
1953 if (fmt == NULL)
1954 return;
1956 va_start(ap, fmt);
1957 if ((msg = g_strdup_vprintf(fmt, ap)) == NULL)
1958 errx(1, "startpage_add failed");
1959 va_end(ap);
1961 s = g_malloc0(sizeof *s);
1962 s->line = msg;
1964 TAILQ_INSERT_TAIL(&spl, s, entry);
1966 gchar *show_g_object_settings(GObject *, char *, int);
1968 char *
1969 xt_g_object_serialize(GValue *value, const gchar *tname, char *str, int recurse)
1971 int typeno = 0;
1972 char *valstr, *tmpstr, *tmpsettings;
1973 GObject *object;
1975 typeno = G_TYPE_FUNDAMENTAL( G_VALUE_TYPE(value) );
1976 switch ( typeno ) {
1977 case G_TYPE_ENUM:
1978 valstr = g_strdup_printf("%d",
1979 g_value_get_enum(value));
1980 break;
1981 case G_TYPE_CHAR:
1982 valstr = g_strdup_printf("%c",
1983 #if GLIB_CHECK_VERSION(2, 32, 0)
1984 g_value_get_schar(value));
1985 #else
1986 g_value_get_char(value));
1987 #endif
1988 break;
1989 case G_TYPE_UCHAR:
1990 valstr = g_strdup_printf("%c",
1991 g_value_get_uchar(value));
1992 break;
1993 case G_TYPE_LONG:
1994 valstr = g_strdup_printf("%ld",
1995 g_value_get_long(value));
1996 break;
1997 case G_TYPE_ULONG:
1998 valstr = g_strdup_printf("%ld",
1999 g_value_get_ulong(value));
2000 break;
2001 case G_TYPE_INT:
2002 valstr = g_strdup_printf("%d",
2003 g_value_get_int(value));
2004 break;
2005 case G_TYPE_INT64:
2006 valstr = g_strdup_printf("%" PRIo64,
2007 (int64_t) g_value_get_int64(value));
2008 break;
2009 case G_TYPE_UINT:
2010 valstr = g_strdup_printf("%d",
2011 g_value_get_uint(value));
2012 break;
2013 case G_TYPE_UINT64:
2014 valstr = g_strdup_printf("%" PRIu64,
2015 (uint64_t) g_value_get_uint64(value));
2016 break;
2017 case G_TYPE_FLAGS:
2018 valstr = g_strdup_printf("0x%x",
2019 g_value_get_flags(value));
2020 break;
2021 case G_TYPE_BOOLEAN:
2022 valstr = g_strdup_printf("%s",
2023 g_value_get_boolean(value) ? "TRUE" : "FALSE");
2024 break;
2025 case G_TYPE_FLOAT:
2026 valstr = g_strdup_printf("%f",
2027 g_value_get_float(value));
2028 break;
2029 case G_TYPE_DOUBLE:
2030 valstr = g_strdup_printf("%f",
2031 g_value_get_double(value));
2032 break;
2033 case G_TYPE_STRING:
2034 valstr = g_strdup_printf("\"%s\"",
2035 g_value_get_string(value));
2036 break;
2037 case G_TYPE_POINTER:
2038 valstr = g_strdup_printf("%p",
2039 g_value_get_pointer(value));
2040 break;
2041 case G_TYPE_OBJECT:
2042 object = g_value_get_object(value);
2043 if (object != NULL) {
2044 if (recurse) {
2045 tmpstr = g_strdup_printf("%s ", str);
2046 tmpsettings = show_g_object_settings( object,
2047 tmpstr, recurse);
2048 g_free(tmpstr);
2050 if (strrchr(tmpsettings, '\n') != NULL) {
2051 valstr = g_strdup_printf("%s%s }",
2052 tmpsettings, str);
2053 g_free(tmpsettings);
2054 } else {
2055 valstr = tmpsettings;
2057 } else {
2058 valstr = g_strdup_printf("<...>");
2060 } else {
2061 valstr = g_strdup_printf("settings[] = NULL");
2063 break;
2064 default:
2065 valstr = g_strdup_printf("type %s unhandled", tname);
2067 return valstr;
2070 gchar *
2071 show_g_object_settings(GObject *o, char *str, int recurse)
2073 char *b, *p, *body, *valstr, *tmpstr;
2074 guint n_props = 0;
2075 int i, typeno = 0;
2076 GParamSpec *pspec;
2077 const gchar *tname;
2078 GValue value;
2079 GParamSpec **proplist;
2080 const gchar *name;
2082 if (!G_IS_OBJECT(o)) {
2083 fprintf(stderr, "%s is not a g_object\n", str);
2084 return g_strdup("");
2086 proplist = g_object_class_list_properties(
2087 G_OBJECT_GET_CLASS(o), &n_props);
2089 if (GTK_IS_WIDGET(o)) {
2090 name = gtk_widget_get_name(GTK_WIDGET(o));
2091 } else {
2092 name = "settings";
2094 if (n_props == 0) {
2095 body = g_strdup_printf("%s[0] = { }", name);
2096 goto end_show_g_objects;
2099 body = g_strdup_printf("%s[%d] = {\n", name, n_props);
2100 for (i=0; i < n_props; i++) {
2101 pspec = proplist[i];
2102 tname = G_OBJECT_TYPE_NAME(pspec);
2103 bzero(&value, sizeof value);
2104 valstr = NULL;
2106 if (!(pspec->flags & G_PARAM_READABLE))
2107 valstr = g_strdup_printf("not a readable property");
2108 else {
2109 g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
2110 g_object_get_property(G_OBJECT(o), pspec->name,
2111 &value);
2112 typeno = G_TYPE_FUNDAMENTAL( G_VALUE_TYPE(&value) );
2115 /* based on the type, recurse and display values */
2116 if (valstr == NULL) {
2117 valstr = xt_g_object_serialize(&value, tname, str,
2118 recurse);
2121 tmpstr = g_strdup_printf("%-13s %s%s%s,", tname, pspec->name,
2122 (typeno == G_TYPE_OBJECT) ? "." : " = ", valstr);
2123 b = body;
2125 #define XT_G_OBJECT_SPACING 50
2126 p = strrchr(tmpstr, '\n');
2127 if (p == NULL && strlen(tmpstr) > XT_G_OBJECT_SPACING) {
2128 body = g_strdup_printf(
2129 "%s%s %-50s\n%s %50s /* %3d flags=0x%08x */\n",
2130 body, str, tmpstr, str, "", i, pspec->flags);
2131 } else {
2132 char *fmt;
2133 int strspaces;
2134 if (p == NULL)
2135 strspaces = XT_G_OBJECT_SPACING;
2136 else
2137 strspaces = strlen(tmpstr) - (strlen(p) - strlen(str)) + XT_G_OBJECT_SPACING + 5;
2138 fmt = g_strdup_printf("%%s%%s %%-%ds /* %%3d flags=0x%%08x */\n", strspaces);
2139 body = g_strdup_printf(fmt, body, str, tmpstr, i, pspec->flags);
2140 g_free(fmt);
2142 g_free(tmpstr);
2143 g_free(b);
2144 g_free(valstr);
2146 end_show_g_objects:
2147 g_free(proplist);
2148 return (body);
2151 char *
2152 xt_append_settings(char *str, GObject *object, char *name, int recurse)
2154 char *newstr, *settings;
2156 settings = show_g_object_settings(object, name, recurse);
2157 if (str == NULL)
2158 str = g_strdup("");
2160 newstr = g_strdup_printf("%s%s %s%s };\n", str, name, settings, name);
2161 g_free(str);
2163 return newstr;
2167 about_webkit(struct tab *t, struct karg *arg)
2169 char *page, *body, *settingstr;
2171 settingstr = xt_append_settings(NULL, G_OBJECT(t->settings),
2172 "t->settings", 0);
2173 body = g_strdup_printf("<pre>%s</pre>\n", settingstr);
2174 g_free(settingstr);
2176 page = get_html_page("About Webkit", body, "", 0);
2177 g_free(body);
2179 load_webkit_string(t, page, XT_URI_ABOUT_WEBKIT, 0);
2180 g_free(page);
2182 return (0);
2185 static int toplevelcount = 0;
2187 void
2188 xt_append_toplevel(GtkWindow *w, char **body)
2190 char *n;
2192 n = g_strdup_printf("toplevel#%d", toplevelcount++);
2193 *body = xt_append_settings(*body, G_OBJECT(w), n, 0);
2194 g_free(n);
2198 allthethings(struct tab *t, struct karg *arg)
2200 GList *list;
2201 char *page, *body, *b;
2203 body = xt_append_settings(NULL, G_OBJECT(t->wv), "t->wv", 1);
2204 body = xt_append_settings(body, G_OBJECT(t->inspector),
2205 "t->inspector", 1);
2206 #if 0 /* not until warnings are gone */
2207 body = xt_append_settings(body, G_OBJECT(session),
2208 "session", 1);
2209 #endif
2210 toplevelcount = 0;
2211 list = gtk_window_list_toplevels();
2212 g_list_foreach(list, (GFunc)g_object_ref, NULL);
2213 g_list_foreach(list, (GFunc)xt_append_toplevel, &body);
2214 g_list_foreach(list, (GFunc)g_object_unref, NULL);
2215 g_list_free(list);
2217 b = body;
2218 body = g_strdup_printf("<pre>%scan paste clipboard = %d\n</pre>", body,
2219 webkit_web_view_can_paste_clipboard(t->wv));
2220 g_free(b);
2222 page = get_html_page("About All The Things _o/", body, "", 0);
2223 g_free(body);
2225 load_webkit_string(t, page, XT_URI_ABOUT_ALLTHETHINGS, 0);
2226 g_free(page);
2228 return (0);