2 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
3 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
4 * Copyright (c) 2010, 2011, 2012 Edd Barrett <vext01@gmail.com>
5 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
6 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
7 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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" \
52 "border: 1px solid black;" \
55 ".progress-inner{float: left;" \
57 " background: green}\n" \
58 ".dlstatus{font-size: small;" \
59 " text-align: center}\n" \
62 /* XTP classes (xxxt://<class>) */
63 #define XT_XTP_INVALID (0) /* invalid */
64 #define XT_XTP_DL (1) /* downloads */
65 #define XT_XTP_HL (2) /* history */
66 #define XT_XTP_CL (3) /* cookies */
67 #define XT_XTP_FL (4) /* favorites */
69 /* XTP download actions */
70 #define XT_XTP_DL_LIST (1)
71 #define XT_XTP_DL_CANCEL (2)
72 #define XT_XTP_DL_REMOVE (3)
73 #define XT_XTP_DL_UNLINK (4)
74 #define XT_XTP_DL_START (5)
76 /* XTP history actions */
77 #define XT_XTP_HL_LIST (1)
78 #define XT_XTP_HL_REMOVE (2)
80 /* XTP cookie actions */
81 #define XT_XTP_CL_LIST (1)
82 #define XT_XTP_CL_REMOVE (2)
83 #define XT_XTP_CL_REMOVE_DOMAIN (3)
84 #define XT_XTP_CL_REMOVE_ALL (4)
86 /* XTP cookie actions */
87 #define XT_XTP_FL_LIST (1)
88 #define XT_XTP_FL_REMOVE (2)
90 int js_show_wl(struct tab
*, struct karg
*);
91 int pl_show_wl(struct tab
*, struct karg
*);
92 int set(struct tab
*, struct karg
*);
93 int marco(struct tab
*, struct karg
*);
94 int startpage(struct tab
*, struct karg
*);
95 const char * marco_message(int *);
96 void update_cookie_tabs(struct tab
*apart_from
);
97 int about_webkit(struct tab
*, struct karg
*);
98 int allthethings(struct tab
*, struct karg
*);
100 struct about_type about_list
[] = {
101 { XT_URI_ABOUT_ABOUT
, about
},
102 { XT_URI_ABOUT_ALLTHETHINGS
, allthethings
},
103 { XT_URI_ABOUT_BLANK
, blank
},
104 { XT_URI_ABOUT_CERTS
, ca_cmd
},
105 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
106 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
107 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
108 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
109 { XT_URI_ABOUT_HELP
, help
},
110 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
111 { XT_URI_ABOUT_JSWL
, js_show_wl
},
112 { XT_URI_ABOUT_SET
, set
},
113 { XT_URI_ABOUT_STATS
, stats
},
114 { XT_URI_ABOUT_MARCO
, marco
},
115 { XT_URI_ABOUT_STARTPAGE
, startpage
},
116 { XT_URI_ABOUT_PLUGINWL
, pl_show_wl
},
117 { XT_URI_ABOUT_WEBKIT
, about_webkit
},
122 * We use these to prevent people putting xxxt:// URLs on
123 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
125 #define XT_XTP_SES_KEY_SZ 8
126 #define XT_XTP_SES_KEY_HEX_FMT \
127 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8
129 char *dl_session_key
; /* downloads */
130 char *hl_session_key
; /* history list */
131 char *cl_session_key
; /* cookie list */
132 char *fl_session_key
; /* favorites list */
134 int updating_fl_tabs
= 0;
135 int updating_dl_tabs
= 0;
136 int updating_hl_tabs
= 0;
137 int updating_cl_tabs
= 0;
138 struct download_list downloads
;
141 about_list_size(void)
143 return (LENGTH(about_list
));
147 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
151 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
153 "<title>%s</title>\n"
162 addstyles
? XT_PAGE_STYLE
: "",
171 * Display a web page from a HTML string in memory, rather than from a URL
174 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
179 /* we set this to indicate we want to manually do navaction */
181 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
183 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
185 /* set t->xtp_meaning */
186 for (i
= 0; i
< LENGTH(about_list
); i
++)
187 if (!strcmp(title
, about_list
[i
].name
)) {
192 webkit_web_view_load_string(t
->wv
, str
, NULL
, encoding
,
194 #if GTK_CHECK_VERSION(2, 20, 0)
195 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
196 gtk_widget_hide(t
->spinner
);
198 snprintf(file
, sizeof file
, "%s" PS
"%s", resource_dir
, icons
[0]);
199 xt_icon_from_file(t
, file
);
204 blank(struct tab
*t
, struct karg
*args
)
207 show_oops(NULL
, "blank invalid parameters");
209 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
215 about(struct tab
*t
, struct karg
*args
)
220 show_oops(NULL
, "about invalid parameters");
222 body
= g_strdup_printf("<b>Version: %s</b>"
223 #ifdef XXXTERM_BUILDSTR
224 "<br><b>Build: %s</b>"
226 "<br><b>WebKit: %d.%d.%d</b>"
227 "<br><b>User Agent: %d.%d</b>"
228 #ifdef WEBKITGTK_API_VERSION
229 "<br><b>WebKit API: %.1f</b>"
234 "<li>Marco Peereboom <marco@peereboom.us></li>"
235 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
236 "<li>Edd Barrett <vext01@gmail.com></li>"
237 "<li>Todd T. Fries <todd@fries.net></li>"
238 "<li>Raphael Graf <r@undefined.ch></li>"
239 "<li>Michal Mazurek <akfaew@jasminek.net></li>"
241 "Copyrights and licenses can be found on the XXXTerm "
242 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
244 #ifdef XXXTERM_BUILDSTR
245 version
, XXXTERM_BUILDSTR
,
249 WEBKIT_MAJOR_VERSION
, WEBKIT_MINOR_VERSION
, WEBKIT_MICRO_VERSION
,
250 WEBKIT_USER_AGENT_MAJOR_VERSION
, WEBKIT_USER_AGENT_MINOR_VERSION
251 #ifdef WEBKITGTK_API_VERSION
252 ,WEBKITGTK_API_VERSION
256 page
= get_html_page("About", body
, "", 0);
259 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
266 help(struct tab
*t
, struct karg
*args
)
268 char *page
, *head
, *body
;
271 show_oops(NULL
, "help invalid parameters");
273 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
274 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
276 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
277 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
278 "cgi-bin/man-cgi?xxxterm</a>";
280 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
282 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
289 stats(struct tab
*t
, struct karg
*args
)
291 char *page
, *body
, *s
, line
[64 * 1024];
292 uint64_t line_count
= 0;
296 show_oops(NULL
, "stats invalid parameters");
299 if (save_rejected_cookies
) {
300 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
302 s
= fgets(line
, sizeof line
, r_cookie_f
);
303 if (s
== NULL
|| feof(r_cookie_f
) ||
309 snprintf(line
, sizeof line
,
310 "<br/>Cookies blocked(*) total: %" PRIu64
,
313 show_oops(t
, "Can't open blocked cookies file: %s",
317 body
= g_strdup_printf(
318 "Cookies blocked(*) this session: %" PRIu64
320 "<p><small><b>*</b> results vary based on settings</small></p>",
324 page
= get_html_page("Statistics", body
, "", 0);
327 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
334 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
335 size_t cert_count
, char *title
)
337 gnutls_datum_t cinfo
;
343 for (i
= 0; i
< cert_count
; i
++) {
344 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
349 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
350 body
, i
, cinfo
.data
);
351 gnutls_free(cinfo
.data
);
355 tmp
= get_html_page(title
, body
, "", 0);
358 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
363 ca_cmd(struct tab
*t
, struct karg
*args
)
366 int rv
= 1, certs
= 0, certs_read
;
369 gnutls_x509_crt_t
*c
= NULL
;
370 char *certs_buf
= NULL
, *s
;
372 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
373 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
377 if (fstat(fileno(f
), &sb
) == -1) {
378 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
382 certs_buf
= g_malloc(sb
.st_size
+ 1);
383 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
384 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
387 certs_buf
[sb
.st_size
] = '\0';
390 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
392 s
+= strlen("BEGIN CERTIFICATE");
395 bzero(&dt
, sizeof dt
);
396 dt
.data
= (unsigned char *)certs_buf
;
397 dt
.size
= sb
.st_size
;
398 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
399 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
400 GNUTLS_X509_FMT_PEM
, 0);
401 if (certs_read
<= 0) {
402 show_oops(t
, "No cert(s) available");
405 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
418 cookie_show_wl(struct tab
*t
, struct karg
*args
)
420 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
421 wl_show(t
, args
, "Cookie White List", &c_wl
);
427 js_show_wl(struct tab
*t
, struct karg
*args
)
429 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
430 wl_show(t
, args
, "JavaScript White List", &js_wl
);
436 cookie_cmd(struct tab
*t
, struct karg
*args
)
438 if (args
->i
& XT_SHOW
)
439 wl_show(t
, args
, "Cookie White List", &c_wl
);
440 else if (args
->i
& XT_WL_TOGGLE
) {
441 args
->i
|= XT_WL_RELOAD
;
443 } else if (args
->i
& XT_SAVE
) {
444 args
->i
|= XT_WL_RELOAD
;
445 wl_save(t
, args
, XT_WL_COOKIE
);
446 } else if (args
->i
& XT_DELETE
) {
448 update_cookie_tabs(NULL
);
455 js_cmd(struct tab
*t
, struct karg
*args
)
457 if (args
->i
& XT_SHOW
)
458 wl_show(t
, args
, "JavaScript White List", &js_wl
);
459 else if (args
->i
& XT_SAVE
) {
460 args
->i
|= XT_WL_RELOAD
;
461 wl_save(t
, args
, XT_WL_JAVASCRIPT
);
462 } else if (args
->i
& XT_WL_TOGGLE
) {
463 args
->i
|= XT_WL_RELOAD
;
465 } else if (args
->i
& XT_DELETE
)
466 show_oops(t
, "'js delete' currently unimplemented");
472 pl_show_wl(struct tab
*t
, struct karg
*args
)
474 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
475 wl_show(t
, args
, "Plugin White List", &pl_wl
);
481 pl_cmd(struct tab
*t
, struct karg
*args
)
483 if (args
->i
& XT_SHOW
)
484 wl_show(t
, args
, "Plugin White List", &pl_wl
);
485 else if (args
->i
& XT_SAVE
) {
486 args
->i
|= XT_WL_RELOAD
;
487 wl_save(t
, args
, XT_WL_PLUGIN
);
488 } else if (args
->i
& XT_WL_TOGGLE
) {
489 args
->i
|= XT_WL_RELOAD
;
491 } else if (args
->i
& XT_DELETE
)
492 show_oops(t
, "'plugin delete' currently unimplemented");
498 * cancel, remove, etc. downloads
501 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
503 struct download find
, *d
= NULL
;
505 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
507 /* some commands require a valid download id */
508 if (cmd
!= XT_XTP_DL_LIST
) {
509 /* lookup download in question */
511 d
= RB_FIND(download_list
, &downloads
, &find
);
514 show_oops(t
, "%s: no such download", __func__
);
519 /* decide what to do */
521 case XT_XTP_DL_START
:
522 /* our downloads always needs to be
523 * restarted if called from here
525 download_start(t
, d
, XT_DL_RESTART
);
527 case XT_XTP_DL_CANCEL
:
528 webkit_download_cancel(d
->download
);
529 g_object_unref(d
->download
);
530 RB_REMOVE(download_list
, &downloads
, d
);
532 case XT_XTP_DL_UNLINK
:
534 unlink(webkit_download_get_destination_uri(d
->download
));
536 unlink(webkit_download_get_destination_uri(d
->download
) +
540 case XT_XTP_DL_REMOVE
:
541 webkit_download_cancel(d
->download
); /* just incase */
542 g_object_unref(d
->download
);
543 RB_REMOVE(download_list
, &downloads
, d
);
549 show_oops(t
, "%s: unknown command", __func__
);
552 xtp_page_dl(t
, NULL
);
556 * Actions on history, only does one thing for now, but
557 * we provide the function for future actions
560 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
562 struct history
*h
, *next
;
566 case XT_XTP_HL_REMOVE
:
567 /* walk backwards, as listed in reverse */
568 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
569 next
= RB_PREV(history_list
, &hl
, h
);
571 RB_REMOVE(history_list
, &hl
, h
);
572 g_free((gpointer
) h
->title
);
573 g_free((gpointer
) h
->uri
);
581 /* Nothing - just xtp_page_hl() below */
584 show_oops(t
, "%s: unknown command", __func__
);
588 xtp_page_hl(t
, NULL
);
591 /* remove a favorite */
593 remove_favorite(struct tab
*t
, int index
)
595 char file
[PATH_MAX
], *title
, *uri
= NULL
;
596 char *new_favs
, *tmp
;
602 snprintf(file
, sizeof file
, "%s" PS
"%s", work_dir
, XT_FAVS_FILE
);
604 if ((f
= fopen(file
, "r")) == NULL
) {
605 show_oops(t
, "%s: can't open favorites: %s",
606 __func__
, strerror(errno
));
610 /* build a string which will become the new favroites file */
611 new_favs
= g_strdup("");
614 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
615 if (feof(f
) || ferror(f
))
617 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
624 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
625 if (feof(f
) || ferror(f
)) {
626 show_oops(t
, "%s: can't parse favorites %s",
627 __func__
, strerror(errno
));
632 /* as long as this isn't the one we are deleting add to file */
635 new_favs
= g_strdup_printf("%s%s\n%s\n",
636 new_favs
, title
, uri
);
648 /* write back new favorites file */
649 if ((f
= fopen(file
, "w")) == NULL
) {
650 show_oops(t
, "%s: can't open favorites: %s",
651 __func__
, strerror(errno
));
655 if (fwrite(new_favs
, strlen(new_favs
), 1, f
) != 1)
656 show_oops(t
, "%s: can't fwrite", __func__
);
669 add_favorite(struct tab
*t
, struct karg
*args
)
674 size_t urilen
, linelen
;
675 const gchar
*uri
, *title
;
680 /* don't allow adding of xtp pages to favorites */
681 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
682 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
686 snprintf(file
, sizeof file
, "%s" PS
"%s", work_dir
, XT_FAVS_FILE
);
687 if ((f
= fopen(file
, "r+")) == NULL
) {
688 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
692 title
= get_title(t
, FALSE
);
695 if (title
== NULL
|| uri
== NULL
) {
696 show_oops(t
, "can't add page to favorites");
700 urilen
= strlen(uri
);
703 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
704 if (feof(f
) || ferror(f
))
707 if (linelen
== urilen
&& !strcmp(line
, uri
))
714 fprintf(f
, "\n%s\n%s", title
, uri
);
720 update_favorite_tabs(NULL
);
726 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
730 /* nothing, just the below call to xtp_page_fl() */
732 case XT_XTP_FL_REMOVE
:
733 remove_favorite(t
, arg
);
736 show_oops(t
, "%s: invalid favorites command", __func__
);
740 xtp_page_fl(t
, NULL
);
744 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
748 /* nothing, just xtp_page_cl() */
750 case XT_XTP_CL_REMOVE
:
753 case XT_XTP_CL_REMOVE_DOMAIN
:
754 remove_cookie_domain(arg
);
756 case XT_XTP_CL_REMOVE_ALL
:
760 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
764 xtp_page_cl(t
, NULL
);
767 /* link an XTP class to it's session key and handler function */
768 struct xtp_despatch
{
771 void (*handle_func
)(struct tab
*, uint8_t, int);
774 struct xtp_despatch xtp_despatches
[] = {
775 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
776 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
777 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
778 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
779 { XT_XTP_INVALID
, NULL
, NULL
}
783 * generate a session key to secure xtp commands.
784 * pass in a ptr to the key in question and it will
785 * be modified in place.
788 generate_xtp_session_key(char **key
)
790 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
797 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
798 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
799 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
800 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
802 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
806 xtp_generate_keys(void)
808 /* generate session keys for xtp pages */
809 generate_xtp_session_key(&dl_session_key
);
810 generate_xtp_session_key(&hl_session_key
);
811 generate_xtp_session_key(&cl_session_key
);
812 generate_xtp_session_key(&fl_session_key
);
816 * validate a xtp session key.
820 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
822 if (strcmp(trusted
, untrusted
) != 0) {
823 show_oops(t
, "%s: xtp session key mismatch possible spoof",
832 * is the url xtp protocol? (xxxt://)
833 * if so, parse and despatch correct bahvior
836 parse_xtp_url(struct tab
*t
, const char *url
)
838 char *dup
= NULL
, *p
, *last
= NULL
;
839 uint8_t n_tokens
= 0;
840 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
841 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
846 * tokens array meaning:
848 * tokens[1] = session key
850 * tokens[3] = optional argument
853 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
855 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
858 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
860 /* split out the url */
861 for ((p
= strtok_r(dup
, "/", &last
)); p
;
862 (p
= strtok_r(NULL
, "/", &last
))) {
864 tokens
[n_tokens
++] = p
;
867 /* should be atleast three fields 'class/seskey/command/arg' */
871 dsp
= xtp_despatches
;
872 req_class
= atoi(tokens
[0]);
873 while (dsp
->xtp_class
) {
874 if (dsp
->xtp_class
== req_class
) {
881 /* did we find one atall? */
882 if (dsp_match
== NULL
) {
883 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
887 /* check session key and call despatch function */
888 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
889 ret
= TRUE
; /* all is well, this was a valid xtp request */
890 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
901 * update all favorite tabs apart from one. Pass NULL if
902 * you want to update all.
905 update_favorite_tabs(struct tab
*apart_from
)
908 if (!updating_fl_tabs
) {
909 updating_fl_tabs
= 1; /* stop infinite recursion */
910 TAILQ_FOREACH(t
, &tabs
, entry
)
911 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
912 && (t
!= apart_from
))
913 xtp_page_fl(t
, NULL
);
914 updating_fl_tabs
= 0;
919 * update all download tabs apart from one. Pass NULL if
920 * you want to update all.
923 update_download_tabs(struct tab
*apart_from
)
926 if (!updating_dl_tabs
) {
927 updating_dl_tabs
= 1; /* stop infinite recursion */
928 TAILQ_FOREACH(t
, &tabs
, entry
)
929 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
930 && (t
!= apart_from
))
931 xtp_page_dl(t
, NULL
);
932 updating_dl_tabs
= 0;
937 * update all cookie tabs apart from one. Pass NULL if
938 * you want to update all.
941 update_cookie_tabs(struct tab
*apart_from
)
944 if (!updating_cl_tabs
) {
945 updating_cl_tabs
= 1; /* stop infinite recursion */
946 TAILQ_FOREACH(t
, &tabs
, entry
)
947 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
948 && (t
!= apart_from
))
949 xtp_page_cl(t
, NULL
);
950 updating_cl_tabs
= 0;
955 * update all history tabs apart from one. Pass NULL if
956 * you want to update all.
959 update_history_tabs(struct tab
*apart_from
)
963 if (!updating_hl_tabs
) {
964 updating_hl_tabs
= 1; /* stop infinite recursion */
965 TAILQ_FOREACH(t
, &tabs
, entry
)
966 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
967 && (t
!= apart_from
))
968 xtp_page_hl(t
, NULL
);
969 updating_hl_tabs
= 0;
973 /* show a list of favorites (bookmarks) */
975 xtp_page_fl(struct tab
*t
, struct karg
*args
)
979 char *uri
= NULL
, *title
= NULL
;
980 size_t len
, lineno
= 0;
982 char *body
, *tmp
, *page
= NULL
;
983 const char delim
[3] = {'\\', '\\', '\0'};
985 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
988 warn("%s: bad param", __func__
);
990 /* new session key */
991 if (!updating_fl_tabs
)
992 generate_xtp_session_key(&fl_session_key
);
995 snprintf(file
, sizeof file
, "%s" PS
"%s", work_dir
, XT_FAVS_FILE
);
996 if ((f
= fopen(file
, "r")) == NULL
) {
997 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
1002 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
1003 "<th style='width: 40px'>#</th><th>Link</th>"
1004 "<th style='width: 40px'>Rm</th></tr>\n");
1007 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
1009 if (strlen(title
) == 0) {
1015 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
1016 if (feof(f
) || ferror(f
)) {
1017 show_oops(t
, "favorites file corrupt");
1023 body
= g_strdup_printf("%s<tr>"
1025 "<td><a href='%s'>%s</a></td>"
1026 "<td style='text-align: center'>"
1027 "<a href='%s%d/%s/%d/%d'>X</a></td>"
1029 body
, i
, uri
, title
,
1030 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
1042 /* if none, say so */
1045 body
= g_strdup_printf("%s<tr>"
1046 "<td colspan='3' style='text-align: center'>"
1047 "No favorites - To add one use the 'favadd' command."
1048 "</td></tr>", body
);
1053 body
= g_strdup_printf("%s</table>", body
);
1063 page
= get_html_page("Favorites", body
, "", 1);
1064 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
1068 update_favorite_tabs(t
);
1077 * Return a new string with a download row (in html)
1078 * appended. Old string is freed.
1081 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
1084 WebKitDownloadStatus stat
;
1085 const gchar
*destination
;
1086 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
1088 char cur_sz
[FMT_SCALED_STRSIZE
];
1089 char tot_sz
[FMT_SCALED_STRSIZE
];
1092 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
1094 /* All actions wil take this form:
1095 * xxxt://class/seskey
1097 xtp_prefix
= g_strdup_printf("%s%d/%s/",
1098 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
1100 stat
= webkit_download_get_status(dl
->download
);
1103 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
1104 status_html
= g_strdup_printf("Finished");
1105 cmd_html
= g_strdup_printf(
1106 "<a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1107 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
, xtp_prefix
,
1108 XT_XTP_DL_UNLINK
, dl
->id
);
1110 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
1111 /* gather size info */
1112 progress
= 100 * webkit_download_get_progress(dl
->download
);
1115 webkit_download_get_current_size(dl
->download
), cur_sz
);
1117 webkit_download_get_total_size(dl
->download
), tot_sz
);
1119 status_html
= g_strdup_printf(
1120 "<div style='width: 100%%' align='center'>"
1121 "<div class='progress-outer'>"
1122 "<div class='progress-inner' style='width: %.2f%%'>"
1123 "</div></div></div>"
1124 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
1125 progress
, cur_sz
, tot_sz
, progress
);
1127 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
1128 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
1132 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
1133 status_html
= g_strdup_printf("Cancelled");
1134 cmd_html
= g_strdup_printf(
1135 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1136 xtp_prefix
, XT_XTP_DL_START
, dl
->id
,
1137 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
, xtp_prefix
,
1138 XT_XTP_DL_UNLINK
, dl
->id
);
1140 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
1141 status_html
= g_strdup_printf("Error!");
1142 cmd_html
= g_strdup_printf(
1143 "<a href='%s%d/%d'>Restart</a> / <a href='%s%d/%d'>Remove</a> / <a href='%s%d/%d'>Unlink</a>",
1144 xtp_prefix
, XT_XTP_DL_START
, dl
->id
,
1145 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
, xtp_prefix
,
1146 XT_XTP_DL_UNLINK
, dl
->id
);
1148 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
1149 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Start</a> / <a href='%s%d/%d'>Cancel</a>",
1150 xtp_prefix
, XT_XTP_DL_START
, dl
->id
, xtp_prefix
,
1151 XT_XTP_DL_CANCEL
, dl
->id
);
1152 status_html
= g_strdup_printf("Created");
1155 show_oops(t
, "%s: unknown download status", __func__
);
1158 destination
= webkit_download_get_destination_uri(dl
->download
);
1159 /* we might not have a destination set yet */
1161 destination
= webkit_download_get_suggested_filename(dl
->download
);
1162 new_html
= g_strdup_printf(
1163 "%s\n<tr><td>%s</td><td>%s</td>"
1164 "<td style='text-align:center'>%s</td></tr>\n",
1165 html
, basename((char *)destination
),
1166 status_html
, cmd_html
);
1170 g_free(status_html
);
1180 /* cookie management XTP page */
1182 xtp_page_cl(struct tab
*t
, struct karg
*args
)
1184 char *body
, *page
, *tmp
;
1185 int i
= 1; /* all ids start 1 */
1187 GSList
*sc
, *pc
, *pc_start
;
1189 char *type
, *table_headers
, *last_domain
;
1191 DNPRINTF(XT_D_CMD
, "%s", __func__
);
1194 show_oops(NULL
, "%s invalid parameters", __func__
);
1198 /* Generate a new session key */
1199 if (!updating_cl_tabs
)
1200 generate_xtp_session_key(&cl_session_key
);
1203 table_headers
= g_strdup_printf("<table><tr>"
1206 "<th style='width:200px'>Value</th>"
1210 "<th>HTTP<br />only</th>"
1211 "<th style='width:40px'>Rm</th></tr>\n");
1213 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
1214 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
1217 body
= g_strdup_printf("<div align=\"center\"><a href=\"%s%d/%s/%d\">"
1218 "[ Remove All Cookies From All Domains ]</a></div>\n",
1219 XT_XTP_STR
, XT_XTP_CL
, cl_session_key
, XT_XTP_CL_REMOVE_ALL
);
1221 last_domain
= strdup("");
1222 for (; sc
; sc
= sc
->next
) {
1225 if (strcmp(last_domain
, c
->domain
) != 0) {
1229 last_domain
= strdup(c
->domain
);
1233 body
= g_strdup_printf("%s</table>"
1234 "<h2>%s</h2><div align=\"center\">"
1235 "<a href='%s%d/%s/%d/%d'>"
1236 "[ Remove All From This Domain ]"
1239 XT_XTP_STR
, XT_XTP_CL
, cl_session_key
,
1240 XT_XTP_CL_REMOVE_DOMAIN
, domain_id
,
1245 body
= g_strdup_printf("<h2>%s</h2>"
1246 "<div align=\"center\">"
1247 "<a href='%s%d/%s/%d/%d'>"
1248 "[ Remove All From This Domain ]</a></div>%s\n",
1249 c
->domain
, XT_XTP_STR
, XT_XTP_CL
,
1250 cl_session_key
, XT_XTP_CL_REMOVE_DOMAIN
,
1251 domain_id
, table_headers
);
1256 for (pc
= pc_start
; pc
; pc
= pc
->next
)
1257 if (soup_cookie_equal(pc
->data
, c
)) {
1258 type
= "Session + Persistent";
1263 body
= g_strdup_printf(
1266 "<td style='word-wrap:normal'>%s</td>"
1268 " <textarea rows='4'>%s</textarea>"
1274 "<td style='text-align:center'>"
1275 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1282 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
1297 soup_cookies_free(sc
);
1298 soup_cookies_free(pc
);
1300 /* small message if there are none */
1302 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
1303 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
1306 body
= g_strdup_printf("%s</table>", body
);
1309 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
1311 g_free(table_headers
);
1312 g_free(last_domain
);
1314 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
1315 update_cookie_tabs(t
);
1323 xtp_page_hl(struct tab
*t
, struct karg
*args
)
1325 char *body
, *page
, *tmp
;
1327 int i
= 1; /* all ids start 1 */
1329 DNPRINTF(XT_D_CMD
, "%s", __func__
);
1332 show_oops(NULL
, "%s invalid parameters", __func__
);
1336 /* Generate a new session key */
1337 if (!updating_hl_tabs
)
1338 generate_xtp_session_key(&hl_session_key
);
1341 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
1342 "<th>URI</th><th>Title</th><th>Last visited</th><th style='width: 40px'>Rm</th></tr>\n");
1344 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1346 body
= g_strdup_printf(
1348 "<td><a href='%s'>%s</a></td>"
1351 "<td style='text-align: center'>"
1352 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
1353 body
, h
->uri
, h
->uri
, h
->title
, ctime(&h
->time
),
1354 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
1355 XT_XTP_HL_REMOVE
, i
);
1361 /* small message if there are none */
1364 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
1365 "colspan='3'>No History</td></tr>\n", body
);
1370 body
= g_strdup_printf("%s</table>", body
);
1373 page
= get_html_page("History", body
, "", TRUE
);
1377 * update all history manager tabs as the xtp session
1378 * key has now changed. No need to update the current tab.
1379 * Already did that above.
1381 update_history_tabs(t
);
1383 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
1390 * Generate a web page detailing the status of any downloads
1393 xtp_page_dl(struct tab
*t
, struct karg
*args
)
1395 struct download
*dl
;
1396 char *body
, *page
, *tmp
;
1400 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
1403 show_oops(NULL
, "%s invalid parameters", __func__
);
1408 * Generate a new session key for next page instance.
1409 * This only happens for the top level call to xtp_page_dl()
1410 * in which case updating_dl_tabs is 0.
1412 if (!updating_dl_tabs
)
1413 generate_xtp_session_key(&dl_session_key
);
1415 /* header - with refresh so as to update */
1416 if (refresh_interval
>= 1)
1417 ref
= g_strdup_printf(
1418 "<meta http-equiv='refresh' content='%u"
1419 ";url=%s%d/%s/%d' />\n",
1428 body
= g_strdup_printf("<div align='center'>"
1429 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
1430 "</p><table><tr><th style='width: 60%%'>"
1431 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
1432 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
1434 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
1435 body
= xtp_page_dl_row(t
, body
, dl
);
1439 /* message if no downloads in list */
1442 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
1443 " style='text-align: center'>"
1444 "No downloads</td></tr>\n", body
);
1449 body
= g_strdup_printf("%s</table></div>", body
);
1452 page
= get_html_page("Downloads", body
, ref
, 1);
1457 * update all download manager tabs as the xtp session
1458 * key has now changed. No need to update the current tab.
1459 * Already did that above.
1461 update_download_tabs(t
);
1463 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
1470 startpage(struct tab
*t
, struct karg
*args
)
1472 char *page
, *body
, *b
;
1476 show_oops(NULL
, "startpage invalid parameters");
1478 body
= g_strdup_printf("<b>Startup Exception(s):</b><p>");
1480 TAILQ_FOREACH(s
, &spl
, entry
) {
1482 body
= g_strdup_printf("%s%s<br>", body
, s
->line
);
1486 page
= get_html_page("Startup Exception", body
, "", 0);
1489 load_webkit_string(t
, page
, XT_URI_ABOUT_STARTPAGE
);
1496 startpage_add(const char *fmt
, ...)
1506 if (vasprintf(&msg
, fmt
, ap
) == -1)
1507 errx(1, "startpage_add failed");
1510 s
= g_malloc0(sizeof *s
);
1513 TAILQ_INSERT_TAIL(&spl
, s
, entry
);
1517 show_g_object_settings(GObject
*o
, char *str
, int recurse
)
1519 char *b
, *body
, *valstr
;
1526 const gchar
*string
;
1535 GParamSpec
**proplist
;
1536 char *tmpstr
, *tmpsettings
;
1538 if (!G_IS_OBJECT(o
)) {
1539 fprintf(stderr
, "%s is not a g_object\n", str
);
1540 return g_strdup("");
1542 proplist
= g_object_class_list_properties(
1543 G_OBJECT_GET_CLASS(o
), &n_props
);
1544 body
= g_strdup_printf("%s: %3d settings\n", str
, n_props
);
1545 for (i
=0; i
< n_props
; i
++) {
1546 pspec
= proplist
[i
];
1547 tname
= G_OBJECT_TYPE_NAME(pspec
);
1548 bzero(&value
, sizeof value
);
1551 if (!(pspec
->flags
& G_PARAM_READABLE
))
1552 valstr
= g_strdup_printf("not a readable property");
1554 g_value_init(&value
, G_PARAM_SPEC_VALUE_TYPE(pspec
));
1555 g_object_get_property(G_OBJECT(o
), pspec
->name
,
1559 /* based on the type, recurse and display values */
1560 if (valstr
== NULL
) {
1561 typeno
= G_TYPE_FUNDAMENTAL( G_VALUE_TYPE(&value
) );
1564 number
= g_value_get_enum(&value
);
1565 valstr
= g_strdup_printf("%d", number
);
1568 number
= g_value_get_int(&value
);
1569 valstr
= g_strdup_printf("%d", number
);
1572 number64
= (int64_t)g_value_get_int64(&value
);
1573 valstr
= g_strdup_printf("%" PRIo64
, number64
);
1576 unumber
= g_value_get_uint(&value
);
1577 valstr
= g_strdup_printf("%d", unumber
);
1581 (uint64_t)g_value_get_uint64(&value
);
1583 g_strdup_printf("%" PRIu64
, unumber64
);
1586 unumber
= g_value_get_flags(&value
);
1587 valstr
= g_strdup_printf("0x%x", unumber
);
1589 case G_TYPE_BOOLEAN
:
1590 boolean
= g_value_get_boolean(&value
);
1591 valstr
= g_strdup_printf("%s",
1592 boolean
? "TRUE" : "FALSE");
1595 fp
= g_value_get_float(&value
);
1596 valstr
= g_strdup_printf("%f", fp
);
1599 fpd
= g_value_get_double(&value
);
1600 valstr
= g_strdup_printf("%f", fpd
);
1603 string
= g_value_get_string(&value
);
1604 valstr
= g_strdup_printf("\"%s\"",
1608 object
= g_value_get_object(&value
);
1609 if (object
!= NULL
) {
1611 tmpstr
= g_strdup_printf("%s ",
1613 tmpsettings
= show_g_object_settings(
1614 object
, tmpstr
, recurse
);
1615 valstr
= g_strdup_printf(
1619 g_free(tmpsettings
);
1621 valstr
= g_strdup_printf("<...>");
1624 valstr
= g_strdup_printf("NULL");
1628 valstr
= g_strdup_printf(
1629 "type %s unhandled",
1635 body
= g_strdup_printf(
1636 "%s%s: %3d: flags=0x%08x, %-13s %s = %s\n",
1637 body
, str
, i
, pspec
->flags
, tname
, pspec
->name
,
1647 about_webkit(struct tab
*t
, struct karg
*arg
)
1649 char *page
, *body
, *settingstr
;
1651 settingstr
= show_g_object_settings(G_OBJECT(t
->settings
),
1653 body
= g_strdup_printf("<pre>%s</pre>\n", settingstr
);
1656 page
= get_html_page("About Webkit", body
, "", 0);
1659 load_webkit_string(t
, page
, XT_URI_ABOUT_WEBKIT
);
1666 allthethings(struct tab
*t
, struct karg
*arg
)
1668 char *page
, *body
, *b
, *settingstr
;
1669 extern GtkWidget
*main_window
;
1671 body
= show_g_object_settings(G_OBJECT(t
->wv
), "t->wv", 1);
1673 settingstr
= show_g_object_settings(G_OBJECT(t
->inspector
),
1675 body
= g_strdup_printf("%s%s", body
, settingstr
);
1679 settingstr
= show_g_object_settings(G_OBJECT(main_window
),
1681 body
= g_strdup_printf("%s%s", body
, settingstr
);
1685 body
= g_strdup_printf("<pre>%scan paste clipboard = %d\n</pre>", body
,
1686 webkit_web_view_can_paste_clipboard(t
->wv
));
1689 page
= get_html_page("About All The Things _o/", body
, "", 0);
1692 load_webkit_string(t
, page
, XT_URI_ABOUT_ALLTHETHINGS
);