3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010 Edd Barrett <vext01@gmail.com>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 * inverse color browsing
26 * multi letter commands
27 * pre and post counts for commands
29 * autocompletion on various inputs
30 * create privacy browsing
31 * - encrypted local data
45 #include "linux/tree.h"
49 #include <sys/queue.h>
50 #include <sys/types.h>
52 #include <sys/socket.h>
56 #include <gdk/gdkkeysyms.h>
57 #include <webkit/webkit.h>
58 #include <libsoup/soup.h>
59 #include <gnutls/gnutls.h>
60 #include <JavaScriptCore/JavaScript.h>
61 #include <gnutls/x509.h>
63 #include "javascript.h"
66 javascript.h borrowed from vimprobable2 under the following license:
68 Copyright (c) 2009 Leon Winter
69 Copyright (c) 2009 Hannes Schueller
70 Copyright (c) 2009 Matto Fransen
72 Permission is hereby granted, free of charge, to any person obtaining a copy
73 of this software and associated documentation files (the "Software"), to deal
74 in the Software without restriction, including without limitation the rights
75 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
76 copies of the Software, and to permit persons to whom the Software is
77 furnished to do so, subject to the following conditions:
79 The above copyright notice and this permission notice shall be included in
80 all copies or substantial portions of the Software.
82 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
83 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
84 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
85 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
86 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
87 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
91 static char *version
= "$xxxterm$";
93 /* hooked functions */
94 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
95 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
100 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
101 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
102 #define XT_D_MOVE 0x0001
103 #define XT_D_KEY 0x0002
104 #define XT_D_TAB 0x0004
105 #define XT_D_URL 0x0008
106 #define XT_D_CMD 0x0010
107 #define XT_D_NAV 0x0020
108 #define XT_D_DOWNLOAD 0x0040
109 #define XT_D_CONFIG 0x0080
110 #define XT_D_JS 0x0100
111 #define XT_D_FAVORITE 0x0200
112 #define XT_D_PRINTING 0x0400
113 #define XT_D_COOKIE 0x0800
114 u_int32_t swm_debug
= 0
129 #define DPRINTF(x...)
130 #define DNPRINTF(n,x...)
133 #define LENGTH(x) (sizeof x / sizeof x[0])
134 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
135 ~(GDK_BUTTON1_MASK) & \
136 ~(GDK_BUTTON2_MASK) & \
137 ~(GDK_BUTTON3_MASK) & \
138 ~(GDK_BUTTON4_MASK) & \
150 TAILQ_ENTRY(tab
) entry
;
152 GtkWidget
*tab_content
;
155 GtkWidget
*uri_entry
;
156 GtkWidget
*search_entry
;
158 GtkWidget
*browser_win
;
163 GtkWidget
*js_toggle
;
167 /* adjustments for browser */
170 GtkAdjustment
*adjust_h
;
171 GtkAdjustment
*adjust_v
;
177 int xtp_meaning
; /* identifies dls/favorites */
182 #define XT_HINT_NONE (0)
183 #define XT_HINT_NUMERICAL (1)
184 #define XT_HINT_ALPHANUM (2)
193 WebKitWebSettings
*settings
;
197 TAILQ_HEAD(tab_list
, tab
);
200 RB_ENTRY(history
) entry
;
204 RB_HEAD(history_list
, history
);
207 RB_ENTRY(download
) entry
;
209 WebKitDownload
*download
;
212 RB_HEAD(download_list
, download
);
215 RB_ENTRY(domain
) entry
;
217 int handy
; /* app use */
219 RB_HEAD(domain_list
, domain
);
222 TAILQ_ENTRY(undo
) entry
;
225 int back
; /* Keeps track of how many back
226 * history items there are. */
228 TAILQ_HEAD(undo_tailq
, undo
);
230 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
231 int next_download_id
= 1;
239 #define XT_NAME ("XXXTerm")
240 #define XT_DIR (".xxxterm")
241 #define XT_CERT_DIR ("certs/")
242 #define XT_CONF_FILE ("xxxterm.conf")
243 #define XT_FAVS_FILE ("favorites")
244 #define XT_SAVED_TABS_FILE ("saved_tabs")
245 #define XT_RESTART_TABS_FILE ("restart_tabs")
246 #define XT_SOCKET_FILE ("socket")
247 #define XT_HISTORY_FILE ("history")
248 #define XT_CB_HANDLED (TRUE)
249 #define XT_CB_PASSTHROUGH (FALSE)
250 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
251 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
252 #define XT_DLMAN_REFRESH "10"
253 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
254 "td {overflow: hidden;}\n" \
255 "th {background-color: #cccccc}" \
256 "table {width: 90%%; border: 1px black" \
257 " solid; table-layout: fixed}\n</style>\n\n"
258 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
259 #define XT_MAX_UNDO_CLOSE_TAB (32)
262 #define SZ_KB ((uint64_t) 1024)
263 #define SZ_MB (SZ_KB * SZ_KB)
264 #define SZ_GB (SZ_KB * SZ_KB * SZ_KB)
265 #define SZ_TB (SZ_KB * SZ_KB * SZ_KB * SZ_KB)
268 * xxxterm "protocol" (xtp)
269 * We use this for managing stuff like downloads and favorites. They
270 * make magical HTML pages in memory which have xxxt:// links in order
271 * to communicate with xxxterm's internals. These links take the format:
272 * xxxt://class/session_key/action/arg
274 * Don't begin xtp class/actions as 0. atoi returns that on error.
276 * Typically we have not put addition of items in this framework, as
277 * adding items is either done via an ex-command or via a keybinding instead.
280 #define XT_XTP_STR "xxxt://"
282 /* XTP classes (xxxt://<class>) */
283 #define XT_XTP_DL 1 /* downloads */
284 #define XT_XTP_HL 2 /* history */
285 #define XT_XTP_CL 3 /* cookies */
286 #define XT_XTP_FL 4 /* favorites */
288 /* XTP download actions */
289 #define XT_XTP_DL_LIST 1
290 #define XT_XTP_DL_CANCEL 2
291 #define XT_XTP_DL_REMOVE 3
293 /* XTP history actions */
294 #define XT_XTP_HL_LIST 1
295 #define XT_XTP_HL_REMOVE 2
297 /* XTP cookie actions */
298 #define XT_XTP_CL_LIST 1
299 #define XT_XTP_CL_REMOVE 2
301 /* XTP cookie actions */
302 #define XT_XTP_FL_LIST 1
303 #define XT_XTP_FL_REMOVE 2
305 /* xtp tab meanings - identifies which tabs have xtp pages in */
306 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
307 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
308 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
309 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
310 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
313 #define XT_MOVE_INVALID (0)
314 #define XT_MOVE_DOWN (1)
315 #define XT_MOVE_UP (2)
316 #define XT_MOVE_BOTTOM (3)
317 #define XT_MOVE_TOP (4)
318 #define XT_MOVE_PAGEDOWN (5)
319 #define XT_MOVE_PAGEUP (6)
320 #define XT_MOVE_HALFDOWN (7)
321 #define XT_MOVE_HALFUP (8)
322 #define XT_MOVE_LEFT (9)
323 #define XT_MOVE_FARLEFT (10)
324 #define XT_MOVE_RIGHT (11)
325 #define XT_MOVE_FARRIGHT (12)
327 #define XT_TAB_LAST (-4)
328 #define XT_TAB_FIRST (-3)
329 #define XT_TAB_PREV (-2)
330 #define XT_TAB_NEXT (-1)
331 #define XT_TAB_INVALID (0)
332 #define XT_TAB_NEW (1)
333 #define XT_TAB_DELETE (2)
334 #define XT_TAB_DELQUIT (3)
335 #define XT_TAB_OPEN (4)
336 #define XT_TAB_UNDO_CLOSE (5)
338 #define XT_NAV_INVALID (0)
339 #define XT_NAV_BACK (1)
340 #define XT_NAV_FORWARD (2)
341 #define XT_NAV_RELOAD (3)
342 #define XT_NAV_RELOAD_CACHE (4)
344 #define XT_FOCUS_INVALID (0)
345 #define XT_FOCUS_URI (1)
346 #define XT_FOCUS_SEARCH (2)
348 #define XT_SEARCH_INVALID (0)
349 #define XT_SEARCH_NEXT (1)
350 #define XT_SEARCH_PREV (2)
352 #define XT_PASTE_CURRENT_TAB (0)
353 #define XT_PASTE_NEW_TAB (1)
355 #define XT_FONT_SET (0)
357 #define XT_WL_TOGGLE (1<<0)
358 #define XT_WL_ENABLE (1<<1)
359 #define XT_WL_DISABLE (1<<2)
360 #define XT_WL_FQDN (1<<3) /* default */
361 #define XT_WL_TOPLEVEL (1<<4)
363 #define XT_CMD_OPEN (0)
364 #define XT_CMD_OPEN_CURRENT (1)
365 #define XT_CMD_TABNEW (2)
366 #define XT_CMD_TABNEW_CURRENT (3)
373 TAILQ_ENTRY(mime_type
) entry
;
375 TAILQ_HEAD(mime_type_list
, mime_type
);
381 TAILQ_ENTRY(alias
) entry
;
383 TAILQ_HEAD(alias_list
, alias
);
385 /* settings that require restart */
386 int showtabs
= 1; /* show tabs on notebook */
387 int showurl
= 1; /* show url toolbar on notebook */
388 int tabless
= 0; /* allow only 1 tab */
389 int enable_socket
= 0;
390 int single_instance
= 0; /* only allow one xxxterm to run */
391 int fancy_bar
= 1; /* fancy toolbar */
393 /* runtime settings */
394 int ctrl_click_focus
= 0; /* ctrl click gets focus */
395 int cookies_enabled
= 1; /* enable cookies */
396 int read_only_cookies
= 0; /* enable to not write cookies */
397 int enable_scripts
= 0;
398 int enable_plugins
= 0;
399 int default_font_size
= 12;
400 int window_height
= 768;
401 int window_width
= 1024;
402 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
403 unsigned refresh_interval
= 10; /* download refresh interval */
404 int enable_cookie_whitelist
= 1;
405 int enable_js_whitelist
= 1;
406 time_t session_timeout
= 3600; /* cookie session timeout */
407 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
408 char *ssl_ca_file
= NULL
;
409 char *resource_dir
= NULL
;
410 gboolean ssl_strict_certs
= FALSE
;
411 int append_next
= 1; /* append tab after current tab */
413 char *search_string
= NULL
;
414 char *http_proxy
= NULL
;
415 char download_dir
[PATH_MAX
];
416 char runtime_settings
[PATH_MAX
]; /* override of settings */
417 int allow_volatile_cookies
= 0;
418 int save_global_history
= 0; /* save global history to disk */
421 int set_download_dir(struct settings
*, char *);
422 int set_runtime_dir(struct settings
*, char *);
423 int set_cookie_policy(struct settings
*, char *);
424 int add_alias(struct settings
*, char *);
425 int add_mime_type(struct settings
*, char *);
426 int add_cookie_wl(struct settings
*, char *);
427 int add_js_wl(struct settings
*, char *);
428 void button_set_stockid(GtkWidget
*, char *);
429 GtkWidget
* create_button(char *, char *, int);
431 char *get_cookie_policy(struct settings
*);
433 char *get_download_dir(struct settings
*);
434 char *get_runtime_dir(struct settings
*);
436 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
437 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
438 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
439 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
442 int (*set
)(struct settings
*, char *);
443 char *(*get
)(struct settings
*);
444 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
447 struct special s_cookie
= {
453 struct special s_alias
= {
459 struct special s_mime
= {
465 struct special s_js
= {
471 struct special s_cookie_wl
= {
477 struct special s_download_dir
= {
483 struct special s_runtime
= {
492 #define XT_S_INVALID (0)
496 #define XT_SF_RESTART (1<<0)
497 #define XT_SF_RUNTIME (1<<1)
502 { "append_next", XT_S_INT
, 0 , &append_next
, NULL
, NULL
},
503 { "allow_volatile_cookies", XT_S_INT
, 0 , &allow_volatile_cookies
, NULL
, NULL
},
504 { "cookies_enabled", XT_S_INT
, 0 , &cookies_enabled
, NULL
, NULL
},
505 { "cookie_policy", XT_S_INT
, 0 , NULL
, NULL
, &s_cookie
},
506 { "ctrl_click_focus", XT_S_INT
, 0 , &ctrl_click_focus
, NULL
, NULL
},
507 { "default_font_size", XT_S_INT
, 0 , &default_font_size
, NULL
, NULL
},
508 { "download_dir", XT_S_STR
, 0 , NULL
, NULL
, &s_download_dir
},
509 { "enable_cookie_whitelist", XT_S_INT
, 0 , &enable_cookie_whitelist
, NULL
, NULL
},
510 { "enable_js_whitelist", XT_S_INT
, 0 , &enable_js_whitelist
, NULL
, NULL
},
511 { "enable_plugins", XT_S_INT
, 0 , &enable_plugins
, NULL
, NULL
},
512 { "enable_scripts", XT_S_INT
, 0 , &enable_scripts
, NULL
, NULL
},
513 { "enable_socket", XT_S_INT
, XT_SF_RESTART
, &enable_socket
, NULL
, NULL
},
514 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
, &fancy_bar
, NULL
, NULL
},
515 { "home", XT_S_STR
, 0 , NULL
, &home
, NULL
},
516 { "http_proxy", XT_S_STR
, 0 , NULL
, &http_proxy
, NULL
},
517 { "icon_size", XT_S_INT
, 0 , &icon_size
, NULL
, NULL
},
518 { "read_only_cookies", XT_S_INT
, 0 , &read_only_cookies
, NULL
, NULL
},
519 { "refresh_interval", XT_S_INT
, 0 , &refresh_interval
, NULL
, NULL
},
520 { "resource_dir", XT_S_STR
, 0 , NULL
, &resource_dir
, NULL
},
521 { "runtime_settings", XT_S_STR
, 0 , NULL
, NULL
, &s_runtime
},
522 { "search_string", XT_S_STR
, 0 , NULL
, &search_string
, NULL
},
523 { "session_timeout", XT_S_INT
, 0 , &session_timeout
, NULL
, NULL
},
524 { "save_global_history", XT_S_INT
, XT_SF_RESTART
, &save_global_history
, NULL
, NULL
},
525 { "single_instance", XT_S_INT
, XT_SF_RESTART
, &single_instance
, NULL
, NULL
},
526 { "ssl_ca_file", XT_S_STR
, 0 , NULL
, &ssl_ca_file
, NULL
},
527 { "ssl_strict_certs", XT_S_INT
, 0 , &ssl_strict_certs
, NULL
, NULL
},
528 { "window_height", XT_S_INT
, 0 , &window_height
, NULL
, NULL
},
529 { "window_width", XT_S_INT
, 0 , &window_width
, NULL
, NULL
},
531 /* runtime settings */
532 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
533 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
534 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
535 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
539 extern char *__progname
;
542 GtkWidget
*main_window
;
543 GtkNotebook
*notebook
;
544 GtkWidget
*arrow
, *abtn
;
545 struct tab_list tabs
;
546 struct history_list hl
;
547 struct download_list downloads
;
548 struct domain_list c_wl
;
549 struct domain_list js_wl
;
550 struct undo_tailq undos
;
552 int updating_dl_tabs
= 0;
553 int updating_hl_tabs
= 0;
554 int updating_cl_tabs
= 0;
555 int updating_fl_tabs
= 0;
557 uint64_t blocked_cookies
= 0;
560 get_as_string(struct settings
*s
)
571 warnx("get_as_string skip %s\n", s
->name
);
572 } else if (s
->type
== XT_S_INT
)
573 r
= g_strdup_printf("%d", *s
->ival
);
574 else if (s
->type
== XT_S_STR
)
575 r
= g_strdup(*s
->sval
);
577 r
= g_strdup_printf("INVALID TYPE");
583 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
588 for (i
= 0; i
< LENGTH(rs
); i
++) {
589 if (rs
[i
].s
&& rs
[i
].s
->walk
)
590 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
592 s
= get_as_string(&rs
[i
]);
593 cb(&rs
[i
], s
, cb_args
);
600 set_cookie_policy(struct settings
*s
, char *val
)
602 if (!strcmp(val
, "no3rdparty"))
603 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
604 else if (!strcmp(val
, "accept"))
605 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
606 else if (!strcmp(val
, "reject"))
607 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
615 get_cookie_policy(struct settings
*s
)
619 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
620 r
= g_strdup("no3rdparty");
621 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
622 r
= g_strdup("accept");
623 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
624 r
= g_strdup("reject");
632 get_download_dir(struct settings
*s
)
634 if (download_dir
[0] == '\0')
636 return (g_strdup(download_dir
));
640 set_download_dir(struct settings
*s
, char *val
)
643 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
644 pwd
->pw_dir
, &val
[1]);
646 strlcpy(download_dir
, val
, sizeof download_dir
);
652 get_runtime_dir(struct settings
*s
)
654 if (runtime_settings
[0] == '\0')
656 return (g_strdup(runtime_settings
));
660 set_runtime_dir(struct settings
*s
, char *val
)
663 snprintf(runtime_settings
, sizeof runtime_settings
, "%s/%s",
664 pwd
->pw_dir
, &val
[1]);
666 strlcpy(runtime_settings
, val
, sizeof runtime_settings
);
673 * We use these to prevent people putting xxxt:// URLs on
674 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
676 #define XT_XTP_SES_KEY_SZ 8
677 #define XT_XTP_SES_KEY_HEX_FMT \
678 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
679 char *dl_session_key
; /* downloads */
680 char *hl_session_key
; /* history list */
681 char *cl_session_key
; /* cookie list */
682 char *fl_session_key
; /* favorites list */
684 char work_dir
[PATH_MAX
];
685 char certs_dir
[PATH_MAX
];
686 char cookie_file
[PATH_MAX
];
687 SoupURI
*proxy_uri
= NULL
;
688 SoupSession
*session
;
689 SoupCookieJar
*s_cookiejar
;
690 SoupCookieJar
*p_cookiejar
;
692 struct mime_type_list mtl
;
693 struct alias_list aliases
;
696 void create_new_tab(char *, struct undo
*, int);
697 void delete_tab(struct tab
*);
698 void adjustfont_webkit(struct tab
*, int);
699 int run_script(struct tab
*, char *);
700 int download_rb_cmp(struct download
*, struct download
*);
701 int xtp_page_hl(struct tab
*t
, struct karg
*args
);
702 int xtp_page_dl(struct tab
*t
, struct karg
*args
);
703 int xtp_page_cl(struct tab
*t
, struct karg
*args
);
704 int xtp_page_fl(struct tab
*t
, struct karg
*args
);
707 history_rb_cmp(struct history
*h1
, struct history
*h2
)
709 return (strcmp(h1
->uri
, h2
->uri
));
711 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
714 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
716 return (strcmp(d1
->d
, d2
->d
));
718 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
721 * generate a session key to secure xtp commands.
722 * pass in a ptr to the key in question and it will
723 * be modified in place.
726 generate_xtp_session_key(char **key
)
728 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
735 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
736 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
737 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
738 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
740 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
744 * validate a xtp session key.
748 validate_xtp_session_key(char *trusted
, char *untrusted
)
750 if (strcmp(trusted
, untrusted
) != 0) {
751 warn("%s: xtp session key mismatch possible spoof", __func__
);
759 download_rb_cmp(struct download
*e1
, struct download
*e2
)
761 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
763 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
765 struct valid_url_types
{
776 valid_url_type(char *url
)
780 for (i
= 0; i
< LENGTH(vut
); i
++)
781 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
788 print_cookie(char *msg
, SoupCookie
*c
)
794 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
795 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
796 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
797 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
798 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
799 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
800 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
801 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
802 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
803 DNPRINTF(XT_D_COOKIE
, "====================================\n");
807 walk_alias(struct settings
*s
,
808 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
813 if (s
== NULL
|| cb
== NULL
)
814 errx(1, "walk_alias");
816 TAILQ_FOREACH(a
, &aliases
, entry
) {
817 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
824 match_alias(char *url_in
)
828 char *url_out
= NULL
;
831 if (strsep(&arg
, " \t") == NULL
)
832 errx(1, "match_alias: NULL URL");
834 TAILQ_FOREACH(a
, &aliases
, entry
) {
835 if (!strcmp(url_in
, a
->a_name
))
840 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
843 url_out
= g_strdup_printf(a
->a_uri
, arg
);
845 url_out
= g_strdup(a
->a_uri
);
852 guess_url_type(char *url_in
)
855 char *url_out
= NULL
;
857 url_out
= match_alias(url_in
);
861 /* XXX not sure about this heuristic */
862 if (stat(url_in
, &sb
) == 0)
863 url_out
= g_strdup_printf("file://%s", url_in
);
865 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
867 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
873 add_alias(struct settings
*s
, char *line
)
879 errx(1, "add_alias");
882 a
= g_malloc(sizeof(*a
));
884 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
)
885 errx(1, "add_alias: incomplete alias definition");
887 if (strlen(alias
) == 0 || strlen(l
) == 0)
888 errx(1, "add_alias: invalid alias definition");
890 a
->a_name
= g_strdup(alias
);
891 a
->a_uri
= g_strdup(l
);
893 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
895 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
901 add_mime_type(struct settings
*s
, char *line
)
907 /* XXX this could be smarter */
910 errx(1, "add_mime_type");
913 m
= g_malloc(sizeof(*m
));
915 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
)
916 errx(1, "add_mime_type: invalid mime_type");
918 if (mime_type
[strlen(mime_type
) - 1] == '*') {
919 mime_type
[strlen(mime_type
) - 1] = '\0';
924 if (strlen(mime_type
) == 0 || strlen(l
) == 0)
925 errx(1, "add_mime_type: invalid mime_type");
927 m
->mt_type
= g_strdup(mime_type
);
928 m
->mt_action
= g_strdup(l
);
930 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
931 m
->mt_type
, m
->mt_action
, m
->mt_default
);
933 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
939 find_mime_type(char *mime_type
)
941 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
943 TAILQ_FOREACH(m
, &mtl
, entry
) {
945 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
948 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
961 walk_mime_type(struct settings
*s
,
962 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
967 if (s
== NULL
|| cb
== NULL
)
968 errx(1, "walk_mime_type");
970 TAILQ_FOREACH(m
, &mtl
, entry
) {
971 str
= g_strdup_printf("%s%s --> %s",
973 m
->mt_default
? "*" : "",
981 wl_add(char *str
, struct domain_list
*wl
, int handy
)
986 if (str
== NULL
|| wl
== NULL
)
991 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
993 /* treat *.moo.com the same as .moo.com */
994 if (str
[0] == '*' && str
[1] == '.')
996 else if (str
[0] == '.')
1001 d
= g_malloc(sizeof *d
);
1003 d
->d
= g_strdup_printf(".%s", str
);
1005 d
->d
= g_strdup(str
);
1008 if (RB_INSERT(domain_list
, wl
, d
))
1011 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1022 add_cookie_wl(struct settings
*s
, char *entry
)
1024 wl_add(entry
, &c_wl
, 1);
1029 walk_cookie_wl(struct settings
*s
,
1030 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1034 if (s
== NULL
|| cb
== NULL
)
1035 errx(1, "walk_cookie_wl");
1037 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1038 cb(s
, d
->d
, cb_args
);
1042 walk_js_wl(struct settings
*s
,
1043 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1047 if (s
== NULL
|| cb
== NULL
)
1048 errx(1, "walk_js_wl");
1050 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1051 cb(s
, d
->d
, cb_args
);
1055 add_js_wl(struct settings
*s
, char *entry
)
1057 wl_add(entry
, &js_wl
, 1 /* persistent */);
1062 wl_find(const gchar
*search
, struct domain_list
*wl
)
1065 struct domain
*d
= NULL
, dfind
;
1068 if (search
== NULL
|| wl
== NULL
)
1070 if (strlen(search
) < 2)
1073 if (search
[0] != '.')
1074 s
= g_strdup_printf(".%s", search
);
1076 s
= g_strdup(search
);
1078 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1081 d
= RB_FIND(domain_list
, wl
, &dfind
);
1095 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1101 if (s
== NULL
|| wl
== NULL
)
1104 if (!strncmp(s
, "http://", strlen("http://")))
1105 s
= &s
[strlen("http://")];
1106 else if (!strncmp(s
, "https://", strlen("https://")))
1107 s
= &s
[strlen("https://")];
1112 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1113 /* chop string at first slash */
1114 if (s
[i
] == '/' || s
[i
] == '\0') {
1117 r
= wl_find(ss
, wl
);
1126 get_toplevel_domain(char *domain
)
1133 if (strlen(domain
) < 2)
1136 s
= &domain
[strlen(domain
) - 1];
1137 while (s
!= domain
) {
1151 config_parse(char *filename
, int runtime
)
1154 char *line
, *cp
, *var
, *val
;
1155 size_t len
, lineno
= 0;
1157 char **s
, file
[PATH_MAX
];
1160 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1162 if (filename
== NULL
)
1165 if (runtime
&& runtime_settings
[0] != '\0') {
1166 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
1167 if (stat(file
, &sb
)) {
1168 warnx("runtime file doesn't exist, creating it");
1169 if ((f
= fopen(file
, "w")) == NULL
)
1171 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1175 strlcpy(file
, filename
, sizeof file
);
1177 if ((config
= fopen(file
, "r")) == NULL
) {
1178 warn("config_parse: cannot open %s", filename
);
1183 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1184 if (feof(config
) || ferror(config
))
1188 cp
+= (long)strspn(cp
, WS
);
1189 if (cp
[0] == '\0') {
1195 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1198 cp
+= (long)strspn(cp
, WS
);
1200 if ((val
= strsep(&cp
, "\0")) == NULL
)
1203 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1206 for (i
= 0, handled
= 0; i
< LENGTH(rs
); i
++) {
1207 if (strcmp(var
, rs
[i
].name
))
1211 if (rs
[i
].s
->set(&rs
[i
], val
))
1212 errx(1, "invalid value for %s", var
);
1216 switch (rs
[i
].type
) {
1225 errx(1, "invalid sval for %s",
1234 errx(1, "invalid type for %s", var
);
1240 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1249 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1255 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1259 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1262 JSStringGetUTF8CString(jsref
, s
, l
);
1263 JSStringRelease(jsref
);
1269 disable_hints(struct tab
*t
)
1271 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1272 bzero(t
->hint_num
, sizeof t
->hint_num
);
1273 run_script(t
, "vimprobable_clear()");
1275 t
->hint_mode
= XT_HINT_NONE
;
1279 enable_hints(struct tab
*t
)
1281 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1282 run_script(t
, "vimprobable_show_hints()");
1284 t
->hint_mode
= XT_HINT_NONE
;
1287 #define XT_JS_OPEN ("open;")
1288 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1289 #define XT_JS_FIRE ("fire;")
1290 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1291 #define XT_JS_FOUND ("found;")
1292 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1295 run_script(struct tab
*t
, char *s
)
1297 JSGlobalContextRef ctx
;
1298 WebKitWebFrame
*frame
;
1300 JSValueRef val
, exception
;
1303 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1304 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1306 frame
= webkit_web_view_get_main_frame(t
->wv
);
1307 ctx
= webkit_web_frame_get_global_context(frame
);
1309 str
= JSStringCreateWithUTF8CString(s
);
1310 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1311 NULL
, 0, &exception
);
1312 JSStringRelease(str
);
1314 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1316 es
= js_ref_to_string(ctx
, exception
);
1317 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1321 es
= js_ref_to_string(ctx
, val
);
1322 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1324 /* handle return value right here */
1325 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1327 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1330 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1331 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1332 &es
[XT_JS_FIRE_LEN
]);
1337 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1338 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1349 hint(struct tab
*t
, struct karg
*args
)
1352 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1354 if (t
->hints_on
== 0)
1362 /* Doesn't work fully, due to the following bug:
1363 * https://bugs.webkit.org/show_bug.cgi?id=51747
1366 restore_global_history(void)
1368 char file
[PATH_MAX
];
1374 snprintf(file
, sizeof file
, "%s/%s/%s",
1375 pwd
->pw_dir
, XT_DIR
, XT_HISTORY_FILE
);
1377 if ((f
= fopen(file
, "r")) == NULL
) {
1378 warnx("%s: fopen", __func__
);
1383 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1384 if (feof(f
) || ferror(f
))
1387 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1388 if (feof(f
) || ferror(f
)) {
1390 warnx("%s: broken history file\n", __func__
);
1394 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1395 webkit_web_history_item_new_with_data(uri
, title
);
1396 h
= g_malloc(sizeof(struct history
));
1397 h
->uri
= g_strdup(uri
);
1398 h
->title
= g_strdup(title
);
1399 RB_INSERT(history_list
, &hl
, h
);
1401 warnx("%s: failed to restore history\n", __func__
);
1417 save_global_history_to_disk(void)
1419 char file
[PATH_MAX
];
1423 snprintf(file
, sizeof file
, "%s/%s/%s",
1424 pwd
->pw_dir
, XT_DIR
, XT_HISTORY_FILE
);
1426 if ((f
= fopen(file
, "w")) == NULL
) {
1427 warnx("%s: fopen", __func__
);
1431 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1432 if (h
->uri
&& h
->title
)
1433 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
1442 quit(struct tab
*t
, struct karg
*args
)
1444 if (save_global_history
)
1445 save_global_history_to_disk();
1453 restore_saved_tabs(void)
1455 char file
[PATH_MAX
];
1458 int empty_saved_tabs_file
= 1;
1459 int unlink_file
= 0;
1462 snprintf(file
, sizeof file
, "%s/%s/%s",
1463 pwd
->pw_dir
, XT_DIR
, XT_RESTART_TABS_FILE
);
1464 if (stat(file
, &sb
) == -1)
1465 snprintf(file
, sizeof file
, "%s/%s/%s",
1466 pwd
->pw_dir
, XT_DIR
, XT_SAVED_TABS_FILE
);
1470 if ((f
= fopen(file
, "r")) == NULL
)
1471 return (empty_saved_tabs_file
);
1474 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1475 if (feof(f
) || ferror(f
))
1478 if (uri
&& strlen(uri
)) {
1479 create_new_tab(uri
, NULL
, 1);
1480 empty_saved_tabs_file
= 0;
1492 return (empty_saved_tabs_file
);
1496 save_tabs(struct tab
*t
, struct karg
*a
)
1498 char file
[PATH_MAX
];
1501 WebKitWebFrame
*frame
;
1509 snprintf(file
, sizeof file
, "%s/%s/%s",
1510 pwd
->pw_dir
, XT_DIR
, a
->s
);
1512 if ((f
= fopen(file
, "w")) == NULL
) {
1517 TAILQ_FOREACH(ti
, &tabs
, entry
) {
1518 frame
= webkit_web_view_get_main_frame(ti
->wv
);
1519 uri
= webkit_web_frame_get_uri(frame
);
1520 if (uri
&& strlen(uri
) > 0)
1521 fprintf(f
, "%s\n", uri
);
1530 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
1534 a
.s
= XT_SAVED_TABS_FILE
;
1542 yank_uri(struct tab
*t
, struct karg
*args
)
1544 WebKitWebFrame
*frame
;
1546 GtkClipboard
*clipboard
;
1548 frame
= webkit_web_view_get_main_frame(t
->wv
);
1549 uri
= webkit_web_frame_get_uri(frame
);
1553 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1554 gtk_clipboard_set_text(clipboard
, uri
, -1);
1565 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
1567 struct paste_args
*pap
;
1572 pap
= (struct paste_args
*)data
;
1575 case XT_PASTE_CURRENT_TAB
:
1576 webkit_web_view_load_uri(pap
->t
->wv
, text
);
1578 case XT_PASTE_NEW_TAB
:
1579 create_new_tab((char *)text
, NULL
, 1);
1587 paste_uri(struct tab
*t
, struct karg
*args
)
1589 GtkClipboard
*clipboard
;
1590 struct paste_args
*pap
;
1592 pap
= g_malloc(sizeof(struct paste_args
));
1597 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1598 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
1604 find_domain(const char *s
, int add_dot
)
1607 char *r
= NULL
, *ss
= NULL
;
1612 if (!strncmp(s
, "http://", strlen("http://")))
1613 s
= &s
[strlen("http://")];
1614 else if (!strncmp(s
, "https://", strlen("https://")))
1615 s
= &s
[strlen("https://")];
1621 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
1622 /* chop string at first slash */
1623 if (ss
[i
] == '/' || ss
[i
] == '\0') {
1626 r
= g_strdup_printf(".%s", ss
);
1637 toggle_cwl(struct tab
*t
, struct karg
*args
)
1639 WebKitWebFrame
*frame
;
1642 char *dom
= NULL
, *dom_toggle
= NULL
;
1648 frame
= webkit_web_view_get_main_frame(t
->wv
);
1649 uri
= (char *)webkit_web_frame_get_uri(frame
);
1650 dom
= find_domain(uri
, 1);
1651 d
= wl_find(dom
, &c_wl
);
1657 if (args
->i
& XT_WL_TOGGLE
)
1659 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
1661 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
1664 if (args
->i
& XT_WL_TOPLEVEL
)
1665 dom_toggle
= get_toplevel_domain(dom
);
1670 /* enable cookies for domain */
1671 wl_add(dom_toggle
, &c_wl
, 0);
1673 /* disable cookies for domain */
1674 RB_REMOVE(domain_list
, &c_wl
, d
);
1677 webkit_web_view_reload(t
->wv
);
1684 toggle_js(struct tab
*t
, struct karg
*args
)
1687 WebKitWebFrame
*frame
;
1690 char *dom
= NULL
, *dom_toggle
= NULL
;
1695 g_object_get((GObject
*)t
->settings
,
1696 "enable-scripts", &es
, (char *)NULL
);
1697 if (args
->i
& XT_WL_TOGGLE
)
1699 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
1701 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
1706 frame
= webkit_web_view_get_main_frame(t
->wv
);
1707 uri
= (char *)webkit_web_frame_get_uri(frame
);
1708 dom
= find_domain(uri
, 1);
1709 if (uri
== NULL
|| dom
== NULL
) {
1710 webkit_web_view_load_string(t
->wv
,
1711 "<html><body>Can't toggle domain in JavaScript white list</body></html>",
1718 if (args
->i
& XT_WL_TOPLEVEL
)
1719 dom_toggle
= get_toplevel_domain(dom
);
1724 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
1725 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
1727 d
= wl_find(dom_toggle
, &js_wl
);
1729 RB_REMOVE(domain_list
, &js_wl
, d
);
1730 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
1732 g_object_set((GObject
*)t
->settings
,
1733 "enable-scripts", es
, (char *)NULL
);
1734 webkit_web_view_set_settings(t
->wv
, t
->settings
);
1735 webkit_web_view_reload(t
->wv
);
1743 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
1747 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
1752 toggle_src(struct tab
*t
, struct karg
*args
)
1759 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
1760 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
1761 webkit_web_view_reload(t
->wv
);
1767 focus(struct tab
*t
, struct karg
*args
)
1769 if (t
== NULL
|| args
== NULL
)
1772 if (args
->i
== XT_FOCUS_URI
)
1773 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
1774 else if (args
->i
== XT_FOCUS_SEARCH
)
1775 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
1781 stats(struct tab
*t
, struct karg
*args
)
1788 stats
= g_strdup_printf(XT_DOCTYPE
1791 "<title>Statistics</title>"
1793 "<h1>Statistics</h1>"
1795 "Cookies blocked(*) this session: %llu\n"
1796 "<p><small><b>*</b> results vary based on settings"
1802 webkit_web_view_load_string(t
->wv
, stats
, NULL
, NULL
, "");
1809 about(struct tab
*t
, struct karg
*args
)
1816 about
= g_strdup_printf(XT_DOCTYPE
1819 "<title>About</title>"
1823 "<b>Version: %s</b><p>"
1826 "<li>Marco Peereboom <marco@peereboom.us></li>"
1827 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
1828 "<li>Edd Barrett <vext01@gmail.com> </li>"
1830 "Copyrights and licenses can be found on the XXXterm "
1831 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
1837 webkit_web_view_load_string(t
->wv
, about
, NULL
, NULL
, "");
1844 help(struct tab
*t
, struct karg
*args
)
1854 "<title>XXXterm</title>"
1855 "<meta http-equiv=\"REFRESH\" content=\"0;"
1856 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
1859 "XXXterm man page <a href=\"http://opensource.conformal.com/"
1860 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
1861 "cgi-bin/man-cgi?xxxterm</a>"
1866 webkit_web_view_load_string(t
->wv
, help
, NULL
, NULL
, "");
1872 * update all favorite tabs apart from one. Pass NULL if
1873 * you want to update all.
1876 update_favorite_tabs(struct tab
*apart_from
)
1879 if (!updating_fl_tabs
) {
1880 updating_fl_tabs
= 1; /* stop infinite recursion */
1881 TAILQ_FOREACH(t
, &tabs
, entry
)
1882 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
1883 && (t
!= apart_from
))
1884 xtp_page_fl(t
, NULL
);
1885 updating_fl_tabs
= 0;
1889 /* show a list of favorites (bookmarks) */
1891 xtp_page_fl(struct tab
*t
, struct karg
*args
)
1893 char file
[PATH_MAX
];
1895 char *uri
= NULL
, *title
= NULL
;
1896 size_t len
, lineno
= 0;
1898 char *header
, *body
, *tmp
, *html
= NULL
;
1900 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
1903 warn("%s: bad param", __func__
);
1905 /* mark tab as favorite list */
1906 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
1908 /* new session key */
1909 if (!updating_fl_tabs
)
1910 generate_xtp_session_key(&fl_session_key
);
1912 /* open favorites */
1913 snprintf(file
, sizeof file
, "%s/%s/%s",
1914 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
1915 if ((f
= fopen(file
, "r")) == NULL
) {
1921 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
1922 "<title>Favorites</title>\n"
1925 "<h1>Favorites</h1>\n",
1929 body
= g_strdup_printf("<div align='center'><table><tr>"
1930 "<th style='width: 4%%'>#</th><th>Link</th>"
1931 "<th style='width: 15%%'>Remove</th></tr>\n");
1934 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
1935 if (feof(f
) || ferror(f
))
1943 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
1944 if (feof(f
) || ferror(f
)) {
1945 errx(0, "%s: can't parse favorites\n",
1952 body
= g_strdup_printf("%s<tr>"
1954 "<td><a href='%s'>%s</a></td>"
1955 "<td style='text-align: center'>"
1956 "<a href='%s%d/%s/%d/%d'>X</a></td>"
1958 body
, i
, uri
, title
,
1959 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
1971 /* if none, say so */
1974 body
= g_strdup_printf("%s<tr>"
1975 "<td colspan='3' style='text-align: center'>"
1976 "No favorites - To add one use the 'favadd' command."
1977 "</td></tr>", body
);
1988 html
= g_strdup_printf("%s%s</table></div></html>",
1990 webkit_web_view_load_string(t
->wv
, html
, NULL
, NULL
, "");
1993 update_favorite_tabs(t
);
2006 getparams(char *cmd
, char *cmp
)
2011 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2012 rv
= cmd
+ strlen(cmp
);
2015 if (strlen(rv
) == 0)
2024 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2025 size_t cert_count
, char *title
)
2027 gnutls_datum_t cinfo
;
2028 char *tmp
, *header
, *body
, *footer
;
2031 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2032 footer
= g_strdup("</body></html>");
2033 body
= g_strdup("");
2035 for (i
= 0; i
< cert_count
; i
++) {
2036 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2041 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2042 body
, i
, cinfo
.data
);
2043 gnutls_free(cinfo
.data
);
2047 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2051 webkit_web_view_load_string(t
->wv
, tmp
, NULL
, NULL
, NULL
);
2056 ca_cmd(struct tab
*t
, struct karg
*args
)
2059 int rv
= 1, certs
= 0, certs_read
;
2062 gnutls_x509_crt_t
*c
= NULL
;
2063 char *certs_buf
= NULL
, *s
;
2065 /* yeah yeah stat race */
2066 if (stat(ssl_ca_file
, &sb
)) {
2067 warn("no CA file: %s", ssl_ca_file
);
2071 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
)
2074 certs_buf
= g_malloc(sb
.st_size
+ 1);
2075 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2079 certs_buf
[sb
.st_size
] = '\0';
2082 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2084 s
+= strlen("BEGIN CERTIFICATE");
2087 bzero(&dt
, sizeof dt
);
2088 dt
.data
= certs_buf
;
2089 dt
.size
= sb
.st_size
;
2090 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2091 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
, GNUTLS_X509_FMT_PEM
, 0);
2092 if (certs_read
<= 0) {
2093 warnx("couldn't read certs");
2096 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2109 connect_socket_from_uri(char *uri
, char *domain
, size_t domain_sz
)
2112 struct addrinfo hints
, *res
= NULL
, *ai
;
2116 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2119 su
= soup_uri_new(uri
);
2122 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2125 snprintf(port
, sizeof port
, "%d", su
->port
);
2126 bzero(&hints
, sizeof(struct addrinfo
));
2127 hints
.ai_flags
= AI_CANONNAME
;
2128 hints
.ai_family
= AF_UNSPEC
;
2129 hints
.ai_socktype
= SOCK_STREAM
;
2131 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2134 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2135 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2138 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2141 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2145 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2150 strlcpy(domain
, su
->host
, domain_sz
);
2161 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2164 gnutls_deinit(gsession
);
2166 gnutls_certificate_free_credentials(xcred
);
2172 start_tls(int s
, gnutls_session_t
*gs
, gnutls_certificate_credentials_t
*xc
)
2174 gnutls_certificate_credentials_t xcred
;
2175 gnutls_session_t gsession
;
2178 if (gs
== NULL
|| xc
== NULL
)
2181 gnutls_certificate_allocate_credentials(&xcred
);
2182 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2183 GNUTLS_X509_FMT_PEM
);
2184 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2185 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2186 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2187 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2188 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2189 warnx("gnutls_handshake failed %d", rv
);
2190 stop_tls(gsession
, xcred
);
2194 gnutls_credentials_type_t cred
;
2195 cred
= gnutls_auth_get_type(gsession
);
2196 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2197 warnx("invalid credential type");
2198 stop_tls(gsession
, xcred
);
2210 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2214 const gnutls_datum_t
*cl
;
2215 gnutls_x509_crt_t
*all_certs
;
2218 if (certs
== NULL
|| cert_count
== NULL
)
2220 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2222 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2226 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2227 for (i
= 0; i
< len
; i
++) {
2228 gnutls_x509_crt_init(&all_certs
[i
]);
2229 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2230 GNUTLS_X509_FMT_PEM
< 0)) {
2244 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2248 for (i
= 0; i
< cert_count
; i
++)
2249 gnutls_x509_crt_deinit(certs
[i
]);
2254 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2255 size_t cert_count
, char *domain
)
2258 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2263 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2266 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2267 if ((f
= fopen(file
, "w")) == NULL
) {
2272 for (i
= 0; i
< cert_count
; i
++) {
2273 cert_buf_sz
= sizeof cert_buf
;
2274 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2275 cert_buf
, &cert_buf_sz
)) {
2276 warnx("gnutls_x509_crt_export");
2279 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2280 warn("fwrite certs");
2285 /* not the best spot but oh well */
2286 gdk_color_parse("lightblue", &color
);
2287 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2293 load_compare_cert(struct tab
*t
, struct karg
*args
)
2295 WebKitWebFrame
*frame
;
2296 char *uri
, domain
[8182], file
[PATH_MAX
];
2297 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2298 int s
= -1, rv
= 1, i
;
2302 gnutls_session_t gsession
;
2303 gnutls_x509_crt_t
*certs
;
2304 gnutls_certificate_credentials_t xcred
;
2309 frame
= webkit_web_view_get_main_frame(t
->wv
);
2310 uri
= (char *)webkit_web_frame_get_uri(frame
);
2311 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2315 if (start_tls(s
, &gsession
, &xcred
)) {
2321 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2322 warnx("get_connection_certs");
2326 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2327 if ((f
= fopen(file
, "r")) == NULL
)
2330 for (i
= 0; i
< cert_count
; i
++) {
2331 cert_buf_sz
= sizeof cert_buf
;
2332 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2333 cert_buf
, &cert_buf_sz
)) {
2334 warnx("gnutls_x509_crt_export");
2337 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2338 warn("fread certs");
2339 rv
= -1; /* critical */
2342 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
2343 warnx("invalid cert");
2344 rv
= -1; /* critical */
2353 free_connection_certs(certs
, cert_count
);
2355 /* we close the socket first for speed */
2358 stop_tls(gsession
, xcred
);
2364 cert_cmd(struct tab
*t
, struct karg
*args
)
2366 WebKitWebFrame
*frame
;
2367 char *uri
, *action
, domain
[8182];
2370 gnutls_session_t gsession
;
2371 gnutls_x509_crt_t
*certs
;
2372 gnutls_certificate_credentials_t xcred
;
2377 if ((action
= getparams(args
->s
, "cert")))
2382 frame
= webkit_web_view_get_main_frame(t
->wv
);
2383 uri
= (char *)webkit_web_frame_get_uri(frame
);
2384 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2388 if (start_tls(s
, &gsession
, &xcred
)) {
2394 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2395 warnx("get_connection_certs");
2399 if (!strcmp(action
, "show"))
2400 show_certs(t
, certs
, cert_count
, "Certificate Chain");
2401 else if (!strcmp(action
, "save"))
2402 save_certs(t
, certs
, cert_count
, domain
);
2404 free_connection_certs(certs
, cert_count
);
2406 /* we close the socket first for speed */
2409 stop_tls(gsession
, xcred
);
2415 remove_cookie(int index
)
2421 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
2423 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2425 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
2429 print_cookie("remove cookie", c
);
2430 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
2435 soup_cookies_free(cf
);
2441 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
2444 char *tmp
, *header
, *body
, *footer
;
2445 int p_js
= 0, s_js
= 0;
2447 if (g_str_has_prefix(args
, "show a") ||
2448 !strcmp(args
, "show")) {
2452 } else if (g_str_has_prefix(args
, "show p")) {
2453 /* show persistent */
2455 } else if (g_str_has_prefix(args
, "show s")) {
2461 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
2463 footer
= g_strdup("</body></html>");
2464 body
= g_strdup("");
2469 body
= g_strdup_printf("%s<h2>Persitent</h2>", body
);
2471 RB_FOREACH_REVERSE(d
, domain_list
, wl
) {
2475 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
2483 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
2485 RB_FOREACH_REVERSE(d
, domain_list
, wl
) {
2489 body
= g_strdup_printf("%s%s", body
, d
->d
);
2494 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2498 webkit_web_view_load_string(t
->wv
, tmp
, NULL
, NULL
, NULL
);
2504 wl_save(struct tab
*t
, struct karg
*args
, int js
)
2506 char file
[PATH_MAX
];
2508 char *line
= NULL
, *lt
= NULL
;
2510 WebKitWebFrame
*frame
;
2511 char *dom
= NULL
, *uri
, *dom_save
= NULL
;
2518 if (t
== NULL
|| args
== NULL
)
2521 if (runtime_settings
[0] == '\0')
2524 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
2525 if ((f
= fopen(file
, "r+")) == NULL
)
2528 frame
= webkit_web_view_get_main_frame(t
->wv
);
2529 uri
= (char *)webkit_web_frame_get_uri(frame
);
2530 dom
= find_domain(uri
, 1);
2531 if (uri
== NULL
|| dom
== NULL
) {
2532 webkit_web_view_load_string(t
->wv
,
2533 "<html><body>Can't add domain to JavaScript white list</body></html>",
2540 if (g_str_has_prefix(args
->s
, "save d")) {
2542 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
2543 /* XXX this needs use feedback */
2544 warnx("bad bad bad");
2547 flags
= XT_WL_TOPLEVEL
;
2548 } else if (g_str_has_prefix(args
->s
, "save f") ||
2549 !strcmp(args
->s
, "save")) {
2554 /* XXX this needs use feedback */
2555 warnx("invalid command");
2559 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
2562 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
2565 if (!strcmp(line
, lt
))
2571 fprintf(f
, "%s\n", lt
);
2576 d
= wl_find(dom_save
, &js_wl
);
2579 d
= wl_find(dom_save
, &c_wl
);
2582 /* find and add to persistent jar */
2583 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2584 for (;cf
; cf
= cf
->next
) {
2586 if (!strcmp(dom_save
, ci
->domain
) ||
2587 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
2588 c
= soup_cookie_copy(ci
);
2589 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
2592 soup_cookies_free(cf
);
2610 cookie_cmd(struct tab
*t
, struct karg
*args
)
2615 if ((cmd
= getparams(args
->s
, "cookie")))
2621 if (g_str_has_prefix(cmd
, "show")) {
2622 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
2623 } else if (g_str_has_prefix(cmd
, "save")) {
2626 } else if (g_str_has_prefix(cmd
, "toggle")) {
2628 if (g_str_has_prefix(cmd
, "toggle d"))
2629 a
.i
|= XT_WL_TOPLEVEL
;
2633 } else if (g_str_has_prefix(cmd
, "delete")) {
2640 js_cmd(struct tab
*t
, struct karg
*args
)
2645 if ((cmd
= getparams(args
->s
, "js")))
2651 if (g_str_has_prefix(cmd
, "show")) {
2652 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
2653 } else if (g_str_has_prefix(cmd
, "save")) {
2656 } else if (g_str_has_prefix(cmd
, "toggle")) {
2658 if (g_str_has_prefix(cmd
, "toggle d"))
2659 a
.i
|= XT_WL_TOPLEVEL
;
2663 } else if (g_str_has_prefix(cmd
, "delete")) {
2670 add_favorite(struct tab
*t
, struct karg
*args
)
2672 char file
[PATH_MAX
];
2675 size_t urilen
, linelen
;
2676 WebKitWebFrame
*frame
;
2677 const gchar
*uri
, *title
;
2682 /* don't allow adding of xtp pages to favorites */
2683 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
2684 warn("%s: can't add xtp pages to favorites", __func__
);
2688 snprintf(file
, sizeof file
, "%s/%s/%s",
2689 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
2690 if ((f
= fopen(file
, "r+")) == NULL
) {
2695 title
= webkit_web_view_get_title(t
->wv
);
2696 frame
= webkit_web_view_get_main_frame(t
->wv
);
2697 uri
= webkit_web_frame_get_uri(frame
);
2701 if (title
== NULL
|| uri
== NULL
) {
2702 webkit_web_view_load_string(t
->wv
,
2703 "<html><body>can't add page to favorites</body></html>",
2710 urilen
= strlen(uri
);
2713 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
2714 if (linelen
== urilen
&& !strcmp(line
, uri
))
2720 fprintf(f
, "\n%s\n%s", title
, uri
);
2726 update_favorite_tabs(NULL
);
2732 navaction(struct tab
*t
, struct karg
*args
)
2734 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
2735 t
->tab_id
, args
->i
);
2739 webkit_web_view_go_back(t
->wv
);
2741 case XT_NAV_FORWARD
:
2742 webkit_web_view_go_forward(t
->wv
);
2745 webkit_web_view_reload(t
->wv
);
2747 case XT_NAV_RELOAD_CACHE
:
2748 webkit_web_view_reload_bypass_cache(t
->wv
);
2751 return (XT_CB_PASSTHROUGH
);
2755 move(struct tab
*t
, struct karg
*args
)
2757 GtkAdjustment
*adjust
;
2758 double pi
, si
, pos
, ps
, upper
, lower
, max
;
2763 case XT_MOVE_BOTTOM
:
2765 case XT_MOVE_PAGEDOWN
:
2766 case XT_MOVE_PAGEUP
:
2767 case XT_MOVE_HALFDOWN
:
2768 case XT_MOVE_HALFUP
:
2769 adjust
= t
->adjust_v
;
2772 adjust
= t
->adjust_h
;
2776 pos
= gtk_adjustment_get_value(adjust
);
2777 ps
= gtk_adjustment_get_page_size(adjust
);
2778 upper
= gtk_adjustment_get_upper(adjust
);
2779 lower
= gtk_adjustment_get_lower(adjust
);
2780 si
= gtk_adjustment_get_step_increment(adjust
);
2781 pi
= gtk_adjustment_get_page_increment(adjust
);
2784 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
2785 "max %f si %f pi %f\n",
2786 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
2787 pos
, ps
, upper
, lower
, max
, si
, pi
);
2793 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2798 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2800 case XT_MOVE_BOTTOM
:
2801 case XT_MOVE_FARRIGHT
:
2802 gtk_adjustment_set_value(adjust
, max
);
2805 case XT_MOVE_FARLEFT
:
2806 gtk_adjustment_set_value(adjust
, lower
);
2808 case XT_MOVE_PAGEDOWN
:
2810 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2812 case XT_MOVE_PAGEUP
:
2814 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2816 case XT_MOVE_HALFDOWN
:
2818 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2820 case XT_MOVE_HALFUP
:
2822 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2825 return (XT_CB_PASSTHROUGH
);
2828 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
2830 return (XT_CB_HANDLED
);
2834 tabaction(struct tab
*t
, struct karg
*args
)
2836 int rv
= XT_CB_HANDLED
;
2837 char *url
= NULL
, *newuri
= NULL
;
2840 DNPRINTF(XT_D_TAB
, "tabaction: %p %d %d\n", t
, args
->i
, t
->focus_wv
);
2843 return (XT_CB_PASSTHROUGH
);
2847 if ((url
= getparams(args
->s
, "tabnew")))
2848 create_new_tab(url
, NULL
, 1);
2850 create_new_tab(NULL
, NULL
, 1);
2855 case XT_TAB_DELQUIT
:
2856 if (gtk_notebook_get_n_pages(notebook
) > 1)
2862 if ((url
= getparams(args
->s
, "open")) ||
2863 ((url
= getparams(args
->s
, "op"))) ||
2864 ((url
= getparams(args
->s
, "o"))))
2867 rv
= XT_CB_PASSTHROUGH
;
2871 if (valid_url_type(url
)) {
2872 newuri
= guess_url_type(url
);
2875 webkit_web_view_load_uri(t
->wv
, url
);
2879 case XT_TAB_UNDO_CLOSE
:
2880 if (undo_count
== 0) {
2881 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
2885 u
= TAILQ_FIRST(&undos
);
2886 create_new_tab(u
->uri
, u
, 1);
2888 TAILQ_REMOVE(&undos
, u
, entry
);
2890 /* u->history is freed in create_new_tab() */
2895 rv
= XT_CB_PASSTHROUGH
;
2909 resizetab(struct tab
*t
, struct karg
*args
)
2911 if (t
== NULL
|| args
== NULL
)
2912 errx(1, "resizetab");
2914 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
2915 t
->tab_id
, args
->i
);
2917 adjustfont_webkit(t
, args
->i
);
2919 return (XT_CB_HANDLED
);
2923 movetab(struct tab
*t
, struct karg
*args
)
2928 if (t
== NULL
|| args
== NULL
)
2931 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
2932 t
->tab_id
, args
->i
);
2934 if (args
->i
== XT_TAB_INVALID
)
2935 return (XT_CB_PASSTHROUGH
);
2937 if (args
->i
< XT_TAB_INVALID
) {
2938 /* next or previous tab */
2939 if (TAILQ_EMPTY(&tabs
))
2940 return (XT_CB_PASSTHROUGH
);
2944 /* if at the last page, loop around to the first */
2945 if (gtk_notebook_get_current_page(notebook
) ==
2946 gtk_notebook_get_n_pages(notebook
) - 1) {
2947 gtk_notebook_set_current_page(notebook
, 0);
2949 gtk_notebook_next_page(notebook
);
2953 /* if at the first page, loop around to the last */
2954 if (gtk_notebook_current_page(notebook
) == 0) {
2955 gtk_notebook_set_current_page(notebook
,
2956 gtk_notebook_get_n_pages(notebook
) - 1);
2958 gtk_notebook_prev_page(notebook
);
2962 gtk_notebook_set_current_page(notebook
, 0);
2965 gtk_notebook_set_current_page(notebook
, -1);
2968 return (XT_CB_PASSTHROUGH
);
2971 return (XT_CB_HANDLED
);
2976 if (t
->tab_id
== x
) {
2977 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
2978 return (XT_CB_HANDLED
);
2981 TAILQ_FOREACH(tt
, &tabs
, entry
) {
2982 if (tt
->tab_id
== x
) {
2983 gtk_notebook_set_current_page(notebook
, x
);
2984 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
2986 gtk_widget_grab_focus(GTK_WIDGET(tt
->wv
));
2990 return (XT_CB_HANDLED
);
2994 command(struct tab
*t
, struct karg
*args
)
2996 WebKitWebFrame
*frame
;
2997 char *s
= NULL
, *ss
= NULL
;
3001 if (t
== NULL
|| args
== NULL
)
3020 case XT_CMD_OPEN_CURRENT
:
3023 case XT_CMD_TABNEW_CURRENT
:
3024 if (!s
) /* FALL THROUGH? */
3026 frame
= webkit_web_view_get_main_frame(t
->wv
);
3027 uri
= webkit_web_frame_get_uri(frame
);
3028 if (uri
&& strlen(uri
)) {
3029 ss
= g_strdup_printf("%s%s", s
, uri
);
3034 warnx("command: invalid command %c\n", args
->i
);
3035 return (XT_CB_PASSTHROUGH
);
3038 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3040 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3041 gdk_color_parse("white", &color
);
3042 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3043 gtk_widget_show(t
->cmd
);
3044 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3045 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3050 return (XT_CB_HANDLED
);
3054 * Return a new string with a download row (in html)
3055 * appended. Old string is freed.
3058 xtp_page_dl_row(char *html
, struct download
*dl
)
3061 WebKitDownloadStatus stat
;
3062 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3064 char cur_sz
[FMT_SCALED_STRSIZE
];
3065 char tot_sz
[FMT_SCALED_STRSIZE
];
3068 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3070 /* All actions wil take this form:
3071 * xxxt://class/seskey
3073 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3074 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3076 stat
= webkit_download_get_status(dl
->download
);
3079 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3080 status_html
= g_strdup_printf("Finished");
3081 cmd_html
= g_strdup_printf(
3082 "<a href='%s%d/%d'>Remove</a>",
3083 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3085 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3086 /* gather size info */
3087 progress
= 100 * webkit_download_get_progress(dl
->download
);
3090 webkit_download_get_current_size(dl
->download
), cur_sz
);
3092 webkit_download_get_total_size(dl
->download
), tot_sz
);
3094 status_html
= g_strdup_printf("%s of %s (%.2f%%)", cur_sz
,
3096 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3097 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3101 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3102 status_html
= g_strdup_printf("Cancelled");
3103 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3104 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3106 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3107 status_html
= g_strdup_printf("Error!");
3108 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3109 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3111 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3112 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3113 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3114 status_html
= g_strdup_printf("Starting");
3117 warn("%s: unknown download status", __func__
);
3120 new_html
= g_strdup_printf(
3121 "%s\n<tr><td>%s</td><td>%s</td>"
3122 "<td style='text-align:center'>%s</td></tr>\n",
3123 html
, webkit_download_get_uri(dl
->download
),
3124 status_html
, cmd_html
);
3128 g_free(status_html
);
3139 * update all download tabs apart from one. Pass NULL if
3140 * you want to update all.
3143 update_download_tabs(struct tab
*apart_from
)
3146 if (!updating_dl_tabs
) {
3147 updating_dl_tabs
= 1; /* stop infinite recursion */
3148 TAILQ_FOREACH(t
, &tabs
, entry
)
3149 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3150 && (t
!= apart_from
))
3151 xtp_page_dl(t
, NULL
);
3152 updating_dl_tabs
= 0;
3157 * update all cookie tabs apart from one. Pass NULL if
3158 * you want to update all.
3161 update_cookie_tabs(struct tab
*apart_from
)
3164 if (!updating_cl_tabs
) {
3165 updating_cl_tabs
= 1; /* stop infinite recursion */
3166 TAILQ_FOREACH(t
, &tabs
, entry
)
3167 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3168 && (t
!= apart_from
))
3169 xtp_page_cl(t
, NULL
);
3170 updating_cl_tabs
= 0;
3175 * update all history tabs apart from one. Pass NULL if
3176 * you want to update all.
3179 update_history_tabs(struct tab
*apart_from
)
3183 if (!updating_hl_tabs
) {
3184 updating_hl_tabs
= 1; /* stop infinite recursion */
3185 TAILQ_FOREACH(t
, &tabs
, entry
)
3186 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
3187 && (t
!= apart_from
))
3188 xtp_page_hl(t
, NULL
);
3189 updating_hl_tabs
= 0;
3193 /* cookie management XTP page */
3195 xtp_page_cl(struct tab
*t
, struct karg
*args
)
3197 char *header
, *body
, *footer
, *page
, *tmp
;
3198 int i
= 1; /* all ids start 1 */
3202 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3205 errx(1, "%s: null tab", __func__
);
3207 /* mark this tab as cookie jar */
3208 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
3210 /* Generate a new session key */
3211 if (!updating_cl_tabs
)
3212 generate_xtp_session_key(&cl_session_key
);
3215 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3216 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
3217 "</head><body><h1>Cookie Jar</h1>\n");
3220 body
= g_strdup_printf("<div align='center'><table><tr>"
3227 "<th>HTTP_only</th>"
3228 "<th>Remove</th></tr>\n");
3230 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3232 for (; cf
; cf
= cf
->next
) {
3236 body
= g_strdup_printf(
3238 "<td style='width: 10%%; word-break: break-all'>%s</td>"
3239 "<td style='width: 20%%; word-break: break-all'>%s</td>"
3240 "<td style='width: 10%%; word-break: break-all'>%s</td>"
3241 "<td style='width: 8%%; word-break: break-all'>%s</td>"
3242 "<td style='width: 12%%; word-break: break-all'>%s</td>"
3243 "<td style='width: 3%%; text-align: center'>%d</td>"
3244 "<td style='width: 3%%; text-align: center'>%d</td>"
3245 "<td style='width: 3%%; text-align: center'>"
3246 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3253 soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "",
3267 soup_cookies_free(cf
);
3269 /* small message if there are none */
3272 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3273 "colspan='8'>No Cookies</td></tr>\n", body
);
3278 footer
= g_strdup_printf("</table></div></body></html>");
3280 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3286 webkit_web_view_load_string(t
->wv
, page
, "text/html", "UTF-8", "");
3287 update_cookie_tabs(t
);
3295 xtp_page_hl(struct tab
*t
, struct karg
*args
)
3297 char *header
, *body
, *footer
, *page
, *tmp
;
3299 int i
= 1; /* all ids start 1 */
3301 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3304 errx(1, "%s: null tab", __func__
);
3306 /* mark this tab as history manager */
3307 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
3309 /* Generate a new session key */
3310 if (!updating_hl_tabs
)
3311 generate_xtp_session_key(&hl_session_key
);
3314 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
3315 "<title>History</title>\n"
3318 "<h1>History</h1>\n",
3322 body
= g_strdup_printf("<div align='center'><table><tr>"
3323 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
3325 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
3327 body
= g_strdup_printf(
3329 "<td><a href='%s'>%s</a></td>"
3331 "<td style='text-align: center'>"
3332 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3333 body
, h
->uri
, h
->uri
, h
->title
,
3334 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
3335 XT_XTP_HL_REMOVE
, i
);
3341 /* small message if there are none */
3344 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3345 "colspan='3'>No History</td></tr>\n", body
);
3350 footer
= g_strdup_printf("</table></div></body></html>");
3352 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3355 * update all history manager tabs as the xtp session
3356 * key has now changed. No need to update the current tab.
3357 * Already did that above.
3359 update_history_tabs(t
);
3365 webkit_web_view_load_string(t
->wv
, page
, "text/html", "UTF-8", "");
3372 * Generate a web page detailing the status of any downloads
3375 xtp_page_dl(struct tab
*t
, struct karg
*args
)
3377 struct download
*dl
;
3378 char *header
, *body
, *footer
, *page
, *tmp
;
3382 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
3385 errx(1, "%s: null tab", __func__
);
3387 /* mark as a download manager tab */
3388 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
3391 * Generate a new session key for next page instance.
3392 * This only happens for the top level call to xtp_page_dl()
3393 * in which case updating_dl_tabs is 0.
3395 if (!updating_dl_tabs
)
3396 generate_xtp_session_key(&dl_session_key
);
3398 /* header - with refresh so as to update */
3399 if (refresh_interval
>= 1)
3400 ref
= g_strdup_printf(
3401 "<meta http-equiv='refresh' content='%u"
3402 ";url=%s%d/%s/%d' />\n",
3412 header
= g_strdup_printf(
3414 "<title>Downloads</title>\n%s%s</head>\n",
3415 XT_DOCTYPE XT_HTML_TAG
,
3419 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
3420 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
3421 "</p><table><tr><th style='width: 60%%'>"
3422 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
3423 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
3425 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
3426 body
= xtp_page_dl_row(body
, dl
);
3430 /* message if no downloads in list */
3433 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
3434 " style='text-align: center'>"
3435 "No downloads</td></tr>\n", body
);
3440 footer
= g_strdup_printf("</table></div></body></html>");
3442 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3446 * update all download manager tabs as the xtp session
3447 * key has now changed. No need to update the current tab.
3448 * Already did that above.
3450 update_download_tabs(t
);
3457 webkit_web_view_load_string(t
->wv
, page
, "text/html", "UTF-8", "");
3464 search(struct tab
*t
, struct karg
*args
)
3468 if (t
== NULL
|| args
== NULL
)
3470 if (t
->search_text
== NULL
) {
3471 if (global_search
== NULL
)
3472 return (XT_CB_PASSTHROUGH
);
3474 t
->search_text
= g_strdup(global_search
);
3475 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
3476 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
3480 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
3481 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
3484 case XT_SEARCH_NEXT
:
3485 d
= t
->search_forward
;
3487 case XT_SEARCH_PREV
:
3488 d
= !t
->search_forward
;
3491 return (XT_CB_PASSTHROUGH
);
3494 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
3496 return (XT_CB_HANDLED
);
3499 struct settings_args
{
3505 print_setting(struct settings
*s
, char *val
, void *cb_args
)
3508 struct settings_args
*sa
= cb_args
;
3511 warnx("*** %s", s
->name
);
3515 if (s
->flags
& XT_SF_RUNTIME
)
3521 *sa
->body
= g_strdup_printf(
3523 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
3524 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
3536 set(struct tab
*t
, struct karg
*args
)
3538 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
3540 struct settings_args sa
;
3542 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
3543 bzero(&sa
, sizeof sa
);
3547 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3548 "\n<head><title>Settings</title>\n"
3549 "</head><body><h1>Settings</h1>\n");
3552 body
= g_strdup_printf("<div align='center'><table><tr>"
3553 "<th align='left'>Setting</th>"
3554 "<th align='left'>Value</th></tr>\n");
3556 settings_walk(print_setting
, &sa
);
3559 /* small message if there are none */
3562 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3563 "colspan='2'>No settings</td></tr>\n", body
);
3568 footer
= g_strdup_printf("</table></div></body></html>");
3570 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3576 webkit_web_view_load_string(t
->wv
, page
, "text/html", "UTF-8", "");
3578 fprintf(stderr
, "pars %s\n", pars
);
3581 return (XT_CB_PASSTHROUGH
);
3585 * Make a hardcopy of the page
3588 print_page(struct tab
*t
, struct karg
*args
)
3590 WebKitWebFrame
*frame
;
3592 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
3595 * for now we just call the GTK print box,
3596 * but later we might decide to hook in a command.
3598 frame
= webkit_web_view_get_main_frame(t
->wv
);
3599 webkit_web_frame_print(frame
);
3605 go_home(struct tab
*t
, struct karg
*args
)
3609 newuri
= guess_url_type((char *)home
);
3610 webkit_web_view_load_uri(t
->wv
, newuri
);
3617 restart(struct tab
*t
, struct karg
*args
)
3621 a
.s
= XT_RESTART_TABS_FILE
;
3623 execvp(start_argv
[0], start_argv
);
3629 /* inherent to GTK not all keys will be caught at all times */
3630 /* XXX sort key bindings */
3631 struct key_bindings
{
3635 int (*func
)(struct tab
*, struct karg
*);
3638 { GDK_MOD1_MASK
, 0, GDK_d
, xtp_page_dl
, {0} },
3639 { GDK_MOD1_MASK
, 0, GDK_h
, xtp_page_hl
, {0} },
3640 { GDK_CONTROL_MASK
, 0, GDK_p
, print_page
, {0}},
3641 { 0, 0, GDK_slash
, command
, {.i
= '/'} },
3642 { GDK_SHIFT_MASK
, 0, GDK_question
, command
, {.i
= '?'} },
3643 { GDK_SHIFT_MASK
, 0, GDK_colon
, command
, {.i
= ':'} },
3644 { GDK_CONTROL_MASK
, 0, GDK_q
, quit
, {0} },
3645 { GDK_MOD1_MASK
, 0, GDK_q
, restart
, {0} },
3646 { GDK_CONTROL_MASK
, 0, GDK_j
, toggle_js
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
3647 { GDK_MOD1_MASK
, 0, GDK_c
, toggle_cwl
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
3648 { GDK_CONTROL_MASK
, 0, GDK_s
, toggle_src
, {0} },
3649 { 0, 0, GDK_y
, yank_uri
, {0} },
3650 { 0, 0, GDK_p
, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
} },
3651 { GDK_SHIFT_MASK
, 0, GDK_P
, paste_uri
, {.i
= XT_PASTE_NEW_TAB
} },
3654 { 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
3655 { GDK_SHIFT_MASK
, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
3658 { 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
3659 { 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
3661 /* command aliases (handy when -S flag is used) */
3662 { 0, 0, GDK_F9
, command
, {.i
= XT_CMD_OPEN
} },
3663 { 0, 0, GDK_F10
, command
, {.i
= XT_CMD_OPEN_CURRENT
} },
3664 { 0, 0, GDK_F11
, command
, {.i
= XT_CMD_TABNEW
} },
3665 { 0, 0, GDK_F12
, command
, {.i
= XT_CMD_TABNEW_CURRENT
} },
3668 { 0, 0, GDK_f
, hint
, {.i
= 0} },
3671 { 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
3672 { GDK_MOD1_MASK
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
3673 { GDK_SHIFT_MASK
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
3674 { GDK_MOD1_MASK
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
3675 { 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
3676 { GDK_CONTROL_MASK
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
3677 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_R
, navaction
, {.i
= XT_NAV_RELOAD_CACHE
} },
3678 { GDK_CONTROL_MASK
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
3679 { GDK_MOD1_MASK
, 1, GDK_f
, xtp_page_fl
, {0} },
3681 /* vertical movement */
3682 { 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
3683 { 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
3684 { 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
3685 { 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
3686 { GDK_SHIFT_MASK
, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
3687 { 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
3688 { 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
3689 { 0, 0, GDK_g
, move
, {.i
= XT_MOVE_TOP
} }, /* XXX make this work */
3690 { 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3691 { GDK_CONTROL_MASK
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3692 { GDK_CONTROL_MASK
, 0, GDK_d
, move
, {.i
= XT_MOVE_HALFDOWN
} },
3693 { 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3694 { 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
3695 { GDK_CONTROL_MASK
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
3696 { GDK_CONTROL_MASK
, 0, GDK_u
, move
, {.i
= XT_MOVE_HALFUP
} },
3697 /* horizontal movement */
3698 { 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
3699 { 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
3700 { 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
3701 { 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
3702 { GDK_SHIFT_MASK
, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
3703 { 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
3706 { GDK_CONTROL_MASK
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
3707 { GDK_CONTROL_MASK
, 1, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
3708 { GDK_SHIFT_MASK
, 0, GDK_U
, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
3709 { GDK_CONTROL_MASK
, 0, GDK_1
, movetab
, {.i
= 1} },
3710 { GDK_CONTROL_MASK
, 0, GDK_2
, movetab
, {.i
= 2} },
3711 { GDK_CONTROL_MASK
, 0, GDK_3
, movetab
, {.i
= 3} },
3712 { GDK_CONTROL_MASK
, 0, GDK_4
, movetab
, {.i
= 4} },
3713 { GDK_CONTROL_MASK
, 0, GDK_5
, movetab
, {.i
= 5} },
3714 { GDK_CONTROL_MASK
, 0, GDK_6
, movetab
, {.i
= 6} },
3715 { GDK_CONTROL_MASK
, 0, GDK_7
, movetab
, {.i
= 7} },
3716 { GDK_CONTROL_MASK
, 0, GDK_8
, movetab
, {.i
= 8} },
3717 { GDK_CONTROL_MASK
, 0, GDK_9
, movetab
, {.i
= 9} },
3718 { GDK_CONTROL_MASK
, 0, GDK_0
, movetab
, {.i
= 10} },
3719 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
3720 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
3721 { GDK_CONTROL_MASK
, 0, GDK_Left
, movetab
, {.i
= XT_TAB_PREV
} },
3722 { GDK_CONTROL_MASK
, 0, GDK_Right
, movetab
, {.i
= XT_TAB_NEXT
} },
3723 { GDK_CONTROL_MASK
, 0, GDK_minus
, resizetab
, {.i
= -1} },
3724 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_plus
, resizetab
, {.i
= 1} },
3725 { GDK_CONTROL_MASK
, 0, GDK_equal
, resizetab
, {.i
= 1} },
3731 int (*func
)(struct tab
*, struct karg
*);
3734 { "q!", 0, quit
, {0} },
3735 { "qa", 0, quit
, {0} },
3736 { "qa!", 0, quit
, {0} },
3737 { "w", 0, save_tabs
, {.s
= XT_SAVED_TABS_FILE
} },
3738 { "wq", 0, save_tabs_and_quit
, {.s
= XT_SAVED_TABS_FILE
} },
3739 { "wq!", 0, save_tabs_and_quit
, {.s
= XT_SAVED_TABS_FILE
} },
3740 { "help", 0, help
, {0} },
3741 { "about", 0, about
, {0} },
3742 { "stats", 0, stats
, {0} },
3743 { "version", 0, about
, {0} },
3744 { "cookies", 0, xtp_page_cl
, {0} },
3745 { "fav", 0, xtp_page_fl
, {0} },
3746 { "favadd", 0, add_favorite
, {0} },
3747 { "js", 2, js_cmd
, {0} },
3748 { "cookie", 2, cookie_cmd
, {0} },
3749 { "cert", 1, cert_cmd
, {0} },
3750 { "ca", 0, ca_cmd
, {0} },
3751 { "dl" , 0, xtp_page_dl
, {0} },
3752 { "h" , 0, xtp_page_hl
, {0} },
3753 { "hist" , 0, xtp_page_hl
, {0} },
3754 { "history" , 0, xtp_page_hl
, {0} },
3755 { "home" , 0, go_home
, {0} },
3756 { "restart" , 0, restart
, {0} },
3758 { "1", 0, move
, {.i
= XT_MOVE_TOP
} },
3759 { "print", 0, print_page
, {0} },
3762 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3763 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3764 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3765 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
3766 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
3767 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
3768 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
3769 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
3770 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
3771 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
3772 /* XXX add count to these commands */
3773 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
3774 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
3775 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
3776 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
3777 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
3778 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
3779 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
3780 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
3781 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
3782 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
3785 { "set", 1, set
, {0} },
3789 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
3791 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
3793 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
3800 * cancel, remove, etc. downloads
3803 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
3805 struct download find
, *d
;
3807 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
3809 /* some commands require a valid download id */
3810 if (cmd
!= XT_XTP_DL_LIST
) {
3811 /* lookup download in question */
3813 d
= RB_FIND(download_list
, &downloads
, &find
);
3816 warn("%s: no such download", __func__
);
3821 /* decide what to do */
3823 case XT_XTP_DL_CANCEL
:
3824 webkit_download_cancel(d
->download
);
3826 case XT_XTP_DL_REMOVE
:
3827 webkit_download_cancel(d
->download
); /* just incase */
3828 g_object_unref(d
->download
);
3829 RB_REMOVE(download_list
, &downloads
, d
);
3831 case XT_XTP_DL_LIST
:
3835 warn("%s: unknown command", __func__
);
3838 xtp_page_dl(t
, NULL
);
3842 * Actions on history, only does one thing for now, but
3843 * we provide the function for future actions
3846 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
3848 struct history
*h
, *next
;
3852 case XT_XTP_HL_REMOVE
:
3853 /* walk backwards, as listed in reverse */
3854 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
3855 next
= RB_PREV(history_list
, &hl
, h
);
3857 RB_REMOVE(history_list
, &hl
, h
);
3858 g_free((gpointer
) h
->title
);
3859 g_free((gpointer
) h
->uri
);
3866 case XT_XTP_HL_LIST
:
3867 /* Nothing - just xtp_page_hl() below */
3870 warn("%s: unknown command", __func__
);
3874 xtp_page_hl(t
, NULL
);
3877 /* remove a favorite */
3879 remove_favorite(int index
)
3881 char file
[PATH_MAX
], *title
, *uri
;
3882 char *new_favs
, *tmp
;
3887 /* open favorites */
3888 snprintf(file
, sizeof file
, "%s/%s/%s",
3889 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
3891 if ((f
= fopen(file
, "r")) == NULL
) {
3892 warn("%s: can't open favorites", __func__
);
3896 /* build a string which will become the new favroites file */
3897 new_favs
= g_strdup_printf("%s", "");
3900 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
3901 if (feof(f
) || ferror(f
))
3909 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
3910 if (feof(f
) || ferror(f
)) {
3911 warn("%s: can't parse favorites", __func__
);
3916 /* as long as this isn't the one we are deleting add to file */
3919 new_favs
= g_strdup_printf("%s%s\n%s\n",
3920 new_favs
, title
, uri
);
3932 /* write back new favorites file */
3933 if ((f
= fopen(file
, "w")) == NULL
) {
3934 warn("%s: can't open favorites", __func__
);
3938 fwrite(new_favs
, strlen(new_favs
), 1, f
);
3951 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
3954 case XT_XTP_FL_LIST
:
3955 /* nothing, just the below call to xtp_page_fl() */
3957 case XT_XTP_FL_REMOVE
:
3958 remove_favorite(arg
);
3961 warn("%s: invalid favorites command", __func__
);
3965 xtp_page_fl(t
, NULL
);
3969 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
3972 case XT_XTP_CL_LIST
:
3973 /* nothing, just xtp_page_cl() */
3975 case XT_XTP_CL_REMOVE
:
3979 warn("%s: unknown cookie xtp command", __func__
);
3983 xtp_page_cl(t
, NULL
);
3986 /* link an XTP class to it's session key and handler function */
3987 struct xtp_despatch
{
3990 void (*handle_func
)(struct tab
*, uint8_t, int);
3993 struct xtp_despatch xtp_despatches
[] = {
3994 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
3995 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
3996 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
3997 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
3998 { NULL
, NULL
, NULL
}
4002 * is the url xtp protocol? (xxxt://)
4003 * if so, parse and despatch correct bahvior
4006 parse_xtp_url(struct tab
*t
, const char *url
)
4008 char *dup
= NULL
, *p
, *last
;
4009 uint8_t n_tokens
= 0;
4010 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
4011 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
4015 * tokens array meaning:
4017 * tokens[1] = session key
4018 * tokens[2] = action
4019 * tokens[3] = optional argument
4022 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
4024 /*xtp tab meaning is normal unless proven special */
4025 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4027 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
4030 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
4032 /* split out the url */
4033 for ((p
= strtok_r(dup
, "/", &last
)); p
;
4034 (p
= strtok_r(NULL
, "/", &last
))) {
4036 tokens
[n_tokens
++] = p
;
4039 /* should be atleast three fields 'class/seskey/command/arg' */
4043 dsp
= xtp_despatches
;
4044 req_class
= atoi(tokens
[0]);
4045 while (dsp
->xtp_class
!= NULL
) {
4046 if (dsp
->xtp_class
== req_class
) {
4053 /* did we find one atall? */
4054 if (dsp_match
== NULL
) {
4055 warn("%s: no matching xtp despatch found", __func__
);
4059 /* check session key and call despatch function */
4060 if (validate_xtp_session_key(*(dsp_match
->session_key
), tokens
[1])) {
4061 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
4074 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
4076 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
4077 char *newuri
= NULL
;
4079 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
4082 errx(1, "activate_uri_entry_cb");
4087 uri
+= strspn(uri
, "\t ");
4089 /* if xxxt:// treat specially */
4090 if (!parse_xtp_url(t
, uri
)) {
4091 if (valid_url_type((char *)uri
)) {
4092 newuri
= guess_url_type((char *)uri
);
4096 webkit_web_view_load_uri(t
->wv
, uri
);
4097 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4105 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
4107 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
4108 char *newuri
= NULL
;
4110 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
4113 errx(1, "activate_search_entry_cb");
4115 if (search_string
== NULL
) {
4116 warnx("no search_string");
4120 newuri
= g_strdup_printf(search_string
, search
);
4122 webkit_web_view_load_uri(t
->wv
, newuri
);
4123 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4130 check_and_set_js(gchar
*uri
, struct tab
*t
)
4132 struct domain
*d
= NULL
;
4135 if (uri
== NULL
|| t
== NULL
)
4138 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
4143 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
4144 es
? "enable" : "disable", uri
);
4146 g_object_set((GObject
*)t
->settings
,
4147 "enable-scripts", es
, (char *)NULL
);
4148 webkit_web_view_set_settings(t
->wv
, t
->settings
);
4150 button_set_stockid(t
->js_toggle
,
4151 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
4155 show_ca_status(struct tab
*t
, const char *uri
)
4157 WebKitWebFrame
*frame
;
4158 WebKitWebDataSource
*source
;
4159 WebKitNetworkRequest
*request
;
4160 SoupMessage
*message
;
4162 gchar
*col_str
= "white";
4165 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
4166 ssl_strict_certs
, ssl_ca_file
, uri
);
4170 if (ssl_ca_file
== NULL
) {
4171 if (g_str_has_prefix(uri
, "http://"))
4173 if (g_str_has_prefix(uri
, "https://")) {
4179 if (g_str_has_prefix(uri
, "http://") ||
4180 !g_str_has_prefix(uri
, "https://"))
4183 frame
= webkit_web_view_get_main_frame(t
->wv
);
4184 source
= webkit_web_frame_get_data_source(frame
);
4185 request
= webkit_web_data_source_get_request(source
);
4186 message
= webkit_network_request_get_message(request
);
4188 if (message
&& (soup_message_get_flags(message
) &
4189 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
4193 r
= load_compare_cert(t
, NULL
);
4195 col_str
= "lightblue";
4204 gdk_color_parse(col_str
, &color
);
4205 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
4210 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
4212 WebKitWebFrame
*frame
;
4213 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
4214 struct history
*h
, find
;
4216 const gchar
*s_loading
;
4218 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d\n",
4219 webkit_web_view_get_load_status(wview
));
4222 errx(1, "notify_load_status_cb");
4224 switch (webkit_web_view_get_load_status(wview
)) {
4225 case WEBKIT_LOAD_PROVISIONAL
:
4226 #if GTK_CHECK_VERSION(2, 20, 0)
4227 gtk_widget_show(t
->spinner
);
4228 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
4230 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
4232 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
4235 /* take focus if we are visible */
4236 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
4237 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4241 case WEBKIT_LOAD_COMMITTED
:
4242 frame
= webkit_web_view_get_main_frame(wview
);
4243 uri
= webkit_web_frame_get_uri(frame
);
4245 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
4247 /* check if js white listing is enabled */
4248 if (enable_js_whitelist
) {
4249 frame
= webkit_web_view_get_main_frame(wview
);
4250 uri
= webkit_web_frame_get_uri(frame
);
4251 check_and_set_js((gchar
*)uri
, t
);
4254 show_ca_status(t
, uri
);
4257 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
4258 title
= webkit_web_view_get_title(wview
);
4259 frame
= webkit_web_view_get_main_frame(wview
);
4260 uri
= webkit_web_frame_get_uri(frame
);
4268 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
4269 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
4272 if (!strncmp(uri
, "http://", strlen("http://")) ||
4273 !strncmp(uri
, "https://", strlen("https://")) ||
4274 !strncmp(uri
, "file://", strlen("file://")))
4279 h
= RB_FIND(history_list
, &hl
, &find
);
4283 h
= g_malloc(sizeof *h
);
4284 h
->uri
= g_strdup(uri
);
4286 h
->title
= g_strdup(title
);
4288 h
->title
= g_strdup(uri
);
4289 RB_INSERT(history_list
, &hl
, h
);
4290 update_history_tabs(NULL
);
4295 case WEBKIT_LOAD_FINISHED
:
4296 #if WEBKIT_CHECK_VERSION(1, 1, 18)
4297 case WEBKIT_LOAD_FAILED
:
4299 #if GTK_CHECK_VERSION(2, 20, 0)
4300 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
4301 gtk_widget_hide(t
->spinner
);
4303 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
4304 if (s_loading
&& !strcmp(s_loading
, "Loading"))
4305 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
4307 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
4311 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
4312 webkit_web_view_can_go_back(wview
));
4314 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
4315 webkit_web_view_can_go_forward(wview
));
4319 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
4321 run_script(t
, JS_HINTING
);
4325 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
4327 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
4328 progress
== 100 ? 0 : (double)progress
/ 100);
4332 webview_nw_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
4333 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
4334 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
4339 errx(1, "webview_nw_cb");
4341 DNPRINTF(XT_D_NAV
, "webview_nw_cb: %s\n",
4342 webkit_network_request_get_uri(request
));
4344 /* open in current tab */
4345 uri
= (char *)webkit_network_request_get_uri(request
);
4346 webkit_web_view_load_uri(t
->wv
, uri
);
4347 webkit_web_policy_decision_ignore(pd
);
4349 return (TRUE
); /* we made the decission */
4353 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
4354 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
4355 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
4360 errx(1, "webview_npd_cb");
4362 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
4364 webkit_network_request_get_uri(request
));
4366 uri
= (char *)webkit_network_request_get_uri(request
);
4368 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
4370 create_new_tab(uri
, NULL
, ctrl_click_focus
);
4371 webkit_web_policy_decision_ignore(pd
);
4372 return (TRUE
); /* we made the decission */
4375 webkit_web_policy_decision_use(pd
);
4376 return (TRUE
); /* we made the decission */
4380 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
4383 errx(1, "webview_cwv_cb");
4385 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
4386 webkit_web_view_get_uri(wv
));
4392 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
4394 /* we can not eat the event without throwing gtk off so defer it */
4396 /* catch middle click */
4397 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
4402 /* catch ctrl click */
4403 if (e
->type
== GDK_BUTTON_RELEASE
&&
4404 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
4409 return (XT_CB_PASSTHROUGH
);
4413 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
4415 struct mime_type
*m
;
4417 m
= find_mime_type(mime_type
);
4432 execlp(m
->mt_action
, m
->mt_action
,
4433 webkit_network_request_get_uri(request
), (void *)NULL
);
4442 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
4443 WebKitNetworkRequest
*request
, char *mime_type
,
4444 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
4447 errx(1, "webview_mimetype_cb");
4449 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
4450 t
->tab_id
, mime_type
);
4452 if (run_mimehandler(t
, mime_type
, request
) == 0) {
4453 webkit_web_policy_decision_ignore(decision
);
4454 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4458 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
4459 webkit_web_policy_decision_download(decision
);
4467 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
, struct tab
*t
)
4469 const gchar
*filename
;
4471 struct download
*download_entry
;
4474 if (wk_download
== NULL
|| t
== NULL
)
4475 errx(1, "%s: invalid pointers", __func__
);
4477 filename
= webkit_download_get_suggested_filename(wk_download
);
4478 if (filename
== NULL
)
4479 return (FALSE
); /* abort download */
4481 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
4483 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
4484 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
4486 webkit_download_set_destination_uri(wk_download
, uri
);
4488 if (webkit_download_get_status(wk_download
) ==
4489 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
4490 warn("%s: download failed to start", __func__
);
4492 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
4494 download_entry
= g_malloc(sizeof(struct download
));
4495 download_entry
->download
= wk_download
;
4496 download_entry
->tab
= t
;
4497 download_entry
->id
= next_download_id
++;
4498 RB_INSERT(download_list
, &downloads
, download_entry
);
4499 /* get from history */
4500 g_object_ref(wk_download
);
4501 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
4507 /* sync other download manager tabs */
4508 update_download_tabs(NULL
);
4511 * NOTE: never redirect/render the current tab before this
4512 * function returns. This will cause the download to never start.
4514 return (ret
); /* start download */
4517 /* XXX currently unused */
4519 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
4521 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
4524 errx(1, "webview_hover_cb");
4531 t
->hover
= g_strdup(uri
);
4532 } else if (t
->hover
) {
4539 webview_keypress_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
4542 char s
[2], buf
[128];
4543 const char *errstr
= NULL
;
4546 /* don't use w directly; use t->whatever instead */
4549 errx(1, "webview_keypress_cb");
4551 DNPRINTF(XT_D_KEY
, "webview_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
4552 e
->keyval
, e
->state
, t
);
4556 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
4558 return (XT_CB_HANDLED
);
4562 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
4563 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
4565 /* we have a string */
4567 /* we have a number */
4568 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
4576 /* XXX unfuck this */
4577 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
4578 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
4579 /* last input was numerical */
4581 l
= strlen(t
->hint_num
);
4588 t
->hint_num
[l
] = '\0';
4592 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
4593 /* last input was alphanumerical */
4595 l
= strlen(t
->hint_buf
);
4602 t
->hint_buf
[l
] = '\0';
4612 /* numerical input */
4613 if (CLEAN(e
->state
) == 0 &&
4614 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
4615 snprintf(s
, sizeof s
, "%c", e
->keyval
);
4616 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
4617 DNPRINTF(XT_D_JS
, "webview_keypress_cb: numerical %s\n",
4620 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
4622 DNPRINTF(XT_D_JS
, "webview_keypress_cb: invalid link number\n");
4625 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
4627 t
->hint_mode
= XT_HINT_NUMERICAL
;
4631 /* empty the counter buffer */
4632 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
4633 return (XT_CB_HANDLED
);
4636 /* alphanumerical input */
4638 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
4639 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
4640 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
4641 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
4642 snprintf(s
, sizeof s
, "%c", e
->keyval
);
4643 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
4644 DNPRINTF(XT_D_JS
, "webview_keypress_cb: alphanumerical %s\n",
4647 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
4650 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
4652 t
->hint_mode
= XT_HINT_ALPHANUM
;
4655 /* empty the counter buffer */
4656 bzero(t
->hint_num
, sizeof t
->hint_num
);
4657 return (XT_CB_HANDLED
);
4660 return (XT_CB_HANDLED
);
4663 for (i
= 0; i
< LENGTH(keys
); i
++)
4664 if (e
->keyval
== keys
[i
].key
&& CLEAN(e
->state
) ==
4666 keys
[i
].func(t
, &keys
[i
].arg
);
4667 return (XT_CB_HANDLED
);
4670 return (XT_CB_PASSTHROUGH
);
4674 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
4676 const gchar
*c
= gtk_entry_get_text(w
);
4680 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
4681 e
->keyval
, e
->state
, t
);
4684 errx(1, "cmd_keyrelease_cb");
4686 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
4687 e
->keyval
, e
->state
, t
);
4691 if (strlen(c
) == 1) {
4692 webkit_web_view_unmark_text_matches(t
->wv
);
4698 else if (c
[0] == '?')
4704 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
4706 /* not found, mark red */
4707 gdk_color_parse("red", &color
);
4708 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4709 /* unmark and remove selection */
4710 webkit_web_view_unmark_text_matches(t
->wv
);
4711 /* my kingdom for a way to unselect text in webview */
4713 /* found, highlight all */
4714 webkit_web_view_unmark_text_matches(t
->wv
);
4715 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
4716 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4717 gdk_color_parse("white", &color
);
4718 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4721 return (XT_CB_PASSTHROUGH
);
4726 cmd_complete(struct tab
*t
, char *s
)
4729 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
4731 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
4733 for (i
= 0; i
< LENGTH(cmds
); i
++) {
4734 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
4735 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
4737 gtk_entry_set_text(w
, ":");
4738 gtk_entry_append_text(w
, cmds
[i
].cmd
);
4739 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
4749 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
4754 errx(1, "entry_key_cb");
4756 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
4757 e
->keyval
, e
->state
, t
);
4759 if (e
->keyval
== GDK_Escape
)
4760 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4762 for (i
= 0; i
< LENGTH(keys
); i
++)
4763 if (e
->keyval
== keys
[i
].key
&&
4764 CLEAN(e
->state
) == keys
[i
].mask
&&
4765 keys
[i
].use_in_entry
) {
4766 keys
[i
].func(t
, &keys
[i
].arg
);
4767 return (XT_CB_HANDLED
);
4770 return (XT_CB_PASSTHROUGH
);
4774 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
4776 int rv
= XT_CB_HANDLED
;
4777 const gchar
*c
= gtk_entry_get_text(w
);
4780 errx(1, "cmd_keypress_cb");
4782 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
4783 e
->keyval
, e
->state
, t
);
4787 e
->keyval
= GDK_Escape
;
4788 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
4789 e
->keyval
= GDK_Escape
;
4791 switch (e
->keyval
) {
4797 if (strchr (c
, ' ')) {
4798 /* par completion */
4799 fprintf(stderr
, "completeme par\n");
4803 cmd_complete(t
, (char *)&c
[1]);
4808 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
4812 gtk_widget_hide(t
->cmd
);
4813 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4816 if (c
[0] == '/' || c
[0] == '?')
4817 webkit_web_view_unmark_text_matches(t
->wv
);
4821 rv
= XT_CB_PASSTHROUGH
;
4827 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
4830 errx(1, "cmd_focusout_cb");
4832 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d focus_wv %d\n",
4833 t
->tab_id
, t
->focus_wv
);
4835 /* abort command when losing focus */
4836 gtk_widget_hide(t
->cmd
);
4838 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4840 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
4842 return (XT_CB_PASSTHROUGH
);
4846 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
4850 const gchar
*c
= gtk_entry_get_text(entry
);
4853 errx(1, "cmd_activate_cb");
4855 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
4860 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
4866 if (c
[0] == '/' || c
[0] == '?') {
4867 if (t
->search_text
) {
4868 g_free(t
->search_text
);
4869 t
->search_text
= NULL
;
4872 t
->search_text
= g_strdup(s
);
4874 g_free(global_search
);
4875 global_search
= g_strdup(s
);
4876 t
->search_forward
= c
[0] == '/';
4881 for (i
= 0; i
< LENGTH(cmds
); i
++)
4882 if (cmds
[i
].params
) {
4883 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
4884 cmds
[i
].arg
.s
= g_strdup(s
);
4885 goto execute_command
;
4888 if (!strcmp(s
, cmds
[i
].cmd
))
4889 goto execute_command
;
4893 gtk_widget_hide(t
->cmd
);
4897 gtk_widget_hide(t
->cmd
);
4898 cmds
[i
].func(t
, &cmds
[i
].arg
);
4902 backward_cb(GtkWidget
*w
, struct tab
*t
)
4905 errx(1, "backward_cb");
4907 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
4909 webkit_web_view_go_back(t
->wv
);
4913 forward_cb(GtkWidget
*w
, struct tab
*t
)
4916 errx(1, "forward_cb");
4918 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
4920 webkit_web_view_go_forward(t
->wv
);
4924 stop_cb(GtkWidget
*w
, struct tab
*t
)
4926 WebKitWebFrame
*frame
;
4931 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
4933 frame
= webkit_web_view_get_main_frame(t
->wv
);
4934 if (frame
== NULL
) {
4935 warnx("stop_cb: no frame");
4939 webkit_web_frame_stop_loading(frame
);
4943 setup_webkit(struct tab
*t
)
4945 g_object_set((GObject
*)t
->settings
,
4946 "user-agent", t
->user_agent
, (char *)NULL
);
4947 g_object_set((GObject
*)t
->settings
,
4948 "enable-scripts", enable_scripts
, (char *)NULL
);
4949 g_object_set((GObject
*)t
->settings
,
4950 "enable-plugins", enable_plugins
, (char *)NULL
);
4951 adjustfont_webkit(t
, XT_FONT_SET
);
4953 webkit_web_view_set_settings(t
->wv
, t
->settings
);
4957 create_browser(struct tab
*t
)
4963 errx(1, "create_browser");
4965 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
4966 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
4967 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
4968 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
4970 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
4971 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
4972 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
4974 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
4975 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
4978 t
->settings
= webkit_web_settings_new();
4980 g_object_get((GObject
*)t
->settings
, "user-agent", &strval
,
4982 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
4995 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
4996 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
4997 gtk_widget_set_name(w
, "xxxterm");
4998 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
4999 g_signal_connect(G_OBJECT(w
), "delete_event",
5000 G_CALLBACK (gtk_main_quit
), NULL
);
5006 create_toolbar(struct tab
*t
)
5008 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
5010 b
= gtk_hbox_new(FALSE
, 0);
5012 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
5015 /* backward button */
5016 t
->backward
= create_button("go-back", GTK_STOCK_GO_BACK
, 0);
5017 gtk_widget_set_sensitive(t
->backward
, FALSE
);
5018 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
5019 G_CALLBACK(backward_cb
), t
);
5020 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
5022 /* forward button */
5023 t
->forward
= create_button("go-forward",GTK_STOCK_GO_FORWARD
, 0);
5024 gtk_widget_set_sensitive(t
->forward
, FALSE
);
5025 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
5026 G_CALLBACK(forward_cb
), t
);
5027 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
5031 t
->stop
= create_button("stop", GTK_STOCK_STOP
, 0);
5032 gtk_widget_set_sensitive(t
->stop
, FALSE
);
5033 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
5034 G_CALLBACK(stop_cb
), t
);
5035 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
5039 t
->js_toggle
= create_button("js-toggle", enable_scripts
?
5040 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
5041 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
5042 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
5043 G_CALLBACK(js_toggle_cb
), t
);
5044 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
5047 t
->uri_entry
= gtk_entry_new();
5048 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
5049 G_CALLBACK(activate_uri_entry_cb
), t
);
5050 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
5051 (GCallback
)entry_key_cb
, t
);
5052 eb1
= gtk_hbox_new(FALSE
, 0);
5053 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
5054 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
5055 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
5058 if (fancy_bar
&& search_string
) {
5060 t
->search_entry
= gtk_entry_new();
5061 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
5062 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
5063 G_CALLBACK(activate_search_entry_cb
), t
);
5064 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
5065 (GCallback
)entry_key_cb
, t
);
5066 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
5067 eb2
= gtk_hbox_new(FALSE
, 0);
5068 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
5069 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
5071 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
5081 TAILQ_FOREACH(t
, &tabs
, entry
)
5082 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
5086 undo_close_tab_save(struct tab
*t
)
5090 struct undo
*u1
, *u2
;
5091 WebKitWebFrame
*frame
;
5092 WebKitWebBackForwardList
*bfl
;
5094 WebKitWebHistoryItem
*item
;
5096 frame
= webkit_web_view_get_main_frame(t
->wv
);
5097 uri
= webkit_web_frame_get_uri(frame
);
5099 if (uri
&& !strlen(uri
))
5102 u1
= g_malloc0(sizeof(struct undo
));
5103 u1
->uri
= g_strdup(uri
);
5105 bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
5107 m
= webkit_web_back_forward_list_get_forward_length(bfl
);
5108 n
= webkit_web_back_forward_list_get_back_length(bfl
);
5111 /* forward history */
5112 items
= webkit_web_back_forward_list_get_forward_list_with_limit(bfl
, m
);
5116 u1
->history
= g_list_prepend(u1
->history
,
5117 webkit_web_history_item_copy(item
));
5118 items
= g_list_next(items
);
5123 item
= webkit_web_back_forward_list_get_current_item(bfl
);
5124 u1
->history
= g_list_prepend(u1
->history
,
5125 webkit_web_history_item_copy(item
));
5129 items
= webkit_web_back_forward_list_get_back_list_with_limit(bfl
, n
);
5133 u1
->history
= g_list_prepend(u1
->history
,
5134 webkit_web_history_item_copy(item
));
5135 items
= g_list_next(items
);
5138 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
5140 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
5141 u2
= TAILQ_LAST(&undos
, undo_tailq
);
5142 TAILQ_REMOVE(&undos
, u2
, entry
);
5144 g_list_free(u2
->history
);
5153 delete_tab(struct tab
*t
)
5155 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
5160 /* halt all webkit activity */
5161 webkit_web_view_stop_loading(t
->wv
);
5163 undo_close_tab_save(t
);
5165 gtk_widget_destroy(t
->vbox
);
5166 g_free(t
->user_agent
);
5169 TAILQ_REMOVE(&tabs
, t
, entry
);
5171 if (TAILQ_EMPTY(&tabs
))
5172 create_new_tab(NULL
, NULL
, 1);
5176 adjustfont_webkit(struct tab
*t
, int adjust
)
5179 errx(1, "adjustfont_webkit");
5181 if (adjust
== XT_FONT_SET
)
5182 t
->font_size
= default_font_size
;
5184 t
->font_size
+= adjust
;
5185 g_object_set((GObject
*)t
->settings
, "default-font-size",
5186 t
->font_size
, (char *)NULL
);
5187 g_object_get((GObject
*)t
->settings
, "default-font-size",
5188 &t
->font_size
, (char *)NULL
);
5192 append_tab(struct tab
*t
)
5197 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
5198 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
5202 create_new_tab(char *title
, struct undo
*u
, int focus
)
5205 int load
= 1, id
, notfound
;
5206 char *newuri
= NULL
;
5208 WebKitWebHistoryItem
*item
;
5210 WebKitWebBackForwardList
*bfl
;
5212 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
5214 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
5215 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
5219 t
= g_malloc0(sizeof *t
);
5221 if (title
== NULL
) {
5222 title
= "(untitled)";
5225 if (valid_url_type(title
)) {
5226 newuri
= guess_url_type(title
);
5231 t
->vbox
= gtk_vbox_new(FALSE
, 0);
5233 /* label + button for tab */
5234 b
= gtk_hbox_new(FALSE
, 0);
5237 #if GTK_CHECK_VERSION(2, 20, 0)
5238 t
->spinner
= gtk_spinner_new ();
5240 t
->label
= gtk_label_new(title
);
5241 bb
= create_button("my-close-button", GTK_STOCK_CLOSE
, 1);
5242 gtk_widget_set_size_request(t
->label
, 100, 0);
5243 gtk_widget_set_size_request(b
, 130, 0);
5244 gtk_notebook_set_homogeneous_tabs(notebook
, TRUE
);
5246 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
5247 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
5248 #if GTK_CHECK_VERSION(2, 20, 0)
5249 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
5253 t
->toolbar
= create_toolbar(t
);
5254 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
5257 t
->browser_win
= create_browser(t
);
5258 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
5261 t
->cmd
= gtk_entry_new();
5262 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
5263 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
5264 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
5266 /* xtp meaning is normal by default */
5267 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5269 /* and show it all */
5270 gtk_widget_show_all(b
);
5271 gtk_widget_show_all(t
->vbox
);
5273 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
5277 id
= gtk_notebook_get_current_page(notebook
);
5278 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5279 if (tt
->tab_id
== id
) {
5281 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
5282 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
5292 #if GTK_CHECK_VERSION(2, 20, 0)
5293 /* turn spinner off if we are a new tab without uri */
5295 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5296 gtk_widget_hide(t
->spinner
);
5299 /* make notebook tabs reorderable */
5300 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
5302 g_object_connect((GObject
*)t
->cmd
,
5303 "signal::key-press-event", (GCallback
)cmd_keypress_cb
, t
,
5304 "signal::key-release-event", (GCallback
)cmd_keyrelease_cb
, t
,
5305 "signal::focus-out-event", (GCallback
)cmd_focusout_cb
, t
,
5306 "signal::activate", (GCallback
)cmd_activate_cb
, t
,
5309 g_object_connect((GObject
*)t
->wv
,
5310 "signal-after::key-press-event", (GCallback
)webview_keypress_cb
, t
,
5311 /* "signal::hovering-over-link", (GCallback)webview_hover_cb, t, */
5312 "signal::download-requested", (GCallback
)webview_download_cb
, t
,
5313 "signal::mime-type-policy-decision-requested", (GCallback
)webview_mimetype_cb
, t
,
5314 "signal::navigation-policy-decision-requested", (GCallback
)webview_npd_cb
, t
,
5315 "signal::new-window-policy-decision-requested", (GCallback
)webview_nw_cb
, t
,
5316 "signal::create-web-view", (GCallback
)webview_cwv_cb
, t
,
5317 "signal::event", (GCallback
)webview_event_cb
, t
,
5318 "signal::load-finished", (GCallback
)webview_load_finished_cb
, t
,
5319 "signal::load-progress-changed", (GCallback
)webview_progress_changed_cb
, t
,
5321 g_signal_connect(t
->wv
, "notify::load-status",
5322 G_CALLBACK(notify_load_status_cb
), t
);
5324 /* hijack the unused keys as if we were the browser */
5325 g_object_connect((GObject
*)t
->toolbar
,
5326 "signal-after::key-press-event", (GCallback
)webview_keypress_cb
, t
,
5329 g_signal_connect(G_OBJECT(bb
), "button_press_event",
5330 G_CALLBACK(tab_close_cb
), t
);
5333 gtk_widget_hide(t
->cmd
);
5335 gtk_widget_hide(t
->toolbar
);
5338 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
5339 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
5344 webkit_web_view_load_uri(t
->wv
, title
);
5346 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
5348 /* restore the tab's history */
5349 if (u
&& u
->history
) {
5350 bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
5355 webkit_web_back_forward_list_add_item(bfl
, item
);
5356 items
= g_list_next(items
);
5359 item
= g_list_nth_data(u
->history
, u
->back
);
5360 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
5363 g_list_free(u
->history
);
5371 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
5377 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
5379 TAILQ_FOREACH(t
, &tabs
, entry
) {
5380 if (t
->tab_id
== pn
) {
5381 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
5384 uri
= webkit_web_view_get_title(t
->wv
);
5387 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
5389 gtk_widget_hide(t
->cmd
);
5392 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5398 menuitem_response(struct tab
*t
)
5400 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
5404 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
5406 GtkWidget
*menu
, *menu_items
;
5407 GdkEventButton
*bevent
;
5408 WebKitWebFrame
*frame
;
5412 if (event
->type
== GDK_BUTTON_PRESS
) {
5413 bevent
= (GdkEventButton
*) event
;
5414 menu
= gtk_menu_new();
5416 TAILQ_FOREACH(ti
, &tabs
, entry
) {
5417 frame
= webkit_web_view_get_main_frame(ti
->wv
);
5418 uri
= webkit_web_frame_get_uri(frame
);
5419 /* XXX make sure there is something to print */
5420 /* XXX add gui pages in here to look purdy */
5423 if (strlen(uri
) == 0)
5425 menu_items
= gtk_menu_item_new_with_label(uri
);
5426 gtk_menu_append(GTK_MENU (menu
), menu_items
);
5427 gtk_widget_show(menu_items
);
5429 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
5430 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
5434 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
5435 bevent
->button
, bevent
->time
);
5437 /* unref object so it'll free itself when popped down */
5438 g_object_ref_sink(menu
);
5439 g_object_unref(menu
);
5441 return (TRUE
/* eat event */);
5444 return (FALSE
/* propagate */);
5448 icon_size_map(int icon_size
)
5450 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
5451 icon_size
> GTK_ICON_SIZE_DIALOG
)
5452 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
5458 create_button(char *name
, char *stockid
, int size
)
5460 GtkWidget
*button
, *image
;
5464 "style \"%s-style\"\n"
5466 " GtkWidget::focus-padding = 0\n"
5467 " GtkWidget::focus-line-width = 0\n"
5471 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
5472 gtk_rc_parse_string(rcstring
);
5474 button
= gtk_button_new();
5475 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
5476 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
5478 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
5479 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
5480 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
5481 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
5482 gtk_widget_set_name(button
, name
);
5488 button_set_stockid(GtkWidget
*button
, char *stockid
)
5491 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
5492 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
5493 gtk_button_set_image(GTK_BUTTON(button
), image
);
5502 char file
[PATH_MAX
];
5505 vbox
= gtk_vbox_new(FALSE
, 0);
5506 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
5507 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
5509 gtk_notebook_set_show_tabs(notebook
, FALSE
);
5511 gtk_notebook_set_tab_hborder(notebook
, 0);
5512 gtk_notebook_set_tab_vborder(notebook
, 0);
5514 gtk_notebook_set_show_border(notebook
, FALSE
);
5515 gtk_notebook_set_scrollable(notebook
, TRUE
);
5516 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
5518 abtn
= gtk_button_new();
5519 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
5520 gtk_widget_set_size_request(arrow
, -1, -1);
5521 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
5522 gtk_widget_set_size_request(abtn
, -1, 20);
5523 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
5525 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
5526 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
5527 gtk_widget_set_size_request(vbox
, -1, -1);
5529 g_object_connect((GObject
*)notebook
,
5530 "signal::switch-page", (GCallback
)notebook_switchpage_cb
, NULL
,
5532 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
5533 G_CALLBACK(arrow_cb
), NULL
);
5535 main_window
= create_window();
5536 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
5537 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
5540 for (i
= 0; i
< LENGTH(icons
); i
++) {
5541 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
5542 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5543 l
= g_list_append(l
, pb
);
5545 gtk_window_set_default_icon_list(l
);
5547 gtk_widget_show_all(abtn
);
5548 gtk_widget_show_all(main_window
);
5552 set_hook(void **hook
, char *name
)
5555 errx(1, "set_hook");
5557 if (*hook
== NULL
) {
5558 *hook
= dlsym(RTLD_NEXT
, name
);
5560 errx(1, "can't hook %s", name
);
5564 /* override libsoup soup_cookie_equal because it doesn't look at domain */
5566 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
5568 g_return_val_if_fail(cookie1
, FALSE
);
5569 g_return_val_if_fail(cookie2
, FALSE
);
5571 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
5572 !strcmp (cookie1
->value
, cookie2
->value
) &&
5573 !strcmp (cookie1
->path
, cookie2
->path
) &&
5574 !strcmp (cookie1
->domain
, cookie2
->domain
));
5578 transfer_cookies(void)
5581 SoupCookie
*sc
, *pc
;
5583 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
5585 for (;cf
; cf
= cf
->next
) {
5587 sc
= soup_cookie_copy(pc
);
5588 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
5591 soup_cookies_free(cf
);
5595 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
5600 print_cookie("soup_cookie_jar_delete_cookie", c
);
5602 if (jar
== NULL
|| c
== NULL
)
5605 /* find and remove from persistent jar */
5606 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
5608 for (;cf
; cf
= cf
->next
) {
5610 if (soup_cookie_equal(ci
, c
)) {
5611 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
5616 soup_cookies_free(cf
);
5618 /* delete from session jar */
5619 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
5623 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
5628 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
5629 jar
, p_cookiejar
, s_cookiejar
);
5631 /* see if we are up and running */
5632 if (p_cookiejar
== NULL
) {
5633 _soup_cookie_jar_add_cookie(jar
, cookie
);
5636 /* disallow p_cookiejar adds, shouldn't happen */
5637 if (jar
== p_cookiejar
)
5640 if ((d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
5642 DNPRINTF(XT_D_COOKIE
,
5643 "soup_cookie_jar_add_cookie: reject %s\n",
5645 if (!allow_volatile_cookies
)
5649 if (cookie
->expires
== NULL
&& session_timeout
) {
5650 soup_cookie_set_expires(cookie
,
5651 soup_date_new_from_now(session_timeout
));
5652 print_cookie("modified add cookie", cookie
);
5655 /* see if we are white listed for persistence */
5656 if (d
&& d
->handy
) {
5657 /* add to persistent jar */
5658 c
= soup_cookie_copy(cookie
);
5659 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
5660 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
5663 /* add to session jar */
5664 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
5665 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
5669 setup_cookies(char *file
)
5671 if (cookies_enabled
== 0)
5674 set_hook((void *)&_soup_cookie_jar_add_cookie
,
5675 "soup_cookie_jar_add_cookie");
5676 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
5677 "soup_cookie_jar_delete_cookie");
5680 * the following code is intricate due to overriding several libsoup
5682 * do not alter order of these operations.
5684 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
5686 s_cookiejar
= soup_cookie_jar_new();
5687 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
5688 cookie_policy
, (void *)NULL
);
5691 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
5695 setup_proxy(char *uri
)
5698 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
5699 soup_uri_free(proxy_uri
);
5703 if (http_proxy
!= uri
) {
5710 http_proxy
= g_strdup(uri
);
5711 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
5712 proxy_uri
= soup_uri_new(http_proxy
);
5713 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
5718 send_url_to_socket(char *url
)
5720 int s
, len
, rv
= -1;
5721 struct sockaddr_un sa
;
5723 pwd
= getpwuid(getuid());
5725 errx(1, "invalid user %d", getuid());
5727 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
5728 warnx("send_url_to_socket: socket");
5732 sa
.sun_family
= AF_UNIX
;
5733 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
5734 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
5737 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
5738 warnx("send_url_to_socket: connect");
5742 if (send(s
, url
, strlen(url
) + 1, 0) == -1) {
5743 warnx("send_url_to_socket: send");
5752 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
5755 char str
[XT_MAX_URL_LENGTH
];
5756 socklen_t t
= sizeof(struct sockaddr_un
);
5757 struct sockaddr_un sa
;
5762 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
5763 warn("socket_watcher: accept");
5767 if (getpeereid(s
, &uid
, &gid
) == -1) {
5768 warn("socket_watcher: getpeereid");
5771 if (uid
!= getuid() || gid
!= getgid()) {
5772 warnx("socket_watcher: unauthorized user");
5778 warnx("socket_watcher: not a valid user");
5782 n
= recv(s
, str
, sizeof(str
), 0);
5786 create_new_tab(str
, NULL
, 1);
5793 struct sockaddr_un sa
;
5795 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
5796 warn("is_running: socket");
5800 sa
.sun_family
= AF_UNIX
;
5801 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
5802 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
5805 /* connect to see if there is a listener */
5806 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
5807 rv
= 0; /* not running */
5809 rv
= 1; /* already running */
5820 struct sockaddr_un sa
;
5822 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
5823 warn("build_socket: socket");
5827 sa
.sun_family
= AF_UNIX
;
5828 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
5829 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
5832 /* connect to see if there is a listener */
5833 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
5834 /* no listener so we will */
5835 unlink(sa
.sun_path
);
5837 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
5838 warn("build_socket: bind");
5842 if (listen(s
, 1) == -1) {
5843 warn("build_socket: listen");
5859 "%s [-nSTVt][-f file] url ...\n", __progname
);
5864 main(int argc
, char *argv
[])
5867 int c
, focus
= 1, s
, optn
= 0;
5868 char conf
[PATH_MAX
] = { '\0' };
5869 char file
[PATH_MAX
];
5870 char *env_proxy
= NULL
;
5875 while ((c
= getopt(argc
, argv
, "STVf:tn")) != -1) {
5884 errx(0 , "Version: %s", version
);
5887 strlcpy(conf
, optarg
, sizeof(conf
));
5906 RB_INIT(&downloads
);
5908 TAILQ_INIT(&aliases
);
5911 gnutls_global_init();
5913 /* generate session keys for xtp pages */
5914 generate_xtp_session_key(&dl_session_key
);
5915 generate_xtp_session_key(&hl_session_key
);
5916 generate_xtp_session_key(&cl_session_key
);
5917 generate_xtp_session_key(&fl_session_key
);
5920 gtk_init(&argc
, &argv
);
5921 if (!g_thread_supported())
5922 g_thread_init(NULL
);
5924 pwd
= getpwuid(getuid());
5926 errx(1, "invalid user %d", getuid());
5928 /* set download dir */
5929 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
5931 /* set default string settings */
5932 home
= g_strdup("http://www.peereboom.us");
5933 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
5935 /* read config file */
5936 if (strlen(conf
) == 0)
5937 snprintf(conf
, sizeof conf
, "%s/.%s",
5938 pwd
->pw_dir
, XT_CONF_FILE
);
5939 config_parse(conf
, 0);
5941 /* working directory */
5942 snprintf(work_dir
, sizeof work_dir
, "%s/%s", pwd
->pw_dir
, XT_DIR
);
5943 if (stat(work_dir
, &sb
)) {
5944 if (mkdir(work_dir
, S_IRWXU
) == -1)
5945 err(1, "mkdir work_dir");
5946 if (stat(work_dir
, &sb
))
5947 err(1, "stat work_dir");
5949 if (S_ISDIR(sb
.st_mode
) == 0)
5950 errx(1, "%s not a dir", work_dir
);
5951 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
5952 warnx("fixing invalid permissions on %s", work_dir
);
5953 if (chmod(work_dir
, S_IRWXU
) == -1)
5958 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s/%s",
5959 pwd
->pw_dir
, XT_DIR
, XT_CERT_DIR
);
5960 if (stat(certs_dir
, &sb
)) {
5961 if (mkdir(certs_dir
, S_IRWXU
) == -1)
5962 err(1, "mkdir certs_dir");
5963 if (stat(certs_dir
, &sb
))
5964 err(1, "stat certs_dir");
5966 if (S_ISDIR(sb
.st_mode
) == 0)
5967 errx(1, "%s not a dir", certs_dir
);
5968 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
5969 warnx("fixing invalid permissions on %s", certs_dir
);
5970 if (chmod(certs_dir
, S_IRWXU
) == -1)
5974 /* runtime settings that can override config file */
5975 if (runtime_settings
[0] != '\0')
5976 config_parse(runtime_settings
, 1);
5979 if (!strcmp(download_dir
, pwd
->pw_dir
))
5980 strlcat(download_dir
, "/downloads", sizeof download_dir
);
5981 if (stat(download_dir
, &sb
)) {
5982 if (mkdir(download_dir
, S_IRWXU
) == -1)
5983 err(1, "mkdir download_dir");
5984 if (stat(download_dir
, &sb
))
5985 err(1, "stat download_dir");
5987 if (S_ISDIR(sb
.st_mode
) == 0)
5988 errx(1, "%s not a dir", download_dir
);
5989 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
5990 warnx("fixing invalid permissions on %s", download_dir
);
5991 if (chmod(download_dir
, S_IRWXU
) == -1)
5995 /* favorites file */
5996 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5997 if (stat(file
, &sb
)) {
5998 warnx("favorites file doesn't exist, creating it");
5999 if ((f
= fopen(file
, "w")) == NULL
)
6000 err(1, "favorites");
6005 session
= webkit_get_default_session();
6006 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
6007 setup_cookies(file
);
6011 if (stat(ssl_ca_file
, &sb
)) {
6012 warn("no CA file: %s", ssl_ca_file
);
6013 g_free(ssl_ca_file
);
6016 g_object_set(session
,
6017 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
6018 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
6023 env_proxy
= getenv("http_proxy");
6025 setup_proxy(env_proxy
);
6027 setup_proxy(http_proxy
);
6029 /* see if there is already an xxxterm running */
6030 if (single_instance
&& is_running()) {
6032 warnx("already running");
6037 send_url_to_socket(argv
[0]);
6048 if (save_global_history
)
6049 restore_global_history();
6051 focus
= restore_saved_tabs();
6054 create_new_tab(argv
[0], NULL
, focus
);
6061 create_new_tab(home
, NULL
, 1);
6064 if ((s
= build_socket()) != -1)
6065 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
6069 gnutls_global_deinit();