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 * multi letter commands
24 * pre and post counts for commands
25 * autocompletion on various inputs
26 * create privacy browsing
27 * - encrypted local data
43 #include <sys/types.h>
45 #if defined(__linux__)
46 #include "linux/util.h"
47 #include "linux/tree.h"
48 #elif defined(__FreeBSD__)
50 #include "freebsd/util.h"
56 #include <sys/queue.h>
58 #include <sys/socket.h>
62 #include <gdk/gdkkeysyms.h>
63 #include <webkit/webkit.h>
64 #include <libsoup/soup.h>
65 #include <gnutls/gnutls.h>
66 #include <JavaScriptCore/JavaScript.h>
67 #include <gnutls/x509.h>
69 #include "javascript.h"
72 javascript.h borrowed from vimprobable2 under the following license:
74 Copyright (c) 2009 Leon Winter
75 Copyright (c) 2009 Hannes Schueller
76 Copyright (c) 2009 Matto Fransen
78 Permission is hereby granted, free of charge, to any person obtaining a copy
79 of this software and associated documentation files (the "Software"), to deal
80 in the Software without restriction, including without limitation the rights
81 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
82 copies of the Software, and to permit persons to whom the Software is
83 furnished to do so, subject to the following conditions:
85 The above copyright notice and this permission notice shall be included in
86 all copies or substantial portions of the Software.
88 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
89 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
90 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
91 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
92 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
93 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
97 static char *version
= "$xxxterm$";
99 /* hooked functions */
100 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
101 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
106 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
107 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
108 #define XT_D_MOVE 0x0001
109 #define XT_D_KEY 0x0002
110 #define XT_D_TAB 0x0004
111 #define XT_D_URL 0x0008
112 #define XT_D_CMD 0x0010
113 #define XT_D_NAV 0x0020
114 #define XT_D_DOWNLOAD 0x0040
115 #define XT_D_CONFIG 0x0080
116 #define XT_D_JS 0x0100
117 #define XT_D_FAVORITE 0x0200
118 #define XT_D_PRINTING 0x0400
119 #define XT_D_COOKIE 0x0800
120 #define XT_D_KEYBINDING 0x1000
121 u_int32_t swm_debug
= 0
137 #define DPRINTF(x...)
138 #define DNPRINTF(n,x...)
141 #define LENGTH(x) (sizeof x / sizeof x[0])
142 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
143 ~(GDK_BUTTON1_MASK) & \
144 ~(GDK_BUTTON2_MASK) & \
145 ~(GDK_BUTTON3_MASK) & \
146 ~(GDK_BUTTON4_MASK) & \
158 TAILQ_ENTRY(tab
) entry
;
160 GtkWidget
*tab_content
;
163 GtkWidget
*uri_entry
;
164 GtkWidget
*search_entry
;
166 GtkWidget
*browser_win
;
167 GtkWidget
*statusbar
;
173 GtkWidget
*js_toggle
;
174 GtkEntryCompletion
*completion
;
178 WebKitWebHistoryItem
*item
;
179 WebKitWebBackForwardList
*bfl
;
182 WebKitNetworkRequest
*icon_request
;
183 WebKitDownload
*icon_download
;
184 GdkPixbuf
*icon_pixbuf
;
185 gchar
*icon_dest_uri
;
187 /* adjustments for browser */
190 GtkAdjustment
*adjust_h
;
191 GtkAdjustment
*adjust_v
;
197 int xtp_meaning
; /* identifies dls/favorites */
202 #define XT_HINT_NONE (0)
203 #define XT_HINT_NUMERICAL (1)
204 #define XT_HINT_ALPHANUM (2)
208 /* custom stylesheet */
217 WebKitWebSettings
*settings
;
221 TAILQ_HEAD(tab_list
, tab
);
224 RB_ENTRY(history
) entry
;
228 RB_HEAD(history_list
, history
);
231 RB_ENTRY(download
) entry
;
233 WebKitDownload
*download
;
236 RB_HEAD(download_list
, download
);
239 RB_ENTRY(domain
) entry
;
241 int handy
; /* app use */
243 RB_HEAD(domain_list
, domain
);
246 TAILQ_ENTRY(undo
) entry
;
249 int back
; /* Keeps track of how many back
250 * history items there are. */
252 TAILQ_HEAD(undo_tailq
, undo
);
254 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
255 int next_download_id
= 1;
263 #define XT_NAME ("XXXTerm")
264 #define XT_DIR (".xxxterm")
265 #define XT_CACHE_DIR ("cache")
266 #define XT_CERT_DIR ("certs/")
267 #define XT_SESSIONS_DIR ("sessions/")
268 #define XT_CONF_FILE ("xxxterm.conf")
269 #define XT_FAVS_FILE ("favorites")
270 #define XT_SAVED_TABS_FILE ("main_session")
271 #define XT_RESTART_TABS_FILE ("restart_tabs")
272 #define XT_SOCKET_FILE ("socket")
273 #define XT_HISTORY_FILE ("history")
274 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
275 #define XT_CB_HANDLED (TRUE)
276 #define XT_CB_PASSTHROUGH (FALSE)
277 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
278 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
279 #define XT_DLMAN_REFRESH "10"
280 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
281 "td {overflow: hidden;" \
282 " padding: 2px 2px 2px 2px;" \
283 " border: 1px solid black}\n" \
284 "tr:hover {background: #ffff99 ;}\n" \
285 "th {background-color: #cccccc;" \
286 " border: 1px solid black}" \
287 "table {border-spacing: 0; " \
289 " border: 1px black solid;}\n" \
291 " border: 1px solid black;" \
297 " background: green;}" \
299 " font-size: small;" \
300 " text-align: center;}" \
302 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
303 #define XT_MAX_UNDO_CLOSE_TAB (32)
304 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
305 #define XT_PRINT_EXTRA_MARGIN 10
308 #define SZ_KB ((uint64_t) 1024)
309 #define SZ_MB (SZ_KB * SZ_KB)
310 #define SZ_GB (SZ_KB * SZ_KB * SZ_KB)
311 #define SZ_TB (SZ_KB * SZ_KB * SZ_KB * SZ_KB)
314 * xxxterm "protocol" (xtp)
315 * We use this for managing stuff like downloads and favorites. They
316 * make magical HTML pages in memory which have xxxt:// links in order
317 * to communicate with xxxterm's internals. These links take the format:
318 * xxxt://class/session_key/action/arg
320 * Don't begin xtp class/actions as 0. atoi returns that on error.
322 * Typically we have not put addition of items in this framework, as
323 * adding items is either done via an ex-command or via a keybinding instead.
326 #define XT_XTP_STR "xxxt://"
328 /* XTP classes (xxxt://<class>) */
329 #define XT_XTP_DL 1 /* downloads */
330 #define XT_XTP_HL 2 /* history */
331 #define XT_XTP_CL 3 /* cookies */
332 #define XT_XTP_FL 4 /* favorites */
334 /* XTP download actions */
335 #define XT_XTP_DL_LIST 1
336 #define XT_XTP_DL_CANCEL 2
337 #define XT_XTP_DL_REMOVE 3
339 /* XTP history actions */
340 #define XT_XTP_HL_LIST 1
341 #define XT_XTP_HL_REMOVE 2
343 /* XTP cookie actions */
344 #define XT_XTP_CL_LIST 1
345 #define XT_XTP_CL_REMOVE 2
347 /* XTP cookie actions */
348 #define XT_XTP_FL_LIST 1
349 #define XT_XTP_FL_REMOVE 2
351 /* xtp tab meanings - identifies which tabs have xtp pages in */
352 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
353 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
354 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
355 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
356 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
359 #define XT_MOVE_INVALID (0)
360 #define XT_MOVE_DOWN (1)
361 #define XT_MOVE_UP (2)
362 #define XT_MOVE_BOTTOM (3)
363 #define XT_MOVE_TOP (4)
364 #define XT_MOVE_PAGEDOWN (5)
365 #define XT_MOVE_PAGEUP (6)
366 #define XT_MOVE_HALFDOWN (7)
367 #define XT_MOVE_HALFUP (8)
368 #define XT_MOVE_LEFT (9)
369 #define XT_MOVE_FARLEFT (10)
370 #define XT_MOVE_RIGHT (11)
371 #define XT_MOVE_FARRIGHT (12)
373 #define XT_TAB_LAST (-4)
374 #define XT_TAB_FIRST (-3)
375 #define XT_TAB_PREV (-2)
376 #define XT_TAB_NEXT (-1)
377 #define XT_TAB_INVALID (0)
378 #define XT_TAB_NEW (1)
379 #define XT_TAB_DELETE (2)
380 #define XT_TAB_DELQUIT (3)
381 #define XT_TAB_OPEN (4)
382 #define XT_TAB_UNDO_CLOSE (5)
383 #define XT_TAB_SHOW (6)
384 #define XT_TAB_HIDE (7)
386 #define XT_NAV_INVALID (0)
387 #define XT_NAV_BACK (1)
388 #define XT_NAV_FORWARD (2)
389 #define XT_NAV_RELOAD (3)
390 #define XT_NAV_RELOAD_CACHE (4)
392 #define XT_FOCUS_INVALID (0)
393 #define XT_FOCUS_URI (1)
394 #define XT_FOCUS_SEARCH (2)
396 #define XT_SEARCH_INVALID (0)
397 #define XT_SEARCH_NEXT (1)
398 #define XT_SEARCH_PREV (2)
400 #define XT_PASTE_CURRENT_TAB (0)
401 #define XT_PASTE_NEW_TAB (1)
403 #define XT_FONT_SET (0)
405 #define XT_URL_SHOW (1)
406 #define XT_URL_HIDE (2)
408 #define XT_STATUSBAR_SHOW (1)
409 #define XT_STATUSBAR_HIDE (2)
411 #define XT_WL_TOGGLE (1<<0)
412 #define XT_WL_ENABLE (1<<1)
413 #define XT_WL_DISABLE (1<<2)
414 #define XT_WL_FQDN (1<<3) /* default */
415 #define XT_WL_TOPLEVEL (1<<4)
417 #define XT_CMD_OPEN (0)
418 #define XT_CMD_OPEN_CURRENT (1)
419 #define XT_CMD_TABNEW (2)
420 #define XT_CMD_TABNEW_CURRENT (3)
422 #define XT_STATUS_NOTHING (0)
423 #define XT_STATUS_LINK (1)
424 #define XT_STATUS_URI (2)
425 #define XT_STATUS_LOADING (3)
427 #define XT_SES_DONOTHING (0)
428 #define XT_SES_CLOSETABS (1)
430 #define XT_COOKIE_NORMAL (0)
431 #define XT_COOKIE_WHITELIST (1)
438 TAILQ_ENTRY(mime_type
) entry
;
440 TAILQ_HEAD(mime_type_list
, mime_type
);
446 TAILQ_ENTRY(alias
) entry
;
448 TAILQ_HEAD(alias_list
, alias
);
450 /* settings that require restart */
451 int tabless
= 0; /* allow only 1 tab */
452 int enable_socket
= 0;
453 int single_instance
= 0; /* only allow one xxxterm to run */
454 int fancy_bar
= 1; /* fancy toolbar */
455 int browser_mode
= XT_COOKIE_NORMAL
;
457 /* runtime settings */
458 int show_tabs
= 1; /* show tabs on notebook */
459 int show_url
= 1; /* show url toolbar on notebook */
460 int show_statusbar
= 0; /* vimperator style status bar */
461 int ctrl_click_focus
= 0; /* ctrl click gets focus */
462 int cookies_enabled
= 1; /* enable cookies */
463 int read_only_cookies
= 0; /* enable to not write cookies */
464 int enable_scripts
= 1;
465 int enable_plugins
= 0;
466 int default_font_size
= 12;
467 gfloat default_zoom_level
= 1.0;
468 int window_height
= 768;
469 int window_width
= 1024;
470 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
471 unsigned refresh_interval
= 10; /* download refresh interval */
472 int enable_cookie_whitelist
= 0;
473 int enable_js_whitelist
= 0;
474 time_t session_timeout
= 3600; /* cookie session timeout */
475 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
476 char *ssl_ca_file
= NULL
;
477 char *resource_dir
= NULL
;
478 gboolean ssl_strict_certs
= FALSE
;
479 int append_next
= 1; /* append tab after current tab */
481 char *search_string
= NULL
;
482 char *http_proxy
= NULL
;
483 char download_dir
[PATH_MAX
];
484 char runtime_settings
[PATH_MAX
]; /* override of settings */
485 int allow_volatile_cookies
= 0;
486 int save_global_history
= 0; /* save global history to disk */
487 char *user_agent
= NULL
;
488 int save_rejected_cookies
= 0;
489 time_t session_autosave
= 0;
490 int guess_search
= 0;
494 int set_download_dir(struct settings
*, char *);
495 int set_work_dir(struct settings
*, char *);
496 int set_runtime_dir(struct settings
*, char *);
497 int set_browser_mode(struct settings
*, char *);
498 int set_cookie_policy(struct settings
*, char *);
499 int add_alias(struct settings
*, char *);
500 int add_mime_type(struct settings
*, char *);
501 int add_cookie_wl(struct settings
*, char *);
502 int add_js_wl(struct settings
*, char *);
503 int add_kb(struct settings
*, char *);
504 void button_set_stockid(GtkWidget
*, char *);
505 GtkWidget
* create_button(char *, char *, int);
507 char *get_browser_mode(struct settings
*);
508 char *get_cookie_policy(struct settings
*);
510 char *get_download_dir(struct settings
*);
511 char *get_work_dir(struct settings
*);
512 char *get_runtime_dir(struct settings
*);
514 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
515 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
516 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
517 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
518 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
521 int (*set
)(struct settings
*, char *);
522 char *(*get
)(struct settings
*);
523 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
526 struct special s_browser_mode
= {
532 struct special s_cookie
= {
538 struct special s_alias
= {
544 struct special s_mime
= {
550 struct special s_js
= {
556 struct special s_kb
= {
562 struct special s_cookie_wl
= {
568 struct special s_download_dir
= {
574 struct special s_work_dir
= {
583 #define XT_S_INVALID (0)
586 #define XT_S_FLOAT (3)
588 #define XT_SF_RESTART (1<<0)
589 #define XT_SF_RUNTIME (1<<1)
595 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
596 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
597 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
598 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
599 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
600 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
601 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
602 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
603 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
604 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
605 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
606 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
607 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
608 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
609 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
610 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
611 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
612 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
613 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
614 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
615 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
616 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
617 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
618 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
619 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
620 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
621 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
622 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
623 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
624 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
625 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
626 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
627 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
628 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
629 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
630 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
631 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
633 /* runtime settings */
634 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
635 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
636 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
637 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
638 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
641 int about(struct tab
*, struct karg
*);
642 int blank(struct tab
*, struct karg
*);
643 int cookie_show_wl(struct tab
*, struct karg
*);
644 int js_show_wl(struct tab
*, struct karg
*);
645 int help(struct tab
*, struct karg
*);
646 int set(struct tab
*, struct karg
*);
647 int stats(struct tab
*, struct karg
*);
648 int xtp_page_cl(struct tab
*, struct karg
*);
649 int xtp_page_dl(struct tab
*, struct karg
*);
650 int xtp_page_fl(struct tab
*, struct karg
*);
651 int xtp_page_hl(struct tab
*, struct karg
*);
653 #define XT_URI_ABOUT ("about:")
654 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
655 #define XT_URI_ABOUT_ABOUT ("about")
656 #define XT_URI_ABOUT_BLANK ("blank")
657 #define XT_URI_ABOUT_CERTS ("certs") /* XXX NOT YET */
658 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
659 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
660 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
661 #define XT_URI_ABOUT_FAVORITES ("favorites")
662 #define XT_URI_ABOUT_HELP ("help")
663 #define XT_URI_ABOUT_HISTORY ("history")
664 #define XT_URI_ABOUT_JSWL ("jswl")
665 #define XT_URI_ABOUT_SET ("set")
666 #define XT_URI_ABOUT_STATS ("stats")
670 int (*func
)(struct tab
*, struct karg
*);
672 { XT_URI_ABOUT_ABOUT
, about
},
673 { XT_URI_ABOUT_BLANK
, blank
},
674 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
675 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
676 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
677 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
678 { XT_URI_ABOUT_HELP
, help
},
679 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
680 { XT_URI_ABOUT_JSWL
, js_show_wl
},
681 { XT_URI_ABOUT_SET
, set
},
682 { XT_URI_ABOUT_STATS
, stats
},
686 extern char *__progname
;
689 GtkWidget
*main_window
;
690 GtkNotebook
*notebook
;
691 GtkWidget
*arrow
, *abtn
;
692 struct tab_list tabs
;
693 struct history_list hl
;
694 struct download_list downloads
;
695 struct domain_list c_wl
;
696 struct domain_list js_wl
;
697 struct undo_tailq undos
;
698 struct keybinding_list kbl
;
700 int updating_dl_tabs
= 0;
701 int updating_hl_tabs
= 0;
702 int updating_cl_tabs
= 0;
703 int updating_fl_tabs
= 0;
705 uint64_t blocked_cookies
= 0;
706 char named_session
[PATH_MAX
];
707 void update_favicon(struct tab
*);
708 int icon_size_map(int);
710 GtkListStore
*completion_model
;
711 void completion_add(struct tab
*);
712 void completion_add_uri(const gchar
*);
717 int saved_errno
, status
;
722 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
726 if (errno
!= ECHILD
) {
728 clog_warn("sigchild: waitpid:");
734 if (WIFEXITED(status
)) {
735 if (WEXITSTATUS(status
) != 0) {
737 clog_warnx("sigchild: child exit status: %d",
738 WEXITSTATUS(status));
743 clog_warnx("sigchild: child is terminated abnormally");
752 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
758 /* we set this to indicate we want to manually do navaction */
760 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
761 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
764 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, title
);
765 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
768 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
769 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
770 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
771 GTK_ENTRY_ICON_PRIMARY
, pb
);
772 gdk_pixbuf_unref(pb
);
777 set_status(struct tab
*t
, gchar
*s
, int status
)
785 case XT_STATUS_LOADING
:
786 type
= g_strdup_printf("Loading: %s", s
);
790 type
= g_strdup_printf("Link: %s", s
);
792 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
796 type
= g_strdup_printf("%s", s
);
798 t
->status
= g_strdup(type
);
802 t
->status
= g_strdup(s
);
804 case XT_STATUS_NOTHING
:
809 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
815 hide_oops(struct tab
*t
)
817 gtk_widget_hide(t
->oops
);
821 hide_cmd(struct tab
*t
)
823 gtk_widget_hide(t
->cmd
);
827 show_cmd(struct tab
*t
)
829 gtk_widget_hide(t
->oops
);
830 gtk_widget_show(t
->cmd
);
834 show_oops(struct tab
*t
, const char *fmt
, ...)
843 if (vasprintf(&msg
, fmt
, ap
) == -1)
844 errx(1, "show_oops failed");
847 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
848 gtk_widget_hide(t
->cmd
);
849 gtk_widget_show(t
->oops
);
852 /* XXX collapse with show_oops */
854 show_oops_s(const char *fmt
, ...)
858 struct tab
*ti
, *t
= NULL
;
863 TAILQ_FOREACH(ti
, &tabs
, entry
)
864 if (ti
->tab_id
== gtk_notebook_current_page(notebook
)) {
872 if (vasprintf(&msg
, fmt
, ap
) == -1)
873 errx(1, "show_oops_s failed");
876 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
877 gtk_widget_hide(t
->cmd
);
878 gtk_widget_show(t
->oops
);
882 get_as_string(struct settings
*s
)
893 warnx("get_as_string skip %s\n", s
->name
);
894 } else if (s
->type
== XT_S_INT
)
895 r
= g_strdup_printf("%d", *s
->ival
);
896 else if (s
->type
== XT_S_STR
)
897 r
= g_strdup(*s
->sval
);
898 else if (s
->type
== XT_S_FLOAT
)
899 r
= g_strdup_printf("%f", *s
->fval
);
901 r
= g_strdup_printf("INVALID TYPE");
907 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
912 for (i
= 0; i
< LENGTH(rs
); i
++) {
913 if (rs
[i
].s
&& rs
[i
].s
->walk
)
914 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
916 s
= get_as_string(&rs
[i
]);
917 cb(&rs
[i
], s
, cb_args
);
924 set_browser_mode(struct settings
*s
, char *val
)
926 if (!strcmp(val
, "whitelist")) {
927 browser_mode
= XT_COOKIE_WHITELIST
;
928 allow_volatile_cookies
= 0;
929 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
931 enable_cookie_whitelist
= 1;
932 read_only_cookies
= 0;
933 save_rejected_cookies
= 0;
934 session_timeout
= 3600;
936 enable_js_whitelist
= 1;
937 } else if (!strcmp(val
, "normal")) {
938 browser_mode
= XT_COOKIE_NORMAL
;
939 allow_volatile_cookies
= 0;
940 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
942 enable_cookie_whitelist
= 0;
943 read_only_cookies
= 0;
944 save_rejected_cookies
= 0;
945 session_timeout
= 3600;
947 enable_js_whitelist
= 0;
955 get_browser_mode(struct settings
*s
)
959 if (browser_mode
== XT_COOKIE_WHITELIST
)
960 r
= g_strdup("whitelist");
961 else if (browser_mode
== XT_COOKIE_NORMAL
)
962 r
= g_strdup("normal");
970 set_cookie_policy(struct settings
*s
, char *val
)
972 if (!strcmp(val
, "no3rdparty"))
973 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
974 else if (!strcmp(val
, "accept"))
975 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
976 else if (!strcmp(val
, "reject"))
977 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
985 get_cookie_policy(struct settings
*s
)
989 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
990 r
= g_strdup("no3rdparty");
991 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
992 r
= g_strdup("accept");
993 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
994 r
= g_strdup("reject");
1002 get_download_dir(struct settings
*s
)
1004 if (download_dir
[0] == '\0')
1006 return (g_strdup(download_dir
));
1010 set_download_dir(struct settings
*s
, char *val
)
1013 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1014 pwd
->pw_dir
, &val
[1]);
1016 strlcpy(download_dir
, val
, sizeof download_dir
);
1023 * We use these to prevent people putting xxxt:// URLs on
1024 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1026 #define XT_XTP_SES_KEY_SZ 8
1027 #define XT_XTP_SES_KEY_HEX_FMT \
1028 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1029 char *dl_session_key
; /* downloads */
1030 char *hl_session_key
; /* history list */
1031 char *cl_session_key
; /* cookie list */
1032 char *fl_session_key
; /* favorites list */
1034 char work_dir
[PATH_MAX
];
1035 char certs_dir
[PATH_MAX
];
1036 char cache_dir
[PATH_MAX
];
1037 char sessions_dir
[PATH_MAX
];
1038 char cookie_file
[PATH_MAX
];
1039 SoupURI
*proxy_uri
= NULL
;
1040 SoupSession
*session
;
1041 SoupCookieJar
*s_cookiejar
;
1042 SoupCookieJar
*p_cookiejar
;
1043 char rc_fname
[PATH_MAX
];
1045 struct mime_type_list mtl
;
1046 struct alias_list aliases
;
1049 struct tab
*create_new_tab(char *, struct undo
*, int);
1050 void delete_tab(struct tab
*);
1051 void adjustfont_webkit(struct tab
*, int);
1052 int run_script(struct tab
*, char *);
1053 int download_rb_cmp(struct download
*, struct download
*);
1056 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1058 return (strcmp(h1
->uri
, h2
->uri
));
1060 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1063 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1065 return (strcmp(d1
->d
, d2
->d
));
1067 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1070 get_work_dir(struct settings
*s
)
1072 if (work_dir
[0] == '\0')
1074 return (g_strdup(work_dir
));
1078 set_work_dir(struct settings
*s
, char *val
)
1081 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1082 pwd
->pw_dir
, &val
[1]);
1084 strlcpy(work_dir
, val
, sizeof work_dir
);
1090 * generate a session key to secure xtp commands.
1091 * pass in a ptr to the key in question and it will
1092 * be modified in place.
1095 generate_xtp_session_key(char **key
)
1097 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1103 /* make a new one */
1104 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1105 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1106 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1107 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1109 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1113 * validate a xtp session key.
1117 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1119 if (strcmp(trusted
, untrusted
) != 0) {
1120 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1129 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1131 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1133 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1135 struct valid_url_types
{
1146 valid_url_type(char *url
)
1150 for (i
= 0; i
< LENGTH(vut
); i
++)
1151 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1158 print_cookie(char *msg
, SoupCookie
*c
)
1164 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1165 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1166 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1167 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1168 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1169 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1170 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1171 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1172 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1173 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1177 walk_alias(struct settings
*s
,
1178 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1183 if (s
== NULL
|| cb
== NULL
) {
1184 show_oops_s("walk_alias invalid parameters");
1188 TAILQ_FOREACH(a
, &aliases
, entry
) {
1189 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1190 cb(s
, str
, cb_args
);
1196 match_alias(char *url_in
)
1200 char *url_out
= NULL
, *search
, *enc_arg
;
1202 search
= g_strdup(url_in
);
1204 if (strsep(&arg
, " \t") == NULL
) {
1205 show_oops_s("match_alias: NULL URL");
1209 TAILQ_FOREACH(a
, &aliases
, entry
) {
1210 if (!strcmp(search
, a
->a_name
))
1215 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1218 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1219 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1222 url_out
= g_strdup(a
->a_uri
);
1230 guess_url_type(char *url_in
)
1233 char *url_out
= NULL
, *enc_search
= NULL
;
1235 url_out
= match_alias(url_in
);
1236 if (url_out
!= NULL
)
1241 * If there is no dot nor slash in the string and it isn't a
1242 * path to a local file and doesn't resolves to an IP, assume
1243 * that the user wants to search for the string.
1246 if (strchr(url_in
, '.') == NULL
&&
1247 strchr(url_in
, '/') == NULL
&&
1248 stat(url_in
, &sb
) != 0 &&
1249 gethostbyname(url_in
) == NULL
) {
1251 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1252 url_out
= g_strdup_printf(search_string
, enc_search
);
1258 /* XXX not sure about this heuristic */
1259 if (stat(url_in
, &sb
) == 0)
1260 url_out
= g_strdup_printf("file://%s", url_in
);
1262 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1264 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1270 load_uri(struct tab
*t
, gchar
*uri
)
1273 gchar
*newuri
= NULL
;
1279 /* Strip leading spaces. */
1280 while(*uri
&& isspace(*uri
))
1283 if (strlen(uri
) == 0) {
1288 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1289 for (i
= 0; i
< LENGTH(about_list
); i
++)
1290 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1291 bzero(&args
, sizeof args
);
1292 about_list
[i
].func(t
, &args
);
1295 show_oops(t
, "invalid about page");
1299 if (valid_url_type(uri
)) {
1300 newuri
= guess_url_type(uri
);
1304 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1305 webkit_web_view_load_uri(t
->wv
, uri
);
1312 get_uri(WebKitWebView
*wv
)
1314 WebKitWebFrame
*frame
;
1317 frame
= webkit_web_view_get_main_frame(wv
);
1318 uri
= webkit_web_frame_get_uri(frame
);
1320 if (uri
&& strlen(uri
) > 0)
1327 add_alias(struct settings
*s
, char *line
)
1330 struct alias
*a
= NULL
;
1332 if (s
== NULL
|| line
== NULL
) {
1333 show_oops_s("add_alias invalid parameters");
1338 a
= g_malloc(sizeof(*a
));
1340 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1341 show_oops_s("add_alias: incomplete alias definition");
1344 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1345 show_oops_s("add_alias: invalid alias definition");
1349 a
->a_name
= g_strdup(alias
);
1350 a
->a_uri
= g_strdup(l
);
1352 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1354 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1364 add_mime_type(struct settings
*s
, char *line
)
1368 struct mime_type
*m
= NULL
;
1370 /* XXX this could be smarter */
1373 show_oops_s("add_mime_type invalid parameters");
1378 m
= g_malloc(sizeof(*m
));
1380 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1381 show_oops_s("add_mime_type: invalid mime_type");
1384 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1385 mime_type
[strlen(mime_type
) - 1] = '\0';
1390 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1391 show_oops_s("add_mime_type: invalid mime_type");
1395 m
->mt_type
= g_strdup(mime_type
);
1396 m
->mt_action
= g_strdup(l
);
1398 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1399 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1401 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1411 find_mime_type(char *mime_type
)
1413 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1415 TAILQ_FOREACH(m
, &mtl
, entry
) {
1416 if (m
->mt_default
&&
1417 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1420 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1433 walk_mime_type(struct settings
*s
,
1434 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1436 struct mime_type
*m
;
1439 if (s
== NULL
|| cb
== NULL
)
1440 show_oops_s("walk_mime_type invalid parameters");
1442 TAILQ_FOREACH(m
, &mtl
, entry
) {
1443 str
= g_strdup_printf("%s%s --> %s",
1445 m
->mt_default
? "*" : "",
1447 cb(s
, str
, cb_args
);
1453 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1458 if (str
== NULL
|| wl
== NULL
)
1460 if (strlen(str
) < 2)
1463 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1465 /* treat *.moo.com the same as .moo.com */
1466 if (str
[0] == '*' && str
[1] == '.')
1468 else if (str
[0] == '.')
1473 d
= g_malloc(sizeof *d
);
1475 d
->d
= g_strdup_printf(".%s", str
);
1477 d
->d
= g_strdup(str
);
1480 if (RB_INSERT(domain_list
, wl
, d
))
1483 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1494 add_cookie_wl(struct settings
*s
, char *entry
)
1496 wl_add(entry
, &c_wl
, 1);
1501 walk_cookie_wl(struct settings
*s
,
1502 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1506 if (s
== NULL
|| cb
== NULL
) {
1507 show_oops_s("walk_cookie_wl invalid parameters");
1511 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1512 cb(s
, d
->d
, cb_args
);
1516 walk_js_wl(struct settings
*s
,
1517 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1521 if (s
== NULL
|| cb
== NULL
) {
1522 show_oops_s("walk_js_wl invalid parameters");
1526 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1527 cb(s
, d
->d
, cb_args
);
1531 add_js_wl(struct settings
*s
, char *entry
)
1533 wl_add(entry
, &js_wl
, 1 /* persistent */);
1538 wl_find(const gchar
*search
, struct domain_list
*wl
)
1541 struct domain
*d
= NULL
, dfind
;
1544 if (search
== NULL
|| wl
== NULL
)
1546 if (strlen(search
) < 2)
1549 if (search
[0] != '.')
1550 s
= g_strdup_printf(".%s", search
);
1552 s
= g_strdup(search
);
1554 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1557 d
= RB_FIND(domain_list
, wl
, &dfind
);
1571 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1577 if (s
== NULL
|| wl
== NULL
)
1580 if (!strncmp(s
, "http://", strlen("http://")))
1581 s
= &s
[strlen("http://")];
1582 else if (!strncmp(s
, "https://", strlen("https://")))
1583 s
= &s
[strlen("https://")];
1588 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1589 /* chop string at first slash */
1590 if (s
[i
] == '/' || s
[i
] == '\0') {
1593 r
= wl_find(ss
, wl
);
1602 get_toplevel_domain(char *domain
)
1609 if (strlen(domain
) < 2)
1612 s
= &domain
[strlen(domain
) - 1];
1613 while (s
!= domain
) {
1629 settings_add(char *var
, char *val
)
1636 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1637 if (strcmp(var
, rs
[i
].name
))
1641 if (rs
[i
].s
->set(&rs
[i
], val
))
1642 errx(1, "invalid value for %s", var
);
1646 switch (rs
[i
].type
) {
1655 errx(1, "invalid sval for %s",
1669 errx(1, "invalid type for %s", var
);
1678 config_parse(char *filename
, int runtime
)
1681 char *line
, *cp
, *var
, *val
;
1682 size_t len
, lineno
= 0;
1684 char file
[PATH_MAX
];
1687 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1689 if (filename
== NULL
)
1692 if (runtime
&& runtime_settings
[0] != '\0') {
1693 snprintf(file
, sizeof file
, "%s/%s",
1694 work_dir
, runtime_settings
);
1695 if (stat(file
, &sb
)) {
1696 warnx("runtime file doesn't exist, creating it");
1697 if ((f
= fopen(file
, "w")) == NULL
)
1699 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1703 strlcpy(file
, filename
, sizeof file
);
1705 if ((config
= fopen(file
, "r")) == NULL
) {
1706 warn("config_parse: cannot open %s", filename
);
1711 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1712 if (feof(config
) || ferror(config
))
1716 cp
+= (long)strspn(cp
, WS
);
1717 if (cp
[0] == '\0') {
1723 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1724 errx(1, "invalid config file entry: %s", line
);
1726 cp
+= (long)strspn(cp
, WS
);
1728 if ((val
= strsep(&cp
, "\0")) == NULL
)
1731 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1732 handled
= settings_add(var
, val
);
1734 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1743 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1749 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1753 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1756 JSStringGetUTF8CString(jsref
, s
, l
);
1757 JSStringRelease(jsref
);
1763 disable_hints(struct tab
*t
)
1765 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1766 bzero(t
->hint_num
, sizeof t
->hint_num
);
1767 run_script(t
, "vimprobable_clear()");
1769 t
->hint_mode
= XT_HINT_NONE
;
1773 enable_hints(struct tab
*t
)
1775 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1776 run_script(t
, "vimprobable_show_hints()");
1778 t
->hint_mode
= XT_HINT_NONE
;
1781 #define XT_JS_OPEN ("open;")
1782 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1783 #define XT_JS_FIRE ("fire;")
1784 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1785 #define XT_JS_FOUND ("found;")
1786 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1789 run_script(struct tab
*t
, char *s
)
1791 JSGlobalContextRef ctx
;
1792 WebKitWebFrame
*frame
;
1794 JSValueRef val
, exception
;
1797 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1798 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1800 frame
= webkit_web_view_get_main_frame(t
->wv
);
1801 ctx
= webkit_web_frame_get_global_context(frame
);
1803 str
= JSStringCreateWithUTF8CString(s
);
1804 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1805 NULL
, 0, &exception
);
1806 JSStringRelease(str
);
1808 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1810 es
= js_ref_to_string(ctx
, exception
);
1811 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1815 es
= js_ref_to_string(ctx
, val
);
1816 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1818 /* handle return value right here */
1819 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1821 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1824 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1825 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1826 &es
[XT_JS_FIRE_LEN
]);
1831 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1832 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1843 hint(struct tab
*t
, struct karg
*args
)
1846 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1848 if (t
->hints_on
== 0)
1857 apply_style(struct tab
*t
)
1859 g_object_set(G_OBJECT(t
->settings
),
1860 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
1864 userstyle(struct tab
*t
, struct karg
*args
)
1866 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
1870 g_object_set(G_OBJECT(t
->settings
),
1871 "user-stylesheet-uri", NULL
, (char *)NULL
);
1880 * Doesn't work fully, due to the following bug:
1881 * https://bugs.webkit.org/show_bug.cgi?id=51747
1884 restore_global_history(void)
1886 char file
[PATH_MAX
];
1892 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1894 if ((f
= fopen(file
, "r")) == NULL
) {
1895 warnx("%s: fopen", __func__
);
1900 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1901 if (feof(f
) || ferror(f
))
1904 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1905 if (feof(f
) || ferror(f
)) {
1907 warnx("%s: broken history file\n", __func__
);
1911 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1912 webkit_web_history_item_new_with_data(uri
, title
);
1913 h
= g_malloc(sizeof(struct history
));
1914 h
->uri
= g_strdup(uri
);
1915 h
->title
= g_strdup(title
);
1916 RB_INSERT(history_list
, &hl
, h
);
1917 completion_add_uri(h
->uri
);
1919 warnx("%s: failed to restore history\n", __func__
);
1935 save_global_history_to_disk(struct tab
*t
)
1937 char file
[PATH_MAX
];
1941 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1943 if ((f
= fopen(file
, "w")) == NULL
) {
1944 show_oops(t
, "%s: global history file: %s",
1945 __func__
, strerror(errno
));
1949 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1950 if (h
->uri
&& h
->title
)
1951 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
1960 quit(struct tab
*t
, struct karg
*args
)
1962 if (save_global_history
)
1963 save_global_history_to_disk(t
);
1971 open_tabs(struct tab
*t
, struct karg
*a
)
1973 char file
[PATH_MAX
];
1977 struct tab
*ti
, *tt
;
1982 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1983 if ((f
= fopen(file
, "r")) == NULL
)
1986 ti
= TAILQ_LAST(&tabs
, tab_list
);
1989 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1990 if (feof(f
) || ferror(f
))
1993 /* retrieve session name */
1994 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
1995 strlcpy(named_session
,
1996 &uri
[strlen(XT_SAVE_SESSION_ID
)],
1997 sizeof named_session
);
2001 if (uri
&& strlen(uri
))
2002 create_new_tab(uri
, NULL
, 1);
2008 /* close open tabs */
2009 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2011 tt
= TAILQ_FIRST(&tabs
);
2030 restore_saved_tabs(void)
2032 char file
[PATH_MAX
];
2033 int unlink_file
= 0;
2038 snprintf(file
, sizeof file
, "%s/%s",
2039 sessions_dir
, XT_RESTART_TABS_FILE
);
2040 if (stat(file
, &sb
) == -1)
2041 a
.s
= XT_SAVED_TABS_FILE
;
2044 a
.s
= XT_RESTART_TABS_FILE
;
2047 a
.i
= XT_SES_DONOTHING
;
2048 rv
= open_tabs(NULL
, &a
);
2057 save_tabs(struct tab
*t
, struct karg
*a
)
2059 char file
[PATH_MAX
];
2064 const gchar
**arr
= NULL
;
2069 snprintf(file
, sizeof file
, "%s/%s",
2070 sessions_dir
, named_session
);
2072 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2074 if ((f
= fopen(file
, "w")) == NULL
) {
2075 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2079 /* save session name */
2080 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2082 /* save tabs, in the order they are arranged in the notebook */
2083 TAILQ_FOREACH(ti
, &tabs
, entry
)
2086 arr
= g_malloc0(len
* sizeof(gchar
*));
2088 TAILQ_FOREACH(ti
, &tabs
, entry
) {
2089 if ((uri
= get_uri(ti
->wv
)) != NULL
)
2090 arr
[gtk_notebook_page_num(notebook
, ti
->vbox
)] = uri
;
2093 for (i
= 0; i
< len
; i
++)
2095 fprintf(f
, "%s\n", arr
[i
]);
2104 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2116 yank_uri(struct tab
*t
, struct karg
*args
)
2119 GtkClipboard
*clipboard
;
2121 if ((uri
= get_uri(t
->wv
)) == NULL
)
2124 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2125 gtk_clipboard_set_text(clipboard
, uri
, -1);
2136 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
2138 struct paste_args
*pap
;
2140 if (data
== NULL
|| text
== NULL
|| !strlen(text
))
2143 pap
= (struct paste_args
*)data
;
2146 case XT_PASTE_CURRENT_TAB
:
2147 load_uri(pap
->t
, (gchar
*)text
);
2149 case XT_PASTE_NEW_TAB
:
2150 create_new_tab((gchar
*)text
, NULL
, 1);
2158 paste_uri(struct tab
*t
, struct karg
*args
)
2160 GtkClipboard
*clipboard
;
2161 struct paste_args
*pap
;
2163 pap
= g_malloc(sizeof(struct paste_args
));
2168 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2169 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
2175 find_domain(const gchar
*s
, int add_dot
)
2178 char *r
= NULL
, *ss
= NULL
;
2183 if (!strncmp(s
, "http://", strlen("http://")))
2184 s
= &s
[strlen("http://")];
2185 else if (!strncmp(s
, "https://", strlen("https://")))
2186 s
= &s
[strlen("https://")];
2192 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2193 /* chop string at first slash */
2194 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2197 r
= g_strdup_printf(".%s", ss
);
2208 toggle_cwl(struct tab
*t
, struct karg
*args
)
2212 char *dom
= NULL
, *dom_toggle
= NULL
;
2218 uri
= get_uri(t
->wv
);
2219 dom
= find_domain(uri
, 1);
2220 d
= wl_find(dom
, &c_wl
);
2227 if (args
->i
& XT_WL_TOGGLE
)
2229 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2231 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2234 if (args
->i
& XT_WL_TOPLEVEL
)
2235 dom_toggle
= get_toplevel_domain(dom
);
2240 /* enable cookies for domain */
2241 wl_add(dom_toggle
, &c_wl
, 0);
2243 /* disable cookies for domain */
2244 RB_REMOVE(domain_list
, &c_wl
, d
);
2246 webkit_web_view_reload(t
->wv
);
2253 toggle_js(struct tab
*t
, struct karg
*args
)
2258 char *dom
= NULL
, *dom_toggle
= NULL
;
2263 g_object_get(G_OBJECT(t
->settings
),
2264 "enable-scripts", &es
, (char *)NULL
);
2265 if (args
->i
& XT_WL_TOGGLE
)
2267 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2269 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2274 uri
= get_uri(t
->wv
);
2275 dom
= find_domain(uri
, 1);
2277 if (uri
== NULL
|| dom
== NULL
) {
2278 show_oops(t
, "Can't toggle domain in JavaScript white list");
2282 if (args
->i
& XT_WL_TOPLEVEL
)
2283 dom_toggle
= get_toplevel_domain(dom
);
2288 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2289 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2291 d
= wl_find(dom_toggle
, &js_wl
);
2293 RB_REMOVE(domain_list
, &js_wl
, d
);
2294 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2296 g_object_set(G_OBJECT(t
->settings
),
2297 "enable-scripts", es
, (char *)NULL
);
2298 g_object_set(G_OBJECT(t
->settings
),
2299 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2300 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2301 webkit_web_view_reload(t
->wv
);
2309 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2313 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
2318 toggle_src(struct tab
*t
, struct karg
*args
)
2325 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2326 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2327 webkit_web_view_reload(t
->wv
);
2333 focus_webview(struct tab
*t
)
2338 /* only grab focus if we are visible */
2339 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2340 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2344 focus(struct tab
*t
, struct karg
*args
)
2346 if (t
== NULL
|| args
== NULL
)
2352 if (args
->i
== XT_FOCUS_URI
)
2353 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2354 else if (args
->i
== XT_FOCUS_SEARCH
)
2355 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2361 stats(struct tab
*t
, struct karg
*args
)
2363 char *stats
, *s
, line
[64 * 1024];
2364 uint64_t line_count
= 0;
2368 show_oops_s("stats invalid parameters");
2371 if (save_rejected_cookies
) {
2372 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2374 s
= fgets(line
, sizeof line
, r_cookie_f
);
2375 if (s
== NULL
|| feof(r_cookie_f
) ||
2381 snprintf(line
, sizeof line
,
2382 "<br>Cookies blocked(*) total: %llu", line_count
);
2384 show_oops(t
, "Can't open blocked cookies file: %s",
2388 stats
= g_strdup_printf(XT_DOCTYPE
2391 "<title>Statistics</title>"
2393 "<h1>Statistics</h1>"
2395 "Cookies blocked(*) this session: %llu"
2397 "<p><small><b>*</b> results vary based on settings"
2403 load_webkit_string(t
, stats
, XT_URI_ABOUT_STATS
);
2410 blank(struct tab
*t
, struct karg
*args
)
2413 show_oops_s("about invalid parameters");
2415 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2420 about(struct tab
*t
, struct karg
*args
)
2425 show_oops_s("about invalid parameters");
2427 about
= g_strdup_printf(XT_DOCTYPE
2430 "<title>About</title>"
2434 "<b>Version: %s</b><p>"
2437 "<li>Marco Peereboom <marco@peereboom.us></li>"
2438 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2439 "<li>Edd Barrett <vext01@gmail.com> </li>"
2440 "<li>Todd T. Fries <todd@fries.net> </li>"
2442 "Copyrights and licenses can be found on the XXXterm "
2443 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
2449 load_webkit_string(t
, about
, XT_URI_ABOUT_ABOUT
);
2456 help(struct tab
*t
, struct karg
*args
)
2461 show_oops_s("help invalid parameters");
2466 "<title>XXXterm</title>"
2467 "<meta http-equiv=\"REFRESH\" content=\"0;"
2468 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2471 "XXXterm man page <a href=\"http://opensource.conformal.com/"
2472 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2473 "cgi-bin/man-cgi?xxxterm</a>"
2478 load_webkit_string(t
, help
, XT_URI_ABOUT_HELP
);
2484 * update all favorite tabs apart from one. Pass NULL if
2485 * you want to update all.
2488 update_favorite_tabs(struct tab
*apart_from
)
2491 if (!updating_fl_tabs
) {
2492 updating_fl_tabs
= 1; /* stop infinite recursion */
2493 TAILQ_FOREACH(t
, &tabs
, entry
)
2494 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2495 && (t
!= apart_from
))
2496 xtp_page_fl(t
, NULL
);
2497 updating_fl_tabs
= 0;
2501 /* show a list of favorites (bookmarks) */
2503 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2505 char file
[PATH_MAX
];
2507 char *uri
= NULL
, *title
= NULL
;
2508 size_t len
, lineno
= 0;
2510 char *header
, *body
, *tmp
, *html
= NULL
;
2512 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2515 warn("%s: bad param", __func__
);
2517 /* mark tab as favorite list */
2518 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2520 /* new session key */
2521 if (!updating_fl_tabs
)
2522 generate_xtp_session_key(&fl_session_key
);
2524 /* open favorites */
2525 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2526 if ((f
= fopen(file
, "r")) == NULL
) {
2527 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2532 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2533 "<title>Favorites</title>\n"
2536 "<h1>Favorites</h1>\n",
2540 body
= g_strdup_printf("<div align='center'><table><tr>"
2541 "<th style='width: 4%%'>#</th><th>Link</th>"
2542 "<th style='width: 15%%'>Remove</th></tr>\n");
2545 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2546 if (feof(f
) || ferror(f
))
2554 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2555 if (feof(f
) || ferror(f
)) {
2556 show_oops(t
, "favorites file corrupt");
2562 body
= g_strdup_printf("%s<tr>"
2564 "<td><a href='%s'>%s</a></td>"
2565 "<td style='text-align: center'>"
2566 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2568 body
, i
, uri
, title
,
2569 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2581 /* if none, say so */
2584 body
= g_strdup_printf("%s<tr>"
2585 "<td colspan='3' style='text-align: center'>"
2586 "No favorites - To add one use the 'favadd' command."
2587 "</td></tr>", body
);
2598 html
= g_strdup_printf("%s%s</table></div></html>",
2600 load_webkit_string(t
, html
, XT_URI_ABOUT_FAVORITES
);
2603 update_favorite_tabs(t
);
2616 getparams(char *cmd
, char *cmp
)
2621 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2622 rv
= cmd
+ strlen(cmp
);
2625 if (strlen(rv
) == 0)
2634 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2635 size_t cert_count
, char *title
)
2637 gnutls_datum_t cinfo
;
2638 char *tmp
, *header
, *body
, *footer
;
2641 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2642 footer
= g_strdup("</body></html>");
2643 body
= g_strdup("");
2645 for (i
= 0; i
< cert_count
; i
++) {
2646 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2651 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2652 body
, i
, cinfo
.data
);
2653 gnutls_free(cinfo
.data
);
2657 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2661 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2666 ca_cmd(struct tab
*t
, struct karg
*args
)
2669 int rv
= 1, certs
= 0, certs_read
;
2672 gnutls_x509_crt_t
*c
= NULL
;
2673 char *certs_buf
= NULL
, *s
;
2675 /* yeah yeah stat race */
2676 if (stat(ssl_ca_file
, &sb
)) {
2677 show_oops(t
, "no CA file: %s", ssl_ca_file
);
2681 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2682 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2686 certs_buf
= g_malloc(sb
.st_size
+ 1);
2687 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2688 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2691 certs_buf
[sb
.st_size
] = '\0';
2694 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2696 s
+= strlen("BEGIN CERTIFICATE");
2699 bzero(&dt
, sizeof dt
);
2700 dt
.data
= certs_buf
;
2701 dt
.size
= sb
.st_size
;
2702 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2703 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
,
2704 GNUTLS_X509_FMT_PEM
, 0);
2705 if (certs_read
<= 0) {
2706 show_oops(t
, "No cert(s) available");
2709 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2722 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2725 struct addrinfo hints
, *res
= NULL
, *ai
;
2729 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2732 su
= soup_uri_new(uri
);
2735 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2738 snprintf(port
, sizeof port
, "%d", su
->port
);
2739 bzero(&hints
, sizeof(struct addrinfo
));
2740 hints
.ai_flags
= AI_CANONNAME
;
2741 hints
.ai_family
= AF_UNSPEC
;
2742 hints
.ai_socktype
= SOCK_STREAM
;
2744 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2747 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2748 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2751 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2754 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2758 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2763 strlcpy(domain
, su
->host
, domain_sz
);
2774 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2777 gnutls_deinit(gsession
);
2779 gnutls_certificate_free_credentials(xcred
);
2785 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2786 gnutls_certificate_credentials_t
*xc
)
2788 gnutls_certificate_credentials_t xcred
;
2789 gnutls_session_t gsession
;
2792 if (gs
== NULL
|| xc
== NULL
)
2798 gnutls_certificate_allocate_credentials(&xcred
);
2799 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2800 GNUTLS_X509_FMT_PEM
);
2801 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2802 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2803 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2804 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2805 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2806 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2808 gnutls_error_is_fatal(rv
),
2809 gnutls_strerror_name(rv
));
2810 stop_tls(gsession
, xcred
);
2814 gnutls_credentials_type_t cred
;
2815 cred
= gnutls_auth_get_type(gsession
);
2816 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2817 stop_tls(gsession
, xcred
);
2829 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2833 const gnutls_datum_t
*cl
;
2834 gnutls_x509_crt_t
*all_certs
;
2837 if (certs
== NULL
|| cert_count
== NULL
)
2839 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2841 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2845 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2846 for (i
= 0; i
< len
; i
++) {
2847 gnutls_x509_crt_init(&all_certs
[i
]);
2848 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2849 GNUTLS_X509_FMT_PEM
< 0)) {
2863 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2867 for (i
= 0; i
< cert_count
; i
++)
2868 gnutls_x509_crt_deinit(certs
[i
]);
2873 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2874 size_t cert_count
, char *domain
)
2877 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2882 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2885 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2886 if ((f
= fopen(file
, "w")) == NULL
) {
2887 show_oops(t
, "Can't create cert file %s %s",
2888 file
, strerror(errno
));
2892 for (i
= 0; i
< cert_count
; i
++) {
2893 cert_buf_sz
= sizeof cert_buf
;
2894 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2895 cert_buf
, &cert_buf_sz
)) {
2896 show_oops(t
, "gnutls_x509_crt_export failed");
2899 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2900 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2905 /* not the best spot but oh well */
2906 gdk_color_parse("lightblue", &color
);
2907 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2908 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2909 gdk_color_parse("black", &color
);
2910 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2916 load_compare_cert(struct tab
*t
, struct karg
*args
)
2919 char domain
[8182], file
[PATH_MAX
];
2920 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2921 int s
= -1, rv
= 1, i
;
2925 gnutls_session_t gsession
;
2926 gnutls_x509_crt_t
*certs
;
2927 gnutls_certificate_credentials_t xcred
;
2932 if ((uri
= get_uri(t
->wv
)) == NULL
)
2935 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2939 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2940 show_oops(t
, "Start TLS failed");
2945 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2946 show_oops(t
, "Can't get connection certificates");
2950 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2951 if ((f
= fopen(file
, "r")) == NULL
)
2954 for (i
= 0; i
< cert_count
; i
++) {
2955 cert_buf_sz
= sizeof cert_buf
;
2956 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2957 cert_buf
, &cert_buf_sz
)) {
2960 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2961 rv
= -1; /* critical */
2964 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
2965 rv
= -1; /* critical */
2974 free_connection_certs(certs
, cert_count
);
2976 /* we close the socket first for speed */
2979 stop_tls(gsession
, xcred
);
2985 cert_cmd(struct tab
*t
, struct karg
*args
)
2988 char *action
, domain
[8182];
2991 gnutls_session_t gsession
;
2992 gnutls_x509_crt_t
*certs
;
2993 gnutls_certificate_credentials_t xcred
;
2998 if ((action
= getparams(args
->s
, "cert")))
3003 if ((uri
= get_uri(t
->wv
)) == NULL
) {
3004 show_oops(t
, "Invalid URI");
3008 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3009 show_oops(t
, "Invalid certidicate URI: %s", uri
);
3014 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3015 show_oops(t
, "Start TLS failed");
3020 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3021 show_oops(t
, "get_connection_certs failed");
3025 if (!strcmp(action
, "show"))
3026 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3027 else if (!strcmp(action
, "save"))
3028 save_certs(t
, certs
, cert_count
, domain
);
3030 show_oops(t
, "Invalid command: %s", action
);
3032 free_connection_certs(certs
, cert_count
);
3034 /* we close the socket first for speed */
3037 stop_tls(gsession
, xcred
);
3043 remove_cookie(int index
)
3049 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3051 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3053 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3057 print_cookie("remove cookie", c
);
3058 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3063 soup_cookies_free(cf
);
3069 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
3072 char *tmp
, *header
, *body
, *footer
;
3073 int p_js
= 0, s_js
= 0;
3075 /* we set this to indicate we want to manually do navaction */
3076 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3078 if (g_str_has_prefix(args
, "show a") ||
3079 !strcmp(args
, "show")) {
3083 } else if (g_str_has_prefix(args
, "show p")) {
3084 /* show persistent */
3086 } else if (g_str_has_prefix(args
, "show s")) {
3092 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
3094 footer
= g_strdup("</body></html>");
3095 body
= g_strdup("");
3100 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3102 RB_FOREACH(d
, domain_list
, wl
) {
3106 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3114 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3116 RB_FOREACH(d
, domain_list
, wl
) {
3120 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
3125 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3130 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3132 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3138 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3140 char file
[PATH_MAX
];
3142 char *line
= NULL
, *lt
= NULL
;
3145 char *dom
= NULL
, *dom_save
= NULL
;
3152 if (t
== NULL
|| args
== NULL
)
3155 if (runtime_settings
[0] == '\0')
3158 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3159 if ((f
= fopen(file
, "r+")) == NULL
)
3162 uri
= get_uri(t
->wv
);
3163 dom
= find_domain(uri
, 1);
3164 if (uri
== NULL
|| dom
== NULL
) {
3165 show_oops(t
, "Can't add domain to %s white list",
3166 js
? "JavaScript" : "cookie");
3170 if (g_str_has_prefix(args
->s
, "save d")) {
3172 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3173 show_oops(t
, "invalid domain: %s", dom
);
3176 flags
= XT_WL_TOPLEVEL
;
3177 } else if (g_str_has_prefix(args
->s
, "save f") ||
3178 !strcmp(args
->s
, "save")) {
3183 show_oops(t
, "invalid command: %s", args
->s
);
3187 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3190 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3193 if (!strcmp(line
, lt
))
3199 fprintf(f
, "%s\n", lt
);
3204 d
= wl_find(dom_save
, &js_wl
);
3206 settings_add("js_wl", dom_save
);
3207 d
= wl_find(dom_save
, &js_wl
);
3211 d
= wl_find(dom_save
, &c_wl
);
3213 settings_add("cookie_wl", dom_save
);
3214 d
= wl_find(dom_save
, &c_wl
);
3218 /* find and add to persistent jar */
3219 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3220 for (;cf
; cf
= cf
->next
) {
3222 if (!strcmp(dom_save
, ci
->domain
) ||
3223 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3224 c
= soup_cookie_copy(ci
);
3225 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3228 soup_cookies_free(cf
);
3246 js_show_wl(struct tab
*t
, struct karg
*args
)
3248 wl_show(t
, "show all", "JavaScript White List", &js_wl
);
3254 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3256 wl_show(t
, "show all", "Cookie White List", &c_wl
);
3262 cookie_cmd(struct tab
*t
, struct karg
*args
)
3267 if ((cmd
= getparams(args
->s
, "cookie")))
3273 if (g_str_has_prefix(cmd
, "show")) {
3274 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
3275 } else if (g_str_has_prefix(cmd
, "save")) {
3278 } else if (g_str_has_prefix(cmd
, "toggle")) {
3280 if (g_str_has_prefix(cmd
, "toggle d"))
3281 a
.i
|= XT_WL_TOPLEVEL
;
3285 } else if (g_str_has_prefix(cmd
, "delete")) {
3286 show_oops(t
, "'cookie delete' currently unimplemented");
3288 show_oops(t
, "unknown cookie command: %s", cmd
);
3294 js_cmd(struct tab
*t
, struct karg
*args
)
3299 if ((cmd
= getparams(args
->s
, "js")))
3304 if (g_str_has_prefix(cmd
, "show")) {
3305 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
3306 } else if (g_str_has_prefix(cmd
, "save")) {
3309 } else if (g_str_has_prefix(cmd
, "toggle")) {
3311 if (g_str_has_prefix(cmd
, "toggle d"))
3312 a
.i
|= XT_WL_TOPLEVEL
;
3316 } else if (g_str_has_prefix(cmd
, "delete")) {
3317 show_oops(t
, "'js delete' currently unimplemented");
3319 show_oops(t
, "unknown js command: %s", cmd
);
3325 add_favorite(struct tab
*t
, struct karg
*args
)
3327 char file
[PATH_MAX
];
3330 size_t urilen
, linelen
;
3331 const gchar
*uri
, *title
;
3336 /* don't allow adding of xtp pages to favorites */
3337 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3338 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3342 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3343 if ((f
= fopen(file
, "r+")) == NULL
) {
3344 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3348 title
= webkit_web_view_get_title(t
->wv
);
3349 uri
= get_uri(t
->wv
);
3354 if (title
== NULL
|| uri
== NULL
) {
3355 show_oops(t
, "can't add page to favorites");
3359 urilen
= strlen(uri
);
3362 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3363 if (feof(f
) || ferror(f
))
3366 if (linelen
== urilen
&& !strcmp(line
, uri
))
3373 fprintf(f
, "\n%s\n%s", title
, uri
);
3379 update_favorite_tabs(NULL
);
3385 navaction(struct tab
*t
, struct karg
*args
)
3387 WebKitWebHistoryItem
*item
;
3389 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3390 t
->tab_id
, args
->i
);
3393 if (args
->i
== XT_NAV_BACK
)
3394 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3396 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3398 return (XT_CB_PASSTHROUGH
);
3399 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3401 return (XT_CB_PASSTHROUGH
);
3406 webkit_web_view_go_back(t
->wv
);
3408 case XT_NAV_FORWARD
:
3409 webkit_web_view_go_forward(t
->wv
);
3412 webkit_web_view_reload(t
->wv
);
3414 case XT_NAV_RELOAD_CACHE
:
3415 webkit_web_view_reload_bypass_cache(t
->wv
);
3418 return (XT_CB_PASSTHROUGH
);
3422 move(struct tab
*t
, struct karg
*args
)
3424 GtkAdjustment
*adjust
;
3425 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3430 case XT_MOVE_BOTTOM
:
3432 case XT_MOVE_PAGEDOWN
:
3433 case XT_MOVE_PAGEUP
:
3434 case XT_MOVE_HALFDOWN
:
3435 case XT_MOVE_HALFUP
:
3436 adjust
= t
->adjust_v
;
3439 adjust
= t
->adjust_h
;
3443 pos
= gtk_adjustment_get_value(adjust
);
3444 ps
= gtk_adjustment_get_page_size(adjust
);
3445 upper
= gtk_adjustment_get_upper(adjust
);
3446 lower
= gtk_adjustment_get_lower(adjust
);
3447 si
= gtk_adjustment_get_step_increment(adjust
);
3448 pi
= gtk_adjustment_get_page_increment(adjust
);
3451 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3452 "max %f si %f pi %f\n",
3453 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3454 pos
, ps
, upper
, lower
, max
, si
, pi
);
3460 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3465 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3467 case XT_MOVE_BOTTOM
:
3468 case XT_MOVE_FARRIGHT
:
3469 gtk_adjustment_set_value(adjust
, max
);
3472 case XT_MOVE_FARLEFT
:
3473 gtk_adjustment_set_value(adjust
, lower
);
3475 case XT_MOVE_PAGEDOWN
:
3477 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3479 case XT_MOVE_PAGEUP
:
3481 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3483 case XT_MOVE_HALFDOWN
:
3485 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3487 case XT_MOVE_HALFUP
:
3489 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3492 return (XT_CB_PASSTHROUGH
);
3495 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3497 return (XT_CB_HANDLED
);
3501 url_set_visibility(void)
3505 TAILQ_FOREACH(t
, &tabs
, entry
) {
3506 if (show_url
== 0) {
3507 gtk_widget_hide(t
->toolbar
);
3510 gtk_widget_show(t
->toolbar
);
3515 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3518 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3520 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3524 statusbar_set_visibility(void)
3528 TAILQ_FOREACH(t
, &tabs
, entry
) {
3529 if (show_statusbar
== 0) {
3530 gtk_widget_hide(t
->statusbar
);
3533 gtk_widget_show(t
->statusbar
);
3538 url_set(struct tab
*t
, int enable_url_entry
)
3543 show_url
= enable_url_entry
;
3545 if (enable_url_entry
) {
3546 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3547 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3548 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3550 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3551 GTK_ENTRY_ICON_PRIMARY
);
3553 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3554 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3555 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3556 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3562 fullscreen(struct tab
*t
, struct karg
*args
)
3564 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3567 return (XT_CB_PASSTHROUGH
);
3569 if (show_url
== 0) {
3577 url_set_visibility();
3578 notebook_tab_set_visibility(notebook
);
3580 return (XT_CB_HANDLED
);
3584 statusaction(struct tab
*t
, struct karg
*args
)
3586 int rv
= XT_CB_HANDLED
;
3588 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3591 return (XT_CB_PASSTHROUGH
);
3594 case XT_STATUSBAR_SHOW
:
3595 if (show_statusbar
== 0) {
3597 statusbar_set_visibility();
3600 case XT_STATUSBAR_HIDE
:
3601 if (show_statusbar
== 1) {
3603 statusbar_set_visibility();
3611 urlaction(struct tab
*t
, struct karg
*args
)
3613 int rv
= XT_CB_HANDLED
;
3615 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3618 return (XT_CB_PASSTHROUGH
);
3622 if (show_url
== 0) {
3624 url_set_visibility();
3628 if (show_url
== 1) {
3630 url_set_visibility();
3638 tabaction(struct tab
*t
, struct karg
*args
)
3640 int rv
= XT_CB_HANDLED
;
3644 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3647 return (XT_CB_PASSTHROUGH
);
3651 if ((url
= getparams(args
->s
, "tabnew")))
3652 create_new_tab(url
, NULL
, 1);
3654 create_new_tab(NULL
, NULL
, 1);
3659 case XT_TAB_DELQUIT
:
3660 if (gtk_notebook_get_n_pages(notebook
) > 1)
3666 if ((url
= getparams(args
->s
, "open")) ||
3667 ((url
= getparams(args
->s
, "op"))) ||
3668 ((url
= getparams(args
->s
, "o"))))
3671 rv
= XT_CB_PASSTHROUGH
;
3677 if (show_tabs
== 0) {
3679 notebook_tab_set_visibility(notebook
);
3683 if (show_tabs
== 1) {
3685 notebook_tab_set_visibility(notebook
);
3688 case XT_TAB_UNDO_CLOSE
:
3689 if (undo_count
== 0) {
3690 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3694 u
= TAILQ_FIRST(&undos
);
3695 create_new_tab(u
->uri
, u
, 1);
3697 TAILQ_REMOVE(&undos
, u
, entry
);
3699 /* u->history is freed in create_new_tab() */
3704 rv
= XT_CB_PASSTHROUGH
;
3718 resizetab(struct tab
*t
, struct karg
*args
)
3720 if (t
== NULL
|| args
== NULL
) {
3721 show_oops_s("resizetab invalid parameters");
3722 return (XT_CB_PASSTHROUGH
);
3725 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3726 t
->tab_id
, args
->i
);
3728 adjustfont_webkit(t
, args
->i
);
3730 return (XT_CB_HANDLED
);
3734 movetab(struct tab
*t
, struct karg
*args
)
3739 if (t
== NULL
|| args
== NULL
) {
3740 show_oops_s("movetab invalid parameters");
3741 return (XT_CB_PASSTHROUGH
);
3744 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3745 t
->tab_id
, args
->i
);
3747 if (args
->i
== XT_TAB_INVALID
)
3748 return (XT_CB_PASSTHROUGH
);
3750 if (args
->i
< XT_TAB_INVALID
) {
3751 /* next or previous tab */
3752 if (TAILQ_EMPTY(&tabs
))
3753 return (XT_CB_PASSTHROUGH
);
3757 /* if at the last page, loop around to the first */
3758 if (gtk_notebook_get_current_page(notebook
) ==
3759 gtk_notebook_get_n_pages(notebook
) - 1)
3760 gtk_notebook_set_current_page(notebook
, 0);
3762 gtk_notebook_next_page(notebook
);
3765 /* if at the first page, loop around to the last */
3766 if (gtk_notebook_current_page(notebook
) == 0)
3767 gtk_notebook_set_current_page(notebook
,
3768 gtk_notebook_get_n_pages(notebook
) - 1);
3770 gtk_notebook_prev_page(notebook
);
3773 gtk_notebook_set_current_page(notebook
, 0);
3776 gtk_notebook_set_current_page(notebook
, -1);
3779 return (XT_CB_PASSTHROUGH
);
3782 return (XT_CB_HANDLED
);
3787 if (t
->tab_id
== x
) {
3788 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3789 return (XT_CB_HANDLED
);
3792 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3793 if (tt
->tab_id
== x
) {
3794 gtk_notebook_set_current_page(notebook
, x
);
3795 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3801 return (XT_CB_HANDLED
);
3805 command(struct tab
*t
, struct karg
*args
)
3807 char *s
= NULL
, *ss
= NULL
;
3811 if (t
== NULL
|| args
== NULL
) {
3812 show_oops_s("command invalid parameters");
3813 return (XT_CB_PASSTHROUGH
);
3832 case XT_CMD_OPEN_CURRENT
:
3835 case XT_CMD_TABNEW_CURRENT
:
3836 if (!s
) /* FALL THROUGH? */
3838 if ((uri
= get_uri(t
->wv
)) != NULL
) {
3839 ss
= g_strdup_printf("%s%s", s
, uri
);
3844 show_oops(t
, "command: invalid opcode %d", args
->i
);
3845 return (XT_CB_PASSTHROUGH
);
3848 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3850 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3851 gdk_color_parse("white", &color
);
3852 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3854 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3855 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3860 return (XT_CB_HANDLED
);
3864 * Return a new string with a download row (in html)
3865 * appended. Old string is freed.
3868 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3871 WebKitDownloadStatus stat
;
3872 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3874 char cur_sz
[FMT_SCALED_STRSIZE
];
3875 char tot_sz
[FMT_SCALED_STRSIZE
];
3878 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3880 /* All actions wil take this form:
3881 * xxxt://class/seskey
3883 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3884 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3886 stat
= webkit_download_get_status(dl
->download
);
3889 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3890 status_html
= g_strdup_printf("Finished");
3891 cmd_html
= g_strdup_printf(
3892 "<a href='%s%d/%d'>Remove</a>",
3893 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3895 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3896 /* gather size info */
3897 progress
= 100 * webkit_download_get_progress(dl
->download
);
3900 webkit_download_get_current_size(dl
->download
), cur_sz
);
3902 webkit_download_get_total_size(dl
->download
), tot_sz
);
3904 status_html
= g_strdup_printf(
3905 "<div style='width: 100%%' align='center'>"
3906 "<div class='progress-outer'>"
3907 "<div class='progress-inner' style='width: %.2f%%'>"
3908 "</div></div></div>"
3909 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
3910 progress
, cur_sz
, tot_sz
, progress
);
3912 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3913 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3917 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3918 status_html
= g_strdup_printf("Cancelled");
3919 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3920 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3922 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3923 status_html
= g_strdup_printf("Error!");
3924 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3925 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3927 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3928 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3929 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3930 status_html
= g_strdup_printf("Starting");
3933 show_oops(t
, "%s: unknown download status", __func__
);
3936 new_html
= g_strdup_printf(
3937 "%s\n<tr><td>%s</td><td>%s</td>"
3938 "<td style='text-align:center'>%s</td></tr>\n",
3939 html
, basename(webkit_download_get_uri(dl
->download
)),
3940 status_html
, cmd_html
);
3944 g_free(status_html
);
3955 * update all download tabs apart from one. Pass NULL if
3956 * you want to update all.
3959 update_download_tabs(struct tab
*apart_from
)
3962 if (!updating_dl_tabs
) {
3963 updating_dl_tabs
= 1; /* stop infinite recursion */
3964 TAILQ_FOREACH(t
, &tabs
, entry
)
3965 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3966 && (t
!= apart_from
))
3967 xtp_page_dl(t
, NULL
);
3968 updating_dl_tabs
= 0;
3973 * update all cookie tabs apart from one. Pass NULL if
3974 * you want to update all.
3977 update_cookie_tabs(struct tab
*apart_from
)
3980 if (!updating_cl_tabs
) {
3981 updating_cl_tabs
= 1; /* stop infinite recursion */
3982 TAILQ_FOREACH(t
, &tabs
, entry
)
3983 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3984 && (t
!= apart_from
))
3985 xtp_page_cl(t
, NULL
);
3986 updating_cl_tabs
= 0;
3991 * update all history tabs apart from one. Pass NULL if
3992 * you want to update all.
3995 update_history_tabs(struct tab
*apart_from
)
3999 if (!updating_hl_tabs
) {
4000 updating_hl_tabs
= 1; /* stop infinite recursion */
4001 TAILQ_FOREACH(t
, &tabs
, entry
)
4002 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4003 && (t
!= apart_from
))
4004 xtp_page_hl(t
, NULL
);
4005 updating_hl_tabs
= 0;
4009 /* cookie management XTP page */
4011 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4013 char *header
, *body
, *footer
, *page
, *tmp
;
4014 int i
= 1; /* all ids start 1 */
4015 GSList
*sc
, *pc
, *pc_start
;
4017 char *type
, *table_headers
;
4018 char *last_domain
= strdup("");
4020 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4023 show_oops_s("%s invalid parameters", __func__
);
4026 /* mark this tab as cookie jar */
4027 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
4029 /* Generate a new session key */
4030 if (!updating_cl_tabs
)
4031 generate_xtp_session_key(&cl_session_key
);
4034 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4035 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
4036 "</head><body><h1>Cookie Jar</h1>\n");
4039 table_headers
= g_strdup_printf("<div align='center'><table><tr>"
4046 "<th>HTTP<br />only</th>"
4047 "<th>Rm</th></tr>\n");
4049 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4050 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4054 for (; sc
; sc
= sc
->next
) {
4057 if (strcmp(last_domain
, c
->domain
) != 0) {
4060 last_domain
= strdup(c
->domain
);
4064 body
= g_strdup_printf("%s</table></div>"
4066 body
, c
->domain
, table_headers
);
4070 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4071 c
->domain
, table_headers
);
4076 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4077 if (soup_cookie_equal(pc
->data
, c
)) {
4078 type
= "Session + Persistent";
4083 body
= g_strdup_printf(
4085 "<td style='width: text-align: center'>%s</td>"
4086 "<td style='width: 1px'>%s</td>"
4087 "<td style='width=70%%;overflow: visible'>"
4088 " <textarea rows='4'>%s</textarea>"
4092 "<td style='width: 1px; text-align: center'>%d</td>"
4093 "<td style='width: 1px; text-align: center'>%d</td>"
4094 "<td style='width: 1px; text-align: center'>"
4095 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4102 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4117 soup_cookies_free(sc
);
4118 soup_cookies_free(pc
);
4120 /* small message if there are none */
4122 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4123 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4127 footer
= g_strdup_printf("</table></div></body></html>");
4129 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4134 g_free(table_headers
);
4135 g_free(last_domain
);
4137 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4138 update_cookie_tabs(t
);
4146 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4148 char *header
, *body
, *footer
, *page
, *tmp
;
4150 int i
= 1; /* all ids start 1 */
4152 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4155 show_oops_s("%s invalid parameters", __func__
);
4159 /* mark this tab as history manager */
4160 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
4162 /* Generate a new session key */
4163 if (!updating_hl_tabs
)
4164 generate_xtp_session_key(&hl_session_key
);
4167 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
4168 "<title>History</title>\n"
4171 "<h1>History</h1>\n",
4175 body
= g_strdup_printf("<div align='center'><table><tr>"
4176 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
4178 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4180 body
= g_strdup_printf(
4182 "<td><a href='%s'>%s</a></td>"
4184 "<td style='text-align: center'>"
4185 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4186 body
, h
->uri
, h
->uri
, h
->title
,
4187 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4188 XT_XTP_HL_REMOVE
, i
);
4194 /* small message if there are none */
4197 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4198 "colspan='3'>No History</td></tr>\n", body
);
4203 footer
= g_strdup_printf("</table></div></body></html>");
4205 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4208 * update all history manager tabs as the xtp session
4209 * key has now changed. No need to update the current tab.
4210 * Already did that above.
4212 update_history_tabs(t
);
4218 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4225 * Generate a web page detailing the status of any downloads
4228 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4230 struct download
*dl
;
4231 char *header
, *body
, *footer
, *page
, *tmp
;
4235 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4238 show_oops_s("%s invalid parameters", __func__
);
4241 /* mark as a download manager tab */
4242 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
4245 * Generate a new session key for next page instance.
4246 * This only happens for the top level call to xtp_page_dl()
4247 * in which case updating_dl_tabs is 0.
4249 if (!updating_dl_tabs
)
4250 generate_xtp_session_key(&dl_session_key
);
4252 /* header - with refresh so as to update */
4253 if (refresh_interval
>= 1)
4254 ref
= g_strdup_printf(
4255 "<meta http-equiv='refresh' content='%u"
4256 ";url=%s%d/%s/%d' />\n",
4266 header
= g_strdup_printf(
4268 "<title>Downloads</title>\n%s%s</head>\n",
4269 XT_DOCTYPE XT_HTML_TAG
,
4273 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
4274 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4275 "</p><table><tr><th style='width: 60%%'>"
4276 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4277 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4279 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4280 body
= xtp_page_dl_row(t
, body
, dl
);
4284 /* message if no downloads in list */
4287 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4288 " style='text-align: center'>"
4289 "No downloads</td></tr>\n", body
);
4294 footer
= g_strdup_printf("</table></div></body></html>");
4296 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4300 * update all download manager tabs as the xtp session
4301 * key has now changed. No need to update the current tab.
4302 * Already did that above.
4304 update_download_tabs(t
);
4311 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4318 search(struct tab
*t
, struct karg
*args
)
4322 if (t
== NULL
|| args
== NULL
) {
4323 show_oops_s("search invalid parameters");
4326 if (t
->search_text
== NULL
) {
4327 if (global_search
== NULL
)
4328 return (XT_CB_PASSTHROUGH
);
4330 t
->search_text
= g_strdup(global_search
);
4331 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4332 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4336 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4337 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4340 case XT_SEARCH_NEXT
:
4341 d
= t
->search_forward
;
4343 case XT_SEARCH_PREV
:
4344 d
= !t
->search_forward
;
4347 return (XT_CB_PASSTHROUGH
);
4350 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4352 return (XT_CB_HANDLED
);
4355 struct settings_args
{
4361 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4364 struct settings_args
*sa
= cb_args
;
4369 if (s
->flags
& XT_SF_RUNTIME
)
4375 *sa
->body
= g_strdup_printf(
4377 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
4378 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
4390 set(struct tab
*t
, struct karg
*args
)
4392 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
4394 struct settings_args sa
;
4396 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
4397 bzero(&sa
, sizeof sa
);
4401 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4402 "\n<head><title>Settings</title>\n"
4403 "</head><body><h1>Settings</h1>\n");
4406 body
= g_strdup_printf("<div align='center'><table><tr>"
4407 "<th align='left'>Setting</th>"
4408 "<th align='left'>Value</th></tr>\n");
4410 settings_walk(print_setting
, &sa
);
4413 /* small message if there are none */
4416 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4417 "colspan='2'>No settings</td></tr>\n", body
);
4422 footer
= g_strdup_printf("</table></div></body></html>");
4424 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4430 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4432 show_oops(t
, "Invalid command: %s", pars
);
4434 return (XT_CB_PASSTHROUGH
);
4438 session_save(struct tab
*t
, char *filename
, char **ret
)
4444 f
+= strlen("save");
4445 while (*f
== ' ' && *f
!= '\0')
4451 if (f
[0] == '.' || f
[0] == '/')
4455 if (save_tabs(t
, &a
))
4457 strlcpy(named_session
, f
, sizeof named_session
);
4465 session_open(struct tab
*t
, char *filename
, char **ret
)
4471 f
+= strlen("open");
4472 while (*f
== ' ' && *f
!= '\0')
4478 if (f
[0] == '.' || f
[0] == '/')
4482 a
.i
= XT_SES_CLOSETABS
;
4483 if (open_tabs(t
, &a
))
4486 strlcpy(named_session
, f
, sizeof named_session
);
4494 session_delete(struct tab
*t
, char *filename
, char **ret
)
4496 char file
[PATH_MAX
];
4500 f
+= strlen("delete");
4501 while (*f
== ' ' && *f
!= '\0')
4507 if (f
[0] == '.' || f
[0] == '/')
4510 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, f
);
4514 if (!strcmp(f
, named_session
))
4515 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4516 sizeof named_session
);
4524 session_cmd(struct tab
*t
, struct karg
*args
)
4526 char *action
= NULL
;
4527 char *filename
= NULL
;
4532 if ((action
= getparams(args
->s
, "session")))
4537 if (!strcmp(action
, "show"))
4538 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4539 XT_SAVED_TABS_FILE
: named_session
);
4540 else if (g_str_has_prefix(action
, "save ")) {
4541 if (session_save(t
, action
, &filename
)) {
4542 show_oops(t
, "Can't save session: %s",
4543 filename
? filename
: "INVALID");
4546 } else if (g_str_has_prefix(action
, "open ")) {
4547 if (session_open(t
, action
, &filename
)) {
4548 show_oops(t
, "Can't open session: %s",
4549 filename
? filename
: "INVALID");
4552 } else if (g_str_has_prefix(action
, "delete ")) {
4553 if (session_delete(t
, action
, &filename
)) {
4554 show_oops(t
, "Can't delete session: %s",
4555 filename
? filename
: "INVALID");
4559 show_oops(t
, "Invalid command: %s", action
);
4561 return (XT_CB_PASSTHROUGH
);
4565 * Make a hardcopy of the page
4568 print_page(struct tab
*t
, struct karg
*args
)
4570 WebKitWebFrame
*frame
;
4572 GtkPrintOperation
*op
;
4573 GtkPrintOperationAction action
;
4574 GtkPrintOperationResult print_res
;
4575 GError
*g_err
= NULL
;
4576 int marg_l
, marg_r
, marg_t
, marg_b
;
4578 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4580 ps
= gtk_page_setup_new();
4581 op
= gtk_print_operation_new();
4582 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4583 frame
= webkit_web_view_get_main_frame(t
->wv
);
4585 /* the default margins are too small, so we will bump them */
4586 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4587 XT_PRINT_EXTRA_MARGIN
;
4588 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4589 XT_PRINT_EXTRA_MARGIN
;
4590 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4591 XT_PRINT_EXTRA_MARGIN
;
4592 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4593 XT_PRINT_EXTRA_MARGIN
;
4596 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4597 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4598 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4599 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4601 gtk_print_operation_set_default_page_setup(op
, ps
);
4603 /* this appears to free 'op' and 'ps' */
4604 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4606 /* check it worked */
4607 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4608 show_oops_s("can't print: %s", g_err
->message
);
4609 g_error_free (g_err
);
4617 go_home(struct tab
*t
, struct karg
*args
)
4624 restart(struct tab
*t
, struct karg
*args
)
4628 a
.s
= XT_RESTART_TABS_FILE
;
4630 execvp(start_argv
[0], start_argv
);
4636 #define CTRL GDK_CONTROL_MASK
4637 #define MOD1 GDK_MOD1_MASK
4638 #define SHFT GDK_SHIFT_MASK
4640 /* inherent to GTK not all keys will be caught at all times */
4641 /* XXX sort key bindings */
4642 struct key_binding
{
4647 int (*func
)(struct tab
*, struct karg
*);
4649 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4651 { "cookiejar", MOD1
, 0, GDK_j
, xtp_page_cl
, {0} },
4652 { "downloadmgr", MOD1
, 0, GDK_d
, xtp_page_dl
, {0} },
4653 { "history", MOD1
, 0, GDK_h
, xtp_page_hl
, {0} },
4654 { "print", CTRL
, 0, GDK_p
, print_page
, {0}},
4655 { NULL
, 0, 0, GDK_slash
, command
, {.i
= '/'} },
4656 { NULL
, 0, 0, GDK_question
, command
, {.i
= '?'} },
4657 { NULL
, 0, 0, GDK_colon
, command
, {.i
= ':'} },
4658 { "quit", CTRL
, 0, GDK_q
, quit
, {0} },
4659 { "restart", MOD1
, 0, GDK_q
, restart
, {0} },
4660 { "togglejs", CTRL
, 0, GDK_j
, toggle_js
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4661 { "togglecookie", MOD1
, 0, GDK_c
, toggle_cwl
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4662 { "togglesrc", CTRL
, 0, GDK_s
, toggle_src
, {0} },
4663 { "yankuri", 0, 0, GDK_y
, yank_uri
, {0} },
4664 { "pasteuricur", 0, 0, GDK_p
, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
} },
4665 { "pasteurinew", 0, 0, GDK_P
, paste_uri
, {.i
= XT_PASTE_NEW_TAB
} },
4668 { "searchnext", 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
4669 { "searchprev", 0, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
4672 { "focusaddress", 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
4673 { "focussearch", 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
4675 /* command aliases (handy when -S flag is used) */
4676 { NULL
, 0, 0, GDK_F9
, command
, {.i
= XT_CMD_OPEN
} },
4677 { NULL
, 0, 0, GDK_F10
, command
, {.i
= XT_CMD_OPEN_CURRENT
} },
4678 { NULL
, 0, 0, GDK_F11
, command
, {.i
= XT_CMD_TABNEW
} },
4679 { NULL
, 0, 0, GDK_F12
, command
, {.i
= XT_CMD_TABNEW_CURRENT
} },
4682 { "hinting", 0, 0, GDK_f
, hint
, {.i
= 0} },
4684 /* custom stylesheet */
4685 { "userstyle", 0, 0, GDK_i
, userstyle
, {.i
= 0 } },
4688 { "goback", 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
4689 { "goback", MOD1
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
4690 { "goforward", SHFT
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
4691 { "goforward", MOD1
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
4692 { "reload", 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
4693 { "reload", CTRL
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
4694 { "reloadforce", CTRL
, 0, GDK_R
, navaction
, {.i
= XT_NAV_RELOAD_CACHE
} },
4695 { "reload" , CTRL
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
4696 { "favorites", MOD1
, 1, GDK_f
, xtp_page_fl
, {0} },
4698 /* vertical movement */
4699 { "scrolldown", 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
4700 { "scrolldown", 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
4701 { "scrollup", 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
4702 { "scrollup", 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
4703 { "scrollbottom", 0, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
4704 { "scrollbottom", 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
4705 { "scrolltop", 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
4706 { "scrolltop", 0, 0, GDK_g
, move
, {.i
= XT_MOVE_TOP
} },
4707 { "scrollpagedown", 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4708 { "scrollpagedown", CTRL
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4709 { "scrollhalfdown", CTRL
, 0, GDK_d
, move
, {.i
= XT_MOVE_HALFDOWN
} },
4710 { "scrollpagedown", 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4711 { "scrollpageup", 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
4712 { "scrollpageup", CTRL
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
4713 { "scrollhalfup", CTRL
, 0, GDK_u
, move
, {.i
= XT_MOVE_HALFUP
} },
4714 /* horizontal movement */
4715 { "scrollright", 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
4716 { "scrollright", 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
4717 { "scrollleft", 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
4718 { "scrollleft", 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
4719 { "scrollfarright", 0, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
4720 { "scrollfarleft", 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
4723 { "tabnew", CTRL
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
4724 { "tabclose", CTRL
, 1, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
4725 { "tabundoclose", 0, 0, GDK_U
, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
4726 { "tabgoto1", CTRL
, 0, GDK_1
, movetab
, {.i
= 1} },
4727 { "tabgoto2", CTRL
, 0, GDK_2
, movetab
, {.i
= 2} },
4728 { "tabgoto3", CTRL
, 0, GDK_3
, movetab
, {.i
= 3} },
4729 { "tabgoto4", CTRL
, 0, GDK_4
, movetab
, {.i
= 4} },
4730 { "tabgoto5", CTRL
, 0, GDK_5
, movetab
, {.i
= 5} },
4731 { "tabgoto6", CTRL
, 0, GDK_6
, movetab
, {.i
= 6} },
4732 { "tabgoto7", CTRL
, 0, GDK_7
, movetab
, {.i
= 7} },
4733 { "tabgoto8", CTRL
, 0, GDK_8
, movetab
, {.i
= 8} },
4734 { "tabgoto9", CTRL
, 0, GDK_9
, movetab
, {.i
= 9} },
4735 { "tabgoto10", CTRL
, 0, GDK_0
, movetab
, {.i
= 10} },
4736 { "tabgotofirst", CTRL
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
4737 { "tabgotolast", CTRL
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
4738 { "tabgotoprev", CTRL
, 0, GDK_Left
, movetab
, {.i
= XT_TAB_PREV
} },
4739 { "tabgotonext", CTRL
, 0, GDK_Right
, movetab
, {.i
= XT_TAB_NEXT
} },
4740 { "focusout", CTRL
, 0, GDK_minus
, resizetab
, {.i
= -1} },
4741 { "focusin", CTRL
, 0, GDK_plus
, resizetab
, {.i
= 1} },
4742 { "focusin", CTRL
, 0, GDK_equal
, resizetab
, {.i
= 1} },
4744 TAILQ_HEAD(keybinding_list
, key_binding
);
4747 walk_kb(struct settings
*s
,
4748 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4750 struct key_binding
*k
;
4753 if (s
== NULL
|| cb
== NULL
) {
4754 show_oops_s("walk_kb invalid parameters");
4758 TAILQ_FOREACH(k
, &kbl
, entry
) {
4759 if (k
->name
== NULL
)
4764 if (gdk_keyval_name(k
->key
) == NULL
)
4767 strlcat(str
, k
->name
, sizeof str
);
4768 strlcat(str
, ",", sizeof str
);
4770 if (k
->mask
& GDK_SHIFT_MASK
)
4771 strlcat(str
, "S-", sizeof str
);
4772 if (k
->mask
& GDK_CONTROL_MASK
)
4773 strlcat(str
, "C-", sizeof str
);
4774 if (k
->mask
& GDK_MOD1_MASK
)
4775 strlcat(str
, "M1-", sizeof str
);
4776 if (k
->mask
& GDK_MOD2_MASK
)
4777 strlcat(str
, "M2-", sizeof str
);
4778 if (k
->mask
& GDK_MOD3_MASK
)
4779 strlcat(str
, "M3-", sizeof str
);
4780 if (k
->mask
& GDK_MOD4_MASK
)
4781 strlcat(str
, "M4-", sizeof str
);
4782 if (k
->mask
& GDK_MOD5_MASK
)
4783 strlcat(str
, "M5-", sizeof str
);
4785 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4786 cb(s
, str
, cb_args
);
4790 init_keybindings(void)
4793 struct key_binding
*k
;
4795 for (i
= 0; i
< LENGTH(keys
); i
++) {
4796 k
= g_malloc0(sizeof *k
);
4797 k
->name
= keys
[i
].name
;
4798 k
->mask
= keys
[i
].mask
;
4799 k
->use_in_entry
= keys
[i
].use_in_entry
;
4800 k
->key
= keys
[i
].key
;
4801 k
->func
= keys
[i
].func
;
4802 bcopy(&keys
[i
].arg
, &k
->arg
, sizeof k
->arg
);
4803 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4805 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4806 k
->name
? k
->name
: "unnamed key");
4811 keybinding_clearall(void)
4813 struct key_binding
*k
, *next
;
4815 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4816 next
= TAILQ_NEXT(k
, entry
);
4817 if (k
->name
== NULL
)
4820 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4821 k
->name
? k
->name
: "unnamed key");
4822 TAILQ_REMOVE(&kbl
, k
, entry
);
4828 keybinding_add(char *kb
, char *value
, struct key_binding
*orig
)
4830 struct key_binding
*k
;
4831 guint keyval
, mask
= 0;
4834 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s %s\n", kb
, value
, orig
->name
);
4838 if (strcmp(kb
, orig
->name
))
4841 /* find modifier keys */
4842 if (strstr(value
, "S-"))
4843 mask
|= GDK_SHIFT_MASK
;
4844 if (strstr(value
, "C-"))
4845 mask
|= GDK_CONTROL_MASK
;
4846 if (strstr(value
, "M1-"))
4847 mask
|= GDK_MOD1_MASK
;
4848 if (strstr(value
, "M2-"))
4849 mask
|= GDK_MOD2_MASK
;
4850 if (strstr(value
, "M3-"))
4851 mask
|= GDK_MOD3_MASK
;
4852 if (strstr(value
, "M4-"))
4853 mask
|= GDK_MOD4_MASK
;
4854 if (strstr(value
, "M5-"))
4855 mask
|= GDK_MOD5_MASK
;
4858 for (i
= strlen(value
) - 1; i
> 0; i
--)
4859 if (value
[i
] == '-')
4860 value
= &value
[i
+ 1];
4862 /* validate keyname */
4863 keyval
= gdk_keyval_from_name(value
);
4864 if (keyval
== GDK_VoidSymbol
) {
4865 warnx("invalid keybinding name %s", value
);
4868 /* must run this test too, gtk+ doesn't handle 10 for example */
4869 if (gdk_keyval_name(keyval
) == NULL
) {
4870 warnx("invalid keybinding name %s", value
);
4874 /* make sure it isn't a dupe */
4875 TAILQ_FOREACH(k
, &kbl
, entry
)
4876 if (k
->key
== keyval
&& k
->mask
== mask
) {
4877 warnx("duplicate keybinding for %s", value
);
4882 k
= g_malloc0(sizeof *k
);
4883 k
->name
= orig
->name
;
4885 k
->use_in_entry
= orig
->use_in_entry
;
4887 k
->func
= orig
->func
;
4888 bcopy(&orig
->arg
, &k
->arg
, sizeof k
->arg
);
4890 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
4895 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
4896 k
->name
, gdk_keyval_name(keyval
));
4898 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4904 add_kb(struct settings
*s
, char *entry
)
4909 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
4911 /* clearall is special */
4912 if (!strcmp(entry
, "clearall")) {
4913 keybinding_clearall();
4917 kb
= strstr(entry
, ",");
4923 /* make sure it is a valid keybinding */
4924 for (i
= 0; i
< LENGTH(keys
); i
++)
4925 if (keys
[i
].name
&& !strcmp(entry
, keys
[i
].name
)) {
4926 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s 0x%x %d 0x%x\n",
4929 keys
[i
].use_in_entry
,
4932 return (keybinding_add(entry
, value
, &keys
[i
]));
4941 int (*func
)(struct tab
*, struct karg
*);
4944 { "q!", 0, quit
, {0} },
4945 { "qa", 0, quit
, {0} },
4946 { "qa!", 0, quit
, {0} },
4947 { "w", 0, save_tabs
, {0} },
4948 { "wq", 0, save_tabs_and_quit
, {0} },
4949 { "wq!", 0, save_tabs_and_quit
, {0} },
4950 { "help", 0, help
, {0} },
4951 { "about", 0, about
, {0} },
4952 { "stats", 0, stats
, {0} },
4953 { "version", 0, about
, {0} },
4954 { "cookies", 0, xtp_page_cl
, {0} },
4955 { "fav", 0, xtp_page_fl
, {0} },
4956 { "favadd", 0, add_favorite
, {0} },
4957 { "js", 2, js_cmd
, {0} },
4958 { "cookie", 2, cookie_cmd
, {0} },
4959 { "cert", 1, cert_cmd
, {0} },
4960 { "ca", 0, ca_cmd
, {0} },
4961 { "dl", 0, xtp_page_dl
, {0} },
4962 { "h", 0, xtp_page_hl
, {0} },
4963 { "hist", 0, xtp_page_hl
, {0} },
4964 { "history", 0, xtp_page_hl
, {0} },
4965 { "home", 0, go_home
, {0} },
4966 { "restart", 0, restart
, {0} },
4967 { "urlhide", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4968 { "urlh", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4969 { "urlshow", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4970 { "urls", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4971 { "statushide", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4972 { "statush", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4973 { "statusshow", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4974 { "statuss", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4976 { "1", 0, move
, {.i
= XT_MOVE_TOP
} },
4977 { "print", 0, print_page
, {0} },
4980 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4981 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4982 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4983 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4984 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4985 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4986 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4987 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4988 { "tabshow", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4989 { "tabs", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4990 { "tabhide", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4991 { "tabh", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4992 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4993 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4994 /* XXX add count to these commands */
4995 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4996 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4997 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4998 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4999 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
5000 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
5001 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
5002 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
5003 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
5004 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
5007 { "set", 1, set
, {0} },
5008 { "fullscreen", 0, fullscreen
, {0} },
5009 { "f", 0, fullscreen
, {0} },
5012 { "session", 1, session_cmd
, {0} },
5016 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5024 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5026 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5028 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5035 * cancel, remove, etc. downloads
5038 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5040 struct download find
, *d
;
5042 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5044 /* some commands require a valid download id */
5045 if (cmd
!= XT_XTP_DL_LIST
) {
5046 /* lookup download in question */
5048 d
= RB_FIND(download_list
, &downloads
, &find
);
5051 show_oops(t
, "%s: no such download", __func__
);
5056 /* decide what to do */
5058 case XT_XTP_DL_CANCEL
:
5059 webkit_download_cancel(d
->download
);
5061 case XT_XTP_DL_REMOVE
:
5062 webkit_download_cancel(d
->download
); /* just incase */
5063 g_object_unref(d
->download
);
5064 RB_REMOVE(download_list
, &downloads
, d
);
5066 case XT_XTP_DL_LIST
:
5070 show_oops(t
, "%s: unknown command", __func__
);
5073 xtp_page_dl(t
, NULL
);
5077 * Actions on history, only does one thing for now, but
5078 * we provide the function for future actions
5081 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5083 struct history
*h
, *next
;
5087 case XT_XTP_HL_REMOVE
:
5088 /* walk backwards, as listed in reverse */
5089 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5090 next
= RB_PREV(history_list
, &hl
, h
);
5092 RB_REMOVE(history_list
, &hl
, h
);
5093 g_free((gpointer
) h
->title
);
5094 g_free((gpointer
) h
->uri
);
5101 case XT_XTP_HL_LIST
:
5102 /* Nothing - just xtp_page_hl() below */
5105 show_oops(t
, "%s: unknown command", __func__
);
5109 xtp_page_hl(t
, NULL
);
5112 /* remove a favorite */
5114 remove_favorite(struct tab
*t
, int index
)
5116 char file
[PATH_MAX
], *title
, *uri
;
5117 char *new_favs
, *tmp
;
5122 /* open favorites */
5123 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5125 if ((f
= fopen(file
, "r")) == NULL
) {
5126 show_oops(t
, "%s: can't open favorites: %s",
5127 __func__
, strerror(errno
));
5131 /* build a string which will become the new favroites file */
5132 new_favs
= g_strdup_printf("%s", "");
5135 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5136 if (feof(f
) || ferror(f
))
5138 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5145 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5146 if (feof(f
) || ferror(f
)) {
5147 show_oops(t
, "%s: can't parse favorites %s",
5148 __func__
, strerror(errno
));
5153 /* as long as this isn't the one we are deleting add to file */
5156 new_favs
= g_strdup_printf("%s%s\n%s\n",
5157 new_favs
, title
, uri
);
5169 /* write back new favorites file */
5170 if ((f
= fopen(file
, "w")) == NULL
) {
5171 show_oops(t
, "%s: can't open favorites: %s",
5172 __func__
, strerror(errno
));
5176 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5189 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5192 case XT_XTP_FL_LIST
:
5193 /* nothing, just the below call to xtp_page_fl() */
5195 case XT_XTP_FL_REMOVE
:
5196 remove_favorite(t
, arg
);
5199 show_oops(t
, "%s: invalid favorites command", __func__
);
5203 xtp_page_fl(t
, NULL
);
5207 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5210 case XT_XTP_CL_LIST
:
5211 /* nothing, just xtp_page_cl() */
5213 case XT_XTP_CL_REMOVE
:
5217 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5221 xtp_page_cl(t
, NULL
);
5224 /* link an XTP class to it's session key and handler function */
5225 struct xtp_despatch
{
5228 void (*handle_func
)(struct tab
*, uint8_t, int);
5231 struct xtp_despatch xtp_despatches
[] = {
5232 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5233 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5234 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5235 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5236 { NULL
, NULL
, NULL
}
5240 * is the url xtp protocol? (xxxt://)
5241 * if so, parse and despatch correct bahvior
5244 parse_xtp_url(struct tab
*t
, const char *url
)
5246 char *dup
= NULL
, *p
, *last
;
5247 uint8_t n_tokens
= 0;
5248 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5249 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5253 * tokens array meaning:
5255 * tokens[1] = session key
5256 * tokens[2] = action
5257 * tokens[3] = optional argument
5260 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5262 /*xtp tab meaning is normal unless proven special */
5263 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5265 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5268 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5270 /* split out the url */
5271 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5272 (p
= strtok_r(NULL
, "/", &last
))) {
5274 tokens
[n_tokens
++] = p
;
5277 /* should be atleast three fields 'class/seskey/command/arg' */
5281 dsp
= xtp_despatches
;
5282 req_class
= atoi(tokens
[0]);
5283 while (dsp
->xtp_class
!= NULL
) {
5284 if (dsp
->xtp_class
== req_class
) {
5291 /* did we find one atall? */
5292 if (dsp_match
== NULL
) {
5293 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5297 /* check session key and call despatch function */
5298 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5299 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5312 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5314 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5316 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5319 show_oops_s("activate_uri_entry_cb invalid parameters");
5324 show_oops(t
, "activate_uri_entry_cb no uri");
5328 uri
+= strspn(uri
, "\t ");
5330 /* if xxxt:// treat specially */
5331 if (!parse_xtp_url(t
, uri
)) {
5332 load_uri(t
, (gchar
*)uri
);
5338 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5340 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5341 char *newuri
= NULL
;
5344 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5347 show_oops_s("activate_search_entry_cb invalid parameters");
5351 if (search_string
== NULL
) {
5352 show_oops(t
, "no search_string");
5356 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5357 newuri
= g_strdup_printf(search_string
, enc_search
);
5360 webkit_web_view_load_uri(t
->wv
, newuri
);
5368 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5370 struct domain
*d
= NULL
;
5373 if (uri
== NULL
|| t
== NULL
)
5376 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5381 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5382 es
? "enable" : "disable", uri
);
5384 g_object_set(G_OBJECT(t
->settings
),
5385 "enable-scripts", es
, (char *)NULL
);
5386 g_object_set(G_OBJECT(t
->settings
),
5387 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5388 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5390 button_set_stockid(t
->js_toggle
,
5391 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5395 show_ca_status(struct tab
*t
, const char *uri
)
5397 WebKitWebFrame
*frame
;
5398 WebKitWebDataSource
*source
;
5399 WebKitNetworkRequest
*request
;
5400 SoupMessage
*message
;
5402 gchar
*col_str
= "white";
5405 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5406 ssl_strict_certs
, ssl_ca_file
, uri
);
5410 if (ssl_ca_file
== NULL
) {
5411 if (g_str_has_prefix(uri
, "http://"))
5413 if (g_str_has_prefix(uri
, "https://")) {
5419 if (g_str_has_prefix(uri
, "http://") ||
5420 !g_str_has_prefix(uri
, "https://"))
5423 frame
= webkit_web_view_get_main_frame(t
->wv
);
5424 source
= webkit_web_frame_get_data_source(frame
);
5425 request
= webkit_web_data_source_get_request(source
);
5426 message
= webkit_network_request_get_message(request
);
5428 if (message
&& (soup_message_get_flags(message
) &
5429 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5433 r
= load_compare_cert(t
, NULL
);
5435 col_str
= "lightblue";
5444 gdk_color_parse(col_str
, &color
);
5445 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5447 if (!strcmp(col_str
, "white")) {
5448 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5450 gdk_color_parse("black", &color
);
5451 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5454 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5456 gdk_color_parse("black", &color
);
5457 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5464 free_favicon(struct tab
*t
)
5466 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p pix %p\n",
5467 __func__
, t
->icon_download
, t
->icon_request
, t
->icon_pixbuf
);
5469 if (t
->icon_request
)
5470 g_object_unref(t
->icon_request
);
5472 g_object_unref(t
->icon_pixbuf
);
5473 if (t
->icon_dest_uri
)
5474 g_free(t
->icon_dest_uri
);
5476 t
->icon_pixbuf
= NULL
;
5477 t
->icon_request
= NULL
;
5478 t
->icon_dest_uri
= NULL
;
5482 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5484 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5485 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5487 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5488 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5490 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5491 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5495 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pixbuf
)
5497 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5498 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5500 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5501 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5503 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5504 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5508 is_valid_icon(char *file
)
5511 const char *mime_type
;
5515 gf
= g_file_new_for_path(file
);
5516 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5518 mime_type
= g_file_info_get_content_type(fi
);
5519 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5520 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5521 g_strcmp0(mime_type
, "image/png") == 0 ||
5522 g_strcmp0(mime_type
, "image/gif") == 0 ||
5523 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5531 set_favicon_from_file(struct tab
*t
, char *file
)
5534 GdkPixbuf
*pixbuf
, *scaled
;
5537 if (t
== NULL
|| file
== NULL
)
5539 if (t
->icon_pixbuf
) {
5540 DNPRINTF(XT_D_DOWNLOAD
, "%s: icon already set\n", __func__
);
5544 if (g_str_has_prefix(file
, "file://"))
5545 file
+= strlen("file://");
5546 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5548 if (!stat(file
, &sb
)) {
5549 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5550 /* corrupt icon so trash it */
5551 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5554 /* no need to set icon to default here */
5559 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
5560 if (pixbuf
== NULL
) {
5561 xt_icon_from_name(t
, "text-html");
5565 g_object_get(pixbuf
, "width", &width
, "height", &height
,
5567 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
5568 __func__
, t
->tab_id
, width
, height
);
5570 if (width
> 16 || height
> 16) {
5571 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5572 GDK_INTERP_BILINEAR
);
5573 g_object_unref(pixbuf
);
5577 if (scaled
== NULL
) {
5578 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5579 GDK_INTERP_BILINEAR
);
5583 t
->icon_pixbuf
= scaled
;
5584 xt_icon_from_pixbuf(t
, t
->icon_pixbuf
);
5588 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5591 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5592 struct tab
*tt
= NULL
, *t
= NULL
;
5595 * find the webview instead of passing in the tab as it could have been
5596 * deleted from underneath us.
5598 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5607 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5608 __func__
, t
->tab_id
, status
);
5611 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5613 t
->icon_download
= NULL
;
5616 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5619 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5622 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5624 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5625 __func__
, t
->tab_id
);
5626 t
->icon_download
= NULL
;
5629 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5632 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5633 __func__
, t
->icon_dest_uri
);
5634 set_favicon_from_file(t
, t
->icon_dest_uri
);
5635 /* these will be freed post callback */
5636 t
->icon_request
= NULL
;
5637 t
->icon_download
= NULL
;
5645 abort_favicon_download(struct tab
*t
)
5647 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5649 if (t
->icon_download
) {
5650 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5651 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5652 webkit_download_cancel(t
->icon_download
);
5653 t
->icon_download
= NULL
;
5657 xt_icon_from_name(t
, "text-html");
5661 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5663 gchar
*name_hash
, file
[PATH_MAX
];
5666 DNPRINTF(XT_D_DOWNLOAD
, "notify_icon_loaded_cb %s\n", uri
);
5668 if (uri
== NULL
|| t
== NULL
)
5671 if (t
->icon_request
) {
5672 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5676 /* check to see if we got the icon in cache */
5677 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5678 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5681 if (!stat(file
, &sb
)) {
5682 if (sb
.st_size
> 0) {
5683 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5685 set_favicon_from_file(t
, file
);
5689 /* corrupt icon so trash it */
5690 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5695 /* create download for icon */
5696 t
->icon_request
= webkit_network_request_new(uri
);
5697 if (t
->icon_request
== NULL
) {
5698 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5703 t
->icon_download
= webkit_download_new(t
->icon_request
);
5704 if (t
->icon_download
== NULL
) {
5705 fprintf(stderr
, "%s: icon_download", __func__
);
5709 /* we have to free icon_dest_uri later */
5710 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5711 webkit_download_set_destination_uri(t
->icon_download
,
5714 if (webkit_download_get_status(t
->icon_download
) ==
5715 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5716 fprintf(stderr
, "%s: download failed to start", __func__
);
5717 g_object_unref(t
->icon_request
);
5718 g_free(t
->icon_dest_uri
);
5719 t
->icon_request
= NULL
;
5720 t
->icon_dest_uri
= NULL
;
5724 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5725 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5727 webkit_download_start(t
->icon_download
);
5731 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5733 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5734 struct history
*h
, find
;
5735 const gchar
*s_loading
;
5738 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d\n",
5739 webkit_web_view_get_load_status(wview
));
5742 show_oops_s("notify_load_status_cb invalid paramters");
5746 switch (webkit_web_view_get_load_status(wview
)) {
5747 case WEBKIT_LOAD_PROVISIONAL
:
5749 abort_favicon_download(t
);
5750 #if GTK_CHECK_VERSION(2, 20, 0)
5751 gtk_widget_show(t
->spinner
);
5752 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5754 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5756 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5762 case WEBKIT_LOAD_COMMITTED
:
5764 if ((uri
= get_uri(wview
)) != NULL
) {
5765 if (strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
5766 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5772 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
5775 /* check if js white listing is enabled */
5776 if (enable_js_whitelist
) {
5777 uri
= get_uri(wview
);
5778 check_and_set_js(uri
, t
);
5784 show_ca_status(t
, uri
);
5786 /* we know enough to autosave the session */
5787 if (session_autosave
) {
5793 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
5797 case WEBKIT_LOAD_FINISHED
:
5799 uri
= get_uri(wview
);
5801 if (!strncmp(uri
, "http://", strlen("http://")) ||
5802 !strncmp(uri
, "https://", strlen("https://")) ||
5803 !strncmp(uri
, "file://", strlen("file://"))) {
5805 h
= RB_FIND(history_list
, &hl
, &find
);
5807 title
= webkit_web_view_get_title(wview
);
5808 set
= title
? title
: uri
;
5809 h
= g_malloc(sizeof *h
);
5810 h
->uri
= g_strdup(uri
);
5811 h
->title
= g_strdup(set
);
5812 RB_INSERT(history_list
, &hl
, h
);
5813 completion_add_uri(h
->uri
);
5814 update_history_tabs(NULL
);
5818 set_status(t
, (char *)uri
, XT_STATUS_URI
);
5819 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5820 case WEBKIT_LOAD_FAILED
:
5823 #if GTK_CHECK_VERSION(2, 20, 0)
5824 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5825 gtk_widget_hide(t
->spinner
);
5827 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
5828 if (s_loading
&& !strcmp(s_loading
, "Loading"))
5829 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5831 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
5836 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
5838 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
5839 webkit_web_view_can_go_back(wview
));
5841 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
5842 webkit_web_view_can_go_forward(wview
));
5844 /* take focus if we are visible */
5849 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5851 const gchar
*set
= NULL
, *title
= NULL
;
5853 title
= webkit_web_view_get_title(wview
);
5854 set
= title
? title
: get_uri(wview
);
5855 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
5856 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
5860 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5862 run_script(t
, JS_HINTING
);
5866 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
5868 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
5869 progress
== 100 ? 0 : (double)progress
/ 100);
5870 if (show_url
== 0) {
5871 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
5872 progress
== 100 ? 0 : (double)progress
/ 100);
5877 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5878 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5879 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5884 show_oops_s("webview_npd_cb invalid parameters");
5888 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
5890 webkit_network_request_get_uri(request
));
5892 uri
= (char *)webkit_network_request_get_uri(request
);
5894 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
5896 create_new_tab(uri
, NULL
, ctrl_click_focus
);
5897 webkit_web_policy_decision_ignore(pd
);
5898 return (TRUE
); /* we made the decission */
5901 webkit_web_policy_decision_use(pd
);
5902 return (TRUE
); /* we made the decission */
5906 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5909 struct domain
*d
= NULL
;
5911 WebKitWebView
*webview
= NULL
;
5913 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
5914 webkit_web_view_get_uri(wv
));
5916 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
5917 uri
= webkit_web_view_get_uri(wv
);
5918 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5921 tt
= create_new_tab(NULL
, NULL
, 1);
5923 } else if (enable_scripts
== 1) {
5924 tt
= create_new_tab(NULL
, NULL
, 1);
5932 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
5935 struct domain
*d
= NULL
;
5937 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
5939 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
5940 uri
= webkit_web_view_get_uri(wv
);
5941 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5945 } else if (enable_scripts
== 1)
5952 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
5954 /* we can not eat the event without throwing gtk off so defer it */
5956 /* catch middle click */
5957 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
5962 /* catch ctrl click */
5963 if (e
->type
== GDK_BUTTON_RELEASE
&&
5964 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
5969 return (XT_CB_PASSTHROUGH
);
5973 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
5975 struct mime_type
*m
;
5977 m
= find_mime_type(mime_type
);
5983 show_oops(t
, "can't fork mime handler");
5992 execlp(m
->mt_action
, m
->mt_action
,
5993 webkit_network_request_get_uri(request
), (void *)NULL
);
6002 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6003 WebKitNetworkRequest
*request
, char *mime_type
,
6004 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6007 show_oops_s("webview_mimetype_cb invalid parameters");
6011 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6012 t
->tab_id
, mime_type
);
6014 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6015 webkit_web_policy_decision_ignore(decision
);
6020 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6021 webkit_web_policy_decision_download(decision
);
6029 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6032 const gchar
*filename
;
6034 struct download
*download_entry
;
6037 if (wk_download
== NULL
|| t
== NULL
) {
6038 show_oops_s("%s invalid parameters", __func__
);
6042 filename
= webkit_download_get_suggested_filename(wk_download
);
6043 if (filename
== NULL
)
6044 return (FALSE
); /* abort download */
6046 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
6048 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6049 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6051 webkit_download_set_destination_uri(wk_download
, uri
);
6053 if (webkit_download_get_status(wk_download
) ==
6054 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6055 show_oops(t
, "%s: download failed to start", __func__
);
6057 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6059 download_entry
= g_malloc(sizeof(struct download
));
6060 download_entry
->download
= wk_download
;
6061 download_entry
->tab
= t
;
6062 download_entry
->id
= next_download_id
++;
6063 RB_INSERT(download_list
, &downloads
, download_entry
);
6064 /* get from history */
6065 g_object_ref(wk_download
);
6066 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6072 /* sync other download manager tabs */
6073 update_download_tabs(NULL
);
6076 * NOTE: never redirect/render the current tab before this
6077 * function returns. This will cause the download to never start.
6079 return (ret
); /* start download */
6083 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6085 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6088 show_oops_s("webview_hover_cb");
6093 set_status(t
, uri
, XT_STATUS_LINK
);
6096 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6101 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6103 char s
[2], buf
[128];
6104 const char *errstr
= NULL
;
6107 /* don't use w directly; use t->whatever instead */
6110 show_oops_s("wv_keypress_after_cb");
6111 return (XT_CB_PASSTHROUGH
);
6114 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6115 e
->keyval
, e
->state
, t
);
6119 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6121 return (XT_CB_HANDLED
);
6125 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6126 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6128 /* we have a string */
6130 /* we have a number */
6131 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6139 /* XXX unfuck this */
6140 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6141 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6142 /* last input was numerical */
6144 l
= strlen(t
->hint_num
);
6151 t
->hint_num
[l
] = '\0';
6155 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6156 /* last input was alphanumerical */
6158 l
= strlen(t
->hint_buf
);
6165 t
->hint_buf
[l
] = '\0';
6175 /* numerical input */
6176 if (CLEAN(e
->state
) == 0 &&
6177 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6178 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6179 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6180 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6183 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6185 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6188 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6190 t
->hint_mode
= XT_HINT_NUMERICAL
;
6194 /* empty the counter buffer */
6195 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6196 return (XT_CB_HANDLED
);
6199 /* alphanumerical input */
6201 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6202 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6203 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6204 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6205 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6206 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6207 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6210 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6213 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6215 t
->hint_mode
= XT_HINT_ALPHANUM
;
6218 /* empty the counter buffer */
6219 bzero(t
->hint_num
, sizeof t
->hint_num
);
6220 return (XT_CB_HANDLED
);
6223 return (XT_CB_HANDLED
);
6226 struct key_binding
*k
;
6227 TAILQ_FOREACH(k
, &kbl
, entry
)
6228 if (e
->keyval
== k
->key
) {
6230 if ((e
->state
& (CTRL
| MOD1
)) == 0) {
6231 k
->func(t
, &k
->arg
);
6232 return (XT_CB_HANDLED
);
6235 else if ((e
->state
& k
->mask
) == k
->mask
) {
6236 k
->func(t
, &k
->arg
);
6237 return (XT_CB_HANDLED
);
6241 return (XT_CB_PASSTHROUGH
);
6245 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6249 return (XT_CB_PASSTHROUGH
);
6253 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6255 const gchar
*c
= gtk_entry_get_text(w
);
6259 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6260 e
->keyval
, e
->state
, t
);
6263 show_oops_s("cmd_keyrelease_cb invalid parameters");
6264 return (XT_CB_PASSTHROUGH
);
6267 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6268 e
->keyval
, e
->state
, t
);
6272 if (strlen(c
) == 1) {
6273 webkit_web_view_unmark_text_matches(t
->wv
);
6279 else if (c
[0] == '?')
6285 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6287 /* not found, mark red */
6288 gdk_color_parse("red", &color
);
6289 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6290 /* unmark and remove selection */
6291 webkit_web_view_unmark_text_matches(t
->wv
);
6292 /* my kingdom for a way to unselect text in webview */
6294 /* found, highlight all */
6295 webkit_web_view_unmark_text_matches(t
->wv
);
6296 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6297 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6298 gdk_color_parse("white", &color
);
6299 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6302 return (XT_CB_PASSTHROUGH
);
6307 cmd_complete(struct tab
*t
, char *s
)
6310 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6312 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
6314 for (i
= 0; i
< LENGTH(cmds
); i
++) {
6315 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
6316 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
6318 gtk_entry_set_text(w
, ":");
6319 gtk_entry_append_text(w
, cmds
[i
].cmd
);
6320 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6330 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6333 show_oops_s("entry_key_cb invalid parameters");
6334 return (XT_CB_PASSTHROUGH
);
6337 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6338 e
->keyval
, e
->state
, t
);
6342 if (e
->keyval
== GDK_Escape
) {
6343 /* don't use focus_webview(t) because we want to type :cmds */
6344 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6347 struct key_binding
*k
;
6348 TAILQ_FOREACH(k
, &kbl
, entry
)
6349 if (e
->keyval
== k
->key
&& k
->use_in_entry
) {
6351 if ((e
->state
& (CTRL
| MOD1
)) == 0) {
6352 k
->func(t
, &k
->arg
);
6353 return (XT_CB_HANDLED
);
6356 else if ((e
->state
& k
->mask
) == k
->mask
) {
6357 k
->func(t
, &k
->arg
);
6358 return (XT_CB_HANDLED
);
6362 return (XT_CB_PASSTHROUGH
);
6366 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6368 int rv
= XT_CB_HANDLED
;
6369 const gchar
*c
= gtk_entry_get_text(w
);
6372 show_oops_s("cmd_keypress_cb parameters");
6373 return (XT_CB_PASSTHROUGH
);
6376 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6377 e
->keyval
, e
->state
, t
);
6381 e
->keyval
= GDK_Escape
;
6382 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6383 e
->keyval
= GDK_Escape
;
6385 switch (e
->keyval
) {
6391 if (strchr (c
, ' ')) {
6392 /* par completion */
6393 fprintf(stderr
, "completeme par\n");
6397 cmd_complete(t
, (char *)&c
[1]);
6402 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
6410 if (c
[0] == '/' || c
[0] == '?')
6411 webkit_web_view_unmark_text_matches(t
->wv
);
6415 rv
= XT_CB_PASSTHROUGH
;
6421 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6424 show_oops_s("cmd_focusout_cb invalid parameters");
6425 return (XT_CB_PASSTHROUGH
);
6427 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
6432 if (show_url
== 0 || t
->focus_wv
)
6435 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6437 return (XT_CB_PASSTHROUGH
);
6441 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6445 const gchar
*c
= gtk_entry_get_text(entry
);
6448 show_oops_s("cmd_activate_cb invalid parameters");
6452 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
6457 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6463 if (c
[0] == '/' || c
[0] == '?') {
6464 if (t
->search_text
) {
6465 g_free(t
->search_text
);
6466 t
->search_text
= NULL
;
6469 t
->search_text
= g_strdup(s
);
6471 g_free(global_search
);
6472 global_search
= g_strdup(s
);
6473 t
->search_forward
= c
[0] == '/';
6478 for (i
= 0; i
< LENGTH(cmds
); i
++)
6479 if (cmds
[i
].params
) {
6480 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
6481 cmds
[i
].arg
.s
= g_strdup(s
);
6482 goto execute_command
;
6485 if (!strcmp(s
, cmds
[i
].cmd
))
6486 goto execute_command
;
6488 show_oops(t
, "Invalid command: %s", s
);
6495 cmds
[i
].func(t
, &cmds
[i
].arg
);
6498 backward_cb(GtkWidget
*w
, struct tab
*t
)
6503 show_oops_s("backward_cb invalid parameters");
6507 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
6514 forward_cb(GtkWidget
*w
, struct tab
*t
)
6519 show_oops_s("forward_cb invalid parameters");
6523 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
6525 a
.i
= XT_NAV_FORWARD
;
6530 stop_cb(GtkWidget
*w
, struct tab
*t
)
6532 WebKitWebFrame
*frame
;
6535 show_oops_s("stop_cb invalid parameters");
6539 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
6541 frame
= webkit_web_view_get_main_frame(t
->wv
);
6542 if (frame
== NULL
) {
6543 show_oops(t
, "stop_cb: no frame");
6547 webkit_web_frame_stop_loading(frame
);
6548 abort_favicon_download(t
);
6552 setup_webkit(struct tab
*t
)
6554 g_object_set(G_OBJECT(t
->settings
),
6555 "user-agent", t
->user_agent
, (char *)NULL
);
6556 g_object_set(G_OBJECT(t
->settings
),
6557 "enable-scripts", enable_scripts
, (char *)NULL
);
6558 g_object_set(G_OBJECT(t
->settings
),
6559 "enable-plugins", enable_plugins
, (char *)NULL
);
6560 g_object_set(G_OBJECT(t
->settings
),
6561 "javascript-can-open-windows-automatically", enable_scripts
, (char *)NULL
);
6562 g_object_set(G_OBJECT(t
->wv
),
6563 "full-content-zoom", TRUE
, (char *)NULL
);
6564 adjustfont_webkit(t
, XT_FONT_SET
);
6566 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6570 create_browser(struct tab
*t
)
6576 show_oops_s("create_browser invalid parameters");
6580 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
6581 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
6582 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
6583 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
6585 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
6586 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
6587 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
6589 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
6590 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
6593 t
->settings
= webkit_web_settings_new();
6595 if (user_agent
== NULL
) {
6596 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
6598 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
6601 t
->user_agent
= g_strdup(user_agent
);
6603 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
6615 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
6616 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
6617 gtk_widget_set_name(w
, "xxxterm");
6618 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
6619 g_signal_connect(G_OBJECT(w
), "delete_event",
6620 G_CALLBACK (gtk_main_quit
), NULL
);
6626 create_toolbar(struct tab
*t
)
6628 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
6630 b
= gtk_hbox_new(FALSE
, 0);
6632 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
6635 /* backward button */
6636 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
6637 gtk_widget_set_sensitive(t
->backward
, FALSE
);
6638 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
6639 G_CALLBACK(backward_cb
), t
);
6640 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
6642 /* forward button */
6643 t
->forward
= create_button("GoForward",GTK_STOCK_GO_FORWARD
, 0);
6644 gtk_widget_set_sensitive(t
->forward
, FALSE
);
6645 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
6646 G_CALLBACK(forward_cb
), t
);
6647 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
6651 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
6652 gtk_widget_set_sensitive(t
->stop
, FALSE
);
6653 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
6654 G_CALLBACK(stop_cb
), t
);
6655 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
6659 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
6660 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
6661 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
6662 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
6663 G_CALLBACK(js_toggle_cb
), t
);
6664 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
6667 t
->uri_entry
= gtk_entry_new();
6668 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
6669 G_CALLBACK(activate_uri_entry_cb
), t
);
6670 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
6671 G_CALLBACK(entry_key_cb
), t
);
6673 eb1
= gtk_hbox_new(FALSE
, 0);
6674 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
6675 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
6676 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
6679 if (fancy_bar
&& search_string
) {
6681 t
->search_entry
= gtk_entry_new();
6682 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
6683 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
6684 G_CALLBACK(activate_search_entry_cb
), t
);
6685 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
6686 G_CALLBACK(entry_key_cb
), t
);
6687 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
6688 eb2
= gtk_hbox_new(FALSE
, 0);
6689 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
6690 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
6692 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
6702 TAILQ_FOREACH(t
, &tabs
, entry
)
6703 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
6707 undo_close_tab_save(struct tab
*t
)
6711 struct undo
*u1
, *u2
;
6713 WebKitWebHistoryItem
*item
;
6715 if ((uri
= get_uri(t
->wv
)) == NULL
)
6718 u1
= g_malloc0(sizeof(struct undo
));
6719 u1
->uri
= g_strdup(uri
);
6721 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6723 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
6724 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
6727 /* forward history */
6728 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
6732 u1
->history
= g_list_prepend(u1
->history
,
6733 webkit_web_history_item_copy(item
));
6734 items
= g_list_next(items
);
6739 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
6740 u1
->history
= g_list_prepend(u1
->history
,
6741 webkit_web_history_item_copy(item
));
6745 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
6749 u1
->history
= g_list_prepend(u1
->history
,
6750 webkit_web_history_item_copy(item
));
6751 items
= g_list_next(items
);
6754 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
6756 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
6757 u2
= TAILQ_LAST(&undos
, undo_tailq
);
6758 TAILQ_REMOVE(&undos
, u2
, entry
);
6760 g_list_free(u2
->history
);
6769 delete_tab(struct tab
*t
)
6773 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
6778 TAILQ_REMOVE(&tabs
, t
, entry
);
6780 /* halt all webkit activity */
6781 abort_favicon_download(t
);
6782 webkit_web_view_stop_loading(t
->wv
);
6783 undo_close_tab_save(t
);
6785 gtk_widget_destroy(t
->vbox
);
6786 g_free(t
->user_agent
);
6787 g_free(t
->stylesheet
);
6791 if (TAILQ_EMPTY(&tabs
))
6792 create_new_tab(NULL
, NULL
, 1);
6795 /* recreate session */
6796 if (session_autosave
) {
6803 adjustfont_webkit(struct tab
*t
, int adjust
)
6808 show_oops_s("adjustfont_webkit invalid parameters");
6812 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
6813 if (adjust
== XT_FONT_SET
) {
6814 t
->font_size
= default_font_size
;
6815 zoom
= default_zoom_level
;
6816 t
->font_size
+= adjust
;
6817 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
6818 t
->font_size
, (char *)NULL
);
6819 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
6820 &t
->font_size
, (char *)NULL
);
6822 t
->font_size
+= adjust
;
6823 zoom
+= adjust
/25.0;
6828 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
6829 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
6833 append_tab(struct tab
*t
)
6838 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
6839 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
6843 create_new_tab(char *title
, struct undo
*u
, int focus
)
6846 int load
= 1, id
, notfound
;
6848 WebKitWebHistoryItem
*item
;
6852 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
6854 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
6855 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
6859 t
= g_malloc0(sizeof *t
);
6861 if (title
== NULL
) {
6862 title
= "(untitled)";
6866 t
->vbox
= gtk_vbox_new(FALSE
, 0);
6868 /* label + button for tab */
6869 b
= gtk_hbox_new(FALSE
, 0);
6872 #if GTK_CHECK_VERSION(2, 20, 0)
6873 t
->spinner
= gtk_spinner_new ();
6875 t
->label
= gtk_label_new(title
);
6876 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
6877 gtk_widget_set_size_request(t
->label
, 100, 0);
6878 gtk_widget_set_size_request(b
, 130, 0);
6880 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
6881 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
6882 #if GTK_CHECK_VERSION(2, 20, 0)
6883 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
6887 t
->toolbar
= create_toolbar(t
);
6888 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
6891 t
->browser_win
= create_browser(t
);
6892 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
6894 /* oops message for user feedback */
6895 t
->oops
= gtk_entry_new();
6896 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
6897 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
6898 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
6899 gdk_color_parse("red", &color
);
6900 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
6901 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
6904 t
->cmd
= gtk_entry_new();
6905 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
6906 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
6907 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
6910 t
->statusbar
= gtk_entry_new();
6911 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
6912 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
6913 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
6914 gdk_color_parse("black", &color
);
6915 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6916 gdk_color_parse("white", &color
);
6917 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6918 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
6920 /* xtp meaning is normal by default */
6921 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6923 /* set empty favicon */
6924 xt_icon_from_name(t
, "text-html");
6926 /* and show it all */
6927 gtk_widget_show_all(b
);
6928 gtk_widget_show_all(t
->vbox
);
6930 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
6934 id
= gtk_notebook_get_current_page(notebook
);
6935 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6936 if (tt
->tab_id
== id
) {
6938 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
6939 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
6949 #if GTK_CHECK_VERSION(2, 20, 0)
6950 /* turn spinner off if we are a new tab without uri */
6952 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6953 gtk_widget_hide(t
->spinner
);
6956 /* make notebook tabs reorderable */
6957 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
6959 g_object_connect(G_OBJECT(t
->cmd
),
6960 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
6961 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
6962 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
6963 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
6966 /* reuse wv_button_cb to hide oops */
6967 g_object_connect(G_OBJECT(t
->oops
),
6968 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
6971 g_object_connect(G_OBJECT(t
->wv
),
6972 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
6973 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
6974 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
6975 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
6976 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
6977 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
6978 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
6979 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
6980 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
6981 "signal::event", G_CALLBACK(webview_event_cb
), t
,
6982 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
6983 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
6984 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6985 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
6987 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
6989 g_signal_connect(t
->wv
,
6990 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
6991 g_signal_connect(t
->wv
,
6992 "notify::title", G_CALLBACK(notify_title_cb
), t
);
6994 /* hijack the unused keys as if we were the browser */
6995 g_object_connect(G_OBJECT(t
->toolbar
),
6996 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
6999 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7000 G_CALLBACK(tab_close_cb
), t
);
7005 url_set_visibility();
7006 statusbar_set_visibility();
7009 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7010 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7015 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7019 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7024 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7025 /* restore the tab's history */
7026 if (u
&& u
->history
) {
7030 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7031 items
= g_list_next(items
);
7034 item
= g_list_nth_data(u
->history
, u
->back
);
7036 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7039 g_list_free(u
->history
);
7041 webkit_web_back_forward_list_clear(t
->bfl
);
7047 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
7053 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7055 TAILQ_FOREACH(t
, &tabs
, entry
) {
7056 if (t
->tab_id
== pn
) {
7057 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7060 uri
= webkit_web_view_get_title(t
->wv
);
7063 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7075 menuitem_response(struct tab
*t
)
7077 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7081 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
7083 GtkWidget
*menu
, *menu_items
;
7084 GdkEventButton
*bevent
;
7088 if (event
->type
== GDK_BUTTON_PRESS
) {
7089 bevent
= (GdkEventButton
*) event
;
7090 menu
= gtk_menu_new();
7092 TAILQ_FOREACH(ti
, &tabs
, entry
) {
7093 if ((uri
= get_uri(ti
->wv
)) == NULL
)
7094 /* XXX make sure there is something to print */
7095 /* XXX add gui pages in here to look purdy */
7097 menu_items
= gtk_menu_item_new_with_label(uri
);
7098 gtk_menu_append(GTK_MENU (menu
), menu_items
);
7099 gtk_widget_show(menu_items
);
7101 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
7102 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
7106 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
7107 bevent
->button
, bevent
->time
);
7109 /* unref object so it'll free itself when popped down */
7110 g_object_ref_sink(menu
);
7111 g_object_unref(menu
);
7113 return (TRUE
/* eat event */);
7116 return (FALSE
/* propagate */);
7120 icon_size_map(int icon_size
)
7122 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
7123 icon_size
> GTK_ICON_SIZE_DIALOG
)
7124 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7130 create_button(char *name
, char *stockid
, int size
)
7132 GtkWidget
*button
, *image
;
7135 rcstring
= g_strdup_printf(
7136 "style \"%s-style\"\n"
7138 " GtkWidget::focus-padding = 0\n"
7139 " GtkWidget::focus-line-width = 0\n"
7143 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
7144 gtk_rc_parse_string(rcstring
);
7146 button
= gtk_button_new();
7147 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
7148 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
7150 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
7151 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7152 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
7153 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
7154 gtk_widget_set_name(button
, name
);
7155 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
7156 gtk_widget_set_tooltip_text(button
, name
);
7162 button_set_stockid(GtkWidget
*button
, char *stockid
)
7165 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
7166 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7167 gtk_button_set_image(GTK_BUTTON(button
), image
);
7176 char file
[PATH_MAX
];
7179 vbox
= gtk_vbox_new(FALSE
, 0);
7180 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
7181 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
7182 gtk_notebook_set_tab_hborder(notebook
, 0);
7183 gtk_notebook_set_tab_vborder(notebook
, 0);
7184 gtk_notebook_set_scrollable(notebook
, TRUE
);
7185 notebook_tab_set_visibility(notebook
);
7186 gtk_notebook_set_show_border(notebook
, FALSE
);
7187 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
7189 abtn
= gtk_button_new();
7190 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
7191 gtk_widget_set_size_request(arrow
, -1, -1);
7192 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
7193 gtk_widget_set_size_request(abtn
, -1, 20);
7194 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
7196 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
7197 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
7198 gtk_widget_set_size_request(vbox
, -1, -1);
7200 g_object_connect(G_OBJECT(notebook
),
7201 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
7203 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
7204 G_CALLBACK(arrow_cb
), NULL
);
7206 main_window
= create_window();
7207 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
7208 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
7211 for (i
= 0; i
< LENGTH(icons
); i
++) {
7212 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
7213 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
7214 l
= g_list_append(l
, pb
);
7216 gtk_window_set_default_icon_list(l
);
7218 gtk_widget_show_all(abtn
);
7219 gtk_widget_show_all(main_window
);
7223 set_hook(void **hook
, char *name
)
7226 errx(1, "set_hook");
7228 if (*hook
== NULL
) {
7229 *hook
= dlsym(RTLD_NEXT
, name
);
7231 errx(1, "can't hook %s", name
);
7235 /* override libsoup soup_cookie_equal because it doesn't look at domain */
7237 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
7239 g_return_val_if_fail(cookie1
, FALSE
);
7240 g_return_val_if_fail(cookie2
, FALSE
);
7242 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
7243 !strcmp (cookie1
->value
, cookie2
->value
) &&
7244 !strcmp (cookie1
->path
, cookie2
->path
) &&
7245 !strcmp (cookie1
->domain
, cookie2
->domain
));
7249 transfer_cookies(void)
7252 SoupCookie
*sc
, *pc
;
7254 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7256 for (;cf
; cf
= cf
->next
) {
7258 sc
= soup_cookie_copy(pc
);
7259 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
7262 soup_cookies_free(cf
);
7266 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
7271 print_cookie("soup_cookie_jar_delete_cookie", c
);
7273 if (cookies_enabled
== 0)
7276 if (jar
== NULL
|| c
== NULL
)
7279 /* find and remove from persistent jar */
7280 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7282 for (;cf
; cf
= cf
->next
) {
7284 if (soup_cookie_equal(ci
, c
)) {
7285 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
7290 soup_cookies_free(cf
);
7292 /* delete from session jar */
7293 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
7297 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
7299 struct domain
*d
= NULL
;
7303 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
7304 jar
, p_cookiejar
, s_cookiejar
);
7306 if (cookies_enabled
== 0)
7309 /* see if we are up and running */
7310 if (p_cookiejar
== NULL
) {
7311 _soup_cookie_jar_add_cookie(jar
, cookie
);
7314 /* disallow p_cookiejar adds, shouldn't happen */
7315 if (jar
== p_cookiejar
)
7318 if (enable_cookie_whitelist
&&
7319 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
7321 DNPRINTF(XT_D_COOKIE
,
7322 "soup_cookie_jar_add_cookie: reject %s\n",
7324 if (save_rejected_cookies
) {
7325 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
7326 show_oops_s("can't open reject cookie file");
7329 fseek(r_cookie_f
, 0, SEEK_END
);
7330 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
7331 cookie
->http_only
? "#HttpOnly_" : "",
7333 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
7335 cookie
->secure
? "TRUE" : "FALSE",
7337 (gulong
)soup_date_to_time_t(cookie
->expires
) :
7344 if (!allow_volatile_cookies
)
7348 if (cookie
->expires
== NULL
&& session_timeout
) {
7349 soup_cookie_set_expires(cookie
,
7350 soup_date_new_from_now(session_timeout
));
7351 print_cookie("modified add cookie", cookie
);
7354 /* see if we are white listed for persistence */
7355 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
7356 /* add to persistent jar */
7357 c
= soup_cookie_copy(cookie
);
7358 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
7359 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
7362 /* add to session jar */
7363 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
7364 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
7370 char file
[PATH_MAX
];
7372 set_hook((void *)&_soup_cookie_jar_add_cookie
,
7373 "soup_cookie_jar_add_cookie");
7374 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
7375 "soup_cookie_jar_delete_cookie");
7377 if (cookies_enabled
== 0)
7381 * the following code is intricate due to overriding several libsoup
7383 * do not alter order of these operations.
7386 /* rejected cookies */
7387 if (save_rejected_cookies
)
7388 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
7390 /* persistent cookies */
7391 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
7392 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
7394 /* session cookies */
7395 s_cookiejar
= soup_cookie_jar_new();
7396 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
7397 cookie_policy
, (void *)NULL
);
7400 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
7404 setup_proxy(char *uri
)
7407 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
7408 soup_uri_free(proxy_uri
);
7412 if (http_proxy
!= uri
) {
7419 http_proxy
= g_strdup(uri
);
7420 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
7421 proxy_uri
= soup_uri_new(http_proxy
);
7422 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
7427 send_url_to_socket(char *url
)
7429 int s
, len
, rv
= -1;
7430 struct sockaddr_un sa
;
7432 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7433 warnx("send_url_to_socket: socket");
7437 sa
.sun_family
= AF_UNIX
;
7438 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7439 work_dir
, XT_SOCKET_FILE
);
7442 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7443 warnx("send_url_to_socket: connect");
7447 if (send(s
, url
, strlen(url
) + 1, 0) == -1) {
7448 warnx("send_url_to_socket: send");
7457 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
7460 char str
[XT_MAX_URL_LENGTH
];
7461 socklen_t t
= sizeof(struct sockaddr_un
);
7462 struct sockaddr_un sa
;
7467 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
7468 warn("socket_watcher: accept");
7472 if (getpeereid(s
, &uid
, &gid
) == -1) {
7473 warn("socket_watcher: getpeereid");
7476 if (uid
!= getuid() || gid
!= getgid()) {
7477 warnx("socket_watcher: unauthorized user");
7483 warnx("socket_watcher: not a valid user");
7487 n
= recv(s
, str
, sizeof(str
), 0);
7491 create_new_tab(str
, NULL
, 1);
7498 struct sockaddr_un sa
;
7500 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7501 warn("is_running: socket");
7505 sa
.sun_family
= AF_UNIX
;
7506 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7507 work_dir
, XT_SOCKET_FILE
);
7510 /* connect to see if there is a listener */
7511 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
7512 rv
= 0; /* not running */
7514 rv
= 1; /* already running */
7525 struct sockaddr_un sa
;
7527 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7528 warn("build_socket: socket");
7532 sa
.sun_family
= AF_UNIX
;
7533 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7534 work_dir
, XT_SOCKET_FILE
);
7537 /* connect to see if there is a listener */
7538 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7539 /* no listener so we will */
7540 unlink(sa
.sun_path
);
7542 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7543 warn("build_socket: bind");
7547 if (listen(s
, 1) == -1) {
7548 warn("build_socket: listen");
7561 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
7562 GtkTreeIter
*iter
, struct tab
*t
)
7566 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
7573 completion_add_uri(const gchar
*uri
)
7577 /* add uri to list_store */
7578 gtk_list_store_append(completion_model
, &iter
);
7579 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
7583 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
7584 GtkTreeIter
*iter
, gpointer user_data
)
7586 gchar
*value
, *voffset
;
7588 gboolean match
= FALSE
;
7592 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
7598 if (!strncmp(key
, value
, len
))
7601 voffset
= strstr(value
, "/") + 2;
7602 if (!strncmp(key
, voffset
, len
))
7604 else if (g_str_has_prefix(voffset
, "www.")) {
7605 voffset
= voffset
+ strlen("www.");
7606 if (!strncmp(key
, voffset
, len
))
7616 completion_add(struct tab
*t
)
7618 /* enable completion for tab */
7619 t
->completion
= gtk_entry_completion_new();
7620 gtk_entry_completion_set_text_column(t
->completion
, 0);
7621 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
7622 gtk_entry_completion_set_model(t
->completion
,
7623 GTK_TREE_MODEL(completion_model
));
7624 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
7626 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
7627 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
7628 G_CALLBACK(completion_select_cb
), t
);
7635 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
7640 main(int argc
, char *argv
[])
7643 int c
, s
, optn
= 0, focus
= 1;
7644 char conf
[PATH_MAX
] = { '\0' };
7645 char file
[PATH_MAX
];
7646 char *env_proxy
= NULL
;
7649 struct sigaction sact
;
7653 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
7655 while ((c
= getopt(argc
, argv
, "STVf:s:tn")) != -1) {
7664 errx(0 , "Version: %s", version
);
7667 strlcpy(conf
, optarg
, sizeof(conf
));
7670 strlcpy(named_session
, optarg
, sizeof(named_session
));
7688 RB_INIT(&downloads
);
7692 TAILQ_INIT(&aliases
);
7698 gnutls_global_init();
7700 /* generate session keys for xtp pages */
7701 generate_xtp_session_key(&dl_session_key
);
7702 generate_xtp_session_key(&hl_session_key
);
7703 generate_xtp_session_key(&cl_session_key
);
7704 generate_xtp_session_key(&fl_session_key
);
7707 gtk_init(&argc
, &argv
);
7708 if (!g_thread_supported())
7709 g_thread_init(NULL
);
7712 bzero(&sact
, sizeof(sact
));
7713 sigemptyset(&sact
.sa_mask
);
7714 sact
.sa_handler
= sigchild
;
7715 sact
.sa_flags
= SA_NOCLDSTOP
;
7716 sigaction(SIGCHLD
, &sact
, NULL
);
7718 /* set download dir */
7719 pwd
= getpwuid(getuid());
7721 errx(1, "invalid user %d", getuid());
7722 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
7724 /* set default string settings */
7725 home
= g_strdup("http://www.peereboom.us");
7726 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
7727 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
7728 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
7730 /* read config file */
7731 if (strlen(conf
) == 0)
7732 snprintf(conf
, sizeof conf
, "%s/.%s",
7733 pwd
->pw_dir
, XT_CONF_FILE
);
7734 config_parse(conf
, 0);
7736 /* working directory */
7737 if (strlen(work_dir
) == 0)
7738 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
7739 pwd
->pw_dir
, XT_DIR
);
7740 if (stat(work_dir
, &sb
)) {
7741 if (mkdir(work_dir
, S_IRWXU
) == -1)
7742 err(1, "mkdir work_dir");
7743 if (stat(work_dir
, &sb
))
7744 err(1, "stat work_dir");
7746 if (S_ISDIR(sb
.st_mode
) == 0)
7747 errx(1, "%s not a dir", work_dir
);
7748 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7749 warnx("fixing invalid permissions on %s", work_dir
);
7750 if (chmod(work_dir
, S_IRWXU
) == -1)
7754 /* icon cache dir */
7755 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
7756 if (stat(cache_dir
, &sb
)) {
7757 if (mkdir(cache_dir
, S_IRWXU
) == -1)
7758 err(1, "mkdir cache_dir");
7759 if (stat(cache_dir
, &sb
))
7760 err(1, "stat cache_dir");
7762 if (S_ISDIR(sb
.st_mode
) == 0)
7763 errx(1, "%s not a dir", cache_dir
);
7764 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7765 warnx("fixing invalid permissions on %s", cache_dir
);
7766 if (chmod(cache_dir
, S_IRWXU
) == -1)
7771 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
7772 if (stat(certs_dir
, &sb
)) {
7773 if (mkdir(certs_dir
, S_IRWXU
) == -1)
7774 err(1, "mkdir certs_dir");
7775 if (stat(certs_dir
, &sb
))
7776 err(1, "stat certs_dir");
7778 if (S_ISDIR(sb
.st_mode
) == 0)
7779 errx(1, "%s not a dir", certs_dir
);
7780 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7781 warnx("fixing invalid permissions on %s", certs_dir
);
7782 if (chmod(certs_dir
, S_IRWXU
) == -1)
7787 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
7788 work_dir
, XT_SESSIONS_DIR
);
7789 if (stat(sessions_dir
, &sb
)) {
7790 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
7791 err(1, "mkdir sessions_dir");
7792 if (stat(sessions_dir
, &sb
))
7793 err(1, "stat sessions_dir");
7795 if (S_ISDIR(sb
.st_mode
) == 0)
7796 errx(1, "%s not a dir", sessions_dir
);
7797 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7798 warnx("fixing invalid permissions on %s", sessions_dir
);
7799 if (chmod(sessions_dir
, S_IRWXU
) == -1)
7802 /* runtime settings that can override config file */
7803 if (runtime_settings
[0] != '\0')
7804 config_parse(runtime_settings
, 1);
7807 if (!strcmp(download_dir
, pwd
->pw_dir
))
7808 strlcat(download_dir
, "/downloads", sizeof download_dir
);
7809 if (stat(download_dir
, &sb
)) {
7810 if (mkdir(download_dir
, S_IRWXU
) == -1)
7811 err(1, "mkdir download_dir");
7812 if (stat(download_dir
, &sb
))
7813 err(1, "stat download_dir");
7815 if (S_ISDIR(sb
.st_mode
) == 0)
7816 errx(1, "%s not a dir", download_dir
);
7817 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7818 warnx("fixing invalid permissions on %s", download_dir
);
7819 if (chmod(download_dir
, S_IRWXU
) == -1)
7823 /* favorites file */
7824 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
7825 if (stat(file
, &sb
)) {
7826 warnx("favorites file doesn't exist, creating it");
7827 if ((f
= fopen(file
, "w")) == NULL
)
7828 err(1, "favorites");
7833 session
= webkit_get_default_session();
7838 if (stat(ssl_ca_file
, &sb
)) {
7839 warn("no CA file: %s", ssl_ca_file
);
7840 g_free(ssl_ca_file
);
7843 g_object_set(session
,
7844 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
7845 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
7850 env_proxy
= getenv("http_proxy");
7852 setup_proxy(env_proxy
);
7854 setup_proxy(http_proxy
);
7856 /* see if there is already an xxxterm running */
7857 if (single_instance
&& is_running()) {
7859 warnx("already running");
7864 send_url_to_socket(argv
[0]);
7872 /* uri completion */
7873 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
7878 if (save_global_history
)
7879 restore_global_history();
7881 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
7882 restore_saved_tabs();
7884 a
.s
= named_session
;
7885 a
.i
= XT_SES_DONOTHING
;
7886 open_tabs(NULL
, &a
);
7890 create_new_tab(argv
[0], NULL
, focus
);
7897 if (TAILQ_EMPTY(&tabs
))
7898 create_new_tab(home
, NULL
, 1);
7901 if ((s
= build_socket()) != -1)
7902 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
7906 gnutls_global_deinit();