remove extra padding around the scrollbar; this makes the scrollbar disappear
[xombrero.git] / about.c
blobf59d2801c15a3c4ffeded6ec909ec03145d4319a
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 "</style>\n"
63 /* XTP classes (xxxt://<class>) */
64 #define XT_XTP_INVALID (0) /* invalid */
65 #define XT_XTP_DL (1) /* downloads */
66 #define XT_XTP_HL (2) /* history */
67 #define XT_XTP_CL (3) /* cookies */
68 #define XT_XTP_FL (4) /* favorites */
69 #define XT_XTP_SL (5) /* search */
70 #define XT_XTP_AB (6) /* about */
71 #define XT_XTP_SV (7) /* security violation */
73 /* XTP download actions */
74 #define XT_XTP_DL_LIST (1)
75 #define XT_XTP_DL_CANCEL (2)
76 #define XT_XTP_DL_REMOVE (3)
77 #define XT_XTP_DL_UNLINK (4)
78 #define XT_XTP_DL_START (5)
80 /* XTP history actions */
81 #define XT_XTP_HL_LIST (1)
82 #define XT_XTP_HL_REMOVE (2)
83 #define XT_XTP_HL_REMOVE_ALL (3)
85 /* XTP cookie actions */
86 #define XT_XTP_CL_LIST (1)
87 #define XT_XTP_CL_REMOVE (2)
88 #define XT_XTP_CL_REMOVE_DOMAIN (3)
89 #define XT_XTP_CL_REMOVE_ALL (4)
91 /* XTP cookie actions */
92 #define XT_XTP_FL_LIST (1)
93 #define XT_XTP_FL_REMOVE (2)
95 /* XPT search actions */
96 #define XT_XTP_SL_SET (1)
98 /* XPT about actions */
99 #define XT_XTP_AB_EDIT_CONF (1)
101 /* XTP security violation actions */
102 #define XT_XTP_SV_SHOW_CERT (1)
103 #define XT_XTP_SV_ALLOW_SESSION (2)
104 #define XT_XTP_SV_CACHE (3)
106 int js_show_wl(struct tab *, struct karg *);
107 int pl_show_wl(struct tab *, struct karg *);
108 int set(struct tab *, struct karg *);
109 int marco(struct tab *, struct karg *);
110 int startpage(struct tab *, struct karg *);
111 const char * marco_message(int *);
112 void update_cookie_tabs(struct tab *apart_from);
113 int about_webkit(struct tab *, struct karg *);
114 int allthethings(struct tab *, struct karg *);
116 struct about_type about_list[] = {
117 { XT_URI_ABOUT_ABOUT, xtp_page_ab },
118 { XT_URI_ABOUT_ALLTHETHINGS, allthethings },
119 { XT_URI_ABOUT_BLANK, blank },
120 { XT_URI_ABOUT_CERTS, ca_cmd },
121 { XT_URI_ABOUT_COOKIEWL, cookie_show_wl },
122 { XT_URI_ABOUT_COOKIEJAR, xtp_page_cl },
123 { XT_URI_ABOUT_DOWNLOADS, xtp_page_dl },
124 { XT_URI_ABOUT_FAVORITES, xtp_page_fl },
125 { XT_URI_ABOUT_HELP, help },
126 { XT_URI_ABOUT_HISTORY, xtp_page_hl },
127 { XT_URI_ABOUT_JSWL, js_show_wl },
128 { XT_URI_ABOUT_SET, set },
129 { XT_URI_ABOUT_STATS, stats },
130 { XT_URI_ABOUT_MARCO, marco },
131 { XT_URI_ABOUT_STARTPAGE, startpage },
132 { XT_URI_ABOUT_PLUGINWL, pl_show_wl },
133 { XT_URI_ABOUT_WEBKIT, about_webkit },
134 { XT_URI_ABOUT_SEARCH, xtp_page_sl },
135 { XT_URI_ABOUT_SECVIOLATION, NULL },
138 struct search_type {
139 const char *name;
140 const char *url;
141 } search_list[] = {
142 { "Google (SSL)", "https://encrypted.google.com/search?q=%s&&client=xombrero" },
143 { "Bing", "http://www.bing.com/search?q=%s" },
144 { "Yahoo", "http://search.yahoo.com/search?p=%s" },
145 { "DuckDuckGo", "https://duckduckgo.com/?q=%s" },
146 { "DuckDuckGo (HTML)", "https://duckduckgo.com/html?q=%s" },
147 { "DuckDuckGo (Lite)", "https://duckduckgo.com/lite?q=%s" },
148 { "Ixquick", "https://ixquick.com/do/search?q=%s" },
149 { "Startpage", "https://startpage.com/do/search?q=%s" },
153 * Session IDs.
154 * We use these to prevent people putting xxxt:// URLs on
155 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
157 #define XT_XTP_SES_KEY_SZ 8
158 #define XT_XTP_SES_KEY_HEX_FMT \
159 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8
161 char *dl_session_key; /* downloads */
162 char *hl_session_key; /* history list */
163 char *cl_session_key; /* cookie list */
164 char *fl_session_key; /* favorites list */
165 char *sl_session_key; /* search */
166 char *ab_session_key; /* about */
167 char *sv_session_key; /* secviolation */
169 int updating_ab_tabs = 0;
170 int updating_fl_tabs = 0;
171 int updating_dl_tabs = 0;
172 int updating_hl_tabs = 0;
173 int updating_cl_tabs = 0;
174 int updating_sl_tabs = 0;
175 int updating_sv_tabs = 0;
176 struct download_list downloads;
178 size_t
179 about_list_size(void)
181 return (LENGTH(about_list));
184 gchar *
185 get_html_page(gchar *title, gchar *body, gchar *head, bool addstyles)
187 gchar *r;
189 r = g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
190 "<head>\n"
191 "<title>%s</title>\n"
192 "%s"
193 "%s"
194 "</head>\n"
195 "<body>\n"
196 "<h1>%s</h1>\n"
197 "%s\n</body>\n"
198 "</html>",
199 title,
200 addstyles ? XT_PAGE_STYLE : "",
201 head,
202 title,
203 body);
205 return (r);
209 * Display a web page from a HTML string in memory, rather than from a URL
211 void
212 load_webkit_string(struct tab *t, const char *str, gchar *title)
214 char file[PATH_MAX];
215 int i;
217 /* we set this to indicate we want to manually do navaction */
218 if (t->bfl)
219 t->item = webkit_web_back_forward_list_get_current_item(t->bfl);
221 t->xtp_meaning = XT_XTP_TAB_MEANING_NORMAL;
222 if (title) {
223 /* set t->xtp_meaning */
224 for (i = 0; i < LENGTH(about_list); i++)
225 if (!strcmp(title, about_list[i].name)) {
226 t->xtp_meaning = i;
227 break;
230 webkit_web_view_load_string(t->wv, str, NULL, encoding,
231 "file://");
232 #if GTK_CHECK_VERSION(2, 20, 0)
233 gtk_spinner_stop(GTK_SPINNER(t->spinner));
234 gtk_widget_hide(t->spinner);
235 #endif
236 snprintf(file, sizeof file, "%s" PS "%s", resource_dir, icons[0]);
237 xt_icon_from_file(t, file);
242 blank(struct tab *t, struct karg *args)
244 if (t == NULL)
245 show_oops(NULL, "blank invalid parameters");
247 load_webkit_string(t, "", XT_URI_ABOUT_BLANK);
249 return (0);
253 help(struct tab *t, struct karg *args)
255 char *page, *head, *body;
257 if (t == NULL)
258 show_oops(NULL, "help invalid parameters");
260 head = "<meta http-equiv=\"REFRESH\" content=\"0;"
261 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xombrero\">"
262 "</head>\n";
263 body = "xombrero man page <a href=\"http://opensource.conformal.com/"
264 "cgi-bin/man-cgi?xombrero\">http://opensource.conformal.com/"
265 "cgi-bin/man-cgi?xombrero</a>";
267 page = get_html_page(XT_NAME, body, head, FALSE);
269 load_webkit_string(t, page, XT_URI_ABOUT_HELP);
270 g_free(page);
272 return (0);
276 stats(struct tab *t, struct karg *args)
278 char *page, *body, *s, line[64 * 1024];
279 uint64_t line_count = 0;
280 FILE *r_cookie_f;
282 if (t == NULL)
283 show_oops(NULL, "stats invalid parameters");
285 line[0] = '\0';
286 if (save_rejected_cookies) {
287 if ((r_cookie_f = fopen(rc_fname, "r"))) {
288 for (;;) {
289 s = fgets(line, sizeof line, r_cookie_f);
290 if (s == NULL || feof(r_cookie_f) ||
291 ferror(r_cookie_f))
292 break;
293 line_count++;
295 fclose(r_cookie_f);
296 snprintf(line, sizeof line,
297 "<br/>Cookies blocked(*) total: %" PRIu64,
298 line_count);
299 } else
300 show_oops(t, "Can't open blocked cookies file: %s",
301 strerror(errno));
304 body = g_strdup_printf(
305 "Cookies blocked(*) this session: %" PRIu64
306 "%s"
307 "<p><small><b>*</b> results vary based on settings</small></p>",
308 blocked_cookies,
309 line);
311 page = get_html_page("Statistics", body, "", 0);
312 g_free(body);
314 load_webkit_string(t, page, XT_URI_ABOUT_STATS);
315 g_free(page);
317 return (0);
320 void
321 show_certs(struct tab *t, gnutls_x509_crt_t *certs,
322 size_t cert_count, char *title)
324 gnutls_datum_t cinfo;
325 char *tmp, *body;
326 int i;
328 body = g_strdup("");
330 for (i = 0; i < cert_count; i++) {
331 if (gnutls_x509_crt_print(certs[i], GNUTLS_CRT_PRINT_FULL,
332 &cinfo))
333 return;
335 tmp = body;
336 body = g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
337 body, i, cinfo.data);
338 gnutls_free(cinfo.data);
339 g_free(tmp);
342 tmp = get_html_page(title, body, "", 0);
343 g_free(body);
345 load_webkit_string(t, tmp, XT_URI_ABOUT_CERTS);
346 g_free(tmp);
350 ca_cmd(struct tab *t, struct karg *args)
352 FILE *f = NULL;
353 int rv = 1, certs = 0, certs_read;
354 struct stat sb;
355 gnutls_datum_t dt;
356 gnutls_x509_crt_t *c = NULL;
357 char *certs_buf = NULL, *s;
359 if ((f = fopen(ssl_ca_file, "r")) == NULL) {
360 show_oops(t, "Can't open CA file: %s", ssl_ca_file);
361 return (1);
364 if (fstat(fileno(f), &sb) == -1) {
365 show_oops(t, "Can't stat CA file: %s", ssl_ca_file);
366 goto done;
369 certs_buf = g_malloc(sb.st_size + 1);
370 if (fread(certs_buf, 1, sb.st_size, f) != sb.st_size) {
371 show_oops(t, "Can't read CA file: %s", strerror(errno));
372 goto done;
374 certs_buf[sb.st_size] = '\0';
376 s = certs_buf;
377 while ((s = strstr(s, "BEGIN CERTIFICATE"))) {
378 certs++;
379 s += strlen("BEGIN CERTIFICATE");
382 bzero(&dt, sizeof dt);
383 dt.data = (unsigned char *)certs_buf;
384 dt.size = sb.st_size;
385 c = g_malloc(sizeof(gnutls_x509_crt_t) * certs);
386 certs_read = gnutls_x509_crt_list_import(c, (unsigned int *)&certs, &dt,
387 GNUTLS_X509_FMT_PEM, 0);
388 if (certs_read <= 0) {
389 show_oops(t, "No cert(s) available");
390 goto done;
392 show_certs(t, c, certs_read, "Certificate Authority Certificates");
393 done:
394 if (c)
395 g_free(c);
396 if (certs_buf)
397 g_free(certs_buf);
398 if (f)
399 fclose(f);
401 return (rv);
405 cookie_show_wl(struct tab *t, struct karg *args)
407 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
408 wl_show(t, args, "Cookie White List", &c_wl);
410 return (0);
414 js_show_wl(struct tab *t, struct karg *args)
416 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
417 wl_show(t, args, "JavaScript White List", &js_wl);
419 return (0);
423 cookie_cmd(struct tab *t, struct karg *args)
425 if (args->i & XT_SHOW)
426 wl_show(t, args, "Cookie White List", &c_wl);
427 else if (args->i & XT_WL_TOGGLE) {
428 args->i |= XT_WL_RELOAD;
429 toggle_cwl(t, args);
430 } else if (args->i & XT_SAVE) {
431 args->i |= XT_WL_RELOAD;
432 wl_save(t, args, XT_WL_COOKIE);
433 } else if (args->i & XT_DELETE) {
434 remove_cookie_all();
435 update_cookie_tabs(NULL);
438 return (0);
442 js_cmd(struct tab *t, struct karg *args)
444 if (args->i & XT_SHOW)
445 wl_show(t, args, "JavaScript White List", &js_wl);
446 else if (args->i & XT_SAVE) {
447 args->i |= XT_WL_RELOAD;
448 wl_save(t, args, XT_WL_JAVASCRIPT);
449 } else if (args->i & XT_WL_TOGGLE) {
450 args->i |= XT_WL_RELOAD;
451 toggle_js(t, args);
452 } else if (args->i & XT_DELETE)
453 show_oops(t, "'js delete' currently unimplemented");
455 return (0);
459 pl_show_wl(struct tab *t, struct karg *args)
461 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
462 wl_show(t, args, "Plugin White List", &pl_wl);
464 return (0);
468 pl_cmd(struct tab *t, struct karg *args)
470 if (args->i & XT_SHOW)
471 wl_show(t, args, "Plugin White List", &pl_wl);
472 else if (args->i & XT_SAVE) {
473 args->i |= XT_WL_RELOAD;
474 wl_save(t, args, XT_WL_PLUGIN);
475 } else if (args->i & XT_WL_TOGGLE) {
476 args->i |= XT_WL_RELOAD;
477 toggle_pl(t, args);
478 } else if (args->i & XT_DELETE)
479 show_oops(t, "'plugin delete' currently unimplemented");
481 return (0);
485 * cancel, remove, etc. downloads
487 void
488 xtp_handle_dl(struct tab *t, uint8_t cmd, int id)
490 struct download find, *d = NULL;
491 #ifndef __MINGW32__
492 char *file = NULL;
493 const char *uri = NULL;
494 #endif
496 DNPRINTF(XT_D_DOWNLOAD, "download control: cmd %d, id %d\n", cmd, id);
498 /* some commands require a valid download id */
499 if (cmd != XT_XTP_DL_LIST) {
500 /* lookup download in question */
501 find.id = id;
502 d = RB_FIND(download_list, &downloads, &find);
504 if (d == NULL) {
505 show_oops(t, "%s: no such download", __func__);
506 return;
510 /* decide what to do */
511 switch (cmd) {
512 case XT_XTP_DL_START:
513 /* our downloads always needs to be
514 * restarted if called from here
516 download_start(t, d, XT_DL_RESTART);
517 break;
518 case XT_XTP_DL_CANCEL:
519 webkit_download_cancel(d->download);
520 g_object_unref(d->download);
521 RB_REMOVE(download_list, &downloads, d);
522 break;
523 case XT_XTP_DL_UNLINK:
524 #ifdef __MINGW32__
525 /* XXX uri's aren't handled properly on windows? */
526 unlink(webkit_download_get_destination_uri(d->download));
527 #else
528 uri = webkit_download_get_destination_uri(d->download);
529 if ((file = g_filename_from_uri(uri, NULL, NULL)) != NULL) {
530 unlink(file);
531 g_free(file);
533 #endif
534 /* FALLTHROUGH */
535 case XT_XTP_DL_REMOVE:
536 webkit_download_cancel(d->download); /* just incase */
537 g_object_unref(d->download);
538 RB_REMOVE(download_list, &downloads, d);
539 break;
540 case XT_XTP_DL_LIST:
541 /* Nothing */
542 break;
543 default:
544 show_oops(t, "%s: unknown command", __func__);
545 break;
547 xtp_page_dl(t, NULL);
550 void
551 xtp_handle_hl(struct tab *t, uint8_t cmd, int id)
553 struct history *h, *next, *ht;
554 int i = 1;
556 switch (cmd) {
557 case XT_XTP_HL_REMOVE:
558 /* walk backwards, as listed in reverse */
559 for (h = RB_MAX(history_list, &hl); h != NULL; h = next) {
560 next = RB_PREV(history_list, &hl, h);
561 if (id == i) {
562 RB_REMOVE(history_list, &hl, h);
563 g_free((gpointer) h->title);
564 g_free((gpointer) h->uri);
565 g_free(h);
566 break;
568 i++;
570 break;
571 case XT_XTP_HL_REMOVE_ALL:
572 RB_FOREACH_SAFE(h, history_list, &hl, ht)
573 RB_REMOVE(history_list, &hl, h);
574 break;
575 case XT_XTP_HL_LIST:
576 /* Nothing - just xtp_page_hl() below */
577 break;
578 default:
579 show_oops(t, "%s: unknown command", __func__);
580 break;
583 xtp_page_hl(t, NULL);
586 /* remove a favorite */
587 void
588 remove_favorite(struct tab *t, int index)
590 char file[PATH_MAX], *title, *uri = NULL;
591 char *new_favs, *tmp;
592 FILE *f;
593 int i;
594 size_t len, lineno;
596 /* open favorites */
597 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
599 if ((f = fopen(file, "r")) == NULL) {
600 show_oops(t, "%s: can't open favorites: %s",
601 __func__, strerror(errno));
602 return;
605 /* build a string which will become the new favroites file */
606 new_favs = g_strdup("");
608 for (i = 1;;) {
609 if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
610 if (feof(f) || ferror(f))
611 break;
612 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
613 if (len == 0) {
614 free(title);
615 title = NULL;
616 continue;
619 if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) {
620 if (feof(f) || ferror(f)) {
621 show_oops(t, "%s: can't parse favorites %s",
622 __func__, strerror(errno));
623 goto clean;
627 /* as long as this isn't the one we are deleting add to file */
628 if (i != index) {
629 tmp = new_favs;
630 new_favs = g_strdup_printf("%s%s\n%s\n",
631 new_favs, title, uri);
632 g_free(tmp);
635 free(uri);
636 uri = NULL;
637 free(title);
638 title = NULL;
639 i++;
641 fclose(f);
643 /* write back new favorites file */
644 if ((f = fopen(file, "w")) == NULL) {
645 show_oops(t, "%s: can't open favorites: %s",
646 __func__, strerror(errno));
647 goto clean;
650 if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
651 show_oops(t, "%s: can't fwrite", __func__);
652 fclose(f);
654 clean:
655 if (uri)
656 free(uri);
657 if (title)
658 free(title);
660 g_free(new_favs);
664 add_favorite(struct tab *t, struct karg *args)
666 char file[PATH_MAX];
667 FILE *f;
668 char *line = NULL;
669 size_t urilen, linelen;
670 const gchar *uri, *title;
672 if (t == NULL)
673 return (1);
675 /* don't allow adding of xtp pages to favorites */
676 if (t->xtp_meaning != XT_XTP_TAB_MEANING_NORMAL) {
677 show_oops(t, "%s: can't add xtp pages to favorites", __func__);
678 return (1);
681 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
682 if ((f = fopen(file, "r+")) == NULL) {
683 show_oops(t, "Can't open favorites file: %s", strerror(errno));
684 return (1);
687 title = get_title(t, FALSE);
688 uri = get_uri(t);
690 if (title == NULL || uri == NULL) {
691 show_oops(t, "can't add page to favorites");
692 goto done;
695 urilen = strlen(uri);
697 for (;;) {
698 if ((line = fparseln(f, &linelen, NULL, NULL, 0)) == NULL)
699 if (feof(f) || ferror(f))
700 break;
702 if (linelen == urilen && !strcmp(line, uri))
703 goto done;
705 free(line);
706 line = NULL;
709 fprintf(f, "\n%s\n%s", title, uri);
710 done:
711 if (line)
712 free(line);
713 fclose(f);
715 update_favorite_tabs(NULL);
717 return (0);
720 char *
721 search_engine_add(char *body, const char *name, const char *url, int select)
723 char *b = body;
725 body = g_strdup_printf("%s<tr>"
726 "<td>%s</td>"
727 "<td>%s</td>"
728 "<td style='text-align: center'>"
729 "<a href='%s%d/%s/%d/%d'>[ Select ]</a></td>"
730 "</tr>\n",
731 body,
732 name,
733 url,
734 XT_XTP_STR, XT_XTP_SL, sl_session_key, XT_XTP_SL_SET, select);
735 g_free(b);
736 return (body);
739 void
740 xtp_handle_ab(struct tab *t, uint8_t cmd, int arg)
742 char config[PATH_MAX];
743 char *cmdstr;
744 char **sv;
746 switch (cmd) {
747 case XT_XTP_AB_EDIT_CONF:
748 if (external_editor == NULL || strlen(external_editor) == 0) {
749 show_oops(t, "external_editor is unset");
750 break;
753 snprintf(config, sizeof config, "%s" PS ".%s", pwd->pw_dir,
754 XT_CONF_FILE);
755 sv = g_strsplit(external_editor, "<file>", -1);
756 cmdstr = g_strjoinv(config, sv);
757 g_strfreev(sv);
758 sv = g_strsplit_set(cmdstr, " \t", -1);
760 if (!g_spawn_async(NULL, sv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
761 NULL, NULL))
762 show_oops(t, "%s: could not spawn process", __func__);
764 g_strfreev(sv);
765 g_free(cmdstr);
766 break;
767 default:
768 show_oops(t, "%s, invalid about command", __func__);
769 break;
771 xtp_page_ab(t, NULL);
773 void
774 xtp_handle_fl(struct tab *t, uint8_t cmd, int arg)
776 switch (cmd) {
777 case XT_XTP_FL_LIST:
778 /* nothing, just the below call to xtp_page_fl() */
779 break;
780 case XT_XTP_FL_REMOVE:
781 remove_favorite(t, arg);
782 break;
783 default:
784 show_oops(t, "%s: invalid favorites command", __func__);
785 break;
788 xtp_page_fl(t, NULL);
791 void
792 xtp_handle_cl(struct tab *t, uint8_t cmd, int arg)
794 switch (cmd) {
795 case XT_XTP_CL_LIST:
796 /* nothing, just xtp_page_cl() */
797 break;
798 case XT_XTP_CL_REMOVE:
799 remove_cookie(arg);
800 break;
801 case XT_XTP_CL_REMOVE_DOMAIN:
802 remove_cookie_domain(arg);
803 break;
804 case XT_XTP_CL_REMOVE_ALL:
805 remove_cookie_all();
806 break;
807 default:
808 show_oops(t, "%s: unknown cookie xtp command", __func__);
809 break;
812 xtp_page_cl(t, NULL);
815 void
816 xtp_handle_sl(struct tab *t, uint8_t cmd, int arg)
818 struct stat sb;
819 FILE *f;
820 size_t linelen;
821 int found = 0;
822 const char *search;
823 char file[PATH_MAX];
824 char delim[3] = { '\0', '\0', '\0' };
825 char *line, *lt, *enc_search, *uri;
826 char *contents, *tmp;
827 char **sv;
829 switch (cmd) {
830 case XT_XTP_SL_SET:
831 set_search_string((char *)search_list[arg].url);
832 if (runtime_settings == NULL || strlen(runtime_settings) == 0) {
833 show_oops(t, "could not set search_string in "
834 "runtime");
835 break;
837 snprintf(file, sizeof file, "%s" PS "%s", work_dir,
838 runtime_settings);
839 if (stat(file, &sb) || (f = fopen(file, "r+")) == NULL) {
840 show_oops(t, "could not set search_string in runtime");
841 break;
843 lt = g_strdup_printf("search_string=%s",
844 (char *)search_list[arg].url);
845 contents = g_strdup("");
846 while (!feof(f)) {
847 line = fparseln(f, &linelen, NULL, delim, 0);
848 if (line == NULL || linelen == 0)
849 continue;
850 tmp = contents;
851 if (strstr(line, "search_string=") == NULL)
852 contents = g_strdup_printf("%s%s\n", contents,
853 line);
854 else {
855 found = 1;
856 contents = g_strdup_printf("%s%s\n", contents,
857 lt);
859 g_free(tmp);
860 free(line);
861 line = NULL;
863 if (found == 0) {
864 tmp = contents;
865 contents = g_strdup_printf("%s%s\n", contents, lt);
866 g_free(tmp);
868 if ((f = freopen(file, "w", f)) == NULL)
869 show_oops(t, "could not set search_string in runtime");
870 else {
871 fputs(contents, f);
872 fclose(f);
874 g_free(lt);
875 g_free(contents);
876 break;
877 default:
878 show_oops(t, "%s: unknown search xtp command", __func__);
879 break;
882 search = gtk_entry_get_text(GTK_ENTRY(t->search_entry)); /* static */
883 enc_search = soup_uri_encode(search, XT_RESERVED_CHARS);
884 sv = g_strsplit(search_string, "%s", 2);
885 uri = g_strjoinv(enc_search, sv);
886 load_uri(t, uri);
887 g_free(enc_search);
888 g_strfreev(sv);
889 g_free(uri);
892 void
893 xtp_handle_sv(struct tab *t, uint8_t cmd, int id)
895 SoupURI *soupuri = NULL;
896 struct karg args = {0};
897 struct secviolation find, *sv;
898 struct sv_ignore *svi = NULL;
900 find.xtp_arg = id;
901 if ((sv = RB_FIND(secviolation_list, &svl, &find)) == NULL)
902 return;
904 args.ptr = (void *)sv->t;
905 args.s = sv->uri;
907 switch (cmd) {
908 case XT_XTP_SV_SHOW_CERT:
909 args.i = XT_SHOW;
910 cert_cmd(t, &args);
911 break;
912 case XT_XTP_SV_ALLOW_SESSION:
913 soupuri = soup_uri_new(sv->uri);
914 svi = malloc(sizeof(struct sv_ignore));
915 svi->domain = g_strdup(soupuri->host);
916 RB_INSERT(sv_ignore_list, &svil, svi);
917 load_uri(t, sv->uri);
918 focus_webview(t);
919 break;
920 case XT_XTP_SV_CACHE:
921 args.i = XT_CACHE;
922 cert_cmd(t, &args);
923 load_uri(t, sv->uri);
924 focus_webview(t);
925 break;
926 default:
927 show_oops(t, "%s: invalid secviolation command", __func__);
928 break;
931 g_free(sv->uri);
932 if (soupuri)
933 soup_uri_free(soupuri);
934 RB_REMOVE(secviolation_list, &svl, sv);
937 /* link an XTP class to it's session key and handler function */
938 struct xtp_despatch {
939 uint8_t xtp_class;
940 char **session_key;
941 void (*handle_func)(struct tab *, uint8_t, int);
944 struct xtp_despatch xtp_despatches[] = {
945 { XT_XTP_DL, &dl_session_key, xtp_handle_dl },
946 { XT_XTP_HL, &hl_session_key, xtp_handle_hl },
947 { XT_XTP_FL, &fl_session_key, xtp_handle_fl },
948 { XT_XTP_CL, &cl_session_key, xtp_handle_cl },
949 { XT_XTP_SL, &sl_session_key, xtp_handle_sl },
950 { XT_XTP_AB, &ab_session_key, xtp_handle_ab },
951 { XT_XTP_SV, &sv_session_key, xtp_handle_sv },
952 { XT_XTP_INVALID, NULL, NULL }
956 * generate a session key to secure xtp commands.
957 * pass in a ptr to the key in question and it will
958 * be modified in place.
960 void
961 generate_xtp_session_key(char **key)
963 uint8_t rand_bytes[XT_XTP_SES_KEY_SZ];
965 /* free old key */
966 if (*key)
967 g_free(*key);
969 /* make a new one */
970 arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ);
971 *key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT,
972 rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
973 rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]);
975 DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key);
978 void
979 xtp_generate_keys(void)
981 /* generate session keys for xtp pages */
982 generate_xtp_session_key(&dl_session_key);
983 generate_xtp_session_key(&hl_session_key);
984 generate_xtp_session_key(&cl_session_key);
985 generate_xtp_session_key(&fl_session_key);
986 generate_xtp_session_key(&ab_session_key);
987 generate_xtp_session_key(&sv_session_key);
991 * validate a xtp session key.
992 * return (1) if OK
995 validate_xtp_session_key(struct tab *t, char *trusted, char *untrusted)
997 if (strcmp(trusted, untrusted) != 0) {
998 show_oops(t, "%s: xtp session key mismatch possible spoof",
999 __func__);
1000 return (0);
1003 return (1);
1007 * is the url xtp protocol? (xxxt://)
1008 * if so, parse and despatch correct bahvior
1011 parse_xtp_url(struct tab *t, const char *url)
1013 char *dup = NULL, *p, *last = NULL;
1014 uint8_t n_tokens = 0;
1015 char *tokens[4] = {NULL, NULL, NULL, ""};
1016 struct xtp_despatch *dsp, *dsp_match = NULL;
1017 uint8_t req_class;
1018 int ret = FALSE;
1021 * tokens array meaning:
1022 * tokens[0] = class
1023 * tokens[1] = session key
1024 * tokens[2] = action
1025 * tokens[3] = optional argument
1028 DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, url);
1030 if (strncmp(url, XT_XTP_STR, strlen(XT_XTP_STR)))
1031 goto clean;
1033 dup = g_strdup(url + strlen(XT_XTP_STR));
1035 /* split out the url */
1036 for ((p = strtok_r(dup, "/", &last)); p;
1037 (p = strtok_r(NULL, "/", &last))) {
1038 if (n_tokens < 4)
1039 tokens[n_tokens++] = p;
1042 /* should be atleast three fields 'class/seskey/command/arg' */
1043 if (n_tokens < 3)
1044 goto clean;
1046 dsp = xtp_despatches;
1047 req_class = atoi(tokens[0]);
1048 while (dsp->xtp_class) {
1049 if (dsp->xtp_class == req_class) {
1050 dsp_match = dsp;
1051 break;
1053 dsp++;
1056 /* did we find one atall? */
1057 if (dsp_match == NULL) {
1058 show_oops(t, "%s: no matching xtp despatch found", __func__);
1059 goto clean;
1062 /* check session key and call despatch function */
1063 if (validate_xtp_session_key(t, *(dsp_match->session_key), tokens[1])) {
1064 ret = TRUE; /* all is well, this was a valid xtp request */
1065 dsp_match->handle_func(t, atoi(tokens[2]), atoi(tokens[3]));
1068 clean:
1069 if (dup)
1070 g_free(dup);
1072 return (ret);
1076 * update all favorite tabs apart from one. Pass NULL if
1077 * you want to update all.
1079 void
1080 update_favorite_tabs(struct tab *apart_from)
1082 struct tab *t;
1083 if (!updating_fl_tabs) {
1084 updating_fl_tabs = 1; /* stop infinite recursion */
1085 TAILQ_FOREACH(t, &tabs, entry)
1086 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL)
1087 && (t != apart_from))
1088 xtp_page_fl(t, NULL);
1089 updating_fl_tabs = 0;
1094 * update all download tabs apart from one. Pass NULL if
1095 * you want to update all.
1097 void
1098 update_download_tabs(struct tab *apart_from)
1100 struct tab *t;
1101 if (!updating_dl_tabs) {
1102 updating_dl_tabs = 1; /* stop infinite recursion */
1103 TAILQ_FOREACH(t, &tabs, entry)
1104 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_DL)
1105 && (t != apart_from))
1106 xtp_page_dl(t, NULL);
1107 updating_dl_tabs = 0;
1112 * update all cookie tabs apart from one. Pass NULL if
1113 * you want to update all.
1115 void
1116 update_cookie_tabs(struct tab *apart_from)
1118 struct tab *t;
1119 if (!updating_cl_tabs) {
1120 updating_cl_tabs = 1; /* stop infinite recursion */
1121 TAILQ_FOREACH(t, &tabs, entry)
1122 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL)
1123 && (t != apart_from))
1124 xtp_page_cl(t, NULL);
1125 updating_cl_tabs = 0;
1130 * update all history tabs apart from one. Pass NULL if
1131 * you want to update all.
1133 void
1134 update_history_tabs(struct tab *apart_from)
1136 struct tab *t;
1138 if (!updating_hl_tabs) {
1139 updating_hl_tabs = 1; /* stop infinite recursion */
1140 TAILQ_FOREACH(t, &tabs, entry)
1141 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL)
1142 && (t != apart_from))
1143 xtp_page_hl(t, NULL);
1144 updating_hl_tabs = 0;
1149 * update all search tabs apart from one. Pass NULL if
1150 * you want to update all.
1152 void
1153 update_search_tabs(struct tab *apart_from)
1155 struct tab *t;
1157 if (!updating_sl_tabs) {
1158 updating_sl_tabs = 1; /* stop infinite recursion */
1159 TAILQ_FOREACH(t, &tabs, entry)
1160 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_SL)
1161 && (t != apart_from))
1162 xtp_page_sl(t, NULL);
1163 updating_sl_tabs = 0;
1168 * update all about tabs apart from one. Pass NULL if
1169 * you want to update all.
1171 void
1172 update_about_tabs(struct tab *apart_from)
1174 struct tab *t;
1176 if (!updating_ab_tabs) {
1177 updating_ab_tabs = 1; /* stop infinite recursion */
1178 TAILQ_FOREACH(t, &tabs, entry)
1179 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_AB)
1180 && (t != apart_from))
1181 xtp_page_ab(t, NULL);
1182 updating_ab_tabs = 0;
1187 * update all secviolation tabs apart from one. Pass NULL if
1188 * you want to update all.
1190 void
1191 update_secviolation_tabs(struct tab *apart_from)
1193 struct tab *t;
1195 if (!updating_sv_tabs) {
1196 updating_sv_tabs = 1; /* stop infinite recursion */
1197 TAILQ_FOREACH(t, &tabs, entry)
1198 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_SV)
1199 && (t != apart_from))
1200 xtp_page_sv(t, NULL);
1201 updating_sv_tabs = 0;
1206 xtp_page_ab(struct tab *t, struct karg *args)
1208 char *page, *body;
1210 if (t == NULL)
1211 show_oops(NULL, "about invalid parameters");
1214 * Generate a new session key for next page instance.
1215 * This only happens for the top level call to xtp_page_ab()
1216 * in which case updating_sl_tabs is 0.
1218 if (!updating_ab_tabs)
1219 generate_xtp_session_key(&ab_session_key);
1221 body = g_strdup_printf("<b>Version: %s</b>"
1222 #ifdef XOMBRERO_BUILDSTR
1223 "<br><b>Build: %s</b>"
1224 #endif
1225 "<br><b>WebKit: %d.%d.%d</b>"
1226 "<br><b>User Agent: %d.%d</b>"
1227 #ifdef WEBKITGTK_API_VERSION
1228 "<br><b>WebKit API: %.1f</b>"
1229 #endif
1230 "<br><b>Configuration: %s" PS "<a href='%s%d/%s/%d'>%s</a>"
1231 " (remember to reload after any changes)</b>"
1232 "<p>"
1233 "Authors:"
1234 "<ul>"
1235 "<li>Marco Peereboom &lt;marco@peereboom.us&gt;</li>"
1236 "<li>Stevan Andjelkovic &lt;stevan@student.chalmers.se&gt;</li>"
1237 "<li>Edd Barrett &lt;vext01@gmail.com&gt;</li>"
1238 "<li>Todd T. Fries &lt;todd@fries.net&gt;</li>"
1239 "<li>Raphael Graf &lt;r@undefined.ch&gt;</li>"
1240 "<li>Michal Mazurek &lt;akfaew@jasminek.net&gt;</li>"
1241 "<li>Josh Rickmar &lt;jrick@devio.us&gt;</li>"
1242 "</ul>"
1243 "Copyrights and licenses can be found on the xombrero "
1244 "<a href=\"http://opensource.conformal.com/wiki/xombrero\">website</a>"
1245 "</p>",
1246 #ifdef XOMBRERO_BUILDSTR
1247 version, XOMBRERO_BUILDSTR,
1248 #else
1249 version,
1250 #endif
1251 WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION,
1252 WEBKIT_USER_AGENT_MAJOR_VERSION, WEBKIT_USER_AGENT_MINOR_VERSION
1253 #ifdef WEBKITGTK_API_VERSION
1254 ,WEBKITGTK_API_VERSION
1255 #endif
1256 ,pwd->pw_dir,
1257 XT_XTP_STR,
1258 XT_XTP_AB,
1259 ab_session_key,
1260 XT_XTP_AB_EDIT_CONF,
1261 XT_CONF_FILE
1264 page = get_html_page("About", body, "", 0);
1265 g_free(body);
1267 load_webkit_string(t, page, XT_URI_ABOUT_ABOUT);
1269 update_about_tabs(t);
1271 g_free(page);
1273 return (0);
1276 /* show a list of favorites (bookmarks) */
1278 xtp_page_fl(struct tab *t, struct karg *args)
1280 char file[PATH_MAX];
1281 FILE *f;
1282 char *uri = NULL, *title = NULL;
1283 size_t len, lineno = 0;
1284 int i, failed = 0;
1285 char *body, *tmp, *page = NULL;
1286 const char delim[3] = {'\\', '\\', '\0'};
1288 DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
1290 if (t == NULL)
1291 warn("%s: bad param", __func__);
1293 /* new session key */
1294 if (!updating_fl_tabs)
1295 generate_xtp_session_key(&fl_session_key);
1297 /* open favorites */
1298 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
1299 if ((f = fopen(file, "r")) == NULL) {
1300 show_oops(t, "Can't open favorites file: %s", strerror(errno));
1301 return (1);
1304 /* body */
1305 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
1306 "<th style='width: 40px'>&#35;</th><th>Link</th>"
1307 "<th style='width: 40px'>Rm</th></tr>\n");
1309 for (i = 1;;) {
1310 if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
1311 break;
1312 if (strlen(title) == 0) {
1313 free(title);
1314 title = NULL;
1315 continue;
1318 if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
1319 if (feof(f) || ferror(f)) {
1320 show_oops(t, "favorites file corrupt");
1321 failed = 1;
1322 break;
1325 tmp = body;
1326 body = g_strdup_printf("%s<tr>"
1327 "<td>%d</td>"
1328 "<td><a href='%s'>%s</a></td>"
1329 "<td style='text-align: center'>"
1330 "<a href='%s%d/%s/%d/%d'>X</a></td>"
1331 "</tr>\n",
1332 body, i, uri, title,
1333 XT_XTP_STR, XT_XTP_FL, fl_session_key, XT_XTP_FL_REMOVE, i);
1335 g_free(tmp);
1337 free(uri);
1338 uri = NULL;
1339 free(title);
1340 title = NULL;
1341 i++;
1343 fclose(f);
1345 /* if none, say so */
1346 if (i == 1) {
1347 tmp = body;
1348 body = g_strdup_printf("%s<tr>"
1349 "<td colspan='3' style='text-align: center'>"
1350 "No favorites - To add one use the 'favadd' command."
1351 "</td></tr>", body);
1352 g_free(tmp);
1355 tmp = body;
1356 body = g_strdup_printf("%s</table>", body);
1357 g_free(tmp);
1359 if (uri)
1360 free(uri);
1361 if (title)
1362 free(title);
1364 /* render */
1365 if (!failed) {
1366 page = get_html_page("Favorites", body, "", 1);
1367 load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES);
1368 g_free(page);
1371 update_favorite_tabs(t);
1373 if (body)
1374 g_free(body);
1376 return (failed);
1380 * Return a new string with a download row (in html)
1381 * appended. Old string is freed.
1383 char *
1384 xtp_page_dl_row(struct tab *t, char *html, struct download *dl)
1387 WebKitDownloadStatus stat;
1388 const gchar *destination;
1389 char *status_html = NULL, *cmd_html = NULL, *new_html;
1390 gdouble progress;
1391 char cur_sz[FMT_SCALED_STRSIZE];
1392 char tot_sz[FMT_SCALED_STRSIZE];
1393 char *xtp_prefix;
1395 DNPRINTF(XT_D_DOWNLOAD, "%s: dl->id %d\n", __func__, dl->id);
1397 /* All actions wil take this form:
1398 * xxxt://class/seskey
1400 xtp_prefix = g_strdup_printf("%s%d/%s/",
1401 XT_XTP_STR, XT_XTP_DL, dl_session_key);
1403 stat = webkit_download_get_status(dl->download);
1405 switch (stat) {
1406 case WEBKIT_DOWNLOAD_STATUS_FINISHED:
1407 status_html = g_strdup_printf("Finished");
1408 cmd_html = g_strdup_printf(
1409 "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1410 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1411 XT_XTP_DL_UNLINK, dl->id);
1412 break;
1413 case WEBKIT_DOWNLOAD_STATUS_STARTED:
1414 /* gather size info */
1415 progress = 100 * webkit_download_get_progress(dl->download);
1417 fmt_scaled(
1418 webkit_download_get_current_size(dl->download), cur_sz);
1419 fmt_scaled(
1420 webkit_download_get_total_size(dl->download), tot_sz);
1422 status_html = g_strdup_printf(
1423 "<div style='width: 100%%' align='center'>"
1424 "<div class='progress-outer'>"
1425 "<div class='progress-inner' style='width: %.2f%%'>"
1426 "</div></div></div>"
1427 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
1428 progress, cur_sz, tot_sz, progress);
1430 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1431 xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
1433 break;
1434 /* LLL */
1435 case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
1436 status_html = g_strdup_printf("Cancelled");
1437 cmd_html = g_strdup_printf(
1438 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1439 xtp_prefix, XT_XTP_DL_START, dl->id,
1440 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1441 XT_XTP_DL_UNLINK, dl->id);
1442 break;
1443 case WEBKIT_DOWNLOAD_STATUS_ERROR:
1444 status_html = g_strdup_printf("Error!");
1445 cmd_html = g_strdup_printf(
1446 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1447 xtp_prefix, XT_XTP_DL_START, dl->id,
1448 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1449 XT_XTP_DL_UNLINK, dl->id);
1450 break;
1451 case WEBKIT_DOWNLOAD_STATUS_CREATED:
1452 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Start</a> / <a href='%s%d/%d'>Cancel</a>",
1453 xtp_prefix, XT_XTP_DL_START, dl->id, xtp_prefix,
1454 XT_XTP_DL_CANCEL, dl->id);
1455 status_html = g_strdup_printf("Created");
1456 break;
1457 default:
1458 show_oops(t, "%s: unknown download status", __func__);
1461 destination = webkit_download_get_destination_uri(dl->download);
1462 /* we might not have a destination set yet */
1463 if (!destination)
1464 destination = webkit_download_get_suggested_filename(dl->download);
1465 new_html = g_strdup_printf(
1466 "%s\n<tr><td>%s</td><td>%s</td>"
1467 "<td style='text-align:center'>%s</td></tr>\n",
1468 html, basename((char *)destination),
1469 status_html, cmd_html);
1470 g_free(html);
1472 if (status_html)
1473 g_free(status_html);
1475 if (cmd_html)
1476 g_free(cmd_html);
1478 g_free(xtp_prefix);
1480 return new_html;
1483 /* cookie management XTP page */
1485 xtp_page_cl(struct tab *t, struct karg *args)
1487 char *body, *page, *tmp;
1488 int i = 1; /* all ids start 1 */
1489 int domain_id = 0;
1490 GSList *sc, *pc, *pc_start;
1491 SoupCookie *c;
1492 char *type, *table_headers, *last_domain;
1494 DNPRINTF(XT_D_CMD, "%s", __func__);
1496 if (t == NULL) {
1497 show_oops(NULL, "%s invalid parameters", __func__);
1498 return (1);
1501 /* Generate a new session key */
1502 if (!updating_cl_tabs)
1503 generate_xtp_session_key(&cl_session_key);
1505 /* table headers */
1506 table_headers = g_strdup_printf("<table><tr>"
1507 "<th>Type</th>"
1508 "<th>Name</th>"
1509 "<th style='width:200px'>Value</th>"
1510 "<th>Path</th>"
1511 "<th>Expires</th>"
1512 "<th>Secure</th>"
1513 "<th>HTTP<br />only</th>"
1514 "<th style='width:40px'>Rm</th></tr>\n");
1516 sc = soup_cookie_jar_all_cookies(s_cookiejar);
1517 pc = soup_cookie_jar_all_cookies(p_cookiejar);
1518 pc_start = pc;
1520 body = g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
1521 "[ Remove All Cookies From All Domains ]</a></div>\n",
1522 XT_XTP_STR, XT_XTP_CL, cl_session_key, XT_XTP_CL_REMOVE_ALL);
1524 last_domain = strdup("");
1525 for (; sc; sc = sc->next) {
1526 c = sc->data;
1528 if (strcmp(last_domain, c->domain) != 0) {
1529 /* new domain */
1530 domain_id ++;
1531 free(last_domain);
1532 last_domain = strdup(c->domain);
1534 if (body != NULL) {
1535 tmp = body;
1536 body = g_strdup_printf("%s</table>"
1537 "<h2>%s</h2><div align=\"center\">"
1538 "<a href='%s%d/%s/%d/%d'>"
1539 "[ Remove All From This Domain ]"
1540 "</a></div>%s\n",
1541 body, c->domain,
1542 XT_XTP_STR, XT_XTP_CL, cl_session_key,
1543 XT_XTP_CL_REMOVE_DOMAIN, domain_id,
1544 table_headers);
1545 g_free(tmp);
1546 } else {
1547 /* first domain */
1548 body = g_strdup_printf("<h2>%s</h2>"
1549 "<div align=\"center\">"
1550 "<a href='%s%d/%s/%d/%d'>"
1551 "[ Remove All From This Domain ]</a></div>%s\n",
1552 c->domain, XT_XTP_STR, XT_XTP_CL,
1553 cl_session_key, XT_XTP_CL_REMOVE_DOMAIN,
1554 domain_id, table_headers);
1558 type = "Session";
1559 for (pc = pc_start; pc; pc = pc->next)
1560 if (soup_cookie_equal(pc->data, c)) {
1561 type = "Session + Persistent";
1562 break;
1565 tmp = body;
1566 body = g_strdup_printf(
1567 "%s\n<tr>"
1568 "<td>%s</td>"
1569 "<td style='word-wrap:normal'>%s</td>"
1570 "<td>"
1571 " <textarea rows='4'>%s</textarea>"
1572 "</td>"
1573 "<td>%s</td>"
1574 "<td>%s</td>"
1575 "<td>%d</td>"
1576 "<td>%d</td>"
1577 "<td style='text-align:center'>"
1578 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1579 body,
1580 type,
1581 c->name,
1582 c->value,
1583 c->path,
1584 c->expires ?
1585 soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
1586 c->secure,
1587 c->http_only,
1589 XT_XTP_STR,
1590 XT_XTP_CL,
1591 cl_session_key,
1592 XT_XTP_CL_REMOVE,
1596 g_free(tmp);
1597 i++;
1600 soup_cookies_free(sc);
1601 soup_cookies_free(pc);
1603 /* small message if there are none */
1604 if (i == 1) {
1605 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1606 "colspan='8'>No Cookies</td></tr>\n", table_headers);
1608 tmp = body;
1609 body = g_strdup_printf("%s</table>", body);
1610 g_free(tmp);
1612 page = get_html_page("Cookie Jar", body, "", TRUE);
1613 g_free(body);
1614 g_free(table_headers);
1615 g_free(last_domain);
1617 load_webkit_string(t, page, XT_URI_ABOUT_COOKIEJAR);
1618 update_cookie_tabs(t);
1620 g_free(page);
1622 return (0);
1626 xtp_page_hl(struct tab *t, struct karg *args)
1628 char *body, *page, *tmp;
1629 struct history *h;
1630 int i = 1; /* all ids start 1 */
1632 DNPRINTF(XT_D_CMD, "%s", __func__);
1634 if (t == NULL) {
1635 show_oops(NULL, "%s invalid parameters", __func__);
1636 return (1);
1639 /* Generate a new session key */
1640 if (!updating_hl_tabs)
1641 generate_xtp_session_key(&hl_session_key);
1643 /* body */
1644 body = g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
1645 "[ Remove All ]</a></div>"
1646 "<table style='table-layout:fixed'><tr>"
1647 "<th>URI</th><th>Title</th><th>Last visited</th>"
1648 "<th style='width: 40px'>Rm</th></tr>\n",
1649 XT_XTP_STR, XT_XTP_HL, hl_session_key, XT_XTP_HL_REMOVE_ALL);
1651 RB_FOREACH_REVERSE(h, history_list, &hl) {
1652 tmp = body;
1653 body = g_strdup_printf(
1654 "%s\n<tr>"
1655 "<td><a href='%s'>%s</a></td>"
1656 "<td>%s</td>"
1657 "<td>%s</td>"
1658 "<td style='text-align: center'>"
1659 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1660 body, h->uri, h->uri, h->title, ctime(&h->time),
1661 XT_XTP_STR, XT_XTP_HL, hl_session_key,
1662 XT_XTP_HL_REMOVE, i);
1664 g_free(tmp);
1665 i++;
1668 /* small message if there are none */
1669 if (i == 1) {
1670 tmp = body;
1671 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1672 "colspan='4'>No History</td></tr>\n", body);
1673 g_free(tmp);
1676 tmp = body;
1677 body = g_strdup_printf("%s</table>", body);
1678 g_free(tmp);
1680 page = get_html_page("History", body, "", TRUE);
1681 g_free(body);
1684 * update all history manager tabs as the xtp session
1685 * key has now changed. No need to update the current tab.
1686 * Already did that above.
1688 update_history_tabs(t);
1690 load_webkit_string(t, page, XT_URI_ABOUT_HISTORY);
1691 g_free(page);
1693 return (0);
1697 * Generate a web page detailing the status of any downloads
1700 xtp_page_dl(struct tab *t, struct karg *args)
1702 struct download *dl;
1703 char *body, *page, *tmp;
1704 char *ref;
1705 int n_dl = 1;
1707 DNPRINTF(XT_D_DOWNLOAD, "%s", __func__);
1709 if (t == NULL) {
1710 show_oops(NULL, "%s invalid parameters", __func__);
1711 return (1);
1715 * Generate a new session key for next page instance.
1716 * This only happens for the top level call to xtp_page_dl()
1717 * in which case updating_dl_tabs is 0.
1719 if (!updating_dl_tabs)
1720 generate_xtp_session_key(&dl_session_key);
1722 /* header - with refresh so as to update */
1723 if (refresh_interval >= 1)
1724 ref = g_strdup_printf(
1725 "<meta http-equiv='refresh' content='%u"
1726 ";url=%s%d/%s/%d' />\n",
1727 refresh_interval,
1728 XT_XTP_STR,
1729 XT_XTP_DL,
1730 dl_session_key,
1731 XT_XTP_DL_LIST);
1732 else
1733 ref = g_strdup("");
1735 body = g_strdup_printf("<div align='center'>"
1736 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
1737 "</p><table><tr><th style='width: 60%%'>"
1738 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
1739 XT_XTP_STR, XT_XTP_DL, dl_session_key, XT_XTP_DL_LIST);
1741 RB_FOREACH_REVERSE(dl, download_list, &downloads) {
1742 body = xtp_page_dl_row(t, body, dl);
1743 n_dl++;
1746 /* message if no downloads in list */
1747 if (n_dl == 1) {
1748 tmp = body;
1749 body = g_strdup_printf("%s\n<tr><td colspan='3'"
1750 " style='text-align: center'>"
1751 "No downloads</td></tr>\n", body);
1752 g_free(tmp);
1755 tmp = body;
1756 body = g_strdup_printf("%s</table></div>", body);
1757 g_free(tmp);
1759 page = get_html_page("Downloads", body, ref, 1);
1760 g_free(ref);
1761 g_free(body);
1764 * update all download manager tabs as the xtp session
1765 * key has now changed. No need to update the current tab.
1766 * Already did that above.
1768 update_download_tabs(t);
1770 load_webkit_string(t, page, XT_URI_ABOUT_DOWNLOADS);
1771 g_free(page);
1773 return (0);
1777 xtp_page_sl(struct tab *t, struct karg *args)
1779 int i;
1780 char *page, *body, *tmp;
1782 DNPRINTF(XT_D_SEARCH, "%s", __func__);
1785 * Generate a new session key for next page instance.
1786 * This only happens for the top level call to xtp_page_sl()
1787 * in which case updating_sl_tabs is 0.
1789 if (!updating_sl_tabs)
1790 generate_xtp_session_key(&sl_session_key);
1792 if (t == NULL) {
1793 show_oops(NULL, "%s invalid parameters", __func__);
1794 return (1);
1797 body = g_strdup_printf("<p>The xombrero authors will not choose a "
1798 "default search engine for you. What follows is a list of search "
1799 "engines (in no particular order) you may be interested in. "
1800 "To permanently choose a search engine, click [ Select ] to save "
1801 "<tt>search_string</tt> as a runtime setting, or set "
1802 "<tt>search_string</tt> to the appropriate URL in your xombrero "
1803 "configuration.</p>");
1805 tmp = body;
1806 body = g_strdup_printf("%s\n<table style='table-layout:fixed'><tr>"
1807 "<th style='width: 200px'>Name</th><th>URL</th>"
1808 "<th style='width: 100px'>Select</th></tr>\n", body);
1809 g_free(tmp);
1811 for (i = 0; i < (sizeof search_list / sizeof (struct search_type)); ++i)
1812 body = search_engine_add(body, search_list[i].name,
1813 search_list[i].url, i);
1815 tmp = body;
1816 body = g_strdup_printf("%s</table>", body);
1817 g_free(tmp);
1819 page = get_html_page("Choose a search engine", body, "", 1);
1820 g_free(body);
1823 * update all search tabs as the xtp session key has now changed. No
1824 * need to update the current tab. Already did that above.
1826 update_search_tabs(t);
1828 load_webkit_string(t, page, XT_URI_ABOUT_SEARCH);
1829 g_free(page);
1831 return (0);
1835 xtp_page_sv(struct tab *t, struct karg *args)
1837 SoupURI *soupuri;
1838 static int arg = 0;
1839 struct secviolation find, *sv;
1840 char *page, *body;
1842 if (t == NULL)
1843 show_oops(NULL, "secviolation invalid parameters");
1845 /* Generate a new session key for next page instance.
1846 * This only happens for the top level call to xtp_page_ab()
1847 * in which case updating_sv_tabs = 0.
1849 if (!updating_sv_tabs)
1850 generate_xtp_session_key(&sv_session_key);
1852 if (args == NULL) {
1853 find.xtp_arg = t->xtp_arg;
1854 sv = RB_FIND(secviolation_list, &svl, &find);
1855 if (sv == NULL)
1856 return (-1);
1857 } else {
1858 sv = g_malloc(sizeof(struct secviolation));
1859 sv->xtp_arg = ++arg;
1860 t->xtp_arg = arg;
1861 sv->t = t;
1862 sv->uri = args->s;
1863 RB_INSERT(secviolation_list, &svl, sv);
1866 if (sv->uri == NULL || (soupuri = soup_uri_new(sv->uri)) == NULL)
1867 return (-1);
1869 body = g_strdup_printf(
1870 "The domain of the page you have tried to access, %s, has a "
1871 "different remote certificate then the local cached version from a "
1872 "previous visit. As a security precaution to help prevent against "
1873 "man-in-the-middle attacks, please choose one of the following "
1874 "actions to continue, or disable the <tt>warn_cert_changes</tt> "
1875 "setting in your xombrero configuration."
1876 "<p><b>Choose an action:"
1877 "<br><a href='%s%d/%s/%d/%d'>Show Certificate</a>"
1878 "<br><a href='%s%d/%s/%d/%d'>Allow for this Session</a>"
1879 "<br><a href='%s%d/%s/%d/%d'>Cache new certificate</a>",
1880 soupuri->host,
1881 XT_XTP_STR, XT_XTP_SV, sv_session_key, XT_XTP_SV_SHOW_CERT,
1882 sv->xtp_arg,
1883 XT_XTP_STR, XT_XTP_SV, sv_session_key, XT_XTP_SV_ALLOW_SESSION,
1884 sv->xtp_arg,
1885 XT_XTP_STR, XT_XTP_SV, sv_session_key, XT_XTP_SV_CACHE,
1886 sv->xtp_arg);
1888 page = get_html_page("Security Violation", body, "", 0);
1889 g_free(body);
1891 update_secviolation_tabs(t);
1893 load_webkit_string(t, page, XT_URI_ABOUT_SECVIOLATION);
1895 g_free(page);
1896 if (soupuri)
1897 soup_uri_free(soupuri);
1899 return (0);
1903 startpage(struct tab *t, struct karg *args)
1905 char *page, *body, *b;
1906 struct sp *s;
1908 if (t == NULL)
1909 show_oops(NULL, "startpage invalid parameters");
1911 body = g_strdup_printf("<b>Startup Exception(s):</b><p>");
1913 TAILQ_FOREACH(s, &spl, entry) {
1914 b = body;
1915 body = g_strdup_printf("%s%s<br>", body, s->line);
1916 g_free(b);
1919 page = get_html_page("Startup Exception", body, "", 0);
1920 g_free(body);
1922 load_webkit_string(t, page, XT_URI_ABOUT_STARTPAGE);
1923 g_free(page);
1925 return (0);
1928 void
1929 startpage_add(const char *fmt, ...)
1931 va_list ap;
1932 char *msg;
1933 struct sp *s;
1935 if (fmt == NULL)
1936 return;
1938 va_start(ap, fmt);
1939 if (vasprintf(&msg, fmt, ap) == -1)
1940 errx(1, "startpage_add failed");
1941 va_end(ap);
1943 s = g_malloc0(sizeof *s);
1944 s->line = msg;
1946 TAILQ_INSERT_TAIL(&spl, s, entry);
1949 gchar *
1950 show_g_object_settings(GObject *o, char *str, int recurse)
1952 char *b, *body, *valstr;
1953 guint n_props = 0;
1954 int i;
1955 GParamSpec *pspec;
1956 const gchar *tname;
1957 GValue value;
1958 int typeno;
1959 const gchar *string;
1960 gboolean boolean;
1961 gfloat fp;
1962 gdouble fpd;
1963 gint number;
1964 guint unumber;
1965 int64_t number64;
1966 uint64_t unumber64;
1967 GObject *object;
1968 GParamSpec **proplist;
1969 char *tmpstr, *tmpsettings;
1971 if (!G_IS_OBJECT(o)) {
1972 fprintf(stderr, "%s is not a g_object\n", str);
1973 return g_strdup("");
1975 proplist = g_object_class_list_properties(
1976 G_OBJECT_GET_CLASS(o), &n_props);
1977 body = g_strdup_printf("%s: %3d settings\n", str, n_props);
1978 for (i=0; i < n_props; i++) {
1979 pspec = proplist[i];
1980 tname = G_OBJECT_TYPE_NAME(pspec);
1981 bzero(&value, sizeof value);
1982 valstr = NULL;
1984 if (!(pspec->flags & G_PARAM_READABLE))
1985 valstr = g_strdup_printf("not a readable property");
1986 else {
1987 g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
1988 g_object_get_property(G_OBJECT(o), pspec->name,
1989 &value);
1992 /* based on the type, recurse and display values */
1993 if (valstr == NULL) {
1994 typeno = G_TYPE_FUNDAMENTAL( G_VALUE_TYPE(&value) );
1995 switch ( typeno ) {
1996 case G_TYPE_ENUM:
1997 number = g_value_get_enum(&value);
1998 valstr = g_strdup_printf("%d", number);
1999 break;
2000 case G_TYPE_INT:
2001 number = g_value_get_int(&value);
2002 valstr = g_strdup_printf("%d", number);
2003 break;
2004 case G_TYPE_INT64:
2005 number64 = (int64_t)g_value_get_int64(&value);
2006 valstr = g_strdup_printf("%" PRIo64, number64);
2007 break;
2008 case G_TYPE_UINT:
2009 unumber = g_value_get_uint(&value);
2010 valstr = g_strdup_printf("%d", unumber);
2011 break;
2012 case G_TYPE_UINT64:
2013 unumber64 =
2014 (uint64_t)g_value_get_uint64(&value);
2015 valstr =
2016 g_strdup_printf("%" PRIu64, unumber64);
2017 break;
2018 case G_TYPE_FLAGS:
2019 unumber = g_value_get_flags(&value);
2020 valstr = g_strdup_printf("0x%x", unumber);
2021 break;
2022 case G_TYPE_BOOLEAN:
2023 boolean = g_value_get_boolean(&value);
2024 valstr = g_strdup_printf("%s",
2025 boolean ? "TRUE" : "FALSE");
2026 break;
2027 case G_TYPE_FLOAT:
2028 fp = g_value_get_float(&value);
2029 valstr = g_strdup_printf("%f", fp);
2030 break;
2031 case G_TYPE_DOUBLE:
2032 fpd = g_value_get_double(&value);
2033 valstr = g_strdup_printf("%f", fpd);
2034 break;
2035 case G_TYPE_STRING:
2036 string = g_value_get_string(&value);
2037 valstr = g_strdup_printf("\"%s\"",
2038 string);
2039 break;
2040 case G_TYPE_OBJECT:
2041 object = g_value_get_object(&value);
2042 if (object != NULL) {
2043 if (recurse) {
2044 tmpstr = g_strdup_printf("%s ",
2045 str);
2046 tmpsettings = show_g_object_settings(
2047 object, tmpstr, recurse);
2048 valstr = g_strdup_printf(
2049 "{\n%s%s }\n",
2050 tmpsettings, str);
2051 g_free(tmpstr);
2052 g_free(tmpsettings);
2053 } else {
2054 valstr = g_strdup_printf("<...>");
2056 } else {
2057 valstr = g_strdup_printf("NULL");
2059 break;
2060 default:
2061 valstr = g_strdup_printf(
2062 "type %s unhandled",
2063 tname);
2067 b = body;
2068 body = g_strdup_printf(
2069 "%s%s: %3d: flags=0x%08x, %-13s %s = %s\n",
2070 body, str, i, pspec->flags, tname, pspec->name,
2071 valstr);
2072 g_free(b);
2073 g_free(valstr);
2075 g_free(proplist);
2076 return (body);
2080 about_webkit(struct tab *t, struct karg *arg)
2082 char *page, *body, *settingstr;
2084 settingstr = show_g_object_settings(G_OBJECT(t->settings),
2085 "t->settings", 0);
2086 body = g_strdup_printf("<pre>%s</pre>\n", settingstr);
2087 g_free(settingstr);
2089 page = get_html_page("About Webkit", body, "", 0);
2090 g_free(body);
2092 load_webkit_string(t, page, XT_URI_ABOUT_WEBKIT);
2093 g_free(page);
2095 return (0);
2099 allthethings(struct tab *t, struct karg *arg)
2101 char *page, *body, *b, *settingstr;
2102 extern GtkWidget *main_window;
2104 body = show_g_object_settings(G_OBJECT(t->wv), "t->wv", 1);
2105 b = body;
2106 settingstr = show_g_object_settings(G_OBJECT(t->inspector),
2107 "t->inspector", 1);
2108 body = g_strdup_printf("%s%s", body, settingstr);
2109 g_free(b);
2110 g_free(settingstr);
2111 b = body;
2112 settingstr = show_g_object_settings(G_OBJECT(main_window),
2113 "main_window", 1);
2114 body = g_strdup_printf("%s%s", body, settingstr);
2115 g_free(b);
2116 g_free(settingstr);
2117 b = body;
2118 body = g_strdup_printf("<pre>%scan paste clipboard = %d\n</pre>", body,
2119 webkit_web_view_can_paste_clipboard(t->wv));
2120 g_free(b);
2122 page = get_html_page("About All The Things _o/", body, "", 0);
2123 g_free(body);
2125 load_webkit_string(t, page, XT_URI_ABOUT_ALLTHETHINGS);
2126 g_free(page);
2128 return (0);