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>
7 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * multi letter commands
25 * pre and post counts for commands
26 * autocompletion on various inputs
27 * create privacy browsing
28 * - encrypted local data
44 #include <sys/types.h>
46 #if defined(__linux__)
47 #include "linux/util.h"
48 #include "linux/tree.h"
49 #elif defined(__FreeBSD__)
51 #include "freebsd/util.h"
57 #include <sys/queue.h>
59 #include <sys/socket.h>
62 #include <sys/resource.h>
65 #include <gdk/gdkkeysyms.h>
67 #if GTK_CHECK_VERSION(3,0,0)
68 /* we still use GDK_* instead of GDK_KEY_* */
69 #include <gdk/gdkkeysyms-compat.h>
72 #include <webkit/webkit.h>
73 #include <libsoup/soup.h>
74 #include <gnutls/gnutls.h>
75 #include <JavaScriptCore/JavaScript.h>
76 #include <gnutls/x509.h>
78 #include "javascript.h"
81 javascript.h borrowed from vimprobable2 under the following license:
83 Copyright (c) 2009 Leon Winter
84 Copyright (c) 2009 Hannes Schueller
85 Copyright (c) 2009 Matto Fransen
87 Permission is hereby granted, free of charge, to any person obtaining a copy
88 of this software and associated documentation files (the "Software"), to deal
89 in the Software without restriction, including without limitation the rights
90 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
91 copies of the Software, and to permit persons to whom the Software is
92 furnished to do so, subject to the following conditions:
94 The above copyright notice and this permission notice shall be included in
95 all copies or substantial portions of the Software.
97 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
98 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
99 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
100 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
101 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
102 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
106 static char *version
= "$xxxterm$";
108 /* hooked functions */
109 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
110 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
115 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
116 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
117 #define XT_D_MOVE 0x0001
118 #define XT_D_KEY 0x0002
119 #define XT_D_TAB 0x0004
120 #define XT_D_URL 0x0008
121 #define XT_D_CMD 0x0010
122 #define XT_D_NAV 0x0020
123 #define XT_D_DOWNLOAD 0x0040
124 #define XT_D_CONFIG 0x0080
125 #define XT_D_JS 0x0100
126 #define XT_D_FAVORITE 0x0200
127 #define XT_D_PRINTING 0x0400
128 #define XT_D_COOKIE 0x0800
129 #define XT_D_KEYBINDING 0x1000
130 #define XT_D_CLIP 0x2000
131 u_int32_t swm_debug
= 0
148 #define DPRINTF(x...)
149 #define DNPRINTF(n,x...)
152 #define LENGTH(x) (sizeof x / sizeof x[0])
153 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
154 ~(GDK_BUTTON1_MASK) & \
155 ~(GDK_BUTTON2_MASK) & \
156 ~(GDK_BUTTON3_MASK) & \
157 ~(GDK_BUTTON4_MASK) & \
169 TAILQ_ENTRY(tab
) entry
;
171 GtkWidget
*tab_content
;
174 GtkWidget
*uri_entry
;
175 GtkWidget
*search_entry
;
177 GtkWidget
*browser_win
;
178 GtkWidget
*statusbar
;
186 GtkWidget
*js_toggle
;
187 GtkEntryCompletion
*completion
;
191 WebKitWebHistoryItem
*item
;
192 WebKitWebBackForwardList
*bfl
;
195 WebKitNetworkRequest
*icon_request
;
196 WebKitDownload
*icon_download
;
197 gchar
*icon_dest_uri
;
199 /* adjustments for browser */
202 GtkAdjustment
*adjust_h
;
203 GtkAdjustment
*adjust_v
;
209 int xtp_meaning
; /* identifies dls/favorites */
214 #define XT_HINT_NONE (0)
215 #define XT_HINT_NUMERICAL (1)
216 #define XT_HINT_ALPHANUM (2)
220 /* custom stylesheet */
229 WebKitWebSettings
*settings
;
233 TAILQ_HEAD(tab_list
, tab
);
236 RB_ENTRY(history
) entry
;
240 RB_HEAD(history_list
, history
);
243 RB_ENTRY(download
) entry
;
245 WebKitDownload
*download
;
248 RB_HEAD(download_list
, download
);
251 RB_ENTRY(domain
) entry
;
253 int handy
; /* app use */
255 RB_HEAD(domain_list
, domain
);
258 TAILQ_ENTRY(undo
) entry
;
261 int back
; /* Keeps track of how many back
262 * history items there are. */
264 TAILQ_HEAD(undo_tailq
, undo
);
266 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
267 int next_download_id
= 1;
276 #define XT_NAME ("XXXTerm")
277 #define XT_DIR (".xxxterm")
278 #define XT_CACHE_DIR ("cache")
279 #define XT_CERT_DIR ("certs/")
280 #define XT_SESSIONS_DIR ("sessions/")
281 #define XT_CONF_FILE ("xxxterm.conf")
282 #define XT_FAVS_FILE ("favorites")
283 #define XT_SAVED_TABS_FILE ("main_session")
284 #define XT_RESTART_TABS_FILE ("restart_tabs")
285 #define XT_SOCKET_FILE ("socket")
286 #define XT_HISTORY_FILE ("history")
287 #define XT_REJECT_FILE ("rejected.txt")
288 #define XT_COOKIE_FILE ("cookies.txt")
289 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
290 #define XT_CB_HANDLED (TRUE)
291 #define XT_CB_PASSTHROUGH (FALSE)
292 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
293 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
294 #define XT_DLMAN_REFRESH "10"
295 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
296 "td{overflow: hidden;" \
297 " padding: 2px 2px 2px 2px;" \
298 " border: 1px solid black;" \
299 " vertical-align:top;" \
300 " word-wrap: break-word}\n" \
301 "tr:hover{background: #ffff99}\n" \
302 "th{background-color: #cccccc;" \
303 " border: 1px solid black}\n" \
304 "table{width: 100%%;" \
305 " border: 1px black solid;" \
306 " border-collapse:collapse}\n" \
308 "border: 1px solid black;" \
311 ".progress-inner{float: left;" \
313 " background: green}\n" \
314 ".dlstatus{font-size: small;" \
315 " text-align: center}\n" \
317 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
318 #define XT_MAX_UNDO_CLOSE_TAB (32)
319 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
320 #define XT_PRINT_EXTRA_MARGIN 10
323 #define XT_COLOR_RED "#cc0000"
324 #define XT_COLOR_YELLOW "#ffff66"
325 #define XT_COLOR_BLUE "lightblue"
326 #define XT_COLOR_GREEN "#99ff66"
327 #define XT_COLOR_WHITE "white"
328 #define XT_COLOR_BLACK "black"
331 * xxxterm "protocol" (xtp)
332 * We use this for managing stuff like downloads and favorites. They
333 * make magical HTML pages in memory which have xxxt:// links in order
334 * to communicate with xxxterm's internals. These links take the format:
335 * xxxt://class/session_key/action/arg
337 * Don't begin xtp class/actions as 0. atoi returns that on error.
339 * Typically we have not put addition of items in this framework, as
340 * adding items is either done via an ex-command or via a keybinding instead.
343 #define XT_XTP_STR "xxxt://"
345 /* XTP classes (xxxt://<class>) */
346 #define XT_XTP_INVALID 0 /* invalid */
347 #define XT_XTP_DL 1 /* downloads */
348 #define XT_XTP_HL 2 /* history */
349 #define XT_XTP_CL 3 /* cookies */
350 #define XT_XTP_FL 4 /* favorites */
352 /* XTP download actions */
353 #define XT_XTP_DL_LIST 1
354 #define XT_XTP_DL_CANCEL 2
355 #define XT_XTP_DL_REMOVE 3
357 /* XTP history actions */
358 #define XT_XTP_HL_LIST 1
359 #define XT_XTP_HL_REMOVE 2
361 /* XTP cookie actions */
362 #define XT_XTP_CL_LIST 1
363 #define XT_XTP_CL_REMOVE 2
365 /* XTP cookie actions */
366 #define XT_XTP_FL_LIST 1
367 #define XT_XTP_FL_REMOVE 2
370 #define XT_MOVE_INVALID (0)
371 #define XT_MOVE_DOWN (1)
372 #define XT_MOVE_UP (2)
373 #define XT_MOVE_BOTTOM (3)
374 #define XT_MOVE_TOP (4)
375 #define XT_MOVE_PAGEDOWN (5)
376 #define XT_MOVE_PAGEUP (6)
377 #define XT_MOVE_HALFDOWN (7)
378 #define XT_MOVE_HALFUP (8)
379 #define XT_MOVE_LEFT (9)
380 #define XT_MOVE_FARLEFT (10)
381 #define XT_MOVE_RIGHT (11)
382 #define XT_MOVE_FARRIGHT (12)
384 #define XT_TAB_LAST (-4)
385 #define XT_TAB_FIRST (-3)
386 #define XT_TAB_PREV (-2)
387 #define XT_TAB_NEXT (-1)
388 #define XT_TAB_INVALID (0)
389 #define XT_TAB_NEW (1)
390 #define XT_TAB_DELETE (2)
391 #define XT_TAB_DELQUIT (3)
392 #define XT_TAB_OPEN (4)
393 #define XT_TAB_UNDO_CLOSE (5)
394 #define XT_TAB_SHOW (6)
395 #define XT_TAB_HIDE (7)
397 #define XT_NAV_INVALID (0)
398 #define XT_NAV_BACK (1)
399 #define XT_NAV_FORWARD (2)
400 #define XT_NAV_RELOAD (3)
401 #define XT_NAV_RELOAD_CACHE (4)
403 #define XT_FOCUS_INVALID (0)
404 #define XT_FOCUS_URI (1)
405 #define XT_FOCUS_SEARCH (2)
407 #define XT_SEARCH_INVALID (0)
408 #define XT_SEARCH_NEXT (1)
409 #define XT_SEARCH_PREV (2)
411 #define XT_PASTE_CURRENT_TAB (0)
412 #define XT_PASTE_NEW_TAB (1)
414 #define XT_FONT_SET (0)
416 #define XT_URL_SHOW (1)
417 #define XT_URL_HIDE (2)
419 #define XT_STATUSBAR_SHOW (1)
420 #define XT_STATUSBAR_HIDE (2)
422 #define XT_WL_TOGGLE (1<<0)
423 #define XT_WL_ENABLE (1<<1)
424 #define XT_WL_DISABLE (1<<2)
425 #define XT_WL_FQDN (1<<3) /* default */
426 #define XT_WL_TOPLEVEL (1<<4)
427 #define XT_WL_PERSISTENT (1<<5)
428 #define XT_WL_SESSION (1<<6)
429 #define XT_WL_RELOAD (1<<7)
431 #define XT_SHOW (1<<7)
432 #define XT_DELETE (1<<8)
433 #define XT_SAVE (1<<9)
434 #define XT_OPEN (1<<10)
436 #define XT_CMD_OPEN (0)
437 #define XT_CMD_OPEN_CURRENT (1)
438 #define XT_CMD_TABNEW (2)
439 #define XT_CMD_TABNEW_CURRENT (3)
441 #define XT_STATUS_NOTHING (0)
442 #define XT_STATUS_LINK (1)
443 #define XT_STATUS_URI (2)
444 #define XT_STATUS_LOADING (3)
446 #define XT_SES_DONOTHING (0)
447 #define XT_SES_CLOSETABS (1)
449 #define XT_BM_NORMAL (0)
450 #define XT_BM_WHITELIST (1)
451 #define XT_BM_KIOSK (2)
453 #define XT_PREFIX (1<<0)
454 #define XT_USERARG (1<<1)
455 #define XT_URLARG (1<<2)
456 #define XT_INTARG (1<<3)
464 TAILQ_ENTRY(mime_type
) entry
;
466 TAILQ_HEAD(mime_type_list
, mime_type
);
472 TAILQ_ENTRY(alias
) entry
;
474 TAILQ_HEAD(alias_list
, alias
);
476 /* settings that require restart */
477 int tabless
= 0; /* allow only 1 tab */
478 int enable_socket
= 0;
479 int single_instance
= 0; /* only allow one xxxterm to run */
480 int fancy_bar
= 1; /* fancy toolbar */
481 int browser_mode
= XT_BM_NORMAL
;
482 int enable_localstorage
= 0;
484 /* runtime settings */
485 int show_tabs
= 1; /* show tabs on notebook */
486 int show_url
= 1; /* show url toolbar on notebook */
487 int show_statusbar
= 0; /* vimperator style status bar */
488 int ctrl_click_focus
= 0; /* ctrl click gets focus */
489 int cookies_enabled
= 1; /* enable cookies */
490 int read_only_cookies
= 0; /* enable to not write cookies */
491 int enable_scripts
= 1;
492 int enable_plugins
= 0;
493 int default_font_size
= 12;
494 gfloat default_zoom_level
= 1.0;
495 int window_height
= 768;
496 int window_width
= 1024;
497 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
498 int refresh_interval
= 10; /* download refresh interval */
499 int enable_cookie_whitelist
= 0;
500 int enable_js_whitelist
= 0;
501 int session_timeout
= 3600; /* cookie session timeout */
502 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
503 char *ssl_ca_file
= NULL
;
504 char *resource_dir
= NULL
;
505 gboolean ssl_strict_certs
= FALSE
;
506 int append_next
= 1; /* append tab after current tab */
508 char *search_string
= NULL
;
509 char *http_proxy
= NULL
;
510 char download_dir
[PATH_MAX
];
511 char runtime_settings
[PATH_MAX
]; /* override of settings */
512 int allow_volatile_cookies
= 0;
513 int save_global_history
= 0; /* save global history to disk */
514 char *user_agent
= NULL
;
515 int save_rejected_cookies
= 0;
516 int session_autosave
= 0;
517 int guess_search
= 0;
518 int dns_prefetch
= FALSE
;
519 gint max_connections
= 25;
520 gint max_host_connections
= 5;
521 gint enable_spell_checking
= 0;
522 char *spell_check_languages
= NULL
;
524 char *cmd_font_name
= NULL
;
525 char *statusbar_font_name
= NULL
;
526 PangoFontDescription
*cmd_font
;
527 PangoFontDescription
*statusbar_font
;
531 int set_download_dir(struct settings
*, char *);
532 int set_work_dir(struct settings
*, char *);
533 int set_runtime_dir(struct settings
*, char *);
534 int set_browser_mode(struct settings
*, char *);
535 int set_cookie_policy(struct settings
*, char *);
536 int add_alias(struct settings
*, char *);
537 int add_mime_type(struct settings
*, char *);
538 int add_cookie_wl(struct settings
*, char *);
539 int add_js_wl(struct settings
*, char *);
540 int add_kb(struct settings
*, char *);
541 void button_set_stockid(GtkWidget
*, char *);
542 GtkWidget
* create_button(char *, char *, int);
544 char *get_browser_mode(struct settings
*);
545 char *get_cookie_policy(struct settings
*);
547 char *get_download_dir(struct settings
*);
548 char *get_work_dir(struct settings
*);
549 char *get_runtime_dir(struct settings
*);
551 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
552 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
553 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
554 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
555 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
557 void recalc_tabs(void);
560 int (*set
)(struct settings
*, char *);
561 char *(*get
)(struct settings
*);
562 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
565 struct special s_browser_mode
= {
571 struct special s_cookie
= {
577 struct special s_alias
= {
583 struct special s_mime
= {
589 struct special s_js
= {
595 struct special s_kb
= {
601 struct special s_cookie_wl
= {
607 struct special s_download_dir
= {
613 struct special s_work_dir
= {
622 #define XT_S_INVALID (0)
625 #define XT_S_FLOAT (3)
627 #define XT_SF_RESTART (1<<0)
628 #define XT_SF_RUNTIME (1<<1)
634 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
635 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
636 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
637 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
638 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
639 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
640 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
641 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
642 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
643 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
644 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
645 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
646 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
647 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
648 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
649 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
650 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
651 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
652 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
653 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
654 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
655 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
656 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
657 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
658 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
659 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
660 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
661 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
662 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
663 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
664 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
665 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
666 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
667 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
668 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
669 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
670 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
671 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
672 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
673 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
674 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
675 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
678 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
679 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
681 /* runtime settings */
682 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
683 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
684 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
685 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
686 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
689 int about(struct tab
*, struct karg
*);
690 int blank(struct tab
*, struct karg
*);
691 int ca_cmd(struct tab
*, struct karg
*);
692 int cookie_show_wl(struct tab
*, struct karg
*);
693 int js_show_wl(struct tab
*, struct karg
*);
694 int help(struct tab
*, struct karg
*);
695 int set(struct tab
*, struct karg
*);
696 int stats(struct tab
*, struct karg
*);
697 int marco(struct tab
*, struct karg
*);
698 const char * marco_message(int *);
699 int xtp_page_cl(struct tab
*, struct karg
*);
700 int xtp_page_dl(struct tab
*, struct karg
*);
701 int xtp_page_fl(struct tab
*, struct karg
*);
702 int xtp_page_hl(struct tab
*, struct karg
*);
703 void xt_icon_from_file(struct tab
*, char *);
704 const gchar
*get_uri(struct tab
*);
705 const gchar
*get_title(struct tab
*);
707 #define XT_URI_ABOUT ("about:")
708 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
709 #define XT_URI_ABOUT_ABOUT ("about")
710 #define XT_URI_ABOUT_BLANK ("blank")
711 #define XT_URI_ABOUT_CERTS ("certs") /* XXX NOT YET */
712 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
713 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
714 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
715 #define XT_URI_ABOUT_FAVORITES ("favorites")
716 #define XT_URI_ABOUT_HELP ("help")
717 #define XT_URI_ABOUT_HISTORY ("history")
718 #define XT_URI_ABOUT_JSWL ("jswl")
719 #define XT_URI_ABOUT_SET ("set")
720 #define XT_URI_ABOUT_STATS ("stats")
721 #define XT_URI_ABOUT_MARCO ("marco")
725 int (*func
)(struct tab
*, struct karg
*);
727 { XT_URI_ABOUT_ABOUT
, about
},
728 { XT_URI_ABOUT_BLANK
, blank
},
729 { XT_URI_ABOUT_CERTS
, ca_cmd
},
730 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
731 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
732 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
733 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
734 { XT_URI_ABOUT_HELP
, help
},
735 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
736 { XT_URI_ABOUT_JSWL
, js_show_wl
},
737 { XT_URI_ABOUT_SET
, set
},
738 { XT_URI_ABOUT_STATS
, stats
},
739 { XT_URI_ABOUT_MARCO
, marco
},
742 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
743 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
744 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
745 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
746 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
747 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
748 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
751 extern char *__progname
;
754 GtkWidget
*main_window
;
755 GtkNotebook
*notebook
;
756 GtkWidget
*arrow
, *abtn
;
757 struct tab_list tabs
;
758 struct history_list hl
;
759 struct download_list downloads
;
760 struct domain_list c_wl
;
761 struct domain_list js_wl
;
762 struct undo_tailq undos
;
763 struct keybinding_list kbl
;
765 int updating_dl_tabs
= 0;
766 int updating_hl_tabs
= 0;
767 int updating_cl_tabs
= 0;
768 int updating_fl_tabs
= 0;
770 uint64_t blocked_cookies
= 0;
771 char named_session
[PATH_MAX
];
772 int icon_size_map(int);
774 GtkListStore
*completion_model
;
775 void completion_add(struct tab
*);
776 void completion_add_uri(const gchar
*);
777 GtkListStore
*buffers_store
;
778 void xxx_dir(char *);
783 int saved_errno
, status
;
788 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
792 if (errno
!= ECHILD
) {
794 clog_warn("sigchild: waitpid:");
800 if (WIFEXITED(status
)) {
801 if (WEXITSTATUS(status
) != 0) {
803 clog_warnx("sigchild: child exit status: %d",
804 WEXITSTATUS(status));
809 clog_warnx("sigchild: child is terminated abnormally");
818 is_g_object_setting(GObject
*o
, char *str
)
820 guint n_props
= 0, i
;
821 GParamSpec
**proplist
;
823 if (! G_IS_OBJECT(o
))
826 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
829 for (i
=0; i
< n_props
; i
++) {
830 if (! strcmp(proplist
[i
]->name
, str
))
837 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
841 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
843 "<title>%s</title>\n"
852 addstyles
? XT_PAGE_STYLE
: "",
861 * Display a web page from a HTML string in memory, rather than from a URL
864 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
869 /* we set this to indicate we want to manually do navaction */
871 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
873 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
875 /* set t->xtp_meaning */
876 for (i
= 0; i
< LENGTH(about_list
); i
++)
877 if (!strcmp(title
, about_list
[i
].name
)) {
882 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
883 #if GTK_CHECK_VERSION(2, 20, 0)
884 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
885 gtk_widget_hide(t
->spinner
);
887 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
888 xt_icon_from_file(t
, file
);
893 get_current_tab(void)
897 TAILQ_FOREACH(t
, &tabs
, entry
) {
898 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
902 warnx("%s: no current tab", __func__
);
908 set_status(struct tab
*t
, gchar
*s
, int status
)
916 case XT_STATUS_LOADING
:
917 type
= g_strdup_printf("Loading: %s", s
);
921 type
= g_strdup_printf("Link: %s", s
);
923 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
927 type
= g_strdup_printf("%s", s
);
929 t
->status
= g_strdup(type
);
933 t
->status
= g_strdup(s
);
935 case XT_STATUS_NOTHING
:
940 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
946 hide_cmd(struct tab
*t
)
948 gtk_widget_hide(t
->cmd
);
952 show_cmd(struct tab
*t
)
954 gtk_widget_hide(t
->oops
);
955 gtk_widget_show(t
->cmd
);
959 hide_buffers(struct tab
*t
)
961 gtk_widget_hide(t
->buffers
);
962 gtk_list_store_clear(buffers_store
);
972 sort_tabs_by_page_num(struct tab
***stabs
)
977 num_tabs
= gtk_notebook_get_n_pages(notebook
);
979 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
981 TAILQ_FOREACH(t
, &tabs
, entry
)
982 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
988 buffers_make_list(void)
991 const gchar
*title
= NULL
;
993 struct tab
**stabs
= NULL
;
995 num_tabs
= sort_tabs_by_page_num(&stabs
);
997 for (i
= 0; i
< num_tabs
; i
++)
999 gtk_list_store_append(buffers_store
, &iter
);
1000 title
= get_title(stabs
[i
]);
1001 gtk_list_store_set(buffers_store
, &iter
,
1002 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1012 show_buffers(struct tab
*t
)
1014 buffers_make_list();
1015 gtk_widget_show(t
->buffers
);
1016 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1020 toggle_buffers(struct tab
*t
)
1022 if (gtk_widget_get_visible(t
->buffers
))
1029 buffers(struct tab
*t
, struct karg
*args
)
1037 hide_oops(struct tab
*t
)
1039 gtk_widget_hide(t
->oops
);
1043 show_oops(struct tab
*at
, const char *fmt
, ...)
1047 struct tab
*t
= NULL
;
1053 if ((t
= get_current_tab()) == NULL
)
1059 if (vasprintf(&msg
, fmt
, ap
) == -1)
1060 errx(1, "show_oops failed");
1063 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1064 gtk_widget_hide(t
->cmd
);
1065 gtk_widget_show(t
->oops
);
1069 get_as_string(struct settings
*s
)
1080 warnx("get_as_string skip %s\n", s
->name
);
1081 } else if (s
->type
== XT_S_INT
)
1082 r
= g_strdup_printf("%d", *s
->ival
);
1083 else if (s
->type
== XT_S_STR
)
1084 r
= g_strdup(*s
->sval
);
1085 else if (s
->type
== XT_S_FLOAT
)
1086 r
= g_strdup_printf("%f", *s
->fval
);
1088 r
= g_strdup_printf("INVALID TYPE");
1094 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1099 for (i
= 0; i
< LENGTH(rs
); i
++) {
1100 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1101 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1103 s
= get_as_string(&rs
[i
]);
1104 cb(&rs
[i
], s
, cb_args
);
1111 set_browser_mode(struct settings
*s
, char *val
)
1113 if (!strcmp(val
, "whitelist")) {
1114 browser_mode
= XT_BM_WHITELIST
;
1115 allow_volatile_cookies
= 0;
1116 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1117 cookies_enabled
= 1;
1118 enable_cookie_whitelist
= 1;
1119 read_only_cookies
= 0;
1120 save_rejected_cookies
= 0;
1121 session_timeout
= 3600;
1123 enable_js_whitelist
= 1;
1124 enable_localstorage
= 0;
1125 } else if (!strcmp(val
, "normal")) {
1126 browser_mode
= XT_BM_NORMAL
;
1127 allow_volatile_cookies
= 0;
1128 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1129 cookies_enabled
= 1;
1130 enable_cookie_whitelist
= 0;
1131 read_only_cookies
= 0;
1132 save_rejected_cookies
= 0;
1133 session_timeout
= 3600;
1135 enable_js_whitelist
= 0;
1136 enable_localstorage
= 1;
1137 } else if (!strcmp(val
, "kiosk")) {
1138 browser_mode
= XT_BM_KIOSK
;
1139 allow_volatile_cookies
= 0;
1140 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1141 cookies_enabled
= 1;
1142 enable_cookie_whitelist
= 0;
1143 read_only_cookies
= 0;
1144 save_rejected_cookies
= 0;
1145 session_timeout
= 3600;
1147 enable_js_whitelist
= 0;
1148 enable_localstorage
= 1;
1158 get_browser_mode(struct settings
*s
)
1162 if (browser_mode
== XT_BM_WHITELIST
)
1163 r
= g_strdup("whitelist");
1164 else if (browser_mode
== XT_BM_NORMAL
)
1165 r
= g_strdup("normal");
1166 else if (browser_mode
== XT_BM_KIOSK
)
1167 r
= g_strdup("kiosk");
1175 set_cookie_policy(struct settings
*s
, char *val
)
1177 if (!strcmp(val
, "no3rdparty"))
1178 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1179 else if (!strcmp(val
, "accept"))
1180 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1181 else if (!strcmp(val
, "reject"))
1182 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1190 get_cookie_policy(struct settings
*s
)
1194 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1195 r
= g_strdup("no3rdparty");
1196 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1197 r
= g_strdup("accept");
1198 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1199 r
= g_strdup("reject");
1207 get_download_dir(struct settings
*s
)
1209 if (download_dir
[0] == '\0')
1211 return (g_strdup(download_dir
));
1215 set_download_dir(struct settings
*s
, char *val
)
1218 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1219 pwd
->pw_dir
, &val
[1]);
1221 strlcpy(download_dir
, val
, sizeof download_dir
);
1228 * We use these to prevent people putting xxxt:// URLs on
1229 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1231 #define XT_XTP_SES_KEY_SZ 8
1232 #define XT_XTP_SES_KEY_HEX_FMT \
1233 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1234 char *dl_session_key
; /* downloads */
1235 char *hl_session_key
; /* history list */
1236 char *cl_session_key
; /* cookie list */
1237 char *fl_session_key
; /* favorites list */
1239 char work_dir
[PATH_MAX
];
1240 char certs_dir
[PATH_MAX
];
1241 char cache_dir
[PATH_MAX
];
1242 char sessions_dir
[PATH_MAX
];
1243 char cookie_file
[PATH_MAX
];
1244 SoupURI
*proxy_uri
= NULL
;
1245 SoupSession
*session
;
1246 SoupCookieJar
*s_cookiejar
;
1247 SoupCookieJar
*p_cookiejar
;
1248 char rc_fname
[PATH_MAX
];
1250 struct mime_type_list mtl
;
1251 struct alias_list aliases
;
1254 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1255 void delete_tab(struct tab
*);
1256 void adjustfont_webkit(struct tab
*, int);
1257 int run_script(struct tab
*, char *);
1258 int download_rb_cmp(struct download
*, struct download
*);
1259 gboolean
cmd_execute(struct tab
*t
, char *str
);
1262 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1264 return (strcmp(h1
->uri
, h2
->uri
));
1266 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1269 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1271 return (strcmp(d1
->d
, d2
->d
));
1273 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1276 get_work_dir(struct settings
*s
)
1278 if (work_dir
[0] == '\0')
1280 return (g_strdup(work_dir
));
1284 set_work_dir(struct settings
*s
, char *val
)
1287 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1288 pwd
->pw_dir
, &val
[1]);
1290 strlcpy(work_dir
, val
, sizeof work_dir
);
1296 * generate a session key to secure xtp commands.
1297 * pass in a ptr to the key in question and it will
1298 * be modified in place.
1301 generate_xtp_session_key(char **key
)
1303 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1309 /* make a new one */
1310 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1311 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1312 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1313 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1315 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1319 * validate a xtp session key.
1323 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1325 if (strcmp(trusted
, untrusted
) != 0) {
1326 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1335 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1337 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1339 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1341 struct valid_url_types
{
1352 valid_url_type(char *url
)
1356 for (i
= 0; i
< LENGTH(vut
); i
++)
1357 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1364 print_cookie(char *msg
, SoupCookie
*c
)
1370 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1371 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1372 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1373 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1374 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1375 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1376 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1377 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1378 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1379 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1383 walk_alias(struct settings
*s
,
1384 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1389 if (s
== NULL
|| cb
== NULL
) {
1390 show_oops(NULL
, "walk_alias invalid parameters");
1394 TAILQ_FOREACH(a
, &aliases
, entry
) {
1395 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1396 cb(s
, str
, cb_args
);
1402 match_alias(char *url_in
)
1406 char *url_out
= NULL
, *search
, *enc_arg
;
1408 search
= g_strdup(url_in
);
1410 if (strsep(&arg
, " \t") == NULL
) {
1411 show_oops(NULL
, "match_alias: NULL URL");
1415 TAILQ_FOREACH(a
, &aliases
, entry
) {
1416 if (!strcmp(search
, a
->a_name
))
1421 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1424 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1425 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1428 url_out
= g_strdup(a
->a_uri
);
1436 guess_url_type(char *url_in
)
1439 char *url_out
= NULL
, *enc_search
= NULL
;
1441 url_out
= match_alias(url_in
);
1442 if (url_out
!= NULL
)
1447 * If there is no dot nor slash in the string and it isn't a
1448 * path to a local file and doesn't resolves to an IP, assume
1449 * that the user wants to search for the string.
1452 if (strchr(url_in
, '.') == NULL
&&
1453 strchr(url_in
, '/') == NULL
&&
1454 stat(url_in
, &sb
) != 0 &&
1455 gethostbyname(url_in
) == NULL
) {
1457 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1458 url_out
= g_strdup_printf(search_string
, enc_search
);
1464 /* XXX not sure about this heuristic */
1465 if (stat(url_in
, &sb
) == 0)
1466 url_out
= g_strdup_printf("file://%s", url_in
);
1468 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1470 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1476 load_uri(struct tab
*t
, gchar
*uri
)
1479 gchar
*newuri
= NULL
;
1485 /* Strip leading spaces. */
1486 while (*uri
&& isspace(*uri
))
1489 if (strlen(uri
) == 0) {
1494 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1496 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1497 for (i
= 0; i
< LENGTH(about_list
); i
++)
1498 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1499 bzero(&args
, sizeof args
);
1500 about_list
[i
].func(t
, &args
);
1501 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1505 show_oops(t
, "invalid about page");
1509 if (valid_url_type(uri
)) {
1510 newuri
= guess_url_type(uri
);
1514 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1515 webkit_web_view_load_uri(t
->wv
, uri
);
1522 get_uri(struct tab
*t
)
1524 const gchar
*uri
= NULL
;
1526 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
)
1527 uri
= webkit_web_view_get_uri(t
->wv
);
1529 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, about_list
[t
->xtp_meaning
].name
);
1535 get_title(struct tab
*t
)
1537 const gchar
*set
= NULL
, *title
= NULL
;
1539 title
= webkit_web_view_get_title(t
->wv
);
1540 set
= title
? title
: get_uri(t
);
1541 if (!set
|| t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
) {
1548 add_alias(struct settings
*s
, char *line
)
1551 struct alias
*a
= NULL
;
1553 if (s
== NULL
|| line
== NULL
) {
1554 show_oops(NULL
, "add_alias invalid parameters");
1559 a
= g_malloc(sizeof(*a
));
1561 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1562 show_oops(NULL
, "add_alias: incomplete alias definition");
1565 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1566 show_oops(NULL
, "add_alias: invalid alias definition");
1570 a
->a_name
= g_strdup(alias
);
1571 a
->a_uri
= g_strdup(l
);
1573 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1575 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1585 add_mime_type(struct settings
*s
, char *line
)
1589 struct mime_type
*m
= NULL
;
1590 int downloadfirst
= 0;
1592 /* XXX this could be smarter */
1594 if (line
== NULL
|| strlen(line
) == 0) {
1595 show_oops(NULL
, "add_mime_type invalid parameters");
1604 m
= g_malloc(sizeof(*m
));
1606 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1607 show_oops(NULL
, "add_mime_type: invalid mime_type");
1610 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1611 mime_type
[strlen(mime_type
) - 1] = '\0';
1616 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1617 show_oops(NULL
, "add_mime_type: invalid mime_type");
1621 m
->mt_type
= g_strdup(mime_type
);
1622 m
->mt_action
= g_strdup(l
);
1623 m
->mt_download
= downloadfirst
;
1625 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1626 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1628 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1638 find_mime_type(char *mime_type
)
1640 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1642 TAILQ_FOREACH(m
, &mtl
, entry
) {
1643 if (m
->mt_default
&&
1644 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1647 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1660 walk_mime_type(struct settings
*s
,
1661 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1663 struct mime_type
*m
;
1666 if (s
== NULL
|| cb
== NULL
) {
1667 show_oops(NULL
, "walk_mime_type invalid parameters");
1671 TAILQ_FOREACH(m
, &mtl
, entry
) {
1672 str
= g_strdup_printf("%s%s --> %s",
1674 m
->mt_default
? "*" : "",
1676 cb(s
, str
, cb_args
);
1682 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1687 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1690 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1692 /* treat *.moo.com the same as .moo.com */
1693 if (str
[0] == '*' && str
[1] == '.')
1695 else if (str
[0] == '.')
1700 d
= g_malloc(sizeof *d
);
1702 d
->d
= g_strdup_printf(".%s", str
);
1704 d
->d
= g_strdup(str
);
1707 if (RB_INSERT(domain_list
, wl
, d
))
1710 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1721 add_cookie_wl(struct settings
*s
, char *entry
)
1723 wl_add(entry
, &c_wl
, 1);
1728 walk_cookie_wl(struct settings
*s
,
1729 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1733 if (s
== NULL
|| cb
== NULL
) {
1734 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1738 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1739 cb(s
, d
->d
, cb_args
);
1743 walk_js_wl(struct settings
*s
,
1744 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1748 if (s
== NULL
|| cb
== NULL
) {
1749 show_oops(NULL
, "walk_js_wl invalid parameters");
1753 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1754 cb(s
, d
->d
, cb_args
);
1758 add_js_wl(struct settings
*s
, char *entry
)
1760 wl_add(entry
, &js_wl
, 1 /* persistent */);
1765 wl_find(const gchar
*search
, struct domain_list
*wl
)
1768 struct domain
*d
= NULL
, dfind
;
1771 if (search
== NULL
|| wl
== NULL
)
1773 if (strlen(search
) < 2)
1776 if (search
[0] != '.')
1777 s
= g_strdup_printf(".%s", search
);
1779 s
= g_strdup(search
);
1781 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1784 d
= RB_FIND(domain_list
, wl
, &dfind
);
1798 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1804 if (s
== NULL
|| wl
== NULL
)
1807 if (!strncmp(s
, "http://", strlen("http://")))
1808 s
= &s
[strlen("http://")];
1809 else if (!strncmp(s
, "https://", strlen("https://")))
1810 s
= &s
[strlen("https://")];
1815 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1816 /* chop string at first slash */
1817 if (s
[i
] == '/' || s
[i
] == '\0') {
1820 r
= wl_find(ss
, wl
);
1829 get_toplevel_domain(char *domain
)
1836 if (strlen(domain
) < 2)
1839 s
= &domain
[strlen(domain
) - 1];
1840 while (s
!= domain
) {
1856 settings_add(char *var
, char *val
)
1863 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1864 if (strcmp(var
, rs
[i
].name
))
1868 if (rs
[i
].s
->set(&rs
[i
], val
))
1869 errx(1, "invalid value for %s: %s", var
, val
);
1873 switch (rs
[i
].type
) {
1882 errx(1, "invalid sval for %s",
1896 errx(1, "invalid type for %s", var
);
1905 config_parse(char *filename
, int runtime
)
1908 char *line
, *cp
, *var
, *val
;
1909 size_t len
, lineno
= 0;
1911 char file
[PATH_MAX
];
1914 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1916 if (filename
== NULL
)
1919 if (runtime
&& runtime_settings
[0] != '\0') {
1920 snprintf(file
, sizeof file
, "%s/%s",
1921 work_dir
, runtime_settings
);
1922 if (stat(file
, &sb
)) {
1923 warnx("runtime file doesn't exist, creating it");
1924 if ((f
= fopen(file
, "w")) == NULL
)
1926 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1930 strlcpy(file
, filename
, sizeof file
);
1932 if ((config
= fopen(file
, "r")) == NULL
) {
1933 warn("config_parse: cannot open %s", filename
);
1938 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1939 if (feof(config
) || ferror(config
))
1943 cp
+= (long)strspn(cp
, WS
);
1944 if (cp
[0] == '\0') {
1950 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1951 errx(1, "invalid config file entry: %s", line
);
1953 cp
+= (long)strspn(cp
, WS
);
1955 if ((val
= strsep(&cp
, "\0")) == NULL
)
1958 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
1959 handled
= settings_add(var
, val
);
1961 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1970 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1976 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1980 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1983 JSStringGetUTF8CString(jsref
, s
, l
);
1984 JSStringRelease(jsref
);
1990 disable_hints(struct tab
*t
)
1992 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1993 bzero(t
->hint_num
, sizeof t
->hint_num
);
1994 run_script(t
, "vimprobable_clear()");
1996 t
->hint_mode
= XT_HINT_NONE
;
2000 enable_hints(struct tab
*t
)
2002 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2003 run_script(t
, "vimprobable_show_hints()");
2005 t
->hint_mode
= XT_HINT_NONE
;
2008 #define XT_JS_OPEN ("open;")
2009 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2010 #define XT_JS_FIRE ("fire;")
2011 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2012 #define XT_JS_FOUND ("found;")
2013 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2016 run_script(struct tab
*t
, char *s
)
2018 JSGlobalContextRef ctx
;
2019 WebKitWebFrame
*frame
;
2021 JSValueRef val
, exception
;
2024 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2025 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2027 frame
= webkit_web_view_get_main_frame(t
->wv
);
2028 ctx
= webkit_web_frame_get_global_context(frame
);
2030 str
= JSStringCreateWithUTF8CString(s
);
2031 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2032 NULL
, 0, &exception
);
2033 JSStringRelease(str
);
2035 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2037 es
= js_ref_to_string(ctx
, exception
);
2038 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2042 es
= js_ref_to_string(ctx
, val
);
2043 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2045 /* handle return value right here */
2046 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2048 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
2051 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2052 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2053 &es
[XT_JS_FIRE_LEN
]);
2058 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2059 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2070 hint(struct tab
*t
, struct karg
*args
)
2073 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2075 if (t
->hints_on
== 0)
2084 apply_style(struct tab
*t
)
2086 g_object_set(G_OBJECT(t
->settings
),
2087 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2091 userstyle(struct tab
*t
, struct karg
*args
)
2093 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2097 g_object_set(G_OBJECT(t
->settings
),
2098 "user-stylesheet-uri", NULL
, (char *)NULL
);
2107 * Doesn't work fully, due to the following bug:
2108 * https://bugs.webkit.org/show_bug.cgi?id=51747
2111 restore_global_history(void)
2113 char file
[PATH_MAX
];
2118 const char delim
[3] = {'\\', '\\', '\0'};
2120 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2122 if ((f
= fopen(file
, "r")) == NULL
) {
2123 warnx("%s: fopen", __func__
);
2128 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2129 if (feof(f
) || ferror(f
))
2132 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2133 if (feof(f
) || ferror(f
)) {
2135 warnx("%s: broken history file\n", __func__
);
2139 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2140 webkit_web_history_item_new_with_data(uri
, title
);
2141 h
= g_malloc(sizeof(struct history
));
2142 h
->uri
= g_strdup(uri
);
2143 h
->title
= g_strdup(title
);
2144 RB_INSERT(history_list
, &hl
, h
);
2145 completion_add_uri(h
->uri
);
2147 warnx("%s: failed to restore history\n", __func__
);
2163 save_global_history_to_disk(struct tab
*t
)
2165 char file
[PATH_MAX
];
2169 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2171 if ((f
= fopen(file
, "w")) == NULL
) {
2172 show_oops(t
, "%s: global history file: %s",
2173 __func__
, strerror(errno
));
2177 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2178 if (h
->uri
&& h
->title
)
2179 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2188 quit(struct tab
*t
, struct karg
*args
)
2190 if (save_global_history
)
2191 save_global_history_to_disk(t
);
2199 open_tabs(struct tab
*t
, struct karg
*a
)
2201 char file
[PATH_MAX
];
2205 struct tab
*ti
, *tt
;
2210 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2211 if ((f
= fopen(file
, "r")) == NULL
)
2214 ti
= TAILQ_LAST(&tabs
, tab_list
);
2217 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2218 if (feof(f
) || ferror(f
))
2221 /* retrieve session name */
2222 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2223 strlcpy(named_session
,
2224 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2225 sizeof named_session
);
2229 if (uri
&& strlen(uri
))
2230 create_new_tab(uri
, NULL
, 1, -1);
2236 /* close open tabs */
2237 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2239 tt
= TAILQ_FIRST(&tabs
);
2259 restore_saved_tabs(void)
2261 char file
[PATH_MAX
];
2262 int unlink_file
= 0;
2267 snprintf(file
, sizeof file
, "%s/%s",
2268 sessions_dir
, XT_RESTART_TABS_FILE
);
2269 if (stat(file
, &sb
) == -1)
2270 a
.s
= XT_SAVED_TABS_FILE
;
2273 a
.s
= XT_RESTART_TABS_FILE
;
2276 a
.i
= XT_SES_DONOTHING
;
2277 rv
= open_tabs(NULL
, &a
);
2286 save_tabs(struct tab
*t
, struct karg
*a
)
2288 char file
[PATH_MAX
];
2290 int num_tabs
= 0, i
;
2291 struct tab
**stabs
= NULL
;
2296 snprintf(file
, sizeof file
, "%s/%s",
2297 sessions_dir
, named_session
);
2299 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2301 if ((f
= fopen(file
, "w")) == NULL
) {
2302 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2306 /* save session name */
2307 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2309 /* Save tabs, in the order they are arranged in the notebook. */
2310 num_tabs
= sort_tabs_by_page_num(&stabs
);
2312 for (i
= 0; i
< num_tabs
; i
++)
2313 if (stabs
[i
] && get_uri(stabs
[i
]) != NULL
)
2314 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2318 /* try and make sure this gets to disk NOW. XXX Backup first? */
2319 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2320 show_oops(t
, "May not have managed to save session: %s",
2330 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2342 yank_uri(struct tab
*t
, struct karg
*args
)
2345 GtkClipboard
*clipboard
;
2347 if ((uri
= get_uri(t
)) == NULL
)
2350 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2351 gtk_clipboard_set_text(clipboard
, uri
, -1);
2357 paste_uri(struct tab
*t
, struct karg
*args
)
2359 GtkClipboard
*clipboard
;
2360 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2362 gchar
*p
= NULL
, *uri
;
2364 /* try primary clipboard first */
2365 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2366 p
= gtk_clipboard_wait_for_text(clipboard
);
2368 /* if it failed get whatever text is in cut_buffer0 */
2370 if (gdk_property_get(gdk_get_default_root_window(),
2372 gdk_atom_intern("STRING", FALSE
),
2374 65536 /* picked out of my butt */,
2380 /* yes sir, we need to NUL the string */
2386 while (*uri
&& isspace(*uri
))
2388 if (strlen(uri
) == 0) {
2389 show_oops(t
, "empty paste buffer");
2392 if (guess_search
== 0 && valid_url_type(uri
)) {
2393 /* we can be clever and paste this in search box */
2394 show_oops(t
, "not a valid URL");
2398 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2400 else if (args
->i
== XT_PASTE_NEW_TAB
)
2401 create_new_tab(uri
, NULL
, 1, -1);
2412 find_domain(const gchar
*s
, int add_dot
)
2415 char *r
= NULL
, *ss
= NULL
;
2420 if (!strncmp(s
, "http://", strlen("http://")))
2421 s
= &s
[strlen("http://")];
2422 else if (!strncmp(s
, "https://", strlen("https://")))
2423 s
= &s
[strlen("https://")];
2429 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2430 /* chop string at first slash */
2431 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2434 r
= g_strdup_printf(".%s", ss
);
2445 toggle_cwl(struct tab
*t
, struct karg
*args
)
2449 char *dom
= NULL
, *dom_toggle
= NULL
;
2456 dom
= find_domain(uri
, 1);
2457 d
= wl_find(dom
, &c_wl
);
2464 if (args
->i
& XT_WL_TOGGLE
)
2466 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2468 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2471 if (args
->i
& XT_WL_TOPLEVEL
)
2472 dom_toggle
= get_toplevel_domain(dom
);
2477 /* enable cookies for domain */
2478 wl_add(dom_toggle
, &c_wl
, 0);
2480 /* disable cookies for domain */
2481 RB_REMOVE(domain_list
, &c_wl
, d
);
2483 if (args
->i
& XT_WL_RELOAD
)
2484 webkit_web_view_reload(t
->wv
);
2491 toggle_js(struct tab
*t
, struct karg
*args
)
2496 char *dom
= NULL
, *dom_toggle
= NULL
;
2501 g_object_get(G_OBJECT(t
->settings
),
2502 "enable-scripts", &es
, (char *)NULL
);
2503 if (args
->i
& XT_WL_TOGGLE
)
2505 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2507 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2513 dom
= find_domain(uri
, 1);
2515 if (uri
== NULL
|| dom
== NULL
) {
2516 show_oops(t
, "Can't toggle domain in JavaScript white list");
2520 if (args
->i
& XT_WL_TOPLEVEL
)
2521 dom_toggle
= get_toplevel_domain(dom
);
2526 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2527 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2529 d
= wl_find(dom_toggle
, &js_wl
);
2531 RB_REMOVE(domain_list
, &js_wl
, d
);
2532 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2534 g_object_set(G_OBJECT(t
->settings
),
2535 "enable-scripts", es
, (char *)NULL
);
2536 g_object_set(G_OBJECT(t
->settings
),
2537 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2538 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2540 if (args
->i
& XT_WL_RELOAD
)
2541 webkit_web_view_reload(t
->wv
);
2549 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2553 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2556 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2561 toggle_src(struct tab
*t
, struct karg
*args
)
2568 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2569 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2570 webkit_web_view_reload(t
->wv
);
2576 focus_webview(struct tab
*t
)
2581 /* only grab focus if we are visible */
2582 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2583 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2587 focus(struct tab
*t
, struct karg
*args
)
2589 if (t
== NULL
|| args
== NULL
)
2595 if (args
->i
== XT_FOCUS_URI
)
2596 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2597 else if (args
->i
== XT_FOCUS_SEARCH
)
2598 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2604 stats(struct tab
*t
, struct karg
*args
)
2606 char *page
, *body
, *s
, line
[64 * 1024];
2607 uint64_t line_count
= 0;
2611 show_oops(NULL
, "stats invalid parameters");
2614 if (save_rejected_cookies
) {
2615 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2617 s
= fgets(line
, sizeof line
, r_cookie_f
);
2618 if (s
== NULL
|| feof(r_cookie_f
) ||
2624 snprintf(line
, sizeof line
,
2625 "<br/>Cookies blocked(*) total: %llu", line_count
);
2627 show_oops(t
, "Can't open blocked cookies file: %s",
2631 body
= g_strdup_printf(
2632 "Cookies blocked(*) this session: %llu"
2634 "<p><small><b>*</b> results vary based on settings</small></p>",
2638 page
= get_html_page("Statistics", body
, "", 0);
2641 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2648 marco(struct tab
*t
, struct karg
*args
)
2650 char *page
, line
[64 * 1024];
2654 show_oops(NULL
, "marco invalid parameters");
2657 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2659 page
= get_html_page("Marco Sez...", line
, "", 0);
2661 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2668 blank(struct tab
*t
, struct karg
*args
)
2671 show_oops(NULL
, "blank invalid parameters");
2673 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2678 about(struct tab
*t
, struct karg
*args
)
2683 show_oops(NULL
, "about invalid parameters");
2685 body
= g_strdup_printf("<b>Version: %s</b><p>"
2688 "<li>Marco Peereboom <marco@peereboom.us></li>"
2689 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2690 "<li>Edd Barrett <vext01@gmail.com> </li>"
2691 "<li>Todd T. Fries <todd@fries.net> </li>"
2692 "<li>Raphael Graf <r@undefined.ch> </li>"
2694 "Copyrights and licenses can be found on the XXXterm "
2695 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2699 page
= get_html_page("About", body
, "", 0);
2702 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2709 help(struct tab
*t
, struct karg
*args
)
2711 char *page
, *head
, *body
;
2714 show_oops(NULL
, "help invalid parameters");
2716 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2717 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2719 body
= "XXXterm man page <a href=\"http://opensource.conformal.com/"
2720 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2721 "cgi-bin/man-cgi?xxxterm</a>";
2723 page
= get_html_page("XXXterm", body
, head
, FALSE
);
2725 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2732 * update all favorite tabs apart from one. Pass NULL if
2733 * you want to update all.
2736 update_favorite_tabs(struct tab
*apart_from
)
2739 if (!updating_fl_tabs
) {
2740 updating_fl_tabs
= 1; /* stop infinite recursion */
2741 TAILQ_FOREACH(t
, &tabs
, entry
)
2742 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2743 && (t
!= apart_from
))
2744 xtp_page_fl(t
, NULL
);
2745 updating_fl_tabs
= 0;
2749 /* show a list of favorites (bookmarks) */
2751 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2753 char file
[PATH_MAX
];
2755 char *uri
= NULL
, *title
= NULL
;
2756 size_t len
, lineno
= 0;
2758 char *body
, *tmp
, *page
= NULL
;
2759 const char delim
[3] = {'\\', '\\', '\0'};
2761 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2764 warn("%s: bad param", __func__
);
2766 /* new session key */
2767 if (!updating_fl_tabs
)
2768 generate_xtp_session_key(&fl_session_key
);
2770 /* open favorites */
2771 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2772 if ((f
= fopen(file
, "r")) == NULL
) {
2773 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2778 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
2779 "<th style='width: 40px'>#</th><th>Link</th>"
2780 "<th style='width: 40px'>Rm</th></tr>\n");
2783 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2784 if (feof(f
) || ferror(f
))
2792 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2793 if (feof(f
) || ferror(f
)) {
2794 show_oops(t
, "favorites file corrupt");
2800 body
= g_strdup_printf("%s<tr>"
2802 "<td><a href='%s'>%s</a></td>"
2803 "<td style='text-align: center'>"
2804 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2806 body
, i
, uri
, title
,
2807 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2819 /* if none, say so */
2822 body
= g_strdup_printf("%s<tr>"
2823 "<td colspan='3' style='text-align: center'>"
2824 "No favorites - To add one use the 'favadd' command."
2825 "</td></tr>", body
);
2830 body
= g_strdup_printf("%s</table>", body
);
2840 page
= get_html_page("Favorites", body
, "", 1);
2841 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
2845 update_favorite_tabs(t
);
2854 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2855 size_t cert_count
, char *title
)
2857 gnutls_datum_t cinfo
;
2861 body
= g_strdup("");
2863 for (i
= 0; i
< cert_count
; i
++) {
2864 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2869 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2870 body
, i
, cinfo
.data
);
2871 gnutls_free(cinfo
.data
);
2875 tmp
= get_html_page(title
, body
, "", 0);
2878 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2883 ca_cmd(struct tab
*t
, struct karg
*args
)
2886 int rv
= 1, certs
= 0, certs_read
;
2889 gnutls_x509_crt_t
*c
= NULL
;
2890 char *certs_buf
= NULL
, *s
;
2892 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2893 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
2897 if (fstat(fileno(f
), &sb
) == -1) {
2898 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
2902 certs_buf
= g_malloc(sb
.st_size
+ 1);
2903 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2904 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2907 certs_buf
[sb
.st_size
] = '\0';
2910 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2912 s
+= strlen("BEGIN CERTIFICATE");
2915 bzero(&dt
, sizeof dt
);
2916 dt
.data
= (unsigned char *)certs_buf
;
2917 dt
.size
= sb
.st_size
;
2918 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2919 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
2920 GNUTLS_X509_FMT_PEM
, 0);
2921 if (certs_read
<= 0) {
2922 show_oops(t
, "No cert(s) available");
2925 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2938 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2941 struct addrinfo hints
, *res
= NULL
, *ai
;
2945 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2948 su
= soup_uri_new(uri
);
2951 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2954 snprintf(port
, sizeof port
, "%d", su
->port
);
2955 bzero(&hints
, sizeof(struct addrinfo
));
2956 hints
.ai_flags
= AI_CANONNAME
;
2957 hints
.ai_family
= AF_UNSPEC
;
2958 hints
.ai_socktype
= SOCK_STREAM
;
2960 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2963 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2964 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2967 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2970 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2974 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2979 strlcpy(domain
, su
->host
, domain_sz
);
2990 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2993 gnutls_deinit(gsession
);
2995 gnutls_certificate_free_credentials(xcred
);
3001 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3002 gnutls_certificate_credentials_t
*xc
)
3004 gnutls_certificate_credentials_t xcred
;
3005 gnutls_session_t gsession
;
3008 if (gs
== NULL
|| xc
== NULL
)
3014 gnutls_certificate_allocate_credentials(&xcred
);
3015 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3016 GNUTLS_X509_FMT_PEM
);
3017 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3018 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3019 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3020 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3021 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3022 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3024 gnutls_error_is_fatal(rv
),
3025 gnutls_strerror_name(rv
));
3026 stop_tls(gsession
, xcred
);
3030 gnutls_credentials_type_t cred
;
3031 cred
= gnutls_auth_get_type(gsession
);
3032 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3033 stop_tls(gsession
, xcred
);
3045 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3049 const gnutls_datum_t
*cl
;
3050 gnutls_x509_crt_t
*all_certs
;
3053 if (certs
== NULL
|| cert_count
== NULL
)
3055 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3057 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3061 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3062 for (i
= 0; i
< len
; i
++) {
3063 gnutls_x509_crt_init(&all_certs
[i
]);
3064 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3065 GNUTLS_X509_FMT_PEM
< 0)) {
3079 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3083 for (i
= 0; i
< cert_count
; i
++)
3084 gnutls_x509_crt_deinit(certs
[i
]);
3089 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3090 size_t cert_count
, char *domain
)
3093 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3098 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3101 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3102 if ((f
= fopen(file
, "w")) == NULL
) {
3103 show_oops(t
, "Can't create cert file %s %s",
3104 file
, strerror(errno
));
3108 for (i
= 0; i
< cert_count
; i
++) {
3109 cert_buf_sz
= sizeof cert_buf
;
3110 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3111 cert_buf
, &cert_buf_sz
)) {
3112 show_oops(t
, "gnutls_x509_crt_export failed");
3115 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3116 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3121 /* not the best spot but oh well */
3122 gdk_color_parse("lightblue", &color
);
3123 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3124 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
3125 gdk_color_parse(XT_COLOR_BLACK
, &color
);
3126 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
3132 load_compare_cert(struct tab
*t
, struct karg
*args
)
3135 char domain
[8182], file
[PATH_MAX
];
3136 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3137 int s
= -1, rv
= 1, i
;
3141 gnutls_session_t gsession
;
3142 gnutls_x509_crt_t
*certs
;
3143 gnutls_certificate_credentials_t xcred
;
3148 if ((uri
= get_uri(t
)) == NULL
)
3151 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
3155 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3156 show_oops(t
, "Start TLS failed");
3161 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3162 show_oops(t
, "Can't get connection certificates");
3166 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3167 if ((f
= fopen(file
, "r")) == NULL
)
3170 for (i
= 0; i
< cert_count
; i
++) {
3171 cert_buf_sz
= sizeof cert_buf
;
3172 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3173 cert_buf
, &cert_buf_sz
)) {
3176 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3177 rv
= -1; /* critical */
3180 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3181 rv
= -1; /* critical */
3190 free_connection_certs(certs
, cert_count
);
3192 /* we close the socket first for speed */
3195 stop_tls(gsession
, xcred
);
3201 cert_cmd(struct tab
*t
, struct karg
*args
)
3207 gnutls_session_t gsession
;
3208 gnutls_x509_crt_t
*certs
;
3209 gnutls_certificate_credentials_t xcred
;
3214 if (ssl_ca_file
== NULL
) {
3215 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3219 if ((uri
= get_uri(t
)) == NULL
) {
3220 show_oops(t
, "Invalid URI");
3224 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3225 show_oops(t
, "Invalid certificate URI: %s", uri
);
3230 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3231 show_oops(t
, "Start TLS failed");
3236 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3237 show_oops(t
, "get_connection_certs failed");
3241 if (args
->i
& XT_SHOW
)
3242 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3243 else if (args
->i
& XT_SAVE
)
3244 save_certs(t
, certs
, cert_count
, domain
);
3246 free_connection_certs(certs
, cert_count
);
3248 /* we close the socket first for speed */
3251 stop_tls(gsession
, xcred
);
3257 remove_cookie(int index
)
3263 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3265 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3267 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3271 print_cookie("remove cookie", c
);
3272 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3277 soup_cookies_free(cf
);
3283 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3288 body
= g_strdup("");
3291 if (args
->i
& XT_WL_PERSISTENT
) {
3293 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3295 RB_FOREACH(d
, domain_list
, wl
) {
3299 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3305 if (args
->i
& XT_WL_SESSION
) {
3307 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3309 RB_FOREACH(d
, domain_list
, wl
) {
3313 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3318 tmp
= get_html_page(title
, body
, "", 0);
3321 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3323 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3329 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3331 char file
[PATH_MAX
];
3333 char *line
= NULL
, *lt
= NULL
;
3336 char *dom
= NULL
, *dom_save
= NULL
;
3342 if (t
== NULL
|| args
== NULL
)
3345 if (runtime_settings
[0] == '\0')
3348 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3349 if ((f
= fopen(file
, "r+")) == NULL
)
3353 dom
= find_domain(uri
, 1);
3354 if (uri
== NULL
|| dom
== NULL
) {
3355 show_oops(t
, "Can't add domain to %s white list",
3356 js
? "JavaScript" : "cookie");
3360 if (args
->i
& XT_WL_TOPLEVEL
) {
3362 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3363 show_oops(t
, "invalid domain: %s", dom
);
3366 } else if (args
->i
& XT_WL_FQDN
) {
3372 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3375 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3378 if (!strcmp(line
, lt
))
3384 fprintf(f
, "%s\n", lt
);
3389 d
= wl_find(dom_save
, &js_wl
);
3391 settings_add("js_wl", dom_save
);
3392 d
= wl_find(dom_save
, &js_wl
);
3396 d
= wl_find(dom_save
, &c_wl
);
3398 settings_add("cookie_wl", dom_save
);
3399 d
= wl_find(dom_save
, &c_wl
);
3403 /* find and add to persistent jar */
3404 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3405 for (;cf
; cf
= cf
->next
) {
3407 if (!strcmp(dom_save
, ci
->domain
) ||
3408 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3409 c
= soup_cookie_copy(ci
);
3410 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3413 soup_cookies_free(cf
);
3431 js_show_wl(struct tab
*t
, struct karg
*args
)
3433 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3434 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3440 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3442 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3443 wl_show(t
, args
, "Cookie White List", &c_wl
);
3449 cookie_cmd(struct tab
*t
, struct karg
*args
)
3451 if (args
->i
& XT_SHOW
)
3452 wl_show(t
, args
, "Cookie White List", &c_wl
);
3453 else if (args
->i
& XT_WL_TOGGLE
) {
3454 args
->i
|= XT_WL_RELOAD
;
3455 toggle_cwl(t
, args
);
3456 } else if (args
->i
& XT_SAVE
) {
3457 args
->i
|= XT_WL_RELOAD
;
3458 wl_save(t
, args
, 0);
3459 } else if (args
->i
& XT_DELETE
)
3460 show_oops(t
, "'cookie delete' currently unimplemented");
3466 js_cmd(struct tab
*t
, struct karg
*args
)
3468 if (args
->i
& XT_SHOW
)
3469 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3470 else if (args
->i
& XT_SAVE
) {
3471 args
->i
|= XT_WL_RELOAD
;
3472 wl_save(t
, args
, 1);
3473 } else if (args
->i
& XT_WL_TOGGLE
) {
3474 args
->i
|= XT_WL_RELOAD
;
3476 } else if (args
->i
& XT_DELETE
)
3477 show_oops(t
, "'js delete' currently unimplemented");
3483 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3485 js_toggle_cb(t
->js_toggle
, t
);
3491 add_favorite(struct tab
*t
, struct karg
*args
)
3493 char file
[PATH_MAX
];
3496 size_t urilen
, linelen
;
3497 const gchar
*uri
, *title
;
3502 /* don't allow adding of xtp pages to favorites */
3503 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3504 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3508 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3509 if ((f
= fopen(file
, "r+")) == NULL
) {
3510 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3514 title
= webkit_web_view_get_title(t
->wv
);
3520 if (title
== NULL
|| uri
== NULL
) {
3521 show_oops(t
, "can't add page to favorites");
3525 urilen
= strlen(uri
);
3528 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3529 if (feof(f
) || ferror(f
))
3532 if (linelen
== urilen
&& !strcmp(line
, uri
))
3539 fprintf(f
, "\n%s\n%s", title
, uri
);
3545 update_favorite_tabs(NULL
);
3551 navaction(struct tab
*t
, struct karg
*args
)
3553 WebKitWebHistoryItem
*item
;
3555 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3556 t
->tab_id
, args
->i
);
3559 if (args
->i
== XT_NAV_BACK
)
3560 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3562 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3564 return (XT_CB_PASSTHROUGH
);
3565 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3567 return (XT_CB_PASSTHROUGH
);
3572 webkit_web_view_go_back(t
->wv
);
3574 case XT_NAV_FORWARD
:
3575 webkit_web_view_go_forward(t
->wv
);
3578 webkit_web_view_reload(t
->wv
);
3580 case XT_NAV_RELOAD_CACHE
:
3581 webkit_web_view_reload_bypass_cache(t
->wv
);
3584 return (XT_CB_PASSTHROUGH
);
3588 move(struct tab
*t
, struct karg
*args
)
3590 GtkAdjustment
*adjust
;
3591 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3596 case XT_MOVE_BOTTOM
:
3598 case XT_MOVE_PAGEDOWN
:
3599 case XT_MOVE_PAGEUP
:
3600 case XT_MOVE_HALFDOWN
:
3601 case XT_MOVE_HALFUP
:
3602 adjust
= t
->adjust_v
;
3605 adjust
= t
->adjust_h
;
3609 pos
= gtk_adjustment_get_value(adjust
);
3610 ps
= gtk_adjustment_get_page_size(adjust
);
3611 upper
= gtk_adjustment_get_upper(adjust
);
3612 lower
= gtk_adjustment_get_lower(adjust
);
3613 si
= gtk_adjustment_get_step_increment(adjust
);
3614 pi
= gtk_adjustment_get_page_increment(adjust
);
3617 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3618 "max %f si %f pi %f\n",
3619 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3620 pos
, ps
, upper
, lower
, max
, si
, pi
);
3626 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3631 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3633 case XT_MOVE_BOTTOM
:
3634 case XT_MOVE_FARRIGHT
:
3635 gtk_adjustment_set_value(adjust
, max
);
3638 case XT_MOVE_FARLEFT
:
3639 gtk_adjustment_set_value(adjust
, lower
);
3641 case XT_MOVE_PAGEDOWN
:
3643 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3645 case XT_MOVE_PAGEUP
:
3647 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3649 case XT_MOVE_HALFDOWN
:
3651 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3653 case XT_MOVE_HALFUP
:
3655 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3658 return (XT_CB_PASSTHROUGH
);
3661 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3663 return (XT_CB_HANDLED
);
3667 url_set_visibility(void)
3671 TAILQ_FOREACH(t
, &tabs
, entry
) {
3672 if (show_url
== 0) {
3673 gtk_widget_hide(t
->toolbar
);
3676 gtk_widget_show(t
->toolbar
);
3681 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3684 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3686 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3690 statusbar_set_visibility(void)
3694 TAILQ_FOREACH(t
, &tabs
, entry
) {
3695 if (show_statusbar
== 0) {
3696 gtk_widget_hide(t
->statusbar
);
3699 gtk_widget_show(t
->statusbar
);
3704 url_set(struct tab
*t
, int enable_url_entry
)
3709 show_url
= enable_url_entry
;
3711 if (enable_url_entry
) {
3712 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3713 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3714 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3716 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3717 GTK_ENTRY_ICON_PRIMARY
);
3719 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3720 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3721 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3722 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3728 fullscreen(struct tab
*t
, struct karg
*args
)
3730 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3733 return (XT_CB_PASSTHROUGH
);
3735 if (show_url
== 0) {
3743 url_set_visibility();
3744 notebook_tab_set_visibility(notebook
);
3746 return (XT_CB_HANDLED
);
3750 statusaction(struct tab
*t
, struct karg
*args
)
3752 int rv
= XT_CB_HANDLED
;
3754 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3757 return (XT_CB_PASSTHROUGH
);
3760 case XT_STATUSBAR_SHOW
:
3761 if (show_statusbar
== 0) {
3763 statusbar_set_visibility();
3766 case XT_STATUSBAR_HIDE
:
3767 if (show_statusbar
== 1) {
3769 statusbar_set_visibility();
3777 urlaction(struct tab
*t
, struct karg
*args
)
3779 int rv
= XT_CB_HANDLED
;
3781 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3784 return (XT_CB_PASSTHROUGH
);
3788 if (show_url
== 0) {
3790 url_set_visibility();
3794 if (show_url
== 1) {
3796 url_set_visibility();
3804 tabaction(struct tab
*t
, struct karg
*args
)
3806 int rv
= XT_CB_HANDLED
;
3807 char *url
= args
->s
;
3811 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3814 return (XT_CB_PASSTHROUGH
);
3818 if (strlen(url
) > 0)
3819 create_new_tab(url
, NULL
, 1, args
->p
);
3821 create_new_tab(NULL
, NULL
, 1, args
->p
);
3827 TAILQ_FOREACH(tt
, &tabs
, entry
)
3828 if (tt
->tab_id
== args
->p
- 1) {
3834 case XT_TAB_DELQUIT
:
3835 if (gtk_notebook_get_n_pages(notebook
) > 1)
3841 if (strlen(url
) > 0)
3844 rv
= XT_CB_PASSTHROUGH
;
3850 if (show_tabs
== 0) {
3852 notebook_tab_set_visibility(notebook
);
3856 if (show_tabs
== 1) {
3858 notebook_tab_set_visibility(notebook
);
3861 case XT_TAB_UNDO_CLOSE
:
3862 if (undo_count
== 0) {
3863 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3867 u
= TAILQ_FIRST(&undos
);
3868 create_new_tab(u
->uri
, u
, 1, -1);
3870 TAILQ_REMOVE(&undos
, u
, entry
);
3872 /* u->history is freed in create_new_tab() */
3877 rv
= XT_CB_PASSTHROUGH
;
3891 resizetab(struct tab
*t
, struct karg
*args
)
3893 if (t
== NULL
|| args
== NULL
) {
3894 show_oops(NULL
, "resizetab invalid parameters");
3895 return (XT_CB_PASSTHROUGH
);
3898 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3899 t
->tab_id
, args
->i
);
3901 adjustfont_webkit(t
, args
->i
);
3903 return (XT_CB_HANDLED
);
3907 movetab(struct tab
*t
, struct karg
*args
)
3911 if (t
== NULL
|| args
== NULL
) {
3912 show_oops(NULL
, "movetab invalid parameters");
3913 return (XT_CB_PASSTHROUGH
);
3916 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3917 t
->tab_id
, args
->i
);
3919 if (args
->i
>= XT_TAB_INVALID
)
3920 return (XT_CB_PASSTHROUGH
);
3922 if (TAILQ_EMPTY(&tabs
))
3923 return (XT_CB_PASSTHROUGH
);
3925 n
= gtk_notebook_get_n_pages(notebook
);
3926 dest
= gtk_notebook_get_current_page(notebook
);
3931 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
3940 dest
-= args
->p
% n
;
3953 return (XT_CB_PASSTHROUGH
);
3956 if (dest
< 0 || dest
>= n
)
3957 return (XT_CB_PASSTHROUGH
);
3958 if (t
->tab_id
== dest
) {
3959 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3960 return (XT_CB_HANDLED
);
3963 gtk_notebook_set_current_page(notebook
, dest
);
3965 return (XT_CB_HANDLED
);
3971 command(struct tab
*t
, struct karg
*args
)
3973 char *s
= NULL
, *ss
= NULL
;
3977 if (t
== NULL
|| args
== NULL
) {
3978 show_oops(NULL
, "command invalid parameters");
3979 return (XT_CB_PASSTHROUGH
);
3990 if (cmd_prefix
== 0)
3993 ss
= g_strdup_printf(":%d", cmd_prefix
);
4004 case XT_CMD_OPEN_CURRENT
:
4007 case XT_CMD_TABNEW_CURRENT
:
4008 if (!s
) /* FALL THROUGH? */
4010 if ((uri
= get_uri(t
)) != NULL
) {
4011 ss
= g_strdup_printf("%s%s", s
, uri
);
4016 show_oops(t
, "command: invalid opcode %d", args
->i
);
4017 return (XT_CB_PASSTHROUGH
);
4020 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4022 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4023 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4024 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4026 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4027 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4032 return (XT_CB_HANDLED
);
4036 * Return a new string with a download row (in html)
4037 * appended. Old string is freed.
4040 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4043 WebKitDownloadStatus stat
;
4044 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4046 char cur_sz
[FMT_SCALED_STRSIZE
];
4047 char tot_sz
[FMT_SCALED_STRSIZE
];
4050 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4052 /* All actions wil take this form:
4053 * xxxt://class/seskey
4055 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4056 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4058 stat
= webkit_download_get_status(dl
->download
);
4061 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4062 status_html
= g_strdup_printf("Finished");
4063 cmd_html
= g_strdup_printf(
4064 "<a href='%s%d/%d'>Remove</a>",
4065 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4067 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4068 /* gather size info */
4069 progress
= 100 * webkit_download_get_progress(dl
->download
);
4072 webkit_download_get_current_size(dl
->download
), cur_sz
);
4074 webkit_download_get_total_size(dl
->download
), tot_sz
);
4076 status_html
= g_strdup_printf(
4077 "<div style='width: 100%%' align='center'>"
4078 "<div class='progress-outer'>"
4079 "<div class='progress-inner' style='width: %.2f%%'>"
4080 "</div></div></div>"
4081 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4082 progress
, cur_sz
, tot_sz
, progress
);
4084 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4085 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4089 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4090 status_html
= g_strdup_printf("Cancelled");
4091 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4092 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4094 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4095 status_html
= g_strdup_printf("Error!");
4096 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4097 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4099 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4100 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4101 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4102 status_html
= g_strdup_printf("Starting");
4105 show_oops(t
, "%s: unknown download status", __func__
);
4108 new_html
= g_strdup_printf(
4109 "%s\n<tr><td>%s</td><td>%s</td>"
4110 "<td style='text-align:center'>%s</td></tr>\n",
4111 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4112 status_html
, cmd_html
);
4116 g_free(status_html
);
4127 * update all download tabs apart from one. Pass NULL if
4128 * you want to update all.
4131 update_download_tabs(struct tab
*apart_from
)
4134 if (!updating_dl_tabs
) {
4135 updating_dl_tabs
= 1; /* stop infinite recursion */
4136 TAILQ_FOREACH(t
, &tabs
, entry
)
4137 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4138 && (t
!= apart_from
))
4139 xtp_page_dl(t
, NULL
);
4140 updating_dl_tabs
= 0;
4145 * update all cookie tabs apart from one. Pass NULL if
4146 * you want to update all.
4149 update_cookie_tabs(struct tab
*apart_from
)
4152 if (!updating_cl_tabs
) {
4153 updating_cl_tabs
= 1; /* stop infinite recursion */
4154 TAILQ_FOREACH(t
, &tabs
, entry
)
4155 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4156 && (t
!= apart_from
))
4157 xtp_page_cl(t
, NULL
);
4158 updating_cl_tabs
= 0;
4163 * update all history tabs apart from one. Pass NULL if
4164 * you want to update all.
4167 update_history_tabs(struct tab
*apart_from
)
4171 if (!updating_hl_tabs
) {
4172 updating_hl_tabs
= 1; /* stop infinite recursion */
4173 TAILQ_FOREACH(t
, &tabs
, entry
)
4174 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4175 && (t
!= apart_from
))
4176 xtp_page_hl(t
, NULL
);
4177 updating_hl_tabs
= 0;
4181 /* cookie management XTP page */
4183 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4185 char *body
, *page
, *tmp
;
4186 int i
= 1; /* all ids start 1 */
4187 GSList
*sc
, *pc
, *pc_start
;
4189 char *type
, *table_headers
, *last_domain
;
4191 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4194 show_oops(NULL
, "%s invalid parameters", __func__
);
4198 /* Generate a new session key */
4199 if (!updating_cl_tabs
)
4200 generate_xtp_session_key(&cl_session_key
);
4203 table_headers
= g_strdup_printf("<table><tr>"
4206 "<th style='width:200px'>Value</th>"
4210 "<th>HTTP<br />only</th>"
4211 "<th style='width:40px'>Rm</th></tr>\n");
4213 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4214 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4218 last_domain
= strdup("");
4219 for (; sc
; sc
= sc
->next
) {
4222 if (strcmp(last_domain
, c
->domain
) != 0) {
4225 last_domain
= strdup(c
->domain
);
4229 body
= g_strdup_printf("%s</table>"
4231 body
, c
->domain
, table_headers
);
4235 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4236 c
->domain
, table_headers
);
4241 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4242 if (soup_cookie_equal(pc
->data
, c
)) {
4243 type
= "Session + Persistent";
4248 body
= g_strdup_printf(
4251 "<td style='word-wrap:normal'>%s</td>"
4253 " <textarea rows='4'>%s</textarea>"
4259 "<td style='text-align:center'>"
4260 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4267 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4282 soup_cookies_free(sc
);
4283 soup_cookies_free(pc
);
4285 /* small message if there are none */
4287 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4288 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4291 body
= g_strdup_printf("%s</table>", body
);
4294 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4296 g_free(table_headers
);
4297 g_free(last_domain
);
4299 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4300 update_cookie_tabs(t
);
4308 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4310 char *body
, *page
, *tmp
;
4312 int i
= 1; /* all ids start 1 */
4314 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4317 show_oops(NULL
, "%s invalid parameters", __func__
);
4321 /* Generate a new session key */
4322 if (!updating_hl_tabs
)
4323 generate_xtp_session_key(&hl_session_key
);
4326 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4327 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4329 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4331 body
= g_strdup_printf(
4333 "<td><a href='%s'>%s</a></td>"
4335 "<td style='text-align: center'>"
4336 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4337 body
, h
->uri
, h
->uri
, h
->title
,
4338 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4339 XT_XTP_HL_REMOVE
, i
);
4345 /* small message if there are none */
4348 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4349 "colspan='3'>No History</td></tr>\n", body
);
4354 body
= g_strdup_printf("%s</table>", body
);
4357 page
= get_html_page("History", body
, "", TRUE
);
4361 * update all history manager tabs as the xtp session
4362 * key has now changed. No need to update the current tab.
4363 * Already did that above.
4365 update_history_tabs(t
);
4367 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4374 * Generate a web page detailing the status of any downloads
4377 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4379 struct download
*dl
;
4380 char *body
, *page
, *tmp
;
4384 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4387 show_oops(NULL
, "%s invalid parameters", __func__
);
4392 * Generate a new session key for next page instance.
4393 * This only happens for the top level call to xtp_page_dl()
4394 * in which case updating_dl_tabs is 0.
4396 if (!updating_dl_tabs
)
4397 generate_xtp_session_key(&dl_session_key
);
4399 /* header - with refresh so as to update */
4400 if (refresh_interval
>= 1)
4401 ref
= g_strdup_printf(
4402 "<meta http-equiv='refresh' content='%u"
4403 ";url=%s%d/%s/%d' />\n",
4412 body
= g_strdup_printf("<div align='center'>"
4413 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4414 "</p><table><tr><th style='width: 60%%'>"
4415 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4416 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4418 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4419 body
= xtp_page_dl_row(t
, body
, dl
);
4423 /* message if no downloads in list */
4426 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4427 " style='text-align: center'>"
4428 "No downloads</td></tr>\n", body
);
4433 body
= g_strdup_printf("%s</table></div>", body
);
4436 page
= get_html_page("Downloads", body
, ref
, 1);
4441 * update all download manager tabs as the xtp session
4442 * key has now changed. No need to update the current tab.
4443 * Already did that above.
4445 update_download_tabs(t
);
4447 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4454 search(struct tab
*t
, struct karg
*args
)
4458 if (t
== NULL
|| args
== NULL
) {
4459 show_oops(NULL
, "search invalid parameters");
4462 if (t
->search_text
== NULL
) {
4463 if (global_search
== NULL
)
4464 return (XT_CB_PASSTHROUGH
);
4466 t
->search_text
= g_strdup(global_search
);
4467 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4468 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4472 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4473 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4476 case XT_SEARCH_NEXT
:
4477 d
= t
->search_forward
;
4479 case XT_SEARCH_PREV
:
4480 d
= !t
->search_forward
;
4483 return (XT_CB_PASSTHROUGH
);
4486 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4488 return (XT_CB_HANDLED
);
4491 struct settings_args
{
4497 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4500 struct settings_args
*sa
= cb_args
;
4505 if (s
->flags
& XT_SF_RUNTIME
)
4511 *sa
->body
= g_strdup_printf(
4513 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4514 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4526 set(struct tab
*t
, struct karg
*args
)
4528 char *body
, *page
, *tmp
;
4530 struct settings_args sa
;
4532 bzero(&sa
, sizeof sa
);
4536 body
= g_strdup_printf("<div align='center'><table><tr>"
4537 "<th align='left'>Setting</th>"
4538 "<th align='left'>Value</th></tr>\n");
4540 settings_walk(print_setting
, &sa
);
4543 /* small message if there are none */
4546 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4547 "colspan='2'>No settings</td></tr>\n", body
);
4552 body
= g_strdup_printf("%s</table></div>", body
);
4555 page
= get_html_page("Settings", body
, "", 0);
4559 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4563 return (XT_CB_PASSTHROUGH
);
4567 session_save(struct tab
*t
, char *filename
)
4572 if (strlen(filename
) == 0)
4575 if (filename
[0] == '.' || filename
[0] == '/')
4579 if (save_tabs(t
, &a
))
4581 strlcpy(named_session
, filename
, sizeof named_session
);
4589 session_open(struct tab
*t
, char *filename
)
4594 if (strlen(filename
) == 0)
4597 if (filename
[0] == '.' || filename
[0] == '/')
4601 a
.i
= XT_SES_CLOSETABS
;
4602 if (open_tabs(t
, &a
))
4605 strlcpy(named_session
, filename
, sizeof named_session
);
4613 session_delete(struct tab
*t
, char *filename
)
4615 char file
[PATH_MAX
];
4618 if (strlen(filename
) == 0)
4621 if (filename
[0] == '.' || filename
[0] == '/')
4624 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4628 if (!strcmp(filename
, named_session
))
4629 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4630 sizeof named_session
);
4638 session_cmd(struct tab
*t
, struct karg
*args
)
4640 char *filename
= args
->s
;
4645 if (args
->i
& XT_SHOW
)
4646 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4647 XT_SAVED_TABS_FILE
: named_session
);
4648 else if (args
->i
& XT_SAVE
) {
4649 if (session_save(t
, filename
)) {
4650 show_oops(t
, "Can't save session: %s",
4651 filename
? filename
: "INVALID");
4654 } else if (args
->i
& XT_OPEN
) {
4655 if (session_open(t
, filename
)) {
4656 show_oops(t
, "Can't open session: %s",
4657 filename
? filename
: "INVALID");
4660 } else if (args
->i
& XT_DELETE
) {
4661 if (session_delete(t
, filename
)) {
4662 show_oops(t
, "Can't delete session: %s",
4663 filename
? filename
: "INVALID");
4668 return (XT_CB_PASSTHROUGH
);
4672 * Make a hardcopy of the page
4675 print_page(struct tab
*t
, struct karg
*args
)
4677 WebKitWebFrame
*frame
;
4679 GtkPrintOperation
*op
;
4680 GtkPrintOperationAction action
;
4681 GtkPrintOperationResult print_res
;
4682 GError
*g_err
= NULL
;
4683 int marg_l
, marg_r
, marg_t
, marg_b
;
4685 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4687 ps
= gtk_page_setup_new();
4688 op
= gtk_print_operation_new();
4689 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4690 frame
= webkit_web_view_get_main_frame(t
->wv
);
4692 /* the default margins are too small, so we will bump them */
4693 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4694 XT_PRINT_EXTRA_MARGIN
;
4695 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4696 XT_PRINT_EXTRA_MARGIN
;
4697 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4698 XT_PRINT_EXTRA_MARGIN
;
4699 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4700 XT_PRINT_EXTRA_MARGIN
;
4703 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4704 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4705 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4706 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4708 gtk_print_operation_set_default_page_setup(op
, ps
);
4710 /* this appears to free 'op' and 'ps' */
4711 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4713 /* check it worked */
4714 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4715 show_oops(NULL
, "can't print: %s", g_err
->message
);
4716 g_error_free (g_err
);
4724 go_home(struct tab
*t
, struct karg
*args
)
4731 restart(struct tab
*t
, struct karg
*args
)
4735 a
.s
= XT_RESTART_TABS_FILE
;
4737 execvp(start_argv
[0], start_argv
);
4743 #define CTRL GDK_CONTROL_MASK
4744 #define MOD1 GDK_MOD1_MASK
4745 #define SHFT GDK_SHIFT_MASK
4747 /* inherent to GTK not all keys will be caught at all times */
4748 /* XXX sort key bindings */
4749 struct key_binding
{
4754 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4756 { "cookiejar", MOD1
, 0, GDK_j
},
4757 { "downloadmgr", MOD1
, 0, GDK_d
},
4758 { "history", MOD1
, 0, GDK_h
},
4759 { "print", CTRL
, 0, GDK_p
},
4760 { "search", 0, 0, GDK_slash
},
4761 { "searchb", 0, 0, GDK_question
},
4762 { "command", 0, 0, GDK_colon
},
4763 { "qa", CTRL
, 0, GDK_q
},
4764 { "restart", MOD1
, 0, GDK_q
},
4765 { "js toggle", CTRL
, 0, GDK_j
},
4766 { "cookie toggle", MOD1
, 0, GDK_c
},
4767 { "togglesrc", CTRL
, 0, GDK_s
},
4768 { "yankuri", 0, 0, GDK_y
},
4769 { "pasteuricur", 0, 0, GDK_p
},
4770 { "pasteurinew", 0, 0, GDK_P
},
4771 { "toplevel toggle", 0, 0, GDK_F4
},
4772 { "help", 0, 0, GDK_F1
},
4775 { "searchnext", 0, 0, GDK_n
},
4776 { "searchprevious", 0, 0, GDK_N
},
4779 { "focusaddress", 0, 0, GDK_F6
},
4780 { "focussearch", 0, 0, GDK_F7
},
4783 { "hinting", 0, 0, GDK_f
},
4785 /* custom stylesheet */
4786 { "userstyle", 0, 0, GDK_i
},
4789 { "goback", 0, 0, GDK_BackSpace
},
4790 { "goback", MOD1
, 0, GDK_Left
},
4791 { "goforward", SHFT
, 0, GDK_BackSpace
},
4792 { "goforward", MOD1
, 0, GDK_Right
},
4793 { "reload", 0, 0, GDK_F5
},
4794 { "reload", CTRL
, 0, GDK_r
},
4795 { "reloadforce", CTRL
, 0, GDK_R
},
4796 { "reload", CTRL
, 0, GDK_l
},
4797 { "favorites", MOD1
, 1, GDK_f
},
4799 /* vertical movement */
4800 { "scrolldown", 0, 0, GDK_j
},
4801 { "scrolldown", 0, 0, GDK_Down
},
4802 { "scrollup", 0, 0, GDK_Up
},
4803 { "scrollup", 0, 0, GDK_k
},
4804 { "scrollbottom", 0, 0, GDK_G
},
4805 { "scrollbottom", 0, 0, GDK_End
},
4806 { "scrolltop", 0, 0, GDK_Home
},
4807 { "scrolltop", 0, 0, GDK_g
},
4808 { "scrollpagedown", 0, 0, GDK_space
},
4809 { "scrollpagedown", CTRL
, 0, GDK_f
},
4810 { "scrollhalfdown", CTRL
, 0, GDK_d
},
4811 { "scrollpagedown", 0, 0, GDK_Page_Down
},
4812 { "scrollpageup", 0, 0, GDK_Page_Up
},
4813 { "scrollpageup", CTRL
, 0, GDK_b
},
4814 { "scrollhalfup", CTRL
, 0, GDK_u
},
4815 /* horizontal movement */
4816 { "scrollright", 0, 0, GDK_l
},
4817 { "scrollright", 0, 0, GDK_Right
},
4818 { "scrollleft", 0, 0, GDK_Left
},
4819 { "scrollleft", 0, 0, GDK_h
},
4820 { "scrollfarright", 0, 0, GDK_dollar
},
4821 { "scrollfarleft", 0, 0, GDK_0
},
4824 { "tabnew", CTRL
, 0, GDK_t
},
4825 { "999tabnew", CTRL
, 0, GDK_T
},
4826 { "tabclose", CTRL
, 1, GDK_w
},
4827 { "tabundoclose", 0, 0, GDK_U
},
4828 { "tabnext 1", CTRL
, 0, GDK_1
},
4829 { "tabnext 2", CTRL
, 0, GDK_2
},
4830 { "tabnext 3", CTRL
, 0, GDK_3
},
4831 { "tabnext 4", CTRL
, 0, GDK_4
},
4832 { "tabnext 5", CTRL
, 0, GDK_5
},
4833 { "tabnext 6", CTRL
, 0, GDK_6
},
4834 { "tabnext 7", CTRL
, 0, GDK_7
},
4835 { "tabnext 8", CTRL
, 0, GDK_8
},
4836 { "tabnext 9", CTRL
, 0, GDK_9
},
4837 { "tabnext 10", CTRL
, 0, GDK_0
},
4838 { "tabfirst", CTRL
, 0, GDK_less
},
4839 { "tablast", CTRL
, 0, GDK_greater
},
4840 { "tabprevious", CTRL
, 0, GDK_Left
},
4841 { "tabnext", CTRL
, 0, GDK_Right
},
4842 { "focusout", CTRL
, 0, GDK_minus
},
4843 { "focusin", CTRL
, 0, GDK_plus
},
4844 { "focusin", CTRL
, 0, GDK_equal
},
4846 /* command aliases (handy when -S flag is used) */
4847 { "promptopen", 0, 0, GDK_F9
},
4848 { "promptopencurrent", 0, 0, GDK_F10
},
4849 { "prompttabnew", 0, 0, GDK_F11
},
4850 { "prompttabnewcurrent",0, 0, GDK_F12
},
4852 TAILQ_HEAD(keybinding_list
, key_binding
);
4855 walk_kb(struct settings
*s
,
4856 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4858 struct key_binding
*k
;
4861 if (s
== NULL
|| cb
== NULL
) {
4862 show_oops(NULL
, "walk_kb invalid parameters");
4866 TAILQ_FOREACH(k
, &kbl
, entry
) {
4872 if (gdk_keyval_name(k
->key
) == NULL
)
4875 strlcat(str
, k
->cmd
, sizeof str
);
4876 strlcat(str
, ",", sizeof str
);
4878 if (k
->mask
& GDK_SHIFT_MASK
)
4879 strlcat(str
, "S-", sizeof str
);
4880 if (k
->mask
& GDK_CONTROL_MASK
)
4881 strlcat(str
, "C-", sizeof str
);
4882 if (k
->mask
& GDK_MOD1_MASK
)
4883 strlcat(str
, "M1-", sizeof str
);
4884 if (k
->mask
& GDK_MOD2_MASK
)
4885 strlcat(str
, "M2-", sizeof str
);
4886 if (k
->mask
& GDK_MOD3_MASK
)
4887 strlcat(str
, "M3-", sizeof str
);
4888 if (k
->mask
& GDK_MOD4_MASK
)
4889 strlcat(str
, "M4-", sizeof str
);
4890 if (k
->mask
& GDK_MOD5_MASK
)
4891 strlcat(str
, "M5-", sizeof str
);
4893 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4894 cb(s
, str
, cb_args
);
4899 init_keybindings(void)
4902 struct key_binding
*k
;
4904 for (i
= 0; i
< LENGTH(keys
); i
++) {
4905 k
= g_malloc0(sizeof *k
);
4906 k
->cmd
= keys
[i
].cmd
;
4907 k
->mask
= keys
[i
].mask
;
4908 k
->use_in_entry
= keys
[i
].use_in_entry
;
4909 k
->key
= keys
[i
].key
;
4910 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4912 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4913 k
->cmd
? k
->cmd
: "unnamed key");
4918 keybinding_clearall(void)
4920 struct key_binding
*k
, *next
;
4922 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4923 next
= TAILQ_NEXT(k
, entry
);
4927 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4928 k
->cmd
? k
->cmd
: "unnamed key");
4929 TAILQ_REMOVE(&kbl
, k
, entry
);
4935 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
4937 struct key_binding
*k
;
4938 guint keyval
, mask
= 0;
4941 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
4943 /* Keys which are to be used in entry have been prefixed with an
4944 * exclamation mark. */
4948 /* find modifier keys */
4949 if (strstr(key
, "S-"))
4950 mask
|= GDK_SHIFT_MASK
;
4951 if (strstr(key
, "C-"))
4952 mask
|= GDK_CONTROL_MASK
;
4953 if (strstr(key
, "M1-"))
4954 mask
|= GDK_MOD1_MASK
;
4955 if (strstr(key
, "M2-"))
4956 mask
|= GDK_MOD2_MASK
;
4957 if (strstr(key
, "M3-"))
4958 mask
|= GDK_MOD3_MASK
;
4959 if (strstr(key
, "M4-"))
4960 mask
|= GDK_MOD4_MASK
;
4961 if (strstr(key
, "M5-"))
4962 mask
|= GDK_MOD5_MASK
;
4965 for (i
= strlen(key
) - 1; i
> 0; i
--)
4969 /* validate keyname */
4970 keyval
= gdk_keyval_from_name(key
);
4971 if (keyval
== GDK_VoidSymbol
) {
4972 warnx("invalid keybinding name %s", key
);
4975 /* must run this test too, gtk+ doesn't handle 10 for example */
4976 if (gdk_keyval_name(keyval
) == NULL
) {
4977 warnx("invalid keybinding name %s", key
);
4981 /* Remove eventual dupes. */
4982 TAILQ_FOREACH(k
, &kbl
, entry
)
4983 if (k
->key
== keyval
&& k
->mask
== mask
) {
4984 TAILQ_REMOVE(&kbl
, k
, entry
);
4990 k
= g_malloc0(sizeof *k
);
4991 k
->cmd
= g_strdup(cmd
);
4993 k
->use_in_entry
= use_in_entry
;
4996 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5001 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5002 k
->cmd
, gdk_keyval_name(keyval
));
5004 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5010 add_kb(struct settings
*s
, char *entry
)
5014 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5016 /* clearall is special */
5017 if (!strcmp(entry
, "clearall")) {
5018 keybinding_clearall();
5022 kb
= strstr(entry
, ",");
5028 return (keybinding_add(entry
, key
, key
[0] == '!'));
5034 int (*func
)(struct tab
*, struct karg
*);
5038 { "command", 0, command
, ':', 0 },
5039 { "search", 0, command
, '/', 0 },
5040 { "searchb", 0, command
, '?', 0 },
5041 { "togglesrc", 0, toggle_src
, 0, 0 },
5043 /* yanking and pasting */
5044 { "yankuri", 0, yank_uri
, 0, 0 },
5045 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5046 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5047 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5050 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5051 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5054 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5055 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5058 { "hinting", 0, hint
, 0, 0 },
5060 /* custom stylesheet */
5061 { "userstyle", 0, userstyle
, 0, 0 },
5064 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5065 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5066 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5067 { "reloadforce", 0, navaction
, XT_NAV_RELOAD_CACHE
, 0 },
5069 /* vertical movement */
5070 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5071 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5072 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5073 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5074 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5075 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5076 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5077 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5078 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5079 /* horizontal movement */
5080 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5081 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5082 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5083 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5086 { "favorites", 0, xtp_page_fl
, 0, 0 },
5087 { "fav", 0, xtp_page_fl
, 0, 0 },
5088 { "favadd", 0, add_favorite
, 0, 0 },
5090 { "qall", 0, quit
, 0, 0 },
5091 { "quitall", 0, quit
, 0, 0 },
5092 { "w", 0, save_tabs
, 0, 0 },
5093 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5094 { "help", 0, help
, 0, 0 },
5095 { "about", 0, about
, 0, 0 },
5096 { "stats", 0, stats
, 0, 0 },
5097 { "version", 0, about
, 0, 0 },
5100 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5101 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5102 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5103 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5104 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5105 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5106 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5107 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5108 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5109 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5110 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5112 /* cookie command */
5113 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5114 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5115 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5116 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5117 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5118 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5119 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5120 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5121 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5122 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5123 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5125 /* toplevel (domain) command */
5126 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5127 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5130 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5133 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5134 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5135 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5137 { "ca", 0, ca_cmd
, 0, 0 },
5138 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5139 { "dl", 0, xtp_page_dl
, 0, 0 },
5140 { "h", 0, xtp_page_hl
, 0, 0 },
5141 { "history", 0, xtp_page_hl
, 0, 0 },
5142 { "home", 0, go_home
, 0, 0 },
5143 { "restart", 0, restart
, 0, 0 },
5144 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5145 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5146 { "statushide", 0, statusaction
, XT_STATUSBAR_HIDE
, 0 },
5147 { "statusshow", 0, statusaction
, XT_STATUSBAR_SHOW
, 0 },
5149 { "print", 0, print_page
, 0, 0 },
5152 { "focusin", 0, resizetab
, 1, 0 },
5153 { "focusout", 0, resizetab
, -1, 0 },
5154 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5155 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5156 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5157 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5158 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5159 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5160 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5161 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5162 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5163 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5164 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5165 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5166 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5167 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5168 { "buffers", 0, buffers
, 0, 0 },
5169 { "ls", 0, buffers
, 0, 0 },
5170 { "tabs", 0, buffers
, 0, 0 },
5172 /* command aliases (handy when -S flag is used) */
5173 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5174 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5175 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5176 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5179 { "set", 0, set
, 0, 0 },
5180 { "fullscreen", 0, fullscreen
, 0, 0 },
5181 { "f", 0, fullscreen
, 0, 0 },
5184 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5185 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5186 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5187 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5188 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5195 } cmd_status
= {-1, 0};
5198 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5205 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5211 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5213 a
.i
= XT_NAV_FORWARD
;
5223 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5225 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5227 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5234 * cancel, remove, etc. downloads
5237 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5239 struct download find
, *d
= NULL
;
5241 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5243 /* some commands require a valid download id */
5244 if (cmd
!= XT_XTP_DL_LIST
) {
5245 /* lookup download in question */
5247 d
= RB_FIND(download_list
, &downloads
, &find
);
5250 show_oops(t
, "%s: no such download", __func__
);
5255 /* decide what to do */
5257 case XT_XTP_DL_CANCEL
:
5258 webkit_download_cancel(d
->download
);
5260 case XT_XTP_DL_REMOVE
:
5261 webkit_download_cancel(d
->download
); /* just incase */
5262 g_object_unref(d
->download
);
5263 RB_REMOVE(download_list
, &downloads
, d
);
5265 case XT_XTP_DL_LIST
:
5269 show_oops(t
, "%s: unknown command", __func__
);
5272 xtp_page_dl(t
, NULL
);
5276 * Actions on history, only does one thing for now, but
5277 * we provide the function for future actions
5280 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5282 struct history
*h
, *next
;
5286 case XT_XTP_HL_REMOVE
:
5287 /* walk backwards, as listed in reverse */
5288 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5289 next
= RB_PREV(history_list
, &hl
, h
);
5291 RB_REMOVE(history_list
, &hl
, h
);
5292 g_free((gpointer
) h
->title
);
5293 g_free((gpointer
) h
->uri
);
5300 case XT_XTP_HL_LIST
:
5301 /* Nothing - just xtp_page_hl() below */
5304 show_oops(t
, "%s: unknown command", __func__
);
5308 xtp_page_hl(t
, NULL
);
5311 /* remove a favorite */
5313 remove_favorite(struct tab
*t
, int index
)
5315 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5316 char *new_favs
, *tmp
;
5321 /* open favorites */
5322 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5324 if ((f
= fopen(file
, "r")) == NULL
) {
5325 show_oops(t
, "%s: can't open favorites: %s",
5326 __func__
, strerror(errno
));
5330 /* build a string which will become the new favroites file */
5331 new_favs
= g_strdup("");
5334 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5335 if (feof(f
) || ferror(f
))
5337 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5344 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5345 if (feof(f
) || ferror(f
)) {
5346 show_oops(t
, "%s: can't parse favorites %s",
5347 __func__
, strerror(errno
));
5352 /* as long as this isn't the one we are deleting add to file */
5355 new_favs
= g_strdup_printf("%s%s\n%s\n",
5356 new_favs
, title
, uri
);
5368 /* write back new favorites file */
5369 if ((f
= fopen(file
, "w")) == NULL
) {
5370 show_oops(t
, "%s: can't open favorites: %s",
5371 __func__
, strerror(errno
));
5375 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5388 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5391 case XT_XTP_FL_LIST
:
5392 /* nothing, just the below call to xtp_page_fl() */
5394 case XT_XTP_FL_REMOVE
:
5395 remove_favorite(t
, arg
);
5398 show_oops(t
, "%s: invalid favorites command", __func__
);
5402 xtp_page_fl(t
, NULL
);
5406 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5409 case XT_XTP_CL_LIST
:
5410 /* nothing, just xtp_page_cl() */
5412 case XT_XTP_CL_REMOVE
:
5416 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5420 xtp_page_cl(t
, NULL
);
5423 /* link an XTP class to it's session key and handler function */
5424 struct xtp_despatch
{
5427 void (*handle_func
)(struct tab
*, uint8_t, int);
5430 struct xtp_despatch xtp_despatches
[] = {
5431 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5432 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5433 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5434 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5435 { XT_XTP_INVALID
, NULL
, NULL
}
5439 * is the url xtp protocol? (xxxt://)
5440 * if so, parse and despatch correct bahvior
5443 parse_xtp_url(struct tab
*t
, const char *url
)
5445 char *dup
= NULL
, *p
, *last
;
5446 uint8_t n_tokens
= 0;
5447 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5448 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5453 * tokens array meaning:
5455 * tokens[1] = session key
5456 * tokens[2] = action
5457 * tokens[3] = optional argument
5460 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5462 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5465 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5467 /* split out the url */
5468 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5469 (p
= strtok_r(NULL
, "/", &last
))) {
5471 tokens
[n_tokens
++] = p
;
5474 /* should be atleast three fields 'class/seskey/command/arg' */
5478 dsp
= xtp_despatches
;
5479 req_class
= atoi(tokens
[0]);
5480 while (dsp
->xtp_class
) {
5481 if (dsp
->xtp_class
== req_class
) {
5488 /* did we find one atall? */
5489 if (dsp_match
== NULL
) {
5490 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5494 /* check session key and call despatch function */
5495 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5496 ret
= TRUE
; /* all is well, this was a valid xtp request */
5497 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5510 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5512 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5514 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5517 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5522 show_oops(t
, "activate_uri_entry_cb no uri");
5526 uri
+= strspn(uri
, "\t ");
5528 /* if xxxt:// treat specially */
5529 if (parse_xtp_url(t
, uri
))
5532 /* otherwise continue to load page normally */
5533 load_uri(t
, (gchar
*)uri
);
5538 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5540 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5541 char *newuri
= NULL
;
5544 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5547 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
5551 if (search_string
== NULL
) {
5552 show_oops(t
, "no search_string");
5556 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5557 newuri
= g_strdup_printf(search_string
, enc_search
);
5560 webkit_web_view_load_uri(t
->wv
, newuri
);
5568 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5570 struct domain
*d
= NULL
;
5573 if (uri
== NULL
|| t
== NULL
)
5576 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5581 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5582 es
? "enable" : "disable", uri
);
5584 g_object_set(G_OBJECT(t
->settings
),
5585 "enable-scripts", es
, (char *)NULL
);
5586 g_object_set(G_OBJECT(t
->settings
),
5587 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5588 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5590 button_set_stockid(t
->js_toggle
,
5591 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5595 show_ca_status(struct tab
*t
, const char *uri
)
5597 WebKitWebFrame
*frame
;
5598 WebKitWebDataSource
*source
;
5599 WebKitNetworkRequest
*request
;
5600 SoupMessage
*message
;
5602 gchar
*col_str
= XT_COLOR_WHITE
;
5605 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5606 ssl_strict_certs
, ssl_ca_file
, uri
);
5610 if (ssl_ca_file
== NULL
) {
5611 if (g_str_has_prefix(uri
, "http://"))
5613 if (g_str_has_prefix(uri
, "https://")) {
5614 col_str
= XT_COLOR_RED
;
5619 if (g_str_has_prefix(uri
, "http://") ||
5620 !g_str_has_prefix(uri
, "https://"))
5623 frame
= webkit_web_view_get_main_frame(t
->wv
);
5624 source
= webkit_web_frame_get_data_source(frame
);
5625 request
= webkit_web_data_source_get_request(source
);
5626 message
= webkit_network_request_get_message(request
);
5628 if (message
&& (soup_message_get_flags(message
) &
5629 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5630 col_str
= XT_COLOR_GREEN
;
5633 r
= load_compare_cert(t
, NULL
);
5635 col_str
= XT_COLOR_BLUE
;
5637 col_str
= XT_COLOR_YELLOW
;
5639 col_str
= XT_COLOR_RED
;
5644 gdk_color_parse(col_str
, &color
);
5645 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5647 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
5648 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5650 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5651 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5654 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5656 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5657 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5664 free_favicon(struct tab
*t
)
5666 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
5667 __func__
, t
->icon_download
, t
->icon_request
);
5669 if (t
->icon_request
)
5670 g_object_unref(t
->icon_request
);
5671 if (t
->icon_dest_uri
)
5672 g_free(t
->icon_dest_uri
);
5674 t
->icon_request
= NULL
;
5675 t
->icon_dest_uri
= NULL
;
5679 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5681 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5682 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5684 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5685 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5687 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5688 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5692 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
5694 GdkPixbuf
*pb_scaled
;
5696 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
5697 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16, GDK_INTERP_BILINEAR
);
5701 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5702 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5704 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5705 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5707 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5708 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5710 if (pb_scaled
!= pb
)
5711 g_object_unref(pb_scaled
);
5715 xt_icon_from_file(struct tab
*t
, char *file
)
5719 if (g_str_has_prefix(file
, "file://"))
5720 file
+= strlen("file://");
5722 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5724 xt_icon_from_pixbuf(t
, pb
);
5727 xt_icon_from_name(t
, "text-html");
5731 is_valid_icon(char *file
)
5734 const char *mime_type
;
5738 gf
= g_file_new_for_path(file
);
5739 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5741 mime_type
= g_file_info_get_content_type(fi
);
5742 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5743 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5744 g_strcmp0(mime_type
, "image/png") == 0 ||
5745 g_strcmp0(mime_type
, "image/gif") == 0 ||
5746 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5754 set_favicon_from_file(struct tab
*t
, char *file
)
5758 if (t
== NULL
|| file
== NULL
)
5761 if (g_str_has_prefix(file
, "file://"))
5762 file
+= strlen("file://");
5763 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5765 if (!stat(file
, &sb
)) {
5766 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5767 /* corrupt icon so trash it */
5768 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5771 /* no need to set icon to default here */
5775 xt_icon_from_file(t
, file
);
5779 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5782 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5783 struct tab
*tt
= NULL
, *t
= NULL
;
5786 * find the webview instead of passing in the tab as it could have been
5787 * deleted from underneath us.
5789 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5798 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5799 __func__
, t
->tab_id
, status
);
5802 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5804 t
->icon_download
= NULL
;
5807 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5810 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5813 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5815 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5816 __func__
, t
->tab_id
);
5817 t
->icon_download
= NULL
;
5820 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5823 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5824 __func__
, t
->icon_dest_uri
);
5825 set_favicon_from_file(t
, t
->icon_dest_uri
);
5826 /* these will be freed post callback */
5827 t
->icon_request
= NULL
;
5828 t
->icon_download
= NULL
;
5836 abort_favicon_download(struct tab
*t
)
5838 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5840 if (!WEBKIT_CHECK_VERSION(1, 4, 0)) {
5841 if (t
->icon_download
) {
5842 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5843 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5844 webkit_download_cancel(t
->icon_download
);
5845 t
->icon_download
= NULL
;
5850 xt_icon_from_name(t
, "text-html");
5854 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5857 gchar
*name_hash
, file
[PATH_MAX
];
5860 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
5862 if (uri
== NULL
|| t
== NULL
)
5865 if (WEBKIT_CHECK_VERSION(1, 4, 0)) {
5866 /* take icon from WebKitIconDatabase */
5867 pb
= webkit_web_view_get_icon_pixbuf(wv
);
5869 xt_icon_from_pixbuf(t
, pb
);
5872 xt_icon_from_name(t
, "text-html");
5874 } else if (WEBKIT_CHECK_VERSION(1, 1, 18)) {
5875 /* download icon to cache dir */
5876 if (t
->icon_request
) {
5877 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5881 /* check to see if we got the icon in cache */
5882 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5883 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5886 if (!stat(file
, &sb
)) {
5887 if (sb
.st_size
> 0) {
5888 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5890 set_favicon_from_file(t
, file
);
5894 /* corrupt icon so trash it */
5895 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5900 /* create download for icon */
5901 t
->icon_request
= webkit_network_request_new(uri
);
5902 if (t
->icon_request
== NULL
) {
5903 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5908 t
->icon_download
= webkit_download_new(t
->icon_request
);
5909 if (t
->icon_download
== NULL
)
5912 /* we have to free icon_dest_uri later */
5913 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5914 webkit_download_set_destination_uri(t
->icon_download
,
5917 if (webkit_download_get_status(t
->icon_download
) ==
5918 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5919 g_object_unref(t
->icon_request
);
5920 g_free(t
->icon_dest_uri
);
5921 t
->icon_request
= NULL
;
5922 t
->icon_dest_uri
= NULL
;
5926 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5927 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5929 webkit_download_start(t
->icon_download
);
5934 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5936 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5937 struct history
*h
, find
;
5938 const gchar
*s_loading
;
5941 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
5942 webkit_web_view_get_load_status(wview
), get_uri(t
) ? get_uri(t
) : "NOTHING");
5945 show_oops(NULL
, "notify_load_status_cb invalid parameters");
5949 switch (webkit_web_view_get_load_status(wview
)) {
5950 case WEBKIT_LOAD_PROVISIONAL
:
5952 abort_favicon_download(t
);
5953 #if GTK_CHECK_VERSION(2, 20, 0)
5954 gtk_widget_show(t
->spinner
);
5955 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5957 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5959 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5961 /* take focus if we are visible */
5967 case WEBKIT_LOAD_COMMITTED
:
5969 if ((uri
= get_uri(t
)) != NULL
) {
5970 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5976 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
5979 /* check if js white listing is enabled */
5980 if (enable_js_whitelist
) {
5982 check_and_set_js(uri
, t
);
5988 show_ca_status(t
, uri
);
5990 /* we know enough to autosave the session */
5991 if (session_autosave
) {
5997 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6001 case WEBKIT_LOAD_FINISHED
:
6007 if (!strncmp(uri
, "http://", strlen("http://")) ||
6008 !strncmp(uri
, "https://", strlen("https://")) ||
6009 !strncmp(uri
, "file://", strlen("file://"))) {
6011 h
= RB_FIND(history_list
, &hl
, &find
);
6013 title
= webkit_web_view_get_title(wview
);
6014 set
= title
? title
: uri
;
6015 h
= g_malloc(sizeof *h
);
6016 h
->uri
= g_strdup(uri
);
6017 h
->title
= g_strdup(set
);
6018 RB_INSERT(history_list
, &hl
, h
);
6019 completion_add_uri(h
->uri
);
6020 update_history_tabs(NULL
);
6024 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6025 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6026 case WEBKIT_LOAD_FAILED
:
6029 #if GTK_CHECK_VERSION(2, 20, 0)
6030 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6031 gtk_widget_hide(t
->spinner
);
6033 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
6034 if (s_loading
&& !strcmp(s_loading
, "Loading"))
6035 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6037 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6042 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6044 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6045 webkit_web_view_can_go_back(wview
));
6047 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6048 webkit_web_view_can_go_forward(wview
));
6052 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6054 const gchar
*set
= NULL
, *title
= NULL
;
6056 title
= webkit_web_view_get_title(wview
);
6057 set
= title
? title
: get_uri(t
);
6059 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
6060 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
6062 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6063 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6068 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6070 run_script(t
, JS_HINTING
);
6074 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6076 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6077 progress
== 100 ? 0 : (double)progress
/ 100);
6078 if (show_url
== 0) {
6079 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
6080 progress
== 100 ? 0 : (double)progress
/ 100);
6085 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6086 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6087 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6090 WebKitWebNavigationReason reason
;
6091 struct domain
*d
= NULL
;
6094 show_oops(NULL
, "webview_npd_cb invalid parameters");
6098 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6100 webkit_network_request_get_uri(request
));
6102 uri
= (char *)webkit_network_request_get_uri(request
);
6104 /* if this is an xtp url, we don't load anything else */
6105 if (parse_xtp_url(t
, uri
))
6108 if (t
->ctrl_click
) {
6110 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6111 webkit_web_policy_decision_ignore(pd
);
6112 return (TRUE
); /* we made the decission */
6116 * This is a little hairy but it comes down to this:
6117 * when we run in whitelist mode we have to assist the browser in
6118 * opening the URL that it would have opened in a new tab.
6120 reason
= webkit_web_navigation_action_get_reason(na
);
6121 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6122 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6123 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6124 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6126 webkit_web_policy_decision_use(pd
);
6127 return (TRUE
); /* we made the decision */
6134 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6137 struct domain
*d
= NULL
;
6139 WebKitWebView
*webview
= NULL
;
6141 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6142 webkit_web_view_get_uri(wv
));
6145 /* open in current tab */
6147 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6148 uri
= webkit_web_view_get_uri(wv
);
6149 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6152 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6154 } else if (enable_scripts
== 1) {
6155 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6163 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6166 struct domain
*d
= NULL
;
6168 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6170 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6171 uri
= webkit_web_view_get_uri(wv
);
6172 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6176 } else if (enable_scripts
== 1)
6183 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6185 /* we can not eat the event without throwing gtk off so defer it */
6187 /* catch middle click */
6188 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6193 /* catch ctrl click */
6194 if (e
->type
== GDK_BUTTON_RELEASE
&&
6195 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6200 return (XT_CB_PASSTHROUGH
);
6204 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6206 struct mime_type
*m
;
6208 m
= find_mime_type(mime_type
);
6216 show_oops(t
, "can't fork mime handler");
6225 execlp(m
->mt_action
, m
->mt_action
,
6226 webkit_network_request_get_uri(request
), (void *)NULL
);
6235 get_mime_type(char *file
)
6237 const char *mime_type
;
6241 if (g_str_has_prefix(file
, "file://"))
6242 file
+= strlen("file://");
6244 gf
= g_file_new_for_path(file
);
6245 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6247 mime_type
= g_file_info_get_content_type(fi
);
6255 run_download_mimehandler(char *mime_type
, char *file
)
6257 struct mime_type
*m
;
6259 m
= find_mime_type(mime_type
);
6265 show_oops(NULL
, "can't fork download mime handler");
6275 if (g_str_has_prefix(file
, "file://"))
6276 file
+= strlen("file://");
6277 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6286 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6289 WebKitDownloadStatus status
;
6290 const gchar
*file
= NULL
, *mime
= NULL
;
6292 if (download
== NULL
)
6294 status
= webkit_download_get_status(download
);
6295 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6298 file
= webkit_download_get_destination_uri(download
);
6301 mime
= get_mime_type((char *)file
);
6305 run_download_mimehandler((char *)mime
, (char *)file
);
6309 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6310 WebKitNetworkRequest
*request
, char *mime_type
,
6311 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6314 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6318 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6319 t
->tab_id
, mime_type
);
6321 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6322 webkit_web_policy_decision_ignore(decision
);
6327 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6328 webkit_web_policy_decision_download(decision
);
6336 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6340 const gchar
*suggested_name
;
6341 gchar
*filename
= NULL
;
6343 struct download
*download_entry
;
6346 if (wk_download
== NULL
|| t
== NULL
) {
6347 show_oops(NULL
, "%s invalid parameters", __func__
);
6351 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6352 if (suggested_name
== NULL
)
6353 return (FALSE
); /* abort download */
6364 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6366 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6367 filename
: suggested_name
);
6369 } while (!stat(uri
+ strlen("file://"), &sb
));
6371 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6372 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6374 webkit_download_set_destination_uri(wk_download
, uri
);
6376 if (webkit_download_get_status(wk_download
) ==
6377 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6378 show_oops(t
, "%s: download failed to start", __func__
);
6380 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6382 /* connect "download first" mime handler */
6383 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6384 G_CALLBACK(download_status_changed_cb
), NULL
);
6386 download_entry
= g_malloc(sizeof(struct download
));
6387 download_entry
->download
= wk_download
;
6388 download_entry
->tab
= t
;
6389 download_entry
->id
= next_download_id
++;
6390 RB_INSERT(download_list
, &downloads
, download_entry
);
6391 /* get from history */
6392 g_object_ref(wk_download
);
6393 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6394 show_oops(t
, "Download of '%s' started...",
6395 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6404 /* sync other download manager tabs */
6405 update_download_tabs(NULL
);
6408 * NOTE: never redirect/render the current tab before this
6409 * function returns. This will cause the download to never start.
6411 return (ret
); /* start download */
6415 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6417 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6420 show_oops(NULL
, "webview_hover_cb");
6425 set_status(t
, uri
, XT_STATUS_LINK
);
6428 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6433 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6435 struct key_binding
*k
;
6437 TAILQ_FOREACH(k
, &kbl
, entry
)
6438 if (e
->keyval
== k
->key
&& (entry
? k
->use_in_entry
: 1)) {
6440 if ((e
->state
& (CTRL
| MOD1
)) == 0)
6441 return (cmd_execute(t
, k
->cmd
));
6442 } else if ((e
->state
& k
->mask
) == k
->mask
) {
6443 return (cmd_execute(t
, k
->cmd
));
6447 return (XT_CB_PASSTHROUGH
);
6451 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6453 char s
[2], buf
[128];
6454 const char *errstr
= NULL
;
6457 /* don't use w directly; use t->whatever instead */
6460 show_oops(NULL
, "wv_keypress_after_cb");
6461 return (XT_CB_PASSTHROUGH
);
6464 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6465 e
->keyval
, e
->state
, t
);
6469 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6471 return (XT_CB_HANDLED
);
6475 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6476 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6478 /* we have a string */
6480 /* we have a number */
6481 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6489 /* XXX unfuck this */
6490 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6491 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6492 /* last input was numerical */
6494 l
= strlen(t
->hint_num
);
6501 t
->hint_num
[l
] = '\0';
6505 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6506 /* last input was alphanumerical */
6508 l
= strlen(t
->hint_buf
);
6515 t
->hint_buf
[l
] = '\0';
6525 /* numerical input */
6526 if (CLEAN(e
->state
) == 0 &&
6527 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6528 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6529 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6530 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6533 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6535 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6538 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6540 t
->hint_mode
= XT_HINT_NUMERICAL
;
6544 /* empty the counter buffer */
6545 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6546 return (XT_CB_HANDLED
);
6549 /* alphanumerical input */
6551 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6552 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6553 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6554 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6555 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6556 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6557 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6560 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6563 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6565 t
->hint_mode
= XT_HINT_ALPHANUM
;
6568 /* empty the counter buffer */
6569 bzero(t
->hint_num
, sizeof t
->hint_num
);
6570 return (XT_CB_HANDLED
);
6573 return (XT_CB_HANDLED
);
6576 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6577 if (CLEAN(e
->state
) == 0 && isdigit(s
[0])) {
6578 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
6582 return (handle_keypress(t
, e
, 0));
6586 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6590 /* Hide buffers, if they are visible, with escape. */
6591 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
6592 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6593 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6595 return (XT_CB_HANDLED
);
6598 return (XT_CB_PASSTHROUGH
);
6602 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6604 const gchar
*c
= gtk_entry_get_text(w
);
6608 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6609 e
->keyval
, e
->state
, t
);
6612 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
6613 return (XT_CB_PASSTHROUGH
);
6616 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6617 e
->keyval
, e
->state
, t
);
6621 if (strlen(c
) == 1) {
6622 webkit_web_view_unmark_text_matches(t
->wv
);
6628 else if (c
[0] == '?')
6634 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6636 /* not found, mark red */
6637 gdk_color_parse(XT_COLOR_RED
, &color
);
6638 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6639 /* unmark and remove selection */
6640 webkit_web_view_unmark_text_matches(t
->wv
);
6641 /* my kingdom for a way to unselect text in webview */
6643 /* found, highlight all */
6644 webkit_web_view_unmark_text_matches(t
->wv
);
6645 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6646 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6647 gdk_color_parse(XT_COLOR_WHITE
, &color
);
6648 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6651 return (XT_CB_PASSTHROUGH
);
6655 match_uri(const gchar
*uri
, const gchar
*key
) {
6658 gboolean match
= FALSE
;
6662 if (!strncmp(key
, uri
, len
))
6665 voffset
= strstr(uri
, "/") + 2;
6666 if (!strncmp(key
, voffset
, len
))
6668 else if (g_str_has_prefix(voffset
, "www.")) {
6669 voffset
= voffset
+ strlen("www.");
6670 if (!strncmp(key
, voffset
, len
))
6679 cmd_getlist(int id
, char *key
)
6684 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
6685 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
6686 if (match_uri(h
->uri
, key
)) {
6687 cmd_status
.list
[c
] = (char *)h
->uri
;
6696 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
6698 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
6699 if(cmds
[i
].level
< dep
)
6701 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
, strlen(key
)))
6702 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
6710 cmd_getnext(int dir
)
6712 cmd_status
.index
+= dir
;
6714 if (cmd_status
.index
< 0)
6715 cmd_status
.index
= cmd_status
.len
- 1;
6716 else if (cmd_status
.index
>= cmd_status
.len
)
6717 cmd_status
.index
= 0;
6719 return cmd_status
.list
[cmd_status
.index
];
6723 cmd_tokenize(char *s
, char *tokens
[])
6727 size_t len
= strlen(s
);
6728 bool blank
= len
== 0 || (len
> 0 && s
[len
-1] == ' ');
6730 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3; tok
= strtok_r(NULL
, " ", &last
), i
++)
6740 cmd_complete(struct tab
*t
, char *str
, int dir
)
6742 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6743 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1, matchcount
= 0;
6744 char *tok
, *match
, *s
= g_strdup(str
);
6746 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
6749 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
6752 for (i
= 0; isdigit(s
[i
]); i
++)
6755 for (; isspace(s
[i
]); i
++)
6760 levels
= cmd_tokenize(s
, tokens
);
6762 for (i
= 0; i
< levels
- 1; i
++) {
6765 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6766 if (cmds
[j
].level
< dep
)
6768 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, strlen(tok
))) {
6771 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
6778 if (matchcount
== 1) {
6779 strlcat(res
, tok
, sizeof res
);
6780 strlcat(res
, " ", sizeof res
);
6790 if (cmd_status
.index
== -1)
6791 cmd_getlist(parent
, tokens
[i
]);
6793 if (cmd_status
.len
> 0) {
6794 match
= cmd_getnext(dir
);
6795 strlcat(res
, match
, sizeof res
);
6796 gtk_entry_set_text(w
, res
);
6797 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6804 cmd_execute(struct tab
*t
, char *str
)
6806 struct cmd
*cmd
= NULL
;
6807 char *tok
, *last
, *s
= g_strdup(str
), *sc
, prefixstr
[4];
6808 int j
, len
, c
= 0, dep
= 0, matchcount
= 0, prefix
= -1;
6809 struct karg arg
= {0, NULL
, -1};
6810 int rv
= XT_CB_PASSTHROUGH
;
6815 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
6821 while (isspace(s
[0]))
6824 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
6825 prefix
= atoi(prefixstr
);
6829 for (tok
= strtok_r(s
, " ", &last
); tok
;
6830 tok
= strtok_r(NULL
, " ", &last
)) {
6832 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6833 if (cmds
[j
].level
< dep
)
6835 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1: strlen(tok
);
6836 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, len
)) {
6840 if (len
== strlen(cmds
[j
].cmd
)) {
6846 if (matchcount
== 1) {
6851 show_oops(t
, "Invalid command: %s", str
);
6860 else if (cmd_prefix
> 0)
6863 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.p
> -1) {
6864 show_oops(t
, "No prefix allowed: %s", str
);
6868 arg
.s
= last
? g_strdup(last
) : g_strdup("");
6869 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
6870 arg
.p
= atoi(arg
.s
);
6873 show_oops(t
, "Zero count");
6875 show_oops(t
, "Trailing characters");
6880 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n", __func__
, arg
.p
, arg
.s
);
6896 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6899 show_oops(NULL
, "entry_key_cb invalid parameters");
6900 return (XT_CB_PASSTHROUGH
);
6903 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6904 e
->keyval
, e
->state
, t
);
6908 if (e
->keyval
== GDK_Escape
) {
6909 /* don't use focus_webview(t) because we want to type :cmds */
6910 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6913 return (handle_keypress(t
, e
, 1));
6917 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6919 int rv
= XT_CB_HANDLED
;
6920 const gchar
*c
= gtk_entry_get_text(w
);
6923 show_oops(NULL
, "cmd_keypress_cb parameters");
6924 return (XT_CB_PASSTHROUGH
);
6927 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6928 e
->keyval
, e
->state
, t
);
6932 e
->keyval
= GDK_Escape
;
6933 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6934 e
->keyval
= GDK_Escape
;
6936 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&& e
->keyval
!= GDK_ISO_Left_Tab
)
6937 cmd_status
.index
= -1;
6939 switch (e
->keyval
) {
6942 cmd_complete(t
, (char *)&c
[1], 1);
6944 case GDK_ISO_Left_Tab
:
6946 cmd_complete(t
, (char *)&c
[1], -1);
6950 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
6958 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
6959 webkit_web_view_unmark_text_matches(t
->wv
);
6963 rv
= XT_CB_PASSTHROUGH
;
6969 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6972 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
6973 return (XT_CB_PASSTHROUGH
);
6975 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
6980 if (show_url
== 0 || t
->focus_wv
)
6983 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6985 return (XT_CB_PASSTHROUGH
);
6989 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6992 const gchar
*c
= gtk_entry_get_text(entry
);
6995 show_oops(NULL
, "cmd_activate_cb invalid parameters");
6999 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7006 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7012 if (c
[0] == '/' || c
[0] == '?') {
7013 if (t
->search_text
) {
7014 g_free(t
->search_text
);
7015 t
->search_text
= NULL
;
7018 t
->search_text
= g_strdup(s
);
7020 g_free(global_search
);
7021 global_search
= g_strdup(s
);
7022 t
->search_forward
= c
[0] == '/';
7034 backward_cb(GtkWidget
*w
, struct tab
*t
)
7039 show_oops(NULL
, "backward_cb invalid parameters");
7043 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
7050 forward_cb(GtkWidget
*w
, struct tab
*t
)
7055 show_oops(NULL
, "forward_cb invalid parameters");
7059 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
7061 a
.i
= XT_NAV_FORWARD
;
7066 home_cb(GtkWidget
*w
, struct tab
*t
)
7069 show_oops(NULL
, "home_cb invalid parameters");
7073 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
7079 stop_cb(GtkWidget
*w
, struct tab
*t
)
7081 WebKitWebFrame
*frame
;
7084 show_oops(NULL
, "stop_cb invalid parameters");
7088 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
7090 frame
= webkit_web_view_get_main_frame(t
->wv
);
7091 if (frame
== NULL
) {
7092 show_oops(t
, "stop_cb: no frame");
7096 webkit_web_frame_stop_loading(frame
);
7097 abort_favicon_download(t
);
7101 setup_webkit(struct tab
*t
)
7103 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
7104 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
7105 FALSE
, (char *)NULL
);
7107 warnx("webkit does not have \"enable-dns-prefetching\" property");
7108 g_object_set(G_OBJECT(t
->settings
),
7109 "user-agent", t
->user_agent
, (char *)NULL
);
7110 g_object_set(G_OBJECT(t
->settings
),
7111 "enable-scripts", enable_scripts
, (char *)NULL
);
7112 g_object_set(G_OBJECT(t
->settings
),
7113 "enable-plugins", enable_plugins
, (char *)NULL
);
7114 g_object_set(G_OBJECT(t
->settings
),
7115 "javascript-can-open-windows-automatically", enable_scripts
, (char *)NULL
);
7116 g_object_set(G_OBJECT(t
->settings
),
7117 "enable-html5-database", FALSE
, (char *)NULL
);
7118 g_object_set(G_OBJECT(t
->settings
),
7119 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
7120 g_object_set(G_OBJECT(t
->settings
),
7121 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
7122 g_object_set(G_OBJECT(t
->settings
),
7123 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
7124 g_object_set(G_OBJECT(t
->wv
),
7125 "full-content-zoom", TRUE
, (char *)NULL
);
7126 adjustfont_webkit(t
, XT_FONT_SET
);
7128 webkit_web_view_set_settings(t
->wv
, t
->settings
);
7132 create_browser(struct tab
*t
)
7138 show_oops(NULL
, "create_browser invalid parameters");
7142 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7143 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7144 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7145 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7147 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7148 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7149 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7151 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7152 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7155 t
->settings
= webkit_web_settings_new();
7157 if (user_agent
== NULL
) {
7158 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7160 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7163 t
->user_agent
= g_strdup(user_agent
);
7165 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7177 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7178 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7179 gtk_widget_set_name(w
, "xxxterm");
7180 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7181 g_signal_connect(G_OBJECT(w
), "delete_event",
7182 G_CALLBACK (gtk_main_quit
), NULL
);
7188 create_kiosk_toolbar(struct tab
*t
)
7190 GtkWidget
*toolbar
= NULL
, *b
;
7192 b
= gtk_hbox_new(FALSE
, 0);
7194 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7196 /* backward button */
7197 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7198 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7199 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7200 G_CALLBACK(backward_cb
), t
);
7201 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7203 /* forward button */
7204 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7205 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7206 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7207 G_CALLBACK(forward_cb
), t
);
7208 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7211 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7212 gtk_widget_set_sensitive(t
->gohome
, true);
7213 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7214 G_CALLBACK(home_cb
), t
);
7215 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7217 /* create widgets but don't use them */
7218 t
->uri_entry
= gtk_entry_new();
7219 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7220 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7221 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7227 create_toolbar(struct tab
*t
)
7229 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7231 b
= gtk_hbox_new(FALSE
, 0);
7233 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7236 /* backward button */
7237 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7238 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7239 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7240 G_CALLBACK(backward_cb
), t
);
7241 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7243 /* forward button */
7244 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7245 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7246 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7247 G_CALLBACK(forward_cb
), t
);
7248 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7252 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7253 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7254 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7255 G_CALLBACK(stop_cb
), t
);
7256 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7260 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7261 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7262 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7263 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7264 G_CALLBACK(js_toggle_cb
), t
);
7265 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7268 t
->uri_entry
= gtk_entry_new();
7269 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7270 G_CALLBACK(activate_uri_entry_cb
), t
);
7271 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7272 G_CALLBACK(entry_key_cb
), t
);
7274 eb1
= gtk_hbox_new(FALSE
, 0);
7275 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7276 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7277 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7280 if (fancy_bar
&& search_string
) {
7282 t
->search_entry
= gtk_entry_new();
7283 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7284 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7285 G_CALLBACK(activate_search_entry_cb
), t
);
7286 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7287 G_CALLBACK(entry_key_cb
), t
);
7288 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7289 eb2
= gtk_hbox_new(FALSE
, 0);
7290 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7291 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7293 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7299 create_buffers(struct tab
*t
)
7301 GtkCellRenderer
*renderer
;
7304 view
= gtk_tree_view_new();
7306 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
7308 renderer
= gtk_cell_renderer_text_new();
7309 gtk_tree_view_insert_column_with_attributes
7310 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
7312 renderer
= gtk_cell_renderer_text_new();
7313 gtk_tree_view_insert_column_with_attributes
7314 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
, NULL
);
7316 gtk_tree_view_set_model
7317 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
7323 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
7324 GtkTreeViewColumn
*col
, struct tab
*t
)
7329 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7331 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
, path
)) {
7333 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
7334 gtk_notebook_set_current_page(notebook
, id
- 1);
7345 TAILQ_FOREACH(t
, &tabs
, entry
)
7346 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7350 undo_close_tab_save(struct tab
*t
)
7354 struct undo
*u1
, *u2
;
7356 WebKitWebHistoryItem
*item
;
7358 if ((uri
= get_uri(t
)) == NULL
)
7361 u1
= g_malloc0(sizeof(struct undo
));
7362 u1
->uri
= g_strdup(uri
);
7364 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7366 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
7367 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
7370 /* forward history */
7371 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
7375 u1
->history
= g_list_prepend(u1
->history
,
7376 webkit_web_history_item_copy(item
));
7377 items
= g_list_next(items
);
7382 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
7383 u1
->history
= g_list_prepend(u1
->history
,
7384 webkit_web_history_item_copy(item
));
7388 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
7392 u1
->history
= g_list_prepend(u1
->history
,
7393 webkit_web_history_item_copy(item
));
7394 items
= g_list_next(items
);
7397 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
7399 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
7400 u2
= TAILQ_LAST(&undos
, undo_tailq
);
7401 TAILQ_REMOVE(&undos
, u2
, entry
);
7403 g_list_free(u2
->history
);
7412 delete_tab(struct tab
*t
)
7416 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
7421 TAILQ_REMOVE(&tabs
, t
, entry
);
7423 /* Halt all webkit activity. */
7424 abort_favicon_download(t
);
7425 webkit_web_view_stop_loading(t
->wv
);
7427 /* Save the tab, so we can undo the close. */
7428 undo_close_tab_save(t
);
7430 if (browser_mode
== XT_BM_KIOSK
) {
7431 gtk_widget_destroy(t
->uri_entry
);
7432 gtk_widget_destroy(t
->stop
);
7433 gtk_widget_destroy(t
->js_toggle
);
7436 gtk_widget_destroy(t
->vbox
);
7437 g_free(t
->user_agent
);
7438 g_free(t
->stylesheet
);
7441 if (TAILQ_EMPTY(&tabs
)) {
7442 if (browser_mode
== XT_BM_KIOSK
)
7443 create_new_tab(home
, NULL
, 1, -1);
7445 create_new_tab(NULL
, NULL
, 1, -1);
7448 /* recreate session */
7449 if (session_autosave
) {
7456 adjustfont_webkit(struct tab
*t
, int adjust
)
7461 show_oops(NULL
, "adjustfont_webkit invalid parameters");
7465 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7466 if (adjust
== XT_FONT_SET
) {
7467 t
->font_size
= default_font_size
;
7468 zoom
= default_zoom_level
;
7469 t
->font_size
+= adjust
;
7470 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
7471 t
->font_size
, (char *)NULL
);
7472 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
7473 &t
->font_size
, (char *)NULL
);
7475 t
->font_size
+= adjust
;
7476 zoom
+= adjust
/25.0;
7481 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
7482 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7486 append_tab(struct tab
*t
)
7491 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7492 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
7496 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
7501 WebKitWebHistoryItem
*item
;
7505 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
7507 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
7508 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
7512 t
= g_malloc0(sizeof *t
);
7514 if (title
== NULL
) {
7515 title
= "(untitled)";
7519 t
->vbox
= gtk_vbox_new(FALSE
, 0);
7521 /* label + button for tab */
7522 b
= gtk_hbox_new(FALSE
, 0);
7525 #if GTK_CHECK_VERSION(2, 20, 0)
7526 t
->spinner
= gtk_spinner_new ();
7528 t
->label
= gtk_label_new(title
);
7529 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
7530 gtk_widget_set_size_request(t
->label
, 100, 0);
7531 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
7532 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
7533 gtk_widget_set_size_request(b
, 130, 0);
7535 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
7536 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
7537 #if GTK_CHECK_VERSION(2, 20, 0)
7538 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
7542 if (browser_mode
== XT_BM_KIOSK
)
7543 t
->toolbar
= create_kiosk_toolbar(t
);
7545 t
->toolbar
= create_toolbar(t
);
7547 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
7550 t
->browser_win
= create_browser(t
);
7551 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
7553 /* oops message for user feedback */
7554 t
->oops
= gtk_entry_new();
7555 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
7556 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
7557 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
7558 gdk_color_parse(XT_COLOR_RED
, &color
);
7559 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
7560 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
7563 t
->cmd
= gtk_entry_new();
7564 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
7565 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
7566 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
7567 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
7570 t
->statusbar
= gtk_entry_new();
7571 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
7572 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
7573 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
7574 gdk_color_parse(XT_COLOR_BLACK
, &color
);
7575 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7576 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7577 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7578 gtk_widget_modify_font(GTK_WIDGET(t
->statusbar
), statusbar_font
);
7579 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
7582 t
->buffers
= create_buffers(t
);
7583 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
7585 /* xtp meaning is normal by default */
7586 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7588 /* set empty favicon */
7589 xt_icon_from_name(t
, "text-html");
7591 /* and show it all */
7592 gtk_widget_show_all(b
);
7593 gtk_widget_show_all(t
->vbox
);
7595 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
7598 id
= position
>= 0 ? position
: gtk_notebook_get_current_page(notebook
) + 1;
7599 if (id
> gtk_notebook_get_n_pages(notebook
))
7602 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7603 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
7608 #if GTK_CHECK_VERSION(2, 20, 0)
7609 /* turn spinner off if we are a new tab without uri */
7611 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7612 gtk_widget_hide(t
->spinner
);
7615 /* make notebook tabs reorderable */
7616 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
7618 g_object_connect(G_OBJECT(t
->cmd
),
7619 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
7620 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
7621 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
7622 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
7625 /* reuse wv_button_cb to hide oops */
7626 g_object_connect(G_OBJECT(t
->oops
),
7627 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7630 g_signal_connect(t
->buffers
,
7631 "row-activated", G_CALLBACK(row_activated_cb
), t
);
7632 g_object_connect(G_OBJECT(t
->buffers
),
7633 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
7635 g_object_connect(G_OBJECT(t
->wv
),
7636 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
7637 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7638 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
7639 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
7640 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
7641 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7642 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7643 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
7644 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
7645 "signal::event", G_CALLBACK(webview_event_cb
), t
,
7646 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
7647 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
7648 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
7649 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7651 g_signal_connect(t
->wv
,
7652 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
7653 g_signal_connect(t
->wv
,
7654 "notify::title", G_CALLBACK(notify_title_cb
), t
);
7656 /* hijack the unused keys as if we were the browser */
7657 g_object_connect(G_OBJECT(t
->toolbar
),
7658 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7661 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7662 G_CALLBACK(tab_close_cb
), t
);
7668 url_set_visibility();
7669 statusbar_set_visibility();
7672 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7673 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7678 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7682 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7687 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7688 /* restore the tab's history */
7689 if (u
&& u
->history
) {
7693 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7694 items
= g_list_next(items
);
7697 item
= g_list_nth_data(u
->history
, u
->back
);
7699 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7702 g_list_free(u
->history
);
7704 webkit_web_back_forward_list_clear(t
->bfl
);
7710 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7716 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7718 if (gtk_notebook_get_current_page(notebook
) == -1)
7721 TAILQ_FOREACH(t
, &tabs
, entry
) {
7722 if (t
->tab_id
== pn
) {
7723 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7726 uri
= webkit_web_view_get_title(t
->wv
);
7729 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7735 /* can't use focus_webview here */
7736 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7743 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7750 menuitem_response(struct tab
*t
)
7752 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7756 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
7758 GtkWidget
*menu
, *menu_items
;
7759 GdkEventButton
*bevent
;
7763 if (event
->type
== GDK_BUTTON_PRESS
) {
7764 bevent
= (GdkEventButton
*) event
;
7765 menu
= gtk_menu_new();
7767 TAILQ_FOREACH(ti
, &tabs
, entry
) {
7768 if ((uri
= get_uri(ti
)) == NULL
)
7769 /* XXX make sure there is something to print */
7770 /* XXX add gui pages in here to look purdy */
7772 menu_items
= gtk_menu_item_new_with_label(uri
);
7773 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
7774 gtk_widget_show(menu_items
);
7776 g_signal_connect_swapped((menu_items
),
7777 "activate", G_CALLBACK(menuitem_response
),
7781 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
7782 bevent
->button
, bevent
->time
);
7784 /* unref object so it'll free itself when popped down */
7785 #if !GTK_CHECK_VERSION(3, 0, 0)
7786 /* XXX does not need unref with gtk+3? */
7787 g_object_ref_sink(menu
);
7788 g_object_unref(menu
);
7791 return (TRUE
/* eat event */);
7794 return (FALSE
/* propagate */);
7798 icon_size_map(int icon_size
)
7800 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
7801 icon_size
> GTK_ICON_SIZE_DIALOG
)
7802 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7808 create_button(char *name
, char *stockid
, int size
)
7810 GtkWidget
*button
, *image
;
7814 rcstring
= g_strdup_printf(
7815 "style \"%s-style\"\n"
7817 " GtkWidget::focus-padding = 0\n"
7818 " GtkWidget::focus-line-width = 0\n"
7822 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
7823 gtk_rc_parse_string(rcstring
);
7825 button
= gtk_button_new();
7826 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
7827 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
7829 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
7830 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7831 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
7832 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
7833 gtk_widget_set_name(button
, name
);
7834 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
7840 button_set_stockid(GtkWidget
*button
, char *stockid
)
7844 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
7845 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7846 gtk_button_set_image(GTK_BUTTON(button
), image
);
7850 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
7852 GtkClipboard
*clipboard
;
7853 gchar
*p
= NULL
, *s
= NULL
;
7856 * This code is very aggressive!
7857 * It basically ensures that the primary and regular clipboard are
7858 * always set the same. This obviously messes with standard X protocol
7859 * but those clowns should have come up with something better.
7862 /* XXX make this setting? */
7863 clipboard
= gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
);
7864 p
= gtk_clipboard_wait_for_text(primary
);
7866 DNPRINTF(XT_D_CLIP
, "primary cleaned\n");
7867 p
= gtk_clipboard_wait_for_text(clipboard
);
7869 gtk_clipboard_set_text(primary
, p
, -1);
7871 DNPRINTF(XT_D_CLIP
, "primary got selection\n");
7872 s
= gtk_clipboard_wait_for_text(clipboard
);
7875 * if s and p are the same the string was set by
7876 * clipb_clipboard_cb so do nothing in that case
7877 * to prevent endless loop
7882 gtk_clipboard_set_text(clipboard
, p
, -1);
7892 clipb_clipboard_cb(GtkClipboard
*clipboard
, GdkEvent
*event
, gpointer notused
)
7894 GtkClipboard
*primary
;
7895 gchar
*p
= NULL
, *s
= NULL
;
7897 DNPRINTF(XT_D_CLIP
, "clipboard got content\n");
7899 primary
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
7900 p
= gtk_clipboard_wait_for_text(clipboard
);
7902 s
= gtk_clipboard_wait_for_text(primary
);
7905 * if s and p are the same the string was set by
7906 * clipb_primary_cb so do nothing in that case
7907 * to prevent endless loop and deselection of text
7912 gtk_clipboard_set_text(primary
, p
, -1);
7927 char file
[PATH_MAX
];
7930 vbox
= gtk_vbox_new(FALSE
, 0);
7931 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
7932 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
7933 #if !GTK_CHECK_VERSION(3, 0, 0)
7934 /* XXX seems to be needed with gtk+2 */
7935 gtk_notebook_set_tab_hborder(notebook
, 0);
7936 gtk_notebook_set_tab_vborder(notebook
, 0);
7938 gtk_notebook_set_scrollable(notebook
, TRUE
);
7939 notebook_tab_set_visibility(notebook
);
7940 gtk_notebook_set_show_border(notebook
, FALSE
);
7941 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
7943 abtn
= gtk_button_new();
7944 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
7945 gtk_widget_set_size_request(arrow
, -1, -1);
7946 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
7947 gtk_widget_set_size_request(abtn
, -1, 20);
7949 #if GTK_CHECK_VERSION(2, 20, 0)
7950 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
7952 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
7953 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
7954 gtk_widget_set_size_request(vbox
, -1, -1);
7956 g_object_connect(G_OBJECT(notebook
),
7957 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
7959 g_object_connect(G_OBJECT(notebook
),
7960 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
), NULL
,
7962 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
7963 G_CALLBACK(arrow_cb
), NULL
);
7965 main_window
= create_window();
7966 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
7967 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
7970 for (i
= 0; i
< LENGTH(icons
); i
++) {
7971 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
7972 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
7973 l
= g_list_append(l
, pb
);
7975 gtk_window_set_default_icon_list(l
);
7978 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
7979 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
7980 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
)),
7981 "owner-change", G_CALLBACK(clipb_clipboard_cb
), NULL
);
7983 gtk_widget_show_all(abtn
);
7984 gtk_widget_show_all(main_window
);
7988 set_hook(void **hook
, char *name
)
7991 errx(1, "set_hook");
7993 if (*hook
== NULL
) {
7994 *hook
= dlsym(RTLD_NEXT
, name
);
7996 errx(1, "can't hook %s", name
);
8000 /* override libsoup soup_cookie_equal because it doesn't look at domain */
8002 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
8004 g_return_val_if_fail(cookie1
, FALSE
);
8005 g_return_val_if_fail(cookie2
, FALSE
);
8007 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
8008 !strcmp (cookie1
->value
, cookie2
->value
) &&
8009 !strcmp (cookie1
->path
, cookie2
->path
) &&
8010 !strcmp (cookie1
->domain
, cookie2
->domain
));
8014 transfer_cookies(void)
8017 SoupCookie
*sc
, *pc
;
8019 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8021 for (;cf
; cf
= cf
->next
) {
8023 sc
= soup_cookie_copy(pc
);
8024 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
8027 soup_cookies_free(cf
);
8031 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
8036 print_cookie("soup_cookie_jar_delete_cookie", c
);
8038 if (cookies_enabled
== 0)
8041 if (jar
== NULL
|| c
== NULL
)
8044 /* find and remove from persistent jar */
8045 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8047 for (;cf
; cf
= cf
->next
) {
8049 if (soup_cookie_equal(ci
, c
)) {
8050 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
8055 soup_cookies_free(cf
);
8057 /* delete from session jar */
8058 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
8062 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
8064 struct domain
*d
= NULL
;
8068 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
8069 jar
, p_cookiejar
, s_cookiejar
);
8071 if (cookies_enabled
== 0)
8074 /* see if we are up and running */
8075 if (p_cookiejar
== NULL
) {
8076 _soup_cookie_jar_add_cookie(jar
, cookie
);
8079 /* disallow p_cookiejar adds, shouldn't happen */
8080 if (jar
== p_cookiejar
)
8084 if (jar
== NULL
|| cookie
== NULL
)
8087 if (enable_cookie_whitelist
&&
8088 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
8090 DNPRINTF(XT_D_COOKIE
,
8091 "soup_cookie_jar_add_cookie: reject %s\n",
8093 if (save_rejected_cookies
) {
8094 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
8095 show_oops(NULL
, "can't open reject cookie file");
8098 fseek(r_cookie_f
, 0, SEEK_END
);
8099 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
8100 cookie
->http_only
? "#HttpOnly_" : "",
8102 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
8104 cookie
->secure
? "TRUE" : "FALSE",
8106 (gulong
)soup_date_to_time_t(cookie
->expires
) :
8113 if (!allow_volatile_cookies
)
8117 if (cookie
->expires
== NULL
&& session_timeout
) {
8118 soup_cookie_set_expires(cookie
,
8119 soup_date_new_from_now(session_timeout
));
8120 print_cookie("modified add cookie", cookie
);
8123 /* see if we are white listed for persistence */
8124 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
8125 /* add to persistent jar */
8126 c
= soup_cookie_copy(cookie
);
8127 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
8128 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
8131 /* add to session jar */
8132 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
8133 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
8139 char file
[PATH_MAX
];
8141 set_hook((void *)&_soup_cookie_jar_add_cookie
,
8142 "soup_cookie_jar_add_cookie");
8143 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
8144 "soup_cookie_jar_delete_cookie");
8146 if (cookies_enabled
== 0)
8150 * the following code is intricate due to overriding several libsoup
8152 * do not alter order of these operations.
8155 /* rejected cookies */
8156 if (save_rejected_cookies
)
8157 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
, XT_REJECT_FILE
);
8159 /* persistent cookies */
8160 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
8161 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
8163 /* session cookies */
8164 s_cookiejar
= soup_cookie_jar_new();
8165 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
8166 cookie_policy
, (void *)NULL
);
8169 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
8173 setup_proxy(char *uri
)
8176 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
8177 soup_uri_free(proxy_uri
);
8181 if (http_proxy
!= uri
) {
8188 http_proxy
= g_strdup(uri
);
8189 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
8190 proxy_uri
= soup_uri_new(http_proxy
);
8191 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
8196 send_cmd_to_socket(char *cmd
)
8199 struct sockaddr_un sa
;
8201 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8202 warnx("%s: socket", __func__
);
8206 sa
.sun_family
= AF_UNIX
;
8207 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8208 work_dir
, XT_SOCKET_FILE
);
8211 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8212 warnx("%s: connect", __func__
);
8216 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
8217 warnx("%s: send", __func__
);
8228 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
8231 char str
[XT_MAX_URL_LENGTH
];
8232 socklen_t t
= sizeof(struct sockaddr_un
);
8233 struct sockaddr_un sa
;
8238 gint fd
= g_io_channel_unix_get_fd(source
);
8240 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
8245 if (getpeereid(s
, &uid
, &gid
) == -1) {
8249 if (uid
!= getuid() || gid
!= getgid()) {
8250 warnx("unauthorized user");
8256 warnx("not a valid user");
8260 n
= recv(s
, str
, sizeof(str
), 0);
8264 tt
= TAILQ_LAST(&tabs
, tab_list
);
8265 cmd_execute(tt
, str
);
8273 struct sockaddr_un sa
;
8275 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8276 warn("is_running: socket");
8280 sa
.sun_family
= AF_UNIX
;
8281 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8282 work_dir
, XT_SOCKET_FILE
);
8285 /* connect to see if there is a listener */
8286 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
8287 rv
= 0; /* not running */
8289 rv
= 1; /* already running */
8300 struct sockaddr_un sa
;
8302 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8303 warn("build_socket: socket");
8307 sa
.sun_family
= AF_UNIX
;
8308 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8309 work_dir
, XT_SOCKET_FILE
);
8312 /* connect to see if there is a listener */
8313 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8314 /* no listener so we will */
8315 unlink(sa
.sun_path
);
8317 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8318 warn("build_socket: bind");
8322 if (listen(s
, 1) == -1) {
8323 warn("build_socket: listen");
8336 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8337 GtkTreeIter
*iter
, struct tab
*t
)
8341 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8349 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8350 GtkTreeIter
*iter
, struct tab
*t
)
8354 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8355 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
8356 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
8363 completion_add_uri(const gchar
*uri
)
8367 /* add uri to list_store */
8368 gtk_list_store_append(completion_model
, &iter
);
8369 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
8373 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
8374 GtkTreeIter
*iter
, gpointer user_data
)
8377 gboolean match
= FALSE
;
8379 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
8385 match
= match_uri(value
, key
);
8392 completion_add(struct tab
*t
)
8394 /* enable completion for tab */
8395 t
->completion
= gtk_entry_completion_new();
8396 gtk_entry_completion_set_text_column(t
->completion
, 0);
8397 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
8398 gtk_entry_completion_set_model(t
->completion
,
8399 GTK_TREE_MODEL(completion_model
));
8400 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
8402 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
8403 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
8404 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
8405 G_CALLBACK(completion_select_cb
), t
);
8406 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
8407 G_CALLBACK(completion_hover_cb
), t
);
8415 if (stat(dir
, &sb
)) {
8416 if (mkdir(dir
, S_IRWXU
) == -1)
8417 err(1, "mkdir %s", dir
);
8419 err(1, "stat %s", dir
);
8421 if (S_ISDIR(sb
.st_mode
) == 0)
8422 errx(1, "%s not a dir", dir
);
8423 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8424 warnx("fixing invalid permissions on %s", dir
);
8425 if (chmod(dir
, S_IRWXU
) == -1)
8426 err(1, "chmod %s", dir
);
8434 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
8440 main(int argc
, char *argv
[])
8443 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
8444 char conf
[PATH_MAX
] = { '\0' };
8445 char file
[PATH_MAX
];
8446 char *env_proxy
= NULL
;
8449 struct sigaction sact
;
8450 GIOChannel
*channel
;
8455 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
8457 /* fiddle with ulimits */
8458 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8461 /* just use them all */
8462 rlp
.rlim_cur
= rlp
.rlim_max
;
8463 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8465 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8467 else if (rlp
.rlim_cur
<= 256)
8468 warnx("%s requires at least 256 file descriptors",
8472 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
8481 errx(0 , "Version: %s", version
);
8484 strlcpy(conf
, optarg
, sizeof(conf
));
8487 strlcpy(named_session
, optarg
, sizeof(named_session
));
8508 RB_INIT(&downloads
);
8512 TAILQ_INIT(&aliases
);
8518 gnutls_global_init();
8520 /* generate session keys for xtp pages */
8521 generate_xtp_session_key(&dl_session_key
);
8522 generate_xtp_session_key(&hl_session_key
);
8523 generate_xtp_session_key(&cl_session_key
);
8524 generate_xtp_session_key(&fl_session_key
);
8527 gtk_init(&argc
, &argv
);
8528 if (!g_thread_supported())
8529 g_thread_init(NULL
);
8532 bzero(&sact
, sizeof(sact
));
8533 sigemptyset(&sact
.sa_mask
);
8534 sact
.sa_handler
= sigchild
;
8535 sact
.sa_flags
= SA_NOCLDSTOP
;
8536 sigaction(SIGCHLD
, &sact
, NULL
);
8538 /* set download dir */
8539 pwd
= getpwuid(getuid());
8541 errx(1, "invalid user %d", getuid());
8542 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
8544 /* set default string settings */
8545 home
= g_strdup("https://www.cyphertite.com");
8546 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
8547 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
8548 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
8549 cmd_font_name
= g_strdup("monospace normal 9");
8550 statusbar_font_name
= g_strdup("monospace normal 9");
8553 /* read config file */
8554 if (strlen(conf
) == 0)
8555 snprintf(conf
, sizeof conf
, "%s/.%s",
8556 pwd
->pw_dir
, XT_CONF_FILE
);
8557 config_parse(conf
, 0);
8560 cmd_font
= pango_font_description_from_string(cmd_font_name
);
8561 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
8563 /* working directory */
8564 if (strlen(work_dir
) == 0)
8565 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
8566 pwd
->pw_dir
, XT_DIR
);
8569 /* icon cache dir */
8570 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
8574 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
8578 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
8579 work_dir
, XT_SESSIONS_DIR
);
8580 xxx_dir(sessions_dir
);
8582 /* runtime settings that can override config file */
8583 if (runtime_settings
[0] != '\0')
8584 config_parse(runtime_settings
, 1);
8587 if (!strcmp(download_dir
, pwd
->pw_dir
))
8588 strlcat(download_dir
, "/downloads", sizeof download_dir
);
8589 xxx_dir(download_dir
);
8591 /* favorites file */
8592 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
8593 if (stat(file
, &sb
)) {
8594 warnx("favorites file doesn't exist, creating it");
8595 if ((f
= fopen(file
, "w")) == NULL
)
8596 err(1, "favorites");
8601 session
= webkit_get_default_session();
8606 if (stat(ssl_ca_file
, &sb
)) {
8607 warnx("no CA file: %s", ssl_ca_file
);
8608 g_free(ssl_ca_file
);
8611 g_object_set(session
,
8612 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
8613 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
8618 env_proxy
= getenv("http_proxy");
8620 setup_proxy(env_proxy
);
8622 setup_proxy(http_proxy
);
8625 send_cmd_to_socket(argv
[0]);
8629 /* set some connection parameters */
8630 /* XXX webkit 1.4.X overwrites these values! */
8631 /* https://bugs.webkit.org/show_bug.cgi?id=64355 */
8632 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
8633 g_object_set(session
, "max-conns-per-host", max_host_connections
,
8636 /* see if there is already an xxxterm running */
8637 if (single_instance
&& is_running()) {
8639 warnx("already running");
8645 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
8646 send_cmd_to_socket(cmd
);
8656 /* uri completion */
8657 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
8660 buffers_store
= gtk_list_store_new
8661 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
8666 if (save_global_history
)
8667 restore_global_history();
8669 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
8670 restore_saved_tabs();
8672 a
.s
= named_session
;
8673 a
.i
= XT_SES_DONOTHING
;
8674 open_tabs(NULL
, &a
);
8678 create_new_tab(argv
[0], NULL
, focus
, -1);
8685 if (TAILQ_EMPTY(&tabs
))
8686 create_new_tab(home
, NULL
, 1, -1);
8689 if ((s
= build_socket()) != -1) {
8690 channel
= g_io_channel_unix_new(s
);
8691 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
8696 gnutls_global_deinit();