"Fossies" - the Fresh Open Source Software Archive

Member "xombrero-1.6.4/about.c" (17 Feb 2015, 54697 Bytes) of package /linux/www/old/xombrero-1.6.4.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "about.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.6.3_vs_1.6.4.

    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>
    9  *
   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.
   13  *
   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.
   21  */
   22 
   23 #include <xombrero.h>
   24 
   25 /*
   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
   31  *
   32  * Don't begin xtp class/actions as 0. atoi returns that on error.
   33  *
   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.
   36  */
   37 
   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"
   75 
   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 *);
   87 
   88 /*
   89  * If you change the index of any of these, correct the
   90  * XT_XTP_TAB_MEANING_* macros in xombrero.h!
   91  */
   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 },
  114 };
  115 
  116 struct search_type {
  117     const char      *name;
  118     const char      *url;
  119 } search_list[] = {
  120     { "Google (SSL)",   "https://encrypted.google.com/search?q=%s" },
  121     { "Bing",       "http://www.bing.com/search?q=%s" },
  122     { "Yahoo",      "http://search.yahoo.com/search?p=%s" },
  123     { "DuckDuckGo",     "https://duckduckgo.com/?q=%s" },
  124     { "DuckDuckGo (HTML)",  "https://duckduckgo.com/html?q=%s" },
  125     { "DuckDuckGo (Lite)",  "https://duckduckgo.com/lite?q=%s" },
  126     { "Ixquick",        "https://ixquick.com/do/search?q=%s" },
  127     { "Startpage",      "https://startpage.com/do/search?q=%s" },
  128 };
  129 
  130 /*
  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)
  134  */
  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
  138 
  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;
  147 
  148 size_t
  149 about_list_size(void)
  150 {
  151     return (LENGTH(about_list));
  152 }
  153 
  154 gchar *
  155 get_html_page(gchar *title, gchar *body, gchar *head, bool addstyles)
  156 {
  157     gchar           *r;
  158 
  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);
  174 
  175     return (r);
  176 }
  177 
  178 /*
  179  * Display a web page from a HTML string in memory, rather than from a URL
  180  */
  181 void
  182 load_webkit_string(struct tab *t, const char *str, gchar *title, int nohist)
  183 {
  184     char            file[PATH_MAX];
  185     int         i;
  186 
  187     if (g_signal_handler_is_connected(t->wv, t->progress_handle))
  188         g_signal_handler_disconnect(t->wv, t->progress_handle);
  189 
  190     /* we set this to indicate we want to manually do navaction */
  191     if (t->bfl && !nohist) {
  192         t->item = webkit_web_back_forward_list_get_current_item(t->bfl);
  193         if (t->item)
  194             g_object_ref(t->item);
  195     }
  196 
  197     t->xtp_meaning = XT_XTP_TAB_MEANING_NORMAL;
  198     if (title) {
  199         /* set t->xtp_meaning */
  200         for (i = 0; i < LENGTH(about_list); i++)
  201             if (!strcmp(title, about_list[i].name)) {
  202                 t->xtp_meaning = i;
  203                 break;
  204             }
  205 
  206         webkit_web_view_load_string(t->wv, str, NULL, encoding,
  207             XT_XTP_STR);
  208 #if GTK_CHECK_VERSION(2, 20, 0)
  209         gtk_spinner_stop(GTK_SPINNER(t->spinner));
  210         gtk_widget_hide(t->spinner);
  211 #endif
  212         snprintf(file, sizeof file, "%s" PS "%s", resource_dir, icons[0]);
  213         xt_icon_from_file(t, file);
  214     }
  215 
  216     if (t->xtp_meaning == XT_XTP_TAB_MEANING_NORMAL &&
  217         t->session_key != NULL) {
  218         g_free(t->session_key);
  219         t->session_key = NULL;
  220     }
  221 
  222     t->progress_handle = g_signal_connect(t->wv,
  223         "notify::progress", G_CALLBACK(webview_progress_changed_cb), t);
  224 }
  225 
  226 int
  227 blank(struct tab *t, struct karg *args)
  228 {
  229     if (t == NULL)
  230         show_oops(NULL, "blank invalid parameters");
  231 
  232     load_webkit_string(t, "", XT_URI_ABOUT_BLANK, 0);
  233 
  234     return (0);
  235 }
  236 
  237 int
  238 help(struct tab *t, struct karg *args)
  239 {
  240     char            *page, *head, *body;
  241 
  242     if (t == NULL)
  243         show_oops(NULL, "help invalid parameters");
  244 
  245     head = "<meta http-equiv=\"REFRESH\" content=\"0;"
  246         "url=https://opensource.conformal.com/cgi-bin/man-cgi?xombrero\">"
  247         "</head>\n";
  248     body = "xombrero man page <a href=\"https://opensource.conformal.com/"
  249         "cgi-bin/man-cgi?xombrero\">https://opensource.conformal.com/"
  250         "cgi-bin/man-cgi?xombrero</a>";
  251 
  252     page = get_html_page(XT_NAME, body, head, FALSE);
  253 
  254     load_webkit_string(t, page, XT_URI_ABOUT_HELP, 0);
  255     g_free(page);
  256 
  257     return (0);
  258 }
  259 
  260 int
  261 stats(struct tab *t, struct karg *args)
  262 {
  263     char            *page, *body, *s, line[64 * 1024];
  264     uint64_t        line_count = 0;
  265     FILE            *r_cookie_f;
  266 
  267     if (t == NULL)
  268         show_oops(NULL, "stats invalid parameters");
  269 
  270     line[0] = '\0';
  271     if (save_rejected_cookies) {
  272         if ((r_cookie_f = fopen(rc_fname, "r"))) {
  273             for (;;) {
  274                 s = fgets(line, sizeof line, r_cookie_f);
  275                 if (s == NULL || feof(r_cookie_f) ||
  276                     ferror(r_cookie_f))
  277                     break;
  278                 line_count++;
  279             }
  280             fclose(r_cookie_f);
  281             snprintf(line, sizeof line,
  282                 "<br/>Cookies blocked(*) total: %" PRIu64,
  283                 line_count);
  284         } else
  285             show_oops(t, "Can't open blocked cookies file: %s",
  286                 strerror(errno));
  287     }
  288 
  289     body = g_strdup_printf(
  290         "Cookies blocked(*) this session: %" PRIu64
  291         "%s"
  292         "<p><small><b>*</b> results vary based on settings</small></p>",
  293         blocked_cookies,
  294         line);
  295 
  296     page = get_html_page("Statistics", body, "", 0);
  297     g_free(body);
  298 
  299     load_webkit_string(t, page, XT_URI_ABOUT_STATS, 0);
  300     g_free(page);
  301 
  302     return (0);
  303 }
  304 
  305 void
  306 show_certs(struct tab *t, gnutls_x509_crt_t *certs,
  307     size_t cert_count, char *title)
  308 {
  309     gnutls_datum_t      *cinfo;
  310     char            *tmp, *body;
  311     int         i;
  312 
  313     body = g_strdup("");
  314 
  315     for (i = 0; i < cert_count; i++) {
  316         cinfo = gnutls_malloc(sizeof *cinfo);
  317         if (gnutls_x509_crt_print(certs[i], GNUTLS_CRT_PRINT_FULL,
  318             cinfo)) {
  319             gnutls_free(cinfo);
  320             g_free(body);
  321             return;
  322         }
  323 
  324         tmp = body;
  325         body = g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
  326             body, i, cinfo->data);
  327         gnutls_free(cinfo);
  328         g_free(tmp);
  329     }
  330 
  331     tmp = get_html_page(title, body, "", 0);
  332     g_free(body);
  333 
  334     load_webkit_string(t, tmp, XT_URI_ABOUT_CERTS, 0);
  335     g_free(tmp);
  336 }
  337 
  338 int
  339 ca_cmd(struct tab *t, struct karg *args)
  340 {
  341     FILE            *f = NULL;
  342     int         rv = 1, certs_read;
  343     unsigned int        certs = 0;
  344     struct stat     sb;
  345     gnutls_datum_t      dt;
  346     gnutls_x509_crt_t   *c = NULL;
  347     char            *certs_buf = NULL, *s;
  348 
  349     if ((f = fopen(ssl_ca_file, "r")) == NULL) {
  350         show_oops(t, "Can't open CA file: %s", ssl_ca_file);
  351         return (1);
  352     }
  353 
  354     if (fstat(fileno(f), &sb) == -1) {
  355         show_oops(t, "Can't stat CA file: %s", ssl_ca_file);
  356         goto done;
  357     }
  358 
  359     certs_buf = g_malloc(sb.st_size + 1);
  360     if (fread(certs_buf, 1, sb.st_size, f) != sb.st_size) {
  361         show_oops(t, "Can't read CA file: %s", strerror(errno));
  362         goto done;
  363     }
  364     certs_buf[sb.st_size] = '\0';
  365 
  366     s = certs_buf;
  367     while ((s = strstr(s, "BEGIN CERTIFICATE"))) {
  368         certs++;
  369         s += strlen("BEGIN CERTIFICATE");
  370     }
  371 
  372     bzero(&dt, sizeof dt);
  373     dt.data = (unsigned char *)certs_buf;
  374     dt.size = sb.st_size;
  375     c = gnutls_malloc(sizeof(*c) * certs);
  376     certs_read = gnutls_x509_crt_list_import(c, &certs, &dt,
  377         GNUTLS_X509_FMT_PEM, 0);
  378     if (certs_read <= 0) {
  379         show_oops(t, "No cert(s) available");
  380         goto done;
  381     }
  382     show_certs(t, c, certs_read, "Certificate Authority Certificates");
  383 done:
  384     if (c)
  385         gnutls_free(c);
  386     if (certs_buf)
  387         g_free(certs_buf);
  388     if (f)
  389         fclose(f);
  390 
  391     return (rv);
  392 }
  393 
  394 int
  395 cookie_show_wl(struct tab *t, struct karg *args)
  396 {
  397     args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
  398     wl_show(t, args, "Cookie White List", &c_wl);
  399 
  400     return (0);
  401 }
  402 
  403 int
  404 js_show_wl(struct tab *t, struct karg *args)
  405 {
  406     args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
  407     wl_show(t, args, "JavaScript White List", &js_wl);
  408 
  409     return (0);
  410 }
  411 
  412 int
  413 cookie_cmd(struct tab *t, struct karg *args)
  414 {
  415     if (args->i & XT_SHOW)
  416         wl_show(t, args, "Cookie White List", &c_wl);
  417     else if (args->i & XT_WL_TOGGLE) {
  418         args->i |= XT_WL_RELOAD;
  419         toggle_cwl(t, args);
  420     } else if (args->i & XT_SAVE) {
  421         args->i |= XT_WL_RELOAD;
  422         wl_save(t, args, XT_WL_COOKIE);
  423     } else if (args->i & XT_DELETE) {
  424         remove_cookie_all();
  425         update_cookie_tabs(NULL);
  426     }
  427 
  428     return (0);
  429 }
  430 
  431 int
  432 js_cmd(struct tab *t, struct karg *args)
  433 {
  434     if (args->i & XT_SHOW)
  435         wl_show(t, args, "JavaScript White List", &js_wl);
  436     else if (args->i & XT_SAVE) {
  437         args->i |= XT_WL_RELOAD;
  438         wl_save(t, args, XT_WL_JAVASCRIPT);
  439     } else if (args->i & XT_WL_TOGGLE) {
  440         args->i |= XT_WL_RELOAD;
  441         toggle_js(t, args);
  442     } else if (args->i & XT_DELETE)
  443         show_oops(t, "'js delete' currently unimplemented");
  444 
  445     return (0);
  446 }
  447 
  448 int
  449 pl_show_wl(struct tab *t, struct karg *args)
  450 {
  451     args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
  452     wl_show(t, args, "Plugin White List", &pl_wl);
  453 
  454     return (0);
  455 }
  456 
  457 int
  458 pl_cmd(struct tab *t, struct karg *args)
  459 {
  460     if (args->i & XT_SHOW)
  461         wl_show(t, args, "Plugin White List", &pl_wl);
  462     else if (args->i & XT_SAVE) {
  463         args->i |= XT_WL_RELOAD;
  464         wl_save(t, args, XT_WL_PLUGIN);
  465     } else if (args->i & XT_WL_TOGGLE) {
  466         args->i |= XT_WL_RELOAD;
  467         toggle_pl(t, args);
  468     } else if (args->i & XT_DELETE)
  469         show_oops(t, "'plugin delete' currently unimplemented");
  470 
  471     return (0);
  472 }
  473 
  474 int
  475 https_show_wl(struct tab *t, struct karg *args)
  476 {
  477     args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION;
  478     wl_show(t, args, "HTTPS Force List", &force_https);
  479 
  480     return (0);
  481 }
  482 
  483 int
  484 https_cmd(struct tab *t, struct karg *args)
  485 {
  486     if (args->i & XT_SHOW)
  487         wl_show(t, args, "HTTPS Force List", &force_https);
  488     else if (args->i & XT_SAVE) {
  489         args->i |= XT_WL_RELOAD;
  490         wl_save(t, args, XT_WL_HTTPS);
  491     } else if (args->i & XT_WL_TOGGLE) {
  492         args->i |= XT_WL_RELOAD;
  493         toggle_force_https(t, args);
  494     } else if (args->i & XT_DELETE)
  495         show_oops(t, "https delete' currently unimplemented");
  496 
  497     return (0);
  498 }
  499 
  500 /*
  501  * cancel, remove, etc. downloads
  502  */
  503 void
  504 xtp_handle_dl(struct tab *t, uint8_t cmd, int id, const char *query)
  505 {
  506     struct download     find, *d = NULL;
  507 #ifndef __MINGW32__
  508     char            *file = NULL;
  509     const char      *uri = NULL;
  510 #endif
  511 
  512     DNPRINTF(XT_D_DOWNLOAD, "download control: cmd %d, id %d\n", cmd, id);
  513 
  514     /* some commands require a valid download id */
  515     if (cmd != XT_XTP_DL_LIST) {
  516         /* lookup download in question */
  517         find.id = id;
  518         d = RB_FIND(download_list, &downloads, &find);
  519 
  520         if (d == NULL) {
  521             show_oops(t, "%s: no such download", __func__);
  522             return;
  523         }
  524     }
  525 
  526     /* decide what to do */
  527     switch (cmd) {
  528     case XT_XTP_DL_START:
  529         /* our downloads always needs to be
  530          * restarted if called from here
  531          */
  532         download_start(t, d, XT_DL_RESTART);
  533         break;
  534     case XT_XTP_DL_CANCEL:
  535         webkit_download_cancel(d->download);
  536         g_object_unref(d->download);
  537         RB_REMOVE(download_list, &downloads, d);
  538         break;
  539     case XT_XTP_DL_UNLINK:
  540 #ifdef __MINGW32__
  541         /* XXX uri's aren't handled properly on windows? */
  542         unlink(webkit_download_get_destination_uri(d->download));
  543 #else
  544         uri = webkit_download_get_destination_uri(d->download);
  545         if ((file = g_filename_from_uri(uri, NULL, NULL)) != NULL) {
  546             unlink(file);
  547             g_free(file);
  548         }
  549 #endif
  550         /* FALLTHROUGH */
  551     case XT_XTP_DL_REMOVE:
  552         webkit_download_cancel(d->download); /* just incase */
  553         g_object_unref(d->download);
  554         RB_REMOVE(download_list, &downloads, d);
  555         break;
  556     case XT_XTP_DL_LIST:
  557         /* Nothing */
  558         break;
  559     default:
  560         show_oops(t, "%s: unknown command", __func__);
  561         break;
  562     };
  563     xtp_page_dl(t, NULL);
  564 }
  565 
  566 void
  567 xtp_handle_hl(struct tab *t, uint8_t cmd, int id, const char *query)
  568 {
  569     struct history      *h, *next, *ht;
  570     int         i = 1;
  571 
  572     switch (cmd) {
  573     case XT_XTP_HL_REMOVE:
  574         /* walk backwards, as listed in reverse */
  575         for (h = RB_MAX(history_list, &hl); h != NULL; h = next) {
  576             next = RB_PREV(history_list, &hl, h);
  577             if (id == i) {
  578                 RB_REMOVE(history_list, &hl, h);
  579                 g_free((gpointer) h->title);
  580                 g_free((gpointer) h->uri);
  581                 g_free(h);
  582                 break;
  583             }
  584             i++;
  585         }
  586         break;
  587     case XT_XTP_HL_REMOVE_ALL:
  588         RB_FOREACH_SAFE(h, history_list, &hl, ht)
  589             RB_REMOVE(history_list, &hl, h);
  590         break;
  591     case XT_XTP_HL_LIST:
  592         /* Nothing - just xtp_page_hl() below */
  593         break;
  594     default:
  595         show_oops(t, "%s: unknown command", __func__);
  596         break;
  597     };
  598 
  599     xtp_page_hl(t, NULL);
  600 }
  601 
  602 /* remove a favorite */
  603 void
  604 remove_favorite(struct tab *t, int index)
  605 {
  606     char            file[PATH_MAX], *title, *uri = NULL;
  607     char            *new_favs, *tmp;
  608     FILE            *f;
  609     int         i;
  610     size_t          len, lineno;
  611 
  612     /* open favorites */
  613     snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
  614 
  615     if ((f = fopen(file, "r")) == NULL) {
  616         show_oops(t, "%s: can't open favorites: %s",
  617             __func__, strerror(errno));
  618         return;
  619     }
  620 
  621     /* build a string which will become the new favorites file */
  622     new_favs = g_strdup("");
  623 
  624     for (i = 1;;) {
  625         if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
  626             if (feof(f) || ferror(f))
  627                 break;
  628         /* XXX THIS IS NOT THE RIGHT HEURISTIC */
  629         if (len == 0) {
  630             free(title);
  631             title = NULL;
  632             continue;
  633         }
  634 
  635         if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) {
  636             if (feof(f) || ferror(f)) {
  637                 show_oops(t, "%s: can't parse favorites %s",
  638                     __func__, strerror(errno));
  639                 goto clean;
  640             }
  641         }
  642 
  643         /* as long as this isn't the one we are deleting add to file */
  644         if (i != index) {
  645             tmp = new_favs;
  646             new_favs = g_strdup_printf("%s%s\n%s\n",
  647                 new_favs, title, uri);
  648             g_free(tmp);
  649         }
  650 
  651         free(uri);
  652         uri = NULL;
  653         free(title);
  654         title = NULL;
  655         i++;
  656     }
  657     fclose(f);
  658 
  659     /* write back new favorites file */
  660     if ((f = fopen(file, "w")) == NULL) {
  661         show_oops(t, "%s: can't open favorites: %s",
  662             __func__, strerror(errno));
  663         goto clean;
  664     }
  665 
  666     if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
  667         show_oops(t, "%s: can't fwrite", __func__);
  668     fclose(f);
  669 
  670 clean:
  671     if (uri)
  672         free(uri);
  673     if (title)
  674         free(title);
  675 
  676     g_free(new_favs);
  677 }
  678 
  679 int
  680 add_favorite(struct tab *t, struct karg *args)
  681 {
  682     char            file[PATH_MAX];
  683     FILE            *f;
  684     char            *line = NULL;
  685     size_t          urilen, linelen;
  686     gchar           *argtitle = NULL;
  687     const gchar     *uri, *title;
  688 
  689     if (t == NULL)
  690         return (1);
  691 
  692     /* don't allow adding of xtp pages to favorites */
  693     if (t->xtp_meaning != XT_XTP_TAB_MEANING_NORMAL) {
  694         show_oops(t, "%s: can't add xtp pages to favorites", __func__);
  695         return (1);
  696     }
  697 
  698     snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
  699     if ((f = fopen(file, "r+")) == NULL) {
  700         show_oops(t, "Can't open favorites file: %s", strerror(errno));
  701         return (1);
  702     }
  703 
  704     if (args->s && strlen(g_strstrip(args->s)))
  705         argtitle = html_escape(g_strstrip(args->s));
  706 
  707     title = argtitle ? argtitle : get_title(t, FALSE);
  708     uri = get_uri(t);
  709 
  710     if (title == NULL || uri == NULL) {
  711         show_oops(t, "can't add page to favorites");
  712         goto done;
  713     }
  714 
  715     urilen = strlen(uri);
  716 
  717     for (;;) {
  718         if ((line = fparseln(f, &linelen, NULL, NULL, 0)) == NULL) {
  719             if (feof(f))
  720                 break;
  721             else {
  722                 show_oops(t, "Error reading favorites file: %s",
  723                     strerror(errno));
  724                 goto done;
  725             }
  726         }
  727 
  728         if (linelen == urilen && !strcmp(line, uri))
  729             goto done;
  730 
  731         free(line);
  732         line = NULL;
  733     }
  734 
  735     fprintf(f, "\n%s\n%s", title, uri);
  736 done:
  737     if (argtitle)
  738         g_free(argtitle);
  739     if (line)
  740         free(line);
  741     fclose(f);
  742 
  743     update_favorite_tabs(NULL);
  744 
  745     return (0);
  746 }
  747 
  748 char *
  749 search_engine_add(char *body, const char *name, const char *url,
  750     const char *key, int select)
  751 {
  752     char            *b = body;
  753 
  754     body = g_strdup_printf("%s<tr>"
  755         "<td>%s</td>"
  756         "<td>%s</td>"
  757         "<td style='text-align: center'>"
  758         "<a href='%s%d/%s/%d/%d'>[ Select ]</a></td>"
  759         "</tr>\n",
  760         body,
  761         name,
  762         url,
  763         XT_XTP_STR, XT_XTP_SL, key, XT_XTP_SL_SET, select);
  764     g_free(b);
  765     return (body);
  766 }
  767 
  768 void
  769 xtp_handle_ab(struct tab *t, uint8_t cmd, int arg, const char *query)
  770 {
  771     char            config[PATH_MAX];
  772     char            *cmdstr;
  773     char            **sv;
  774 
  775     switch (cmd) {
  776     case XT_XTP_AB_EDIT_CONF:
  777         if (external_editor == NULL || strlen(external_editor) == 0) {
  778             show_oops(t, "external_editor is unset");
  779             break;
  780         }
  781 
  782         snprintf(config, sizeof config, "%s" PS ".%s", pwd->pw_dir,
  783             XT_CONF_FILE);
  784         sv = g_strsplit(external_editor, "<file>", -1);
  785         cmdstr = g_strjoinv(config, sv);
  786         g_strfreev(sv);
  787         sv = g_strsplit_set(cmdstr, " \t", -1);
  788 
  789         if (!g_spawn_async(NULL, sv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL,
  790             NULL, NULL))
  791             show_oops(t, "%s: could not spawn process", __func__);
  792 
  793         g_strfreev(sv);
  794         g_free(cmdstr);
  795         break;
  796     default:
  797         show_oops(t, "%s, invalid about command", __func__);
  798         break;
  799     };
  800     xtp_page_ab(t, NULL);
  801 }
  802 void
  803 xtp_handle_fl(struct tab *t, uint8_t cmd, int arg, const char *query)
  804 {
  805     struct karg     args = {0};
  806 
  807     switch (cmd) {
  808     case XT_XTP_FL_LIST:
  809         /* nothing, just the below call to xtp_page_fl() */
  810         break;
  811     case XT_XTP_FL_REMOVE:
  812         remove_favorite(t, arg);
  813         args.i = XT_DELETE;
  814         break;
  815     default:
  816         show_oops(t, "%s: invalid favorites command", __func__);
  817         break;
  818     };
  819 
  820     xtp_page_fl(t, &args);
  821 }
  822 
  823 void
  824 xtp_handle_cl(struct tab *t, uint8_t cmd, int arg, const char *query)
  825 {
  826     switch (cmd) {
  827     case XT_XTP_CL_LIST:
  828         /* nothing, just xtp_page_cl() */
  829         break;
  830     case XT_XTP_CL_REMOVE:
  831         remove_cookie(arg);
  832         break;
  833     case XT_XTP_CL_REMOVE_DOMAIN:
  834         remove_cookie_domain(arg);
  835         break;
  836     case XT_XTP_CL_REMOVE_ALL:
  837         remove_cookie_all();
  838         break;
  839     default:
  840         show_oops(t, "%s: unknown cookie xtp command", __func__);
  841         break;
  842     };
  843 
  844     xtp_page_cl(t, NULL);
  845 }
  846 
  847 void
  848 xtp_handle_sl(struct tab *t, uint8_t cmd, int arg, const char *query)
  849 {
  850     const char      *search;
  851     char            *enc_search, *uri;
  852     char            **sv;
  853 
  854     switch (cmd) {
  855     case XT_XTP_SL_SET:
  856         set_search_string((char *)search_list[arg].url);
  857         if (save_runtime_setting("search_string", search_list[arg].url))
  858             show_oops(t, "could not set search_string in runtime");
  859         break;
  860     default:
  861         show_oops(t, "%s: unknown search xtp command", __func__);
  862         break;
  863     };
  864 
  865     search = gtk_entry_get_text(GTK_ENTRY(t->search_entry)); /* static */
  866     enc_search = soup_uri_encode(search, XT_RESERVED_CHARS);
  867     sv = g_strsplit(search_string, "%s", 2);
  868     uri = g_strjoinv(enc_search, sv);
  869     load_uri(t, uri);
  870     g_free(enc_search);
  871     g_strfreev(sv);
  872     g_free(uri);
  873 }
  874 
  875 void
  876 xtp_handle_sv(struct tab *t, uint8_t cmd, int id, const char *query)
  877 {
  878     SoupURI         *soupuri = NULL;
  879     struct karg     args = {0};
  880     struct secviolation find, *sv;
  881 
  882     find.xtp_arg = id;
  883     if ((sv = RB_FIND(secviolation_list, &svl, &find)) == NULL)
  884         return;
  885 
  886     args.ptr = (void *)sv->t;
  887     args.s = sv->uri;
  888 
  889     switch (cmd) {
  890     case XT_XTP_SV_SHOW_NEW_CERT:
  891         args.i = XT_SHOW;
  892         if (cert_cmd(t, &args)) {
  893             xtp_page_sv(t, &args);
  894             return;
  895         }
  896         break;
  897     case XT_XTP_SV_SHOW_CACHED_CERT:
  898         args.i = XT_CACHE | XT_SHOW;
  899         if (cert_cmd(t, &args)) {
  900             xtp_page_sv(t, &args);
  901             return;
  902         }
  903         break;
  904     case XT_XTP_SV_ALLOW_SESSION:
  905         soupuri = soup_uri_new(sv->uri);
  906         wl_add(soupuri->host, &svil, 0);
  907         load_uri(t, sv->uri);
  908         focus_webview(t);
  909         break;
  910     case XT_XTP_SV_CACHE:
  911         args.i = XT_CACHE;
  912         if (cert_cmd(t, &args)) {
  913             xtp_page_sv(t, &args);
  914             return;
  915         }
  916         load_uri(t, sv->uri);
  917         focus_webview(t);
  918         break;
  919     default:
  920         show_oops(t, "%s: invalid secviolation command", __func__);
  921         break;
  922     };
  923 
  924     g_free(sv->uri);
  925     if (soupuri)
  926         soup_uri_free(soupuri);
  927     RB_REMOVE(secviolation_list, &svl, sv);
  928 }
  929 
  930 void
  931 xtp_handle_rt(struct tab *t, uint8_t cmd, int id, const char *query)
  932 {
  933     struct set_reject   *sr;
  934     GHashTable      *new_settings = NULL;
  935     int         modify;
  936     char            *val, *curval, *s;
  937     int         i = 0;
  938 
  939     switch (cmd) {
  940     case XT_XTP_RT_SAVE:
  941         if (query == NULL)
  942             break;
  943         new_settings = soup_form_decode(query);
  944         for (i = 0; i < get_settings_size(); ++i) {
  945             if (!rs[i].activate)
  946                 continue;
  947             val = (char *)g_hash_table_lookup(new_settings,
  948                 rs[i].name);
  949             modify = 0;
  950             switch (rs[i].type) {
  951             case XT_S_INT: /* FALLTHROUGH */
  952             case XT_S_BOOL:
  953                 if (atoi(val) != *rs[i].ival)
  954                     modify = 1;
  955                 break;
  956             case XT_S_DOUBLE:
  957                 if (atof(val) < (*rs[i].dval - 0.0001) ||
  958                     atof(val) > (*rs[i].dval + 0.0001))
  959                     modify = 1;
  960                 break;
  961             case XT_S_STR:
  962                 s = (rs[i].sval == NULL || *rs[i].sval == NULL)
  963                     ? "" : *rs[i].sval;
  964                 if (rs[i].sval && g_strcmp0(val, s))
  965                     modify = 1;
  966                 else if (rs[i].s && rs[i].s->get) {
  967                     curval = rs[i].s->get(NULL);
  968                     if (g_strcmp0(val, curval))
  969                         modify = 1;
  970                     g_free(curval);
  971                 }
  972                 break;
  973             case XT_S_INVALID: /* FALLTHROUGH */
  974             default:
  975                 break;
  976             }
  977             if (rs[i].activate(val)) {
  978                 sr = g_malloc(sizeof *sr);
  979                 sr->name = g_strdup(rs[i].name);
  980                 sr->value = g_strdup(val);
  981                 TAILQ_INSERT_TAIL(&srl, sr, entry);
  982                 continue;
  983             }
  984             if (modify)
  985                 if (save_runtime_setting(rs[i].name, val))
  986                     show_oops(t, "error");
  987         }
  988         break;
  989     default:
  990         show_oops(t, "%s: invalid set command", __func__);
  991         break;
  992     }
  993 
  994     if (new_settings)
  995         g_hash_table_destroy(new_settings);
  996 
  997     xtp_page_rt(t, NULL);
  998 }
  999 
 1000 /* link an XTP class to it's session key and handler function */
 1001 struct xtp_despatch {
 1002     uint8_t         xtp_class;
 1003     void            (*handle_func)(struct tab *, uint8_t, int,
 1004                     const char *query);
 1005 };
 1006 
 1007 struct xtp_despatch     xtp_despatches[] = {
 1008     { XT_XTP_DL, xtp_handle_dl },
 1009     { XT_XTP_HL, xtp_handle_hl },
 1010     { XT_XTP_FL, xtp_handle_fl },
 1011     { XT_XTP_CL, xtp_handle_cl },
 1012     { XT_XTP_SL, xtp_handle_sl },
 1013     { XT_XTP_AB, xtp_handle_ab },
 1014     { XT_XTP_SV, xtp_handle_sv },
 1015     { XT_XTP_RT, xtp_handle_rt },
 1016     { XT_XTP_INVALID, NULL }
 1017 };
 1018 
 1019 /*
 1020  * generate a session key to secure xtp commands.
 1021  * pass in a ptr to the key in question and it will
 1022  * be modified in place.
 1023  */
 1024 void
 1025 generate_xtp_session_key(char **key)
 1026 {
 1027     uint8_t         rand_bytes[XT_XTP_SES_KEY_SZ];
 1028 
 1029     if (key == NULL)
 1030         return;
 1031 
 1032     /* free old key */
 1033     if (*key != NULL)
 1034         g_free(*key);
 1035 
 1036     /* make a new one */
 1037     arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ);
 1038     *key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT,
 1039         rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
 1040         rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]);
 1041 
 1042     DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key);
 1043 }
 1044 
 1045 /*
 1046  * validate a xtp session key.
 1047  * return (1) if OK
 1048  */
 1049 int
 1050 validate_xtp_session_key(struct tab *t, char *key)
 1051 {
 1052     if (t == NULL || t->session_key == NULL || key == NULL)
 1053         return (0);
 1054 
 1055     if (strcmp(t->session_key, key) != 0) {
 1056         show_oops(t, "%s: xtp session key mismatch possible spoof",
 1057             __func__);
 1058         return (0);
 1059     }
 1060 
 1061     return (1);
 1062 }
 1063 
 1064 /*
 1065  * is the url xtp protocol? (xxxt://)
 1066  * if so, parse and despatch correct bahvior
 1067  */
 1068 int
 1069 parse_xtp_url(struct tab *t, const char *uri_str)
 1070 {
 1071     SoupURI         *uri = NULL;
 1072     struct xtp_despatch *dsp, *dsp_match = NULL;
 1073     int         ret = FALSE;
 1074     int         class = 0;
 1075     char            **sv = NULL;
 1076 
 1077     /*
 1078      *   uri->host  = class
 1079      *   sv[0]  = session key
 1080      *   sv[1]  = command
 1081      *   sv[2]  = optional argument
 1082      */
 1083 
 1084     DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, uri_str);
 1085 
 1086     if ((uri = soup_uri_new(uri_str)) == NULL)
 1087         goto clean;
 1088     if (strncmp(uri->scheme, XT_XTP_SCHEME, strlen(XT_XTP_SCHEME)))
 1089         goto clean;
 1090     if (uri->host == NULL || strlen(uri->host) == 0)
 1091         goto clean;
 1092     if ((sv = g_strsplit(uri->path + 1, "/", 3)) == NULL)
 1093         goto clean;
 1094 
 1095     if (sv[0] == NULL || sv[1] == NULL)
 1096         goto clean;
 1097 
 1098     dsp = xtp_despatches;
 1099     class = atoi(uri->host);
 1100     while (dsp->xtp_class) {
 1101         if (dsp->xtp_class == class) {
 1102             dsp_match = dsp;
 1103             break;
 1104         }
 1105         dsp++;
 1106     }
 1107 
 1108     /* did we find one atall? */
 1109     if (dsp_match == NULL) {
 1110         show_oops(t, "%s: no matching xtp despatch found", __func__);
 1111         goto clean;
 1112     }
 1113 
 1114     /* check session key and call despatch function */
 1115     if (validate_xtp_session_key(t, sv[0])) {
 1116         ret = TRUE; /* all is well, this was a valid xtp request */
 1117         if (sv[2])
 1118             dsp_match->handle_func(t, atoi(sv[1]), atoi(sv[2]),
 1119                 uri->query);
 1120         else
 1121             dsp_match->handle_func(t, atoi(sv[1]), 0, uri->query);
 1122     }
 1123 
 1124 clean:
 1125     if (uri)
 1126         soup_uri_free(uri);
 1127     if (sv)
 1128         g_strfreev(sv);
 1129 
 1130     return (ret);
 1131 }
 1132 
 1133 /*
 1134  * update all favorite tabs apart from one. Pass NULL if
 1135  * you want to update all.
 1136  */
 1137 void
 1138 update_favorite_tabs(struct tab *apart_from)
 1139 {
 1140     struct tab          *t;
 1141 
 1142     if (!updating_fl_tabs) {
 1143         updating_fl_tabs = 1; /* stop infinite recursion */
 1144         TAILQ_FOREACH(t, &tabs, entry)
 1145             if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL)
 1146                 && (t != apart_from))
 1147                 xtp_page_fl(t, NULL);
 1148         updating_fl_tabs = 0;
 1149     }
 1150 }
 1151 
 1152 /*
 1153  * update all download tabs apart from one. Pass NULL if
 1154  * you want to update all.
 1155  */
 1156 void
 1157 update_download_tabs(struct tab *apart_from)
 1158 {
 1159     struct tab          *t;
 1160 
 1161     if (!updating_dl_tabs) {
 1162         updating_dl_tabs = 1; /* stop infinite recursion */
 1163         TAILQ_FOREACH(t, &tabs, entry)
 1164             if ((t->xtp_meaning == XT_XTP_TAB_MEANING_DL)
 1165                 && (t != apart_from))
 1166                 xtp_page_dl(t, NULL);
 1167         updating_dl_tabs = 0;
 1168     }
 1169 }
 1170 
 1171 /*
 1172  * update all cookie tabs apart from one. Pass NULL if
 1173  * you want to update all.
 1174  */
 1175 void
 1176 update_cookie_tabs(struct tab *apart_from)
 1177 {
 1178     struct tab          *t;
 1179 
 1180     if (!updating_cl_tabs) {
 1181         updating_cl_tabs = 1; /* stop infinite recursion */
 1182         TAILQ_FOREACH(t, &tabs, entry)
 1183             if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL)
 1184                 && (t != apart_from))
 1185                 xtp_page_cl(t, NULL);
 1186         updating_cl_tabs = 0;
 1187     }
 1188 }
 1189 
 1190 /*
 1191  * update all history tabs apart from one. Pass NULL if
 1192  * you want to update all.
 1193  */
 1194 void
 1195 update_history_tabs(struct tab *apart_from)
 1196 {
 1197     struct tab          *t;
 1198 
 1199     if (!updating_hl_tabs) {
 1200         updating_hl_tabs = 1; /* stop infinite recursion */
 1201         TAILQ_FOREACH(t, &tabs, entry)
 1202             if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL)
 1203                 && (t != apart_from))
 1204                 xtp_page_hl(t, NULL);
 1205         updating_hl_tabs = 0;
 1206     }
 1207 }
 1208 
 1209 /*
 1210  * update all search tabs apart from one. Pass NULL if
 1211  * you want to update all.
 1212  */
 1213 void
 1214 update_search_tabs(struct tab *apart_from)
 1215 {
 1216     struct tab          *t;
 1217 
 1218     if (!updating_sl_tabs) {
 1219         updating_sl_tabs = 1; /* stop infinite recursion */
 1220         TAILQ_FOREACH(t, &tabs, entry)
 1221             if ((t->xtp_meaning == XT_XTP_TAB_MEANING_SL)
 1222                 && (t != apart_from))
 1223                 xtp_page_sl(t, NULL);
 1224         updating_sl_tabs = 0;
 1225     }
 1226 }
 1227 
 1228 int
 1229 xtp_page_ab(struct tab *t, struct karg *args)
 1230 {
 1231     char            *page, *body;
 1232 
 1233     if (t == NULL) {
 1234         show_oops(NULL, "about invalid parameters");
 1235         return (-1);
 1236     }
 1237 
 1238     generate_xtp_session_key(&t->session_key);
 1239 
 1240     body = g_strdup_printf("<b>Version: %s</b>"
 1241 #ifdef XOMBRERO_BUILDSTR
 1242         "<br><b>Build: %s</b>"
 1243 #endif
 1244         "<br><b>WebKit: %d.%d.%d</b>"
 1245         "<br><b>User Agent: %d.%d</b>"
 1246 #ifdef WEBKITGTK_API_VERSION
 1247         "<br><b>WebKit API: %.1f</b>"
 1248 #endif
 1249         "<br><b>Configuration: %s" PS "<a href='%s%d/%s/%d'>.%s</a>"
 1250         " (remember to restart the browser after any changes)</b>"
 1251         "<p>"
 1252         "Authors:"
 1253         "<ul>"
 1254         "<li>Marco Peereboom &lt;marco@peereboom.us&gt;</li>"
 1255         "<li>Stevan Andjelkovic &lt;stevan@student.chalmers.se&gt;</li>"
 1256         "<li>Edd Barrett &lt;vext01@gmail.com&gt;</li>"
 1257         "<li>Todd T. Fries &lt;todd@fries.net&gt;</li>"
 1258         "<li>Raphael Graf &lt;r@undefined.ch&gt;</li>"
 1259         "<li>Michal Mazurek &lt;akfaew@jasminek.net&gt;</li>"
 1260         "<li>Josh Rickmar &lt;jrick@devio.us&gt;</li>"
 1261         "<li>David Hill &lt;dhill@mindcry.org&gt;</li>"
 1262         "</ul>"
 1263         "Copyrights and licenses can be found on the xombrero "
 1264         "<a href=\"https://opensource.conformal.com/wiki/xombrero\">website</a>"
 1265         "</p>",
 1266 #ifdef XOMBRERO_BUILDSTR
 1267         version, XOMBRERO_BUILDSTR,
 1268 #else
 1269         version,
 1270 #endif
 1271         WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION,
 1272         WEBKIT_USER_AGENT_MAJOR_VERSION, WEBKIT_USER_AGENT_MINOR_VERSION
 1273 #ifdef WEBKITGTK_API_VERSION
 1274         ,WEBKITGTK_API_VERSION
 1275 #endif
 1276         ,pwd->pw_dir,
 1277         XT_XTP_STR,
 1278         XT_XTP_AB,
 1279         t->session_key ? t->session_key : "",
 1280         XT_XTP_AB_EDIT_CONF,
 1281         XT_CONF_FILE
 1282         );
 1283 
 1284     page = get_html_page("About", body, "", 0);
 1285     g_free(body);
 1286 
 1287     load_webkit_string(t, page, XT_URI_ABOUT_ABOUT, 0);
 1288 
 1289     g_free(page);
 1290 
 1291     return (0);
 1292 }
 1293 
 1294 /* show a list of favorites (bookmarks) */
 1295 int
 1296 xtp_page_fl(struct tab *t, struct karg *args)
 1297 {
 1298     char            file[PATH_MAX];
 1299     FILE            *f;
 1300     char            *uri = NULL, *title = NULL;
 1301     size_t          len, lineno = 0;
 1302     int         i, failed = 0;
 1303     char            *body, *tmp, *page = NULL;
 1304     const char      delim[3] = {'\\', '\\', '\0'};
 1305 
 1306     DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
 1307 
 1308     if (t == NULL) {
 1309         show_oops(NULL, "%s: bad param", __func__);
 1310         return (-1);
 1311     }
 1312 
 1313     generate_xtp_session_key(&t->session_key);
 1314 
 1315     /* open favorites */
 1316     snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_FAVS_FILE);
 1317     if ((f = fopen(file, "r")) == NULL) {
 1318         show_oops(t, "Can't open favorites file: %s", strerror(errno));
 1319         return (1);
 1320     }
 1321 
 1322     /* body */
 1323     if (args && args->i & XT_DELETE)
 1324         body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
 1325             "<th style='width: 40px'>&#35;</th><th>Link</th>"
 1326             "<th style='width: 40px'>Rm</th></tr>\n");
 1327     else
 1328         body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
 1329             "<th style='width: 40px'>&#35;</th><th>Link</th></tr>\n");
 1330 
 1331     for (i = 1;;) {
 1332         if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
 1333             break;
 1334         if (strlen(title) == 0) {
 1335             free(title);
 1336             title = NULL;
 1337             continue;
 1338         }
 1339 
 1340         if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
 1341             if (feof(f) || ferror(f)) {
 1342                 show_oops(t, "favorites file corrupt");
 1343                 failed = 1;
 1344                 break;
 1345             }
 1346 
 1347         tmp = body;
 1348         if (args && args->i & XT_DELETE)
 1349             body = g_strdup_printf("%s<tr>"
 1350                 "<td>%d</td>"
 1351                 "<td><a href='%s'>%s</a></td>"
 1352                 "<td style='text-align: center'>"
 1353                 "<a href='%s%d/%s/%d/%d'>X</a></td>"
 1354                 "</tr>\n",
 1355                 body, i, uri, title,
 1356                 XT_XTP_STR, XT_XTP_FL,
 1357                 t->session_key ? t->session_key : "",
 1358                 XT_XTP_FL_REMOVE, i);
 1359         else
 1360             body = g_strdup_printf("%s<tr>"
 1361                 "<td>%d</td>"
 1362                 "<td><a href='%s'>%s</a></td>"
 1363                 "</tr>\n",
 1364                 body, i, uri, title);
 1365         g_free(tmp);
 1366 
 1367         free(uri);
 1368         uri = NULL;
 1369         free(title);
 1370         title = NULL;
 1371         i++;
 1372     }
 1373     fclose(f);
 1374 
 1375     /* if none, say so */
 1376     if (i == 1) {
 1377         tmp = body;
 1378         body = g_strdup_printf("%s<tr>"
 1379             "<td colspan='%d' style='text-align: center'>"
 1380             "No favorites - To add one use the 'favadd' command."
 1381             "</td></tr>", body, (args && args->i & XT_DELETE) ? 3 : 2);
 1382         g_free(tmp);
 1383     }
 1384 
 1385     tmp = body;
 1386     body = g_strdup_printf("%s</table>", body);
 1387     g_free(tmp);
 1388 
 1389     if (uri)
 1390         free(uri);
 1391     if (title)
 1392         free(title);
 1393 
 1394     /* render */
 1395     if (!failed) {
 1396         page = get_html_page("Favorites", body, "", 1);
 1397         load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES, 0);
 1398         g_free(page);
 1399     }
 1400 
 1401     update_favorite_tabs(t);
 1402 
 1403     if (body)
 1404         g_free(body);
 1405 
 1406     return (failed);
 1407 }
 1408 
 1409 /*
 1410  * Return a new string with a download row (in html)
 1411  * appended. Old string is freed.
 1412  */
 1413 char *
 1414 xtp_page_dl_row(struct tab *t, char *html, struct download *dl)
 1415 {
 1416 
 1417     WebKitDownloadStatus    stat;
 1418     const gchar     *destination;
 1419     gchar           *d;
 1420     char            *status_html = NULL, *cmd_html = NULL, *new_html;
 1421     gdouble         progress;
 1422     char            cur_sz[FMT_SCALED_STRSIZE];
 1423     char            tot_sz[FMT_SCALED_STRSIZE];
 1424     char            *xtp_prefix;
 1425 
 1426     DNPRINTF(XT_D_DOWNLOAD, "%s: dl->id %d\n", __func__, dl->id);
 1427 
 1428     /* All actions wil take this form:
 1429      * xxxt://class/seskey
 1430      */
 1431     xtp_prefix = g_strdup_printf("%s%d/%s/",
 1432         XT_XTP_STR, XT_XTP_DL, t->session_key);
 1433 
 1434     stat = webkit_download_get_status(dl->download);
 1435 
 1436     switch (stat) {
 1437     case WEBKIT_DOWNLOAD_STATUS_FINISHED:
 1438         status_html = g_strdup_printf("Finished");
 1439         cmd_html = g_strdup_printf(
 1440             "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
 1441             xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
 1442             XT_XTP_DL_UNLINK, dl->id);
 1443         break;
 1444     case WEBKIT_DOWNLOAD_STATUS_STARTED:
 1445         /* gather size info */
 1446         progress = 100 * webkit_download_get_progress(dl->download);
 1447 
 1448         fmt_scaled(
 1449             webkit_download_get_current_size(dl->download), cur_sz);
 1450         fmt_scaled(
 1451             webkit_download_get_total_size(dl->download), tot_sz);
 1452 
 1453         status_html = g_strdup_printf(
 1454             "<div style='width: 100%%' align='center'>"
 1455             "<div class='progress-outer'>"
 1456             "<div class='progress-inner' style='width: %.2f%%'>"
 1457             "</div></div></div>"
 1458             "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
 1459             progress, cur_sz, tot_sz, progress);
 1460 
 1461         cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
 1462             xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
 1463 
 1464         break;
 1465         /* LLL */
 1466     case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
 1467         status_html = g_strdup_printf("Cancelled");
 1468         cmd_html = g_strdup_printf(
 1469             "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
 1470             xtp_prefix, XT_XTP_DL_START, dl->id,
 1471             xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
 1472             XT_XTP_DL_UNLINK, dl->id);
 1473         break;
 1474     case WEBKIT_DOWNLOAD_STATUS_ERROR:
 1475         status_html = g_strdup_printf("Error!");
 1476         cmd_html = g_strdup_printf(
 1477             "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
 1478             xtp_prefix, XT_XTP_DL_START, dl->id,
 1479             xtp_prefix, XT_XTP_DL_REMOVE, dl->id, xtp_prefix,
 1480             XT_XTP_DL_UNLINK, dl->id);
 1481         break;
 1482     case WEBKIT_DOWNLOAD_STATUS_CREATED:
 1483         cmd_html = g_strdup_printf("<a href='%s%d/%d'>Start</a> / <a href='%s%d/%d'>Cancel</a>",
 1484             xtp_prefix, XT_XTP_DL_START, dl->id, xtp_prefix,
 1485             XT_XTP_DL_CANCEL, dl->id);
 1486         status_html = g_strdup_printf("Created");
 1487         break;
 1488     default:
 1489         show_oops(t, "%s: unknown download status", __func__);
 1490     };
 1491 
 1492     destination = webkit_download_get_destination_uri(dl->download);
 1493     /* we might not have a destination set yet */
 1494     if (!destination)
 1495         destination = webkit_download_get_suggested_filename(dl->download);
 1496     d = g_strdup(destination);  /* copy for basename */
 1497     new_html = g_strdup_printf(
 1498         "%s\n<tr><td>%s</td><td>%s</td>"
 1499         "<td style='text-align:center'>%s</td></tr>\n",
 1500         html, basename(d), status_html, cmd_html);
 1501     g_free(d);
 1502     g_free(html);
 1503 
 1504     if (status_html)
 1505         g_free(status_html);
 1506 
 1507     if (cmd_html)
 1508         g_free(cmd_html);
 1509 
 1510     g_free(xtp_prefix);
 1511 
 1512     return new_html;
 1513 }
 1514 
 1515 /* cookie management XTP page */
 1516 int
 1517 xtp_page_cl(struct tab *t, struct karg *args)
 1518 {
 1519     char            *body, *page, *tmp;
 1520     int         i = 1; /* all ids start 1 */
 1521     int         domain_id = 0;
 1522     GSList          *sc, *pc, *pc_start;
 1523     SoupCookie      *c;
 1524     char            *type, *table_headers, *last_domain;
 1525 
 1526     DNPRINTF(XT_D_CMD, "%s", __func__);
 1527 
 1528     if (t == NULL) {
 1529         show_oops(NULL, "%s invalid parameters", __func__);
 1530         return (1);
 1531     }
 1532 
 1533     generate_xtp_session_key(&t->session_key);
 1534 
 1535     /* table headers */
 1536     table_headers = g_strdup_printf("<table><tr>"
 1537         "<th>Type</th>"
 1538         "<th>Name</th>"
 1539         "<th style='width:200px'>Value</th>"
 1540         "<th>Path</th>"
 1541         "<th>Expires</th>"
 1542         "<th>Secure</th>"
 1543         "<th>HTTP<br />only</th>"
 1544         "<th style='width:40px'>Rm</th></tr>\n");
 1545 
 1546     sc = soup_cookie_jar_all_cookies(s_cookiejar);
 1547     pc = soup_cookie_jar_all_cookies(p_cookiejar);
 1548     pc_start = pc;
 1549 
 1550     body = g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
 1551         "[ Remove All Cookies From All Domains ]</a></div>\n",
 1552         XT_XTP_STR, XT_XTP_CL, t->session_key, XT_XTP_CL_REMOVE_ALL);
 1553 
 1554     last_domain = g_strdup("");
 1555     for (; sc; sc = sc->next) {
 1556         c = sc->data;
 1557 
 1558         if (strcmp(last_domain, c->domain) != 0) {
 1559             /* new domain */
 1560             domain_id ++;
 1561             g_free(last_domain);
 1562             last_domain = g_strdup(c->domain);
 1563 
 1564             if (body != NULL) {
 1565                 tmp = body;
 1566                 body = g_strdup_printf("%s</table>"
 1567                     "<h2>%s</h2><div align=\"center\">"
 1568                     "<a href='%s%d/%s/%d/%d'>"
 1569                     "[ Remove All From This Domain ]"
 1570                     "</a></div>%s\n",
 1571                     body, c->domain,
 1572                     XT_XTP_STR, XT_XTP_CL, t->session_key,
 1573                     XT_XTP_CL_REMOVE_DOMAIN, domain_id,
 1574                     table_headers);
 1575                 g_free(tmp);
 1576             } else {
 1577                 /* first domain */
 1578                 body = g_strdup_printf("<h2>%s</h2>"
 1579                     "<div align=\"center\">"
 1580                     "<a href='%s%d/%s/%d/%d'>"
 1581                     "[ Remove All From This Domain ]</a></div>%s\n",
 1582                     c->domain, XT_XTP_STR, XT_XTP_CL,
 1583                     t->session_key, XT_XTP_CL_REMOVE_DOMAIN,
 1584                     domain_id, table_headers);
 1585             }
 1586         }
 1587 
 1588         type = "Session";
 1589         for (pc = pc_start; pc; pc = pc->next)
 1590             if (soup_cookie_equal(pc->data, c)) {
 1591                 type = "Session + Persistent";
 1592                 break;
 1593             }
 1594 
 1595         tmp = body;
 1596         body = g_strdup_printf(
 1597             "%s\n<tr>"
 1598             "<td>%s</td>"
 1599             "<td style='word-wrap:normal'>%s</td>"
 1600             "<td>"
 1601             "  <textarea rows='4'>%s</textarea>"
 1602             "</td>"
 1603             "<td>%s</td>"
 1604             "<td>%s</td>"
 1605             "<td>%d</td>"
 1606             "<td>%d</td>"
 1607             "<td style='text-align:center'>"
 1608             "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
 1609             body,
 1610             type,
 1611             c->name,
 1612             c->value,
 1613             c->path,
 1614             c->expires ?
 1615                 soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
 1616             c->secure,
 1617             c->http_only,
 1618 
 1619             XT_XTP_STR,
 1620             XT_XTP_CL,
 1621             t->session_key,
 1622             XT_XTP_CL_REMOVE,
 1623             i
 1624             );
 1625 
 1626         g_free(tmp);
 1627         i++;
 1628     }
 1629 
 1630     soup_cookies_free(sc);
 1631     soup_cookies_free(pc);
 1632 
 1633     /* small message if there are none */
 1634     if (i == 1) {
 1635         body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
 1636             "colspan='8'>No Cookies</td></tr>\n", table_headers);
 1637     }
 1638     tmp = body;
 1639     body = g_strdup_printf("%s</table>", body);
 1640     g_free(tmp);
 1641 
 1642     page = get_html_page("Cookie Jar", body, "", TRUE);
 1643     g_free(body);
 1644     g_free(table_headers);
 1645     g_free(last_domain);
 1646 
 1647     load_webkit_string(t, page, XT_URI_ABOUT_COOKIEJAR, 0);
 1648     update_cookie_tabs(t);
 1649 
 1650     g_free(page);
 1651 
 1652     return (0);
 1653 }
 1654 
 1655 int
 1656 xtp_page_hl(struct tab *t, struct karg *args)
 1657 {
 1658     char            *body, *page, *tmp;
 1659     struct history      *h;
 1660     int         i = 1; /* all ids start 1 */
 1661 
 1662     DNPRINTF(XT_D_CMD, "%s", __func__);
 1663 
 1664     if (t == NULL) {
 1665         show_oops(NULL, "%s invalid parameters", __func__);
 1666         return (1);
 1667     }
 1668 
 1669     generate_xtp_session_key(&t->session_key);
 1670 
 1671     /* body */
 1672     body = g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
 1673         "[ Remove All ]</a></div>"
 1674         "<table style='table-layout:fixed'><tr>"
 1675         "<th>URI</th><th>Title</th><th>Last visited</th>"
 1676         "<th style='width: 40px'>Rm</th></tr>\n",
 1677         XT_XTP_STR, XT_XTP_HL, t->session_key, XT_XTP_HL_REMOVE_ALL);
 1678 
 1679     RB_FOREACH_REVERSE(h, history_list, &hl) {
 1680         tmp = body;
 1681         body = g_strdup_printf(
 1682             "%s\n<tr>"
 1683             "<td><a href='%s'>%s</a></td>"
 1684             "<td>%s</td>"
 1685             "<td>%s</td>"
 1686             "<td style='text-align: center'>"
 1687             "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
 1688             body, h->uri, h->uri, h->title, ctime(&h->time),
 1689             XT_XTP_STR, XT_XTP_HL, t->session_key,
 1690             XT_XTP_HL_REMOVE, i);
 1691 
 1692         g_free(tmp);
 1693         i++;
 1694     }
 1695 
 1696     /* small message if there are none */
 1697     if (i == 1) {
 1698         tmp = body;
 1699         body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
 1700             "colspan='4'>No History</td></tr>\n", body);
 1701         g_free(tmp);
 1702     }
 1703 
 1704     tmp = body;
 1705     body = g_strdup_printf("%s</table>", body);
 1706     g_free(tmp);
 1707 
 1708     page = get_html_page("History", body, "", TRUE);
 1709     g_free(body);
 1710 
 1711     /*
 1712      * update all history manager tabs as the xtp session
 1713      * key has now changed. No need to update the current tab.
 1714      * Already did that above.
 1715      */
 1716     update_history_tabs(t);
 1717 
 1718     load_webkit_string(t, page, XT_URI_ABOUT_HISTORY, 0);
 1719     g_free(page);
 1720 
 1721     return (0);
 1722 }
 1723 
 1724 /*
 1725  * Generate a web page detailing the status of any downloads
 1726  */
 1727 int
 1728 xtp_page_dl(struct tab *t, struct karg *args)
 1729 {
 1730     struct download     *dl;
 1731     char            *body, *page, *tmp;
 1732     char            *ref;
 1733     int         n_dl = 1;
 1734 
 1735     DNPRINTF(XT_D_DOWNLOAD, "%s", __func__);
 1736 
 1737     if (t == NULL) {
 1738         show_oops(NULL, "%s invalid parameters", __func__);
 1739         return (1);
 1740     }
 1741 
 1742     generate_xtp_session_key(&t->session_key);
 1743 
 1744     /* header - with refresh so as to update */
 1745     if (refresh_interval >= 1)
 1746         ref = g_strdup_printf(
 1747             "<meta http-equiv='refresh' content='%u"
 1748             ";url=%s%d/%s/%d' />\n",
 1749             refresh_interval,
 1750             XT_XTP_STR,
 1751             XT_XTP_DL,
 1752             t->session_key,
 1753             XT_XTP_DL_LIST);
 1754     else
 1755         ref = g_strdup("");
 1756 
 1757     body = g_strdup_printf("<div align='center'>"
 1758         "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
 1759         "</p><table><tr><th style='width: 60%%'>"
 1760         "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
 1761         XT_XTP_STR, XT_XTP_DL, t->session_key, XT_XTP_DL_LIST);
 1762 
 1763     RB_FOREACH_REVERSE(dl, download_list, &downloads) {
 1764         body = xtp_page_dl_row(t, body, dl);
 1765         n_dl++;
 1766     }
 1767 
 1768     /* message if no downloads in list */
 1769     if (n_dl == 1) {
 1770         tmp = body;
 1771         body = g_strdup_printf("%s\n<tr><td colspan='3'"
 1772             " style='text-align: center'>"
 1773             "No downloads</td></tr>\n", body);
 1774         g_free(tmp);
 1775     }
 1776 
 1777     tmp = body;
 1778     body = g_strdup_printf("%s</table></div>", body);
 1779     g_free(tmp);
 1780 
 1781     page = get_html_page("Downloads", body, ref, 1);
 1782     g_free(ref);
 1783     g_free(body);
 1784 
 1785     /*
 1786      * update all download manager tabs as the xtp session
 1787      * key has now changed. No need to update the current tab.
 1788      * Already did that above.
 1789      */
 1790     update_download_tabs(t);
 1791 
 1792     load_webkit_string(t, page, XT_URI_ABOUT_DOWNLOADS, 0);
 1793     g_free(page);
 1794 
 1795     return (0);
 1796 }
 1797 
 1798 int
 1799 xtp_page_sl(struct tab *t, struct karg *args)
 1800 {
 1801     int         i;
 1802     char            *page, *body, *tmp;
 1803 
 1804     DNPRINTF(XT_D_SEARCH, "%s", __func__);
 1805 
 1806     generate_xtp_session_key(&t->session_key);
 1807 
 1808     if (t == NULL) {
 1809         show_oops(NULL, "%s invalid parameters", __func__);
 1810         return (1);
 1811     }
 1812 
 1813     body = g_strdup_printf("<p>The xombrero authors will not choose a "
 1814         "default search engine for you.  What follows is a list of search "
 1815         "engines (in no particular order) you may be interested in.  "
 1816         "To permanently choose a search engine, click [ Select ] to save "
 1817         "<tt>search_string</tt> as a runtime setting, or set "
 1818         "<tt>search_string</tt> to the appropriate URL in your xombrero "
 1819         "configuration.</p>");
 1820 
 1821     tmp = body;
 1822     body = g_strdup_printf("%s\n<table style='table-layout:fixed'><tr>"
 1823         "<th style='width: 200px'>Name</th><th>URL</th>"
 1824         "<th style='width: 100px'>Select</th></tr>\n", body);
 1825     g_free(tmp);
 1826 
 1827     for (i = 0; i < (sizeof search_list / sizeof (struct search_type)); ++i)
 1828         body = search_engine_add(body, search_list[i].name,
 1829             search_list[i].url, t->session_key, i);
 1830 
 1831     tmp = body;
 1832     body = g_strdup_printf("%s</table>", body);
 1833     g_free(tmp);
 1834 
 1835     page = get_html_page("Choose a search engine", body, "", 1);
 1836     g_free(body);
 1837 
 1838     /*
 1839      * update all search tabs as the xtp session key has now changed. No
 1840      * need to update the current tab. Already did that above.
 1841      */
 1842     update_search_tabs(t);
 1843 
 1844     load_webkit_string(t, page, XT_URI_ABOUT_SEARCH, 0);
 1845     g_free(page);
 1846 
 1847     return (0);
 1848 }
 1849 
 1850 int
 1851 xtp_page_sv(struct tab *t, struct karg *args)
 1852 {
 1853     SoupURI         *soupuri;
 1854     static int      arg = 0;
 1855     struct secviolation find, *sv;
 1856     char            *page, *body;
 1857 
 1858     if (t == NULL) {
 1859         show_oops(NULL, "secviolation invalid parameters");
 1860         return (-1);
 1861     }
 1862 
 1863     generate_xtp_session_key(&t->session_key);
 1864 
 1865     if (args == NULL) {
 1866         find.xtp_arg = t->xtp_arg;
 1867         sv = RB_FIND(secviolation_list, &svl, &find);
 1868         if (sv == NULL)
 1869             return (-1);
 1870     } else {
 1871         sv = g_malloc(sizeof(struct secviolation));
 1872         sv->xtp_arg = ++arg;
 1873         t->xtp_arg = arg;
 1874         sv->t = t;
 1875         sv->uri = args->s;
 1876         RB_INSERT(secviolation_list, &svl, sv);
 1877     }
 1878 
 1879     if (sv->uri == NULL || (soupuri = soup_uri_new(sv->uri)) == NULL)
 1880         return (-1);
 1881 
 1882     body = g_strdup_printf(
 1883         "<p><b>You tried to access %s</b>."
 1884         "<p><b>The site's security certificate has been modified.</b>"
 1885         "<p>The domain of the page you have tried to access, <b>%s</b>, "
 1886         "has a different remote certificate then the local cached version "
 1887         "from a previous visit.  As a security precaution to help prevent "
 1888         "against man-in-the-middle attacks, please choose one of the "
 1889         "following actions to continue, or disable the "
 1890         "<tt>warn_cert_changes</tt> setting in your xombrero "
 1891         "configuration."
 1892         "<p><b>Choose an action:"
 1893         "<br><a href='%s%d/%s/%d/%d'>Allow for this session</a>"
 1894         "<br><a href='%s%d/%s/%d/%d'>Cache new certificate</a>"
 1895         "<br><a href='%s%d/%s/%d/%d'>Show cached certificate</a>"
 1896         "<br><a href='%s%d/%s/%d/%d'>Show new certificate</a>",
 1897         sv->uri,
 1898         soupuri->host,
 1899         XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_ALLOW_SESSION,
 1900         sv->xtp_arg,
 1901         XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_CACHE,
 1902         sv->xtp_arg,
 1903         XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_SHOW_CACHED_CERT,
 1904         sv->xtp_arg,
 1905         XT_XTP_STR, XT_XTP_SV, t->session_key, XT_XTP_SV_SHOW_NEW_CERT,
 1906         sv->xtp_arg);
 1907 
 1908     page = get_html_page("Security Violation", body, "", 0);
 1909     g_free(body);
 1910 
 1911     load_webkit_string(t, page, XT_URI_ABOUT_SECVIOLATION, 1);
 1912 
 1913     g_free(page);
 1914     if (soupuri)
 1915         soup_uri_free(soupuri);
 1916 
 1917     return (0);
 1918 }
 1919 
 1920 int
 1921 startpage(struct tab *t, struct karg *args)
 1922 {
 1923     char            *page, *body, *b;
 1924     struct sp       *s;
 1925 
 1926     if (t == NULL)
 1927         show_oops(NULL, "startpage invalid parameters");
 1928 
 1929     body = g_strdup_printf("<b>Startup Exception(s):</b><p>");
 1930 
 1931     TAILQ_FOREACH(s, &spl, entry) {
 1932         b = body;
 1933         body = g_strdup_printf("%s%s<br>", body, s->line);
 1934         g_free(b);
 1935     }
 1936 
 1937     page = get_html_page("Startup Exception", body, "", 0);
 1938     g_free(body);
 1939 
 1940     load_webkit_string(t, page, XT_URI_ABOUT_STARTPAGE, 0);
 1941     g_free(page);
 1942 
 1943     return (0);
 1944 }
 1945 
 1946 void
 1947 startpage_add(const char *fmt, ...)
 1948 {
 1949     va_list         ap;
 1950     char            *msg;
 1951     struct sp       *s;
 1952 
 1953     if (fmt == NULL)
 1954         return;
 1955 
 1956     va_start(ap, fmt);
 1957     if ((msg = g_strdup_vprintf(fmt, ap)) == NULL)
 1958         errx(1, "startpage_add failed");
 1959     va_end(ap);
 1960 
 1961     s = g_malloc0(sizeof *s);
 1962     s->line = msg;
 1963 
 1964     TAILQ_INSERT_TAIL(&spl, s, entry);
 1965 }
 1966 gchar *show_g_object_settings(GObject *, char *, int);
 1967 
 1968 char *
 1969 xt_g_object_serialize(GValue *value, const gchar *tname, char *str, int recurse)
 1970 {
 1971     int     typeno = 0;
 1972     char        *valstr, *tmpstr, *tmpsettings;
 1973     GObject     *object;
 1974 
 1975     typeno = G_TYPE_FUNDAMENTAL( G_VALUE_TYPE(value) );
 1976     switch ( typeno ) {
 1977     case G_TYPE_ENUM:
 1978         valstr = g_strdup_printf("%d",
 1979             g_value_get_enum(value));
 1980         break;
 1981     case G_TYPE_CHAR:
 1982         valstr = g_strdup_printf("%c",
 1983 #if GLIB_CHECK_VERSION(2, 32, 0)
 1984             g_value_get_schar(value));
 1985 #else
 1986             g_value_get_char(value));
 1987 #endif
 1988         break;
 1989     case G_TYPE_UCHAR:
 1990         valstr = g_strdup_printf("%c",
 1991             g_value_get_uchar(value));
 1992         break;
 1993     case G_TYPE_LONG:
 1994         valstr = g_strdup_printf("%ld",
 1995             g_value_get_long(value));
 1996         break;
 1997     case G_TYPE_ULONG:
 1998         valstr = g_strdup_printf("%ld",
 1999             g_value_get_ulong(value));
 2000         break;
 2001     case G_TYPE_INT:
 2002         valstr = g_strdup_printf("%d",
 2003             g_value_get_int(value));
 2004         break;
 2005     case G_TYPE_INT64:
 2006         valstr = g_strdup_printf("%" PRIo64,
 2007             (int64_t) g_value_get_int64(value));
 2008         break;
 2009     case G_TYPE_UINT:
 2010         valstr = g_strdup_printf("%d",
 2011             g_value_get_uint(value));
 2012         break;
 2013     case G_TYPE_UINT64:
 2014         valstr = g_strdup_printf("%" PRIu64,
 2015             (uint64_t) g_value_get_uint64(value));
 2016         break;
 2017     case G_TYPE_FLAGS:
 2018         valstr = g_strdup_printf("0x%x",
 2019             g_value_get_flags(value));
 2020         break;
 2021     case G_TYPE_BOOLEAN:
 2022         valstr = g_strdup_printf("%s",
 2023             g_value_get_boolean(value) ? "TRUE" : "FALSE");
 2024         break;
 2025     case G_TYPE_FLOAT:
 2026         valstr = g_strdup_printf("%f",
 2027             g_value_get_float(value));
 2028         break;
 2029     case G_TYPE_DOUBLE:
 2030         valstr = g_strdup_printf("%f",
 2031             g_value_get_double(value));
 2032         break;
 2033     case G_TYPE_STRING:
 2034         valstr = g_strdup_printf("\"%s\"",
 2035             g_value_get_string(value));
 2036         break;
 2037     case G_TYPE_POINTER:
 2038         valstr = g_strdup_printf("%p",
 2039             g_value_get_pointer(value));
 2040         break;
 2041     case G_TYPE_OBJECT:
 2042         object = g_value_get_object(value);
 2043         if (object != NULL) {
 2044             if (recurse) {
 2045                 tmpstr = g_strdup_printf("%s     ", str);
 2046                 tmpsettings = show_g_object_settings( object,
 2047                     tmpstr, recurse);
 2048                 g_free(tmpstr);
 2049 
 2050                 if (strrchr(tmpsettings, '\n') != NULL) {
 2051                     valstr = g_strdup_printf("%s%s      }",
 2052                         tmpsettings, str);
 2053                     g_free(tmpsettings);
 2054                 } else {
 2055                     valstr = tmpsettings;
 2056                 }
 2057             } else {
 2058                 valstr = g_strdup_printf("<...>");
 2059             }
 2060         } else {
 2061             valstr = g_strdup_printf("settings[] = NULL");
 2062         }
 2063         break;
 2064     default:
 2065         valstr = g_strdup_printf("type %s unhandled", tname);
 2066     }
 2067     return valstr;
 2068 }
 2069 
 2070 gchar *
 2071 show_g_object_settings(GObject *o, char *str, int recurse)
 2072 {
 2073     char        *b, *p, *body, *valstr, *tmpstr;
 2074     guint       n_props = 0;
 2075     int     i, typeno = 0;
 2076     GParamSpec  *pspec;
 2077     const gchar *tname;
 2078     GValue      value;
 2079     GParamSpec  **proplist;
 2080     const gchar *name;
 2081 
 2082     if (!G_IS_OBJECT(o)) {
 2083         fprintf(stderr, "%s is not a g_object\n", str);
 2084         return g_strdup("");
 2085     }
 2086     proplist = g_object_class_list_properties(
 2087         G_OBJECT_GET_CLASS(o), &n_props);
 2088 
 2089     if (GTK_IS_WIDGET(o)) {
 2090         name = gtk_widget_get_name(GTK_WIDGET(o));
 2091     } else {
 2092         name = "settings";
 2093     }
 2094     if (n_props == 0) {
 2095         body = g_strdup_printf("%s[0] = { }", name);
 2096         goto end_show_g_objects;
 2097     }
 2098 
 2099     body = g_strdup_printf("%s[%d] = {\n", name, n_props);
 2100     for (i=0; i < n_props; i++) {
 2101         pspec = proplist[i];
 2102         tname = G_OBJECT_TYPE_NAME(pspec);
 2103         bzero(&value, sizeof value);
 2104         valstr = NULL;
 2105 
 2106         if (!(pspec->flags & G_PARAM_READABLE))
 2107             valstr = g_strdup_printf("not a readable property");
 2108         else {
 2109             g_value_init(&value, G_PARAM_SPEC_VALUE_TYPE(pspec));
 2110             g_object_get_property(G_OBJECT(o), pspec->name,
 2111                 &value);
 2112             typeno = G_TYPE_FUNDAMENTAL( G_VALUE_TYPE(&value) );
 2113         }
 2114 
 2115         /* based on the type, recurse and display values */
 2116         if (valstr == NULL) {
 2117             valstr = xt_g_object_serialize(&value, tname, str,
 2118                 recurse);
 2119         }
 2120 
 2121         tmpstr = g_strdup_printf("%-13s %s%s%s,", tname, pspec->name,
 2122             (typeno == G_TYPE_OBJECT) ? "." : " = ", valstr);
 2123         b = body;
 2124 
 2125 #define XT_G_OBJECT_SPACING     50
 2126         p = strrchr(tmpstr, '\n');
 2127         if (p == NULL && strlen(tmpstr) > XT_G_OBJECT_SPACING) {
 2128             body = g_strdup_printf(
 2129                 "%s%s    %-50s\n%s    %50s /* %3d flags=0x%08x */\n",
 2130                 body, str, tmpstr, str, "", i, pspec->flags);
 2131         } else {
 2132             char *fmt;
 2133             int strspaces;
 2134             if (p == NULL)
 2135                 strspaces = XT_G_OBJECT_SPACING;
 2136             else
 2137                 strspaces = strlen(tmpstr) - (strlen(p) - strlen(str)) + XT_G_OBJECT_SPACING + 5;
 2138             fmt = g_strdup_printf("%%s%%s    %%-%ds /* %%3d flags=0x%%08x */\n", strspaces);
 2139             body = g_strdup_printf(fmt, body, str, tmpstr, i, pspec->flags);
 2140             g_free(fmt);
 2141         }
 2142         g_free(tmpstr);
 2143         g_free(b);
 2144         g_free(valstr);
 2145     }
 2146 end_show_g_objects:
 2147     g_free(proplist);
 2148     return (body);
 2149 }
 2150 
 2151 char *
 2152 xt_append_settings(char *str, GObject *object, char *name, int recurse)
 2153 {
 2154     char *newstr, *settings;
 2155 
 2156     settings = show_g_object_settings(object, name, recurse);
 2157     if (str == NULL)
 2158         str = g_strdup("");
 2159 
 2160     newstr = g_strdup_printf("%s%s %s%s };\n", str, name, settings, name);
 2161     g_free(str);
 2162 
 2163     return newstr;
 2164 }
 2165 
 2166 int
 2167 about_webkit(struct tab *t, struct karg *arg)
 2168 {
 2169     char            *page, *body, *settingstr;
 2170 
 2171     settingstr = xt_append_settings(NULL, G_OBJECT(t->settings),
 2172         "t->settings", 0);
 2173     body = g_strdup_printf("<pre>%s</pre>\n", settingstr);
 2174     g_free(settingstr);
 2175 
 2176     page = get_html_page("About Webkit", body, "", 0);
 2177     g_free(body);
 2178 
 2179     load_webkit_string(t, page, XT_URI_ABOUT_WEBKIT, 0);
 2180     g_free(page);
 2181 
 2182     return (0);
 2183 }
 2184 
 2185 static int      toplevelcount = 0;
 2186 
 2187 void
 2188 xt_append_toplevel(GtkWindow *w, char **body)
 2189 {
 2190     char            *n;
 2191 
 2192     n = g_strdup_printf("toplevel#%d", toplevelcount++);
 2193     *body = xt_append_settings(*body, G_OBJECT(w), n, 0);
 2194     g_free(n);
 2195 }
 2196 
 2197 int
 2198 allthethings(struct tab *t, struct karg *arg)
 2199 {
 2200     GList           *list;
 2201     char            *page, *body, *b;
 2202 
 2203     body = xt_append_settings(NULL, G_OBJECT(t->wv), "t->wv", 1);
 2204     body = xt_append_settings(body, G_OBJECT(t->inspector),
 2205         "t->inspector", 1);
 2206 #if 0 /* not until warnings are gone */
 2207     body = xt_append_settings(body, G_OBJECT(session),
 2208         "session", 1);
 2209 #endif
 2210     toplevelcount = 0;
 2211     list = gtk_window_list_toplevels();
 2212     g_list_foreach(list, (GFunc)g_object_ref, NULL);
 2213     g_list_foreach(list, (GFunc)xt_append_toplevel, &body);
 2214     g_list_foreach(list, (GFunc)g_object_unref, NULL);
 2215     g_list_free(list);
 2216         
 2217     b = body;
 2218     body = g_strdup_printf("<pre>%scan paste clipboard = %d\n</pre>", body,
 2219         webkit_web_view_can_paste_clipboard(t->wv));
 2220     g_free(b);
 2221 
 2222     page = get_html_page("About All The Things _o/", body, "", 0);
 2223     g_free(body);
 2224 
 2225     load_webkit_string(t, page, XT_URI_ABOUT_ALLTHETHINGS, 0);
 2226     g_free(page);
 2227 
 2228     return (0);
 2229 }