Move xtp and about stuff into it's own file
[xxxterm.git] / about.c
blob8d84fa934c7fbad0b98902c41bd8bfba6ac9b916
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 Edd Barrett <vext01@gmail.com>
5 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
6 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
7 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 #include "xxxterm.h"
25 * xxxterm "protocol" (xtp)
26 * We use this for managing stuff like downloads and favorites. They
27 * make magical HTML pages in memory which have xxxt:// links in order
28 * to communicate with xxxterm's internals. These links take the format:
29 * xxxt://class/session_key/action/arg
31 * Don't begin xtp class/actions as 0. atoi returns that on error.
33 * Typically we have not put addition of items in this framework, as
34 * adding items is either done via an ex-command or via a keybinding instead.
37 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
38 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
39 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
40 "td{overflow: hidden;" \
41 " padding: 2px 2px 2px 2px;" \
42 " border: 1px solid black;" \
43 " vertical-align:top;" \
44 " word-wrap: break-word}\n" \
45 "tr:hover{background: #ffff99}\n" \
46 "th{background-color: #cccccc;" \
47 " border: 1px solid black}\n" \
48 "table{width: 100%%;" \
49 " border: 1px black solid;" \
50 " border-collapse:collapse}\n" \
51 ".progress-outer{" \
52 "border: 1px solid black;" \
53 " height: 8px;" \
54 " width: 90%%}\n" \
55 ".progress-inner{float: left;" \
56 " height: 8px;" \
57 " background: green}\n" \
58 ".dlstatus{font-size: small;" \
59 " text-align: center}\n" \
60 "</style>\n"
62 /* XTP classes (xxxt://<class>) */
63 #define XT_XTP_INVALID (0) /* invalid */
64 #define XT_XTP_DL (1) /* downloads */
65 #define XT_XTP_HL (2) /* history */
66 #define XT_XTP_CL (3) /* cookies */
67 #define XT_XTP_FL (4) /* favorites */
69 /* XTP download actions */
70 #define XT_XTP_DL_LIST (1)
71 #define XT_XTP_DL_CANCEL (2)
72 #define XT_XTP_DL_REMOVE (3)
74 /* XTP history actions */
75 #define XT_XTP_HL_LIST (1)
76 #define XT_XTP_HL_REMOVE (2)
78 /* XTP cookie actions */
79 #define XT_XTP_CL_LIST (1)
80 #define XT_XTP_CL_REMOVE (2)
82 /* XTP cookie actions */
83 #define XT_XTP_FL_LIST (1)
84 #define XT_XTP_FL_REMOVE (2)
86 int ca_cmd(struct tab *, struct karg *);
87 int cookie_show_wl(struct tab *, struct karg *);
88 int js_show_wl(struct tab *, struct karg *);
89 int pl_show_wl(struct tab *, struct karg *);
90 int set(struct tab *, struct karg *);
91 int marco(struct tab *, struct karg *);
92 int startpage(struct tab *, struct karg *);
93 const char * marco_message(int *);
95 struct about_type about_list[] = {
96 { XT_URI_ABOUT_ABOUT, about },
97 { XT_URI_ABOUT_BLANK, blank },
98 { XT_URI_ABOUT_CERTS, ca_cmd },
99 { XT_URI_ABOUT_COOKIEWL, cookie_show_wl },
100 { XT_URI_ABOUT_COOKIEJAR, xtp_page_cl },
101 { XT_URI_ABOUT_DOWNLOADS, xtp_page_dl },
102 { XT_URI_ABOUT_FAVORITES, xtp_page_fl },
103 { XT_URI_ABOUT_HELP, help },
104 { XT_URI_ABOUT_HISTORY, xtp_page_hl },
105 { XT_URI_ABOUT_JSWL, js_show_wl },
106 { XT_URI_ABOUT_SET, set },
107 { XT_URI_ABOUT_STATS, stats },
108 { XT_URI_ABOUT_MARCO, marco },
109 { XT_URI_ABOUT_STARTPAGE, startpage },
110 { XT_URI_ABOUT_PLUGINWL, pl_show_wl },
114 * Session IDs.
115 * We use these to prevent people putting xxxt:// URLs on
116 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
118 #define XT_XTP_SES_KEY_SZ 8
119 #define XT_XTP_SES_KEY_HEX_FMT \
120 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
121 char *dl_session_key; /* downloads */
122 char *hl_session_key; /* history list */
123 char *cl_session_key; /* cookie list */
124 char *fl_session_key; /* favorites list */
126 int updating_fl_tabs = 0;
127 int updating_dl_tabs = 0;
128 int updating_hl_tabs = 0;
129 int updating_cl_tabs = 0;
130 struct download_list downloads;
132 size_t
133 about_list_size(void)
135 return (LENGTH(about_list));
138 gchar *
139 get_html_page(gchar *title, gchar *body, gchar *head, bool addstyles)
141 gchar *r;
143 r = g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
144 "<head>\n"
145 "<title>%s</title>\n"
146 "%s"
147 "%s"
148 "</head>\n"
149 "<body>\n"
150 "<h1>%s</h1>\n"
151 "%s\n</body>\n"
152 "</html>",
153 title,
154 addstyles ? XT_PAGE_STYLE : "",
155 head,
156 title,
157 body);
159 return (r);
163 * Display a web page from a HTML string in memory, rather than from a URL
165 void
166 load_webkit_string(struct tab *t, const char *str, gchar *title)
168 char file[PATH_MAX];
169 int i;
171 /* we set this to indicate we want to manually do navaction */
172 if (t->bfl)
173 t->item = webkit_web_back_forward_list_get_current_item(t->bfl);
175 t->xtp_meaning = XT_XTP_TAB_MEANING_NORMAL;
176 if (title) {
177 /* set t->xtp_meaning */
178 for (i = 0; i < LENGTH(about_list); i++)
179 if (!strcmp(title, about_list[i].name)) {
180 t->xtp_meaning = i;
181 break;
184 webkit_web_view_load_string(t->wv, str, NULL, encoding,
185 "file://");
186 #if GTK_CHECK_VERSION(2, 20, 0)
187 gtk_spinner_stop(GTK_SPINNER(t->spinner));
188 gtk_widget_hide(t->spinner);
189 #endif
190 snprintf(file, sizeof file, "%s/%s", resource_dir, icons[0]);
191 xt_icon_from_file(t, file);
196 blank(struct tab *t, struct karg *args)
198 if (t == NULL)
199 show_oops(NULL, "blank invalid parameters");
201 load_webkit_string(t, "", XT_URI_ABOUT_BLANK);
203 return (0);
207 about(struct tab *t, struct karg *args)
209 char *page, *body;
211 if (t == NULL)
212 show_oops(NULL, "about invalid parameters");
214 body = g_strdup_printf("<b>Version: %s</b>"
215 #ifdef XXXTERM_BUILDSTR
216 "<br><b>Build: %s</b>"
217 #endif
218 "<p>"
219 "Authors:"
220 "<ul>"
221 "<li>Marco Peereboom &lt;marco@peereboom.us&gt;</li>"
222 "<li>Stevan Andjelkovic &lt;stevan@student.chalmers.se&gt;</li>"
223 "<li>Edd Barrett &lt;vext01@gmail.com&gt;</li>"
224 "<li>Todd T. Fries &lt;todd@fries.net&gt;</li>"
225 "<li>Raphael Graf &lt;r@undefined.ch&gt;</li>"
226 "<li>Michal Mazurek &lt;akfaew@jasminek.net&gt;<li>"
227 "</ul>"
228 "Copyrights and licenses can be found on the XXXTerm "
229 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
230 "</p>",
231 #ifdef XXXTERM_BUILDSTR
232 version, XXXTERM_BUILDSTR
233 #else
234 version
235 #endif
238 page = get_html_page("About", body, "", 0);
239 g_free(body);
241 load_webkit_string(t, page, XT_URI_ABOUT_ABOUT);
242 g_free(page);
244 return (0);
248 help(struct tab *t, struct karg *args)
250 char *page, *head, *body;
252 if (t == NULL)
253 show_oops(NULL, "help invalid parameters");
255 head = "<meta http-equiv=\"REFRESH\" content=\"0;"
256 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
257 "</head>\n";
258 body = "XXXTerm man page <a href=\"http://opensource.conformal.com/"
259 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
260 "cgi-bin/man-cgi?xxxterm</a>";
262 page = get_html_page(XT_NAME, body, head, FALSE);
264 load_webkit_string(t, page, XT_URI_ABOUT_HELP);
265 g_free(page);
267 return (0);
271 stats(struct tab *t, struct karg *args)
273 char *page, *body, *s, line[64 * 1024];
274 long long unsigned int line_count = 0;
275 FILE *r_cookie_f;
277 if (t == NULL)
278 show_oops(NULL, "stats invalid parameters");
280 line[0] = '\0';
281 if (save_rejected_cookies) {
282 if ((r_cookie_f = fopen(rc_fname, "r"))) {
283 for (;;) {
284 s = fgets(line, sizeof line, r_cookie_f);
285 if (s == NULL || feof(r_cookie_f) ||
286 ferror(r_cookie_f))
287 break;
288 line_count++;
290 fclose(r_cookie_f);
291 snprintf(line, sizeof line,
292 "<br/>Cookies blocked(*) total: %llu", line_count);
293 } else
294 show_oops(t, "Can't open blocked cookies file: %s",
295 strerror(errno));
298 body = g_strdup_printf(
299 "Cookies blocked(*) this session: %llu"
300 "%s"
301 "<p><small><b>*</b> results vary based on settings</small></p>",
302 blocked_cookies,
303 line);
305 page = get_html_page("Statistics", body, "", 0);
306 g_free(body);
308 load_webkit_string(t, page, XT_URI_ABOUT_STATS);
309 g_free(page);
311 return (0);
315 * cancel, remove, etc. downloads
317 void
318 xtp_handle_dl(struct tab *t, uint8_t cmd, int id)
320 struct download find, *d = NULL;
322 DNPRINTF(XT_D_DOWNLOAD, "download control: cmd %d, id %d\n", cmd, id);
324 /* some commands require a valid download id */
325 if (cmd != XT_XTP_DL_LIST) {
326 /* lookup download in question */
327 find.id = id;
328 d = RB_FIND(download_list, &downloads, &find);
330 if (d == NULL) {
331 show_oops(t, "%s: no such download", __func__);
332 return;
336 /* decide what to do */
337 switch (cmd) {
338 case XT_XTP_DL_CANCEL:
339 webkit_download_cancel(d->download);
340 break;
341 case XT_XTP_DL_REMOVE:
342 webkit_download_cancel(d->download); /* just incase */
343 g_object_unref(d->download);
344 RB_REMOVE(download_list, &downloads, d);
345 break;
346 case XT_XTP_DL_LIST:
347 /* Nothing */
348 break;
349 default:
350 show_oops(t, "%s: unknown command", __func__);
351 break;
353 xtp_page_dl(t, NULL);
357 * Actions on history, only does one thing for now, but
358 * we provide the function for future actions
360 void
361 xtp_handle_hl(struct tab *t, uint8_t cmd, int id)
363 struct history *h, *next;
364 int i = 1;
366 switch (cmd) {
367 case XT_XTP_HL_REMOVE:
368 /* walk backwards, as listed in reverse */
369 for (h = RB_MAX(history_list, &hl); h != NULL; h = next) {
370 next = RB_PREV(history_list, &hl, h);
371 if (id == i) {
372 RB_REMOVE(history_list, &hl, h);
373 g_free((gpointer) h->title);
374 g_free((gpointer) h->uri);
375 g_free(h);
376 break;
378 i++;
380 break;
381 case XT_XTP_HL_LIST:
382 /* Nothing - just xtp_page_hl() below */
383 break;
384 default:
385 show_oops(t, "%s: unknown command", __func__);
386 break;
389 xtp_page_hl(t, NULL);
392 /* remove a favorite */
393 void
394 remove_favorite(struct tab *t, int index)
396 char file[PATH_MAX], *title, *uri = NULL;
397 char *new_favs, *tmp;
398 FILE *f;
399 int i;
400 size_t len, lineno;
402 /* open favorites */
403 snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE);
405 if ((f = fopen(file, "r")) == NULL) {
406 show_oops(t, "%s: can't open favorites: %s",
407 __func__, strerror(errno));
408 return;
411 /* build a string which will become the new favroites file */
412 new_favs = g_strdup("");
414 for (i = 1;;) {
415 if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL)
416 if (feof(f) || ferror(f))
417 break;
418 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
419 if (len == 0) {
420 free(title);
421 title = NULL;
422 continue;
425 if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) {
426 if (feof(f) || ferror(f)) {
427 show_oops(t, "%s: can't parse favorites %s",
428 __func__, strerror(errno));
429 goto clean;
433 /* as long as this isn't the one we are deleting add to file */
434 if (i != index) {
435 tmp = new_favs;
436 new_favs = g_strdup_printf("%s%s\n%s\n",
437 new_favs, title, uri);
438 g_free(tmp);
441 free(uri);
442 uri = NULL;
443 free(title);
444 title = NULL;
445 i++;
447 fclose(f);
449 /* write back new favorites file */
450 if ((f = fopen(file, "w")) == NULL) {
451 show_oops(t, "%s: can't open favorites: %s",
452 __func__, strerror(errno));
453 goto clean;
456 if (fwrite(new_favs, strlen(new_favs), 1, f) != 1)
457 show_oops(t, "%s: can't fwrite"); /* shut gcc up */
458 fclose(f);
460 clean:
461 if (uri)
462 free(uri);
463 if (title)
464 free(title);
466 g_free(new_favs);
469 void
470 xtp_handle_fl(struct tab *t, uint8_t cmd, int arg)
472 switch (cmd) {
473 case XT_XTP_FL_LIST:
474 /* nothing, just the below call to xtp_page_fl() */
475 break;
476 case XT_XTP_FL_REMOVE:
477 remove_favorite(t, arg);
478 break;
479 default:
480 show_oops(t, "%s: invalid favorites command", __func__);
481 break;
484 xtp_page_fl(t, NULL);
487 void
488 xtp_handle_cl(struct tab *t, uint8_t cmd, int arg)
490 switch (cmd) {
491 case XT_XTP_CL_LIST:
492 /* nothing, just xtp_page_cl() */
493 break;
494 case XT_XTP_CL_REMOVE:
495 remove_cookie(arg);
496 break;
497 default:
498 show_oops(t, "%s: unknown cookie xtp command", __func__);
499 break;
502 xtp_page_cl(t, NULL);
505 /* link an XTP class to it's session key and handler function */
506 struct xtp_despatch {
507 uint8_t xtp_class;
508 char **session_key;
509 void (*handle_func)(struct tab *, uint8_t, int);
512 struct xtp_despatch xtp_despatches[] = {
513 { XT_XTP_DL, &dl_session_key, xtp_handle_dl },
514 { XT_XTP_HL, &hl_session_key, xtp_handle_hl },
515 { XT_XTP_FL, &fl_session_key, xtp_handle_fl },
516 { XT_XTP_CL, &cl_session_key, xtp_handle_cl },
517 { XT_XTP_INVALID, NULL, NULL }
521 * generate a session key to secure xtp commands.
522 * pass in a ptr to the key in question and it will
523 * be modified in place.
525 void
526 generate_xtp_session_key(char **key)
528 uint8_t rand_bytes[XT_XTP_SES_KEY_SZ];
530 /* free old key */
531 if (*key)
532 g_free(*key);
534 /* make a new one */
535 arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ);
536 *key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT,
537 rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3],
538 rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]);
540 DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key);
543 void
544 xtp_generate_keys(void)
546 /* generate session keys for xtp pages */
547 generate_xtp_session_key(&dl_session_key);
548 generate_xtp_session_key(&hl_session_key);
549 generate_xtp_session_key(&cl_session_key);
550 generate_xtp_session_key(&fl_session_key);
554 * validate a xtp session key.
555 * return (1) if OK
558 validate_xtp_session_key(struct tab *t, char *trusted, char *untrusted)
560 if (strcmp(trusted, untrusted) != 0) {
561 show_oops(t, "%s: xtp session key mismatch possible spoof",
562 __func__);
563 return (0);
566 return (1);
570 * is the url xtp protocol? (xxxt://)
571 * if so, parse and despatch correct bahvior
574 parse_xtp_url(struct tab *t, const char *url)
576 char *dup = NULL, *p, *last = NULL;
577 uint8_t n_tokens = 0;
578 char *tokens[4] = {NULL, NULL, NULL, ""};
579 struct xtp_despatch *dsp, *dsp_match = NULL;
580 uint8_t req_class;
581 int ret = FALSE;
584 * tokens array meaning:
585 * tokens[0] = class
586 * tokens[1] = session key
587 * tokens[2] = action
588 * tokens[3] = optional argument
591 DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, url);
593 if (strncmp(url, XT_XTP_STR, strlen(XT_XTP_STR)))
594 goto clean;
596 dup = g_strdup(url + strlen(XT_XTP_STR));
598 /* split out the url */
599 for ((p = strtok_r(dup, "/", &last)); p;
600 (p = strtok_r(NULL, "/", &last))) {
601 if (n_tokens < 4)
602 tokens[n_tokens++] = p;
605 /* should be atleast three fields 'class/seskey/command/arg' */
606 if (n_tokens < 3)
607 goto clean;
609 dsp = xtp_despatches;
610 req_class = atoi(tokens[0]);
611 while (dsp->xtp_class) {
612 if (dsp->xtp_class == req_class) {
613 dsp_match = dsp;
614 break;
616 dsp++;
619 /* did we find one atall? */
620 if (dsp_match == NULL) {
621 show_oops(t, "%s: no matching xtp despatch found", __func__);
622 goto clean;
625 /* check session key and call despatch function */
626 if (validate_xtp_session_key(t, *(dsp_match->session_key), tokens[1])) {
627 ret = TRUE; /* all is well, this was a valid xtp request */
628 dsp_match->handle_func(t, atoi(tokens[2]), atoi(tokens[3]));
631 clean:
632 if (dup)
633 g_free(dup);
635 return (ret);
639 * update all favorite tabs apart from one. Pass NULL if
640 * you want to update all.
642 void
643 update_favorite_tabs(struct tab *apart_from)
645 struct tab *t;
646 if (!updating_fl_tabs) {
647 updating_fl_tabs = 1; /* stop infinite recursion */
648 TAILQ_FOREACH(t, &tabs, entry)
649 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL)
650 && (t != apart_from))
651 xtp_page_fl(t, NULL);
652 updating_fl_tabs = 0;
657 * update all download tabs apart from one. Pass NULL if
658 * you want to update all.
660 void
661 update_download_tabs(struct tab *apart_from)
663 struct tab *t;
664 if (!updating_dl_tabs) {
665 updating_dl_tabs = 1; /* stop infinite recursion */
666 TAILQ_FOREACH(t, &tabs, entry)
667 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_DL)
668 && (t != apart_from))
669 xtp_page_dl(t, NULL);
670 updating_dl_tabs = 0;
675 * update all cookie tabs apart from one. Pass NULL if
676 * you want to update all.
678 void
679 update_cookie_tabs(struct tab *apart_from)
681 struct tab *t;
682 if (!updating_cl_tabs) {
683 updating_cl_tabs = 1; /* stop infinite recursion */
684 TAILQ_FOREACH(t, &tabs, entry)
685 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL)
686 && (t != apart_from))
687 xtp_page_cl(t, NULL);
688 updating_cl_tabs = 0;
693 * update all history tabs apart from one. Pass NULL if
694 * you want to update all.
696 void
697 update_history_tabs(struct tab *apart_from)
699 struct tab *t;
701 if (!updating_hl_tabs) {
702 updating_hl_tabs = 1; /* stop infinite recursion */
703 TAILQ_FOREACH(t, &tabs, entry)
704 if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL)
705 && (t != apart_from))
706 xtp_page_hl(t, NULL);
707 updating_hl_tabs = 0;
711 /* show a list of favorites (bookmarks) */
713 xtp_page_fl(struct tab *t, struct karg *args)
715 char file[PATH_MAX];
716 FILE *f;
717 char *uri = NULL, *title = NULL;
718 size_t len, lineno = 0;
719 int i, failed = 0;
720 char *body, *tmp, *page = NULL;
721 const char delim[3] = {'\\', '\\', '\0'};
723 DNPRINTF(XT_D_FAVORITE, "%s:", __func__);
725 if (t == NULL)
726 warn("%s: bad param", __func__);
728 /* new session key */
729 if (!updating_fl_tabs)
730 generate_xtp_session_key(&fl_session_key);
732 /* open favorites */
733 snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE);
734 if ((f = fopen(file, "r")) == NULL) {
735 show_oops(t, "Can't open favorites file: %s", strerror(errno));
736 return (1);
739 /* body */
740 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
741 "<th style='width: 40px'>&#35;</th><th>Link</th>"
742 "<th style='width: 40px'>Rm</th></tr>\n");
744 for (i = 1;;) {
745 if ((title = fparseln(f, &len, &lineno, delim, 0)) == NULL)
746 break;
747 if (strlen(title) == 0 || title[0] == '#') {
748 free(title);
749 title = NULL;
750 continue;
753 if ((uri = fparseln(f, &len, &lineno, delim, 0)) == NULL)
754 if (feof(f) || ferror(f)) {
755 show_oops(t, "favorites file corrupt");
756 failed = 1;
757 break;
760 tmp = body;
761 body = g_strdup_printf("%s<tr>"
762 "<td>%d</td>"
763 "<td><a href='%s'>%s</a></td>"
764 "<td style='text-align: center'>"
765 "<a href='%s%d/%s/%d/%d'>X</a></td>"
766 "</tr>\n",
767 body, i, uri, title,
768 XT_XTP_STR, XT_XTP_FL, fl_session_key, XT_XTP_FL_REMOVE, i);
770 g_free(tmp);
772 free(uri);
773 uri = NULL;
774 free(title);
775 title = NULL;
776 i++;
778 fclose(f);
780 /* if none, say so */
781 if (i == 1) {
782 tmp = body;
783 body = g_strdup_printf("%s<tr>"
784 "<td colspan='3' style='text-align: center'>"
785 "No favorites - To add one use the 'favadd' command."
786 "</td></tr>", body);
787 g_free(tmp);
790 tmp = body;
791 body = g_strdup_printf("%s</table>", body);
792 g_free(tmp);
794 if (uri)
795 free(uri);
796 if (title)
797 free(title);
799 /* render */
800 if (!failed) {
801 page = get_html_page("Favorites", body, "", 1);
802 load_webkit_string(t, page, XT_URI_ABOUT_FAVORITES);
803 g_free(page);
806 update_favorite_tabs(t);
808 if (body)
809 g_free(body);
811 return (failed);
815 * Return a new string with a download row (in html)
816 * appended. Old string is freed.
818 char *
819 xtp_page_dl_row(struct tab *t, char *html, struct download *dl)
822 WebKitDownloadStatus stat;
823 char *status_html = NULL, *cmd_html = NULL, *new_html;
824 gdouble progress;
825 char cur_sz[FMT_SCALED_STRSIZE];
826 char tot_sz[FMT_SCALED_STRSIZE];
827 char *xtp_prefix;
829 DNPRINTF(XT_D_DOWNLOAD, "%s: dl->id %d\n", __func__, dl->id);
831 /* All actions wil take this form:
832 * xxxt://class/seskey
834 xtp_prefix = g_strdup_printf("%s%d/%s/",
835 XT_XTP_STR, XT_XTP_DL, dl_session_key);
837 stat = webkit_download_get_status(dl->download);
839 switch (stat) {
840 case WEBKIT_DOWNLOAD_STATUS_FINISHED:
841 status_html = g_strdup_printf("Finished");
842 cmd_html = g_strdup_printf(
843 "<a href='%s%d/%d'>Remove</a>",
844 xtp_prefix, XT_XTP_DL_REMOVE, dl->id);
845 break;
846 case WEBKIT_DOWNLOAD_STATUS_STARTED:
847 /* gather size info */
848 progress = 100 * webkit_download_get_progress(dl->download);
850 fmt_scaled(
851 webkit_download_get_current_size(dl->download), cur_sz);
852 fmt_scaled(
853 webkit_download_get_total_size(dl->download), tot_sz);
855 status_html = g_strdup_printf(
856 "<div style='width: 100%%' align='center'>"
857 "<div class='progress-outer'>"
858 "<div class='progress-inner' style='width: %.2f%%'>"
859 "</div></div></div>"
860 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
861 progress, cur_sz, tot_sz, progress);
863 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
864 xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
866 break;
867 /* LLL */
868 case WEBKIT_DOWNLOAD_STATUS_CANCELLED:
869 status_html = g_strdup_printf("Cancelled");
870 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
871 xtp_prefix, XT_XTP_DL_REMOVE, dl->id);
872 break;
873 case WEBKIT_DOWNLOAD_STATUS_ERROR:
874 status_html = g_strdup_printf("Error!");
875 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
876 xtp_prefix, XT_XTP_DL_REMOVE, dl->id);
877 break;
878 case WEBKIT_DOWNLOAD_STATUS_CREATED:
879 cmd_html = g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
880 xtp_prefix, XT_XTP_DL_CANCEL, dl->id);
881 status_html = g_strdup_printf("Starting");
882 break;
883 default:
884 show_oops(t, "%s: unknown download status", __func__);
887 new_html = g_strdup_printf(
888 "%s\n<tr><td>%s</td><td>%s</td>"
889 "<td style='text-align:center'>%s</td></tr>\n",
890 html, basename((char *)webkit_download_get_destination_uri(dl->download)),
891 status_html, cmd_html);
892 g_free(html);
894 if (status_html)
895 g_free(status_html);
897 if (cmd_html)
898 g_free(cmd_html);
900 g_free(xtp_prefix);
902 return new_html;
905 /* cookie management XTP page */
907 xtp_page_cl(struct tab *t, struct karg *args)
909 char *body, *page, *tmp;
910 int i = 1; /* all ids start 1 */
911 GSList *sc, *pc, *pc_start;
912 SoupCookie *c;
913 char *type, *table_headers, *last_domain;
915 DNPRINTF(XT_D_CMD, "%s", __func__);
917 if (t == NULL) {
918 show_oops(NULL, "%s invalid parameters", __func__);
919 return (1);
922 /* Generate a new session key */
923 if (!updating_cl_tabs)
924 generate_xtp_session_key(&cl_session_key);
926 /* table headers */
927 table_headers = g_strdup_printf("<table><tr>"
928 "<th>Type</th>"
929 "<th>Name</th>"
930 "<th style='width:200px'>Value</th>"
931 "<th>Path</th>"
932 "<th>Expires</th>"
933 "<th>Secure</th>"
934 "<th>HTTP<br />only</th>"
935 "<th style='width:40px'>Rm</th></tr>\n");
937 sc = soup_cookie_jar_all_cookies(s_cookiejar);
938 pc = soup_cookie_jar_all_cookies(p_cookiejar);
939 pc_start = pc;
941 body = NULL;
942 last_domain = strdup("");
943 for (; sc; sc = sc->next) {
944 c = sc->data;
946 if (strcmp(last_domain, c->domain) != 0) {
947 /* new domain */
948 free(last_domain);
949 last_domain = strdup(c->domain);
951 if (body != NULL) {
952 tmp = body;
953 body = g_strdup_printf("%s</table>"
954 "<h2>%s</h2>%s\n",
955 body, c->domain, table_headers);
956 g_free(tmp);
957 } else {
958 /* first domain */
959 body = g_strdup_printf("<h2>%s</h2>%s\n",
960 c->domain, table_headers);
964 type = "Session";
965 for (pc = pc_start; pc; pc = pc->next)
966 if (soup_cookie_equal(pc->data, c)) {
967 type = "Session + Persistent";
968 break;
971 tmp = body;
972 body = g_strdup_printf(
973 "%s\n<tr>"
974 "<td>%s</td>"
975 "<td style='word-wrap:normal'>%s</td>"
976 "<td>"
977 " <textarea rows='4'>%s</textarea>"
978 "</td>"
979 "<td>%s</td>"
980 "<td>%s</td>"
981 "<td>%d</td>"
982 "<td>%d</td>"
983 "<td style='text-align:center'>"
984 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
985 body,
986 type,
987 c->name,
988 c->value,
989 c->path,
990 c->expires ?
991 soup_date_to_string(c->expires, SOUP_DATE_COOKIE) : "",
992 c->secure,
993 c->http_only,
995 XT_XTP_STR,
996 XT_XTP_CL,
997 cl_session_key,
998 XT_XTP_CL_REMOVE,
1002 g_free(tmp);
1003 i++;
1006 soup_cookies_free(sc);
1007 soup_cookies_free(pc);
1009 /* small message if there are none */
1010 if (i == 1) {
1011 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1012 "colspan='8'>No Cookies</td></tr>\n", table_headers);
1014 tmp = body;
1015 body = g_strdup_printf("%s</table>", body);
1016 g_free(tmp);
1018 page = get_html_page("Cookie Jar", body, "", TRUE);
1019 g_free(body);
1020 g_free(table_headers);
1021 g_free(last_domain);
1023 load_webkit_string(t, page, XT_URI_ABOUT_COOKIEJAR);
1024 update_cookie_tabs(t);
1026 g_free(page);
1028 return (0);
1032 xtp_page_hl(struct tab *t, struct karg *args)
1034 char *body, *page, *tmp;
1035 struct history *h;
1036 int i = 1; /* all ids start 1 */
1038 DNPRINTF(XT_D_CMD, "%s", __func__);
1040 if (t == NULL) {
1041 show_oops(NULL, "%s invalid parameters", __func__);
1042 return (1);
1045 /* Generate a new session key */
1046 if (!updating_hl_tabs)
1047 generate_xtp_session_key(&hl_session_key);
1049 /* body */
1050 body = g_strdup_printf("<table style='table-layout:fixed'><tr>"
1051 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
1053 RB_FOREACH_REVERSE(h, history_list, &hl) {
1054 tmp = body;
1055 body = g_strdup_printf(
1056 "%s\n<tr>"
1057 "<td><a href='%s'>%s</a></td>"
1058 "<td>%s</td>"
1059 "<td style='text-align: center'>"
1060 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1061 body, h->uri, h->uri, h->title,
1062 XT_XTP_STR, XT_XTP_HL, hl_session_key,
1063 XT_XTP_HL_REMOVE, i);
1065 g_free(tmp);
1066 i++;
1069 /* small message if there are none */
1070 if (i == 1) {
1071 tmp = body;
1072 body = g_strdup_printf("%s\n<tr><td style='text-align:center'"
1073 "colspan='3'>No History</td></tr>\n", body);
1074 g_free(tmp);
1077 tmp = body;
1078 body = g_strdup_printf("%s</table>", body);
1079 g_free(tmp);
1081 page = get_html_page("History", body, "", TRUE);
1082 g_free(body);
1085 * update all history manager tabs as the xtp session
1086 * key has now changed. No need to update the current tab.
1087 * Already did that above.
1089 update_history_tabs(t);
1091 load_webkit_string(t, page, XT_URI_ABOUT_HISTORY);
1092 g_free(page);
1094 return (0);
1098 * Generate a web page detailing the status of any downloads
1101 xtp_page_dl(struct tab *t, struct karg *args)
1103 struct download *dl;
1104 char *body, *page, *tmp;
1105 char *ref;
1106 int n_dl = 1;
1108 DNPRINTF(XT_D_DOWNLOAD, "%s", __func__);
1110 if (t == NULL) {
1111 show_oops(NULL, "%s invalid parameters", __func__);
1112 return (1);
1116 * Generate a new session key for next page instance.
1117 * This only happens for the top level call to xtp_page_dl()
1118 * in which case updating_dl_tabs is 0.
1120 if (!updating_dl_tabs)
1121 generate_xtp_session_key(&dl_session_key);
1123 /* header - with refresh so as to update */
1124 if (refresh_interval >= 1)
1125 ref = g_strdup_printf(
1126 "<meta http-equiv='refresh' content='%u"
1127 ";url=%s%d/%s/%d' />\n",
1128 refresh_interval,
1129 XT_XTP_STR,
1130 XT_XTP_DL,
1131 dl_session_key,
1132 XT_XTP_DL_LIST);
1133 else
1134 ref = g_strdup("");
1136 body = g_strdup_printf("<div align='center'>"
1137 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
1138 "</p><table><tr><th style='width: 60%%'>"
1139 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
1140 XT_XTP_STR, XT_XTP_DL, dl_session_key, XT_XTP_DL_LIST);
1142 RB_FOREACH_REVERSE(dl, download_list, &downloads) {
1143 body = xtp_page_dl_row(t, body, dl);
1144 n_dl++;
1147 /* message if no downloads in list */
1148 if (n_dl == 1) {
1149 tmp = body;
1150 body = g_strdup_printf("%s\n<tr><td colspan='3'"
1151 " style='text-align: center'>"
1152 "No downloads</td></tr>\n", body);
1153 g_free(tmp);
1156 tmp = body;
1157 body = g_strdup_printf("%s</table></div>", body);
1158 g_free(tmp);
1160 page = get_html_page("Downloads", body, ref, 1);
1161 g_free(ref);
1162 g_free(body);
1165 * update all download manager tabs as the xtp session
1166 * key has now changed. No need to update the current tab.
1167 * Already did that above.
1169 update_download_tabs(t);
1171 load_webkit_string(t, page, XT_URI_ABOUT_DOWNLOADS);
1172 g_free(page);
1174 return (0);