-g instead of -ggdb3
[xxxterm.git] / about.c
blobc15840e6134acec333ff5690ae02905520fd2995
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>
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 #include <xxxterm.h>
25 * xxxterm "protocol" (xtp)
26 * We use this for managing stuff like downloads and favorites. They
27 * make magical HTML pages in memory which have xxxt:// links in order
28 * to communicate with xxxterm's internals. These links take the format:
29 * xxxt://class/session_key/action/arg
31 * Don't begin xtp class/actions as 0. atoi returns that on error.
33 * Typically we have not put addition of items in this framework, as
34 * adding items is either done via an ex-command or via a keybinding instead.
37 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
38 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
39 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
40 "td{overflow: hidden;" \
41 " padding: 2px 2px 2px 2px;" \
42 " border: 1px solid black;" \
43 " vertical-align:top;" \
44 " word-wrap: break-word}\n" \
45 "tr:hover{background: #ffff99}\n" \
46 "th{background-color: #cccccc;" \
47 " border: 1px solid black}\n" \
48 "table{width: 100%%;" \
49 " border: 1px black solid;" \
50 " border-collapse:collapse}\n" \
51 ".progress-outer{" \
52 "border: 1px solid black;" \
53 " height: 8px;" \
54 " width: 90%%}\n" \
55 ".progress-inner{float: left;" \
56 " height: 8px;" \
57 " background: green}\n" \
58 ".dlstatus{font-size: small;" \
59 " text-align: center}\n" \
60 "</style>\n"
62 /* XTP classes (xxxt://<class>) */
63 #define XT_XTP_INVALID (0) /* invalid */
64 #define XT_XTP_DL (1) /* downloads */
65 #define XT_XTP_HL (2) /* history */
66 #define XT_XTP_CL (3) /* cookies */
67 #define XT_XTP_FL (4) /* favorites */
69 /* XTP download actions */
70 #define XT_XTP_DL_LIST (1)
71 #define XT_XTP_DL_CANCEL (2)
72 #define XT_XTP_DL_REMOVE (3)
73 #define XT_XTP_DL_UNLINK (4)
74 #define XT_XTP_DL_START (5)
76 /* XTP history actions */
77 #define XT_XTP_HL_LIST (1)
78 #define XT_XTP_HL_REMOVE (2)
80 /* XTP cookie actions */
81 #define XT_XTP_CL_LIST (1)
82 #define XT_XTP_CL_REMOVE (2)
83 #define XT_XTP_CL_REMOVE_DOMAIN (3)
84 #define XT_XTP_CL_REMOVE_ALL (4)
86 /* XTP cookie actions */
87 #define XT_XTP_FL_LIST (1)
88 #define XT_XTP_FL_REMOVE (2)
90 int js_show_wl(struct tab *, struct karg *);
91 int pl_show_wl(struct tab *, struct karg *);
92 int set(struct tab *, struct karg *);
93 int marco(struct tab *, struct karg *);
94 int startpage(struct tab *, struct karg *);
95 const char * marco_message(int *);
96 void update_cookie_tabs(struct tab *apart_from);
97 int about_webkit(struct tab *, struct karg *);
98 int allthethings(struct tab *, struct karg *);
100 struct about_type about_list[] = {
101 { XT_URI_ABOUT_ABOUT, about },
102 { XT_URI_ABOUT_ALLTHETHINGS, allthethings },
103 { XT_URI_ABOUT_BLANK, blank },
104 { XT_URI_ABOUT_CERTS, ca_cmd },
105 { XT_URI_ABOUT_COOKIEWL, cookie_show_wl },
106 { XT_URI_ABOUT_COOKIEJAR, xtp_page_cl },
107 { XT_URI_ABOUT_DOWNLOADS, xtp_page_dl },
108 { XT_URI_ABOUT_FAVORITES, xtp_page_fl },
109 { XT_URI_ABOUT_HELP, help },
110 { XT_URI_ABOUT_HISTORY, xtp_page_hl },
111 { XT_URI_ABOUT_JSWL, js_show_wl },
112 { XT_URI_ABOUT_SET, set },
113 { XT_URI_ABOUT_STATS, stats },
114 { XT_URI_ABOUT_MARCO, marco },
115 { XT_URI_ABOUT_STARTPAGE, startpage },
116 { XT_URI_ABOUT_PLUGINWL, pl_show_wl },
117 { XT_URI_ABOUT_WEBKIT, about_webkit },
121 * Session IDs.
122 * We use these to prevent people putting xxxt:// URLs on
123 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
125 #define XT_XTP_SES_KEY_SZ 8
126 #define XT_XTP_SES_KEY_HEX_FMT \
127 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8
129 char *dl_session_key; /* downloads */
130 char *hl_session_key; /* history list */
131 char *cl_session_key; /* cookie list */
132 char *fl_session_key; /* favorites list */
134 int updating_fl_tabs = 0;
135 int updating_dl_tabs = 0;
136 int updating_hl_tabs = 0;
137 int updating_cl_tabs = 0;
138 struct download_list downloads;
140 size_t
141 about_list_size(void)
143 return (LENGTH(about_list));
146 gchar *
147 get_html_page(gchar *title, gchar *body, gchar *head, bool addstyles)
149 gchar *r;
151 r = g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
152 "<head>\n"
153 "<title>%s</title>\n"
154 "%s"
155 "%s"
156 "</head>\n"
157 "<body>\n"
158 "<h1>%s</h1>\n"
159 "%s\n</body>\n"
160 "</html>",
161 title,
162 addstyles ? XT_PAGE_STYLE : "",
163 head,
164 title,
165 body);
167 return (r);
171 * Display a web page from a HTML string in memory, rather than from a URL
173 void
174 load_webkit_string(struct tab *t, const char *str, gchar *title)
176 char file[PATH_MAX];
177 int i;
179 /* we set this to indicate we want to manually do navaction */
180 if (t->bfl)
181 t->item = webkit_web_back_forward_list_get_current_item(t->bfl);
183 t->xtp_meaning = XT_XTP_TAB_MEANING_NORMAL;
184 if (title) {
185 /* set t->xtp_meaning */
186 for (i = 0; i < LENGTH(about_list); i++)
187 if (!strcmp(title, about_list[i].name)) {
188 t->xtp_meaning = i;
189 break;
192 webkit_web_view_load_string(t->wv, str, NULL, encoding,
193 "file://");
194 #if GTK_CHECK_VERSION(2, 20, 0)
195 gtk_spinner_stop(GTK_SPINNER(t->spinner));
196 gtk_widget_hide(t->spinner);
197 #endif
198 snprintf(file, sizeof file, "%s" PS "%s", resource_dir, icons[0]);
199 xt_icon_from_file(t, file);
204 blank(struct tab *t, struct karg *args)
206 if (t == NULL)
207 show_oops(NULL, "blank invalid parameters");
209 load_webkit_string(t, "", XT_URI_ABOUT_BLANK);
211 return (0);
215 about(struct tab *t, struct karg *args)
217 char *page, *body;
219 if (t == NULL)
220 show_oops(NULL, "about invalid parameters");
222 body = g_strdup_printf("<b>Version: %s</b>"
223 #ifdef XXXTERM_BUILDSTR
224 "<br><b>Build: %s</b>"
225 #endif
226 "<br><b>WebKit: %d.%d.%d</b>"
227 "<br><b>User Agent: %d.%d</b>"
228 #ifdef WEBKITGTK_API_VERSION
229 "<br><b>WebKit API: %.1f</b>"
230 #endif
231 "<p>"
232 "Authors:"
233 "<ul>"
234 "<li>Marco Peereboom &lt;marco@peereboom.us&gt;</li>"
235 "<li>Stevan Andjelkovic &lt;stevan@student.chalmers.se&gt;</li>"
236 "<li>Edd Barrett &lt;vext01@gmail.com&gt;</li>"
237 "<li>Todd T. Fries &lt;todd@fries.net&gt;</li>"
238 "<li>Raphael Graf &lt;r@undefined.ch&gt;</li>"
239 "<li>Michal Mazurek &lt;akfaew@jasminek.net&gt;</li>"
240 "</ul>"
241 "Copyrights and licenses can be found on the XXXTerm "
242 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
243 "</p>",
244 #ifdef XXXTERM_BUILDSTR
245 version, XXXTERM_BUILDSTR,
246 #else
247 version,
248 #endif
249 WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION,
250 WEBKIT_USER_AGENT_MAJOR_VERSION, WEBKIT_USER_AGENT_MINOR_VERSION
251 #ifdef WEBKITGTK_API_VERSION
252 ,WEBKITGTK_API_VERSION
253 #endif
256 page = get_html_page("About", body, "", 0);
257 g_free(body);
259 load_webkit_string(t, page, XT_URI_ABOUT_ABOUT);
260 g_free(page);
262 return (0);
266 help(struct tab *t, struct karg *args)
268 char *page, *head, *body;
270 if (t == NULL)
271 show_oops(NULL, "help invalid parameters");
273 head = "<meta http-equiv=\"REFRESH\" content=\"0;"
274 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
275 "</head>\n";
276 body = "XXXTerm man page <a href=\"http://opensource.conformal.com/"
277 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
278 "cgi-bin/man-cgi?xxxterm</a>";
280 page = get_html_page(XT_NAME, body, head, FALSE);
282 load_webkit_string(t, page, XT_URI_ABOUT_HELP);
283 g_free(page);
285 return (0);
289 stats(struct tab *t, struct karg *args)
291 char *page, *body, *s, line[64 * 1024];
292 uint64_t line_count = 0;
293 FILE *r_cookie_f;
295 if (t == NULL)
296 show_oops(NULL, "stats invalid parameters");
298 line[0] = '\0';
299 if (save_rejected_cookies) {
300 if ((r_cookie_f = fopen(rc_fname, "r"))) {
301 for (;;) {
302 s = fgets(line, sizeof line, r_cookie_f);
303 if (s == NULL || feof(r_cookie_f) ||
304 ferror(r_cookie_f))
305 break;
306 line_count++;
308 fclose(r_cookie_f);
309 snprintf(line, sizeof line,
310 "<br/>Cookies blocked(*) total: %" PRIu64,
311 line_count);
312 } else
313 show_oops(t, "Can't open blocked cookies file: %s",
314 strerror(errno));
317 body = g_strdup_printf(
318 "Cookies blocked(*) this session: %" PRIu64
319 "%s"
320 "<p><small><b>*</b> results vary based on settings</small></p>",
321 blocked_cookies,
322 line);
324 page = get_html_page("Statistics", body, "", 0);
325 g_free(body);
327 load_webkit_string(t, page, XT_URI_ABOUT_STATS);
328 g_free(page);
330 return (0);
333 void
334 show_certs(struct tab *t, gnutls_x509_crt_t *certs,
335 size_t cert_count, char *title)
337 gnutls_datum_t cinfo;
338 char *tmp, *body;
339 int i;
341 body = g_strdup("");
343 for (i = 0; i < cert_count; i++) {
344 if (gnutls_x509_crt_print(certs[i], GNUTLS_CRT_PRINT_FULL,
345 &cinfo))
346 return;
348 tmp = body;
349 body = g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
350 body, i, cinfo.data);
351 gnutls_free(cinfo.data);
352 g_free(tmp);
355 tmp = get_html_page(title, body, "", 0);
356 g_free(body);
358 load_webkit_string(t, tmp, XT_URI_ABOUT_CERTS);
359 g_free(tmp);
363 ca_cmd(struct tab *t, struct karg *args)
365 FILE *f = NULL;
366 int rv = 1, certs = 0, certs_read;
367 struct stat sb;
368 gnutls_datum_t dt;
369 gnutls_x509_crt_t *c = NULL;
370 char *certs_buf = NULL, *s;
372 if ((f = fopen(ssl_ca_file, "r")) == NULL) {
373 show_oops(t, "Can't open CA file: %s", ssl_ca_file);
374 return (1);
377 if (fstat(fileno(f), &sb) == -1) {
378 show_oops(t, "Can't stat CA file: %s", ssl_ca_file);
379 goto done;
382 certs_buf = g_malloc(sb.st_size + 1);
383 if (fread(certs_buf, 1, sb.st_size, f) != sb.st_size) {
384 show_oops(t, "Can't read CA file: %s", strerror(errno));
385 goto done;
387 certs_buf[sb.st_size] = '\0';
389 s = certs_buf;
390 while ((s = strstr(s, "BEGIN CERTIFICATE"))) {
391 certs++;
392 s += strlen("BEGIN CERTIFICATE");
395 bzero(&dt, sizeof dt);
396 dt.data = (unsigned char *)certs_buf;
397 dt.size = sb.st_size;
398 c = g_malloc(sizeof(gnutls_x509_crt_t) * certs);
399 certs_read = gnutls_x509_crt_list_import(c, (unsigned int *)&certs, &dt,
400 GNUTLS_X509_FMT_PEM, 0);
401 if (certs_read <= 0) {
402 show_oops(t, "No cert(s) available");
403 goto done;
405 show_certs(t, c, certs_read, "Certificate Authority Certificates");
406 done:
407 if (c)
408 g_free(c);
409 if (certs_buf)
410 g_free(certs_buf);
411 if (f)
412 fclose(f);
414 return (rv);
418 cookie_show_wl(struct tab *t, struct karg *args)
420 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
421 wl_show(t, args, "Cookie White List", &c_wl);
423 return (0);
427 js_show_wl(struct tab *t, struct karg *args)
429 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
430 wl_show(t, args, "JavaScript White List", &js_wl);
432 return (0);
436 cookie_cmd(struct tab *t, struct karg *args)
438 if (args->i & XT_SHOW)
439 wl_show(t, args, "Cookie White List", &c_wl);
440 else if (args->i & XT_WL_TOGGLE) {
441 args->i |= XT_WL_RELOAD;
442 toggle_cwl(t, args);
443 } else if (args->i & XT_SAVE) {
444 args->i |= XT_WL_RELOAD;
445 wl_save(t, args, XT_WL_COOKIE);
446 } else if (args->i & XT_DELETE) {
447 remove_cookie_all();
448 update_cookie_tabs(NULL);
451 return (0);
455 js_cmd(struct tab *t, struct karg *args)
457 if (args->i & XT_SHOW)
458 wl_show(t, args, "JavaScript White List", &js_wl);
459 else if (args->i & XT_SAVE) {
460 args->i |= XT_WL_RELOAD;
461 wl_save(t, args, XT_WL_JAVASCRIPT);
462 } else if (args->i & XT_WL_TOGGLE) {
463 args->i |= XT_WL_RELOAD;
464 toggle_js(t, args);
465 } else if (args->i & XT_DELETE)
466 show_oops(t, "'js delete' currently unimplemented");
468 return (0);
472 pl_show_wl(struct tab *t, struct karg *args)
474 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
475 wl_show(t, args, "Plugin White List", &pl_wl);
477 return (0);
481 pl_cmd(struct tab *t, struct karg *args)
483 if (args->i & XT_SHOW)
484 wl_show(t, args, "Plugin White List", &pl_wl);
485 else if (args->i & XT_SAVE) {
486 args->i |= XT_WL_RELOAD;
487 wl_save(t, args, XT_WL_PLUGIN);
488 } else if (args->i & XT_WL_TOGGLE) {
489 args->i |= XT_WL_RELOAD;
490 toggle_pl(t, args);
491 } else if (args->i & XT_DELETE)
492 show_oops(t, "'plugin delete' currently unimplemented");
494 return (0);
498 * cancel, remove, etc. downloads
500 void
501 xtp_handle_dl(struct tab *t, uint8_t cmd, int id)
503 struct download find, *d = NULL;
505 DNPRINTF(XT_D_DOWNLOAD, "download control: cmd %d, id %d\n", cmd, id);
507 /* some commands require a valid download id */
508 if (cmd != XT_XTP_DL_LIST) {
509 /* lookup download in question */
510 find.id = id;
511 d = RB_FIND(download_list, &downloads, &find);
513 if (d == NULL) {
514 show_oops(t, "%s: no such download", __func__);
515 return;
519 /* decide what to do */
520 switch (cmd) {
521 case XT_XTP_DL_START:
522 /* our downloads always needs to be
523 * restarted if called from here
525 download_start(t, d, XT_DL_RESTART);
526 break;
527 case XT_XTP_DL_CANCEL:
528 webkit_download_cancel(d->download);
529 g_object_unref(d->download);
530 RB_REMOVE(download_list, &downloads, d);
531 break;
532 case XT_XTP_DL_UNLINK:
533 #ifdef __MINGW32__
534 unlink(webkit_download_get_destination_uri(d->download));
535 #else
536 unlink(webkit_download_get_destination_uri(d->download) +
537 strlen("file://"));
538 #endif
539 /* FALLTHROUGH */
540 case XT_XTP_DL_REMOVE:
541 webkit_download_cancel(d->download); /* just incase */
542 g_object_unref(d->download);
543 RB_REMOVE(download_list, &downloads, d);
544 break;
545 case XT_XTP_DL_LIST:
546 /* Nothing */
547 break;
548 default:
549 show_oops(t, "%s: unknown command", __func__);
550 break;
552 xtp_page_dl(t, NULL);
556 * Actions on history, only does one thing for now, but
557 * we provide the function for future actions
559 void
560 xtp_handle_hl(struct tab *t, uint8_t cmd, int id)
562 struct history *h, *next;
563 int i = 1;
565 switch (cmd) {
566 case XT_XTP_HL_REMOVE:
567 /* walk backwards, as listed in reverse */
568 for (h = RB_MAX(history_list, &hl); h != NULL; h = next) {
569 next = RB_PREV(history_list, &hl, h);
570 if (id == i) {
571 RB_REMOVE(history_list, &hl, h);
572 g_free((gpointer) h->title);
573 g_free((gpointer) h->uri);
574 g_free(h);
575 break;
577 i++;
579 break;
580 case XT_XTP_HL_LIST:
581 /* Nothing - just xtp_page_hl() below */
582 break;
583 default:
584 show_oops(t, "%s: unknown command", __func__);
585 break;
588 xtp_page_hl(t, NULL);
591 /* remove a favorite */
592 void
593 remove_favorite(struct tab *t, int index)
595 char file[PATH_MAX], *title, *uri = NULL;
596 char *new_favs, *tmp;
597 FILE *f;
598 int i;
599 size_t len, lineno;
601 /* open favorites */
602 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
604 if ((f = fopen(file, "r")) == NULL) {
605 show_oops(t, "%s: can't open favorites: %s",
606 __func__, strerror(errno));
607 return;
610 /* build a string which will become the new favroites file */
611 new_favs = g_strdup("");
613 for (i = 1;;) {
614 if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
615 if (feof(f) || ferror(f))
616 break;
617 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
618 if (len == 0) {
619 free(title);
620 title = NULL;
621 continue;
624 if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) {
625 if (feof(f) || ferror(f)) {
626 show_oops(t, "%s: can't parse favorites %s",
627 __func__, strerror(errno));
628 goto clean;
632 /* as long as this isn't the one we are deleting add to file */
633 if (i != index) {
634 tmp = new_favs;
635 new_favs = g_strdup_printf("%s%s\n%s\n",
636 new_favs, title, uri);
637 g_free(tmp);
640 free(uri);
641 uri = NULL;
642 free(title);
643 title = NULL;
644 i++;
646 fclose(f);
648 /* write back new favorites file */
649 if ((f = fopen(file, "w")) == NULL) {
650 show_oops(t, "%s: can't open favorites: %s",
651 __func__, strerror(errno));
652 goto clean;
655 if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
656 show_oops(t, "%s: can't fwrite", __func__);
657 fclose(f);
659 clean:
660 if (uri)
661 free(uri);
662 if (title)
663 free(title);
665 g_free(new_favs);
669 add_favorite(struct tab *t, struct karg *args)
671 char file[PATH_MAX];
672 FILE *f;
673 char *line = NULL;
674 size_t urilen, linelen;
675 const gchar *uri, *title;
677 if (t == NULL)
678 return (1);
680 /* don't allow adding of xtp pages to favorites */
681 if (t->xtp_meaning != XT_XTP_TAB_MEANING_NORMAL) {
682 show_oops(t, "%s: can't add xtp pages to favorites", __func__);
683 return (1);
686 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
687 if ((f = fopen(file, "r+")) == NULL) {
688 show_oops(t, "Can't open favorites file: %s", strerror(errno));
689 return (1);
692 title = get_title(t, FALSE);
693 uri = get_uri(t);
695 if (title == NULL || uri == NULL) {
696 show_oops(t, "can't add page to favorites");
697 goto done;
700 urilen = strlen(uri);
702 for (;;) {
703 if ((line = fparseln(f, &linelen, NULL, NULL, 0)) == NULL)
704 if (feof(f) || ferror(f))
705 break;
707 if (linelen == urilen && !strcmp(line, uri))
708 goto done;
710 free(line);
711 line = NULL;
714 fprintf(f, "\n%s\n%s", title, uri);
715 done:
716 if (line)
717 free(line);
718 fclose(f);
720 update_favorite_tabs(NULL);
722 return (0);
725 void
726 xtp_handle_fl(struct tab *t, uint8_t cmd, int arg)
728 switch (cmd) {
729 case XT_XTP_FL_LIST:
730 /* nothing, just the below call to xtp_page_fl() */
731 break;
732 case XT_XTP_FL_REMOVE:
733 remove_favorite(t, arg);
734 break;
735 default:
736 show_oops(t, "%s: invalid favorites command", __func__);
737 break;
740 xtp_page_fl(t, NULL);
743 void
744 xtp_handle_cl(struct tab *t, uint8_t cmd, int arg)
746 switch (cmd) {
747 case XT_XTP_CL_LIST:
748 /* nothing, just xtp_page_cl() */
749 break;
750 case XT_XTP_CL_REMOVE:
751 remove_cookie(arg);
752 break;
753 case XT_XTP_CL_REMOVE_DOMAIN:
754 remove_cookie_domain(arg);
755 break;
756 case XT_XTP_CL_REMOVE_ALL:
757 remove_cookie_all();
758 break;
759 default:
760 show_oops(t, "%s: unknown cookie xtp command", __func__);
761 break;
764 xtp_page_cl(t, NULL);
767 /* link an XTP class to it's session key and handler function */
768 struct xtp_despatch {
769 uint8_t xtp_class;
770 char **session_key;
771 void (*handle_func)(struct tab *, uint8_t, int);
774 struct xtp_despatch xtp_despatches[] = {
775 { XT_XTP_DL, &dl_session_key, xtp_handle_dl },
776 { XT_XTP_HL, &hl_session_key, xtp_handle_hl },
777 { XT_XTP_FL, &fl_session_key, xtp_handle_fl },
778 { XT_XTP_CL, &cl_session_key, xtp_handle_cl },
779 { XT_XTP_INVALID, NULL, NULL }
783 * generate a session key to secure xtp commands.
784 * pass in a ptr to the key in question and it will
785 * be modified in place.
787 void
788 generate_xtp_session_key(char **key)
790 uint8_t rand_bytes[XT_XTP_SES_KEY_SZ];
792 /* free old key */
793 if (*key)
794 g_free(*key);
796 /* make a new one */
797 arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ);
798 *key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT,
799 rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
800 rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]);
802 DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key);
805 void
806 xtp_generate_keys(void)
808 /* generate session keys for xtp pages */
809 generate_xtp_session_key(&dl_session_key);
810 generate_xtp_session_key(&hl_session_key);
811 generate_xtp_session_key(&cl_session_key);
812 generate_xtp_session_key(&fl_session_key);
816 * validate a xtp session key.
817 * return (1) if OK
820 validate_xtp_session_key(struct tab *t, char *trusted, char *untrusted)
822 if (strcmp(trusted, untrusted) != 0) {
823 show_oops(t, "%s: xtp session key mismatch possible spoof",
824 __func__);
825 return (0);
828 return (1);
832 * is the url xtp protocol? (xxxt://)
833 * if so, parse and despatch correct bahvior
836 parse_xtp_url(struct tab *t, const char *url)
838 char *dup = NULL, *p, *last = NULL;
839 uint8_t n_tokens = 0;
840 char *tokens[4] = {NULL, NULL, NULL, ""};
841 struct xtp_despatch *dsp, *dsp_match = NULL;
842 uint8_t req_class;
843 int ret = FALSE;
846 * tokens array meaning:
847 * tokens[0] = class
848 * tokens[1] = session key
849 * tokens[2] = action
850 * tokens[3] = optional argument
853 DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, url);
855 if (strncmp(url, XT_XTP_STR, strlen(XT_XTP_STR)))
856 goto clean;
858 dup = g_strdup(url + strlen(XT_XTP_STR));
860 /* split out the url */
861 for ((p = strtok_r(dup, "/", &last)); p;
862 (p = strtok_r(NULL, "/", &last))) {
863 if (n_tokens < 4)
864 tokens[n_tokens++] = p;
867 /* should be atleast three fields 'class/seskey/command/arg' */
868 if (n_tokens < 3)
869 goto clean;
871 dsp = xtp_despatches;
872 req_class = atoi(tokens[0]);
873 while (dsp->xtp_class) {
874 if (dsp->xtp_class == req_class) {
875 dsp_match = dsp;
876 break;
878 dsp++;
881 /* did we find one atall? */
882 if (dsp_match == NULL) {
883 show_oops(t, "%s: no matching xtp despatch found", __func__);
884 goto clean;
887 /* check session key and call despatch function */
888 if (validate_xtp_session_key(t, *(dsp_match->session_key), tokens[1])) {
889 ret = TRUE; /* all is well, this was a valid xtp request */
890 dsp_match->handle_func(t, atoi(tokens[2]), atoi(tokens[3]));
893 clean:
894 if (dup)
895 g_free(dup);
897 return (ret);
901 * update all favorite tabs apart from one. Pass NULL if
902 * you want to update all.
904 void
905 update_favorite_tabs(struct tab *apart_from)
907 struct tab *t;
908 if (!updating_fl_tabs) {
909 updating_fl_tabs = 1; /* stop infinite recursion */
910 TAILQ_FOREACH(t, &tabs, entry)
911 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL)
912 && (t != apart_from))
913 xtp_page_fl(t, NULL);
914 updating_fl_tabs = 0;
919 * update all download tabs apart from one. Pass NULL if
920 * you want to update all.
922 void
923 update_download_tabs(struct tab *apart_from)
925 struct tab *t;
926 if (!updating_dl_tabs) {
927 updating_dl_tabs = 1; /* stop infinite recursion */
928 TAILQ_FOREACH(t, &tabs, entry)
929 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_DL)
930 && (t != apart_from))
931 xtp_page_dl(t, NULL);
932 updating_dl_tabs = 0;
937 * update all cookie tabs apart from one. Pass NULL if
938 * you want to update all.
940 void
941 update_cookie_tabs(struct tab *apart_from)
943 struct tab *t;
944 if (!updating_cl_tabs) {
945 updating_cl_tabs = 1; /* stop infinite recursion */
946 TAILQ_FOREACH(t, &tabs, entry)
947 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL)
948 && (t != apart_from))
949 xtp_page_cl(t, NULL);
950 updating_cl_tabs = 0;
955 * update all history tabs apart from one. Pass NULL if
956 * you want to update all.
958 void
959 update_history_tabs(struct tab *apart_from)
961 struct tab *t;
963 if (!updating_hl_tabs) {
964 updating_hl_tabs = 1; /* stop infinite recursion */
965 TAILQ_FOREACH(t, &tabs, entry)
966 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL)
967 && (t != apart_from))
968 xtp_page_hl(t, NULL);
969 updating_hl_tabs = 0;
973 /* show a list of favorites (bookmarks) */
975 xtp_page_fl(struct tab *t, struct karg *args)
977 char file[PATH_MAX];
978 FILE *f;
979 char *uri = NULL, *title = NULL;
980 size_t len, lineno = 0;
981 int i, failed = 0;
982 char *body, *tmp, *page = NULL;
983 const char delim[3] = {'\\', '\\', '\0'};
985 DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
987 if (t == NULL)
988 warn("%s: bad param", __func__);
990 /* new session key */
991 if (!updating_fl_tabs)
992 generate_xtp_session_key(&fl_session_key);
994 /* open favorites */
995 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
996 if ((f = fopen(file, "r")) == NULL) {
997 show_oops(t, "Can't open favorites file: %s", strerror(errno));
998 return (1);
1001 /* body */
1002 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
1003 "<th style='width: 40px'>&#35;</th><th>Link</th>"
1004 "<th style='width: 40px'>Rm</th></tr>\n");
1006 for (i = 1;;) {
1007 if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
1008 break;
1009 if (strlen(title) == 0) {
1010 free(title);
1011 title = NULL;
1012 continue;
1015 if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
1016 if (feof(f) || ferror(f)) {
1017 show_oops(t, "favorites file corrupt");
1018 failed = 1;
1019 break;
1022 tmp = body;
1023 body = g_strdup_printf("%s<tr>"
1024 "<td>%d</td>"
1025 "<td><a href='%s'>%s</a></td>"
1026 "<td style='text-align: center'>"
1027 "<a href='%s%d/%s/%d/%d'>X</a></td>"
1028 "</tr>\n",
1029 body, i, uri, title,
1030 XT_XTP_STR, XT_XTP_FL, fl_session_key, XT_XTP_FL_REMOVE, i);
1032 g_free(tmp);
1034 free(uri);
1035 uri = NULL;
1036 free(title);
1037 title = NULL;
1038 i++;
1040 fclose(f);
1042 /* if none, say so */
1043 if (i == 1) {
1044 tmp = body;
1045 body = g_strdup_printf("%s<tr>"
1046 "<td colspan='3' style='text-align: center'>"
1047 "No favorites - To add one use the 'favadd' command."
1048 "</td></tr>", body);
1049 g_free(tmp);
1052 tmp = body;
1053 body = g_strdup_printf("%s</table>", body);
1054 g_free(tmp);
1056 if (uri)
1057 free(uri);
1058 if (title)
1059 free(title);
1061 /* render */
1062 if (!failed) {
1063 page = get_html_page("Favorites", body, "", 1);
1064 load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES);
1065 g_free(page);
1068 update_favorite_tabs(t);
1070 if (body)
1071 g_free(body);
1073 return (failed);
1077 * Return a new string with a download row (in html)
1078 * appended. Old string is freed.
1080 char *
1081 xtp_page_dl_row(struct tab *t, char *html, struct download *dl)
1084 WebKitDownloadStatus stat;
1085 const gchar *destination;
1086 char *status_html = NULL, *cmd_html = NULL, *new_html;
1087 gdouble progress;
1088 char cur_sz[FMT_SCALED_STRSIZE];
1089 char tot_sz[FMT_SCALED_STRSIZE];
1090 char *xtp_prefix;
1092 DNPRINTF(XT_D_DOWNLOAD, "%s: dl->id %d\n", __func__, dl->id);
1094 /* All actions wil take this form:
1095 * xxxt://class/seskey
1097 xtp_prefix = g_strdup_printf("%s%d/%s/",
1098 XT_XTP_STR, XT_XTP_DL, dl_session_key);
1100 stat = webkit_download_get_status(dl->download);
1102 switch (stat) {
1103 case WEBKIT_DOWNLOAD_STATUS_FINISHED:
1104 status_html = g_strdup_printf("Finished");
1105 cmd_html = g_strdup_printf(
1106 "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1107 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1108 XT_XTP_DL_UNLINK, dl->id);
1109 break;
1110 case WEBKIT_DOWNLOAD_STATUS_STARTED:
1111 /* gather size info */
1112 progress = 100 * webkit_download_get_progress(dl->download);
1114 fmt_scaled(
1115 webkit_download_get_current_size(dl->download), cur_sz);
1116 fmt_scaled(
1117 webkit_download_get_total_size(dl->download), tot_sz);
1119 status_html = g_strdup_printf(
1120 "<div style='width: 100%%' align='center'>"
1121 "<div class='progress-outer'>"
1122 "<div class='progress-inner' style='width: %.2f%%'>"
1123 "</div></div></div>"
1124 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
1125 progress, cur_sz, tot_sz, progress);
1127 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1128 xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
1130 break;
1131 /* LLL */
1132 case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
1133 status_html = g_strdup_printf("Cancelled");
1134 cmd_html = g_strdup_printf(
1135 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1136 xtp_prefix, XT_XTP_DL_START, dl->id,
1137 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1138 XT_XTP_DL_UNLINK, dl->id);
1139 break;
1140 case WEBKIT_DOWNLOAD_STATUS_ERROR:
1141 status_html = g_strdup_printf("Error!");
1142 cmd_html = g_strdup_printf(
1143 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1144 xtp_prefix, XT_XTP_DL_START, dl->id,
1145 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1146 XT_XTP_DL_UNLINK, dl->id);
1147 break;
1148 case WEBKIT_DOWNLOAD_STATUS_CREATED:
1149 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Start</a> / <a href='%s%d/%d'>Cancel</a>",
1150 xtp_prefix, XT_XTP_DL_START, dl->id, xtp_prefix,
1151 XT_XTP_DL_CANCEL, dl->id);
1152 status_html = g_strdup_printf("Created");
1153 break;
1154 default:
1155 show_oops(t, "%s: unknown download status", __func__);
1158 destination = webkit_download_get_destination_uri(dl->download);
1159 /* we might not have a destination set yet */
1160 if (!destination)
1161 destination = webkit_download_get_suggested_filename(dl->download);
1162 new_html = g_strdup_printf(
1163 "%s\n<tr><td>%s</td><td>%s</td>"
1164 "<td style='text-align:center'>%s</td></tr>\n",
1165 html, basename((char *)destination),
1166 status_html, cmd_html);
1167 g_free(html);
1169 if (status_html)
1170 g_free(status_html);
1172 if (cmd_html)
1173 g_free(cmd_html);
1175 g_free(xtp_prefix);
1177 return new_html;
1180 /* cookie management XTP page */
1182 xtp_page_cl(struct tab *t, struct karg *args)
1184 char *body, *page, *tmp;
1185 int i = 1; /* all ids start 1 */
1186 int domain_id = 0;
1187 GSList *sc, *pc, *pc_start;
1188 SoupCookie *c;
1189 char *type, *table_headers, *last_domain;
1191 DNPRINTF(XT_D_CMD, "%s", __func__);
1193 if (t == NULL) {
1194 show_oops(NULL, "%s invalid parameters", __func__);
1195 return (1);
1198 /* Generate a new session key */
1199 if (!updating_cl_tabs)
1200 generate_xtp_session_key(&cl_session_key);
1202 /* table headers */
1203 table_headers = g_strdup_printf("<table><tr>"
1204 "<th>Type</th>"
1205 "<th>Name</th>"
1206 "<th style='width:200px'>Value</th>"
1207 "<th>Path</th>"
1208 "<th>Expires</th>"
1209 "<th>Secure</th>"
1210 "<th>HTTP<br />only</th>"
1211 "<th style='width:40px'>Rm</th></tr>\n");
1213 sc = soup_cookie_jar_all_cookies(s_cookiejar);
1214 pc = soup_cookie_jar_all_cookies(p_cookiejar);
1215 pc_start = pc;
1217 body = g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
1218 "[ Remove All Cookies From All Domains ]</a></div>\n",
1219 XT_XTP_STR, XT_XTP_CL, cl_session_key, XT_XTP_CL_REMOVE_ALL);
1221 last_domain = strdup("");
1222 for (; sc; sc = sc->next) {
1223 c = sc->data;
1225 if (strcmp(last_domain, c->domain) != 0) {
1226 /* new domain */
1227 domain_id ++;
1228 free(last_domain);
1229 last_domain = strdup(c->domain);
1231 if (body != NULL) {
1232 tmp = body;
1233 body = g_strdup_printf("%s</table>"
1234 "<h2>%s</h2><div align=\"center\">"
1235 "<a href='%s%d/%s/%d/%d'>"
1236 "[ Remove All From This Domain ]"
1237 "</a></div>%s\n",
1238 body, c->domain,
1239 XT_XTP_STR, XT_XTP_CL, cl_session_key,
1240 XT_XTP_CL_REMOVE_DOMAIN, domain_id,
1241 table_headers);
1242 g_free(tmp);
1243 } else {
1244 /* first domain */
1245 body = g_strdup_printf("<h2>%s</h2>"
1246 "<div align=\"center\">"
1247 "<a href='%s%d/%s/%d/%d'>"
1248 "[ Remove All From This Domain ]</a></div>%s\n",
1249 c->domain, XT_XTP_STR, XT_XTP_CL,
1250 cl_session_key, XT_XTP_CL_REMOVE_DOMAIN,
1251 domain_id, table_headers);
1255 type = "Session";
1256 for (pc = pc_start; pc; pc = pc->next)
1257 if (soup_cookie_equal(pc->data, c)) {
1258 type = "Session + Persistent";
1259 break;
1262 tmp = body;
1263 body = g_strdup_printf(
1264 "%s\n<tr>"
1265 "<td>%s</td>"
1266 "<td style='word-wrap:normal'>%s</td>"
1267 "<td>"
1268 " <textarea rows='4'>%s</textarea>"
1269 "</td>"
1270 "<td>%s</td>"
1271 "<td>%s</td>"
1272 "<td>%d</td>"
1273 "<td>%d</td>"
1274 "<td style='text-align:center'>"
1275 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1276 body,
1277 type,
1278 c->name,
1279 c->value,
1280 c->path,
1281 c->expires ?
1282 soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
1283 c->secure,
1284 c->http_only,
1286 XT_XTP_STR,
1287 XT_XTP_CL,
1288 cl_session_key,
1289 XT_XTP_CL_REMOVE,
1293 g_free(tmp);
1294 i++;
1297 soup_cookies_free(sc);
1298 soup_cookies_free(pc);
1300 /* small message if there are none */
1301 if (i == 1) {
1302 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1303 "colspan='8'>No Cookies</td></tr>\n", table_headers);
1305 tmp = body;
1306 body = g_strdup_printf("%s</table>", body);
1307 g_free(tmp);
1309 page = get_html_page("Cookie Jar", body, "", TRUE);
1310 g_free(body);
1311 g_free(table_headers);
1312 g_free(last_domain);
1314 load_webkit_string(t, page, XT_URI_ABOUT_COOKIEJAR);
1315 update_cookie_tabs(t);
1317 g_free(page);
1319 return (0);
1323 xtp_page_hl(struct tab *t, struct karg *args)
1325 char *body, *page, *tmp;
1326 struct history *h;
1327 int i = 1; /* all ids start 1 */
1329 DNPRINTF(XT_D_CMD, "%s", __func__);
1331 if (t == NULL) {
1332 show_oops(NULL, "%s invalid parameters", __func__);
1333 return (1);
1336 /* Generate a new session key */
1337 if (!updating_hl_tabs)
1338 generate_xtp_session_key(&hl_session_key);
1340 /* body */
1341 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
1342 "<th>URI</th><th>Title</th><th>Last visited</th><th style='width: 40px'>Rm</th></tr>\n");
1344 RB_FOREACH_REVERSE(h, history_list, &hl) {
1345 tmp = body;
1346 body = g_strdup_printf(
1347 "%s\n<tr>"
1348 "<td><a href='%s'>%s</a></td>"
1349 "<td>%s</td>"
1350 "<td>%s</td>"
1351 "<td style='text-align: center'>"
1352 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1353 body, h->uri, h->uri, h->title, ctime(&h->time),
1354 XT_XTP_STR, XT_XTP_HL, hl_session_key,
1355 XT_XTP_HL_REMOVE, i);
1357 g_free(tmp);
1358 i++;
1361 /* small message if there are none */
1362 if (i == 1) {
1363 tmp = body;
1364 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1365 "colspan='3'>No History</td></tr>\n", body);
1366 g_free(tmp);
1369 tmp = body;
1370 body = g_strdup_printf("%s</table>", body);
1371 g_free(tmp);
1373 page = get_html_page("History", body, "", TRUE);
1374 g_free(body);
1377 * update all history manager tabs as the xtp session
1378 * key has now changed. No need to update the current tab.
1379 * Already did that above.
1381 update_history_tabs(t);
1383 load_webkit_string(t, page, XT_URI_ABOUT_HISTORY);
1384 g_free(page);
1386 return (0);
1390 * Generate a web page detailing the status of any downloads
1393 xtp_page_dl(struct tab *t, struct karg *args)
1395 struct download *dl;
1396 char *body, *page, *tmp;
1397 char *ref;
1398 int n_dl = 1;
1400 DNPRINTF(XT_D_DOWNLOAD, "%s", __func__);
1402 if (t == NULL) {
1403 show_oops(NULL, "%s invalid parameters", __func__);
1404 return (1);
1408 * Generate a new session key for next page instance.
1409 * This only happens for the top level call to xtp_page_dl()
1410 * in which case updating_dl_tabs is 0.
1412 if (!updating_dl_tabs)
1413 generate_xtp_session_key(&dl_session_key);
1415 /* header - with refresh so as to update */
1416 if (refresh_interval >= 1)
1417 ref = g_strdup_printf(
1418 "<meta http-equiv='refresh' content='%u"
1419 ";url=%s%d/%s/%d' />\n",
1420 refresh_interval,
1421 XT_XTP_STR,
1422 XT_XTP_DL,
1423 dl_session_key,
1424 XT_XTP_DL_LIST);
1425 else
1426 ref = g_strdup("");
1428 body = g_strdup_printf("<div align='center'>"
1429 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
1430 "</p><table><tr><th style='width: 60%%'>"
1431 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
1432 XT_XTP_STR, XT_XTP_DL, dl_session_key, XT_XTP_DL_LIST);
1434 RB_FOREACH_REVERSE(dl, download_list, &downloads) {
1435 body = xtp_page_dl_row(t, body, dl);
1436 n_dl++;
1439 /* message if no downloads in list */
1440 if (n_dl == 1) {
1441 tmp = body;
1442 body = g_strdup_printf("%s\n<tr><td colspan='3'"
1443 " style='text-align: center'>"
1444 "No downloads</td></tr>\n", body);
1445 g_free(tmp);
1448 tmp = body;
1449 body = g_strdup_printf("%s</table></div>", body);
1450 g_free(tmp);
1452 page = get_html_page("Downloads", body, ref, 1);
1453 g_free(ref);
1454 g_free(body);
1457 * update all download manager tabs as the xtp session
1458 * key has now changed. No need to update the current tab.
1459 * Already did that above.
1461 update_download_tabs(t);
1463 load_webkit_string(t, page, XT_URI_ABOUT_DOWNLOADS);
1464 g_free(page);
1466 return (0);
1470 startpage(struct tab *t, struct karg *args)
1472 char *page, *body, *b;
1473 struct sp *s;
1475 if (t == NULL)
1476 show_oops(NULL, "startpage invalid parameters");
1478 body = g_strdup_printf("<b>Startup Exception(s):</b><p>");
1480 TAILQ_FOREACH(s, &spl, entry) {
1481 b = body;
1482 body = g_strdup_printf("%s%s<br>", body, s->line);
1483 g_free(b);
1486 page = get_html_page("Startup Exception", body, "", 0);
1487 g_free(body);
1489 load_webkit_string(t, page, XT_URI_ABOUT_STARTPAGE);
1490 g_free(page);
1492 return (0);
1495 void
1496 startpage_add(const char *fmt, ...)
1498 va_list ap;
1499 char *msg;
1500 struct sp *s;
1502 if (fmt == NULL)
1503 return;
1505 va_start(ap, fmt);
1506 if (vasprintf(&msg, fmt, ap) == -1)
1507 errx(1, "startpage_add failed");
1508 va_end(ap);
1510 s = g_malloc0(sizeof *s);
1511 s->line = msg;
1513 TAILQ_INSERT_TAIL(&spl, s, entry);
1516 gchar *
1517 show_g_object_settings(GObject *o, char *str, int recurse)
1519 char *b, *body, *valstr;
1520 guint n_props = 0;
1521 int i;
1522 GParamSpec *pspec;
1523 const gchar *tname;
1524 GValue value;
1525 int typeno;
1526 const gchar *string;
1527 gboolean boolean;
1528 gfloat fp;
1529 gdouble fpd;
1530 gint number;
1531 guint unumber;
1532 int64_t number64;
1533 uint64_t unumber64;
1534 GObject *object;
1535 GParamSpec **proplist;
1536 char *tmpstr, *tmpsettings;
1538 if (!G_IS_OBJECT(o)) {
1539 fprintf(stderr, "%s is not a g_object\n", str);
1540 return g_strdup("");
1542 proplist = g_object_class_list_properties(
1543 G_OBJECT_GET_CLASS(o), &n_props);
1544 body = g_strdup_printf("%s: %3d settings\n", str, n_props);
1545 for (i=0; i < n_props; i++) {
1546 pspec = proplist[i];
1547 tname = G_OBJECT_TYPE_NAME(pspec);
1548 bzero(&value, sizeof value);
1549 valstr = NULL;
1551 if (!(pspec->flags & G_PARAM_READABLE))
1552 valstr = g_strdup_printf("not a readable property");
1553 else {
1554 g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
1555 g_object_get_property(G_OBJECT(o), pspec->name,
1556 &value);
1559 /* based on the type, recurse and display values */
1560 if (valstr == NULL) {
1561 typeno = G_TYPE_FUNDAMENTAL( G_VALUE_TYPE(&value) );
1562 switch ( typeno ) {
1563 case G_TYPE_ENUM:
1564 number = g_value_get_enum(&value);
1565 valstr = g_strdup_printf("%d", number);
1566 break;
1567 case G_TYPE_INT:
1568 number = g_value_get_int(&value);
1569 valstr = g_strdup_printf("%d", number);
1570 break;
1571 case G_TYPE_INT64:
1572 number64 = (int64_t)g_value_get_int64(&value);
1573 valstr = g_strdup_printf("%" PRIo64, number64);
1574 break;
1575 case G_TYPE_UINT:
1576 unumber = g_value_get_uint(&value);
1577 valstr = g_strdup_printf("%d", unumber);
1578 break;
1579 case G_TYPE_UINT64:
1580 unumber64 =
1581 (uint64_t)g_value_get_uint64(&value);
1582 valstr =
1583 g_strdup_printf("%" PRIu64, unumber64);
1584 break;
1585 case G_TYPE_FLAGS:
1586 unumber = g_value_get_flags(&value);
1587 valstr = g_strdup_printf("0x%x", unumber);
1588 break;
1589 case G_TYPE_BOOLEAN:
1590 boolean = g_value_get_boolean(&value);
1591 valstr = g_strdup_printf("%s",
1592 boolean ? "TRUE" : "FALSE");
1593 break;
1594 case G_TYPE_FLOAT:
1595 fp = g_value_get_float(&value);
1596 valstr = g_strdup_printf("%f", fp);
1597 break;
1598 case G_TYPE_DOUBLE:
1599 fpd = g_value_get_double(&value);
1600 valstr = g_strdup_printf("%f", fpd);
1601 break;
1602 case G_TYPE_STRING:
1603 string = g_value_get_string(&value);
1604 valstr = g_strdup_printf("\"%s\"",
1605 string);
1606 break;
1607 case G_TYPE_OBJECT:
1608 object = g_value_get_object(&value);
1609 if (object != NULL) {
1610 if (recurse) {
1611 tmpstr = g_strdup_printf("%s ",
1612 str);
1613 tmpsettings = show_g_object_settings(
1614 object, tmpstr, recurse);
1615 valstr = g_strdup_printf(
1616 "{\n%s%s }\n",
1617 tmpsettings, str);
1618 g_free(tmpstr);
1619 g_free(tmpsettings);
1620 } else {
1621 valstr = g_strdup_printf("<...>");
1623 } else {
1624 valstr = g_strdup_printf("NULL");
1626 break;
1627 default:
1628 valstr = g_strdup_printf(
1629 "type %s unhandled",
1630 tname);
1634 b = body;
1635 body = g_strdup_printf(
1636 "%s%s: %3d: flags=0x%08x, %-13s %s = %s\n",
1637 body, str, i, pspec->flags, tname, pspec->name,
1638 valstr);
1639 g_free(b);
1640 g_free(valstr);
1642 g_free(proplist);
1643 return (body);
1647 about_webkit(struct tab *t, struct karg *arg)
1649 char *page, *body, *settingstr;
1651 settingstr = show_g_object_settings(G_OBJECT(t->settings),
1652 "t->settings", 0);
1653 body = g_strdup_printf("<pre>%s</pre>\n", settingstr);
1654 g_free(settingstr);
1656 page = get_html_page("About Webkit", body, "", 0);
1657 g_free(body);
1659 load_webkit_string(t, page, XT_URI_ABOUT_WEBKIT);
1660 g_free(page);
1662 return (0);
1666 allthethings(struct tab *t, struct karg *arg)
1668 char *page, *body, *b, *settingstr;
1669 extern GtkWidget *main_window;
1671 body = show_g_object_settings(G_OBJECT(t->wv), "t->wv", 1);
1672 b = body;
1673 settingstr = show_g_object_settings(G_OBJECT(t->inspector),
1674 "t->inspector", 1);
1675 body = g_strdup_printf("%s%s", body, settingstr);
1676 g_free(b);
1677 g_free(settingstr);
1678 b = body;
1679 settingstr = show_g_object_settings(G_OBJECT(main_window),
1680 "main_window", 1);
1681 body = g_strdup_printf("%s%s", body, settingstr);
1682 g_free(b);
1683 g_free(settingstr);
1684 b = body;
1685 body = g_strdup_printf("<pre>%scan paste clipboard = %d\n</pre>", body,
1686 webkit_web_view_can_paste_clipboard(t->wv));
1687 g_free(b);
1689 page = get_html_page("About All The Things _o/", body, "", 0);
1690 g_free(body);
1692 load_webkit_string(t, page, XT_URI_ABOUT_ALLTHETHINGS);
1693 g_free(page);
1695 return (0);