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>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * inverse color browsing
26 * multi letter commands
27 * pre and post counts for commands
28 * autocompletion on various inputs
29 * create privacy browsing
30 * - encrypted local data
46 #include <sys/types.h>
48 #if defined(__linux__)
49 #include "linux/util.h"
50 #include "linux/tree.h"
51 #elif defined(__FreeBSD__)
53 #include "freebsd/util.h"
59 #include <sys/queue.h>
61 #include <sys/socket.h>
65 #include <gdk/gdkkeysyms.h>
66 #include <webkit/webkit.h>
67 #include <libsoup/soup.h>
68 #include <gnutls/gnutls.h>
69 #include <JavaScriptCore/JavaScript.h>
70 #include <gnutls/x509.h>
72 #include "javascript.h"
75 javascript.h borrowed from vimprobable2 under the following license:
77 Copyright (c) 2009 Leon Winter
78 Copyright (c) 2009 Hannes Schueller
79 Copyright (c) 2009 Matto Fransen
81 Permission is hereby granted, free of charge, to any person obtaining a copy
82 of this software and associated documentation files (the "Software"), to deal
83 in the Software without restriction, including without limitation the rights
84 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
85 copies of the Software, and to permit persons to whom the Software is
86 furnished to do so, subject to the following conditions:
88 The above copyright notice and this permission notice shall be included in
89 all copies or substantial portions of the Software.
91 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
92 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
93 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
94 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
95 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
96 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
100 static char *version
= "$xxxterm$";
102 /* hooked functions */
103 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
104 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
109 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
110 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
111 #define XT_D_MOVE 0x0001
112 #define XT_D_KEY 0x0002
113 #define XT_D_TAB 0x0004
114 #define XT_D_URL 0x0008
115 #define XT_D_CMD 0x0010
116 #define XT_D_NAV 0x0020
117 #define XT_D_DOWNLOAD 0x0040
118 #define XT_D_CONFIG 0x0080
119 #define XT_D_JS 0x0100
120 #define XT_D_FAVORITE 0x0200
121 #define XT_D_PRINTING 0x0400
122 #define XT_D_COOKIE 0x0800
123 #define XT_D_KEYBINDING 0x1000
124 u_int32_t swm_debug
= 0
140 #define DPRINTF(x...)
141 #define DNPRINTF(n,x...)
144 #define LENGTH(x) (sizeof x / sizeof x[0])
145 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
146 ~(GDK_BUTTON1_MASK) & \
147 ~(GDK_BUTTON2_MASK) & \
148 ~(GDK_BUTTON3_MASK) & \
149 ~(GDK_BUTTON4_MASK) & \
161 TAILQ_ENTRY(tab
) entry
;
163 GtkWidget
*tab_content
;
166 GtkWidget
*uri_entry
;
167 GtkWidget
*search_entry
;
169 GtkWidget
*browser_win
;
170 GtkWidget
*statusbar
;
176 GtkWidget
*js_toggle
;
177 GtkEntryCompletion
*completion
;
181 WebKitWebHistoryItem
*item
;
182 WebKitWebBackForwardList
*bfl
;
185 WebKitNetworkRequest
*icon_request
;
186 WebKitDownload
*icon_download
;
187 GdkPixbuf
*icon_pixbuf
;
188 gchar
*icon_dest_uri
;
190 /* adjustments for browser */
193 GtkAdjustment
*adjust_h
;
194 GtkAdjustment
*adjust_v
;
200 int xtp_meaning
; /* identifies dls/favorites */
205 #define XT_HINT_NONE (0)
206 #define XT_HINT_NUMERICAL (1)
207 #define XT_HINT_ALPHANUM (2)
216 WebKitWebSettings
*settings
;
220 TAILQ_HEAD(tab_list
, tab
);
223 RB_ENTRY(history
) entry
;
227 RB_HEAD(history_list
, history
);
230 RB_ENTRY(download
) entry
;
232 WebKitDownload
*download
;
235 RB_HEAD(download_list
, download
);
238 RB_ENTRY(domain
) entry
;
240 int handy
; /* app use */
242 RB_HEAD(domain_list
, domain
);
245 TAILQ_ENTRY(undo
) entry
;
248 int back
; /* Keeps track of how many back
249 * history items there are. */
251 TAILQ_HEAD(undo_tailq
, undo
);
253 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
254 int next_download_id
= 1;
262 #define XT_NAME ("XXXTerm")
263 #define XT_DIR (".xxxterm")
264 #define XT_CACHE_DIR ("cache")
265 #define XT_CERT_DIR ("certs/")
266 #define XT_SESSIONS_DIR ("sessions/")
267 #define XT_CONF_FILE ("xxxterm.conf")
268 #define XT_FAVS_FILE ("favorites")
269 #define XT_SAVED_TABS_FILE ("main_session")
270 #define XT_RESTART_TABS_FILE ("restart_tabs")
271 #define XT_SOCKET_FILE ("socket")
272 #define XT_HISTORY_FILE ("history")
273 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
274 #define XT_CB_HANDLED (TRUE)
275 #define XT_CB_PASSTHROUGH (FALSE)
276 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
277 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
278 #define XT_DLMAN_REFRESH "10"
279 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
280 "td {overflow: hidden;" \
281 " padding: 2px 2px 2px 2px;" \
282 " border: 1px solid black}\n" \
283 "tr:hover {background: #ffff99 ;}\n" \
284 "th {background-color: #cccccc;" \
285 " border: 1px solid black}" \
286 "table {border-spacing: 0; " \
288 " border: 1px black solid;}\n" \
290 " border: 1px solid black;" \
296 " background: green;}" \
298 " font-size: small;" \
299 " text-align: center;}" \
301 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
302 #define XT_MAX_UNDO_CLOSE_TAB (32)
303 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
304 #define XT_PRINT_EXTRA_MARGIN 10
307 #define SZ_KB ((uint64_t) 1024)
308 #define SZ_MB (SZ_KB * SZ_KB)
309 #define SZ_GB (SZ_KB * SZ_KB * SZ_KB)
310 #define SZ_TB (SZ_KB * SZ_KB * SZ_KB * SZ_KB)
313 * xxxterm "protocol" (xtp)
314 * We use this for managing stuff like downloads and favorites. They
315 * make magical HTML pages in memory which have xxxt:// links in order
316 * to communicate with xxxterm's internals. These links take the format:
317 * xxxt://class/session_key/action/arg
319 * Don't begin xtp class/actions as 0. atoi returns that on error.
321 * Typically we have not put addition of items in this framework, as
322 * adding items is either done via an ex-command or via a keybinding instead.
325 #define XT_XTP_STR "xxxt://"
327 /* XTP classes (xxxt://<class>) */
328 #define XT_XTP_DL 1 /* downloads */
329 #define XT_XTP_HL 2 /* history */
330 #define XT_XTP_CL 3 /* cookies */
331 #define XT_XTP_FL 4 /* favorites */
333 /* XTP download actions */
334 #define XT_XTP_DL_LIST 1
335 #define XT_XTP_DL_CANCEL 2
336 #define XT_XTP_DL_REMOVE 3
338 /* XTP history actions */
339 #define XT_XTP_HL_LIST 1
340 #define XT_XTP_HL_REMOVE 2
342 /* XTP cookie actions */
343 #define XT_XTP_CL_LIST 1
344 #define XT_XTP_CL_REMOVE 2
346 /* XTP cookie actions */
347 #define XT_XTP_FL_LIST 1
348 #define XT_XTP_FL_REMOVE 2
350 /* xtp tab meanings - identifies which tabs have xtp pages in */
351 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
352 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
353 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
354 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
355 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
358 #define XT_MOVE_INVALID (0)
359 #define XT_MOVE_DOWN (1)
360 #define XT_MOVE_UP (2)
361 #define XT_MOVE_BOTTOM (3)
362 #define XT_MOVE_TOP (4)
363 #define XT_MOVE_PAGEDOWN (5)
364 #define XT_MOVE_PAGEUP (6)
365 #define XT_MOVE_HALFDOWN (7)
366 #define XT_MOVE_HALFUP (8)
367 #define XT_MOVE_LEFT (9)
368 #define XT_MOVE_FARLEFT (10)
369 #define XT_MOVE_RIGHT (11)
370 #define XT_MOVE_FARRIGHT (12)
372 #define XT_TAB_LAST (-4)
373 #define XT_TAB_FIRST (-3)
374 #define XT_TAB_PREV (-2)
375 #define XT_TAB_NEXT (-1)
376 #define XT_TAB_INVALID (0)
377 #define XT_TAB_NEW (1)
378 #define XT_TAB_DELETE (2)
379 #define XT_TAB_DELQUIT (3)
380 #define XT_TAB_OPEN (4)
381 #define XT_TAB_UNDO_CLOSE (5)
382 #define XT_TAB_SHOW (6)
383 #define XT_TAB_HIDE (7)
385 #define XT_NAV_INVALID (0)
386 #define XT_NAV_BACK (1)
387 #define XT_NAV_FORWARD (2)
388 #define XT_NAV_RELOAD (3)
389 #define XT_NAV_RELOAD_CACHE (4)
391 #define XT_FOCUS_INVALID (0)
392 #define XT_FOCUS_URI (1)
393 #define XT_FOCUS_SEARCH (2)
395 #define XT_SEARCH_INVALID (0)
396 #define XT_SEARCH_NEXT (1)
397 #define XT_SEARCH_PREV (2)
399 #define XT_PASTE_CURRENT_TAB (0)
400 #define XT_PASTE_NEW_TAB (1)
402 #define XT_FONT_SET (0)
404 #define XT_URL_SHOW (1)
405 #define XT_URL_HIDE (2)
407 #define XT_STATUSBAR_SHOW (1)
408 #define XT_STATUSBAR_HIDE (2)
410 #define XT_WL_TOGGLE (1<<0)
411 #define XT_WL_ENABLE (1<<1)
412 #define XT_WL_DISABLE (1<<2)
413 #define XT_WL_FQDN (1<<3) /* default */
414 #define XT_WL_TOPLEVEL (1<<4)
416 #define XT_CMD_OPEN (0)
417 #define XT_CMD_OPEN_CURRENT (1)
418 #define XT_CMD_TABNEW (2)
419 #define XT_CMD_TABNEW_CURRENT (3)
421 #define XT_STATUS_NOTHING (0)
422 #define XT_STATUS_LINK (1)
423 #define XT_STATUS_URI (2)
424 #define XT_STATUS_LOADING (3)
426 #define XT_SES_DONOTHING (0)
427 #define XT_SES_CLOSETABS (1)
429 #define XT_COOKIE_NORMAL (0)
430 #define XT_COOKIE_WHITELIST (1)
437 TAILQ_ENTRY(mime_type
) entry
;
439 TAILQ_HEAD(mime_type_list
, mime_type
);
445 TAILQ_ENTRY(alias
) entry
;
447 TAILQ_HEAD(alias_list
, alias
);
449 /* settings that require restart */
450 int tabless
= 0; /* allow only 1 tab */
451 int enable_socket
= 0;
452 int single_instance
= 0; /* only allow one xxxterm to run */
453 int fancy_bar
= 1; /* fancy toolbar */
454 int browser_mode
= XT_COOKIE_NORMAL
;
456 /* runtime settings */
457 int show_tabs
= 1; /* show tabs on notebook */
458 int show_url
= 1; /* show url toolbar on notebook */
459 int show_statusbar
= 0; /* vimperator style status bar */
460 int ctrl_click_focus
= 0; /* ctrl click gets focus */
461 int cookies_enabled
= 1; /* enable cookies */
462 int read_only_cookies
= 0; /* enable to not write cookies */
463 int enable_scripts
= 1;
464 int enable_plugins
= 0;
465 int default_font_size
= 12;
466 gfloat default_zoom_level
= 1.0;
467 int window_height
= 768;
468 int window_width
= 1024;
469 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
470 unsigned refresh_interval
= 10; /* download refresh interval */
471 int enable_cookie_whitelist
= 0;
472 int enable_js_whitelist
= 0;
473 time_t session_timeout
= 3600; /* cookie session timeout */
474 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
475 char *ssl_ca_file
= NULL
;
476 char *resource_dir
= NULL
;
477 gboolean ssl_strict_certs
= FALSE
;
478 int append_next
= 1; /* append tab after current tab */
480 char *search_string
= NULL
;
481 char *http_proxy
= NULL
;
482 char download_dir
[PATH_MAX
];
483 char runtime_settings
[PATH_MAX
]; /* override of settings */
484 int allow_volatile_cookies
= 0;
485 int save_global_history
= 0; /* save global history to disk */
486 char *user_agent
= NULL
;
487 int save_rejected_cookies
= 0;
488 time_t session_autosave
= 0;
489 int guess_search
= 0;
493 int set_download_dir(struct settings
*, char *);
494 int set_work_dir(struct settings
*, char *);
495 int set_runtime_dir(struct settings
*, char *);
496 int set_browser_mode(struct settings
*, char *);
497 int set_cookie_policy(struct settings
*, char *);
498 int add_alias(struct settings
*, char *);
499 int add_mime_type(struct settings
*, char *);
500 int add_cookie_wl(struct settings
*, char *);
501 int add_js_wl(struct settings
*, char *);
502 int add_kb(struct settings
*, char *);
503 void button_set_stockid(GtkWidget
*, char *);
504 GtkWidget
* create_button(char *, char *, int);
506 char *get_browser_mode(struct settings
*);
507 char *get_cookie_policy(struct settings
*);
509 char *get_download_dir(struct settings
*);
510 char *get_work_dir(struct settings
*);
511 char *get_runtime_dir(struct settings
*);
513 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
514 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
515 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
516 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
517 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
520 int (*set
)(struct settings
*, char *);
521 char *(*get
)(struct settings
*);
522 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
525 struct special s_browser_mode
= {
531 struct special s_cookie
= {
537 struct special s_alias
= {
543 struct special s_mime
= {
549 struct special s_js
= {
555 struct special s_kb
= {
561 struct special s_cookie_wl
= {
567 struct special s_download_dir
= {
573 struct special s_work_dir
= {
582 #define XT_S_INVALID (0)
585 #define XT_S_FLOAT (3)
587 #define XT_SF_RESTART (1<<0)
588 #define XT_SF_RUNTIME (1<<1)
594 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
595 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
596 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
597 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
598 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
599 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
600 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
601 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
602 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
603 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
604 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
605 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
606 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
607 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
608 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
609 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
610 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
611 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
612 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
613 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
614 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
615 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
616 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
617 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
618 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
619 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
620 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
621 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
622 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
623 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
624 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
625 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
626 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
627 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
628 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
629 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
630 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
632 /* runtime settings */
633 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
634 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
635 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
636 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
637 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
640 int about(struct tab
*, struct karg
*);
641 int blank(struct tab
*, struct karg
*);
642 int cookie_show_wl(struct tab
*, struct karg
*);
643 int js_show_wl(struct tab
*, struct karg
*);
644 int help(struct tab
*, struct karg
*);
645 int set(struct tab
*, struct karg
*);
646 int stats(struct tab
*, struct karg
*);
647 int xtp_page_cl(struct tab
*, struct karg
*);
648 int xtp_page_dl(struct tab
*, struct karg
*);
649 int xtp_page_fl(struct tab
*, struct karg
*);
650 int xtp_page_hl(struct tab
*, struct karg
*);
652 #define XT_URI_ABOUT ("about:")
653 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
654 #define XT_URI_ABOUT_ABOUT ("about")
655 #define XT_URI_ABOUT_BLANK ("blank")
656 #define XT_URI_ABOUT_CERTS ("certs") /* XXX NOT YET */
657 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
658 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
659 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
660 #define XT_URI_ABOUT_FAVORITES ("favorites")
661 #define XT_URI_ABOUT_HELP ("help")
662 #define XT_URI_ABOUT_HISTORY ("history")
663 #define XT_URI_ABOUT_JSWL ("jswl")
664 #define XT_URI_ABOUT_SET ("set")
665 #define XT_URI_ABOUT_STATS ("stats")
669 int (*func
)(struct tab
*, struct karg
*);
671 { XT_URI_ABOUT_ABOUT
, about
},
672 { XT_URI_ABOUT_BLANK
, blank
},
673 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
674 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
675 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
676 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
677 { XT_URI_ABOUT_HELP
, help
},
678 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
679 { XT_URI_ABOUT_JSWL
, js_show_wl
},
680 { XT_URI_ABOUT_SET
, set
},
681 { XT_URI_ABOUT_STATS
, stats
},
685 extern char *__progname
;
688 GtkWidget
*main_window
;
689 GtkNotebook
*notebook
;
690 GtkWidget
*arrow
, *abtn
;
691 struct tab_list tabs
;
692 struct history_list hl
;
693 struct download_list downloads
;
694 struct domain_list c_wl
;
695 struct domain_list js_wl
;
696 struct undo_tailq undos
;
697 struct keybinding_list kbl
;
699 int updating_dl_tabs
= 0;
700 int updating_hl_tabs
= 0;
701 int updating_cl_tabs
= 0;
702 int updating_fl_tabs
= 0;
704 uint64_t blocked_cookies
= 0;
705 char named_session
[PATH_MAX
];
706 void update_favicon(struct tab
*);
707 int icon_size_map(int);
709 GtkListStore
*completion_model
;
710 void completion_add(struct tab
*);
711 void completion_add_uri(const gchar
*);
716 int saved_errno
, status
;
721 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
725 if (errno
!= ECHILD
) {
727 clog_warn("sigchild: waitpid:");
733 if (WIFEXITED(status
)) {
734 if (WEXITSTATUS(status
) != 0) {
736 clog_warnx("sigchild: child exit status: %d",
737 WEXITSTATUS(status));
742 clog_warnx("sigchild: child is terminated abnormally");
751 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
757 /* we set this to indicate we want to manually do navaction */
759 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
760 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
763 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, title
);
764 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
767 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
768 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
769 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
770 GTK_ENTRY_ICON_PRIMARY
, pb
);
771 gdk_pixbuf_unref(pb
);
776 set_status(struct tab
*t
, gchar
*s
, int status
)
784 case XT_STATUS_LOADING
:
785 type
= g_strdup_printf("Loading: %s", s
);
789 type
= g_strdup_printf("Link: %s", s
);
791 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
795 type
= g_strdup_printf("%s", s
);
797 t
->status
= g_strdup(type
);
801 t
->status
= g_strdup(s
);
803 case XT_STATUS_NOTHING
:
808 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
814 hide_oops(struct tab
*t
)
816 gtk_widget_hide(t
->oops
);
820 hide_cmd(struct tab
*t
)
822 gtk_widget_hide(t
->cmd
);
826 show_cmd(struct tab
*t
)
828 gtk_widget_hide(t
->oops
);
829 gtk_widget_show(t
->cmd
);
833 show_oops(struct tab
*t
, const char *fmt
, ...)
842 if (vasprintf(&msg
, fmt
, ap
) == -1)
843 errx(1, "show_oops failed");
846 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
847 gtk_widget_hide(t
->cmd
);
848 gtk_widget_show(t
->oops
);
851 /* XXX collapse with show_oops */
853 show_oops_s(const char *fmt
, ...)
857 struct tab
*ti
, *t
= NULL
;
862 TAILQ_FOREACH(ti
, &tabs
, entry
)
863 if (ti
->tab_id
== gtk_notebook_current_page(notebook
)) {
871 if (vasprintf(&msg
, fmt
, ap
) == -1)
872 errx(1, "show_oops_s failed");
875 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
876 gtk_widget_hide(t
->cmd
);
877 gtk_widget_show(t
->oops
);
881 get_as_string(struct settings
*s
)
892 warnx("get_as_string skip %s\n", s
->name
);
893 } else if (s
->type
== XT_S_INT
)
894 r
= g_strdup_printf("%d", *s
->ival
);
895 else if (s
->type
== XT_S_STR
)
896 r
= g_strdup(*s
->sval
);
897 else if (s
->type
== XT_S_FLOAT
)
898 r
= g_strdup_printf("%f", *s
->fval
);
900 r
= g_strdup_printf("INVALID TYPE");
906 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
911 for (i
= 0; i
< LENGTH(rs
); i
++) {
912 if (rs
[i
].s
&& rs
[i
].s
->walk
)
913 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
915 s
= get_as_string(&rs
[i
]);
916 cb(&rs
[i
], s
, cb_args
);
923 set_browser_mode(struct settings
*s
, char *val
)
925 if (!strcmp(val
, "whitelist")) {
926 browser_mode
= XT_COOKIE_WHITELIST
;
927 allow_volatile_cookies
= 0;
928 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
930 enable_cookie_whitelist
= 1;
931 read_only_cookies
= 0;
932 save_rejected_cookies
= 0;
933 session_timeout
= 3600;
935 enable_js_whitelist
= 1;
936 } else if (!strcmp(val
, "normal")) {
937 browser_mode
= XT_COOKIE_NORMAL
;
938 allow_volatile_cookies
= 0;
939 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
941 enable_cookie_whitelist
= 0;
942 read_only_cookies
= 0;
943 save_rejected_cookies
= 0;
944 session_timeout
= 3600;
946 enable_js_whitelist
= 0;
954 get_browser_mode(struct settings
*s
)
958 if (browser_mode
== XT_COOKIE_WHITELIST
)
959 r
= g_strdup("whitelist");
960 else if (browser_mode
== XT_COOKIE_NORMAL
)
961 r
= g_strdup("normal");
969 set_cookie_policy(struct settings
*s
, char *val
)
971 if (!strcmp(val
, "no3rdparty"))
972 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
973 else if (!strcmp(val
, "accept"))
974 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
975 else if (!strcmp(val
, "reject"))
976 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
984 get_cookie_policy(struct settings
*s
)
988 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
989 r
= g_strdup("no3rdparty");
990 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
991 r
= g_strdup("accept");
992 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
993 r
= g_strdup("reject");
1001 get_download_dir(struct settings
*s
)
1003 if (download_dir
[0] == '\0')
1005 return (g_strdup(download_dir
));
1009 set_download_dir(struct settings
*s
, char *val
)
1012 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1013 pwd
->pw_dir
, &val
[1]);
1015 strlcpy(download_dir
, val
, sizeof download_dir
);
1022 * We use these to prevent people putting xxxt:// URLs on
1023 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1025 #define XT_XTP_SES_KEY_SZ 8
1026 #define XT_XTP_SES_KEY_HEX_FMT \
1027 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1028 char *dl_session_key
; /* downloads */
1029 char *hl_session_key
; /* history list */
1030 char *cl_session_key
; /* cookie list */
1031 char *fl_session_key
; /* favorites list */
1033 char work_dir
[PATH_MAX
];
1034 char certs_dir
[PATH_MAX
];
1035 char cache_dir
[PATH_MAX
];
1036 char sessions_dir
[PATH_MAX
];
1037 char cookie_file
[PATH_MAX
];
1038 SoupURI
*proxy_uri
= NULL
;
1039 SoupSession
*session
;
1040 SoupCookieJar
*s_cookiejar
;
1041 SoupCookieJar
*p_cookiejar
;
1042 char rc_fname
[PATH_MAX
];
1044 struct mime_type_list mtl
;
1045 struct alias_list aliases
;
1048 struct tab
*create_new_tab(char *, struct undo
*, int);
1049 void delete_tab(struct tab
*);
1050 void adjustfont_webkit(struct tab
*, int);
1051 int run_script(struct tab
*, char *);
1052 int download_rb_cmp(struct download
*, struct download
*);
1055 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1057 return (strcmp(h1
->uri
, h2
->uri
));
1059 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1062 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1064 return (strcmp(d1
->d
, d2
->d
));
1066 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1069 get_work_dir(struct settings
*s
)
1071 if (work_dir
[0] == '\0')
1073 return (g_strdup(work_dir
));
1077 set_work_dir(struct settings
*s
, char *val
)
1080 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1081 pwd
->pw_dir
, &val
[1]);
1083 strlcpy(work_dir
, val
, sizeof work_dir
);
1089 * generate a session key to secure xtp commands.
1090 * pass in a ptr to the key in question and it will
1091 * be modified in place.
1094 generate_xtp_session_key(char **key
)
1096 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1102 /* make a new one */
1103 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1104 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1105 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1106 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1108 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1112 * validate a xtp session key.
1116 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1118 if (strcmp(trusted
, untrusted
) != 0) {
1119 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1128 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1130 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1132 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1134 struct valid_url_types
{
1145 valid_url_type(char *url
)
1149 for (i
= 0; i
< LENGTH(vut
); i
++)
1150 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1157 print_cookie(char *msg
, SoupCookie
*c
)
1163 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1164 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1165 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1166 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1167 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1168 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1169 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1170 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1171 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1172 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1176 walk_alias(struct settings
*s
,
1177 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1182 if (s
== NULL
|| cb
== NULL
) {
1183 show_oops_s("walk_alias invalid parameters");
1187 TAILQ_FOREACH(a
, &aliases
, entry
) {
1188 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1189 cb(s
, str
, cb_args
);
1195 match_alias(char *url_in
)
1199 char *url_out
= NULL
, *search
, *enc_arg
;
1201 search
= g_strdup(url_in
);
1203 if (strsep(&arg
, " \t") == NULL
) {
1204 show_oops_s("match_alias: NULL URL");
1208 TAILQ_FOREACH(a
, &aliases
, entry
) {
1209 if (!strcmp(search
, a
->a_name
))
1214 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1217 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1218 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1221 url_out
= g_strdup(a
->a_uri
);
1229 guess_url_type(char *url_in
)
1232 char *url_out
= NULL
, *enc_search
= NULL
;
1234 url_out
= match_alias(url_in
);
1235 if (url_out
!= NULL
)
1240 * If there is no dot nor slash in the string and it isn't a
1241 * path to a local file and doesn't resolves to an IP, assume
1242 * that the user wants to search for the string.
1245 if (strchr(url_in
, '.') == NULL
&&
1246 strchr(url_in
, '/') == NULL
&&
1247 stat(url_in
, &sb
) != 0 &&
1248 gethostbyname(url_in
) == NULL
) {
1250 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1251 url_out
= g_strdup_printf(search_string
, enc_search
);
1257 /* XXX not sure about this heuristic */
1258 if (stat(url_in
, &sb
) == 0)
1259 url_out
= g_strdup_printf("file://%s", url_in
);
1261 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1263 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1269 load_uri(struct tab
*t
, gchar
*uri
)
1272 gchar
*newuri
= NULL
;
1278 /* Strip leading spaces. */
1279 while(*uri
&& isspace(*uri
))
1282 if (strlen(uri
) == 0) {
1287 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1288 for (i
= 0; i
< LENGTH(about_list
); i
++)
1289 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1290 bzero(&args
, sizeof args
);
1291 about_list
[i
].func(t
, &args
);
1294 show_oops(t
, "invalid about page");
1298 if (valid_url_type(uri
)) {
1299 newuri
= guess_url_type(uri
);
1303 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1304 webkit_web_view_load_uri(t
->wv
, uri
);
1311 get_uri(WebKitWebView
*wv
)
1313 WebKitWebFrame
*frame
;
1316 frame
= webkit_web_view_get_main_frame(wv
);
1317 uri
= webkit_web_frame_get_uri(frame
);
1319 if (uri
&& strlen(uri
) > 0)
1326 add_alias(struct settings
*s
, char *line
)
1329 struct alias
*a
= NULL
;
1331 if (s
== NULL
|| line
== NULL
) {
1332 show_oops_s("add_alias invalid parameters");
1337 a
= g_malloc(sizeof(*a
));
1339 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1340 show_oops_s("add_alias: incomplete alias definition");
1343 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1344 show_oops_s("add_alias: invalid alias definition");
1348 a
->a_name
= g_strdup(alias
);
1349 a
->a_uri
= g_strdup(l
);
1351 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1353 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1363 add_mime_type(struct settings
*s
, char *line
)
1367 struct mime_type
*m
= NULL
;
1369 /* XXX this could be smarter */
1372 show_oops_s("add_mime_type invalid parameters");
1377 m
= g_malloc(sizeof(*m
));
1379 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1380 show_oops_s("add_mime_type: invalid mime_type");
1383 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1384 mime_type
[strlen(mime_type
) - 1] = '\0';
1389 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1390 show_oops_s("add_mime_type: invalid mime_type");
1394 m
->mt_type
= g_strdup(mime_type
);
1395 m
->mt_action
= g_strdup(l
);
1397 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1398 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1400 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1410 find_mime_type(char *mime_type
)
1412 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1414 TAILQ_FOREACH(m
, &mtl
, entry
) {
1415 if (m
->mt_default
&&
1416 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1419 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1432 walk_mime_type(struct settings
*s
,
1433 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1435 struct mime_type
*m
;
1438 if (s
== NULL
|| cb
== NULL
)
1439 show_oops_s("walk_mime_type invalid parameters");
1441 TAILQ_FOREACH(m
, &mtl
, entry
) {
1442 str
= g_strdup_printf("%s%s --> %s",
1444 m
->mt_default
? "*" : "",
1446 cb(s
, str
, cb_args
);
1452 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1457 if (str
== NULL
|| wl
== NULL
)
1459 if (strlen(str
) < 2)
1462 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1464 /* treat *.moo.com the same as .moo.com */
1465 if (str
[0] == '*' && str
[1] == '.')
1467 else if (str
[0] == '.')
1472 d
= g_malloc(sizeof *d
);
1474 d
->d
= g_strdup_printf(".%s", str
);
1476 d
->d
= g_strdup(str
);
1479 if (RB_INSERT(domain_list
, wl
, d
))
1482 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1493 add_cookie_wl(struct settings
*s
, char *entry
)
1495 wl_add(entry
, &c_wl
, 1);
1500 walk_cookie_wl(struct settings
*s
,
1501 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1505 if (s
== NULL
|| cb
== NULL
) {
1506 show_oops_s("walk_cookie_wl invalid parameters");
1510 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1511 cb(s
, d
->d
, cb_args
);
1515 walk_js_wl(struct settings
*s
,
1516 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1520 if (s
== NULL
|| cb
== NULL
) {
1521 show_oops_s("walk_js_wl invalid parameters");
1525 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1526 cb(s
, d
->d
, cb_args
);
1530 add_js_wl(struct settings
*s
, char *entry
)
1532 wl_add(entry
, &js_wl
, 1 /* persistent */);
1537 wl_find(const gchar
*search
, struct domain_list
*wl
)
1540 struct domain
*d
= NULL
, dfind
;
1543 if (search
== NULL
|| wl
== NULL
)
1545 if (strlen(search
) < 2)
1548 if (search
[0] != '.')
1549 s
= g_strdup_printf(".%s", search
);
1551 s
= g_strdup(search
);
1553 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1556 d
= RB_FIND(domain_list
, wl
, &dfind
);
1570 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1576 if (s
== NULL
|| wl
== NULL
)
1579 if (!strncmp(s
, "http://", strlen("http://")))
1580 s
= &s
[strlen("http://")];
1581 else if (!strncmp(s
, "https://", strlen("https://")))
1582 s
= &s
[strlen("https://")];
1587 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1588 /* chop string at first slash */
1589 if (s
[i
] == '/' || s
[i
] == '\0') {
1592 r
= wl_find(ss
, wl
);
1601 get_toplevel_domain(char *domain
)
1608 if (strlen(domain
) < 2)
1611 s
= &domain
[strlen(domain
) - 1];
1612 while (s
!= domain
) {
1628 settings_add(char *var
, char *val
)
1635 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1636 if (strcmp(var
, rs
[i
].name
))
1640 if (rs
[i
].s
->set(&rs
[i
], val
))
1641 errx(1, "invalid value for %s", var
);
1645 switch (rs
[i
].type
) {
1654 errx(1, "invalid sval for %s",
1668 errx(1, "invalid type for %s", var
);
1677 config_parse(char *filename
, int runtime
)
1680 char *line
, *cp
, *var
, *val
;
1681 size_t len
, lineno
= 0;
1683 char file
[PATH_MAX
];
1686 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1688 if (filename
== NULL
)
1691 if (runtime
&& runtime_settings
[0] != '\0') {
1692 snprintf(file
, sizeof file
, "%s/%s",
1693 work_dir
, runtime_settings
);
1694 if (stat(file
, &sb
)) {
1695 warnx("runtime file doesn't exist, creating it");
1696 if ((f
= fopen(file
, "w")) == NULL
)
1698 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1702 strlcpy(file
, filename
, sizeof file
);
1704 if ((config
= fopen(file
, "r")) == NULL
) {
1705 warn("config_parse: cannot open %s", filename
);
1710 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1711 if (feof(config
) || ferror(config
))
1715 cp
+= (long)strspn(cp
, WS
);
1716 if (cp
[0] == '\0') {
1722 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1723 errx(1, "invalid config file entry: %s", line
);
1725 cp
+= (long)strspn(cp
, WS
);
1727 if ((val
= strsep(&cp
, "\0")) == NULL
)
1730 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1731 handled
= settings_add(var
, val
);
1733 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1742 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1748 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1752 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1755 JSStringGetUTF8CString(jsref
, s
, l
);
1756 JSStringRelease(jsref
);
1762 disable_hints(struct tab
*t
)
1764 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1765 bzero(t
->hint_num
, sizeof t
->hint_num
);
1766 run_script(t
, "vimprobable_clear()");
1768 t
->hint_mode
= XT_HINT_NONE
;
1772 enable_hints(struct tab
*t
)
1774 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1775 run_script(t
, "vimprobable_show_hints()");
1777 t
->hint_mode
= XT_HINT_NONE
;
1780 #define XT_JS_OPEN ("open;")
1781 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1782 #define XT_JS_FIRE ("fire;")
1783 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1784 #define XT_JS_FOUND ("found;")
1785 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1788 run_script(struct tab
*t
, char *s
)
1790 JSGlobalContextRef ctx
;
1791 WebKitWebFrame
*frame
;
1793 JSValueRef val
, exception
;
1796 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1797 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1799 frame
= webkit_web_view_get_main_frame(t
->wv
);
1800 ctx
= webkit_web_frame_get_global_context(frame
);
1802 str
= JSStringCreateWithUTF8CString(s
);
1803 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1804 NULL
, 0, &exception
);
1805 JSStringRelease(str
);
1807 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1809 es
= js_ref_to_string(ctx
, exception
);
1810 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1814 es
= js_ref_to_string(ctx
, val
);
1815 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1817 /* handle return value right here */
1818 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1820 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1823 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1824 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1825 &es
[XT_JS_FIRE_LEN
]);
1830 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1831 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1842 hint(struct tab
*t
, struct karg
*args
)
1845 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1847 if (t
->hints_on
== 0)
1856 * Doesn't work fully, due to the following bug:
1857 * https://bugs.webkit.org/show_bug.cgi?id=51747
1860 restore_global_history(void)
1862 char file
[PATH_MAX
];
1868 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1870 if ((f
= fopen(file
, "r")) == NULL
) {
1871 warnx("%s: fopen", __func__
);
1876 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1877 if (feof(f
) || ferror(f
))
1880 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1881 if (feof(f
) || ferror(f
)) {
1883 warnx("%s: broken history file\n", __func__
);
1887 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1888 webkit_web_history_item_new_with_data(uri
, title
);
1889 h
= g_malloc(sizeof(struct history
));
1890 h
->uri
= g_strdup(uri
);
1891 h
->title
= g_strdup(title
);
1892 RB_INSERT(history_list
, &hl
, h
);
1893 completion_add_uri(h
->uri
);
1895 warnx("%s: failed to restore history\n", __func__
);
1911 save_global_history_to_disk(struct tab
*t
)
1913 char file
[PATH_MAX
];
1917 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1919 if ((f
= fopen(file
, "w")) == NULL
) {
1920 show_oops(t
, "%s: global history file: %s",
1921 __func__
, strerror(errno
));
1925 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1926 if (h
->uri
&& h
->title
)
1927 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
1936 quit(struct tab
*t
, struct karg
*args
)
1938 if (save_global_history
)
1939 save_global_history_to_disk(t
);
1947 open_tabs(struct tab
*t
, struct karg
*a
)
1949 char file
[PATH_MAX
];
1953 struct tab
*ti
, *tt
;
1958 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1959 if ((f
= fopen(file
, "r")) == NULL
)
1962 ti
= TAILQ_LAST(&tabs
, tab_list
);
1965 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1966 if (feof(f
) || ferror(f
))
1969 /* retrieve session name */
1970 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
1971 strlcpy(named_session
,
1972 &uri
[strlen(XT_SAVE_SESSION_ID
)],
1973 sizeof named_session
);
1977 if (uri
&& strlen(uri
))
1978 create_new_tab(uri
, NULL
, 1);
1984 /* close open tabs */
1985 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
1987 tt
= TAILQ_FIRST(&tabs
);
2006 restore_saved_tabs(void)
2008 char file
[PATH_MAX
];
2009 int unlink_file
= 0;
2014 snprintf(file
, sizeof file
, "%s/%s",
2015 sessions_dir
, XT_RESTART_TABS_FILE
);
2016 if (stat(file
, &sb
) == -1)
2017 a
.s
= XT_SAVED_TABS_FILE
;
2020 a
.s
= XT_RESTART_TABS_FILE
;
2023 a
.i
= XT_SES_DONOTHING
;
2024 rv
= open_tabs(NULL
, &a
);
2033 save_tabs(struct tab
*t
, struct karg
*a
)
2035 char file
[PATH_MAX
];
2040 const gchar
**arr
= NULL
;
2045 snprintf(file
, sizeof file
, "%s/%s",
2046 sessions_dir
, named_session
);
2048 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2050 if ((f
= fopen(file
, "w")) == NULL
) {
2051 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2055 /* save session name */
2056 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2058 /* save tabs, in the order they are arranged in the notebook */
2059 TAILQ_FOREACH(ti
, &tabs
, entry
)
2062 arr
= g_malloc0(len
* sizeof(gchar
*));
2064 TAILQ_FOREACH(ti
, &tabs
, entry
) {
2065 if ((uri
= get_uri(ti
->wv
)) != NULL
)
2066 arr
[gtk_notebook_page_num(notebook
, ti
->vbox
)] = uri
;
2069 for (i
= 0; i
< len
; i
++)
2071 fprintf(f
, "%s\n", arr
[i
]);
2080 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2092 yank_uri(struct tab
*t
, struct karg
*args
)
2095 GtkClipboard
*clipboard
;
2097 if ((uri
= get_uri(t
->wv
)) == NULL
)
2100 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2101 gtk_clipboard_set_text(clipboard
, uri
, -1);
2112 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
2114 struct paste_args
*pap
;
2116 if (data
== NULL
|| text
== NULL
|| !strlen(text
))
2119 pap
= (struct paste_args
*)data
;
2122 case XT_PASTE_CURRENT_TAB
:
2123 load_uri(pap
->t
, (gchar
*)text
);
2125 case XT_PASTE_NEW_TAB
:
2126 create_new_tab((gchar
*)text
, NULL
, 1);
2134 paste_uri(struct tab
*t
, struct karg
*args
)
2136 GtkClipboard
*clipboard
;
2137 struct paste_args
*pap
;
2139 pap
= g_malloc(sizeof(struct paste_args
));
2144 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2145 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
2151 find_domain(const gchar
*s
, int add_dot
)
2154 char *r
= NULL
, *ss
= NULL
;
2159 if (!strncmp(s
, "http://", strlen("http://")))
2160 s
= &s
[strlen("http://")];
2161 else if (!strncmp(s
, "https://", strlen("https://")))
2162 s
= &s
[strlen("https://")];
2168 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2169 /* chop string at first slash */
2170 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2173 r
= g_strdup_printf(".%s", ss
);
2184 toggle_cwl(struct tab
*t
, struct karg
*args
)
2188 char *dom
= NULL
, *dom_toggle
= NULL
;
2194 uri
= get_uri(t
->wv
);
2195 dom
= find_domain(uri
, 1);
2196 d
= wl_find(dom
, &c_wl
);
2203 if (args
->i
& XT_WL_TOGGLE
)
2205 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2207 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2210 if (args
->i
& XT_WL_TOPLEVEL
)
2211 dom_toggle
= get_toplevel_domain(dom
);
2216 /* enable cookies for domain */
2217 wl_add(dom_toggle
, &c_wl
, 0);
2219 /* disable cookies for domain */
2220 RB_REMOVE(domain_list
, &c_wl
, d
);
2222 webkit_web_view_reload(t
->wv
);
2229 toggle_js(struct tab
*t
, struct karg
*args
)
2234 char *dom
= NULL
, *dom_toggle
= NULL
;
2239 g_object_get(G_OBJECT(t
->settings
),
2240 "enable-scripts", &es
, (char *)NULL
);
2241 if (args
->i
& XT_WL_TOGGLE
)
2243 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2245 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2250 uri
= get_uri(t
->wv
);
2251 dom
= find_domain(uri
, 1);
2253 if (uri
== NULL
|| dom
== NULL
) {
2254 show_oops(t
, "Can't toggle domain in JavaScript white list");
2258 if (args
->i
& XT_WL_TOPLEVEL
)
2259 dom_toggle
= get_toplevel_domain(dom
);
2264 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2265 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2267 d
= wl_find(dom_toggle
, &js_wl
);
2269 RB_REMOVE(domain_list
, &js_wl
, d
);
2270 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2272 g_object_set(G_OBJECT(t
->settings
),
2273 "enable-scripts", es
, (char *)NULL
);
2274 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2275 webkit_web_view_reload(t
->wv
);
2283 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2287 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
2292 toggle_src(struct tab
*t
, struct karg
*args
)
2299 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2300 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2301 webkit_web_view_reload(t
->wv
);
2307 focus_webview(struct tab
*t
)
2312 /* only grab focus if we are visible */
2313 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2314 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2318 focus(struct tab
*t
, struct karg
*args
)
2320 if (t
== NULL
|| args
== NULL
)
2326 if (args
->i
== XT_FOCUS_URI
)
2327 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2328 else if (args
->i
== XT_FOCUS_SEARCH
)
2329 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2335 stats(struct tab
*t
, struct karg
*args
)
2337 char *stats
, *s
, line
[64 * 1024];
2338 uint64_t line_count
= 0;
2342 show_oops_s("stats invalid parameters");
2345 if (save_rejected_cookies
) {
2346 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2348 s
= fgets(line
, sizeof line
, r_cookie_f
);
2349 if (s
== NULL
|| feof(r_cookie_f
) ||
2355 snprintf(line
, sizeof line
,
2356 "<br>Cookies blocked(*) total: %llu", line_count
);
2358 show_oops(t
, "Can't open blocked cookies file: %s",
2362 stats
= g_strdup_printf(XT_DOCTYPE
2365 "<title>Statistics</title>"
2367 "<h1>Statistics</h1>"
2369 "Cookies blocked(*) this session: %llu"
2371 "<p><small><b>*</b> results vary based on settings"
2377 load_webkit_string(t
, stats
, XT_URI_ABOUT_STATS
);
2384 blank(struct tab
*t
, struct karg
*args
)
2387 show_oops_s("about invalid parameters");
2389 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2394 about(struct tab
*t
, struct karg
*args
)
2399 show_oops_s("about invalid parameters");
2401 about
= g_strdup_printf(XT_DOCTYPE
2404 "<title>About</title>"
2408 "<b>Version: %s</b><p>"
2411 "<li>Marco Peereboom <marco@peereboom.us></li>"
2412 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2413 "<li>Edd Barrett <vext01@gmail.com> </li>"
2414 "<li>Todd T. Fries <todd@fries.net> </li>"
2416 "Copyrights and licenses can be found on the XXXterm "
2417 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
2423 load_webkit_string(t
, about
, XT_URI_ABOUT_ABOUT
);
2430 help(struct tab
*t
, struct karg
*args
)
2435 show_oops_s("help invalid parameters");
2440 "<title>XXXterm</title>"
2441 "<meta http-equiv=\"REFRESH\" content=\"0;"
2442 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2445 "XXXterm man page <a href=\"http://opensource.conformal.com/"
2446 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2447 "cgi-bin/man-cgi?xxxterm</a>"
2452 load_webkit_string(t
, help
, XT_URI_ABOUT_HELP
);
2458 * update all favorite tabs apart from one. Pass NULL if
2459 * you want to update all.
2462 update_favorite_tabs(struct tab
*apart_from
)
2465 if (!updating_fl_tabs
) {
2466 updating_fl_tabs
= 1; /* stop infinite recursion */
2467 TAILQ_FOREACH(t
, &tabs
, entry
)
2468 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2469 && (t
!= apart_from
))
2470 xtp_page_fl(t
, NULL
);
2471 updating_fl_tabs
= 0;
2475 /* show a list of favorites (bookmarks) */
2477 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2479 char file
[PATH_MAX
];
2481 char *uri
= NULL
, *title
= NULL
;
2482 size_t len
, lineno
= 0;
2484 char *header
, *body
, *tmp
, *html
= NULL
;
2486 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2489 warn("%s: bad param", __func__
);
2491 /* mark tab as favorite list */
2492 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2494 /* new session key */
2495 if (!updating_fl_tabs
)
2496 generate_xtp_session_key(&fl_session_key
);
2498 /* open favorites */
2499 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2500 if ((f
= fopen(file
, "r")) == NULL
) {
2501 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2506 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2507 "<title>Favorites</title>\n"
2510 "<h1>Favorites</h1>\n",
2514 body
= g_strdup_printf("<div align='center'><table><tr>"
2515 "<th style='width: 4%%'>#</th><th>Link</th>"
2516 "<th style='width: 15%%'>Remove</th></tr>\n");
2519 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2520 if (feof(f
) || ferror(f
))
2528 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2529 if (feof(f
) || ferror(f
)) {
2530 show_oops(t
, "favorites file corrupt");
2536 body
= g_strdup_printf("%s<tr>"
2538 "<td><a href='%s'>%s</a></td>"
2539 "<td style='text-align: center'>"
2540 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2542 body
, i
, uri
, title
,
2543 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2555 /* if none, say so */
2558 body
= g_strdup_printf("%s<tr>"
2559 "<td colspan='3' style='text-align: center'>"
2560 "No favorites - To add one use the 'favadd' command."
2561 "</td></tr>", body
);
2572 html
= g_strdup_printf("%s%s</table></div></html>",
2574 load_webkit_string(t
, html
, XT_URI_ABOUT_FAVORITES
);
2577 update_favorite_tabs(t
);
2590 getparams(char *cmd
, char *cmp
)
2595 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2596 rv
= cmd
+ strlen(cmp
);
2599 if (strlen(rv
) == 0)
2608 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2609 size_t cert_count
, char *title
)
2611 gnutls_datum_t cinfo
;
2612 char *tmp
, *header
, *body
, *footer
;
2615 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2616 footer
= g_strdup("</body></html>");
2617 body
= g_strdup("");
2619 for (i
= 0; i
< cert_count
; i
++) {
2620 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2625 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2626 body
, i
, cinfo
.data
);
2627 gnutls_free(cinfo
.data
);
2631 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2635 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2640 ca_cmd(struct tab
*t
, struct karg
*args
)
2643 int rv
= 1, certs
= 0, certs_read
;
2646 gnutls_x509_crt_t
*c
= NULL
;
2647 char *certs_buf
= NULL
, *s
;
2649 /* yeah yeah stat race */
2650 if (stat(ssl_ca_file
, &sb
)) {
2651 show_oops(t
, "no CA file: %s", ssl_ca_file
);
2655 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2656 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2660 certs_buf
= g_malloc(sb
.st_size
+ 1);
2661 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2662 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2665 certs_buf
[sb
.st_size
] = '\0';
2668 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2670 s
+= strlen("BEGIN CERTIFICATE");
2673 bzero(&dt
, sizeof dt
);
2674 dt
.data
= certs_buf
;
2675 dt
.size
= sb
.st_size
;
2676 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2677 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
,
2678 GNUTLS_X509_FMT_PEM
, 0);
2679 if (certs_read
<= 0) {
2680 show_oops(t
, "No cert(s) available");
2683 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2696 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2699 struct addrinfo hints
, *res
= NULL
, *ai
;
2703 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2706 su
= soup_uri_new(uri
);
2709 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2712 snprintf(port
, sizeof port
, "%d", su
->port
);
2713 bzero(&hints
, sizeof(struct addrinfo
));
2714 hints
.ai_flags
= AI_CANONNAME
;
2715 hints
.ai_family
= AF_UNSPEC
;
2716 hints
.ai_socktype
= SOCK_STREAM
;
2718 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2721 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2722 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2725 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2728 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2732 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2737 strlcpy(domain
, su
->host
, domain_sz
);
2748 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2751 gnutls_deinit(gsession
);
2753 gnutls_certificate_free_credentials(xcred
);
2759 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2760 gnutls_certificate_credentials_t
*xc
)
2762 gnutls_certificate_credentials_t xcred
;
2763 gnutls_session_t gsession
;
2766 if (gs
== NULL
|| xc
== NULL
)
2772 gnutls_certificate_allocate_credentials(&xcred
);
2773 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2774 GNUTLS_X509_FMT_PEM
);
2775 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2776 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2777 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2778 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2779 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2780 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2782 gnutls_error_is_fatal(rv
),
2783 gnutls_strerror_name(rv
));
2784 stop_tls(gsession
, xcred
);
2788 gnutls_credentials_type_t cred
;
2789 cred
= gnutls_auth_get_type(gsession
);
2790 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2791 stop_tls(gsession
, xcred
);
2803 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2807 const gnutls_datum_t
*cl
;
2808 gnutls_x509_crt_t
*all_certs
;
2811 if (certs
== NULL
|| cert_count
== NULL
)
2813 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2815 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2819 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2820 for (i
= 0; i
< len
; i
++) {
2821 gnutls_x509_crt_init(&all_certs
[i
]);
2822 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2823 GNUTLS_X509_FMT_PEM
< 0)) {
2837 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2841 for (i
= 0; i
< cert_count
; i
++)
2842 gnutls_x509_crt_deinit(certs
[i
]);
2847 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2848 size_t cert_count
, char *domain
)
2851 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2856 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2859 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2860 if ((f
= fopen(file
, "w")) == NULL
) {
2861 show_oops(t
, "Can't create cert file %s %s",
2862 file
, strerror(errno
));
2866 for (i
= 0; i
< cert_count
; i
++) {
2867 cert_buf_sz
= sizeof cert_buf
;
2868 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2869 cert_buf
, &cert_buf_sz
)) {
2870 show_oops(t
, "gnutls_x509_crt_export failed");
2873 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2874 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2879 /* not the best spot but oh well */
2880 gdk_color_parse("lightblue", &color
);
2881 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2882 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2883 gdk_color_parse("black", &color
);
2884 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2890 load_compare_cert(struct tab
*t
, struct karg
*args
)
2893 char domain
[8182], file
[PATH_MAX
];
2894 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2895 int s
= -1, rv
= 1, i
;
2899 gnutls_session_t gsession
;
2900 gnutls_x509_crt_t
*certs
;
2901 gnutls_certificate_credentials_t xcred
;
2906 if ((uri
= get_uri(t
->wv
)) == NULL
)
2909 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2913 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2914 show_oops(t
, "Start TLS failed");
2919 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2920 show_oops(t
, "Can't get connection certificates");
2924 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2925 if ((f
= fopen(file
, "r")) == NULL
)
2928 for (i
= 0; i
< cert_count
; i
++) {
2929 cert_buf_sz
= sizeof cert_buf
;
2930 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2931 cert_buf
, &cert_buf_sz
)) {
2934 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2935 rv
= -1; /* critical */
2938 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
2939 rv
= -1; /* critical */
2948 free_connection_certs(certs
, cert_count
);
2950 /* we close the socket first for speed */
2953 stop_tls(gsession
, xcred
);
2959 cert_cmd(struct tab
*t
, struct karg
*args
)
2962 char *action
, domain
[8182];
2965 gnutls_session_t gsession
;
2966 gnutls_x509_crt_t
*certs
;
2967 gnutls_certificate_credentials_t xcred
;
2972 if ((action
= getparams(args
->s
, "cert")))
2977 if ((uri
= get_uri(t
->wv
)) == NULL
) {
2978 show_oops(t
, "Invalid URI");
2982 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
2983 show_oops(t
, "Invalid certidicate URI: %s", uri
);
2988 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2989 show_oops(t
, "Start TLS failed");
2994 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2995 show_oops(t
, "get_connection_certs failed");
2999 if (!strcmp(action
, "show"))
3000 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3001 else if (!strcmp(action
, "save"))
3002 save_certs(t
, certs
, cert_count
, domain
);
3004 show_oops(t
, "Invalid command: %s", action
);
3006 free_connection_certs(certs
, cert_count
);
3008 /* we close the socket first for speed */
3011 stop_tls(gsession
, xcred
);
3017 remove_cookie(int index
)
3023 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3025 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3027 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3031 print_cookie("remove cookie", c
);
3032 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3037 soup_cookies_free(cf
);
3043 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
3046 char *tmp
, *header
, *body
, *footer
;
3047 int p_js
= 0, s_js
= 0;
3049 /* we set this to indicate we want to manually do navaction */
3050 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3052 if (g_str_has_prefix(args
, "show a") ||
3053 !strcmp(args
, "show")) {
3057 } else if (g_str_has_prefix(args
, "show p")) {
3058 /* show persistent */
3060 } else if (g_str_has_prefix(args
, "show s")) {
3066 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
3068 footer
= g_strdup("</body></html>");
3069 body
= g_strdup("");
3074 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3076 RB_FOREACH(d
, domain_list
, wl
) {
3080 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3088 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3090 RB_FOREACH(d
, domain_list
, wl
) {
3094 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3099 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3104 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3106 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3112 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3114 char file
[PATH_MAX
];
3116 char *line
= NULL
, *lt
= NULL
;
3119 char *dom
= NULL
, *dom_save
= NULL
;
3126 if (t
== NULL
|| args
== NULL
)
3129 if (runtime_settings
[0] == '\0')
3132 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3133 if ((f
= fopen(file
, "r+")) == NULL
)
3136 uri
= get_uri(t
->wv
);
3137 dom
= find_domain(uri
, 1);
3138 if (uri
== NULL
|| dom
== NULL
) {
3139 show_oops(t
, "Can't add domain to %s white list",
3140 js
? "JavaScript" : "cookie");
3144 if (g_str_has_prefix(args
->s
, "save d")) {
3146 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3147 show_oops(t
, "invalid domain: %s", dom
);
3150 flags
= XT_WL_TOPLEVEL
;
3151 } else if (g_str_has_prefix(args
->s
, "save f") ||
3152 !strcmp(args
->s
, "save")) {
3157 show_oops(t
, "invalid command: %s", args
->s
);
3161 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3164 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3167 if (!strcmp(line
, lt
))
3173 fprintf(f
, "%s\n", lt
);
3178 d
= wl_find(dom_save
, &js_wl
);
3180 settings_add("js_wl", dom_save
);
3181 d
= wl_find(dom_save
, &js_wl
);
3185 d
= wl_find(dom_save
, &c_wl
);
3187 settings_add("cookie_wl", dom_save
);
3188 d
= wl_find(dom_save
, &c_wl
);
3192 /* find and add to persistent jar */
3193 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3194 for (;cf
; cf
= cf
->next
) {
3196 if (!strcmp(dom_save
, ci
->domain
) ||
3197 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3198 c
= soup_cookie_copy(ci
);
3199 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3202 soup_cookies_free(cf
);
3220 js_show_wl(struct tab
*t
, struct karg
*args
)
3222 wl_show(t
, "show all", "JavaScript White List", &js_wl
);
3228 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3230 wl_show(t
, "show all", "Cookie White List", &c_wl
);
3236 cookie_cmd(struct tab
*t
, struct karg
*args
)
3241 if ((cmd
= getparams(args
->s
, "cookie")))
3247 if (g_str_has_prefix(cmd
, "show")) {
3248 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
3249 } else if (g_str_has_prefix(cmd
, "save")) {
3252 } else if (g_str_has_prefix(cmd
, "toggle")) {
3254 if (g_str_has_prefix(cmd
, "toggle d"))
3255 a
.i
|= XT_WL_TOPLEVEL
;
3259 } else if (g_str_has_prefix(cmd
, "delete")) {
3260 show_oops(t
, "'cookie delete' currently unimplemented");
3262 show_oops(t
, "unknown cookie command: %s", cmd
);
3268 js_cmd(struct tab
*t
, struct karg
*args
)
3273 if ((cmd
= getparams(args
->s
, "js")))
3278 if (g_str_has_prefix(cmd
, "show")) {
3279 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
3280 } else if (g_str_has_prefix(cmd
, "save")) {
3283 } else if (g_str_has_prefix(cmd
, "toggle")) {
3285 if (g_str_has_prefix(cmd
, "toggle d"))
3286 a
.i
|= XT_WL_TOPLEVEL
;
3290 } else if (g_str_has_prefix(cmd
, "delete")) {
3291 show_oops(t
, "'js delete' currently unimplemented");
3293 show_oops(t
, "unknown js command: %s", cmd
);
3299 add_favorite(struct tab
*t
, struct karg
*args
)
3301 char file
[PATH_MAX
];
3304 size_t urilen
, linelen
;
3305 const gchar
*uri
, *title
;
3310 /* don't allow adding of xtp pages to favorites */
3311 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3312 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3316 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3317 if ((f
= fopen(file
, "r+")) == NULL
) {
3318 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3322 title
= webkit_web_view_get_title(t
->wv
);
3323 uri
= get_uri(t
->wv
);
3328 if (title
== NULL
|| uri
== NULL
) {
3329 show_oops(t
, "can't add page to favorites");
3333 urilen
= strlen(uri
);
3336 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3337 if (feof(f
) || ferror(f
))
3340 if (linelen
== urilen
&& !strcmp(line
, uri
))
3347 fprintf(f
, "\n%s\n%s", title
, uri
);
3353 update_favorite_tabs(NULL
);
3359 navaction(struct tab
*t
, struct karg
*args
)
3361 WebKitWebHistoryItem
*item
;
3363 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3364 t
->tab_id
, args
->i
);
3367 if (args
->i
== XT_NAV_BACK
)
3368 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3370 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3372 return (XT_CB_PASSTHROUGH
);
3373 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3375 return (XT_CB_PASSTHROUGH
);
3380 webkit_web_view_go_back(t
->wv
);
3382 case XT_NAV_FORWARD
:
3383 webkit_web_view_go_forward(t
->wv
);
3386 webkit_web_view_reload(t
->wv
);
3388 case XT_NAV_RELOAD_CACHE
:
3389 webkit_web_view_reload_bypass_cache(t
->wv
);
3392 return (XT_CB_PASSTHROUGH
);
3396 move(struct tab
*t
, struct karg
*args
)
3398 GtkAdjustment
*adjust
;
3399 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3404 case XT_MOVE_BOTTOM
:
3406 case XT_MOVE_PAGEDOWN
:
3407 case XT_MOVE_PAGEUP
:
3408 case XT_MOVE_HALFDOWN
:
3409 case XT_MOVE_HALFUP
:
3410 adjust
= t
->adjust_v
;
3413 adjust
= t
->adjust_h
;
3417 pos
= gtk_adjustment_get_value(adjust
);
3418 ps
= gtk_adjustment_get_page_size(adjust
);
3419 upper
= gtk_adjustment_get_upper(adjust
);
3420 lower
= gtk_adjustment_get_lower(adjust
);
3421 si
= gtk_adjustment_get_step_increment(adjust
);
3422 pi
= gtk_adjustment_get_page_increment(adjust
);
3425 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3426 "max %f si %f pi %f\n",
3427 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3428 pos
, ps
, upper
, lower
, max
, si
, pi
);
3434 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3439 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3441 case XT_MOVE_BOTTOM
:
3442 case XT_MOVE_FARRIGHT
:
3443 gtk_adjustment_set_value(adjust
, max
);
3446 case XT_MOVE_FARLEFT
:
3447 gtk_adjustment_set_value(adjust
, lower
);
3449 case XT_MOVE_PAGEDOWN
:
3451 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3453 case XT_MOVE_PAGEUP
:
3455 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3457 case XT_MOVE_HALFDOWN
:
3459 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3461 case XT_MOVE_HALFUP
:
3463 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3466 return (XT_CB_PASSTHROUGH
);
3469 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3471 return (XT_CB_HANDLED
);
3475 url_set_visibility(void)
3479 TAILQ_FOREACH(t
, &tabs
, entry
) {
3480 if (show_url
== 0) {
3481 gtk_widget_hide(t
->toolbar
);
3484 gtk_widget_show(t
->toolbar
);
3489 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3492 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3494 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3498 statusbar_set_visibility(void)
3502 TAILQ_FOREACH(t
, &tabs
, entry
) {
3503 if (show_statusbar
== 0) {
3504 gtk_widget_hide(t
->statusbar
);
3507 gtk_widget_show(t
->statusbar
);
3512 url_set(struct tab
*t
, int enable_url_entry
)
3517 show_url
= enable_url_entry
;
3519 if (enable_url_entry
) {
3520 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3521 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3522 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3524 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3525 GTK_ENTRY_ICON_PRIMARY
);
3527 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3528 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3529 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3530 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3536 fullscreen(struct tab
*t
, struct karg
*args
)
3538 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3541 return (XT_CB_PASSTHROUGH
);
3543 if (show_url
== 0) {
3551 url_set_visibility();
3552 notebook_tab_set_visibility(notebook
);
3554 return (XT_CB_HANDLED
);
3558 statusaction(struct tab
*t
, struct karg
*args
)
3560 int rv
= XT_CB_HANDLED
;
3562 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3565 return (XT_CB_PASSTHROUGH
);
3568 case XT_STATUSBAR_SHOW
:
3569 if (show_statusbar
== 0) {
3571 statusbar_set_visibility();
3574 case XT_STATUSBAR_HIDE
:
3575 if (show_statusbar
== 1) {
3577 statusbar_set_visibility();
3585 urlaction(struct tab
*t
, struct karg
*args
)
3587 int rv
= XT_CB_HANDLED
;
3589 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3592 return (XT_CB_PASSTHROUGH
);
3596 if (show_url
== 0) {
3598 url_set_visibility();
3602 if (show_url
== 1) {
3604 url_set_visibility();
3612 tabaction(struct tab
*t
, struct karg
*args
)
3614 int rv
= XT_CB_HANDLED
;
3618 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3621 return (XT_CB_PASSTHROUGH
);
3625 if ((url
= getparams(args
->s
, "tabnew")))
3626 create_new_tab(url
, NULL
, 1);
3628 create_new_tab(NULL
, NULL
, 1);
3633 case XT_TAB_DELQUIT
:
3634 if (gtk_notebook_get_n_pages(notebook
) > 1)
3640 if ((url
= getparams(args
->s
, "open")) ||
3641 ((url
= getparams(args
->s
, "op"))) ||
3642 ((url
= getparams(args
->s
, "o"))))
3645 rv
= XT_CB_PASSTHROUGH
;
3651 if (show_tabs
== 0) {
3653 notebook_tab_set_visibility(notebook
);
3657 if (show_tabs
== 1) {
3659 notebook_tab_set_visibility(notebook
);
3662 case XT_TAB_UNDO_CLOSE
:
3663 if (undo_count
== 0) {
3664 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3668 u
= TAILQ_FIRST(&undos
);
3669 create_new_tab(u
->uri
, u
, 1);
3671 TAILQ_REMOVE(&undos
, u
, entry
);
3673 /* u->history is freed in create_new_tab() */
3678 rv
= XT_CB_PASSTHROUGH
;
3692 resizetab(struct tab
*t
, struct karg
*args
)
3694 if (t
== NULL
|| args
== NULL
) {
3695 show_oops_s("resizetab invalid parameters");
3696 return (XT_CB_PASSTHROUGH
);
3699 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3700 t
->tab_id
, args
->i
);
3702 adjustfont_webkit(t
, args
->i
);
3704 return (XT_CB_HANDLED
);
3708 movetab(struct tab
*t
, struct karg
*args
)
3713 if (t
== NULL
|| args
== NULL
) {
3714 show_oops_s("movetab invalid parameters");
3715 return (XT_CB_PASSTHROUGH
);
3718 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3719 t
->tab_id
, args
->i
);
3721 if (args
->i
== XT_TAB_INVALID
)
3722 return (XT_CB_PASSTHROUGH
);
3724 if (args
->i
< XT_TAB_INVALID
) {
3725 /* next or previous tab */
3726 if (TAILQ_EMPTY(&tabs
))
3727 return (XT_CB_PASSTHROUGH
);
3731 /* if at the last page, loop around to the first */
3732 if (gtk_notebook_get_current_page(notebook
) ==
3733 gtk_notebook_get_n_pages(notebook
) - 1)
3734 gtk_notebook_set_current_page(notebook
, 0);
3736 gtk_notebook_next_page(notebook
);
3739 /* if at the first page, loop around to the last */
3740 if (gtk_notebook_current_page(notebook
) == 0)
3741 gtk_notebook_set_current_page(notebook
,
3742 gtk_notebook_get_n_pages(notebook
) - 1);
3744 gtk_notebook_prev_page(notebook
);
3747 gtk_notebook_set_current_page(notebook
, 0);
3750 gtk_notebook_set_current_page(notebook
, -1);
3753 return (XT_CB_PASSTHROUGH
);
3756 return (XT_CB_HANDLED
);
3761 if (t
->tab_id
== x
) {
3762 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3763 return (XT_CB_HANDLED
);
3766 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3767 if (tt
->tab_id
== x
) {
3768 gtk_notebook_set_current_page(notebook
, x
);
3769 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3775 return (XT_CB_HANDLED
);
3779 command(struct tab
*t
, struct karg
*args
)
3781 char *s
= NULL
, *ss
= NULL
;
3785 if (t
== NULL
|| args
== NULL
) {
3786 show_oops_s("command invalid parameters");
3787 return (XT_CB_PASSTHROUGH
);
3806 case XT_CMD_OPEN_CURRENT
:
3809 case XT_CMD_TABNEW_CURRENT
:
3810 if (!s
) /* FALL THROUGH? */
3812 if ((uri
= get_uri(t
->wv
)) != NULL
) {
3813 ss
= g_strdup_printf("%s%s", s
, uri
);
3818 show_oops(t
, "command: invalid opcode %d", args
->i
);
3819 return (XT_CB_PASSTHROUGH
);
3822 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3824 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3825 gdk_color_parse("white", &color
);
3826 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3828 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3829 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3834 return (XT_CB_HANDLED
);
3838 * Return a new string with a download row (in html)
3839 * appended. Old string is freed.
3842 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3845 WebKitDownloadStatus stat
;
3846 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3848 char cur_sz
[FMT_SCALED_STRSIZE
];
3849 char tot_sz
[FMT_SCALED_STRSIZE
];
3852 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3854 /* All actions wil take this form:
3855 * xxxt://class/seskey
3857 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3858 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3860 stat
= webkit_download_get_status(dl
->download
);
3863 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3864 status_html
= g_strdup_printf("Finished");
3865 cmd_html
= g_strdup_printf(
3866 "<a href='%s%d/%d'>Remove</a>",
3867 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3869 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3870 /* gather size info */
3871 progress
= 100 * webkit_download_get_progress(dl
->download
);
3874 webkit_download_get_current_size(dl
->download
), cur_sz
);
3876 webkit_download_get_total_size(dl
->download
), tot_sz
);
3878 status_html
= g_strdup_printf(
3879 "<div style='width: 100%%' align='center'>"
3880 "<div class='progress-outer'>"
3881 "<div class='progress-inner' style='width: %.2f%%'>"
3882 "</div></div></div>"
3883 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
3884 progress
, cur_sz
, tot_sz
, progress
);
3886 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3887 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3891 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3892 status_html
= g_strdup_printf("Cancelled");
3893 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3894 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3896 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3897 status_html
= g_strdup_printf("Error!");
3898 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3899 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3901 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3902 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3903 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3904 status_html
= g_strdup_printf("Starting");
3907 show_oops(t
, "%s: unknown download status", __func__
);
3910 new_html
= g_strdup_printf(
3911 "%s\n<tr><td>%s</td><td>%s</td>"
3912 "<td style='text-align:center'>%s</td></tr>\n",
3913 html
, basename(webkit_download_get_uri(dl
->download
)),
3914 status_html
, cmd_html
);
3918 g_free(status_html
);
3929 * update all download tabs apart from one. Pass NULL if
3930 * you want to update all.
3933 update_download_tabs(struct tab
*apart_from
)
3936 if (!updating_dl_tabs
) {
3937 updating_dl_tabs
= 1; /* stop infinite recursion */
3938 TAILQ_FOREACH(t
, &tabs
, entry
)
3939 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3940 && (t
!= apart_from
))
3941 xtp_page_dl(t
, NULL
);
3942 updating_dl_tabs
= 0;
3947 * update all cookie tabs apart from one. Pass NULL if
3948 * you want to update all.
3951 update_cookie_tabs(struct tab
*apart_from
)
3954 if (!updating_cl_tabs
) {
3955 updating_cl_tabs
= 1; /* stop infinite recursion */
3956 TAILQ_FOREACH(t
, &tabs
, entry
)
3957 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3958 && (t
!= apart_from
))
3959 xtp_page_cl(t
, NULL
);
3960 updating_cl_tabs
= 0;
3965 * update all history tabs apart from one. Pass NULL if
3966 * you want to update all.
3969 update_history_tabs(struct tab
*apart_from
)
3973 if (!updating_hl_tabs
) {
3974 updating_hl_tabs
= 1; /* stop infinite recursion */
3975 TAILQ_FOREACH(t
, &tabs
, entry
)
3976 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
3977 && (t
!= apart_from
))
3978 xtp_page_hl(t
, NULL
);
3979 updating_hl_tabs
= 0;
3983 /* cookie management XTP page */
3985 xtp_page_cl(struct tab
*t
, struct karg
*args
)
3987 char *header
, *body
, *footer
, *page
, *tmp
;
3988 int i
= 1; /* all ids start 1 */
3989 GSList
*sc
, *pc
, *pc_start
;
3991 char *type
, *table_headers
;
3992 char *last_domain
= strdup("");
3994 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3997 show_oops_s("%s invalid parameters", __func__
);
4000 /* mark this tab as cookie jar */
4001 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
4003 /* Generate a new session key */
4004 if (!updating_cl_tabs
)
4005 generate_xtp_session_key(&cl_session_key
);
4008 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4009 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
4010 "</head><body><h1>Cookie Jar</h1>\n");
4013 table_headers
= g_strdup_printf("<div align='center'><table><tr>"
4020 "<th>HTTP<br />only</th>"
4021 "<th>Rm</th></tr>\n");
4023 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4024 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4028 for (; sc
; sc
= sc
->next
) {
4031 if (strcmp(last_domain
, c
->domain
) != 0) {
4034 last_domain
= strdup(c
->domain
);
4038 body
= g_strdup_printf("%s</table></div>"
4040 body
, c
->domain
, table_headers
);
4044 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4045 c
->domain
, table_headers
);
4050 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4051 if (soup_cookie_equal(pc
->data
, c
)) {
4052 type
= "Session + Persistent";
4057 body
= g_strdup_printf(
4059 "<td style='width: text-align: center'>%s</td>"
4060 "<td style='width: 1px'>%s</td>"
4061 "<td style='width=70%%;overflow: visible'>"
4062 " <textarea rows='4'>%s</textarea>"
4066 "<td style='width: 1px; text-align: center'>%d</td>"
4067 "<td style='width: 1px; text-align: center'>%d</td>"
4068 "<td style='width: 1px; text-align: center'>"
4069 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4076 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4091 soup_cookies_free(sc
);
4092 soup_cookies_free(pc
);
4094 /* small message if there are none */
4096 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4097 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4101 footer
= g_strdup_printf("</table></div></body></html>");
4103 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4108 g_free(table_headers
);
4109 g_free(last_domain
);
4111 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4112 update_cookie_tabs(t
);
4120 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4122 char *header
, *body
, *footer
, *page
, *tmp
;
4124 int i
= 1; /* all ids start 1 */
4126 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4129 show_oops_s("%s invalid parameters", __func__
);
4133 /* mark this tab as history manager */
4134 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
4136 /* Generate a new session key */
4137 if (!updating_hl_tabs
)
4138 generate_xtp_session_key(&hl_session_key
);
4141 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
4142 "<title>History</title>\n"
4145 "<h1>History</h1>\n",
4149 body
= g_strdup_printf("<div align='center'><table><tr>"
4150 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
4152 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4154 body
= g_strdup_printf(
4156 "<td><a href='%s'>%s</a></td>"
4158 "<td style='text-align: center'>"
4159 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4160 body
, h
->uri
, h
->uri
, h
->title
,
4161 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4162 XT_XTP_HL_REMOVE
, i
);
4168 /* small message if there are none */
4171 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4172 "colspan='3'>No History</td></tr>\n", body
);
4177 footer
= g_strdup_printf("</table></div></body></html>");
4179 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4182 * update all history manager tabs as the xtp session
4183 * key has now changed. No need to update the current tab.
4184 * Already did that above.
4186 update_history_tabs(t
);
4192 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4199 * Generate a web page detailing the status of any downloads
4202 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4204 struct download
*dl
;
4205 char *header
, *body
, *footer
, *page
, *tmp
;
4209 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4212 show_oops_s("%s invalid parameters", __func__
);
4215 /* mark as a download manager tab */
4216 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
4219 * Generate a new session key for next page instance.
4220 * This only happens for the top level call to xtp_page_dl()
4221 * in which case updating_dl_tabs is 0.
4223 if (!updating_dl_tabs
)
4224 generate_xtp_session_key(&dl_session_key
);
4226 /* header - with refresh so as to update */
4227 if (refresh_interval
>= 1)
4228 ref
= g_strdup_printf(
4229 "<meta http-equiv='refresh' content='%u"
4230 ";url=%s%d/%s/%d' />\n",
4240 header
= g_strdup_printf(
4242 "<title>Downloads</title>\n%s%s</head>\n",
4243 XT_DOCTYPE XT_HTML_TAG
,
4247 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
4248 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4249 "</p><table><tr><th style='width: 60%%'>"
4250 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4251 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4253 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4254 body
= xtp_page_dl_row(t
, body
, dl
);
4258 /* message if no downloads in list */
4261 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4262 " style='text-align: center'>"
4263 "No downloads</td></tr>\n", body
);
4268 footer
= g_strdup_printf("</table></div></body></html>");
4270 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4274 * update all download manager tabs as the xtp session
4275 * key has now changed. No need to update the current tab.
4276 * Already did that above.
4278 update_download_tabs(t
);
4285 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4292 search(struct tab
*t
, struct karg
*args
)
4296 if (t
== NULL
|| args
== NULL
) {
4297 show_oops_s("search invalid parameters");
4300 if (t
->search_text
== NULL
) {
4301 if (global_search
== NULL
)
4302 return (XT_CB_PASSTHROUGH
);
4304 t
->search_text
= g_strdup(global_search
);
4305 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4306 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4310 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4311 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4314 case XT_SEARCH_NEXT
:
4315 d
= t
->search_forward
;
4317 case XT_SEARCH_PREV
:
4318 d
= !t
->search_forward
;
4321 return (XT_CB_PASSTHROUGH
);
4324 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4326 return (XT_CB_HANDLED
);
4329 struct settings_args
{
4335 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4338 struct settings_args
*sa
= cb_args
;
4343 if (s
->flags
& XT_SF_RUNTIME
)
4349 *sa
->body
= g_strdup_printf(
4351 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
4352 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
4364 set(struct tab
*t
, struct karg
*args
)
4366 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
4368 struct settings_args sa
;
4370 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
4371 bzero(&sa
, sizeof sa
);
4375 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4376 "\n<head><title>Settings</title>\n"
4377 "</head><body><h1>Settings</h1>\n");
4380 body
= g_strdup_printf("<div align='center'><table><tr>"
4381 "<th align='left'>Setting</th>"
4382 "<th align='left'>Value</th></tr>\n");
4384 settings_walk(print_setting
, &sa
);
4387 /* small message if there are none */
4390 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4391 "colspan='2'>No settings</td></tr>\n", body
);
4396 footer
= g_strdup_printf("</table></div></body></html>");
4398 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4404 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4406 show_oops(t
, "Invalid command: %s", pars
);
4408 return (XT_CB_PASSTHROUGH
);
4412 session_save(struct tab
*t
, char *filename
, char **ret
)
4418 f
+= strlen("save");
4419 while (*f
== ' ' && *f
!= '\0')
4425 if (f
[0] == '.' || f
[0] == '/')
4429 if (save_tabs(t
, &a
))
4431 strlcpy(named_session
, f
, sizeof named_session
);
4439 session_open(struct tab
*t
, char *filename
, char **ret
)
4445 f
+= strlen("open");
4446 while (*f
== ' ' && *f
!= '\0')
4452 if (f
[0] == '.' || f
[0] == '/')
4456 a
.i
= XT_SES_CLOSETABS
;
4457 if (open_tabs(t
, &a
))
4460 strlcpy(named_session
, f
, sizeof named_session
);
4468 session_delete(struct tab
*t
, char *filename
, char **ret
)
4470 char file
[PATH_MAX
];
4474 f
+= strlen("delete");
4475 while (*f
== ' ' && *f
!= '\0')
4481 if (f
[0] == '.' || f
[0] == '/')
4484 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, f
);
4488 if (!strcmp(f
, named_session
))
4489 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4490 sizeof named_session
);
4498 session_cmd(struct tab
*t
, struct karg
*args
)
4500 char *action
= NULL
;
4501 char *filename
= NULL
;
4506 if ((action
= getparams(args
->s
, "session")))
4511 if (!strcmp(action
, "show"))
4512 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4513 XT_SAVED_TABS_FILE
: named_session
);
4514 else if (g_str_has_prefix(action
, "save ")) {
4515 if (session_save(t
, action
, &filename
)) {
4516 show_oops(t
, "Can't save session: %s",
4517 filename
? filename
: "INVALID");
4520 } else if (g_str_has_prefix(action
, "open ")) {
4521 if (session_open(t
, action
, &filename
)) {
4522 show_oops(t
, "Can't open session: %s",
4523 filename
? filename
: "INVALID");
4526 } else if (g_str_has_prefix(action
, "delete ")) {
4527 if (session_delete(t
, action
, &filename
)) {
4528 show_oops(t
, "Can't delete session: %s",
4529 filename
? filename
: "INVALID");
4533 show_oops(t
, "Invalid command: %s", action
);
4535 return (XT_CB_PASSTHROUGH
);
4539 * Make a hardcopy of the page
4542 print_page(struct tab
*t
, struct karg
*args
)
4544 WebKitWebFrame
*frame
;
4546 GtkPrintOperation
*op
;
4547 GtkPrintOperationAction action
;
4548 GtkPrintOperationResult print_res
;
4549 GError
*g_err
= NULL
;
4550 int marg_l
, marg_r
, marg_t
, marg_b
;
4552 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4554 ps
= gtk_page_setup_new();
4555 op
= gtk_print_operation_new();
4556 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4557 frame
= webkit_web_view_get_main_frame(t
->wv
);
4559 /* the default margins are too small, so we will bump them */
4560 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4561 XT_PRINT_EXTRA_MARGIN
;
4562 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4563 XT_PRINT_EXTRA_MARGIN
;
4564 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4565 XT_PRINT_EXTRA_MARGIN
;
4566 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4567 XT_PRINT_EXTRA_MARGIN
;
4570 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4571 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4572 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4573 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4575 gtk_print_operation_set_default_page_setup(op
, ps
);
4577 /* this appears to free 'op' and 'ps' */
4578 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4580 /* check it worked */
4581 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4582 show_oops_s("can't print: %s", g_err
->message
);
4583 g_error_free (g_err
);
4591 go_home(struct tab
*t
, struct karg
*args
)
4598 restart(struct tab
*t
, struct karg
*args
)
4602 a
.s
= XT_RESTART_TABS_FILE
;
4604 execvp(start_argv
[0], start_argv
);
4610 #define CTRL GDK_CONTROL_MASK
4611 #define MOD1 GDK_MOD1_MASK
4612 #define SHFT GDK_SHIFT_MASK
4614 /* inherent to GTK not all keys will be caught at all times */
4615 /* XXX sort key bindings */
4616 struct key_binding
{
4621 int (*func
)(struct tab
*, struct karg
*);
4623 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4625 { "cookiejar", MOD1
, 0, GDK_j
, xtp_page_cl
, {0} },
4626 { "downloadmgr", MOD1
, 0, GDK_d
, xtp_page_dl
, {0} },
4627 { "history", MOD1
, 0, GDK_h
, xtp_page_hl
, {0} },
4628 { "print", CTRL
, 0, GDK_p
, print_page
, {0}},
4629 { NULL
, 0, 0, GDK_slash
, command
, {.i
= '/'} },
4630 { NULL
, 0, 0, GDK_question
, command
, {.i
= '?'} },
4631 { NULL
, 0, 0, GDK_colon
, command
, {.i
= ':'} },
4632 { "quit", CTRL
, 0, GDK_q
, quit
, {0} },
4633 { "restart", MOD1
, 0, GDK_q
, restart
, {0} },
4634 { "togglejs", CTRL
, 0, GDK_j
, toggle_js
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4635 { "togglecookie", MOD1
, 0, GDK_c
, toggle_cwl
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4636 { "togglesrc", CTRL
, 0, GDK_s
, toggle_src
, {0} },
4637 { "yankuri", 0, 0, GDK_y
, yank_uri
, {0} },
4638 { "pasteuricur", 0, 0, GDK_p
, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
} },
4639 { "pasteurinew", 0, 0, GDK_P
, paste_uri
, {.i
= XT_PASTE_NEW_TAB
} },
4642 { "searchnext", 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
4643 { "searchprev", 0, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
4646 { "focusaddress", 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
4647 { "focussearch", 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
4649 /* command aliases (handy when -S flag is used) */
4650 { NULL
, 0, 0, GDK_F9
, command
, {.i
= XT_CMD_OPEN
} },
4651 { NULL
, 0, 0, GDK_F10
, command
, {.i
= XT_CMD_OPEN_CURRENT
} },
4652 { NULL
, 0, 0, GDK_F11
, command
, {.i
= XT_CMD_TABNEW
} },
4653 { NULL
, 0, 0, GDK_F12
, command
, {.i
= XT_CMD_TABNEW_CURRENT
} },
4656 { "hinting", 0, 0, GDK_f
, hint
, {.i
= 0} },
4659 { "goback", 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
4660 { "goback", MOD1
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
4661 { "goforward", SHFT
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
4662 { "goforward", MOD1
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
4663 { "reload", 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
4664 { "reload", CTRL
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
4665 { "reloadforce", CTRL
, 0, GDK_R
, navaction
, {.i
= XT_NAV_RELOAD_CACHE
} },
4666 { "reload" , CTRL
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
4667 { "favorites", MOD1
, 1, GDK_f
, xtp_page_fl
, {0} },
4669 /* vertical movement */
4670 { "scrolldown", 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
4671 { "scrolldown", 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
4672 { "scrollup", 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
4673 { "scrollup", 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
4674 { "scrollbottom", 0, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
4675 { "scrollbottom", 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
4676 { "scrolltop", 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
4677 { "scrolltop", 0, 0, GDK_g
, move
, {.i
= XT_MOVE_TOP
} },
4678 { "scrollpagedown", 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4679 { "scrollpagedown", CTRL
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4680 { "scrollhalfdown", CTRL
, 0, GDK_d
, move
, {.i
= XT_MOVE_HALFDOWN
} },
4681 { "scrollpagedown", 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4682 { "scrollpageup", 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
4683 { "scrollpageup", CTRL
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
4684 { "scrollhalfup", CTRL
, 0, GDK_u
, move
, {.i
= XT_MOVE_HALFUP
} },
4685 /* horizontal movement */
4686 { "scrollright", 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
4687 { "scrollright", 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
4688 { "scrollleft", 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
4689 { "scrollleft", 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
4690 { "scrollfarright", 0, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
4691 { "scrollfarleft", 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
4694 { "tabnew", CTRL
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
4695 { "tabclose", CTRL
, 1, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
4696 { "tabundoclose", 0, 0, GDK_U
, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
4697 { "tabgoto1", CTRL
, 0, GDK_1
, movetab
, {.i
= 1} },
4698 { "tabgoto2", CTRL
, 0, GDK_2
, movetab
, {.i
= 2} },
4699 { "tabgoto3", CTRL
, 0, GDK_3
, movetab
, {.i
= 3} },
4700 { "tabgoto4", CTRL
, 0, GDK_4
, movetab
, {.i
= 4} },
4701 { "tabgoto5", CTRL
, 0, GDK_5
, movetab
, {.i
= 5} },
4702 { "tabgoto6", CTRL
, 0, GDK_6
, movetab
, {.i
= 6} },
4703 { "tabgoto7", CTRL
, 0, GDK_7
, movetab
, {.i
= 7} },
4704 { "tabgoto8", CTRL
, 0, GDK_8
, movetab
, {.i
= 8} },
4705 { "tabgoto9", CTRL
, 0, GDK_9
, movetab
, {.i
= 9} },
4706 { "tabgoto10", CTRL
, 0, GDK_0
, movetab
, {.i
= 10} },
4707 { "tabgotofirst", CTRL
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
4708 { "tabgotolast", CTRL
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
4709 { "tabgotoprev", CTRL
, 0, GDK_Left
, movetab
, {.i
= XT_TAB_PREV
} },
4710 { "tabgotonext", CTRL
, 0, GDK_Right
, movetab
, {.i
= XT_TAB_NEXT
} },
4711 { "focusout", CTRL
, 0, GDK_minus
, resizetab
, {.i
= -1} },
4712 { "focusin", CTRL
, 0, GDK_plus
, resizetab
, {.i
= 1} },
4713 { "focusin", CTRL
, 0, GDK_equal
, resizetab
, {.i
= 1} },
4715 TAILQ_HEAD(keybinding_list
, key_binding
);
4718 walk_kb(struct settings
*s
,
4719 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4721 struct key_binding
*k
;
4724 if (s
== NULL
|| cb
== NULL
) {
4725 show_oops_s("walk_kb invalid parameters");
4729 TAILQ_FOREACH(k
, &kbl
, entry
) {
4730 if (k
->name
== NULL
)
4735 if (gdk_keyval_name(k
->key
) == NULL
)
4738 strlcat(str
, k
->name
, sizeof str
);
4739 strlcat(str
, ",", sizeof str
);
4741 if (k
->mask
& GDK_SHIFT_MASK
)
4742 strlcat(str
, "S-", sizeof str
);
4743 if (k
->mask
& GDK_CONTROL_MASK
)
4744 strlcat(str
, "C-", sizeof str
);
4745 if (k
->mask
& GDK_MOD1_MASK
)
4746 strlcat(str
, "M1-", sizeof str
);
4747 if (k
->mask
& GDK_MOD2_MASK
)
4748 strlcat(str
, "M2-", sizeof str
);
4749 if (k
->mask
& GDK_MOD3_MASK
)
4750 strlcat(str
, "M3-", sizeof str
);
4751 if (k
->mask
& GDK_MOD4_MASK
)
4752 strlcat(str
, "M4-", sizeof str
);
4753 if (k
->mask
& GDK_MOD5_MASK
)
4754 strlcat(str
, "M5-", sizeof str
);
4756 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4757 cb(s
, str
, cb_args
);
4761 init_keybindings(void)
4764 struct key_binding
*k
;
4766 for (i
= 0; i
< LENGTH(keys
); i
++) {
4767 k
= g_malloc0(sizeof *k
);
4768 k
->name
= keys
[i
].name
;
4769 k
->mask
= keys
[i
].mask
;
4770 k
->use_in_entry
= keys
[i
].use_in_entry
;
4771 k
->key
= keys
[i
].key
;
4772 k
->func
= keys
[i
].func
;
4773 bcopy(&keys
[i
].arg
, &k
->arg
, sizeof k
->arg
);
4774 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4776 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4777 k
->name
? k
->name
: "unnamed key");
4782 keybinding_clearall(void)
4784 struct key_binding
*k
, *next
;
4786 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4787 next
= TAILQ_NEXT(k
, entry
);
4788 if (k
->name
== NULL
)
4791 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4792 k
->name
? k
->name
: "unnamed key");
4793 TAILQ_REMOVE(&kbl
, k
, entry
);
4799 keybinding_add(char *kb
, char *value
, struct key_binding
*orig
)
4801 struct key_binding
*k
;
4802 guint keyval
, mask
= 0;
4805 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s %s\n", kb
, value
, orig
->name
);
4809 if (strcmp(kb
, orig
->name
))
4812 /* find modifier keys */
4813 if (strstr(value
, "S-"))
4814 mask
|= GDK_SHIFT_MASK
;
4815 if (strstr(value
, "C-"))
4816 mask
|= GDK_CONTROL_MASK
;
4817 if (strstr(value
, "M1-"))
4818 mask
|= GDK_MOD1_MASK
;
4819 if (strstr(value
, "M2-"))
4820 mask
|= GDK_MOD2_MASK
;
4821 if (strstr(value
, "M3-"))
4822 mask
|= GDK_MOD3_MASK
;
4823 if (strstr(value
, "M4-"))
4824 mask
|= GDK_MOD4_MASK
;
4825 if (strstr(value
, "M5-"))
4826 mask
|= GDK_MOD5_MASK
;
4829 for (i
= strlen(value
) - 1; i
> 0; i
--)
4830 if (value
[i
] == '-')
4831 value
= &value
[i
+ 1];
4833 /* validate keyname */
4834 keyval
= gdk_keyval_from_name(value
);
4835 if (keyval
== GDK_VoidSymbol
) {
4836 warnx("invalid keybinding name %s", value
);
4839 /* must run this test too, gtk+ doesn't handle 10 for example */
4840 if (gdk_keyval_name(keyval
) == NULL
) {
4841 warnx("invalid keybinding name %s", value
);
4845 /* make sure it isn't a dupe */
4846 TAILQ_FOREACH(k
, &kbl
, entry
)
4847 if (k
->key
== keyval
&& k
->mask
== mask
) {
4848 warnx("duplicate keybinding for %s", value
);
4853 k
= g_malloc0(sizeof *k
);
4854 k
->name
= orig
->name
;
4856 k
->use_in_entry
= orig
->use_in_entry
;
4858 k
->func
= orig
->func
;
4859 bcopy(&orig
->arg
, &k
->arg
, sizeof k
->arg
);
4861 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
4866 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
4867 k
->name
, gdk_keyval_name(keyval
));
4869 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4875 add_kb(struct settings
*s
, char *entry
)
4880 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
4882 /* clearall is special */
4883 if (!strcmp(entry
, "clearall")) {
4884 keybinding_clearall();
4888 kb
= strstr(entry
, ",");
4894 /* make sure it is a valid keybinding */
4895 for (i
= 0; i
< LENGTH(keys
); i
++)
4896 if (keys
[i
].name
&& !strcmp(entry
, keys
[i
].name
)) {
4897 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s 0x%x %d 0x%x\n",
4900 keys
[i
].use_in_entry
,
4903 return (keybinding_add(entry
, value
, &keys
[i
]));
4912 int (*func
)(struct tab
*, struct karg
*);
4915 { "q!", 0, quit
, {0} },
4916 { "qa", 0, quit
, {0} },
4917 { "qa!", 0, quit
, {0} },
4918 { "w", 0, save_tabs
, {0} },
4919 { "wq", 0, save_tabs_and_quit
, {0} },
4920 { "wq!", 0, save_tabs_and_quit
, {0} },
4921 { "help", 0, help
, {0} },
4922 { "about", 0, about
, {0} },
4923 { "stats", 0, stats
, {0} },
4924 { "version", 0, about
, {0} },
4925 { "cookies", 0, xtp_page_cl
, {0} },
4926 { "fav", 0, xtp_page_fl
, {0} },
4927 { "favadd", 0, add_favorite
, {0} },
4928 { "js", 2, js_cmd
, {0} },
4929 { "cookie", 2, cookie_cmd
, {0} },
4930 { "cert", 1, cert_cmd
, {0} },
4931 { "ca", 0, ca_cmd
, {0} },
4932 { "dl", 0, xtp_page_dl
, {0} },
4933 { "h", 0, xtp_page_hl
, {0} },
4934 { "hist", 0, xtp_page_hl
, {0} },
4935 { "history", 0, xtp_page_hl
, {0} },
4936 { "home", 0, go_home
, {0} },
4937 { "restart", 0, restart
, {0} },
4938 { "urlhide", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4939 { "urlh", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4940 { "urlshow", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4941 { "urls", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4942 { "statushide", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4943 { "statush", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4944 { "statusshow", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4945 { "statuss", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4947 { "1", 0, move
, {.i
= XT_MOVE_TOP
} },
4948 { "print", 0, print_page
, {0} },
4951 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4952 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4953 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4954 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4955 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4956 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4957 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4958 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4959 { "tabshow", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4960 { "tabs", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4961 { "tabhide", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4962 { "tabh", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4963 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4964 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4965 /* XXX add count to these commands */
4966 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4967 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4968 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4969 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4970 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
4971 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
4972 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
4973 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
4974 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4975 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4978 { "set", 1, set
, {0} },
4979 { "fullscreen", 0, fullscreen
, {0} },
4980 { "f", 0, fullscreen
, {0} },
4983 { "session", 1, session_cmd
, {0} },
4987 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4995 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4997 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
4999 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5006 * cancel, remove, etc. downloads
5009 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5011 struct download find
, *d
;
5013 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5015 /* some commands require a valid download id */
5016 if (cmd
!= XT_XTP_DL_LIST
) {
5017 /* lookup download in question */
5019 d
= RB_FIND(download_list
, &downloads
, &find
);
5022 show_oops(t
, "%s: no such download", __func__
);
5027 /* decide what to do */
5029 case XT_XTP_DL_CANCEL
:
5030 webkit_download_cancel(d
->download
);
5032 case XT_XTP_DL_REMOVE
:
5033 webkit_download_cancel(d
->download
); /* just incase */
5034 g_object_unref(d
->download
);
5035 RB_REMOVE(download_list
, &downloads
, d
);
5037 case XT_XTP_DL_LIST
:
5041 show_oops(t
, "%s: unknown command", __func__
);
5044 xtp_page_dl(t
, NULL
);
5048 * Actions on history, only does one thing for now, but
5049 * we provide the function for future actions
5052 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5054 struct history
*h
, *next
;
5058 case XT_XTP_HL_REMOVE
:
5059 /* walk backwards, as listed in reverse */
5060 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5061 next
= RB_PREV(history_list
, &hl
, h
);
5063 RB_REMOVE(history_list
, &hl
, h
);
5064 g_free((gpointer
) h
->title
);
5065 g_free((gpointer
) h
->uri
);
5072 case XT_XTP_HL_LIST
:
5073 /* Nothing - just xtp_page_hl() below */
5076 show_oops(t
, "%s: unknown command", __func__
);
5080 xtp_page_hl(t
, NULL
);
5083 /* remove a favorite */
5085 remove_favorite(struct tab
*t
, int index
)
5087 char file
[PATH_MAX
], *title
, *uri
;
5088 char *new_favs
, *tmp
;
5093 /* open favorites */
5094 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5096 if ((f
= fopen(file
, "r")) == NULL
) {
5097 show_oops(t
, "%s: can't open favorites: %s",
5098 __func__
, strerror(errno
));
5102 /* build a string which will become the new favroites file */
5103 new_favs
= g_strdup_printf("%s", "");
5106 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5107 if (feof(f
) || ferror(f
))
5109 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5116 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5117 if (feof(f
) || ferror(f
)) {
5118 show_oops(t
, "%s: can't parse favorites %s",
5119 __func__
, strerror(errno
));
5124 /* as long as this isn't the one we are deleting add to file */
5127 new_favs
= g_strdup_printf("%s%s\n%s\n",
5128 new_favs
, title
, uri
);
5140 /* write back new favorites file */
5141 if ((f
= fopen(file
, "w")) == NULL
) {
5142 show_oops(t
, "%s: can't open favorites: %s",
5143 __func__
, strerror(errno
));
5147 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5160 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5163 case XT_XTP_FL_LIST
:
5164 /* nothing, just the below call to xtp_page_fl() */
5166 case XT_XTP_FL_REMOVE
:
5167 remove_favorite(t
, arg
);
5170 show_oops(t
, "%s: invalid favorites command", __func__
);
5174 xtp_page_fl(t
, NULL
);
5178 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5181 case XT_XTP_CL_LIST
:
5182 /* nothing, just xtp_page_cl() */
5184 case XT_XTP_CL_REMOVE
:
5188 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5192 xtp_page_cl(t
, NULL
);
5195 /* link an XTP class to it's session key and handler function */
5196 struct xtp_despatch
{
5199 void (*handle_func
)(struct tab
*, uint8_t, int);
5202 struct xtp_despatch xtp_despatches
[] = {
5203 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5204 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5205 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5206 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5207 { NULL
, NULL
, NULL
}
5211 * is the url xtp protocol? (xxxt://)
5212 * if so, parse and despatch correct bahvior
5215 parse_xtp_url(struct tab
*t
, const char *url
)
5217 char *dup
= NULL
, *p
, *last
;
5218 uint8_t n_tokens
= 0;
5219 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5220 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5224 * tokens array meaning:
5226 * tokens[1] = session key
5227 * tokens[2] = action
5228 * tokens[3] = optional argument
5231 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5233 /*xtp tab meaning is normal unless proven special */
5234 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5236 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5239 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5241 /* split out the url */
5242 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5243 (p
= strtok_r(NULL
, "/", &last
))) {
5245 tokens
[n_tokens
++] = p
;
5248 /* should be atleast three fields 'class/seskey/command/arg' */
5252 dsp
= xtp_despatches
;
5253 req_class
= atoi(tokens
[0]);
5254 while (dsp
->xtp_class
!= NULL
) {
5255 if (dsp
->xtp_class
== req_class
) {
5262 /* did we find one atall? */
5263 if (dsp_match
== NULL
) {
5264 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5268 /* check session key and call despatch function */
5269 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5270 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5283 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5285 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5287 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5290 show_oops_s("activate_uri_entry_cb invalid parameters");
5295 show_oops(t
, "activate_uri_entry_cb no uri");
5299 uri
+= strspn(uri
, "\t ");
5301 /* if xxxt:// treat specially */
5302 if (!parse_xtp_url(t
, uri
)) {
5303 load_uri(t
, (gchar
*)uri
);
5309 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5311 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5312 char *newuri
= NULL
;
5315 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5318 show_oops_s("activate_search_entry_cb invalid parameters");
5322 if (search_string
== NULL
) {
5323 show_oops(t
, "no search_string");
5327 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5328 newuri
= g_strdup_printf(search_string
, enc_search
);
5331 webkit_web_view_load_uri(t
->wv
, newuri
);
5339 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5341 struct domain
*d
= NULL
;
5344 if (uri
== NULL
|| t
== NULL
)
5347 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5352 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5353 es
? "enable" : "disable", uri
);
5355 g_object_set(G_OBJECT(t
->settings
),
5356 "enable-scripts", es
, (char *)NULL
);
5357 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5359 button_set_stockid(t
->js_toggle
,
5360 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5364 show_ca_status(struct tab
*t
, const char *uri
)
5366 WebKitWebFrame
*frame
;
5367 WebKitWebDataSource
*source
;
5368 WebKitNetworkRequest
*request
;
5369 SoupMessage
*message
;
5371 gchar
*col_str
= "white";
5374 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5375 ssl_strict_certs
, ssl_ca_file
, uri
);
5379 if (ssl_ca_file
== NULL
) {
5380 if (g_str_has_prefix(uri
, "http://"))
5382 if (g_str_has_prefix(uri
, "https://")) {
5388 if (g_str_has_prefix(uri
, "http://") ||
5389 !g_str_has_prefix(uri
, "https://"))
5392 frame
= webkit_web_view_get_main_frame(t
->wv
);
5393 source
= webkit_web_frame_get_data_source(frame
);
5394 request
= webkit_web_data_source_get_request(source
);
5395 message
= webkit_network_request_get_message(request
);
5397 if (message
&& (soup_message_get_flags(message
) &
5398 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5402 r
= load_compare_cert(t
, NULL
);
5404 col_str
= "lightblue";
5413 gdk_color_parse(col_str
, &color
);
5414 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5416 if (!strcmp(col_str
, "white")) {
5417 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5419 gdk_color_parse("black", &color
);
5420 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5423 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5425 gdk_color_parse("black", &color
);
5426 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5433 free_favicon(struct tab
*t
)
5435 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p pix %p\n",
5436 __func__
, t
->icon_download
, t
->icon_request
, t
->icon_pixbuf
);
5438 if (t
->icon_request
)
5439 g_object_unref(t
->icon_request
);
5441 g_object_unref(t
->icon_pixbuf
);
5442 if (t
->icon_dest_uri
)
5443 g_free(t
->icon_dest_uri
);
5445 t
->icon_pixbuf
= NULL
;
5446 t
->icon_request
= NULL
;
5447 t
->icon_dest_uri
= NULL
;
5451 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5453 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5454 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5456 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5457 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5459 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5460 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5464 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pixbuf
)
5466 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5467 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5469 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5470 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5472 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5473 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5477 is_valid_icon(char *file
)
5480 const char *mime_type
;
5484 gf
= g_file_new_for_path(file
);
5485 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5487 mime_type
= g_file_info_get_content_type(fi
);
5488 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5489 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5490 g_strcmp0(mime_type
, "image/png") == 0 ||
5491 g_strcmp0(mime_type
, "image/gif") == 0 ||
5492 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5500 set_favicon_from_file(struct tab
*t
, char *file
)
5503 GdkPixbuf
*pixbuf
, *scaled
;
5506 if (t
== NULL
|| file
== NULL
)
5508 if (t
->icon_pixbuf
) {
5509 DNPRINTF(XT_D_DOWNLOAD
, "%s: icon already set\n", __func__
);
5513 if (g_str_has_prefix(file
, "file://"))
5514 file
+= strlen("file://");
5515 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5517 if (!stat(file
, &sb
)) {
5518 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5519 /* corrupt icon so trash it */
5520 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5523 /* no need to set icon to default here */
5528 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
5529 if (pixbuf
== NULL
) {
5530 xt_icon_from_name(t
, "text-html");
5534 g_object_get(pixbuf
, "width", &width
, "height", &height
,
5536 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
5537 __func__
, t
->tab_id
, width
, height
);
5539 if (width
> 16 || height
> 16) {
5540 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5541 GDK_INTERP_BILINEAR
);
5542 g_object_unref(pixbuf
);
5546 if (scaled
== NULL
) {
5547 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5548 GDK_INTERP_BILINEAR
);
5552 t
->icon_pixbuf
= scaled
;
5553 xt_icon_from_pixbuf(t
, t
->icon_pixbuf
);
5557 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5560 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5561 struct tab
*tt
= NULL
, *t
= NULL
;
5564 * find the webview instead of passing in the tab as it could have been
5565 * deleted from underneath us.
5567 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5576 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5577 __func__
, t
->tab_id
, status
);
5580 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5582 t
->icon_download
= NULL
;
5585 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5588 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5591 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5593 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5594 __func__
, t
->tab_id
);
5595 t
->icon_download
= NULL
;
5598 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5601 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5602 __func__
, t
->icon_dest_uri
);
5603 set_favicon_from_file(t
, t
->icon_dest_uri
);
5604 /* these will be freed post callback */
5605 t
->icon_request
= NULL
;
5606 t
->icon_download
= NULL
;
5614 abort_favicon_download(struct tab
*t
)
5616 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5618 if (t
->icon_download
) {
5619 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5620 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5621 webkit_download_cancel(t
->icon_download
);
5622 t
->icon_download
= NULL
;
5626 xt_icon_from_name(t
, "text-html");
5630 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5632 gchar
*name_hash
, file
[PATH_MAX
];
5635 DNPRINTF(XT_D_DOWNLOAD
, "notify_icon_loaded_cb %s\n", uri
);
5637 if (uri
== NULL
|| t
== NULL
)
5640 if (t
->icon_request
) {
5641 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5645 /* check to see if we got the icon in cache */
5646 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5647 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5650 if (!stat(file
, &sb
)) {
5651 if (sb
.st_size
> 0) {
5652 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5654 set_favicon_from_file(t
, file
);
5658 /* corrupt icon so trash it */
5659 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5664 /* create download for icon */
5665 t
->icon_request
= webkit_network_request_new(uri
);
5666 if (t
->icon_request
== NULL
) {
5667 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5672 t
->icon_download
= webkit_download_new(t
->icon_request
);
5673 if (t
->icon_download
== NULL
) {
5674 fprintf(stderr
, "%s: icon_download", __func__
);
5678 /* we have to free icon_dest_uri later */
5679 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5680 webkit_download_set_destination_uri(t
->icon_download
,
5683 if (webkit_download_get_status(t
->icon_download
) ==
5684 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5685 fprintf(stderr
, "%s: download failed to start", __func__
);
5686 g_object_unref(t
->icon_request
);
5687 g_free(t
->icon_dest_uri
);
5688 t
->icon_request
= NULL
;
5689 t
->icon_dest_uri
= NULL
;
5693 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5694 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5696 webkit_download_start(t
->icon_download
);
5700 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5702 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5703 struct history
*h
, find
;
5704 const gchar
*s_loading
;
5707 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d\n",
5708 webkit_web_view_get_load_status(wview
));
5711 show_oops_s("notify_load_status_cb invalid paramters");
5715 switch (webkit_web_view_get_load_status(wview
)) {
5716 case WEBKIT_LOAD_PROVISIONAL
:
5718 abort_favicon_download(t
);
5719 #if GTK_CHECK_VERSION(2, 20, 0)
5720 gtk_widget_show(t
->spinner
);
5721 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5723 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5725 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5731 case WEBKIT_LOAD_COMMITTED
:
5733 if ((uri
= get_uri(wview
)) != NULL
) {
5734 if (strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
5735 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5741 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
5744 /* check if js white listing is enabled */
5745 if (enable_js_whitelist
) {
5746 uri
= get_uri(wview
);
5747 check_and_set_js(uri
, t
);
5750 show_ca_status(t
, uri
);
5752 /* we know enough to autosave the session */
5753 if (session_autosave
) {
5759 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
5763 case WEBKIT_LOAD_FINISHED
:
5765 uri
= get_uri(wview
);
5767 if (!strncmp(uri
, "http://", strlen("http://")) ||
5768 !strncmp(uri
, "https://", strlen("https://")) ||
5769 !strncmp(uri
, "file://", strlen("file://"))) {
5771 h
= RB_FIND(history_list
, &hl
, &find
);
5773 title
= webkit_web_view_get_title(wview
);
5774 set
= title
? title
: uri
;
5775 h
= g_malloc(sizeof *h
);
5776 h
->uri
= g_strdup(uri
);
5777 h
->title
= g_strdup(set
);
5778 RB_INSERT(history_list
, &hl
, h
);
5779 completion_add_uri(h
->uri
);
5780 update_history_tabs(NULL
);
5784 set_status(t
, (char *)uri
, XT_STATUS_URI
);
5785 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5786 case WEBKIT_LOAD_FAILED
:
5789 #if GTK_CHECK_VERSION(2, 20, 0)
5790 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5791 gtk_widget_hide(t
->spinner
);
5793 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
5794 if (s_loading
&& !strcmp(s_loading
, "Loading"))
5795 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5797 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
5802 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
5804 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
5805 webkit_web_view_can_go_back(wview
));
5807 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
5808 webkit_web_view_can_go_forward(wview
));
5810 /* take focus if we are visible */
5815 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5817 const gchar
*set
= NULL
, *title
= NULL
;
5819 title
= webkit_web_view_get_title(wview
);
5820 set
= title
? title
: get_uri(wview
);
5821 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
5822 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
5826 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5828 run_script(t
, JS_HINTING
);
5832 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
5834 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
5835 progress
== 100 ? 0 : (double)progress
/ 100);
5836 if (show_url
== 0) {
5837 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
5838 progress
== 100 ? 0 : (double)progress
/ 100);
5843 webview_nw_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5844 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5845 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5850 show_oops_s("webview_nw_cb invalid paramters");
5854 DNPRINTF(XT_D_NAV
, "webview_nw_cb: %s\n",
5855 webkit_network_request_get_uri(request
));
5857 /* open in current tab */
5858 uri
= (char *)webkit_network_request_get_uri(request
);
5859 webkit_web_view_load_uri(t
->wv
, uri
);
5860 webkit_web_policy_decision_ignore(pd
);
5862 return (TRUE
); /* we made the decission */
5866 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5867 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5868 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5873 show_oops_s("webview_npd_cb invalid parameters");
5877 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
5879 webkit_network_request_get_uri(request
));
5881 uri
= (char *)webkit_network_request_get_uri(request
);
5883 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
5885 create_new_tab(uri
, NULL
, ctrl_click_focus
);
5886 webkit_web_policy_decision_ignore(pd
);
5887 return (TRUE
); /* we made the decission */
5890 webkit_web_policy_decision_use(pd
);
5891 return (TRUE
); /* we made the decission */
5895 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5898 struct domain
*d
= NULL
;
5900 WebKitWebView
*webview
= NULL
;
5902 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
5903 webkit_web_view_get_uri(wv
));
5905 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
5906 uri
= webkit_web_view_get_uri(wv
);
5907 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5910 tt
= create_new_tab(NULL
, NULL
, 1);
5912 } else if (enable_scripts
== 1) {
5913 tt
= create_new_tab(NULL
, NULL
, 1);
5921 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
5924 struct domain
*d
= NULL
;
5926 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
5928 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
5929 uri
= webkit_web_view_get_uri(wv
);
5930 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5934 } else if (enable_scripts
== 1)
5941 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
5943 /* we can not eat the event without throwing gtk off so defer it */
5945 /* catch middle click */
5946 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
5951 /* catch ctrl click */
5952 if (e
->type
== GDK_BUTTON_RELEASE
&&
5953 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
5958 return (XT_CB_PASSTHROUGH
);
5962 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
5964 struct mime_type
*m
;
5966 m
= find_mime_type(mime_type
);
5972 show_oops(t
, "can't fork mime handler");
5981 execlp(m
->mt_action
, m
->mt_action
,
5982 webkit_network_request_get_uri(request
), (void *)NULL
);
5991 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
5992 WebKitNetworkRequest
*request
, char *mime_type
,
5993 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
5996 show_oops_s("webview_mimetype_cb invalid parameters");
6000 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6001 t
->tab_id
, mime_type
);
6003 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6004 webkit_web_policy_decision_ignore(decision
);
6009 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6010 webkit_web_policy_decision_download(decision
);
6018 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6021 const gchar
*filename
;
6023 struct download
*download_entry
;
6026 if (wk_download
== NULL
|| t
== NULL
) {
6027 show_oops_s("%s invalid parameters", __func__
);
6031 filename
= webkit_download_get_suggested_filename(wk_download
);
6032 if (filename
== NULL
)
6033 return (FALSE
); /* abort download */
6035 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
6037 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6038 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6040 webkit_download_set_destination_uri(wk_download
, uri
);
6042 if (webkit_download_get_status(wk_download
) ==
6043 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6044 show_oops(t
, "%s: download failed to start", __func__
);
6046 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6048 download_entry
= g_malloc(sizeof(struct download
));
6049 download_entry
->download
= wk_download
;
6050 download_entry
->tab
= t
;
6051 download_entry
->id
= next_download_id
++;
6052 RB_INSERT(download_list
, &downloads
, download_entry
);
6053 /* get from history */
6054 g_object_ref(wk_download
);
6055 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6061 /* sync other download manager tabs */
6062 update_download_tabs(NULL
);
6065 * NOTE: never redirect/render the current tab before this
6066 * function returns. This will cause the download to never start.
6068 return (ret
); /* start download */
6072 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6074 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6077 show_oops_s("webview_hover_cb");
6082 set_status(t
, uri
, XT_STATUS_LINK
);
6085 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6090 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6092 char s
[2], buf
[128];
6093 const char *errstr
= NULL
;
6096 /* don't use w directly; use t->whatever instead */
6099 show_oops_s("wv_keypress_after_cb");
6100 return (XT_CB_PASSTHROUGH
);
6103 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6104 e
->keyval
, e
->state
, t
);
6108 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6110 return (XT_CB_HANDLED
);
6114 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6115 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6117 /* we have a string */
6119 /* we have a number */
6120 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6128 /* XXX unfuck this */
6129 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6130 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6131 /* last input was numerical */
6133 l
= strlen(t
->hint_num
);
6140 t
->hint_num
[l
] = '\0';
6144 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6145 /* last input was alphanumerical */
6147 l
= strlen(t
->hint_buf
);
6154 t
->hint_buf
[l
] = '\0';
6164 /* numerical input */
6165 if (CLEAN(e
->state
) == 0 &&
6166 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6167 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6168 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6169 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6172 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6174 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6177 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6179 t
->hint_mode
= XT_HINT_NUMERICAL
;
6183 /* empty the counter buffer */
6184 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6185 return (XT_CB_HANDLED
);
6188 /* alphanumerical input */
6190 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6191 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6192 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6193 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6194 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6195 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6196 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6199 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6202 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6204 t
->hint_mode
= XT_HINT_ALPHANUM
;
6207 /* empty the counter buffer */
6208 bzero(t
->hint_num
, sizeof t
->hint_num
);
6209 return (XT_CB_HANDLED
);
6212 return (XT_CB_HANDLED
);
6215 struct key_binding
*k
;
6216 TAILQ_FOREACH(k
, &kbl
, entry
)
6217 if (e
->keyval
== k
->key
) {
6219 if ((e
->state
& (CTRL
| MOD1
)) == 0) {
6220 k
->func(t
, &k
->arg
);
6221 return (XT_CB_HANDLED
);
6224 else if ((e
->state
& k
->mask
) == k
->mask
) {
6225 k
->func(t
, &k
->arg
);
6226 return (XT_CB_HANDLED
);
6230 return (XT_CB_PASSTHROUGH
);
6234 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6238 return (XT_CB_PASSTHROUGH
);
6242 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6244 const gchar
*c
= gtk_entry_get_text(w
);
6248 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6249 e
->keyval
, e
->state
, t
);
6252 show_oops_s("cmd_keyrelease_cb invalid parameters");
6253 return (XT_CB_PASSTHROUGH
);
6256 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6257 e
->keyval
, e
->state
, t
);
6261 if (strlen(c
) == 1) {
6262 webkit_web_view_unmark_text_matches(t
->wv
);
6268 else if (c
[0] == '?')
6274 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6276 /* not found, mark red */
6277 gdk_color_parse("red", &color
);
6278 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6279 /* unmark and remove selection */
6280 webkit_web_view_unmark_text_matches(t
->wv
);
6281 /* my kingdom for a way to unselect text in webview */
6283 /* found, highlight all */
6284 webkit_web_view_unmark_text_matches(t
->wv
);
6285 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6286 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6287 gdk_color_parse("white", &color
);
6288 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6291 return (XT_CB_PASSTHROUGH
);
6296 cmd_complete(struct tab
*t
, char *s
)
6299 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6301 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
6303 for (i
= 0; i
< LENGTH(cmds
); i
++) {
6304 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
6305 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
6307 gtk_entry_set_text(w
, ":");
6308 gtk_entry_append_text(w
, cmds
[i
].cmd
);
6309 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6319 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6322 show_oops_s("entry_key_cb invalid parameters");
6323 return (XT_CB_PASSTHROUGH
);
6326 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6327 e
->keyval
, e
->state
, t
);
6331 if (e
->keyval
== GDK_Escape
) {
6332 /* don't use focus_webview(t) because we want to type :cmds */
6333 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6336 struct key_binding
*k
;
6337 TAILQ_FOREACH(k
, &kbl
, entry
)
6338 if (e
->keyval
== k
->key
&& k
->use_in_entry
) {
6340 if ((e
->state
& (CTRL
| MOD1
)) == 0) {
6341 k
->func(t
, &k
->arg
);
6342 return (XT_CB_HANDLED
);
6345 else if ((e
->state
& k
->mask
) == k
->mask
) {
6346 k
->func(t
, &k
->arg
);
6347 return (XT_CB_HANDLED
);
6351 return (XT_CB_PASSTHROUGH
);
6355 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6357 int rv
= XT_CB_HANDLED
;
6358 const gchar
*c
= gtk_entry_get_text(w
);
6361 show_oops_s("cmd_keypress_cb parameters");
6362 return (XT_CB_PASSTHROUGH
);
6365 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6366 e
->keyval
, e
->state
, t
);
6370 e
->keyval
= GDK_Escape
;
6371 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6372 e
->keyval
= GDK_Escape
;
6374 switch (e
->keyval
) {
6380 if (strchr (c
, ' ')) {
6381 /* par completion */
6382 fprintf(stderr
, "completeme par\n");
6386 cmd_complete(t
, (char *)&c
[1]);
6391 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
6399 if (c
[0] == '/' || c
[0] == '?')
6400 webkit_web_view_unmark_text_matches(t
->wv
);
6404 rv
= XT_CB_PASSTHROUGH
;
6410 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6413 show_oops_s("cmd_focusout_cb invalid parameters");
6414 return (XT_CB_PASSTHROUGH
);
6416 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
6421 if (show_url
== 0 || t
->focus_wv
)
6424 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6426 return (XT_CB_PASSTHROUGH
);
6430 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6434 const gchar
*c
= gtk_entry_get_text(entry
);
6437 show_oops_s("cmd_activate_cb invalid parameters");
6441 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
6446 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6452 if (c
[0] == '/' || c
[0] == '?') {
6453 if (t
->search_text
) {
6454 g_free(t
->search_text
);
6455 t
->search_text
= NULL
;
6458 t
->search_text
= g_strdup(s
);
6460 g_free(global_search
);
6461 global_search
= g_strdup(s
);
6462 t
->search_forward
= c
[0] == '/';
6467 for (i
= 0; i
< LENGTH(cmds
); i
++)
6468 if (cmds
[i
].params
) {
6469 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
6470 cmds
[i
].arg
.s
= g_strdup(s
);
6471 goto execute_command
;
6474 if (!strcmp(s
, cmds
[i
].cmd
))
6475 goto execute_command
;
6477 show_oops(t
, "Invalid command: %s", s
);
6484 cmds
[i
].func(t
, &cmds
[i
].arg
);
6487 backward_cb(GtkWidget
*w
, struct tab
*t
)
6492 show_oops_s("backward_cb invalid parameters");
6496 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
6503 forward_cb(GtkWidget
*w
, struct tab
*t
)
6508 show_oops_s("forward_cb invalid parameters");
6512 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
6514 a
.i
= XT_NAV_FORWARD
;
6519 stop_cb(GtkWidget
*w
, struct tab
*t
)
6521 WebKitWebFrame
*frame
;
6524 show_oops_s("stop_cb invalid parameters");
6528 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
6530 frame
= webkit_web_view_get_main_frame(t
->wv
);
6531 if (frame
== NULL
) {
6532 show_oops(t
, "stop_cb: no frame");
6536 webkit_web_frame_stop_loading(frame
);
6537 abort_favicon_download(t
);
6541 setup_webkit(struct tab
*t
)
6543 g_object_set(G_OBJECT(t
->settings
),
6544 "user-agent", t
->user_agent
, (char *)NULL
);
6545 g_object_set(G_OBJECT(t
->settings
),
6546 "enable-scripts", enable_scripts
, (char *)NULL
);
6547 g_object_set(G_OBJECT(t
->settings
),
6548 "enable-plugins", enable_plugins
, (char *)NULL
);
6549 g_object_set(G_OBJECT(t
->wv
),
6550 "full-content-zoom", TRUE
, (char *)NULL
);
6551 adjustfont_webkit(t
, XT_FONT_SET
);
6553 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6557 create_browser(struct tab
*t
)
6563 show_oops_s("create_browser invalid parameters");
6567 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
6568 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
6569 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
6570 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
6572 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
6573 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
6574 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
6576 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
6577 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
6580 t
->settings
= webkit_web_settings_new();
6582 if (user_agent
== NULL
) {
6583 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
6585 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
6588 t
->user_agent
= g_strdup(user_agent
);
6601 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
6602 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
6603 gtk_widget_set_name(w
, "xxxterm");
6604 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
6605 g_signal_connect(G_OBJECT(w
), "delete_event",
6606 G_CALLBACK (gtk_main_quit
), NULL
);
6612 create_toolbar(struct tab
*t
)
6614 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
6616 b
= gtk_hbox_new(FALSE
, 0);
6618 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
6621 /* backward button */
6622 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
6623 gtk_widget_set_sensitive(t
->backward
, FALSE
);
6624 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
6625 G_CALLBACK(backward_cb
), t
);
6626 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
6628 /* forward button */
6629 t
->forward
= create_button("GoForward",GTK_STOCK_GO_FORWARD
, 0);
6630 gtk_widget_set_sensitive(t
->forward
, FALSE
);
6631 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
6632 G_CALLBACK(forward_cb
), t
);
6633 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
6637 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
6638 gtk_widget_set_sensitive(t
->stop
, FALSE
);
6639 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
6640 G_CALLBACK(stop_cb
), t
);
6641 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
6645 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
6646 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
6647 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
6648 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
6649 G_CALLBACK(js_toggle_cb
), t
);
6650 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
6653 t
->uri_entry
= gtk_entry_new();
6654 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
6655 G_CALLBACK(activate_uri_entry_cb
), t
);
6656 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
6657 G_CALLBACK(entry_key_cb
), t
);
6659 eb1
= gtk_hbox_new(FALSE
, 0);
6660 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
6661 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
6662 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
6665 if (fancy_bar
&& search_string
) {
6667 t
->search_entry
= gtk_entry_new();
6668 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
6669 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
6670 G_CALLBACK(activate_search_entry_cb
), t
);
6671 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
6672 G_CALLBACK(entry_key_cb
), t
);
6673 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
6674 eb2
= gtk_hbox_new(FALSE
, 0);
6675 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
6676 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
6678 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
6688 TAILQ_FOREACH(t
, &tabs
, entry
)
6689 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
6693 undo_close_tab_save(struct tab
*t
)
6697 struct undo
*u1
, *u2
;
6699 WebKitWebHistoryItem
*item
;
6701 if ((uri
= get_uri(t
->wv
)) == NULL
)
6704 u1
= g_malloc0(sizeof(struct undo
));
6705 u1
->uri
= g_strdup(uri
);
6707 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6709 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
6710 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
6713 /* forward history */
6714 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
6718 u1
->history
= g_list_prepend(u1
->history
,
6719 webkit_web_history_item_copy(item
));
6720 items
= g_list_next(items
);
6725 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
6726 u1
->history
= g_list_prepend(u1
->history
,
6727 webkit_web_history_item_copy(item
));
6731 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
6735 u1
->history
= g_list_prepend(u1
->history
,
6736 webkit_web_history_item_copy(item
));
6737 items
= g_list_next(items
);
6740 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
6742 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
6743 u2
= TAILQ_LAST(&undos
, undo_tailq
);
6744 TAILQ_REMOVE(&undos
, u2
, entry
);
6746 g_list_free(u2
->history
);
6755 delete_tab(struct tab
*t
)
6759 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
6764 TAILQ_REMOVE(&tabs
, t
, entry
);
6766 /* halt all webkit activity */
6767 abort_favicon_download(t
);
6768 webkit_web_view_stop_loading(t
->wv
);
6769 undo_close_tab_save(t
);
6771 gtk_widget_destroy(t
->vbox
);
6772 g_free(t
->user_agent
);
6776 if (TAILQ_EMPTY(&tabs
))
6777 create_new_tab(NULL
, NULL
, 1);
6780 /* recreate session */
6781 if (session_autosave
) {
6788 adjustfont_webkit(struct tab
*t
, int adjust
)
6792 show_oops_s("adjustfont_webkit invalid parameters");
6796 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
6797 if (adjust
== XT_FONT_SET
) {
6798 t
->font_size
= default_font_size
;
6799 zoom
= default_zoom_level
;
6800 t
->font_size
+= adjust
;
6801 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
6802 t
->font_size
, (char *)NULL
);
6803 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
6804 &t
->font_size
, (char *)NULL
);
6806 t
->font_size
+= adjust
;
6807 zoom
+= adjust
/25.0;
6812 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
6813 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
6817 append_tab(struct tab
*t
)
6822 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
6823 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
6827 create_new_tab(char *title
, struct undo
*u
, int focus
)
6830 int load
= 1, id
, notfound
;
6832 WebKitWebHistoryItem
*item
;
6836 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
6838 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
6839 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
6843 t
= g_malloc0(sizeof *t
);
6845 if (title
== NULL
) {
6846 title
= "(untitled)";
6850 t
->vbox
= gtk_vbox_new(FALSE
, 0);
6852 /* label + button for tab */
6853 b
= gtk_hbox_new(FALSE
, 0);
6856 #if GTK_CHECK_VERSION(2, 20, 0)
6857 t
->spinner
= gtk_spinner_new ();
6859 t
->label
= gtk_label_new(title
);
6860 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
6861 gtk_widget_set_size_request(t
->label
, 100, 0);
6862 gtk_widget_set_size_request(b
, 130, 0);
6864 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
6865 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
6866 #if GTK_CHECK_VERSION(2, 20, 0)
6867 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
6871 t
->toolbar
= create_toolbar(t
);
6872 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
6875 t
->browser_win
= create_browser(t
);
6876 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
6878 /* oops message for user feedback */
6879 t
->oops
= gtk_entry_new();
6880 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
6881 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
6882 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
6883 gdk_color_parse("red", &color
);
6884 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
6885 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
6888 t
->cmd
= gtk_entry_new();
6889 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
6890 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
6891 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
6894 t
->statusbar
= gtk_entry_new();
6895 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
6896 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
6897 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
6898 gdk_color_parse("black", &color
);
6899 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6900 gdk_color_parse("white", &color
);
6901 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6902 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
6904 /* xtp meaning is normal by default */
6905 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6907 /* set empty favicon */
6908 xt_icon_from_name(t
, "text-html");
6910 /* and show it all */
6911 gtk_widget_show_all(b
);
6912 gtk_widget_show_all(t
->vbox
);
6914 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
6918 id
= gtk_notebook_get_current_page(notebook
);
6919 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6920 if (tt
->tab_id
== id
) {
6922 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
6923 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
6933 #if GTK_CHECK_VERSION(2, 20, 0)
6934 /* turn spinner off if we are a new tab without uri */
6936 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6937 gtk_widget_hide(t
->spinner
);
6940 /* make notebook tabs reorderable */
6941 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
6943 g_object_connect(G_OBJECT(t
->cmd
),
6944 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
6945 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
6946 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
6947 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
6950 /* reuse wv_button_cb to hide oops */
6951 g_object_connect(G_OBJECT(t
->oops
),
6952 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
6955 g_object_connect(G_OBJECT(t
->wv
),
6956 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
6957 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
6958 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
6959 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
6960 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
6961 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
6962 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_nw_cb
), t
,
6963 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
6964 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
6965 "signal::event", G_CALLBACK(webview_event_cb
), t
,
6966 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
6967 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
6968 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6969 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
6971 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
6973 g_signal_connect(t
->wv
,
6974 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
6975 g_signal_connect(t
->wv
,
6976 "notify::title", G_CALLBACK(notify_title_cb
), t
);
6978 /* hijack the unused keys as if we were the browser */
6979 g_object_connect(G_OBJECT(t
->toolbar
),
6980 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
6983 g_signal_connect(G_OBJECT(bb
), "button_press_event",
6984 G_CALLBACK(tab_close_cb
), t
);
6989 url_set_visibility();
6990 statusbar_set_visibility();
6993 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
6994 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
6999 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7003 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7008 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7009 /* restore the tab's history */
7010 if (u
&& u
->history
) {
7014 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7015 items
= g_list_next(items
);
7018 item
= g_list_nth_data(u
->history
, u
->back
);
7020 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7023 g_list_free(u
->history
);
7025 webkit_web_back_forward_list_clear(t
->bfl
);
7031 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
7037 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7039 TAILQ_FOREACH(t
, &tabs
, entry
) {
7040 if (t
->tab_id
== pn
) {
7041 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7044 uri
= webkit_web_view_get_title(t
->wv
);
7047 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7059 menuitem_response(struct tab
*t
)
7061 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7065 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
7067 GtkWidget
*menu
, *menu_items
;
7068 GdkEventButton
*bevent
;
7072 if (event
->type
== GDK_BUTTON_PRESS
) {
7073 bevent
= (GdkEventButton
*) event
;
7074 menu
= gtk_menu_new();
7076 TAILQ_FOREACH(ti
, &tabs
, entry
) {
7077 if ((uri
= get_uri(ti
->wv
)) == NULL
)
7078 /* XXX make sure there is something to print */
7079 /* XXX add gui pages in here to look purdy */
7081 menu_items
= gtk_menu_item_new_with_label(uri
);
7082 gtk_menu_append(GTK_MENU (menu
), menu_items
);
7083 gtk_widget_show(menu_items
);
7085 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
7086 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
7090 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
7091 bevent
->button
, bevent
->time
);
7093 /* unref object so it'll free itself when popped down */
7094 g_object_ref_sink(menu
);
7095 g_object_unref(menu
);
7097 return (TRUE
/* eat event */);
7100 return (FALSE
/* propagate */);
7104 icon_size_map(int icon_size
)
7106 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
7107 icon_size
> GTK_ICON_SIZE_DIALOG
)
7108 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7114 create_button(char *name
, char *stockid
, int size
)
7116 GtkWidget
*button
, *image
;
7119 rcstring
= g_strdup_printf(
7120 "style \"%s-style\"\n"
7122 " GtkWidget::focus-padding = 0\n"
7123 " GtkWidget::focus-line-width = 0\n"
7127 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
7128 gtk_rc_parse_string(rcstring
);
7130 button
= gtk_button_new();
7131 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
7132 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
7134 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
7135 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7136 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
7137 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
7138 gtk_widget_set_name(button
, name
);
7139 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
7140 gtk_widget_set_tooltip_text(button
, name
);
7146 button_set_stockid(GtkWidget
*button
, char *stockid
)
7149 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
7150 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7151 gtk_button_set_image(GTK_BUTTON(button
), image
);
7160 char file
[PATH_MAX
];
7163 vbox
= gtk_vbox_new(FALSE
, 0);
7164 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
7165 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
7166 gtk_notebook_set_tab_hborder(notebook
, 0);
7167 gtk_notebook_set_tab_vborder(notebook
, 0);
7168 gtk_notebook_set_scrollable(notebook
, TRUE
);
7169 notebook_tab_set_visibility(notebook
);
7170 gtk_notebook_set_show_border(notebook
, FALSE
);
7171 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
7173 abtn
= gtk_button_new();
7174 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
7175 gtk_widget_set_size_request(arrow
, -1, -1);
7176 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
7177 gtk_widget_set_size_request(abtn
, -1, 20);
7178 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
7180 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
7181 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
7182 gtk_widget_set_size_request(vbox
, -1, -1);
7184 g_object_connect(G_OBJECT(notebook
),
7185 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
7187 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
7188 G_CALLBACK(arrow_cb
), NULL
);
7190 main_window
= create_window();
7191 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
7192 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
7195 for (i
= 0; i
< LENGTH(icons
); i
++) {
7196 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
7197 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
7198 l
= g_list_append(l
, pb
);
7200 gtk_window_set_default_icon_list(l
);
7202 gtk_widget_show_all(abtn
);
7203 gtk_widget_show_all(main_window
);
7207 set_hook(void **hook
, char *name
)
7210 errx(1, "set_hook");
7212 if (*hook
== NULL
) {
7213 *hook
= dlsym(RTLD_NEXT
, name
);
7215 errx(1, "can't hook %s", name
);
7219 /* override libsoup soup_cookie_equal because it doesn't look at domain */
7221 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
7223 g_return_val_if_fail(cookie1
, FALSE
);
7224 g_return_val_if_fail(cookie2
, FALSE
);
7226 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
7227 !strcmp (cookie1
->value
, cookie2
->value
) &&
7228 !strcmp (cookie1
->path
, cookie2
->path
) &&
7229 !strcmp (cookie1
->domain
, cookie2
->domain
));
7233 transfer_cookies(void)
7236 SoupCookie
*sc
, *pc
;
7238 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7240 for (;cf
; cf
= cf
->next
) {
7242 sc
= soup_cookie_copy(pc
);
7243 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
7246 soup_cookies_free(cf
);
7250 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
7255 print_cookie("soup_cookie_jar_delete_cookie", c
);
7257 if (cookies_enabled
== 0)
7260 if (jar
== NULL
|| c
== NULL
)
7263 /* find and remove from persistent jar */
7264 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7266 for (;cf
; cf
= cf
->next
) {
7268 if (soup_cookie_equal(ci
, c
)) {
7269 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
7274 soup_cookies_free(cf
);
7276 /* delete from session jar */
7277 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
7281 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
7283 struct domain
*d
= NULL
;
7287 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
7288 jar
, p_cookiejar
, s_cookiejar
);
7290 if (cookies_enabled
== 0)
7293 /* see if we are up and running */
7294 if (p_cookiejar
== NULL
) {
7295 _soup_cookie_jar_add_cookie(jar
, cookie
);
7298 /* disallow p_cookiejar adds, shouldn't happen */
7299 if (jar
== p_cookiejar
)
7302 if (enable_cookie_whitelist
&&
7303 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
7305 DNPRINTF(XT_D_COOKIE
,
7306 "soup_cookie_jar_add_cookie: reject %s\n",
7308 if (save_rejected_cookies
) {
7309 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
7310 show_oops_s("can't open reject cookie file");
7313 fseek(r_cookie_f
, 0, SEEK_END
);
7314 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
7315 cookie
->http_only
? "#HttpOnly_" : "",
7317 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
7319 cookie
->secure
? "TRUE" : "FALSE",
7321 (gulong
)soup_date_to_time_t(cookie
->expires
) :
7328 if (!allow_volatile_cookies
)
7332 if (cookie
->expires
== NULL
&& session_timeout
) {
7333 soup_cookie_set_expires(cookie
,
7334 soup_date_new_from_now(session_timeout
));
7335 print_cookie("modified add cookie", cookie
);
7338 /* see if we are white listed for persistence */
7339 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
7340 /* add to persistent jar */
7341 c
= soup_cookie_copy(cookie
);
7342 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
7343 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
7346 /* add to session jar */
7347 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
7348 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
7354 char file
[PATH_MAX
];
7356 set_hook((void *)&_soup_cookie_jar_add_cookie
,
7357 "soup_cookie_jar_add_cookie");
7358 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
7359 "soup_cookie_jar_delete_cookie");
7361 if (cookies_enabled
== 0)
7365 * the following code is intricate due to overriding several libsoup
7367 * do not alter order of these operations.
7370 /* rejected cookies */
7371 if (save_rejected_cookies
)
7372 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
7374 /* persistent cookies */
7375 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
7376 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
7378 /* session cookies */
7379 s_cookiejar
= soup_cookie_jar_new();
7380 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
7381 cookie_policy
, (void *)NULL
);
7384 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
7388 setup_proxy(char *uri
)
7391 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
7392 soup_uri_free(proxy_uri
);
7396 if (http_proxy
!= uri
) {
7403 http_proxy
= g_strdup(uri
);
7404 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
7405 proxy_uri
= soup_uri_new(http_proxy
);
7406 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
7411 send_url_to_socket(char *url
)
7413 int s
, len
, rv
= -1;
7414 struct sockaddr_un sa
;
7416 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7417 warnx("send_url_to_socket: socket");
7421 sa
.sun_family
= AF_UNIX
;
7422 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7423 work_dir
, XT_SOCKET_FILE
);
7426 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7427 warnx("send_url_to_socket: connect");
7431 if (send(s
, url
, strlen(url
) + 1, 0) == -1) {
7432 warnx("send_url_to_socket: send");
7441 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
7444 char str
[XT_MAX_URL_LENGTH
];
7445 socklen_t t
= sizeof(struct sockaddr_un
);
7446 struct sockaddr_un sa
;
7451 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
7452 warn("socket_watcher: accept");
7456 if (getpeereid(s
, &uid
, &gid
) == -1) {
7457 warn("socket_watcher: getpeereid");
7460 if (uid
!= getuid() || gid
!= getgid()) {
7461 warnx("socket_watcher: unauthorized user");
7467 warnx("socket_watcher: not a valid user");
7471 n
= recv(s
, str
, sizeof(str
), 0);
7475 create_new_tab(str
, NULL
, 1);
7482 struct sockaddr_un sa
;
7484 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7485 warn("is_running: socket");
7489 sa
.sun_family
= AF_UNIX
;
7490 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7491 work_dir
, XT_SOCKET_FILE
);
7494 /* connect to see if there is a listener */
7495 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
7496 rv
= 0; /* not running */
7498 rv
= 1; /* already running */
7509 struct sockaddr_un sa
;
7511 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7512 warn("build_socket: socket");
7516 sa
.sun_family
= AF_UNIX
;
7517 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7518 work_dir
, XT_SOCKET_FILE
);
7521 /* connect to see if there is a listener */
7522 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7523 /* no listener so we will */
7524 unlink(sa
.sun_path
);
7526 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7527 warn("build_socket: bind");
7531 if (listen(s
, 1) == -1) {
7532 warn("build_socket: listen");
7545 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
7546 GtkTreeIter
*iter
, struct tab
*t
)
7550 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
7557 completion_add_uri(const gchar
*uri
)
7561 /* add uri to list_store */
7562 gtk_list_store_append(completion_model
, &iter
);
7563 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
7567 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
7568 GtkTreeIter
*iter
, gpointer user_data
)
7570 gchar
*value
, *voffset
;
7572 gboolean match
= FALSE
;
7576 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
7582 if (!strncmp(key
, value
, len
))
7585 voffset
= strstr(value
, "/") + 2;
7586 if (!strncmp(key
, voffset
, len
))
7588 else if (g_str_has_prefix(voffset
, "www.")) {
7589 voffset
= voffset
+ strlen("www.");
7590 if (!strncmp(key
, voffset
, len
))
7600 completion_add(struct tab
*t
)
7602 /* enable completion for tab */
7603 t
->completion
= gtk_entry_completion_new();
7604 gtk_entry_completion_set_text_column(t
->completion
, 0);
7605 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
7606 gtk_entry_completion_set_model(t
->completion
,
7607 GTK_TREE_MODEL(completion_model
));
7608 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
7610 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
7611 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
7612 G_CALLBACK(completion_select_cb
), t
);
7619 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
7624 main(int argc
, char *argv
[])
7627 int c
, s
, optn
= 0, focus
= 1;
7628 char conf
[PATH_MAX
] = { '\0' };
7629 char file
[PATH_MAX
];
7630 char *env_proxy
= NULL
;
7633 struct sigaction sact
;
7637 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
7639 while ((c
= getopt(argc
, argv
, "STVf:s:tn")) != -1) {
7648 errx(0 , "Version: %s", version
);
7651 strlcpy(conf
, optarg
, sizeof(conf
));
7654 strlcpy(named_session
, optarg
, sizeof(named_session
));
7672 RB_INIT(&downloads
);
7676 TAILQ_INIT(&aliases
);
7682 gnutls_global_init();
7684 /* generate session keys for xtp pages */
7685 generate_xtp_session_key(&dl_session_key
);
7686 generate_xtp_session_key(&hl_session_key
);
7687 generate_xtp_session_key(&cl_session_key
);
7688 generate_xtp_session_key(&fl_session_key
);
7691 gtk_init(&argc
, &argv
);
7692 if (!g_thread_supported())
7693 g_thread_init(NULL
);
7696 bzero(&sact
, sizeof(sact
));
7697 sigemptyset(&sact
.sa_mask
);
7698 sact
.sa_handler
= sigchild
;
7699 sact
.sa_flags
= SA_NOCLDSTOP
;
7700 sigaction(SIGCHLD
, &sact
, NULL
);
7702 /* set download dir */
7703 pwd
= getpwuid(getuid());
7705 errx(1, "invalid user %d", getuid());
7706 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
7708 /* set default string settings */
7709 home
= g_strdup("http://www.peereboom.us");
7710 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
7711 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
7712 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
7714 /* read config file */
7715 if (strlen(conf
) == 0)
7716 snprintf(conf
, sizeof conf
, "%s/.%s",
7717 pwd
->pw_dir
, XT_CONF_FILE
);
7718 config_parse(conf
, 0);
7720 /* working directory */
7721 if (strlen(work_dir
) == 0)
7722 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
7723 pwd
->pw_dir
, XT_DIR
);
7724 if (stat(work_dir
, &sb
)) {
7725 if (mkdir(work_dir
, S_IRWXU
) == -1)
7726 err(1, "mkdir work_dir");
7727 if (stat(work_dir
, &sb
))
7728 err(1, "stat work_dir");
7730 if (S_ISDIR(sb
.st_mode
) == 0)
7731 errx(1, "%s not a dir", work_dir
);
7732 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7733 warnx("fixing invalid permissions on %s", work_dir
);
7734 if (chmod(work_dir
, S_IRWXU
) == -1)
7738 /* icon cache dir */
7739 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
7740 if (stat(cache_dir
, &sb
)) {
7741 if (mkdir(cache_dir
, S_IRWXU
) == -1)
7742 err(1, "mkdir cache_dir");
7743 if (stat(cache_dir
, &sb
))
7744 err(1, "stat cache_dir");
7746 if (S_ISDIR(sb
.st_mode
) == 0)
7747 errx(1, "%s not a dir", cache_dir
);
7748 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7749 warnx("fixing invalid permissions on %s", cache_dir
);
7750 if (chmod(cache_dir
, S_IRWXU
) == -1)
7755 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
7756 if (stat(certs_dir
, &sb
)) {
7757 if (mkdir(certs_dir
, S_IRWXU
) == -1)
7758 err(1, "mkdir certs_dir");
7759 if (stat(certs_dir
, &sb
))
7760 err(1, "stat certs_dir");
7762 if (S_ISDIR(sb
.st_mode
) == 0)
7763 errx(1, "%s not a dir", certs_dir
);
7764 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7765 warnx("fixing invalid permissions on %s", certs_dir
);
7766 if (chmod(certs_dir
, S_IRWXU
) == -1)
7771 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
7772 work_dir
, XT_SESSIONS_DIR
);
7773 if (stat(sessions_dir
, &sb
)) {
7774 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
7775 err(1, "mkdir sessions_dir");
7776 if (stat(sessions_dir
, &sb
))
7777 err(1, "stat sessions_dir");
7779 if (S_ISDIR(sb
.st_mode
) == 0)
7780 errx(1, "%s not a dir", sessions_dir
);
7781 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7782 warnx("fixing invalid permissions on %s", sessions_dir
);
7783 if (chmod(sessions_dir
, S_IRWXU
) == -1)
7786 /* runtime settings that can override config file */
7787 if (runtime_settings
[0] != '\0')
7788 config_parse(runtime_settings
, 1);
7791 if (!strcmp(download_dir
, pwd
->pw_dir
))
7792 strlcat(download_dir
, "/downloads", sizeof download_dir
);
7793 if (stat(download_dir
, &sb
)) {
7794 if (mkdir(download_dir
, S_IRWXU
) == -1)
7795 err(1, "mkdir download_dir");
7796 if (stat(download_dir
, &sb
))
7797 err(1, "stat download_dir");
7799 if (S_ISDIR(sb
.st_mode
) == 0)
7800 errx(1, "%s not a dir", download_dir
);
7801 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7802 warnx("fixing invalid permissions on %s", download_dir
);
7803 if (chmod(download_dir
, S_IRWXU
) == -1)
7807 /* favorites file */
7808 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
7809 if (stat(file
, &sb
)) {
7810 warnx("favorites file doesn't exist, creating it");
7811 if ((f
= fopen(file
, "w")) == NULL
)
7812 err(1, "favorites");
7817 session
= webkit_get_default_session();
7822 if (stat(ssl_ca_file
, &sb
)) {
7823 warn("no CA file: %s", ssl_ca_file
);
7824 g_free(ssl_ca_file
);
7827 g_object_set(session
,
7828 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
7829 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
7834 env_proxy
= getenv("http_proxy");
7836 setup_proxy(env_proxy
);
7838 setup_proxy(http_proxy
);
7840 /* see if there is already an xxxterm running */
7841 if (single_instance
&& is_running()) {
7843 warnx("already running");
7848 send_url_to_socket(argv
[0]);
7856 /* uri completion */
7857 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
7862 if (save_global_history
)
7863 restore_global_history();
7865 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
7866 restore_saved_tabs();
7868 a
.s
= named_session
;
7869 a
.i
= XT_SES_DONOTHING
;
7870 open_tabs(NULL
, &a
);
7874 create_new_tab(argv
[0], NULL
, focus
);
7881 if (TAILQ_EMPTY(&tabs
))
7882 create_new_tab(home
, NULL
, 1);
7885 if ((s
= build_socket()) != -1)
7886 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
7890 gnutls_global_deinit();