Update the manpage date to today
[xombrero.git] / about.c
blobf41d52d3aa06e620a2dd440225fc5b017d34bfb8
1 /*
2 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
4 * Copyright (c) 2010, 2011, 2012 Edd Barrett <vext01@gmail.com>
5 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
6 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
7 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
8 * Copyright (c) 2012 Josh Rickmar <jrick@devio.us>
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 #include <xombrero.h>
26 * xombrero "protocol" (xtp)
27 * We use this for managing stuff like downloads and favorites. They
28 * make magical HTML pages in memory which have xxxt:// links in order
29 * to communicate with xombrero's internals. These links take the format:
30 * xxxt://class/session_key/action/arg
32 * Don't begin xtp class/actions as 0. atoi returns that on error.
34 * Typically we have not put addition of items in this framework, as
35 * adding items is either done via an ex-command or via a keybinding instead.
38 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
39 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
40 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
41 "td{overflow: hidden;" \
42 " padding: 2px 2px 2px 2px;" \
43 " border: 1px solid black;" \
44 " vertical-align:top;" \
45 " word-wrap: break-word}\n" \
46 "tr:hover{background: #ffff99}\n" \
47 "th{background-color: #cccccc;" \
48 " border: 1px solid black}\n" \
49 "table{width: 100%%;" \
50 " border: 1px black solid;" \
51 " border-collapse:collapse}\n" \
52 ".progress-outer{" \
53 "border: 1px solid black;" \
54 " height: 8px;" \
55 " width: 90%%}\n" \
56 ".progress-inner{float: left;" \
57 " height: 8px;" \
58 " background: green}\n" \
59 ".dlstatus{font-size: small;" \
60 " text-align: center}\n" \
61 "table#settings{background-color: #eee;"\
62 " border: 0px;" \
63 " margin: 15px;}\n" \
64 "table#settings td{border: 0px;}\n" \
65 "table#settings th{border: 0px;}\n" \
66 "table#settings tr{" \
67 " background: #f6f6f6;}\n" \
68 "table#settings tr:nth-child(even){" \
69 " background: #eeeeee;}\n" \
70 "table#settings tr#modified{" \
71 " background: #FFFFBA;}\n" \
72 "table#settings tr#modified:nth-child(even){" \
73 " background: #ffffA0;}\n" \
74 "</style>\n"
76 int js_show_wl(struct tab *, struct karg *);
77 int pl_show_wl(struct tab *, struct karg *);
78 int https_show_wl(struct tab *, struct karg *);
79 int xtp_page_set(struct tab *, struct karg *);
80 int xtp_page_rt(struct tab *, struct karg *);
81 int marco(struct tab *, struct karg *);
82 int startpage(struct tab *, struct karg *);
83 const char * marco_message(int *);
84 void update_cookie_tabs(struct tab *apart_from);
85 int about_webkit(struct tab *, struct karg *);
86 int allthethings(struct tab *, struct karg *);
89 * If you change the index of any of these, correct the
90 * XT_XTP_TAB_MEANING_* macros in xombrero.h!
92 struct about_type about_list[] = {
93 { XT_URI_ABOUT_ABOUT, xtp_page_ab },
94 { XT_URI_ABOUT_ALLTHETHINGS, allthethings },
95 { XT_URI_ABOUT_BLANK, blank },
96 { XT_URI_ABOUT_CERTS, ca_cmd },
97 { XT_URI_ABOUT_COOKIEWL, cookie_show_wl },
98 { XT_URI_ABOUT_COOKIEJAR, xtp_page_cl },
99 { XT_URI_ABOUT_DOWNLOADS, xtp_page_dl },
100 { XT_URI_ABOUT_FAVORITES, xtp_page_fl },
101 { XT_URI_ABOUT_HELP, help },
102 { XT_URI_ABOUT_HISTORY, xtp_page_hl },
103 { XT_URI_ABOUT_JSWL, js_show_wl },
104 { XT_URI_ABOUT_SET, xtp_page_set },
105 { XT_URI_ABOUT_STATS, stats },
106 { XT_URI_ABOUT_MARCO, marco },
107 { XT_URI_ABOUT_STARTPAGE, startpage },
108 { XT_URI_ABOUT_PLUGINWL, pl_show_wl },
109 { XT_URI_ABOUT_HTTPS, https_show_wl },
110 { XT_URI_ABOUT_WEBKIT, about_webkit },
111 { XT_URI_ABOUT_SEARCH, xtp_page_sl },
112 { XT_URI_ABOUT_RUNTIME, xtp_page_rt },
113 { XT_URI_ABOUT_SECVIOLATION, NULL },
116 struct search_type {
117 const char *name;
118 const char *url;
119 } search_list[] = {
120 { "Google (SSL)", "https://encrypted.google.com/search?q=%s&&client=xombrero" },
121 { "Bing", "http://www.bing.com/search?q=%s" },
122 { "Yahoo", "http://search.yahoo.com/search?p=%s" },
123 { "DuckDuckGo", "https://duckduckgo.com/?q=%s" },
124 { "DuckDuckGo (HTML)", "https://duckduckgo.com/html?q=%s" },
125 { "DuckDuckGo (Lite)", "https://duckduckgo.com/lite?q=%s" },
126 { "Ixquick", "https://ixquick.com/do/search?q=%s" },
127 { "Startpage", "https://startpage.com/do/search?q=%s" },
131 * Session IDs.
132 * We use these to prevent people putting xxxt:// URLs on
133 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
135 #define XT_XTP_SES_KEY_SZ 8
136 #define XT_XTP_SES_KEY_HEX_FMT \
137 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8
139 int updating_fl_tabs = 0;
140 int updating_dl_tabs = 0;
141 int updating_hl_tabs = 0;
142 int updating_cl_tabs = 0;
143 int updating_sl_tabs = 0;
144 int updating_sv_tabs = 0;
145 int updating_set_tabs = 0;
146 struct download_list downloads;
148 size_t
149 about_list_size(void)
151 return (LENGTH(about_list));
154 gchar *
155 get_html_page(gchar *title, gchar *body, gchar *head, bool addstyles)
157 gchar *r;
159 r = g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
160 "<head>\n"
161 "<title>%s</title>\n"
162 "%s"
163 "%s"
164 "</head>\n"
165 "<body>\n"
166 "<h1>%s</h1>\n"
167 "%s\n</body>\n"
168 "</html>",
169 title,
170 addstyles ? XT_PAGE_STYLE : "",
171 head,
172 title,
173 body);
175 return (r);
179 * Display a web page from a HTML string in memory, rather than from a URL
181 void
182 load_webkit_string(struct tab *t, const char *str, gchar *title)
184 char file[PATH_MAX];
185 int i;
187 if (g_signal_handler_is_connected(t->wv, t->progress_handle))
188 g_signal_handler_disconnect(t->wv, t->progress_handle);
190 /* we set this to indicate we want to manually do navaction */
191 if (t->bfl) {
192 if (t->item)
193 g_object_unref(t->item);
194 t->item = webkit_web_back_forward_list_get_current_item(t->bfl);
195 if (t->item)
196 g_object_ref(t->item);
199 t->xtp_meaning = XT_XTP_TAB_MEANING_NORMAL;
200 if (title) {
201 /* set t->xtp_meaning */
202 for (i = 0; i < LENGTH(about_list); i++)
203 if (!strcmp(title, about_list[i].name)) {
204 t->xtp_meaning = i;
205 break;
208 webkit_web_view_load_string(t->wv, str, NULL, encoding,
209 XT_XTP_STR);
210 #if GTK_CHECK_VERSION(2, 20, 0)
211 gtk_spinner_stop(GTK_SPINNER(t->spinner));
212 gtk_widget_hide(t->spinner);
213 #endif
214 snprintf(file, sizeof file, "%s" PS "%s", resource_dir, icons[0]);
215 xt_icon_from_file(t, file);
218 if (t->xtp_meaning == XT_XTP_TAB_MEANING_NORMAL &&
219 t->session_key != NULL) {
220 g_free(t->session_key);
221 t->session_key = NULL;
224 t->progress_handle = g_signal_connect(t->wv,
225 "notify::progress", G_CALLBACK(webview_progress_changed_cb), t);
229 blank(struct tab *t, struct karg *args)
231 if (t == NULL)
232 show_oops(NULL, "blank invalid parameters");
234 load_webkit_string(t, "", XT_URI_ABOUT_BLANK);
236 return (0);
240 help(struct tab *t, struct karg *args)
242 char *page, *head, *body;
244 if (t == NULL)
245 show_oops(NULL, "help invalid parameters");
247 head = "<meta http-equiv=\"REFRESH\" content=\"0;"
248 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xombrero\">"
249 "</head>\n";
250 body = "xombrero man page <a href=\"http://opensource.conformal.com/"
251 "cgi-bin/man-cgi?xombrero\">http://opensource.conformal.com/"
252 "cgi-bin/man-cgi?xombrero</a>";
254 page = get_html_page(XT_NAME, body, head, FALSE);
256 load_webkit_string(t, page, XT_URI_ABOUT_HELP);
257 g_free(page);
259 return (0);
263 stats(struct tab *t, struct karg *args)
265 char *page, *body, *s, line[64 * 1024];
266 uint64_t line_count = 0;
267 FILE *r_cookie_f;
269 if (t == NULL)
270 show_oops(NULL, "stats invalid parameters");
272 line[0] = '\0';
273 if (save_rejected_cookies) {
274 if ((r_cookie_f = fopen(rc_fname, "r"))) {
275 for (;;) {
276 s = fgets(line, sizeof line, r_cookie_f);
277 if (s == NULL || feof(r_cookie_f) ||
278 ferror(r_cookie_f))
279 break;
280 line_count++;
282 fclose(r_cookie_f);
283 snprintf(line, sizeof line,
284 "<br/>Cookies blocked(*) total: %" PRIu64,
285 line_count);
286 } else
287 show_oops(t, "Can't open blocked cookies file: %s",
288 strerror(errno));
291 body = g_strdup_printf(
292 "Cookies blocked(*) this session: %" PRIu64
293 "%s"
294 "<p><small><b>*</b> results vary based on settings</small></p>",
295 blocked_cookies,
296 line);
298 page = get_html_page("Statistics", body, "", 0);
299 g_free(body);
301 load_webkit_string(t, page, XT_URI_ABOUT_STATS);
302 g_free(page);
304 return (0);
307 void
308 show_certs(struct tab *t, gnutls_x509_crt_t *certs,
309 size_t cert_count, char *title)
311 gnutls_datum_t cinfo;
312 char *tmp, *body;
313 int i;
315 body = g_strdup("");
317 for (i = 0; i < cert_count; i++) {
318 if (gnutls_x509_crt_print(certs[i], GNUTLS_CRT_PRINT_FULL,
319 &cinfo))
320 return;
322 tmp = body;
323 body = g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
324 body, i, cinfo.data);
325 gnutls_free(cinfo.data);
326 g_free(tmp);
329 tmp = get_html_page(title, body, "", 0);
330 g_free(body);
332 load_webkit_string(t, tmp, XT_URI_ABOUT_CERTS);
333 g_free(tmp);
337 ca_cmd(struct tab *t, struct karg *args)
339 FILE *f = NULL;
340 int rv = 1, certs = 0, certs_read;
341 struct stat sb;
342 gnutls_datum_t dt;
343 gnutls_x509_crt_t *c = NULL;
344 char *certs_buf = NULL, *s;
346 if ((f = fopen(ssl_ca_file, "r")) == NULL) {
347 show_oops(t, "Can't open CA file: %s", ssl_ca_file);
348 return (1);
351 if (fstat(fileno(f), &sb) == -1) {
352 show_oops(t, "Can't stat CA file: %s", ssl_ca_file);
353 goto done;
356 certs_buf = g_malloc(sb.st_size + 1);
357 if (fread(certs_buf, 1, sb.st_size, f) != sb.st_size) {
358 show_oops(t, "Can't read CA file: %s", strerror(errno));
359 goto done;
361 certs_buf[sb.st_size] = '\0';
363 s = certs_buf;
364 while ((s = strstr(s, "BEGIN CERTIFICATE"))) {
365 certs++;
366 s += strlen("BEGIN CERTIFICATE");
369 bzero(&dt, sizeof dt);
370 dt.data = (unsigned char *)certs_buf;
371 dt.size = sb.st_size;
372 c = g_malloc(sizeof(gnutls_x509_crt_t) * certs);
373 certs_read = gnutls_x509_crt_list_import(c, (unsigned int *)&certs, &dt,
374 GNUTLS_X509_FMT_PEM, 0);
375 if (certs_read <= 0) {
376 show_oops(t, "No cert(s) available");
377 goto done;
379 show_certs(t, c, certs_read, "Certificate Authority Certificates");
380 done:
381 if (c)
382 g_free(c);
383 if (certs_buf)
384 g_free(certs_buf);
385 if (f)
386 fclose(f);
388 return (rv);
392 cookie_show_wl(struct tab *t, struct karg *args)
394 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
395 wl_show(t, args, "Cookie White List", &c_wl);
397 return (0);
401 js_show_wl(struct tab *t, struct karg *args)
403 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
404 wl_show(t, args, "JavaScript White List", &js_wl);
406 return (0);
410 cookie_cmd(struct tab *t, struct karg *args)
412 if (args->i & XT_SHOW)
413 wl_show(t, args, "Cookie White List", &c_wl);
414 else if (args->i & XT_WL_TOGGLE) {
415 args->i |= XT_WL_RELOAD;
416 toggle_cwl(t, args);
417 } else if (args->i & XT_SAVE) {
418 args->i |= XT_WL_RELOAD;
419 wl_save(t, args, XT_WL_COOKIE);
420 } else if (args->i & XT_DELETE) {
421 remove_cookie_all();
422 update_cookie_tabs(NULL);
425 return (0);
429 js_cmd(struct tab *t, struct karg *args)
431 if (args->i & XT_SHOW)
432 wl_show(t, args, "JavaScript White List", &js_wl);
433 else if (args->i & XT_SAVE) {
434 args->i |= XT_WL_RELOAD;
435 wl_save(t, args, XT_WL_JAVASCRIPT);
436 } else if (args->i & XT_WL_TOGGLE) {
437 args->i |= XT_WL_RELOAD;
438 toggle_js(t, args);
439 } else if (args->i & XT_DELETE)
440 show_oops(t, "'js delete' currently unimplemented");
442 return (0);
446 pl_show_wl(struct tab *t, struct karg *args)
448 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
449 wl_show(t, args, "Plugin White List", &pl_wl);
451 return (0);
455 pl_cmd(struct tab *t, struct karg *args)
457 if (args->i & XT_SHOW)
458 wl_show(t, args, "Plugin White List", &pl_wl);
459 else if (args->i & XT_SAVE) {
460 args->i |= XT_WL_RELOAD;
461 wl_save(t, args, XT_WL_PLUGIN);
462 } else if (args->i & XT_WL_TOGGLE) {
463 args->i |= XT_WL_RELOAD;
464 toggle_pl(t, args);
465 } else if (args->i & XT_DELETE)
466 show_oops(t, "'plugin delete' currently unimplemented");
468 return (0);
472 https_show_wl(struct tab *t, struct karg *args)
474 args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
475 wl_show(t, args, "HTTPS Force List", &force_https);
477 return (0);
481 https_cmd(struct tab *t, struct karg *args)
483 if (args->i & XT_SHOW)
484 wl_show(t, args, "HTTPS Force List", &force_https);
485 else if (args->i & XT_SAVE) {
486 args->i |= XT_WL_RELOAD;
487 wl_save(t, args, XT_WL_HTTPS);
488 } else if (args->i & XT_WL_TOGGLE) {
489 args->i |= XT_WL_RELOAD;
490 toggle_force_https(t, args);
491 } else if (args->i & XT_DELETE)
492 show_oops(t, "https 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, const char *query)
503 struct download find, *d = NULL;
504 #ifndef __MINGW32__
505 char *file = NULL;
506 const char *uri = NULL;
507 #endif
509 DNPRINTF(XT_D_DOWNLOAD, "download control: cmd %d, id %d\n", cmd, id);
511 /* some commands require a valid download id */
512 if (cmd != XT_XTP_DL_LIST) {
513 /* lookup download in question */
514 find.id = id;
515 d = RB_FIND(download_list, &downloads, &find);
517 if (d == NULL) {
518 show_oops(t, "%s: no such download", __func__);
519 return;
523 /* decide what to do */
524 switch (cmd) {
525 case XT_XTP_DL_START:
526 /* our downloads always needs to be
527 * restarted if called from here
529 download_start(t, d, XT_DL_RESTART);
530 break;
531 case XT_XTP_DL_CANCEL:
532 webkit_download_cancel(d->download);
533 g_object_unref(d->download);
534 RB_REMOVE(download_list, &downloads, d);
535 break;
536 case XT_XTP_DL_UNLINK:
537 #ifdef __MINGW32__
538 /* XXX uri's aren't handled properly on windows? */
539 unlink(webkit_download_get_destination_uri(d->download));
540 #else
541 uri = webkit_download_get_destination_uri(d->download);
542 if ((file = g_filename_from_uri(uri, NULL, NULL)) != NULL) {
543 unlink(file);
544 g_free(file);
546 #endif
547 /* FALLTHROUGH */
548 case XT_XTP_DL_REMOVE:
549 webkit_download_cancel(d->download); /* just incase */
550 g_object_unref(d->download);
551 RB_REMOVE(download_list, &downloads, d);
552 break;
553 case XT_XTP_DL_LIST:
554 /* Nothing */
555 break;
556 default:
557 show_oops(t, "%s: unknown command", __func__);
558 break;
560 xtp_page_dl(t, NULL);
563 void
564 xtp_handle_hl(struct tab *t, uint8_t cmd, int id, const char *query)
566 struct history *h, *next, *ht;
567 int i = 1;
569 switch (cmd) {
570 case XT_XTP_HL_REMOVE:
571 /* walk backwards, as listed in reverse */
572 for (h = RB_MAX(history_list, &hl); h != NULL; h = next) {
573 next = RB_PREV(history_list, &hl, h);
574 if (id == i) {
575 RB_REMOVE(history_list, &hl, h);
576 g_free((gpointer) h->title);
577 g_free((gpointer) h->uri);
578 g_free(h);
579 break;
581 i++;
583 break;
584 case XT_XTP_HL_REMOVE_ALL:
585 RB_FOREACH_SAFE(h, history_list, &hl, ht)
586 RB_REMOVE(history_list, &hl, h);
587 break;
588 case XT_XTP_HL_LIST:
589 /* Nothing - just xtp_page_hl() below */
590 break;
591 default:
592 show_oops(t, "%s: unknown command", __func__);
593 break;
596 xtp_page_hl(t, NULL);
599 /* remove a favorite */
600 void
601 remove_favorite(struct tab *t, int index)
603 char file[PATH_MAX], *title, *uri = NULL;
604 char *new_favs, *tmp;
605 FILE *f;
606 int i;
607 size_t len, lineno;
609 /* open favorites */
610 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
612 if ((f = fopen(file, "r")) == NULL) {
613 show_oops(t, "%s: can't open favorites: %s",
614 __func__, strerror(errno));
615 return;
618 /* build a string which will become the new favroites file */
619 new_favs = g_strdup("");
621 for (i = 1;;) {
622 if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
623 if (feof(f) || ferror(f))
624 break;
625 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
626 if (len == 0) {
627 free(title);
628 title = NULL;
629 continue;
632 if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) {
633 if (feof(f) || ferror(f)) {
634 show_oops(t, "%s: can't parse favorites %s",
635 __func__, strerror(errno));
636 goto clean;
640 /* as long as this isn't the one we are deleting add to file */
641 if (i != index) {
642 tmp = new_favs;
643 new_favs = g_strdup_printf("%s%s\n%s\n",
644 new_favs, title, uri);
645 g_free(tmp);
648 free(uri);
649 uri = NULL;
650 free(title);
651 title = NULL;
652 i++;
654 fclose(f);
656 /* write back new favorites file */
657 if ((f = fopen(file, "w")) == NULL) {
658 show_oops(t, "%s: can't open favorites: %s",
659 __func__, strerror(errno));
660 goto clean;
663 if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
664 show_oops(t, "%s: can't fwrite", __func__);
665 fclose(f);
667 clean:
668 if (uri)
669 free(uri);
670 if (title)
671 free(title);
673 g_free(new_favs);
677 add_favorite(struct tab *t, struct karg *args)
679 char file[PATH_MAX];
680 FILE *f;
681 char *line = NULL;
682 size_t urilen, linelen;
683 const gchar *uri, *title;
685 if (t == NULL)
686 return (1);
688 /* don't allow adding of xtp pages to favorites */
689 if (t->xtp_meaning != XT_XTP_TAB_MEANING_NORMAL) {
690 show_oops(t, "%s: can't add xtp pages to favorites", __func__);
691 return (1);
694 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
695 if ((f = fopen(file, "r+")) == NULL) {
696 show_oops(t, "Can't open favorites file: %s", strerror(errno));
697 return (1);
700 title = get_title(t, FALSE);
701 uri = get_uri(t);
703 if (title == NULL || uri == NULL) {
704 show_oops(t, "can't add page to favorites");
705 goto done;
708 urilen = strlen(uri);
710 for (;;) {
711 if ((line = fparseln(f, &linelen, NULL, NULL, 0)) == NULL)
712 if (feof(f) || ferror(f))
713 break;
715 if (linelen == urilen && !strcmp(line, uri))
716 goto done;
718 free(line);
719 line = NULL;
722 fprintf(f, "\n%s\n%s", title, uri);
723 done:
724 if (line)
725 free(line);
726 fclose(f);
728 update_favorite_tabs(NULL);
730 return (0);
733 char *
734 search_engine_add(char *body, const char *name, const char *url,
735 const char *key, int select)
737 char *b = body;
739 body = g_strdup_printf("%s<tr>"
740 "<td>%s</td>"
741 "<td>%s</td>"
742 "<td style='text-align: center'>"
743 "<a href='%s%d/%s/%d/%d'>[ Select ]</a></td>"
744 "</tr>\n",
745 body,
746 name,
747 url,
748 XT_XTP_STR, XT_XTP_SL, key, XT_XTP_SL_SET, select);
749 g_free(b);
750 return (body);
753 void
754 xtp_handle_ab(struct tab *t, uint8_t cmd, int arg, const char *query)
756 char config[PATH_MAX];
757 char *cmdstr;
758 char **sv;
760 switch (cmd) {
761 case XT_XTP_AB_EDIT_CONF:
762 if (external_editor == NULL || strlen(external_editor) == 0) {
763 show_oops(t, "external_editor is unset");
764 break;
767 snprintf(config, sizeof config, "%s" PS ".%s", pwd->pw_dir,
768 XT_CONF_FILE);
769 sv = g_strsplit(external_editor, "<file>", -1);
770 cmdstr = g_strjoinv(config, sv);
771 g_strfreev(sv);
772 sv = g_strsplit_set(cmdstr, " \t", -1);
774 if (!g_spawn_async(NULL, sv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
775 NULL, NULL))
776 show_oops(t, "%s: could not spawn process", __func__);
778 g_strfreev(sv);
779 g_free(cmdstr);
780 break;
781 default:
782 show_oops(t, "%s, invalid about command", __func__);
783 break;
785 xtp_page_ab(t, NULL);
787 void
788 xtp_handle_fl(struct tab *t, uint8_t cmd, int arg, const char *query)
790 switch (cmd) {
791 case XT_XTP_FL_LIST:
792 /* nothing, just the below call to xtp_page_fl() */
793 break;
794 case XT_XTP_FL_REMOVE:
795 remove_favorite(t, arg);
796 break;
797 default:
798 show_oops(t, "%s: invalid favorites command", __func__);
799 break;
802 xtp_page_fl(t, NULL);
805 void
806 xtp_handle_cl(struct tab *t, uint8_t cmd, int arg, const char *query)
808 switch (cmd) {
809 case XT_XTP_CL_LIST:
810 /* nothing, just xtp_page_cl() */
811 break;
812 case XT_XTP_CL_REMOVE:
813 remove_cookie(arg);
814 break;
815 case XT_XTP_CL_REMOVE_DOMAIN:
816 remove_cookie_domain(arg);
817 break;
818 case XT_XTP_CL_REMOVE_ALL:
819 remove_cookie_all();
820 break;
821 default:
822 show_oops(t, "%s: unknown cookie xtp command", __func__);
823 break;
826 xtp_page_cl(t, NULL);
829 void
830 xtp_handle_sl(struct tab *t, uint8_t cmd, int arg, const char *query)
832 const char *search;
833 char *enc_search, *uri;
834 char **sv;
836 switch (cmd) {
837 case XT_XTP_SL_SET:
838 set_search_string((char *)search_list[arg].url);
839 if (save_runtime_setting("search_string", search_list[arg].url))
840 show_oops(t, "could not set search_string in runtime");
841 break;
842 default:
843 show_oops(t, "%s: unknown search xtp command", __func__);
844 break;
847 search = gtk_entry_get_text(GTK_ENTRY(t->search_entry)); /* static */
848 enc_search = soup_uri_encode(search, XT_RESERVED_CHARS);
849 sv = g_strsplit(search_string, "%s", 2);
850 uri = g_strjoinv(enc_search, sv);
851 load_uri(t, uri);
852 g_free(enc_search);
853 g_strfreev(sv);
854 g_free(uri);
857 void
858 xtp_handle_sv(struct tab *t, uint8_t cmd, int id, const char *query)
860 SoupURI *soupuri = NULL;
861 struct karg args = {0};
862 struct secviolation find, *sv;
864 find.xtp_arg = id;
865 if ((sv = RB_FIND(secviolation_list, &svl, &find)) == NULL)
866 return;
868 args.ptr = (void *)sv->t;
869 args.s = sv->uri;
871 switch (cmd) {
872 case XT_XTP_SV_SHOW_NEW_CERT:
873 args.i = XT_SHOW;
874 if (cert_cmd(t, &args)) {
875 xtp_page_sv(t, &args);
876 return;
878 break;
879 case XT_XTP_SV_SHOW_CACHED_CERT:
880 args.i = XT_CACHE | XT_SHOW;
881 if (cert_cmd(t, &args)) {
882 xtp_page_sv(t, &args);
883 return;
885 break;
886 case XT_XTP_SV_ALLOW_SESSION:
887 soupuri = soup_uri_new(sv->uri);
888 wl_add(soupuri->host, &svil, 0);
889 load_uri(t, sv->uri);
890 focus_webview(t);
891 break;
892 case XT_XTP_SV_CACHE:
893 args.i = XT_CACHE;
894 if (cert_cmd(t, &args)) {
895 xtp_page_sv(t, &args);
896 return;
898 load_uri(t, sv->uri);
899 focus_webview(t);
900 break;
901 default:
902 show_oops(t, "%s: invalid secviolation command", __func__);
903 break;
906 g_free(sv->uri);
907 if (soupuri)
908 soup_uri_free(soupuri);
909 RB_REMOVE(secviolation_list, &svl, sv);
912 void
913 xtp_handle_rt(struct tab *t, uint8_t cmd, int id, const char *query)
915 struct set_reject *sr;
916 GHashTable *new_settings = NULL;
917 int modify;
918 char *val, *curval, *s;
919 int i = 0;
921 switch (cmd) {
922 case XT_XTP_RT_SAVE:
923 if (query == NULL)
924 break;
925 new_settings = soup_form_decode(query);
926 for (i = 0; i < get_settings_size(); ++i) {
927 if (!rs[i].activate)
928 continue;
929 sr = g_malloc(sizeof *sr);
930 val = (char *)g_hash_table_lookup(new_settings,
931 rs[i].name);
932 modify = 0;
933 switch (rs[i].type) {
934 case XT_S_INT: /* FALLTHROUGH */
935 case XT_S_BOOL:
936 if (atoi(val) != *rs[i].ival)
937 modify = 1;
938 break;
939 case XT_S_FLOAT:
940 if (atof(val) != *rs[i].fval)
941 modify = 1;
942 break;
943 case XT_S_STR:
944 s = (rs[i].sval == NULL || *rs[i].sval == NULL)
945 ? "" : *rs[i].sval;
946 if (rs[i].sval && g_strcmp0(val, s))
947 modify = 1;
948 else if (rs[i].s && rs[i].s->get) {
949 curval = rs[i].s->get(NULL);
950 if (g_strcmp0(val, curval))
951 modify = 1;
952 g_free(curval);
954 break;
955 case XT_S_INVALID: /* FALLTHROUGH */
956 default:
957 break;
959 if (rs[i].activate(val)) {
960 sr->name = g_strdup(rs[i].name);
961 sr->value = g_strdup(val);
962 TAILQ_INSERT_TAIL(&srl, sr, entry);
963 continue;
965 if (modify)
966 if (save_runtime_setting(rs[i].name, val))
967 show_oops(t, "error");
969 break;
970 default:
971 show_oops(t, "%s: invalid set command", __func__);
972 break;
975 if (new_settings)
976 g_hash_table_destroy(new_settings);
978 xtp_page_rt(t, NULL);
981 /* link an XTP class to it's session key and handler function */
982 struct xtp_despatch {
983 uint8_t xtp_class;
984 void (*handle_func)(struct tab *, uint8_t, int,
985 const char *query);
988 struct xtp_despatch xtp_despatches[] = {
989 { XT_XTP_DL, xtp_handle_dl },
990 { XT_XTP_HL, xtp_handle_hl },
991 { XT_XTP_FL, xtp_handle_fl },
992 { XT_XTP_CL, xtp_handle_cl },
993 { XT_XTP_SL, xtp_handle_sl },
994 { XT_XTP_AB, xtp_handle_ab },
995 { XT_XTP_SV, xtp_handle_sv },
996 { XT_XTP_RT, xtp_handle_rt },
997 { XT_XTP_INVALID, NULL }
1001 * generate a session key to secure xtp commands.
1002 * pass in a ptr to the key in question and it will
1003 * be modified in place.
1005 void
1006 generate_xtp_session_key(char **key)
1008 uint8_t rand_bytes[XT_XTP_SES_KEY_SZ];
1010 /* free old key */
1011 if (*key)
1012 g_free(*key);
1014 /* make a new one */
1015 arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ);
1016 *key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT,
1017 rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
1018 rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]);
1020 DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key);
1024 * validate a xtp session key.
1025 * return (1) if OK
1028 validate_xtp_session_key(struct tab *t, char *key)
1030 if (t == NULL || t->session_key == NULL || key == NULL)
1031 return (0);
1033 if (strcmp(t->session_key, key) != 0) {
1034 show_oops(t, "%s: xtp session key mismatch possible spoof",
1035 __func__);
1036 return (0);
1039 return (1);
1043 * is the url xtp protocol? (xxxt://)
1044 * if so, parse and despatch correct bahvior
1047 parse_xtp_url(struct tab *t, const char *uri_str)
1049 SoupURI *uri = NULL;
1050 struct xtp_despatch *dsp, *dsp_match = NULL;
1051 int ret = FALSE;
1052 int class = 0;
1053 char **sv = NULL;
1056 * uri->host = class
1057 * sv[0] = session key
1058 * sv[1] = command
1059 * sv[2] = optional argument
1062 DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, uri_str);
1064 if ((uri = soup_uri_new(uri_str)) == NULL)
1065 goto clean;
1066 if (strncmp(uri->scheme, XT_XTP_SCHEME, strlen(XT_XTP_SCHEME)))
1067 goto clean;
1068 if (uri->host == NULL || strlen(uri->host) == 0)
1069 goto clean;
1070 else
1071 class = atoi(uri->host);
1072 if ((sv = g_strsplit(uri->path + 1, "/", 3)) == NULL)
1073 goto clean;
1075 if (sv[0] == NULL || sv[1] == NULL)
1076 goto clean;
1078 dsp = xtp_despatches;
1079 class = atoi(uri->host);
1080 while (dsp->xtp_class) {
1081 if (dsp->xtp_class == class) {
1082 dsp_match = dsp;
1083 break;
1085 dsp++;
1088 /* did we find one atall? */
1089 if (dsp_match == NULL) {
1090 show_oops(t, "%s: no matching xtp despatch found", __func__);
1091 goto clean;
1094 /* check session key and call despatch function */
1095 if (validate_xtp_session_key(t, sv[0])) {
1096 ret = TRUE; /* all is well, this was a valid xtp request */
1097 if (sv[2])
1098 dsp_match->handle_func(t, atoi(sv[1]), atoi(sv[2]),
1099 uri->query);
1100 else
1101 dsp_match->handle_func(t, atoi(sv[1]), 0, uri->query);
1104 clean:
1105 if (uri)
1106 soup_uri_free(uri);
1107 if (sv)
1108 g_strfreev(sv);
1110 return (ret);
1114 * update all favorite tabs apart from one. Pass NULL if
1115 * you want to update all.
1117 void
1118 update_favorite_tabs(struct tab *apart_from)
1120 struct tab *t;
1122 if (!updating_fl_tabs) {
1123 updating_fl_tabs = 1; /* stop infinite recursion */
1124 TAILQ_FOREACH(t, &tabs, entry)
1125 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL)
1126 && (t != apart_from))
1127 xtp_page_fl(t, NULL);
1128 updating_fl_tabs = 0;
1133 * update all download tabs apart from one. Pass NULL if
1134 * you want to update all.
1136 void
1137 update_download_tabs(struct tab *apart_from)
1139 struct tab *t;
1141 if (!updating_dl_tabs) {
1142 updating_dl_tabs = 1; /* stop infinite recursion */
1143 TAILQ_FOREACH(t, &tabs, entry)
1144 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_DL)
1145 && (t != apart_from))
1146 xtp_page_dl(t, NULL);
1147 updating_dl_tabs = 0;
1152 * update all cookie tabs apart from one. Pass NULL if
1153 * you want to update all.
1155 void
1156 update_cookie_tabs(struct tab *apart_from)
1158 struct tab *t;
1160 if (!updating_cl_tabs) {
1161 updating_cl_tabs = 1; /* stop infinite recursion */
1162 TAILQ_FOREACH(t, &tabs, entry)
1163 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL)
1164 && (t != apart_from))
1165 xtp_page_cl(t, NULL);
1166 updating_cl_tabs = 0;
1171 * update all history tabs apart from one. Pass NULL if
1172 * you want to update all.
1174 void
1175 update_history_tabs(struct tab *apart_from)
1177 struct tab *t;
1179 if (!updating_hl_tabs) {
1180 updating_hl_tabs = 1; /* stop infinite recursion */
1181 TAILQ_FOREACH(t, &tabs, entry)
1182 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL)
1183 && (t != apart_from))
1184 xtp_page_hl(t, NULL);
1185 updating_hl_tabs = 0;
1190 * update all search tabs apart from one. Pass NULL if
1191 * you want to update all.
1193 void
1194 update_search_tabs(struct tab *apart_from)
1196 struct tab *t;
1198 if (!updating_sl_tabs) {
1199 updating_sl_tabs = 1; /* stop infinite recursion */
1200 TAILQ_FOREACH(t, &tabs, entry)
1201 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_SL)
1202 && (t != apart_from))
1203 xtp_page_sl(t, NULL);
1204 updating_sl_tabs = 0;
1209 xtp_page_ab(struct tab *t, struct karg *args)
1211 char *page, *body;
1213 if (t == NULL)
1214 show_oops(NULL, "about invalid parameters");
1216 generate_xtp_session_key(&t->session_key);
1218 body = g_strdup_printf("<b>Version: %s</b>"
1219 #ifdef XOMBRERO_BUILDSTR
1220 "<br><b>Build: %s</b>"
1221 #endif
1222 "<br><b>WebKit: %d.%d.%d</b>"
1223 "<br><b>User Agent: %d.%d</b>"
1224 #ifdef WEBKITGTK_API_VERSION
1225 "<br><b>WebKit API: %.1f</b>"
1226 #endif
1227 "<br><b>Configuration: %s" PS "<a href='%s%d/%s/%d'>.%s</a>"
1228 " (remember to restart the browser after any changes)</b>"
1229 "<p>"
1230 "Authors:"
1231 "<ul>"
1232 "<li>Marco Peereboom &lt;marco@peereboom.us&gt;</li>"
1233 "<li>Stevan Andjelkovic &lt;stevan@student.chalmers.se&gt;</li>"
1234 "<li>Edd Barrett &lt;vext01@gmail.com&gt;</li>"
1235 "<li>Todd T. Fries &lt;todd@fries.net&gt;</li>"
1236 "<li>Raphael Graf &lt;r@undefined.ch&gt;</li>"
1237 "<li>Michal Mazurek &lt;akfaew@jasminek.net&gt;</li>"
1238 "<li>Josh Rickmar &lt;jrick@devio.us&gt;</li>"
1239 "</ul>"
1240 "Copyrights and licenses can be found on the xombrero "
1241 "<a href=\"http://opensource.conformal.com/wiki/xombrero\">website</a>"
1242 "</p>",
1243 #ifdef XOMBRERO_BUILDSTR
1244 version, XOMBRERO_BUILDSTR,
1245 #else
1246 version,
1247 #endif
1248 WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION,
1249 WEBKIT_USER_AGENT_MAJOR_VERSION, WEBKIT_USER_AGENT_MINOR_VERSION
1250 #ifdef WEBKITGTK_API_VERSION
1251 ,WEBKITGTK_API_VERSION
1252 #endif
1253 ,pwd->pw_dir,
1254 XT_XTP_STR,
1255 XT_XTP_AB,
1256 t->session_key,
1257 XT_XTP_AB_EDIT_CONF,
1258 XT_CONF_FILE
1261 page = get_html_page("About", body, "", 0);
1262 g_free(body);
1264 load_webkit_string(t, page, XT_URI_ABOUT_ABOUT);
1266 g_free(page);
1268 return (0);
1271 /* show a list of favorites (bookmarks) */
1273 xtp_page_fl(struct tab *t, struct karg *args)
1275 char file[PATH_MAX];
1276 FILE *f;
1277 char *uri = NULL, *title = NULL;
1278 size_t len, lineno = 0;
1279 int i, failed = 0;
1280 char *body, *tmp, *page = NULL;
1281 const char delim[3] = {'\\', '\\', '\0'};
1283 DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
1285 if (t == NULL)
1286 warn("%s: bad param", __func__);
1288 generate_xtp_session_key(&t->session_key);
1290 /* open favorites */
1291 snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
1292 if ((f = fopen(file, "r")) == NULL) {
1293 show_oops(t, "Can't open favorites file: %s", strerror(errno));
1294 return (1);
1297 /* body */
1298 if (args->i & XT_DELETE)
1299 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
1300 "<th style='width: 40px'>&#35;</th><th>Link</th>"
1301 "<th style='width: 40px'>Rm</th></tr>\n");
1302 else
1303 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
1304 "<th style='width: 40px'>&#35;</th><th>Link</th></tr>\n");
1306 for (i = 1;;) {
1307 if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
1308 break;
1309 if (strlen(title) == 0) {
1310 free(title);
1311 title = NULL;
1312 continue;
1315 if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
1316 if (feof(f) || ferror(f)) {
1317 show_oops(t, "favorites file corrupt");
1318 failed = 1;
1319 break;
1322 tmp = body;
1323 if (args->i & XT_DELETE)
1324 body = g_strdup_printf("%s<tr>"
1325 "<td>%d</td>"
1326 "<td><a href='%s'>%s</a></td>"
1327 "<td style='text-align: center'>"
1328 "<a href='%s%d/%s/%d/%d'>X</a></td>"
1329 "</tr>\n",
1330 body, i, uri, title,
1331 XT_XTP_STR, XT_XTP_FL, t->session_key,
1332 XT_XTP_FL_REMOVE, i);
1333 else
1334 body = g_strdup_printf("%s<tr>"
1335 "<td>%d</td>"
1336 "<td><a href='%s'>%s</a></td>"
1337 "</tr>\n",
1338 body, i, uri, title);
1339 g_free(tmp);
1341 free(uri);
1342 uri = NULL;
1343 free(title);
1344 title = NULL;
1345 i++;
1347 fclose(f);
1349 /* if none, say so */
1350 if (i == 1) {
1351 tmp = body;
1352 body = g_strdup_printf("%s<tr>"
1353 "<td colspan='3' style='text-align: center'>"
1354 "No favorites - To add one use the 'favadd' command."
1355 "</td></tr>", body);
1356 g_free(tmp);
1359 tmp = body;
1360 body = g_strdup_printf("%s</table>", body);
1361 g_free(tmp);
1363 if (uri)
1364 free(uri);
1365 if (title)
1366 free(title);
1368 /* render */
1369 if (!failed) {
1370 page = get_html_page("Favorites", body, "", 1);
1371 load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES);
1372 g_free(page);
1375 update_favorite_tabs(t);
1377 if (body)
1378 g_free(body);
1380 return (failed);
1384 * Return a new string with a download row (in html)
1385 * appended. Old string is freed.
1387 char *
1388 xtp_page_dl_row(struct tab *t, char *html, struct download *dl)
1391 WebKitDownloadStatus stat;
1392 const gchar *destination;
1393 char *status_html = NULL, *cmd_html = NULL, *new_html;
1394 gdouble progress;
1395 char cur_sz[FMT_SCALED_STRSIZE];
1396 char tot_sz[FMT_SCALED_STRSIZE];
1397 char *xtp_prefix;
1399 DNPRINTF(XT_D_DOWNLOAD, "%s: dl->id %d\n", __func__, dl->id);
1401 /* All actions wil take this form:
1402 * xxxt://class/seskey
1404 xtp_prefix = g_strdup_printf("%s%d/%s/",
1405 XT_XTP_STR, XT_XTP_DL, t->session_key);
1407 stat = webkit_download_get_status(dl->download);
1409 switch (stat) {
1410 case WEBKIT_DOWNLOAD_STATUS_FINISHED:
1411 status_html = g_strdup_printf("Finished");
1412 cmd_html = g_strdup_printf(
1413 "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1414 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1415 XT_XTP_DL_UNLINK, dl->id);
1416 break;
1417 case WEBKIT_DOWNLOAD_STATUS_STARTED:
1418 /* gather size info */
1419 progress = 100 * webkit_download_get_progress(dl->download);
1421 fmt_scaled(
1422 webkit_download_get_current_size(dl->download), cur_sz);
1423 fmt_scaled(
1424 webkit_download_get_total_size(dl->download), tot_sz);
1426 status_html = g_strdup_printf(
1427 "<div style='width: 100%%' align='center'>"
1428 "<div class='progress-outer'>"
1429 "<div class='progress-inner' style='width: %.2f%%'>"
1430 "</div></div></div>"
1431 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
1432 progress, cur_sz, tot_sz, progress);
1434 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1435 xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
1437 break;
1438 /* LLL */
1439 case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
1440 status_html = g_strdup_printf("Cancelled");
1441 cmd_html = g_strdup_printf(
1442 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1443 xtp_prefix, XT_XTP_DL_START, dl->id,
1444 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1445 XT_XTP_DL_UNLINK, dl->id);
1446 break;
1447 case WEBKIT_DOWNLOAD_STATUS_ERROR:
1448 status_html = g_strdup_printf("Error!");
1449 cmd_html = g_strdup_printf(
1450 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1451 xtp_prefix, XT_XTP_DL_START, dl->id,
1452 xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
1453 XT_XTP_DL_UNLINK, dl->id);
1454 break;
1455 case WEBKIT_DOWNLOAD_STATUS_CREATED:
1456 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Start</a> / <a href='%s%d/%d'>Cancel</a>",
1457 xtp_prefix, XT_XTP_DL_START, dl->id, xtp_prefix,
1458 XT_XTP_DL_CANCEL, dl->id);
1459 status_html = g_strdup_printf("Created");
1460 break;
1461 default:
1462 show_oops(t, "%s: unknown download status", __func__);
1465 destination = webkit_download_get_destination_uri(dl->download);
1466 /* we might not have a destination set yet */
1467 if (!destination)
1468 destination = webkit_download_get_suggested_filename(dl->download);
1469 new_html = g_strdup_printf(
1470 "%s\n<tr><td>%s</td><td>%s</td>"
1471 "<td style='text-align:center'>%s</td></tr>\n",
1472 html, basename((char *)destination),
1473 status_html, cmd_html);
1474 g_free(html);
1476 if (status_html)
1477 g_free(status_html);
1479 if (cmd_html)
1480 g_free(cmd_html);
1482 g_free(xtp_prefix);
1484 return new_html;
1487 /* cookie management XTP page */
1489 xtp_page_cl(struct tab *t, struct karg *args)
1491 char *body, *page, *tmp;
1492 int i = 1; /* all ids start 1 */
1493 int domain_id = 0;
1494 GSList *sc, *pc, *pc_start;
1495 SoupCookie *c;
1496 char *type, *table_headers, *last_domain;
1498 DNPRINTF(XT_D_CMD, "%s", __func__);
1500 if (t == NULL) {
1501 show_oops(NULL, "%s invalid parameters", __func__);
1502 return (1);
1505 generate_xtp_session_key(&t->session_key);
1507 /* table headers */
1508 table_headers = g_strdup_printf("<table><tr>"
1509 "<th>Type</th>"
1510 "<th>Name</th>"
1511 "<th style='width:200px'>Value</th>"
1512 "<th>Path</th>"
1513 "<th>Expires</th>"
1514 "<th>Secure</th>"
1515 "<th>HTTP<br />only</th>"
1516 "<th style='width:40px'>Rm</th></tr>\n");
1518 sc = soup_cookie_jar_all_cookies(s_cookiejar);
1519 pc = soup_cookie_jar_all_cookies(p_cookiejar);
1520 pc_start = pc;
1522 body = g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
1523 "[ Remove All Cookies From All Domains ]</a></div>\n",
1524 XT_XTP_STR, XT_XTP_CL, t->session_key, XT_XTP_CL_REMOVE_ALL);
1526 last_domain = g_strdup("");
1527 for (; sc; sc = sc->next) {
1528 c = sc->data;
1530 if (strcmp(last_domain, c->domain) != 0) {
1531 /* new domain */
1532 domain_id ++;
1533 g_free(last_domain);
1534 last_domain = g_strdup(c->domain);
1536 if (body != NULL) {
1537 tmp = body;
1538 body = g_strdup_printf("%s</table>"
1539 "<h2>%s</h2><div align=\"center\">"
1540 "<a href='%s%d/%s/%d/%d'>"
1541 "[ Remove All From This Domain ]"
1542 "</a></div>%s\n",
1543 body, c->domain,
1544 XT_XTP_STR, XT_XTP_CL, t->session_key,
1545 XT_XTP_CL_REMOVE_DOMAIN, domain_id,
1546 table_headers);
1547 g_free(tmp);
1548 } else {
1549 /* first domain */
1550 body = g_strdup_printf("<h2>%s</h2>"
1551 "<div align=\"center\">"
1552 "<a href='%s%d/%s/%d/%d'>"
1553 "[ Remove All From This Domain ]</a></div>%s\n",
1554 c->domain, XT_XTP_STR, XT_XTP_CL,
1555 t->session_key, XT_XTP_CL_REMOVE_DOMAIN,
1556 domain_id, table_headers);
1560 type = "Session";
1561 for (pc = pc_start; pc; pc = pc->next)
1562 if (soup_cookie_equal(pc->data, c)) {
1563 type = "Session + Persistent";
1564 break;
1567 tmp = body;
1568 body = g_strdup_printf(
1569 "%s\n<tr>"
1570 "<td>%s</td>"
1571 "<td style='word-wrap:normal'>%s</td>"
1572 "<td>"
1573 " <textarea rows='4'>%s</textarea>"
1574 "</td>"
1575 "<td>%s</td>"
1576 "<td>%s</td>"
1577 "<td>%d</td>"
1578 "<td>%d</td>"
1579 "<td style='text-align:center'>"
1580 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1581 body,
1582 type,
1583 c->name,
1584 c->value,
1585 c->path,
1586 c->expires ?
1587 soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
1588 c->secure,
1589 c->http_only,
1591 XT_XTP_STR,
1592 XT_XTP_CL,
1593 t->session_key,
1594 XT_XTP_CL_REMOVE,
1598 g_free(tmp);
1599 i++;
1602 soup_cookies_free(sc);
1603 soup_cookies_free(pc);
1605 /* small message if there are none */
1606 if (i == 1) {
1607 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1608 "colspan='8'>No Cookies</td></tr>\n", table_headers);
1610 tmp = body;
1611 body = g_strdup_printf("%s</table>", body);
1612 g_free(tmp);
1614 page = get_html_page("Cookie Jar", body, "", TRUE);
1615 g_free(body);
1616 g_free(table_headers);
1617 g_free(last_domain);
1619 load_webkit_string(t, page, XT_URI_ABOUT_COOKIEJAR);
1620 update_cookie_tabs(t);
1622 g_free(page);
1624 return (0);
1628 xtp_page_hl(struct tab *t, struct karg *args)
1630 char *body, *page, *tmp;
1631 struct history *h;
1632 int i = 1; /* all ids start 1 */
1634 DNPRINTF(XT_D_CMD, "%s", __func__);
1636 if (t == NULL) {
1637 show_oops(NULL, "%s invalid parameters", __func__);
1638 return (1);
1641 generate_xtp_session_key(&t->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, t->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, t->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);
1714 generate_xtp_session_key(&t->session_key);
1716 /* header - with refresh so as to update */
1717 if (refresh_interval >= 1)
1718 ref = g_strdup_printf(
1719 "<meta http-equiv='refresh' content='%u"
1720 ";url=%s%d/%s/%d' />\n",
1721 refresh_interval,
1722 XT_XTP_STR,
1723 XT_XTP_DL,
1724 t->session_key,
1725 XT_XTP_DL_LIST);
1726 else
1727 ref = g_strdup("");
1729 body = g_strdup_printf("<div align='center'>"
1730 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
1731 "</p><table><tr><th style='width: 60%%'>"
1732 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
1733 XT_XTP_STR, XT_XTP_DL, t->session_key, XT_XTP_DL_LIST);
1735 RB_FOREACH_REVERSE(dl, download_list, &downloads) {
1736 body = xtp_page_dl_row(t, body, dl);
1737 n_dl++;
1740 /* message if no downloads in list */
1741 if (n_dl == 1) {
1742 tmp = body;
1743 body = g_strdup_printf("%s\n<tr><td colspan='3'"
1744 " style='text-align: center'>"
1745 "No downloads</td></tr>\n", body);
1746 g_free(tmp);
1749 tmp = body;
1750 body = g_strdup_printf("%s</table></div>", body);
1751 g_free(tmp);
1753 page = get_html_page("Downloads", body, ref, 1);
1754 g_free(ref);
1755 g_free(body);
1758 * update all download manager tabs as the xtp session
1759 * key has now changed. No need to update the current tab.
1760 * Already did that above.
1762 update_download_tabs(t);
1764 load_webkit_string(t, page, XT_URI_ABOUT_DOWNLOADS);
1765 g_free(page);
1767 return (0);
1771 xtp_page_sl(struct tab *t, struct karg *args)
1773 int i;
1774 char *page, *body, *tmp;
1776 DNPRINTF(XT_D_SEARCH, "%s", __func__);
1778 generate_xtp_session_key(&t->session_key);
1780 if (t == NULL) {
1781 show_oops(NULL, "%s invalid parameters", __func__);
1782 return (1);
1785 body = g_strdup_printf("<p>The xombrero authors will not choose a "
1786 "default search engine for you. What follows is a list of search "
1787 "engines (in no particular order) you may be interested in. "
1788 "To permanently choose a search engine, click [ Select ] to save "
1789 "<tt>search_string</tt> as a runtime setting, or set "
1790 "<tt>search_string</tt> to the appropriate URL in your xombrero "
1791 "configuration.</p>");
1793 tmp = body;
1794 body = g_strdup_printf("%s\n<table style='table-layout:fixed'><tr>"
1795 "<th style='width: 200px'>Name</th><th>URL</th>"
1796 "<th style='width: 100px'>Select</th></tr>\n", body);
1797 g_free(tmp);
1799 for (i = 0; i < (sizeof search_list / sizeof (struct search_type)); ++i)
1800 body = search_engine_add(body, search_list[i].name,
1801 search_list[i].url, t->session_key, i);
1803 tmp = body;
1804 body = g_strdup_printf("%s</table>", body);
1805 g_free(tmp);
1807 page = get_html_page("Choose a search engine", body, "", 1);
1808 g_free(body);
1811 * update all search tabs as the xtp session key has now changed. No
1812 * need to update the current tab. Already did that above.
1814 update_search_tabs(t);
1816 load_webkit_string(t, page, XT_URI_ABOUT_SEARCH);
1817 g_free(page);
1819 return (0);
1823 xtp_page_sv(struct tab *t, struct karg *args)
1825 SoupURI *soupuri;
1826 static int arg = 0;
1827 struct secviolation find, *sv;
1828 char *page, *body;
1830 if (t == NULL)
1831 show_oops(NULL, "secviolation invalid parameters");
1833 generate_xtp_session_key(&t->session_key);
1835 if (args == NULL) {
1836 find.xtp_arg = t->xtp_arg;
1837 sv = RB_FIND(secviolation_list, &svl, &find);
1838 if (sv == NULL)
1839 return (-1);
1840 } else {
1841 sv = g_malloc(sizeof(struct secviolation));
1842 sv->xtp_arg = ++arg;
1843 t->xtp_arg = arg;
1844 sv->t = t;
1845 sv->uri = args->s;
1846 RB_INSERT(secviolation_list, &svl, sv);
1849 if (sv->uri == NULL || (soupuri = soup_uri_new(sv->uri)) == NULL)
1850 return (-1);
1852 body = g_strdup_printf(
1853 "<p><b>You tried to access %s</b>."
1854 "<p><b>The site's security certificate has been modified.</b>"
1855 "<p>The domain of the page you have tried to access, <b>%s</b>, "
1856 "has a different remote certificate then the local cached version "
1857 "from a previous visit. As a security precaution to help prevent "
1858 "against man-in-the-middle attacks, please choose one of the "
1859 "following actions to continue, or disable the "
1860 "<tt>warn_cert_changes</tt> setting in your xombrero "
1861 "configuration."
1862 "<p><b>Choose an action:"
1863 "<br><a href='%s%d/%s/%d/%d'>Allow for this session</a>"
1864 "<br><a href='%s%d/%s/%d/%d'>Cache new certificate</a>"
1865 "<br><a href='%s%d/%s/%d/%d'>Show cached certificate</a>"
1866 "<br><a href='%s%d/%s/%d/%d'>Show new certificate</a>",
1867 sv->uri,
1868 soupuri->host,
1869 XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_ALLOW_SESSION,
1870 sv->xtp_arg,
1871 XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_CACHE,
1872 sv->xtp_arg,
1873 XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_SHOW_CACHED_CERT,
1874 sv->xtp_arg,
1875 XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_SHOW_NEW_CERT,
1876 sv->xtp_arg);
1878 page = get_html_page("Security Violation", body, "", 0);
1879 g_free(body);
1881 load_webkit_string(t, page, XT_URI_ABOUT_SECVIOLATION);
1883 g_free(page);
1884 if (soupuri)
1885 soup_uri_free(soupuri);
1887 return (0);
1891 startpage(struct tab *t, struct karg *args)
1893 char *page, *body, *b;
1894 struct sp *s;
1896 if (t == NULL)
1897 show_oops(NULL, "startpage invalid parameters");
1899 body = g_strdup_printf("<b>Startup Exception(s):</b><p>");
1901 TAILQ_FOREACH(s, &spl, entry) {
1902 b = body;
1903 body = g_strdup_printf("%s%s<br>", body, s->line);
1904 g_free(b);
1907 page = get_html_page("Startup Exception", body, "", 0);
1908 g_free(body);
1910 load_webkit_string(t, page, XT_URI_ABOUT_STARTPAGE);
1911 g_free(page);
1913 return (0);
1916 void
1917 startpage_add(const char *fmt, ...)
1919 va_list ap;
1920 char *msg;
1921 struct sp *s;
1923 if (fmt == NULL)
1924 return;
1926 va_start(ap, fmt);
1927 if ((msg = g_strdup_vprintf(fmt, ap)) == NULL)
1928 errx(1, "startpage_add failed");
1929 va_end(ap);
1931 s = g_malloc0(sizeof *s);
1932 s->line = msg;
1934 TAILQ_INSERT_TAIL(&spl, s, entry);
1936 gchar *show_g_object_settings(GObject *, char *, int);
1938 char *
1939 xt_g_object_serialize(GValue *value, const gchar *tname, char *str, int recurse)
1941 int typeno = 0;
1942 char *valstr, *tmpstr, *tmpsettings;
1943 GObject *object;
1945 typeno = G_TYPE_FUNDAMENTAL( G_VALUE_TYPE(value) );
1946 switch ( typeno ) {
1947 case G_TYPE_ENUM:
1948 valstr = g_strdup_printf("%d",
1949 g_value_get_enum(value));
1950 break;
1951 case G_TYPE_CHAR:
1952 valstr = g_strdup_printf("%c",
1953 g_value_get_schar(value));
1954 break;
1955 case G_TYPE_UCHAR:
1956 valstr = g_strdup_printf("%c",
1957 g_value_get_uchar(value));
1958 break;
1959 case G_TYPE_LONG:
1960 valstr = g_strdup_printf("%ld",
1961 g_value_get_long(value));
1962 break;
1963 case G_TYPE_ULONG:
1964 valstr = g_strdup_printf("%ld",
1965 g_value_get_ulong(value));
1966 break;
1967 case G_TYPE_INT:
1968 valstr = g_strdup_printf("%d",
1969 g_value_get_int(value));
1970 break;
1971 case G_TYPE_INT64:
1972 valstr = g_strdup_printf("%" PRIo64,
1973 (int64_t) g_value_get_int64(value));
1974 break;
1975 case G_TYPE_UINT:
1976 valstr = g_strdup_printf("%d",
1977 g_value_get_uint(value));
1978 break;
1979 case G_TYPE_UINT64:
1980 valstr = g_strdup_printf("%" PRIu64,
1981 (uint64_t) g_value_get_uint64(value));
1982 break;
1983 case G_TYPE_FLAGS:
1984 valstr = g_strdup_printf("0x%x",
1985 g_value_get_flags(value));
1986 break;
1987 case G_TYPE_BOOLEAN:
1988 valstr = g_strdup_printf("%s",
1989 g_value_get_boolean(value) ? "TRUE" : "FALSE");
1990 break;
1991 case G_TYPE_FLOAT:
1992 valstr = g_strdup_printf("%f",
1993 g_value_get_float(value));
1994 break;
1995 case G_TYPE_DOUBLE:
1996 valstr = g_strdup_printf("%f",
1997 g_value_get_double(value));
1998 break;
1999 case G_TYPE_STRING:
2000 valstr = g_strdup_printf("\"%s\"",
2001 g_value_get_string(value));
2002 break;
2003 case G_TYPE_POINTER:
2004 valstr = g_strdup_printf("%p",
2005 g_value_get_pointer(value));
2006 break;
2007 case G_TYPE_OBJECT:
2008 object = g_value_get_object(value);
2009 if (object != NULL) {
2010 if (recurse) {
2011 tmpstr = g_strdup_printf("%s ", str);
2012 tmpsettings = show_g_object_settings( object,
2013 tmpstr, recurse);
2014 g_free(tmpstr);
2016 if (strrchr(tmpsettings, '\n') != NULL) {
2017 valstr = g_strdup_printf("%s%s }",
2018 tmpsettings, str);
2019 g_free(tmpsettings);
2020 } else {
2021 valstr = tmpsettings;
2023 } else {
2024 valstr = g_strdup_printf("<...>");
2026 } else {
2027 valstr = g_strdup_printf("settings[] = NULL");
2029 break;
2030 default:
2031 valstr = g_strdup_printf("type %s unhandled", tname);
2033 return valstr;
2036 gchar *
2037 show_g_object_settings(GObject *o, char *str, int recurse)
2039 char *b, *p, *body, *valstr, *tmpstr;
2040 guint n_props = 0;
2041 int i, typeno = 0;
2042 GParamSpec *pspec;
2043 const gchar *tname;
2044 GValue value;
2045 GParamSpec **proplist;
2046 const gchar *name;
2048 if (!G_IS_OBJECT(o)) {
2049 fprintf(stderr, "%s is not a g_object\n", str);
2050 return g_strdup("");
2052 proplist = g_object_class_list_properties(
2053 G_OBJECT_GET_CLASS(o), &n_props);
2055 if (GTK_IS_WIDGET(o)) {
2056 name = gtk_widget_get_name(GTK_WIDGET(o));
2057 } else {
2058 name = "settings";
2060 if (n_props == 0) {
2061 body = g_strdup_printf("%s[0] = { }", name);
2062 goto end_show_g_objects;
2065 body = g_strdup_printf("%s[%d] = {\n", name, n_props);
2066 for (i=0; i < n_props; i++) {
2067 pspec = proplist[i];
2068 tname = G_OBJECT_TYPE_NAME(pspec);
2069 bzero(&value, sizeof value);
2070 valstr = NULL;
2072 if (!(pspec->flags & G_PARAM_READABLE))
2073 valstr = g_strdup_printf("not a readable property");
2074 else {
2075 g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
2076 g_object_get_property(G_OBJECT(o), pspec->name,
2077 &value);
2078 typeno = G_TYPE_FUNDAMENTAL( G_VALUE_TYPE(&value) );
2081 /* based on the type, recurse and display values */
2082 if (valstr == NULL) {
2083 valstr = xt_g_object_serialize(&value, tname, str,
2084 recurse);
2087 tmpstr = g_strdup_printf("%-13s %s%s%s,", tname, pspec->name,
2088 (typeno == G_TYPE_OBJECT) ? "." : " = ", valstr);
2089 b = body;
2091 #define XT_G_OBJECT_SPACING 50
2092 p = strrchr(tmpstr, '\n');
2093 if (p == NULL && strlen(tmpstr) > XT_G_OBJECT_SPACING) {
2094 body = g_strdup_printf(
2095 "%s%s %-50s\n%s %50s /* %3d flags=0x%08x */\n",
2096 body, str, tmpstr, str, "", i, pspec->flags);
2097 } else {
2098 char *fmt;
2099 int strspaces;
2100 if (p == NULL)
2101 strspaces = XT_G_OBJECT_SPACING;
2102 else
2103 strspaces = strlen(tmpstr) - (strlen(p) - strlen(str)) + XT_G_OBJECT_SPACING + 5;
2104 fmt = g_strdup_printf("%%s%%s %%-%ds /* %%3d flags=0x%%08x */\n", strspaces);
2105 body = g_strdup_printf(fmt, body, str, tmpstr, i, pspec->flags);
2106 g_free(fmt);
2108 g_free(tmpstr);
2109 g_free(b);
2110 g_free(valstr);
2112 end_show_g_objects:
2113 g_free(proplist);
2114 return (body);
2117 char *
2118 xt_append_settings(char *str, GObject *object, char *name, int recurse)
2120 char *newstr, *settings;
2122 settings = show_g_object_settings(object, name, recurse);
2123 if (str == NULL)
2124 str = g_strdup("");
2126 newstr = g_strdup_printf("%s%s %s%s };\n", str, name, settings, name);
2127 g_free(str);
2129 return newstr;
2133 about_webkit(struct tab *t, struct karg *arg)
2135 char *page, *body, *settingstr;
2137 settingstr = xt_append_settings(NULL, G_OBJECT(t->settings),
2138 "t->settings", 0);
2139 body = g_strdup_printf("<pre>%s</pre>\n", settingstr);
2140 g_free(settingstr);
2142 page = get_html_page("About Webkit", body, "", 0);
2143 g_free(body);
2145 load_webkit_string(t, page, XT_URI_ABOUT_WEBKIT);
2146 g_free(page);
2148 return (0);
2151 static int toplevelcount = 0;
2153 void
2154 xt_append_toplevel(GtkWindow *w, char **body)
2156 char *n;
2158 n = g_strdup_printf("toplevel#%d", toplevelcount++);
2159 *body = xt_append_settings(*body, G_OBJECT(w), n, 0);
2160 g_free(n);
2164 allthethings(struct tab *t, struct karg *arg)
2166 GList *list;
2167 char *page, *body, *b;
2169 body = xt_append_settings(NULL, G_OBJECT(t->wv), "t->wv", 1);
2170 body = xt_append_settings(body, G_OBJECT(t->inspector),
2171 "t->inspector", 1);
2172 #if 0 /* not until warnings are gone */
2173 body = xt_append_settings(body, G_OBJECT(session),
2174 "session", 1);
2175 #endif
2176 toplevelcount = 0;
2177 list = gtk_window_list_toplevels();
2178 g_list_foreach(list, (GFunc)g_object_ref, NULL);
2179 g_list_foreach(list, (GFunc)xt_append_toplevel, &body);
2180 g_list_foreach(list, (GFunc)g_object_unref, NULL);
2181 g_list_free(list);
2183 b = body;
2184 body = g_strdup_printf("<pre>%scan paste clipboard = %d\n</pre>", body,
2185 webkit_web_view_can_paste_clipboard(t->wv));
2186 g_free(b);
2188 page = get_html_page("About All The Things _o/", body, "", 0);
2189 g_free(body);
2191 load_webkit_string(t, page, XT_URI_ABOUT_ALLTHETHINGS);
2192 g_free(page);
2194 return (0);