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
;
180 GtkWidget
*uri_entry
;
181 GtkWidget
*search_entry
;
183 GtkWidget
*browser_win
;
184 GtkWidget
*statusbar
;
192 GtkWidget
*js_toggle
;
193 GtkEntryCompletion
*completion
;
197 WebKitWebHistoryItem
*item
;
198 WebKitWebBackForwardList
*bfl
;
201 WebKitNetworkRequest
*icon_request
;
202 WebKitDownload
*icon_download
;
203 gchar
*icon_dest_uri
;
205 /* adjustments for browser */
208 GtkAdjustment
*adjust_h
;
209 GtkAdjustment
*adjust_v
;
215 int xtp_meaning
; /* identifies dls/favorites */
220 #define XT_HINT_NONE (0)
221 #define XT_HINT_NUMERICAL (1)
222 #define XT_HINT_ALPHANUM (2)
226 /* custom stylesheet */
235 WebKitWebSettings
*settings
;
239 TAILQ_HEAD(tab_list
, tab
);
242 RB_ENTRY(history
) entry
;
246 RB_HEAD(history_list
, history
);
249 RB_ENTRY(download
) entry
;
251 WebKitDownload
*download
;
254 RB_HEAD(download_list
, download
);
257 RB_ENTRY(domain
) entry
;
259 int handy
; /* app use */
261 RB_HEAD(domain_list
, domain
);
264 TAILQ_ENTRY(undo
) entry
;
267 int back
; /* Keeps track of how many back
268 * history items there are. */
270 TAILQ_HEAD(undo_tailq
, undo
);
272 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
273 int next_download_id
= 1;
282 #define XT_NAME ("XXXTerm")
283 #define XT_DIR (".xxxterm")
284 #define XT_CACHE_DIR ("cache")
285 #define XT_CERT_DIR ("certs/")
286 #define XT_SESSIONS_DIR ("sessions/")
287 #define XT_CONF_FILE ("xxxterm.conf")
288 #define XT_FAVS_FILE ("favorites")
289 #define XT_SAVED_TABS_FILE ("main_session")
290 #define XT_RESTART_TABS_FILE ("restart_tabs")
291 #define XT_SOCKET_FILE ("socket")
292 #define XT_HISTORY_FILE ("history")
293 #define XT_REJECT_FILE ("rejected.txt")
294 #define XT_COOKIE_FILE ("cookies.txt")
295 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
296 #define XT_CB_HANDLED (TRUE)
297 #define XT_CB_PASSTHROUGH (FALSE)
298 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
299 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
300 #define XT_DLMAN_REFRESH "10"
301 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
302 "td{overflow: hidden;" \
303 " padding: 2px 2px 2px 2px;" \
304 " border: 1px solid black;" \
305 " vertical-align:top;" \
306 " word-wrap: break-word}\n" \
307 "tr:hover{background: #ffff99}\n" \
308 "th{background-color: #cccccc;" \
309 " border: 1px solid black}\n" \
310 "table{width: 100%%;" \
311 " border: 1px black solid;" \
312 " border-collapse:collapse}\n" \
314 "border: 1px solid black;" \
317 ".progress-inner{float: left;" \
319 " background: green}\n" \
320 ".dlstatus{font-size: small;" \
321 " text-align: center}\n" \
323 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
324 #define XT_MAX_UNDO_CLOSE_TAB (32)
325 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
326 #define XT_PRINT_EXTRA_MARGIN 10
329 #define XT_COLOR_RED "#cc0000"
330 #define XT_COLOR_YELLOW "#ffff66"
331 #define XT_COLOR_BLUE "lightblue"
332 #define XT_COLOR_GREEN "#99ff66"
333 #define XT_COLOR_WHITE "white"
334 #define XT_COLOR_BLACK "black"
336 #define XT_COLOR_CT_BACKGROUND "#000000"
337 #define XT_COLOR_CT_INACTIVE "#dddddd"
338 #define XT_COLOR_CT_ACTIVE "#bbbb00"
339 #define XT_COLOR_CT_SEPARATOR "#555555"
342 * xxxterm "protocol" (xtp)
343 * We use this for managing stuff like downloads and favorites. They
344 * make magical HTML pages in memory which have xxxt:// links in order
345 * to communicate with xxxterm's internals. These links take the format:
346 * xxxt://class/session_key/action/arg
348 * Don't begin xtp class/actions as 0. atoi returns that on error.
350 * Typically we have not put addition of items in this framework, as
351 * adding items is either done via an ex-command or via a keybinding instead.
354 #define XT_XTP_STR "xxxt://"
356 /* XTP classes (xxxt://<class>) */
357 #define XT_XTP_INVALID 0 /* invalid */
358 #define XT_XTP_DL 1 /* downloads */
359 #define XT_XTP_HL 2 /* history */
360 #define XT_XTP_CL 3 /* cookies */
361 #define XT_XTP_FL 4 /* favorites */
363 /* XTP download actions */
364 #define XT_XTP_DL_LIST 1
365 #define XT_XTP_DL_CANCEL 2
366 #define XT_XTP_DL_REMOVE 3
368 /* XTP history actions */
369 #define XT_XTP_HL_LIST 1
370 #define XT_XTP_HL_REMOVE 2
372 /* XTP cookie actions */
373 #define XT_XTP_CL_LIST 1
374 #define XT_XTP_CL_REMOVE 2
376 /* XTP cookie actions */
377 #define XT_XTP_FL_LIST 1
378 #define XT_XTP_FL_REMOVE 2
381 #define XT_MOVE_INVALID (0)
382 #define XT_MOVE_DOWN (1)
383 #define XT_MOVE_UP (2)
384 #define XT_MOVE_BOTTOM (3)
385 #define XT_MOVE_TOP (4)
386 #define XT_MOVE_PAGEDOWN (5)
387 #define XT_MOVE_PAGEUP (6)
388 #define XT_MOVE_HALFDOWN (7)
389 #define XT_MOVE_HALFUP (8)
390 #define XT_MOVE_LEFT (9)
391 #define XT_MOVE_FARLEFT (10)
392 #define XT_MOVE_RIGHT (11)
393 #define XT_MOVE_FARRIGHT (12)
395 #define XT_TAB_LAST (-4)
396 #define XT_TAB_FIRST (-3)
397 #define XT_TAB_PREV (-2)
398 #define XT_TAB_NEXT (-1)
399 #define XT_TAB_INVALID (0)
400 #define XT_TAB_NEW (1)
401 #define XT_TAB_DELETE (2)
402 #define XT_TAB_DELQUIT (3)
403 #define XT_TAB_OPEN (4)
404 #define XT_TAB_UNDO_CLOSE (5)
405 #define XT_TAB_SHOW (6)
406 #define XT_TAB_HIDE (7)
407 #define XT_TAB_NEXTSTYLE (8)
409 #define XT_NAV_INVALID (0)
410 #define XT_NAV_BACK (1)
411 #define XT_NAV_FORWARD (2)
412 #define XT_NAV_RELOAD (3)
413 #define XT_NAV_RELOAD_CACHE (4)
415 #define XT_FOCUS_INVALID (0)
416 #define XT_FOCUS_URI (1)
417 #define XT_FOCUS_SEARCH (2)
419 #define XT_SEARCH_INVALID (0)
420 #define XT_SEARCH_NEXT (1)
421 #define XT_SEARCH_PREV (2)
423 #define XT_PASTE_CURRENT_TAB (0)
424 #define XT_PASTE_NEW_TAB (1)
426 #define XT_FONT_SET (0)
428 #define XT_URL_SHOW (1)
429 #define XT_URL_HIDE (2)
431 #define XT_STATUSBAR_SHOW (1)
432 #define XT_STATUSBAR_HIDE (2)
434 #define XT_WL_TOGGLE (1<<0)
435 #define XT_WL_ENABLE (1<<1)
436 #define XT_WL_DISABLE (1<<2)
437 #define XT_WL_FQDN (1<<3) /* default */
438 #define XT_WL_TOPLEVEL (1<<4)
439 #define XT_WL_PERSISTENT (1<<5)
440 #define XT_WL_SESSION (1<<6)
441 #define XT_WL_RELOAD (1<<7)
443 #define XT_SHOW (1<<7)
444 #define XT_DELETE (1<<8)
445 #define XT_SAVE (1<<9)
446 #define XT_OPEN (1<<10)
448 #define XT_CMD_OPEN (0)
449 #define XT_CMD_OPEN_CURRENT (1)
450 #define XT_CMD_TABNEW (2)
451 #define XT_CMD_TABNEW_CURRENT (3)
453 #define XT_STATUS_NOTHING (0)
454 #define XT_STATUS_LINK (1)
455 #define XT_STATUS_URI (2)
456 #define XT_STATUS_LOADING (3)
458 #define XT_SES_DONOTHING (0)
459 #define XT_SES_CLOSETABS (1)
461 #define XT_BM_NORMAL (0)
462 #define XT_BM_WHITELIST (1)
463 #define XT_BM_KIOSK (2)
465 #define XT_PREFIX (1<<0)
466 #define XT_USERARG (1<<1)
467 #define XT_URLARG (1<<2)
468 #define XT_INTARG (1<<3)
470 #define XT_TABS_NORMAL 0
471 #define XT_TABS_COMPACT 1
479 TAILQ_ENTRY(mime_type
) entry
;
481 TAILQ_HEAD(mime_type_list
, mime_type
);
487 TAILQ_ENTRY(alias
) entry
;
489 TAILQ_HEAD(alias_list
, alias
);
491 /* settings that require restart */
492 int tabless
= 0; /* allow only 1 tab */
493 int enable_socket
= 0;
494 int single_instance
= 0; /* only allow one xxxterm to run */
495 int fancy_bar
= 1; /* fancy toolbar */
496 int browser_mode
= XT_BM_NORMAL
;
497 int enable_localstorage
= 0;
499 /* runtime settings */
500 int show_tabs
= 1; /* show tabs on notebook */
501 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
502 int show_url
= 1; /* show url toolbar on notebook */
503 int show_statusbar
= 0; /* vimperator style status bar */
504 int ctrl_click_focus
= 0; /* ctrl click gets focus */
505 int cookies_enabled
= 1; /* enable cookies */
506 int read_only_cookies
= 0; /* enable to not write cookies */
507 int enable_scripts
= 1;
508 int enable_plugins
= 0;
509 int default_font_size
= 12;
510 gfloat default_zoom_level
= 1.0;
511 int window_height
= 768;
512 int window_width
= 1024;
513 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
514 int refresh_interval
= 10; /* download refresh interval */
515 int enable_cookie_whitelist
= 0;
516 int enable_js_whitelist
= 0;
517 int session_timeout
= 3600; /* cookie session timeout */
518 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
519 char *ssl_ca_file
= NULL
;
520 char *resource_dir
= NULL
;
521 gboolean ssl_strict_certs
= FALSE
;
522 int append_next
= 1; /* append tab after current tab */
524 char *search_string
= NULL
;
525 char *http_proxy
= NULL
;
526 char download_dir
[PATH_MAX
];
527 char runtime_settings
[PATH_MAX
]; /* override of settings */
528 int allow_volatile_cookies
= 0;
529 int save_global_history
= 0; /* save global history to disk */
530 char *user_agent
= NULL
;
531 int save_rejected_cookies
= 0;
532 int session_autosave
= 0;
533 int guess_search
= 0;
534 int dns_prefetch
= FALSE
;
535 gint max_connections
= 25;
536 gint max_host_connections
= 5;
537 gint enable_spell_checking
= 0;
538 char *spell_check_languages
= NULL
;
540 char *cmd_font_name
= NULL
;
541 char *statusbar_font_name
= NULL
;
542 PangoFontDescription
*cmd_font
;
543 PangoFontDescription
*statusbar_font
;
547 int set_browser_mode(struct settings
*, char *);
548 int set_cookie_policy(struct settings
*, char *);
549 int set_download_dir(struct settings
*, char *);
550 int set_runtime_dir(struct settings
*, char *);
551 int set_tab_style(struct settings
*, char *);
552 int set_work_dir(struct settings
*, char *);
553 int add_alias(struct settings
*, char *);
554 int add_mime_type(struct settings
*, char *);
555 int add_cookie_wl(struct settings
*, char *);
556 int add_js_wl(struct settings
*, char *);
557 int add_kb(struct settings
*, char *);
558 void button_set_stockid(GtkWidget
*, char *);
559 GtkWidget
* create_button(char *, char *, int);
561 char *get_browser_mode(struct settings
*);
562 char *get_cookie_policy(struct settings
*);
563 char *get_download_dir(struct settings
*);
564 char *get_runtime_dir(struct settings
*);
565 char *get_tab_style(struct settings
*);
566 char *get_work_dir(struct settings
*);
568 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
569 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
570 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
571 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
572 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
574 void recalc_tabs(void);
575 void recolor_compact_tabs(void);
576 void set_current_tab(int page_num
);
579 int (*set
)(struct settings
*, char *);
580 char *(*get
)(struct settings
*);
581 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
584 struct special s_browser_mode
= {
590 struct special s_cookie
= {
596 struct special s_alias
= {
602 struct special s_mime
= {
608 struct special s_js
= {
614 struct special s_kb
= {
620 struct special s_cookie_wl
= {
626 struct special s_download_dir
= {
632 struct special s_work_dir
= {
638 struct special s_tab_style
= {
647 #define XT_S_INVALID (0)
650 #define XT_S_FLOAT (3)
652 #define XT_SF_RESTART (1<<0)
653 #define XT_SF_RUNTIME (1<<1)
659 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
660 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
661 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
662 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
663 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
664 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
665 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
666 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
667 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
668 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
669 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
670 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
671 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
672 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
673 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
674 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
675 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
676 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
677 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
678 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
679 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
680 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
681 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
682 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
683 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
684 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
685 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
686 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
687 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
688 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
689 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
690 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
691 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
692 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
693 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
694 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
695 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
696 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
697 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
698 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
699 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
700 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
701 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
704 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
705 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
707 /* runtime settings */
708 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
709 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
710 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
711 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
712 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
715 int about(struct tab
*, struct karg
*);
716 int blank(struct tab
*, struct karg
*);
717 int ca_cmd(struct tab
*, struct karg
*);
718 int cookie_show_wl(struct tab
*, struct karg
*);
719 int js_show_wl(struct tab
*, struct karg
*);
720 int help(struct tab
*, struct karg
*);
721 int set(struct tab
*, struct karg
*);
722 int stats(struct tab
*, struct karg
*);
723 int marco(struct tab
*, struct karg
*);
724 const char * marco_message(int *);
725 int xtp_page_cl(struct tab
*, struct karg
*);
726 int xtp_page_dl(struct tab
*, struct karg
*);
727 int xtp_page_fl(struct tab
*, struct karg
*);
728 int xtp_page_hl(struct tab
*, struct karg
*);
729 void xt_icon_from_file(struct tab
*, char *);
730 const gchar
*get_uri(struct tab
*);
731 const gchar
*get_title(struct tab
*);
733 #define XT_URI_ABOUT ("about:")
734 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
735 #define XT_URI_ABOUT_ABOUT ("about")
736 #define XT_URI_ABOUT_BLANK ("blank")
737 #define XT_URI_ABOUT_CERTS ("certs") /* XXX NOT YET */
738 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
739 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
740 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
741 #define XT_URI_ABOUT_FAVORITES ("favorites")
742 #define XT_URI_ABOUT_HELP ("help")
743 #define XT_URI_ABOUT_HISTORY ("history")
744 #define XT_URI_ABOUT_JSWL ("jswl")
745 #define XT_URI_ABOUT_SET ("set")
746 #define XT_URI_ABOUT_STATS ("stats")
747 #define XT_URI_ABOUT_MARCO ("marco")
751 int (*func
)(struct tab
*, struct karg
*);
753 { XT_URI_ABOUT_ABOUT
, about
},
754 { XT_URI_ABOUT_BLANK
, blank
},
755 { XT_URI_ABOUT_CERTS
, ca_cmd
},
756 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
757 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
758 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
759 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
760 { XT_URI_ABOUT_HELP
, help
},
761 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
762 { XT_URI_ABOUT_JSWL
, js_show_wl
},
763 { XT_URI_ABOUT_SET
, set
},
764 { XT_URI_ABOUT_STATS
, stats
},
765 { XT_URI_ABOUT_MARCO
, marco
},
768 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
769 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
770 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
771 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
772 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
773 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
774 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
777 extern char *__progname
;
780 GtkWidget
*main_window
;
781 GtkNotebook
*notebook
;
783 GtkWidget
*arrow
, *abtn
;
784 struct tab_list tabs
;
785 struct history_list hl
;
786 struct download_list downloads
;
787 struct domain_list c_wl
;
788 struct domain_list js_wl
;
789 struct undo_tailq undos
;
790 struct keybinding_list kbl
;
792 int updating_dl_tabs
= 0;
793 int updating_hl_tabs
= 0;
794 int updating_cl_tabs
= 0;
795 int updating_fl_tabs
= 0;
797 uint64_t blocked_cookies
= 0;
798 char named_session
[PATH_MAX
];
799 int icon_size_map(int);
801 GtkListStore
*completion_model
;
802 void completion_add(struct tab
*);
803 void completion_add_uri(const gchar
*);
804 GtkListStore
*buffers_store
;
805 void xxx_dir(char *);
810 int saved_errno
, status
;
815 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
819 if (errno
!= ECHILD
) {
821 clog_warn("sigchild: waitpid:");
827 if (WIFEXITED(status
)) {
828 if (WEXITSTATUS(status
) != 0) {
830 clog_warnx("sigchild: child exit status: %d",
831 WEXITSTATUS(status));
836 clog_warnx("sigchild: child is terminated abnormally");
845 is_g_object_setting(GObject
*o
, char *str
)
847 guint n_props
= 0, i
;
848 GParamSpec
**proplist
;
850 if (! G_IS_OBJECT(o
))
853 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
856 for (i
=0; i
< n_props
; i
++) {
857 if (! strcmp(proplist
[i
]->name
, str
))
864 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
868 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
870 "<title>%s</title>\n"
879 addstyles
? XT_PAGE_STYLE
: "",
888 * Display a web page from a HTML string in memory, rather than from a URL
891 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
896 /* we set this to indicate we want to manually do navaction */
898 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
900 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
902 /* set t->xtp_meaning */
903 for (i
= 0; i
< LENGTH(about_list
); i
++)
904 if (!strcmp(title
, about_list
[i
].name
)) {
909 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
910 #if GTK_CHECK_VERSION(2, 20, 0)
911 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
912 gtk_widget_hide(t
->spinner
);
914 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
915 xt_icon_from_file(t
, file
);
920 get_current_tab(void)
924 TAILQ_FOREACH(t
, &tabs
, entry
) {
925 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
929 warnx("%s: no current tab", __func__
);
935 set_status(struct tab
*t
, gchar
*s
, int status
)
943 case XT_STATUS_LOADING
:
944 type
= g_strdup_printf("Loading: %s", s
);
948 type
= g_strdup_printf("Link: %s", s
);
950 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
954 type
= g_strdup_printf("%s", s
);
956 t
->status
= g_strdup(type
);
960 t
->status
= g_strdup(s
);
962 case XT_STATUS_NOTHING
:
967 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
973 hide_cmd(struct tab
*t
)
975 gtk_widget_hide(t
->cmd
);
979 show_cmd(struct tab
*t
)
981 gtk_widget_hide(t
->oops
);
982 gtk_widget_show(t
->cmd
);
986 hide_buffers(struct tab
*t
)
988 gtk_widget_hide(t
->buffers
);
989 gtk_list_store_clear(buffers_store
);
999 sort_tabs_by_page_num(struct tab
***stabs
)
1004 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1006 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1008 TAILQ_FOREACH(t
, &tabs
, entry
)
1009 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1015 buffers_make_list(void)
1018 const gchar
*title
= NULL
;
1020 struct tab
**stabs
= NULL
;
1022 num_tabs
= sort_tabs_by_page_num(&stabs
);
1024 for (i
= 0; i
< num_tabs
; i
++)
1026 gtk_list_store_append(buffers_store
, &iter
);
1027 title
= get_title(stabs
[i
]);
1028 gtk_list_store_set(buffers_store
, &iter
,
1029 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1039 show_buffers(struct tab
*t
)
1041 buffers_make_list();
1042 gtk_widget_show(t
->buffers
);
1043 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1047 toggle_buffers(struct tab
*t
)
1049 if (gtk_widget_get_visible(t
->buffers
))
1056 buffers(struct tab
*t
, struct karg
*args
)
1064 hide_oops(struct tab
*t
)
1066 gtk_widget_hide(t
->oops
);
1070 show_oops(struct tab
*at
, const char *fmt
, ...)
1074 struct tab
*t
= NULL
;
1080 if ((t
= get_current_tab()) == NULL
)
1086 if (vasprintf(&msg
, fmt
, ap
) == -1)
1087 errx(1, "show_oops failed");
1090 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1091 gtk_widget_hide(t
->cmd
);
1092 gtk_widget_show(t
->oops
);
1096 get_as_string(struct settings
*s
)
1107 warnx("get_as_string skip %s\n", s
->name
);
1108 } else if (s
->type
== XT_S_INT
)
1109 r
= g_strdup_printf("%d", *s
->ival
);
1110 else if (s
->type
== XT_S_STR
)
1111 r
= g_strdup(*s
->sval
);
1112 else if (s
->type
== XT_S_FLOAT
)
1113 r
= g_strdup_printf("%f", *s
->fval
);
1115 r
= g_strdup_printf("INVALID TYPE");
1121 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1126 for (i
= 0; i
< LENGTH(rs
); i
++) {
1127 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1128 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1130 s
= get_as_string(&rs
[i
]);
1131 cb(&rs
[i
], s
, cb_args
);
1138 set_browser_mode(struct settings
*s
, char *val
)
1140 if (!strcmp(val
, "whitelist")) {
1141 browser_mode
= XT_BM_WHITELIST
;
1142 allow_volatile_cookies
= 0;
1143 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1144 cookies_enabled
= 1;
1145 enable_cookie_whitelist
= 1;
1146 read_only_cookies
= 0;
1147 save_rejected_cookies
= 0;
1148 session_timeout
= 3600;
1150 enable_js_whitelist
= 1;
1151 enable_localstorage
= 0;
1152 } else if (!strcmp(val
, "normal")) {
1153 browser_mode
= XT_BM_NORMAL
;
1154 allow_volatile_cookies
= 0;
1155 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1156 cookies_enabled
= 1;
1157 enable_cookie_whitelist
= 0;
1158 read_only_cookies
= 0;
1159 save_rejected_cookies
= 0;
1160 session_timeout
= 3600;
1162 enable_js_whitelist
= 0;
1163 enable_localstorage
= 1;
1164 } else if (!strcmp(val
, "kiosk")) {
1165 browser_mode
= XT_BM_KIOSK
;
1166 allow_volatile_cookies
= 0;
1167 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1168 cookies_enabled
= 1;
1169 enable_cookie_whitelist
= 0;
1170 read_only_cookies
= 0;
1171 save_rejected_cookies
= 0;
1172 session_timeout
= 3600;
1174 enable_js_whitelist
= 0;
1175 enable_localstorage
= 1;
1185 get_browser_mode(struct settings
*s
)
1189 if (browser_mode
== XT_BM_WHITELIST
)
1190 r
= g_strdup("whitelist");
1191 else if (browser_mode
== XT_BM_NORMAL
)
1192 r
= g_strdup("normal");
1193 else if (browser_mode
== XT_BM_KIOSK
)
1194 r
= g_strdup("kiosk");
1202 set_cookie_policy(struct settings
*s
, char *val
)
1204 if (!strcmp(val
, "no3rdparty"))
1205 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1206 else if (!strcmp(val
, "accept"))
1207 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1208 else if (!strcmp(val
, "reject"))
1209 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1217 get_cookie_policy(struct settings
*s
)
1221 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1222 r
= g_strdup("no3rdparty");
1223 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1224 r
= g_strdup("accept");
1225 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1226 r
= g_strdup("reject");
1234 get_download_dir(struct settings
*s
)
1236 if (download_dir
[0] == '\0')
1238 return (g_strdup(download_dir
));
1242 set_download_dir(struct settings
*s
, char *val
)
1245 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1246 pwd
->pw_dir
, &val
[1]);
1248 strlcpy(download_dir
, val
, sizeof download_dir
);
1255 * We use these to prevent people putting xxxt:// URLs on
1256 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1258 #define XT_XTP_SES_KEY_SZ 8
1259 #define XT_XTP_SES_KEY_HEX_FMT \
1260 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1261 char *dl_session_key
; /* downloads */
1262 char *hl_session_key
; /* history list */
1263 char *cl_session_key
; /* cookie list */
1264 char *fl_session_key
; /* favorites list */
1266 char work_dir
[PATH_MAX
];
1267 char certs_dir
[PATH_MAX
];
1268 char cache_dir
[PATH_MAX
];
1269 char sessions_dir
[PATH_MAX
];
1270 char cookie_file
[PATH_MAX
];
1271 SoupURI
*proxy_uri
= NULL
;
1272 SoupSession
*session
;
1273 SoupCookieJar
*s_cookiejar
;
1274 SoupCookieJar
*p_cookiejar
;
1275 char rc_fname
[PATH_MAX
];
1277 struct mime_type_list mtl
;
1278 struct alias_list aliases
;
1281 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1282 void delete_tab(struct tab
*);
1283 void adjustfont_webkit(struct tab
*, int);
1284 int run_script(struct tab
*, char *);
1285 int download_rb_cmp(struct download
*, struct download
*);
1286 gboolean
cmd_execute(struct tab
*t
, char *str
);
1289 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1291 return (strcmp(h1
->uri
, h2
->uri
));
1293 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1296 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1298 return (strcmp(d1
->d
, d2
->d
));
1300 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1303 get_work_dir(struct settings
*s
)
1305 if (work_dir
[0] == '\0')
1307 return (g_strdup(work_dir
));
1311 set_work_dir(struct settings
*s
, char *val
)
1314 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1315 pwd
->pw_dir
, &val
[1]);
1317 strlcpy(work_dir
, val
, sizeof work_dir
);
1323 get_tab_style(struct settings
*s
)
1325 if (tab_style
== XT_TABS_NORMAL
)
1326 return (g_strdup("normal"));
1328 return (g_strdup("compact"));
1332 set_tab_style(struct settings
*s
, char *val
)
1334 if (!strcmp(val
, "normal"))
1335 tab_style
= XT_TABS_NORMAL
;
1336 else if (!strcmp(val
, "compact"))
1337 tab_style
= XT_TABS_COMPACT
;
1345 * generate a session key to secure xtp commands.
1346 * pass in a ptr to the key in question and it will
1347 * be modified in place.
1350 generate_xtp_session_key(char **key
)
1352 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1358 /* make a new one */
1359 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1360 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1361 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1362 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1364 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1368 * validate a xtp session key.
1372 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1374 if (strcmp(trusted
, untrusted
) != 0) {
1375 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1384 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1386 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1388 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1390 struct valid_url_types
{
1401 valid_url_type(char *url
)
1405 for (i
= 0; i
< LENGTH(vut
); i
++)
1406 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1413 print_cookie(char *msg
, SoupCookie
*c
)
1419 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1420 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1421 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1422 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1423 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1424 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1425 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1426 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1427 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1428 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1432 walk_alias(struct settings
*s
,
1433 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1438 if (s
== NULL
|| cb
== NULL
) {
1439 show_oops(NULL
, "walk_alias invalid parameters");
1443 TAILQ_FOREACH(a
, &aliases
, entry
) {
1444 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1445 cb(s
, str
, cb_args
);
1451 match_alias(char *url_in
)
1455 char *url_out
= NULL
, *search
, *enc_arg
;
1457 search
= g_strdup(url_in
);
1459 if (strsep(&arg
, " \t") == NULL
) {
1460 show_oops(NULL
, "match_alias: NULL URL");
1464 TAILQ_FOREACH(a
, &aliases
, entry
) {
1465 if (!strcmp(search
, a
->a_name
))
1470 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1473 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1474 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1477 url_out
= g_strdup_printf(a
->a_uri
, "");
1485 guess_url_type(char *url_in
)
1488 char *url_out
= NULL
, *enc_search
= NULL
;
1490 url_out
= match_alias(url_in
);
1491 if (url_out
!= NULL
)
1496 * If there is no dot nor slash in the string and it isn't a
1497 * path to a local file and doesn't resolves to an IP, assume
1498 * that the user wants to search for the string.
1501 if (strchr(url_in
, '.') == NULL
&&
1502 strchr(url_in
, '/') == NULL
&&
1503 stat(url_in
, &sb
) != 0 &&
1504 gethostbyname(url_in
) == NULL
) {
1506 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1507 url_out
= g_strdup_printf(search_string
, enc_search
);
1513 /* XXX not sure about this heuristic */
1514 if (stat(url_in
, &sb
) == 0)
1515 url_out
= g_strdup_printf("file://%s", url_in
);
1517 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1519 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1525 load_uri(struct tab
*t
, gchar
*uri
)
1528 gchar
*newuri
= NULL
;
1534 /* Strip leading spaces. */
1535 while (*uri
&& isspace(*uri
))
1538 if (strlen(uri
) == 0) {
1543 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1545 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1546 for (i
= 0; i
< LENGTH(about_list
); i
++)
1547 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1548 bzero(&args
, sizeof args
);
1549 about_list
[i
].func(t
, &args
);
1550 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1554 show_oops(t
, "invalid about page");
1558 if (valid_url_type(uri
)) {
1559 newuri
= guess_url_type(uri
);
1563 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1564 webkit_web_view_load_uri(t
->wv
, uri
);
1571 get_uri(struct tab
*t
)
1573 const gchar
*uri
= NULL
;
1575 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
)
1576 uri
= webkit_web_view_get_uri(t
->wv
);
1578 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, about_list
[t
->xtp_meaning
].name
);
1584 get_title(struct tab
*t
)
1586 const gchar
*set
= NULL
, *title
= NULL
;
1588 title
= webkit_web_view_get_title(t
->wv
);
1589 set
= title
? title
: get_uri(t
);
1590 if (!set
|| t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
) {
1597 add_alias(struct settings
*s
, char *line
)
1600 struct alias
*a
= NULL
;
1602 if (s
== NULL
|| line
== NULL
) {
1603 show_oops(NULL
, "add_alias invalid parameters");
1608 a
= g_malloc(sizeof(*a
));
1610 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1611 show_oops(NULL
, "add_alias: incomplete alias definition");
1614 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1615 show_oops(NULL
, "add_alias: invalid alias definition");
1619 a
->a_name
= g_strdup(alias
);
1620 a
->a_uri
= g_strdup(l
);
1622 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1624 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1634 add_mime_type(struct settings
*s
, char *line
)
1638 struct mime_type
*m
= NULL
;
1639 int downloadfirst
= 0;
1641 /* XXX this could be smarter */
1643 if (line
== NULL
|| strlen(line
) == 0) {
1644 show_oops(NULL
, "add_mime_type invalid parameters");
1653 m
= g_malloc(sizeof(*m
));
1655 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1656 show_oops(NULL
, "add_mime_type: invalid mime_type");
1659 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1660 mime_type
[strlen(mime_type
) - 1] = '\0';
1665 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1666 show_oops(NULL
, "add_mime_type: invalid mime_type");
1670 m
->mt_type
= g_strdup(mime_type
);
1671 m
->mt_action
= g_strdup(l
);
1672 m
->mt_download
= downloadfirst
;
1674 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1675 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1677 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1687 find_mime_type(char *mime_type
)
1689 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1691 TAILQ_FOREACH(m
, &mtl
, entry
) {
1692 if (m
->mt_default
&&
1693 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1696 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1709 walk_mime_type(struct settings
*s
,
1710 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1712 struct mime_type
*m
;
1715 if (s
== NULL
|| cb
== NULL
) {
1716 show_oops(NULL
, "walk_mime_type invalid parameters");
1720 TAILQ_FOREACH(m
, &mtl
, entry
) {
1721 str
= g_strdup_printf("%s%s --> %s",
1723 m
->mt_default
? "*" : "",
1725 cb(s
, str
, cb_args
);
1731 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1736 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1739 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1741 /* treat *.moo.com the same as .moo.com */
1742 if (str
[0] == '*' && str
[1] == '.')
1744 else if (str
[0] == '.')
1749 d
= g_malloc(sizeof *d
);
1751 d
->d
= g_strdup_printf(".%s", str
);
1753 d
->d
= g_strdup(str
);
1756 if (RB_INSERT(domain_list
, wl
, d
))
1759 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1770 add_cookie_wl(struct settings
*s
, char *entry
)
1772 wl_add(entry
, &c_wl
, 1);
1777 walk_cookie_wl(struct settings
*s
,
1778 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1782 if (s
== NULL
|| cb
== NULL
) {
1783 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1787 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1788 cb(s
, d
->d
, cb_args
);
1792 walk_js_wl(struct settings
*s
,
1793 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1797 if (s
== NULL
|| cb
== NULL
) {
1798 show_oops(NULL
, "walk_js_wl invalid parameters");
1802 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1803 cb(s
, d
->d
, cb_args
);
1807 add_js_wl(struct settings
*s
, char *entry
)
1809 wl_add(entry
, &js_wl
, 1 /* persistent */);
1814 wl_find(const gchar
*search
, struct domain_list
*wl
)
1817 struct domain
*d
= NULL
, dfind
;
1820 if (search
== NULL
|| wl
== NULL
)
1822 if (strlen(search
) < 2)
1825 if (search
[0] != '.')
1826 s
= g_strdup_printf(".%s", search
);
1828 s
= g_strdup(search
);
1830 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1833 d
= RB_FIND(domain_list
, wl
, &dfind
);
1847 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1853 if (s
== NULL
|| wl
== NULL
)
1856 if (!strncmp(s
, "http://", strlen("http://")))
1857 s
= &s
[strlen("http://")];
1858 else if (!strncmp(s
, "https://", strlen("https://")))
1859 s
= &s
[strlen("https://")];
1864 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1865 /* chop string at first slash */
1866 if (s
[i
] == '/' || s
[i
] == '\0') {
1869 r
= wl_find(ss
, wl
);
1878 get_toplevel_domain(char *domain
)
1885 if (strlen(domain
) < 2)
1888 s
= &domain
[strlen(domain
) - 1];
1889 while (s
!= domain
) {
1905 settings_add(char *var
, char *val
)
1912 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1913 if (strcmp(var
, rs
[i
].name
))
1917 if (rs
[i
].s
->set(&rs
[i
], val
))
1918 errx(1, "invalid value for %s: %s", var
, val
);
1922 switch (rs
[i
].type
) {
1931 errx(1, "invalid sval for %s",
1945 errx(1, "invalid type for %s", var
);
1954 config_parse(char *filename
, int runtime
)
1957 char *line
, *cp
, *var
, *val
;
1958 size_t len
, lineno
= 0;
1960 char file
[PATH_MAX
];
1963 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1965 if (filename
== NULL
)
1968 if (runtime
&& runtime_settings
[0] != '\0') {
1969 snprintf(file
, sizeof file
, "%s/%s",
1970 work_dir
, runtime_settings
);
1971 if (stat(file
, &sb
)) {
1972 warnx("runtime file doesn't exist, creating it");
1973 if ((f
= fopen(file
, "w")) == NULL
)
1975 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1979 strlcpy(file
, filename
, sizeof file
);
1981 if ((config
= fopen(file
, "r")) == NULL
) {
1982 warn("config_parse: cannot open %s", filename
);
1987 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1988 if (feof(config
) || ferror(config
))
1992 cp
+= (long)strspn(cp
, WS
);
1993 if (cp
[0] == '\0') {
1999 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2000 errx(1, "invalid config file entry: %s", line
);
2002 cp
+= (long)strspn(cp
, WS
);
2004 if ((val
= strsep(&cp
, "\0")) == NULL
)
2007 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2008 handled
= settings_add(var
, val
);
2010 errx(1, "invalid conf file entry: %s=%s", var
, val
);
2019 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2025 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2029 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2032 JSStringGetUTF8CString(jsref
, s
, l
);
2033 JSStringRelease(jsref
);
2039 disable_hints(struct tab
*t
)
2041 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2042 bzero(t
->hint_num
, sizeof t
->hint_num
);
2043 run_script(t
, "vimprobable_clear()");
2045 t
->hint_mode
= XT_HINT_NONE
;
2049 enable_hints(struct tab
*t
)
2051 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2052 run_script(t
, "vimprobable_show_hints()");
2054 t
->hint_mode
= XT_HINT_NONE
;
2057 #define XT_JS_OPEN ("open;")
2058 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2059 #define XT_JS_FIRE ("fire;")
2060 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2061 #define XT_JS_FOUND ("found;")
2062 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2065 run_script(struct tab
*t
, char *s
)
2067 JSGlobalContextRef ctx
;
2068 WebKitWebFrame
*frame
;
2070 JSValueRef val
, exception
;
2073 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2074 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2076 frame
= webkit_web_view_get_main_frame(t
->wv
);
2077 ctx
= webkit_web_frame_get_global_context(frame
);
2079 str
= JSStringCreateWithUTF8CString(s
);
2080 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2081 NULL
, 0, &exception
);
2082 JSStringRelease(str
);
2084 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2086 es
= js_ref_to_string(ctx
, exception
);
2087 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2091 es
= js_ref_to_string(ctx
, val
);
2092 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2094 /* handle return value right here */
2095 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2097 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
2100 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2101 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2102 &es
[XT_JS_FIRE_LEN
]);
2107 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2108 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2119 hint(struct tab
*t
, struct karg
*args
)
2122 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2124 if (t
->hints_on
== 0)
2133 apply_style(struct tab
*t
)
2135 g_object_set(G_OBJECT(t
->settings
),
2136 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2140 userstyle(struct tab
*t
, struct karg
*args
)
2142 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2146 g_object_set(G_OBJECT(t
->settings
),
2147 "user-stylesheet-uri", NULL
, (char *)NULL
);
2156 * Doesn't work fully, due to the following bug:
2157 * https://bugs.webkit.org/show_bug.cgi?id=51747
2160 restore_global_history(void)
2162 char file
[PATH_MAX
];
2167 const char delim
[3] = {'\\', '\\', '\0'};
2169 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2171 if ((f
= fopen(file
, "r")) == NULL
) {
2172 warnx("%s: fopen", __func__
);
2177 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2178 if (feof(f
) || ferror(f
))
2181 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2182 if (feof(f
) || ferror(f
)) {
2184 warnx("%s: broken history file\n", __func__
);
2188 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2189 webkit_web_history_item_new_with_data(uri
, title
);
2190 h
= g_malloc(sizeof(struct history
));
2191 h
->uri
= g_strdup(uri
);
2192 h
->title
= g_strdup(title
);
2193 RB_INSERT(history_list
, &hl
, h
);
2194 completion_add_uri(h
->uri
);
2196 warnx("%s: failed to restore history\n", __func__
);
2212 save_global_history_to_disk(struct tab
*t
)
2214 char file
[PATH_MAX
];
2218 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2220 if ((f
= fopen(file
, "w")) == NULL
) {
2221 show_oops(t
, "%s: global history file: %s",
2222 __func__
, strerror(errno
));
2226 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2227 if (h
->uri
&& h
->title
)
2228 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2237 quit(struct tab
*t
, struct karg
*args
)
2239 if (save_global_history
)
2240 save_global_history_to_disk(t
);
2248 open_tabs(struct tab
*t
, struct karg
*a
)
2250 char file
[PATH_MAX
];
2254 struct tab
*ti
, *tt
;
2259 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2260 if ((f
= fopen(file
, "r")) == NULL
)
2263 ti
= TAILQ_LAST(&tabs
, tab_list
);
2266 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2267 if (feof(f
) || ferror(f
))
2270 /* retrieve session name */
2271 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2272 strlcpy(named_session
,
2273 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2274 sizeof named_session
);
2278 if (uri
&& strlen(uri
))
2279 create_new_tab(uri
, NULL
, 1, -1);
2285 /* close open tabs */
2286 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2288 tt
= TAILQ_FIRST(&tabs
);
2308 restore_saved_tabs(void)
2310 char file
[PATH_MAX
];
2311 int unlink_file
= 0;
2316 snprintf(file
, sizeof file
, "%s/%s",
2317 sessions_dir
, XT_RESTART_TABS_FILE
);
2318 if (stat(file
, &sb
) == -1)
2319 a
.s
= XT_SAVED_TABS_FILE
;
2322 a
.s
= XT_RESTART_TABS_FILE
;
2325 a
.i
= XT_SES_DONOTHING
;
2326 rv
= open_tabs(NULL
, &a
);
2335 save_tabs(struct tab
*t
, struct karg
*a
)
2337 char file
[PATH_MAX
];
2339 int num_tabs
= 0, i
;
2340 struct tab
**stabs
= NULL
;
2345 snprintf(file
, sizeof file
, "%s/%s",
2346 sessions_dir
, named_session
);
2348 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2350 if ((f
= fopen(file
, "w")) == NULL
) {
2351 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2355 /* save session name */
2356 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2358 /* Save tabs, in the order they are arranged in the notebook. */
2359 num_tabs
= sort_tabs_by_page_num(&stabs
);
2361 for (i
= 0; i
< num_tabs
; i
++)
2362 if (stabs
[i
] && get_uri(stabs
[i
]) != NULL
)
2363 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2367 /* try and make sure this gets to disk NOW. XXX Backup first? */
2368 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2369 show_oops(t
, "May not have managed to save session: %s",
2379 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2391 yank_uri(struct tab
*t
, struct karg
*args
)
2394 GtkClipboard
*clipboard
;
2396 if ((uri
= get_uri(t
)) == NULL
)
2399 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2400 gtk_clipboard_set_text(clipboard
, uri
, -1);
2406 paste_uri(struct tab
*t
, struct karg
*args
)
2408 GtkClipboard
*clipboard
;
2409 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2411 gchar
*p
= NULL
, *uri
;
2413 /* try primary clipboard first */
2414 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2415 p
= gtk_clipboard_wait_for_text(clipboard
);
2417 /* if it failed get whatever text is in cut_buffer0 */
2419 if (gdk_property_get(gdk_get_default_root_window(),
2421 gdk_atom_intern("STRING", FALSE
),
2423 65536 /* picked out of my butt */,
2429 /* yes sir, we need to NUL the string */
2435 while (*uri
&& isspace(*uri
))
2437 if (strlen(uri
) == 0) {
2438 show_oops(t
, "empty paste buffer");
2441 if (guess_search
== 0 && valid_url_type(uri
)) {
2442 /* we can be clever and paste this in search box */
2443 show_oops(t
, "not a valid URL");
2447 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2449 else if (args
->i
== XT_PASTE_NEW_TAB
)
2450 create_new_tab(uri
, NULL
, 1, -1);
2461 find_domain(const gchar
*s
, int add_dot
)
2464 char *r
= NULL
, *ss
= NULL
;
2469 if (!strncmp(s
, "http://", strlen("http://")))
2470 s
= &s
[strlen("http://")];
2471 else if (!strncmp(s
, "https://", strlen("https://")))
2472 s
= &s
[strlen("https://")];
2478 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2479 /* chop string at first slash */
2480 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2483 r
= g_strdup_printf(".%s", ss
);
2494 toggle_cwl(struct tab
*t
, struct karg
*args
)
2498 char *dom
= NULL
, *dom_toggle
= NULL
;
2505 dom
= find_domain(uri
, 1);
2506 d
= wl_find(dom
, &c_wl
);
2513 if (args
->i
& XT_WL_TOGGLE
)
2515 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2517 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2520 if (args
->i
& XT_WL_TOPLEVEL
)
2521 dom_toggle
= get_toplevel_domain(dom
);
2526 /* enable cookies for domain */
2527 wl_add(dom_toggle
, &c_wl
, 0);
2529 /* disable cookies for domain */
2530 RB_REMOVE(domain_list
, &c_wl
, d
);
2532 if (args
->i
& XT_WL_RELOAD
)
2533 webkit_web_view_reload(t
->wv
);
2540 toggle_js(struct tab
*t
, struct karg
*args
)
2545 char *dom
= NULL
, *dom_toggle
= NULL
;
2550 g_object_get(G_OBJECT(t
->settings
),
2551 "enable-scripts", &es
, (char *)NULL
);
2552 if (args
->i
& XT_WL_TOGGLE
)
2554 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2556 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2562 dom
= find_domain(uri
, 1);
2564 if (uri
== NULL
|| dom
== NULL
) {
2565 show_oops(t
, "Can't toggle domain in JavaScript white list");
2569 if (args
->i
& XT_WL_TOPLEVEL
)
2570 dom_toggle
= get_toplevel_domain(dom
);
2575 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2576 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2578 d
= wl_find(dom_toggle
, &js_wl
);
2580 RB_REMOVE(domain_list
, &js_wl
, d
);
2581 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2583 g_object_set(G_OBJECT(t
->settings
),
2584 "enable-scripts", es
, (char *)NULL
);
2585 g_object_set(G_OBJECT(t
->settings
),
2586 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2587 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2589 if (args
->i
& XT_WL_RELOAD
)
2590 webkit_web_view_reload(t
->wv
);
2598 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2602 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2605 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2610 toggle_src(struct tab
*t
, struct karg
*args
)
2617 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2618 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2619 webkit_web_view_reload(t
->wv
);
2625 focus_webview(struct tab
*t
)
2630 /* only grab focus if we are visible */
2631 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2632 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2636 focus(struct tab
*t
, struct karg
*args
)
2638 if (t
== NULL
|| args
== NULL
)
2644 if (args
->i
== XT_FOCUS_URI
)
2645 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2646 else if (args
->i
== XT_FOCUS_SEARCH
)
2647 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2653 stats(struct tab
*t
, struct karg
*args
)
2655 char *page
, *body
, *s
, line
[64 * 1024];
2656 uint64_t line_count
= 0;
2660 show_oops(NULL
, "stats invalid parameters");
2663 if (save_rejected_cookies
) {
2664 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2666 s
= fgets(line
, sizeof line
, r_cookie_f
);
2667 if (s
== NULL
|| feof(r_cookie_f
) ||
2673 snprintf(line
, sizeof line
,
2674 "<br/>Cookies blocked(*) total: %llu", line_count
);
2676 show_oops(t
, "Can't open blocked cookies file: %s",
2680 body
= g_strdup_printf(
2681 "Cookies blocked(*) this session: %llu"
2683 "<p><small><b>*</b> results vary based on settings</small></p>",
2687 page
= get_html_page("Statistics", body
, "", 0);
2690 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2697 marco(struct tab
*t
, struct karg
*args
)
2699 char *page
, line
[64 * 1024];
2703 show_oops(NULL
, "marco invalid parameters");
2706 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2708 page
= get_html_page("Marco Sez...", line
, "", 0);
2710 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2717 blank(struct tab
*t
, struct karg
*args
)
2720 show_oops(NULL
, "blank invalid parameters");
2722 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2727 about(struct tab
*t
, struct karg
*args
)
2732 show_oops(NULL
, "about invalid parameters");
2734 body
= g_strdup_printf("<b>Version: %s</b><p>"
2737 "<li>Marco Peereboom <marco@peereboom.us></li>"
2738 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2739 "<li>Edd Barrett <vext01@gmail.com> </li>"
2740 "<li>Todd T. Fries <todd@fries.net> </li>"
2741 "<li>Raphael Graf <r@undefined.ch> </li>"
2743 "Copyrights and licenses can be found on the XXXterm "
2744 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2748 page
= get_html_page("About", body
, "", 0);
2751 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2758 help(struct tab
*t
, struct karg
*args
)
2760 char *page
, *head
, *body
;
2763 show_oops(NULL
, "help invalid parameters");
2765 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2766 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2768 body
= "XXXterm man page <a href=\"http://opensource.conformal.com/"
2769 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2770 "cgi-bin/man-cgi?xxxterm</a>";
2772 page
= get_html_page("XXXterm", body
, head
, FALSE
);
2774 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2781 * update all favorite tabs apart from one. Pass NULL if
2782 * you want to update all.
2785 update_favorite_tabs(struct tab
*apart_from
)
2788 if (!updating_fl_tabs
) {
2789 updating_fl_tabs
= 1; /* stop infinite recursion */
2790 TAILQ_FOREACH(t
, &tabs
, entry
)
2791 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2792 && (t
!= apart_from
))
2793 xtp_page_fl(t
, NULL
);
2794 updating_fl_tabs
= 0;
2798 /* show a list of favorites (bookmarks) */
2800 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2802 char file
[PATH_MAX
];
2804 char *uri
= NULL
, *title
= NULL
;
2805 size_t len
, lineno
= 0;
2807 char *body
, *tmp
, *page
= NULL
;
2808 const char delim
[3] = {'\\', '\\', '\0'};
2810 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2813 warn("%s: bad param", __func__
);
2815 /* new session key */
2816 if (!updating_fl_tabs
)
2817 generate_xtp_session_key(&fl_session_key
);
2819 /* open favorites */
2820 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2821 if ((f
= fopen(file
, "r")) == NULL
) {
2822 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2827 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
2828 "<th style='width: 40px'>#</th><th>Link</th>"
2829 "<th style='width: 40px'>Rm</th></tr>\n");
2832 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2833 if (feof(f
) || ferror(f
))
2841 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2842 if (feof(f
) || ferror(f
)) {
2843 show_oops(t
, "favorites file corrupt");
2849 body
= g_strdup_printf("%s<tr>"
2851 "<td><a href='%s'>%s</a></td>"
2852 "<td style='text-align: center'>"
2853 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2855 body
, i
, uri
, title
,
2856 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2868 /* if none, say so */
2871 body
= g_strdup_printf("%s<tr>"
2872 "<td colspan='3' style='text-align: center'>"
2873 "No favorites - To add one use the 'favadd' command."
2874 "</td></tr>", body
);
2879 body
= g_strdup_printf("%s</table>", body
);
2889 page
= get_html_page("Favorites", body
, "", 1);
2890 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
2894 update_favorite_tabs(t
);
2903 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2904 size_t cert_count
, char *title
)
2906 gnutls_datum_t cinfo
;
2910 body
= g_strdup("");
2912 for (i
= 0; i
< cert_count
; i
++) {
2913 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2918 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2919 body
, i
, cinfo
.data
);
2920 gnutls_free(cinfo
.data
);
2924 tmp
= get_html_page(title
, body
, "", 0);
2927 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2932 ca_cmd(struct tab
*t
, struct karg
*args
)
2935 int rv
= 1, certs
= 0, certs_read
;
2938 gnutls_x509_crt_t
*c
= NULL
;
2939 char *certs_buf
= NULL
, *s
;
2941 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2942 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
2946 if (fstat(fileno(f
), &sb
) == -1) {
2947 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
2951 certs_buf
= g_malloc(sb
.st_size
+ 1);
2952 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2953 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2956 certs_buf
[sb
.st_size
] = '\0';
2959 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2961 s
+= strlen("BEGIN CERTIFICATE");
2964 bzero(&dt
, sizeof dt
);
2965 dt
.data
= (unsigned char *)certs_buf
;
2966 dt
.size
= sb
.st_size
;
2967 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2968 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
2969 GNUTLS_X509_FMT_PEM
, 0);
2970 if (certs_read
<= 0) {
2971 show_oops(t
, "No cert(s) available");
2974 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2987 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2990 struct addrinfo hints
, *res
= NULL
, *ai
;
2994 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2997 su
= soup_uri_new(uri
);
3000 if (!SOUP_URI_VALID_FOR_HTTP(su
))
3003 snprintf(port
, sizeof port
, "%d", su
->port
);
3004 bzero(&hints
, sizeof(struct addrinfo
));
3005 hints
.ai_flags
= AI_CANONNAME
;
3006 hints
.ai_family
= AF_UNSPEC
;
3007 hints
.ai_socktype
= SOCK_STREAM
;
3009 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
3012 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3013 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3016 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3019 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3023 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
3028 strlcpy(domain
, su
->host
, domain_sz
);
3039 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3042 gnutls_deinit(gsession
);
3044 gnutls_certificate_free_credentials(xcred
);
3050 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3051 gnutls_certificate_credentials_t
*xc
)
3053 gnutls_certificate_credentials_t xcred
;
3054 gnutls_session_t gsession
;
3057 if (gs
== NULL
|| xc
== NULL
)
3063 gnutls_certificate_allocate_credentials(&xcred
);
3064 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3065 GNUTLS_X509_FMT_PEM
);
3066 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3067 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3068 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3069 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3070 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3071 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3073 gnutls_error_is_fatal(rv
),
3074 gnutls_strerror_name(rv
));
3075 stop_tls(gsession
, xcred
);
3079 gnutls_credentials_type_t cred
;
3080 cred
= gnutls_auth_get_type(gsession
);
3081 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3082 stop_tls(gsession
, xcred
);
3094 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3098 const gnutls_datum_t
*cl
;
3099 gnutls_x509_crt_t
*all_certs
;
3102 if (certs
== NULL
|| cert_count
== NULL
)
3104 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3106 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3110 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3111 for (i
= 0; i
< len
; i
++) {
3112 gnutls_x509_crt_init(&all_certs
[i
]);
3113 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3114 GNUTLS_X509_FMT_PEM
< 0)) {
3128 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3132 for (i
= 0; i
< cert_count
; i
++)
3133 gnutls_x509_crt_deinit(certs
[i
]);
3138 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3139 size_t cert_count
, char *domain
)
3142 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3147 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3150 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3151 if ((f
= fopen(file
, "w")) == NULL
) {
3152 show_oops(t
, "Can't create cert file %s %s",
3153 file
, strerror(errno
));
3157 for (i
= 0; i
< cert_count
; i
++) {
3158 cert_buf_sz
= sizeof cert_buf
;
3159 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3160 cert_buf
, &cert_buf_sz
)) {
3161 show_oops(t
, "gnutls_x509_crt_export failed");
3164 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3165 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3170 /* not the best spot but oh well */
3171 gdk_color_parse("lightblue", &color
);
3172 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3173 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
3174 gdk_color_parse(XT_COLOR_BLACK
, &color
);
3175 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
3181 load_compare_cert(struct tab
*t
, struct karg
*args
)
3184 char domain
[8182], file
[PATH_MAX
];
3185 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3186 int s
= -1, rv
= 1, i
;
3190 gnutls_session_t gsession
;
3191 gnutls_x509_crt_t
*certs
;
3192 gnutls_certificate_credentials_t xcred
;
3197 if ((uri
= get_uri(t
)) == NULL
)
3200 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
3204 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3205 show_oops(t
, "Start TLS failed");
3210 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3211 show_oops(t
, "Can't get connection certificates");
3215 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3216 if ((f
= fopen(file
, "r")) == NULL
)
3219 for (i
= 0; i
< cert_count
; i
++) {
3220 cert_buf_sz
= sizeof cert_buf
;
3221 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3222 cert_buf
, &cert_buf_sz
)) {
3225 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3226 rv
= -1; /* critical */
3229 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3230 rv
= -1; /* critical */
3239 free_connection_certs(certs
, cert_count
);
3241 /* we close the socket first for speed */
3244 stop_tls(gsession
, xcred
);
3250 cert_cmd(struct tab
*t
, struct karg
*args
)
3256 gnutls_session_t gsession
;
3257 gnutls_x509_crt_t
*certs
;
3258 gnutls_certificate_credentials_t xcred
;
3263 if (ssl_ca_file
== NULL
) {
3264 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3268 if ((uri
= get_uri(t
)) == NULL
) {
3269 show_oops(t
, "Invalid URI");
3273 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3274 show_oops(t
, "Invalid certificate URI: %s", uri
);
3279 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3280 show_oops(t
, "Start TLS failed");
3285 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3286 show_oops(t
, "get_connection_certs failed");
3290 if (args
->i
& XT_SHOW
)
3291 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3292 else if (args
->i
& XT_SAVE
)
3293 save_certs(t
, certs
, cert_count
, domain
);
3295 free_connection_certs(certs
, cert_count
);
3297 /* we close the socket first for speed */
3300 stop_tls(gsession
, xcred
);
3306 remove_cookie(int index
)
3312 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3314 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3316 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3320 print_cookie("remove cookie", c
);
3321 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3326 soup_cookies_free(cf
);
3332 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3337 body
= g_strdup("");
3340 if (args
->i
& XT_WL_PERSISTENT
) {
3342 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3344 RB_FOREACH(d
, domain_list
, wl
) {
3348 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3354 if (args
->i
& XT_WL_SESSION
) {
3356 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3358 RB_FOREACH(d
, domain_list
, wl
) {
3362 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3367 tmp
= get_html_page(title
, body
, "", 0);
3370 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3372 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3378 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3380 char file
[PATH_MAX
];
3382 char *line
= NULL
, *lt
= NULL
;
3385 char *dom
= NULL
, *dom_save
= NULL
;
3391 if (t
== NULL
|| args
== NULL
)
3394 if (runtime_settings
[0] == '\0')
3397 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3398 if ((f
= fopen(file
, "r+")) == NULL
)
3402 dom
= find_domain(uri
, 1);
3403 if (uri
== NULL
|| dom
== NULL
) {
3404 show_oops(t
, "Can't add domain to %s white list",
3405 js
? "JavaScript" : "cookie");
3409 if (args
->i
& XT_WL_TOPLEVEL
) {
3411 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3412 show_oops(t
, "invalid domain: %s", dom
);
3415 } else if (args
->i
& XT_WL_FQDN
) {
3421 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3424 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3427 if (!strcmp(line
, lt
))
3433 fprintf(f
, "%s\n", lt
);
3438 d
= wl_find(dom_save
, &js_wl
);
3440 settings_add("js_wl", dom_save
);
3441 d
= wl_find(dom_save
, &js_wl
);
3445 d
= wl_find(dom_save
, &c_wl
);
3447 settings_add("cookie_wl", dom_save
);
3448 d
= wl_find(dom_save
, &c_wl
);
3452 /* find and add to persistent jar */
3453 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3454 for (;cf
; cf
= cf
->next
) {
3456 if (!strcmp(dom_save
, ci
->domain
) ||
3457 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3458 c
= soup_cookie_copy(ci
);
3459 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3462 soup_cookies_free(cf
);
3480 js_show_wl(struct tab
*t
, struct karg
*args
)
3482 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3483 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3489 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3491 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3492 wl_show(t
, args
, "Cookie White List", &c_wl
);
3498 cookie_cmd(struct tab
*t
, struct karg
*args
)
3500 if (args
->i
& XT_SHOW
)
3501 wl_show(t
, args
, "Cookie White List", &c_wl
);
3502 else if (args
->i
& XT_WL_TOGGLE
) {
3503 args
->i
|= XT_WL_RELOAD
;
3504 toggle_cwl(t
, args
);
3505 } else if (args
->i
& XT_SAVE
) {
3506 args
->i
|= XT_WL_RELOAD
;
3507 wl_save(t
, args
, 0);
3508 } else if (args
->i
& XT_DELETE
)
3509 show_oops(t
, "'cookie delete' currently unimplemented");
3515 js_cmd(struct tab
*t
, struct karg
*args
)
3517 if (args
->i
& XT_SHOW
)
3518 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3519 else if (args
->i
& XT_SAVE
) {
3520 args
->i
|= XT_WL_RELOAD
;
3521 wl_save(t
, args
, 1);
3522 } else if (args
->i
& XT_WL_TOGGLE
) {
3523 args
->i
|= XT_WL_RELOAD
;
3525 } else if (args
->i
& XT_DELETE
)
3526 show_oops(t
, "'js delete' currently unimplemented");
3532 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3534 js_toggle_cb(t
->js_toggle
, t
);
3540 add_favorite(struct tab
*t
, struct karg
*args
)
3542 char file
[PATH_MAX
];
3545 size_t urilen
, linelen
;
3546 const gchar
*uri
, *title
;
3551 /* don't allow adding of xtp pages to favorites */
3552 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3553 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3557 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3558 if ((f
= fopen(file
, "r+")) == NULL
) {
3559 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3563 title
= webkit_web_view_get_title(t
->wv
);
3569 if (title
== NULL
|| uri
== NULL
) {
3570 show_oops(t
, "can't add page to favorites");
3574 urilen
= strlen(uri
);
3577 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3578 if (feof(f
) || ferror(f
))
3581 if (linelen
== urilen
&& !strcmp(line
, uri
))
3588 fprintf(f
, "\n%s\n%s", title
, uri
);
3594 update_favorite_tabs(NULL
);
3600 navaction(struct tab
*t
, struct karg
*args
)
3602 WebKitWebHistoryItem
*item
;
3604 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3605 t
->tab_id
, args
->i
);
3608 if (args
->i
== XT_NAV_BACK
)
3609 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3611 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3613 return (XT_CB_PASSTHROUGH
);
3614 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3616 return (XT_CB_PASSTHROUGH
);
3621 webkit_web_view_go_back(t
->wv
);
3623 case XT_NAV_FORWARD
:
3624 webkit_web_view_go_forward(t
->wv
);
3627 webkit_web_view_reload(t
->wv
);
3629 case XT_NAV_RELOAD_CACHE
:
3630 webkit_web_view_reload_bypass_cache(t
->wv
);
3633 return (XT_CB_PASSTHROUGH
);
3637 move(struct tab
*t
, struct karg
*args
)
3639 GtkAdjustment
*adjust
;
3640 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3645 case XT_MOVE_BOTTOM
:
3647 case XT_MOVE_PAGEDOWN
:
3648 case XT_MOVE_PAGEUP
:
3649 case XT_MOVE_HALFDOWN
:
3650 case XT_MOVE_HALFUP
:
3651 adjust
= t
->adjust_v
;
3654 adjust
= t
->adjust_h
;
3658 pos
= gtk_adjustment_get_value(adjust
);
3659 ps
= gtk_adjustment_get_page_size(adjust
);
3660 upper
= gtk_adjustment_get_upper(adjust
);
3661 lower
= gtk_adjustment_get_lower(adjust
);
3662 si
= gtk_adjustment_get_step_increment(adjust
);
3663 pi
= gtk_adjustment_get_page_increment(adjust
);
3666 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3667 "max %f si %f pi %f\n",
3668 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3669 pos
, ps
, upper
, lower
, max
, si
, pi
);
3675 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3680 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3682 case XT_MOVE_BOTTOM
:
3683 case XT_MOVE_FARRIGHT
:
3684 gtk_adjustment_set_value(adjust
, max
);
3687 case XT_MOVE_FARLEFT
:
3688 gtk_adjustment_set_value(adjust
, lower
);
3690 case XT_MOVE_PAGEDOWN
:
3692 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3694 case XT_MOVE_PAGEUP
:
3696 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3698 case XT_MOVE_HALFDOWN
:
3700 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3702 case XT_MOVE_HALFUP
:
3704 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3707 return (XT_CB_PASSTHROUGH
);
3710 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3712 return (XT_CB_HANDLED
);
3716 url_set_visibility(void)
3720 TAILQ_FOREACH(t
, &tabs
, entry
) {
3721 if (show_url
== 0) {
3722 gtk_widget_hide(t
->toolbar
);
3725 gtk_widget_show(t
->toolbar
);
3730 notebook_tab_set_visibility()
3732 if (show_tabs
== 0) {
3733 gtk_widget_hide(tab_bar
);
3734 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3736 if (tab_style
== XT_TABS_NORMAL
) {
3737 gtk_widget_hide(tab_bar
);
3738 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3739 } else if (tab_style
== XT_TABS_COMPACT
) {
3740 gtk_widget_show(tab_bar
);
3741 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3747 statusbar_set_visibility(void)
3751 TAILQ_FOREACH(t
, &tabs
, entry
) {
3752 if (show_statusbar
== 0) {
3753 gtk_widget_hide(t
->statusbar
);
3756 gtk_widget_show(t
->statusbar
);
3761 url_set(struct tab
*t
, int enable_url_entry
)
3766 show_url
= enable_url_entry
;
3768 if (enable_url_entry
) {
3769 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3770 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3771 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3773 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3774 GTK_ENTRY_ICON_PRIMARY
);
3776 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3777 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3778 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3779 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3785 fullscreen(struct tab
*t
, struct karg
*args
)
3787 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3790 return (XT_CB_PASSTHROUGH
);
3792 if (show_url
== 0) {
3800 url_set_visibility();
3801 notebook_tab_set_visibility();
3803 return (XT_CB_HANDLED
);
3807 statusaction(struct tab
*t
, struct karg
*args
)
3809 int rv
= XT_CB_HANDLED
;
3811 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3814 return (XT_CB_PASSTHROUGH
);
3817 case XT_STATUSBAR_SHOW
:
3818 if (show_statusbar
== 0) {
3820 statusbar_set_visibility();
3823 case XT_STATUSBAR_HIDE
:
3824 if (show_statusbar
== 1) {
3826 statusbar_set_visibility();
3834 urlaction(struct tab
*t
, struct karg
*args
)
3836 int rv
= XT_CB_HANDLED
;
3838 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3841 return (XT_CB_PASSTHROUGH
);
3845 if (show_url
== 0) {
3847 url_set_visibility();
3851 if (show_url
== 1) {
3853 url_set_visibility();
3861 tabaction(struct tab
*t
, struct karg
*args
)
3863 int rv
= XT_CB_HANDLED
;
3864 char *url
= args
->s
;
3868 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3871 return (XT_CB_PASSTHROUGH
);
3875 if (strlen(url
) > 0)
3876 create_new_tab(url
, NULL
, 1, args
->p
);
3878 create_new_tab(NULL
, NULL
, 1, args
->p
);
3884 TAILQ_FOREACH(tt
, &tabs
, entry
)
3885 if (tt
->tab_id
== args
->p
- 1) {
3891 case XT_TAB_DELQUIT
:
3892 if (gtk_notebook_get_n_pages(notebook
) > 1)
3898 if (strlen(url
) > 0)
3901 rv
= XT_CB_PASSTHROUGH
;
3907 if (show_tabs
== 0) {
3909 notebook_tab_set_visibility();
3913 if (show_tabs
== 1) {
3915 notebook_tab_set_visibility();
3918 case XT_TAB_NEXTSTYLE
:
3919 if (tab_style
== XT_TABS_NORMAL
)
3920 tab_style
= XT_TABS_COMPACT
;
3922 tab_style
= XT_TABS_NORMAL
;
3923 notebook_tab_set_visibility();
3925 case XT_TAB_UNDO_CLOSE
:
3926 if (undo_count
== 0) {
3927 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3931 u
= TAILQ_FIRST(&undos
);
3932 create_new_tab(u
->uri
, u
, 1, -1);
3934 TAILQ_REMOVE(&undos
, u
, entry
);
3936 /* u->history is freed in create_new_tab() */
3941 rv
= XT_CB_PASSTHROUGH
;
3955 resizetab(struct tab
*t
, struct karg
*args
)
3957 if (t
== NULL
|| args
== NULL
) {
3958 show_oops(NULL
, "resizetab invalid parameters");
3959 return (XT_CB_PASSTHROUGH
);
3962 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3963 t
->tab_id
, args
->i
);
3965 adjustfont_webkit(t
, args
->i
);
3967 return (XT_CB_HANDLED
);
3971 movetab(struct tab
*t
, struct karg
*args
)
3975 if (t
== NULL
|| args
== NULL
) {
3976 show_oops(NULL
, "movetab invalid parameters");
3977 return (XT_CB_PASSTHROUGH
);
3980 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3981 t
->tab_id
, args
->i
);
3983 if (args
->i
>= XT_TAB_INVALID
)
3984 return (XT_CB_PASSTHROUGH
);
3986 if (TAILQ_EMPTY(&tabs
))
3987 return (XT_CB_PASSTHROUGH
);
3989 n
= gtk_notebook_get_n_pages(notebook
);
3990 dest
= gtk_notebook_get_current_page(notebook
);
3995 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4004 dest
-= args
->p
% n
;
4017 return (XT_CB_PASSTHROUGH
);
4020 if (dest
< 0 || dest
>= n
)
4021 return (XT_CB_PASSTHROUGH
);
4022 if (t
->tab_id
== dest
) {
4023 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4024 return (XT_CB_HANDLED
);
4027 set_current_tab(dest
);
4029 return (XT_CB_HANDLED
);
4035 command(struct tab
*t
, struct karg
*args
)
4037 char *s
= NULL
, *ss
= NULL
;
4041 if (t
== NULL
|| args
== NULL
) {
4042 show_oops(NULL
, "command invalid parameters");
4043 return (XT_CB_PASSTHROUGH
);
4054 if (cmd_prefix
== 0)
4057 ss
= g_strdup_printf(":%d", cmd_prefix
);
4068 case XT_CMD_OPEN_CURRENT
:
4071 case XT_CMD_TABNEW_CURRENT
:
4072 if (!s
) /* FALL THROUGH? */
4074 if ((uri
= get_uri(t
)) != NULL
) {
4075 ss
= g_strdup_printf("%s%s", s
, uri
);
4080 show_oops(t
, "command: invalid opcode %d", args
->i
);
4081 return (XT_CB_PASSTHROUGH
);
4084 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4086 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4087 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4088 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4090 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4091 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4096 return (XT_CB_HANDLED
);
4100 * Return a new string with a download row (in html)
4101 * appended. Old string is freed.
4104 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4107 WebKitDownloadStatus stat
;
4108 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4110 char cur_sz
[FMT_SCALED_STRSIZE
];
4111 char tot_sz
[FMT_SCALED_STRSIZE
];
4114 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4116 /* All actions wil take this form:
4117 * xxxt://class/seskey
4119 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4120 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4122 stat
= webkit_download_get_status(dl
->download
);
4125 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4126 status_html
= g_strdup_printf("Finished");
4127 cmd_html
= g_strdup_printf(
4128 "<a href='%s%d/%d'>Remove</a>",
4129 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4131 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4132 /* gather size info */
4133 progress
= 100 * webkit_download_get_progress(dl
->download
);
4136 webkit_download_get_current_size(dl
->download
), cur_sz
);
4138 webkit_download_get_total_size(dl
->download
), tot_sz
);
4140 status_html
= g_strdup_printf(
4141 "<div style='width: 100%%' align='center'>"
4142 "<div class='progress-outer'>"
4143 "<div class='progress-inner' style='width: %.2f%%'>"
4144 "</div></div></div>"
4145 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4146 progress
, cur_sz
, tot_sz
, progress
);
4148 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4149 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4153 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4154 status_html
= g_strdup_printf("Cancelled");
4155 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4156 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4158 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4159 status_html
= g_strdup_printf("Error!");
4160 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4161 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4163 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4164 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4165 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4166 status_html
= g_strdup_printf("Starting");
4169 show_oops(t
, "%s: unknown download status", __func__
);
4172 new_html
= g_strdup_printf(
4173 "%s\n<tr><td>%s</td><td>%s</td>"
4174 "<td style='text-align:center'>%s</td></tr>\n",
4175 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4176 status_html
, cmd_html
);
4180 g_free(status_html
);
4191 * update all download tabs apart from one. Pass NULL if
4192 * you want to update all.
4195 update_download_tabs(struct tab
*apart_from
)
4198 if (!updating_dl_tabs
) {
4199 updating_dl_tabs
= 1; /* stop infinite recursion */
4200 TAILQ_FOREACH(t
, &tabs
, entry
)
4201 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4202 && (t
!= apart_from
))
4203 xtp_page_dl(t
, NULL
);
4204 updating_dl_tabs
= 0;
4209 * update all cookie tabs apart from one. Pass NULL if
4210 * you want to update all.
4213 update_cookie_tabs(struct tab
*apart_from
)
4216 if (!updating_cl_tabs
) {
4217 updating_cl_tabs
= 1; /* stop infinite recursion */
4218 TAILQ_FOREACH(t
, &tabs
, entry
)
4219 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4220 && (t
!= apart_from
))
4221 xtp_page_cl(t
, NULL
);
4222 updating_cl_tabs
= 0;
4227 * update all history tabs apart from one. Pass NULL if
4228 * you want to update all.
4231 update_history_tabs(struct tab
*apart_from
)
4235 if (!updating_hl_tabs
) {
4236 updating_hl_tabs
= 1; /* stop infinite recursion */
4237 TAILQ_FOREACH(t
, &tabs
, entry
)
4238 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4239 && (t
!= apart_from
))
4240 xtp_page_hl(t
, NULL
);
4241 updating_hl_tabs
= 0;
4245 /* cookie management XTP page */
4247 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4249 char *body
, *page
, *tmp
;
4250 int i
= 1; /* all ids start 1 */
4251 GSList
*sc
, *pc
, *pc_start
;
4253 char *type
, *table_headers
, *last_domain
;
4255 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4258 show_oops(NULL
, "%s invalid parameters", __func__
);
4262 /* Generate a new session key */
4263 if (!updating_cl_tabs
)
4264 generate_xtp_session_key(&cl_session_key
);
4267 table_headers
= g_strdup_printf("<table><tr>"
4270 "<th style='width:200px'>Value</th>"
4274 "<th>HTTP<br />only</th>"
4275 "<th style='width:40px'>Rm</th></tr>\n");
4277 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4278 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4282 last_domain
= strdup("");
4283 for (; sc
; sc
= sc
->next
) {
4286 if (strcmp(last_domain
, c
->domain
) != 0) {
4289 last_domain
= strdup(c
->domain
);
4293 body
= g_strdup_printf("%s</table>"
4295 body
, c
->domain
, table_headers
);
4299 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4300 c
->domain
, table_headers
);
4305 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4306 if (soup_cookie_equal(pc
->data
, c
)) {
4307 type
= "Session + Persistent";
4312 body
= g_strdup_printf(
4315 "<td style='word-wrap:normal'>%s</td>"
4317 " <textarea rows='4'>%s</textarea>"
4323 "<td style='text-align:center'>"
4324 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4331 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4346 soup_cookies_free(sc
);
4347 soup_cookies_free(pc
);
4349 /* small message if there are none */
4351 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4352 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4355 body
= g_strdup_printf("%s</table>", body
);
4358 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4360 g_free(table_headers
);
4361 g_free(last_domain
);
4363 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4364 update_cookie_tabs(t
);
4372 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4374 char *body
, *page
, *tmp
;
4376 int i
= 1; /* all ids start 1 */
4378 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4381 show_oops(NULL
, "%s invalid parameters", __func__
);
4385 /* Generate a new session key */
4386 if (!updating_hl_tabs
)
4387 generate_xtp_session_key(&hl_session_key
);
4390 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4391 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4393 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4395 body
= g_strdup_printf(
4397 "<td><a href='%s'>%s</a></td>"
4399 "<td style='text-align: center'>"
4400 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4401 body
, h
->uri
, h
->uri
, h
->title
,
4402 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4403 XT_XTP_HL_REMOVE
, i
);
4409 /* small message if there are none */
4412 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4413 "colspan='3'>No History</td></tr>\n", body
);
4418 body
= g_strdup_printf("%s</table>", body
);
4421 page
= get_html_page("History", body
, "", TRUE
);
4425 * update all history manager tabs as the xtp session
4426 * key has now changed. No need to update the current tab.
4427 * Already did that above.
4429 update_history_tabs(t
);
4431 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4438 * Generate a web page detailing the status of any downloads
4441 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4443 struct download
*dl
;
4444 char *body
, *page
, *tmp
;
4448 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4451 show_oops(NULL
, "%s invalid parameters", __func__
);
4456 * Generate a new session key for next page instance.
4457 * This only happens for the top level call to xtp_page_dl()
4458 * in which case updating_dl_tabs is 0.
4460 if (!updating_dl_tabs
)
4461 generate_xtp_session_key(&dl_session_key
);
4463 /* header - with refresh so as to update */
4464 if (refresh_interval
>= 1)
4465 ref
= g_strdup_printf(
4466 "<meta http-equiv='refresh' content='%u"
4467 ";url=%s%d/%s/%d' />\n",
4476 body
= g_strdup_printf("<div align='center'>"
4477 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4478 "</p><table><tr><th style='width: 60%%'>"
4479 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4480 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4482 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4483 body
= xtp_page_dl_row(t
, body
, dl
);
4487 /* message if no downloads in list */
4490 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4491 " style='text-align: center'>"
4492 "No downloads</td></tr>\n", body
);
4497 body
= g_strdup_printf("%s</table></div>", body
);
4500 page
= get_html_page("Downloads", body
, ref
, 1);
4505 * update all download manager tabs as the xtp session
4506 * key has now changed. No need to update the current tab.
4507 * Already did that above.
4509 update_download_tabs(t
);
4511 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4518 search(struct tab
*t
, struct karg
*args
)
4522 if (t
== NULL
|| args
== NULL
) {
4523 show_oops(NULL
, "search invalid parameters");
4526 if (t
->search_text
== NULL
) {
4527 if (global_search
== NULL
)
4528 return (XT_CB_PASSTHROUGH
);
4530 t
->search_text
= g_strdup(global_search
);
4531 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4532 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4536 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4537 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4540 case XT_SEARCH_NEXT
:
4541 d
= t
->search_forward
;
4543 case XT_SEARCH_PREV
:
4544 d
= !t
->search_forward
;
4547 return (XT_CB_PASSTHROUGH
);
4550 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4552 return (XT_CB_HANDLED
);
4555 struct settings_args
{
4561 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4564 struct settings_args
*sa
= cb_args
;
4569 if (s
->flags
& XT_SF_RUNTIME
)
4575 *sa
->body
= g_strdup_printf(
4577 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4578 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4590 set(struct tab
*t
, struct karg
*args
)
4592 char *body
, *page
, *tmp
;
4594 struct settings_args sa
;
4596 bzero(&sa
, sizeof sa
);
4600 body
= g_strdup_printf("<div align='center'><table><tr>"
4601 "<th align='left'>Setting</th>"
4602 "<th align='left'>Value</th></tr>\n");
4604 settings_walk(print_setting
, &sa
);
4607 /* small message if there are none */
4610 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4611 "colspan='2'>No settings</td></tr>\n", body
);
4616 body
= g_strdup_printf("%s</table></div>", body
);
4619 page
= get_html_page("Settings", body
, "", 0);
4623 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4627 return (XT_CB_PASSTHROUGH
);
4631 session_save(struct tab
*t
, char *filename
)
4636 if (strlen(filename
) == 0)
4639 if (filename
[0] == '.' || filename
[0] == '/')
4643 if (save_tabs(t
, &a
))
4645 strlcpy(named_session
, filename
, sizeof named_session
);
4653 session_open(struct tab
*t
, char *filename
)
4658 if (strlen(filename
) == 0)
4661 if (filename
[0] == '.' || filename
[0] == '/')
4665 a
.i
= XT_SES_CLOSETABS
;
4666 if (open_tabs(t
, &a
))
4669 strlcpy(named_session
, filename
, sizeof named_session
);
4677 session_delete(struct tab
*t
, char *filename
)
4679 char file
[PATH_MAX
];
4682 if (strlen(filename
) == 0)
4685 if (filename
[0] == '.' || filename
[0] == '/')
4688 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4692 if (!strcmp(filename
, named_session
))
4693 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4694 sizeof named_session
);
4702 session_cmd(struct tab
*t
, struct karg
*args
)
4704 char *filename
= args
->s
;
4709 if (args
->i
& XT_SHOW
)
4710 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4711 XT_SAVED_TABS_FILE
: named_session
);
4712 else if (args
->i
& XT_SAVE
) {
4713 if (session_save(t
, filename
)) {
4714 show_oops(t
, "Can't save session: %s",
4715 filename
? filename
: "INVALID");
4718 } else if (args
->i
& XT_OPEN
) {
4719 if (session_open(t
, filename
)) {
4720 show_oops(t
, "Can't open session: %s",
4721 filename
? filename
: "INVALID");
4724 } else if (args
->i
& XT_DELETE
) {
4725 if (session_delete(t
, filename
)) {
4726 show_oops(t
, "Can't delete session: %s",
4727 filename
? filename
: "INVALID");
4732 return (XT_CB_PASSTHROUGH
);
4736 * Make a hardcopy of the page
4739 print_page(struct tab
*t
, struct karg
*args
)
4741 WebKitWebFrame
*frame
;
4743 GtkPrintOperation
*op
;
4744 GtkPrintOperationAction action
;
4745 GtkPrintOperationResult print_res
;
4746 GError
*g_err
= NULL
;
4747 int marg_l
, marg_r
, marg_t
, marg_b
;
4749 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4751 ps
= gtk_page_setup_new();
4752 op
= gtk_print_operation_new();
4753 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4754 frame
= webkit_web_view_get_main_frame(t
->wv
);
4756 /* the default margins are too small, so we will bump them */
4757 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4758 XT_PRINT_EXTRA_MARGIN
;
4759 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4760 XT_PRINT_EXTRA_MARGIN
;
4761 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4762 XT_PRINT_EXTRA_MARGIN
;
4763 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4764 XT_PRINT_EXTRA_MARGIN
;
4767 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4768 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4769 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4770 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4772 gtk_print_operation_set_default_page_setup(op
, ps
);
4774 /* this appears to free 'op' and 'ps' */
4775 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4777 /* check it worked */
4778 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4779 show_oops(NULL
, "can't print: %s", g_err
->message
);
4780 g_error_free (g_err
);
4788 go_home(struct tab
*t
, struct karg
*args
)
4795 restart(struct tab
*t
, struct karg
*args
)
4799 a
.s
= XT_RESTART_TABS_FILE
;
4801 execvp(start_argv
[0], start_argv
);
4807 #define CTRL GDK_CONTROL_MASK
4808 #define MOD1 GDK_MOD1_MASK
4809 #define SHFT GDK_SHIFT_MASK
4811 /* inherent to GTK not all keys will be caught at all times */
4812 /* XXX sort key bindings */
4813 struct key_binding
{
4818 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4820 { "cookiejar", MOD1
, 0, GDK_j
},
4821 { "downloadmgr", MOD1
, 0, GDK_d
},
4822 { "history", MOD1
, 0, GDK_h
},
4823 { "print", CTRL
, 0, GDK_p
},
4824 { "search", 0, 0, GDK_slash
},
4825 { "searchb", 0, 0, GDK_question
},
4826 { "command", 0, 0, GDK_colon
},
4827 { "qa", CTRL
, 0, GDK_q
},
4828 { "restart", MOD1
, 0, GDK_q
},
4829 { "js toggle", CTRL
, 0, GDK_j
},
4830 { "cookie toggle", MOD1
, 0, GDK_c
},
4831 { "togglesrc", CTRL
, 0, GDK_s
},
4832 { "yankuri", 0, 0, GDK_y
},
4833 { "pasteuricur", 0, 0, GDK_p
},
4834 { "pasteurinew", 0, 0, GDK_P
},
4835 { "toplevel toggle", 0, 0, GDK_F4
},
4836 { "help", 0, 0, GDK_F1
},
4839 { "searchnext", 0, 0, GDK_n
},
4840 { "searchprevious", 0, 0, GDK_N
},
4843 { "focusaddress", 0, 0, GDK_F6
},
4844 { "focussearch", 0, 0, GDK_F7
},
4847 { "hinting", 0, 0, GDK_f
},
4849 /* custom stylesheet */
4850 { "userstyle", 0, 0, GDK_i
},
4853 { "goback", 0, 0, GDK_BackSpace
},
4854 { "goback", MOD1
, 0, GDK_Left
},
4855 { "goforward", SHFT
, 0, GDK_BackSpace
},
4856 { "goforward", MOD1
, 0, GDK_Right
},
4857 { "reload", 0, 0, GDK_F5
},
4858 { "reload", CTRL
, 0, GDK_r
},
4859 { "reloadforce", CTRL
, 0, GDK_R
},
4860 { "reload", CTRL
, 0, GDK_l
},
4861 { "favorites", MOD1
, 1, GDK_f
},
4863 /* vertical movement */
4864 { "scrolldown", 0, 0, GDK_j
},
4865 { "scrolldown", 0, 0, GDK_Down
},
4866 { "scrollup", 0, 0, GDK_Up
},
4867 { "scrollup", 0, 0, GDK_k
},
4868 { "scrollbottom", 0, 0, GDK_G
},
4869 { "scrollbottom", 0, 0, GDK_End
},
4870 { "scrolltop", 0, 0, GDK_Home
},
4871 { "scrolltop", 0, 0, GDK_g
},
4872 { "scrollpagedown", 0, 0, GDK_space
},
4873 { "scrollpagedown", CTRL
, 0, GDK_f
},
4874 { "scrollhalfdown", CTRL
, 0, GDK_d
},
4875 { "scrollpagedown", 0, 0, GDK_Page_Down
},
4876 { "scrollpageup", 0, 0, GDK_Page_Up
},
4877 { "scrollpageup", CTRL
, 0, GDK_b
},
4878 { "scrollhalfup", CTRL
, 0, GDK_u
},
4879 /* horizontal movement */
4880 { "scrollright", 0, 0, GDK_l
},
4881 { "scrollright", 0, 0, GDK_Right
},
4882 { "scrollleft", 0, 0, GDK_Left
},
4883 { "scrollleft", 0, 0, GDK_h
},
4884 { "scrollfarright", 0, 0, GDK_dollar
},
4885 { "scrollfarleft", 0, 0, GDK_0
},
4888 { "tabnew", CTRL
, 0, GDK_t
},
4889 { "999tabnew", CTRL
, 0, GDK_T
},
4890 { "tabclose", CTRL
, 1, GDK_w
},
4891 { "tabundoclose", 0, 0, GDK_U
},
4892 { "tabnext 1", CTRL
, 0, GDK_1
},
4893 { "tabnext 2", CTRL
, 0, GDK_2
},
4894 { "tabnext 3", CTRL
, 0, GDK_3
},
4895 { "tabnext 4", CTRL
, 0, GDK_4
},
4896 { "tabnext 5", CTRL
, 0, GDK_5
},
4897 { "tabnext 6", CTRL
, 0, GDK_6
},
4898 { "tabnext 7", CTRL
, 0, GDK_7
},
4899 { "tabnext 8", CTRL
, 0, GDK_8
},
4900 { "tabnext 9", CTRL
, 0, GDK_9
},
4901 { "tabnext 10", CTRL
, 0, GDK_0
},
4902 { "tabfirst", CTRL
, 0, GDK_less
},
4903 { "tablast", CTRL
, 0, GDK_greater
},
4904 { "tabprevious", CTRL
, 0, GDK_Left
},
4905 { "tabnext", CTRL
, 0, GDK_Right
},
4906 { "focusout", CTRL
, 0, GDK_minus
},
4907 { "focusin", CTRL
, 0, GDK_plus
},
4908 { "focusin", CTRL
, 0, GDK_equal
},
4910 /* command aliases (handy when -S flag is used) */
4911 { "promptopen", 0, 0, GDK_F9
},
4912 { "promptopencurrent", 0, 0, GDK_F10
},
4913 { "prompttabnew", 0, 0, GDK_F11
},
4914 { "prompttabnewcurrent",0, 0, GDK_F12
},
4916 TAILQ_HEAD(keybinding_list
, key_binding
);
4919 walk_kb(struct settings
*s
,
4920 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4922 struct key_binding
*k
;
4925 if (s
== NULL
|| cb
== NULL
) {
4926 show_oops(NULL
, "walk_kb invalid parameters");
4930 TAILQ_FOREACH(k
, &kbl
, entry
) {
4936 if (gdk_keyval_name(k
->key
) == NULL
)
4939 strlcat(str
, k
->cmd
, sizeof str
);
4940 strlcat(str
, ",", sizeof str
);
4942 if (k
->mask
& GDK_SHIFT_MASK
)
4943 strlcat(str
, "S-", sizeof str
);
4944 if (k
->mask
& GDK_CONTROL_MASK
)
4945 strlcat(str
, "C-", sizeof str
);
4946 if (k
->mask
& GDK_MOD1_MASK
)
4947 strlcat(str
, "M1-", sizeof str
);
4948 if (k
->mask
& GDK_MOD2_MASK
)
4949 strlcat(str
, "M2-", sizeof str
);
4950 if (k
->mask
& GDK_MOD3_MASK
)
4951 strlcat(str
, "M3-", sizeof str
);
4952 if (k
->mask
& GDK_MOD4_MASK
)
4953 strlcat(str
, "M4-", sizeof str
);
4954 if (k
->mask
& GDK_MOD5_MASK
)
4955 strlcat(str
, "M5-", sizeof str
);
4957 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4958 cb(s
, str
, cb_args
);
4963 init_keybindings(void)
4966 struct key_binding
*k
;
4968 for (i
= 0; i
< LENGTH(keys
); i
++) {
4969 k
= g_malloc0(sizeof *k
);
4970 k
->cmd
= keys
[i
].cmd
;
4971 k
->mask
= keys
[i
].mask
;
4972 k
->use_in_entry
= keys
[i
].use_in_entry
;
4973 k
->key
= keys
[i
].key
;
4974 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4976 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4977 k
->cmd
? k
->cmd
: "unnamed key");
4982 keybinding_clearall(void)
4984 struct key_binding
*k
, *next
;
4986 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4987 next
= TAILQ_NEXT(k
, entry
);
4991 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4992 k
->cmd
? k
->cmd
: "unnamed key");
4993 TAILQ_REMOVE(&kbl
, k
, entry
);
4999 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5001 struct key_binding
*k
;
5002 guint keyval
, mask
= 0;
5005 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5007 /* Keys which are to be used in entry have been prefixed with an
5008 * exclamation mark. */
5012 /* find modifier keys */
5013 if (strstr(key
, "S-"))
5014 mask
|= GDK_SHIFT_MASK
;
5015 if (strstr(key
, "C-"))
5016 mask
|= GDK_CONTROL_MASK
;
5017 if (strstr(key
, "M1-"))
5018 mask
|= GDK_MOD1_MASK
;
5019 if (strstr(key
, "M2-"))
5020 mask
|= GDK_MOD2_MASK
;
5021 if (strstr(key
, "M3-"))
5022 mask
|= GDK_MOD3_MASK
;
5023 if (strstr(key
, "M4-"))
5024 mask
|= GDK_MOD4_MASK
;
5025 if (strstr(key
, "M5-"))
5026 mask
|= GDK_MOD5_MASK
;
5029 for (i
= strlen(key
) - 1; i
> 0; i
--)
5033 /* validate keyname */
5034 keyval
= gdk_keyval_from_name(key
);
5035 if (keyval
== GDK_VoidSymbol
) {
5036 warnx("invalid keybinding name %s", key
);
5039 /* must run this test too, gtk+ doesn't handle 10 for example */
5040 if (gdk_keyval_name(keyval
) == NULL
) {
5041 warnx("invalid keybinding name %s", key
);
5045 /* Remove eventual dupes. */
5046 TAILQ_FOREACH(k
, &kbl
, entry
)
5047 if (k
->key
== keyval
&& k
->mask
== mask
) {
5048 TAILQ_REMOVE(&kbl
, k
, entry
);
5054 k
= g_malloc0(sizeof *k
);
5055 k
->cmd
= g_strdup(cmd
);
5057 k
->use_in_entry
= use_in_entry
;
5060 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5065 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5066 k
->cmd
, gdk_keyval_name(keyval
));
5068 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5074 add_kb(struct settings
*s
, char *entry
)
5078 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5080 /* clearall is special */
5081 if (!strcmp(entry
, "clearall")) {
5082 keybinding_clearall();
5086 kb
= strstr(entry
, ",");
5092 return (keybinding_add(entry
, key
, key
[0] == '!'));
5098 int (*func
)(struct tab
*, struct karg
*);
5102 { "command", 0, command
, ':', 0 },
5103 { "search", 0, command
, '/', 0 },
5104 { "searchb", 0, command
, '?', 0 },
5105 { "togglesrc", 0, toggle_src
, 0, 0 },
5107 /* yanking and pasting */
5108 { "yankuri", 0, yank_uri
, 0, 0 },
5109 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5110 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5111 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5114 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5115 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5118 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5119 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5122 { "hinting", 0, hint
, 0, 0 },
5124 /* custom stylesheet */
5125 { "userstyle", 0, userstyle
, 0, 0 },
5128 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5129 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5130 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5131 { "reloadforce", 0, navaction
, XT_NAV_RELOAD_CACHE
, 0 },
5133 /* vertical movement */
5134 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5135 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5136 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5137 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5138 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5139 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5140 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5141 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5142 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5143 /* horizontal movement */
5144 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5145 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5146 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5147 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5150 { "favorites", 0, xtp_page_fl
, 0, 0 },
5151 { "fav", 0, xtp_page_fl
, 0, 0 },
5152 { "favadd", 0, add_favorite
, 0, 0 },
5154 { "qall", 0, quit
, 0, 0 },
5155 { "quitall", 0, quit
, 0, 0 },
5156 { "w", 0, save_tabs
, 0, 0 },
5157 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5158 { "help", 0, help
, 0, 0 },
5159 { "about", 0, about
, 0, 0 },
5160 { "stats", 0, stats
, 0, 0 },
5161 { "version", 0, about
, 0, 0 },
5164 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5165 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5166 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5167 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5168 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5169 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5170 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5171 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5172 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5173 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5174 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5176 /* cookie command */
5177 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5178 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5179 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5180 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5181 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5182 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5183 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5184 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5185 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5186 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5187 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5189 /* toplevel (domain) command */
5190 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5191 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5194 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5197 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5198 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5199 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5201 { "ca", 0, ca_cmd
, 0, 0 },
5202 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5203 { "dl", 0, xtp_page_dl
, 0, 0 },
5204 { "h", 0, xtp_page_hl
, 0, 0 },
5205 { "history", 0, xtp_page_hl
, 0, 0 },
5206 { "home", 0, go_home
, 0, 0 },
5207 { "restart", 0, restart
, 0, 0 },
5208 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5209 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5210 { "statushide", 0, statusaction
, XT_STATUSBAR_HIDE
, 0 },
5211 { "statusshow", 0, statusaction
, XT_STATUSBAR_SHOW
, 0 },
5213 { "print", 0, print_page
, 0, 0 },
5216 { "focusin", 0, resizetab
, 1, 0 },
5217 { "focusout", 0, resizetab
, -1, 0 },
5218 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5219 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5220 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5221 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5222 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5223 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5224 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5225 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5226 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5227 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5228 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5229 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5230 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5231 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5232 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5233 { "buffers", 0, buffers
, 0, 0 },
5234 { "ls", 0, buffers
, 0, 0 },
5235 { "tabs", 0, buffers
, 0, 0 },
5237 /* command aliases (handy when -S flag is used) */
5238 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5239 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5240 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5241 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5244 { "set", 0, set
, 0, 0 },
5245 { "fullscreen", 0, fullscreen
, 0, 0 },
5246 { "f", 0, fullscreen
, 0, 0 },
5249 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5250 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5251 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5252 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5253 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5260 } cmd_status
= {-1, 0};
5263 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5270 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5276 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5278 a
.i
= XT_NAV_FORWARD
;
5288 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5290 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5292 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5299 * cancel, remove, etc. downloads
5302 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5304 struct download find
, *d
= NULL
;
5306 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5308 /* some commands require a valid download id */
5309 if (cmd
!= XT_XTP_DL_LIST
) {
5310 /* lookup download in question */
5312 d
= RB_FIND(download_list
, &downloads
, &find
);
5315 show_oops(t
, "%s: no such download", __func__
);
5320 /* decide what to do */
5322 case XT_XTP_DL_CANCEL
:
5323 webkit_download_cancel(d
->download
);
5325 case XT_XTP_DL_REMOVE
:
5326 webkit_download_cancel(d
->download
); /* just incase */
5327 g_object_unref(d
->download
);
5328 RB_REMOVE(download_list
, &downloads
, d
);
5330 case XT_XTP_DL_LIST
:
5334 show_oops(t
, "%s: unknown command", __func__
);
5337 xtp_page_dl(t
, NULL
);
5341 * Actions on history, only does one thing for now, but
5342 * we provide the function for future actions
5345 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5347 struct history
*h
, *next
;
5351 case XT_XTP_HL_REMOVE
:
5352 /* walk backwards, as listed in reverse */
5353 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5354 next
= RB_PREV(history_list
, &hl
, h
);
5356 RB_REMOVE(history_list
, &hl
, h
);
5357 g_free((gpointer
) h
->title
);
5358 g_free((gpointer
) h
->uri
);
5365 case XT_XTP_HL_LIST
:
5366 /* Nothing - just xtp_page_hl() below */
5369 show_oops(t
, "%s: unknown command", __func__
);
5373 xtp_page_hl(t
, NULL
);
5376 /* remove a favorite */
5378 remove_favorite(struct tab
*t
, int index
)
5380 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5381 char *new_favs
, *tmp
;
5386 /* open favorites */
5387 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5389 if ((f
= fopen(file
, "r")) == NULL
) {
5390 show_oops(t
, "%s: can't open favorites: %s",
5391 __func__
, strerror(errno
));
5395 /* build a string which will become the new favroites file */
5396 new_favs
= g_strdup("");
5399 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5400 if (feof(f
) || ferror(f
))
5402 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5409 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5410 if (feof(f
) || ferror(f
)) {
5411 show_oops(t
, "%s: can't parse favorites %s",
5412 __func__
, strerror(errno
));
5417 /* as long as this isn't the one we are deleting add to file */
5420 new_favs
= g_strdup_printf("%s%s\n%s\n",
5421 new_favs
, title
, uri
);
5433 /* write back new favorites file */
5434 if ((f
= fopen(file
, "w")) == NULL
) {
5435 show_oops(t
, "%s: can't open favorites: %s",
5436 __func__
, strerror(errno
));
5440 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5453 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5456 case XT_XTP_FL_LIST
:
5457 /* nothing, just the below call to xtp_page_fl() */
5459 case XT_XTP_FL_REMOVE
:
5460 remove_favorite(t
, arg
);
5463 show_oops(t
, "%s: invalid favorites command", __func__
);
5467 xtp_page_fl(t
, NULL
);
5471 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5474 case XT_XTP_CL_LIST
:
5475 /* nothing, just xtp_page_cl() */
5477 case XT_XTP_CL_REMOVE
:
5481 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5485 xtp_page_cl(t
, NULL
);
5488 /* link an XTP class to it's session key and handler function */
5489 struct xtp_despatch
{
5492 void (*handle_func
)(struct tab
*, uint8_t, int);
5495 struct xtp_despatch xtp_despatches
[] = {
5496 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5497 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5498 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5499 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5500 { XT_XTP_INVALID
, NULL
, NULL
}
5504 * is the url xtp protocol? (xxxt://)
5505 * if so, parse and despatch correct bahvior
5508 parse_xtp_url(struct tab
*t
, const char *url
)
5510 char *dup
= NULL
, *p
, *last
;
5511 uint8_t n_tokens
= 0;
5512 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5513 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5518 * tokens array meaning:
5520 * tokens[1] = session key
5521 * tokens[2] = action
5522 * tokens[3] = optional argument
5525 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5527 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5530 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5532 /* split out the url */
5533 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5534 (p
= strtok_r(NULL
, "/", &last
))) {
5536 tokens
[n_tokens
++] = p
;
5539 /* should be atleast three fields 'class/seskey/command/arg' */
5543 dsp
= xtp_despatches
;
5544 req_class
= atoi(tokens
[0]);
5545 while (dsp
->xtp_class
) {
5546 if (dsp
->xtp_class
== req_class
) {
5553 /* did we find one atall? */
5554 if (dsp_match
== NULL
) {
5555 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5559 /* check session key and call despatch function */
5560 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5561 ret
= TRUE
; /* all is well, this was a valid xtp request */
5562 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5575 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5577 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5579 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5582 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5587 show_oops(t
, "activate_uri_entry_cb no uri");
5591 uri
+= strspn(uri
, "\t ");
5593 /* if xxxt:// treat specially */
5594 if (parse_xtp_url(t
, uri
))
5597 /* otherwise continue to load page normally */
5598 load_uri(t
, (gchar
*)uri
);
5603 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5605 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5606 char *newuri
= NULL
;
5609 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5612 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
5616 if (search_string
== NULL
) {
5617 show_oops(t
, "no search_string");
5621 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5622 newuri
= g_strdup_printf(search_string
, enc_search
);
5625 webkit_web_view_load_uri(t
->wv
, newuri
);
5633 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5635 struct domain
*d
= NULL
;
5638 if (uri
== NULL
|| t
== NULL
)
5641 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5646 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5647 es
? "enable" : "disable", uri
);
5649 g_object_set(G_OBJECT(t
->settings
),
5650 "enable-scripts", es
, (char *)NULL
);
5651 g_object_set(G_OBJECT(t
->settings
),
5652 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5653 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5655 button_set_stockid(t
->js_toggle
,
5656 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5660 show_ca_status(struct tab
*t
, const char *uri
)
5662 WebKitWebFrame
*frame
;
5663 WebKitWebDataSource
*source
;
5664 WebKitNetworkRequest
*request
;
5665 SoupMessage
*message
;
5667 gchar
*col_str
= XT_COLOR_WHITE
;
5670 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5671 ssl_strict_certs
, ssl_ca_file
, uri
);
5675 if (ssl_ca_file
== NULL
) {
5676 if (g_str_has_prefix(uri
, "http://"))
5678 if (g_str_has_prefix(uri
, "https://")) {
5679 col_str
= XT_COLOR_RED
;
5684 if (g_str_has_prefix(uri
, "http://") ||
5685 !g_str_has_prefix(uri
, "https://"))
5688 frame
= webkit_web_view_get_main_frame(t
->wv
);
5689 source
= webkit_web_frame_get_data_source(frame
);
5690 request
= webkit_web_data_source_get_request(source
);
5691 message
= webkit_network_request_get_message(request
);
5693 if (message
&& (soup_message_get_flags(message
) &
5694 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5695 col_str
= XT_COLOR_GREEN
;
5698 r
= load_compare_cert(t
, NULL
);
5700 col_str
= XT_COLOR_BLUE
;
5702 col_str
= XT_COLOR_YELLOW
;
5704 col_str
= XT_COLOR_RED
;
5709 gdk_color_parse(col_str
, &color
);
5710 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5712 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
5713 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5715 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5716 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5719 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5721 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5722 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5729 free_favicon(struct tab
*t
)
5731 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
5732 __func__
, t
->icon_download
, t
->icon_request
);
5734 if (t
->icon_request
)
5735 g_object_unref(t
->icon_request
);
5736 if (t
->icon_dest_uri
)
5737 g_free(t
->icon_dest_uri
);
5739 t
->icon_request
= NULL
;
5740 t
->icon_dest_uri
= NULL
;
5744 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5746 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5747 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5749 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5750 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5752 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5753 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5757 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
5759 GdkPixbuf
*pb_scaled
;
5761 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
5762 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16, GDK_INTERP_BILINEAR
);
5766 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5767 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5769 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5770 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5772 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5773 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5775 if (pb_scaled
!= pb
)
5776 g_object_unref(pb_scaled
);
5780 xt_icon_from_file(struct tab
*t
, char *file
)
5784 if (g_str_has_prefix(file
, "file://"))
5785 file
+= strlen("file://");
5787 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5789 xt_icon_from_pixbuf(t
, pb
);
5792 xt_icon_from_name(t
, "text-html");
5796 is_valid_icon(char *file
)
5799 const char *mime_type
;
5803 gf
= g_file_new_for_path(file
);
5804 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5806 mime_type
= g_file_info_get_content_type(fi
);
5807 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5808 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5809 g_strcmp0(mime_type
, "image/png") == 0 ||
5810 g_strcmp0(mime_type
, "image/gif") == 0 ||
5811 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5819 set_favicon_from_file(struct tab
*t
, char *file
)
5823 if (t
== NULL
|| file
== NULL
)
5826 if (g_str_has_prefix(file
, "file://"))
5827 file
+= strlen("file://");
5828 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5830 if (!stat(file
, &sb
)) {
5831 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5832 /* corrupt icon so trash it */
5833 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5836 /* no need to set icon to default here */
5840 xt_icon_from_file(t
, file
);
5844 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5847 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5848 struct tab
*tt
= NULL
, *t
= NULL
;
5851 * find the webview instead of passing in the tab as it could have been
5852 * deleted from underneath us.
5854 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5863 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5864 __func__
, t
->tab_id
, status
);
5867 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5869 t
->icon_download
= NULL
;
5872 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5875 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5878 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5880 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5881 __func__
, t
->tab_id
);
5882 t
->icon_download
= NULL
;
5885 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5888 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5889 __func__
, t
->icon_dest_uri
);
5890 set_favicon_from_file(t
, t
->icon_dest_uri
);
5891 /* these will be freed post callback */
5892 t
->icon_request
= NULL
;
5893 t
->icon_download
= NULL
;
5901 abort_favicon_download(struct tab
*t
)
5903 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5905 if (!WEBKIT_CHECK_VERSION(1, 4, 0)) {
5906 if (t
->icon_download
) {
5907 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5908 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5909 webkit_download_cancel(t
->icon_download
);
5910 t
->icon_download
= NULL
;
5915 xt_icon_from_name(t
, "text-html");
5919 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5922 gchar
*name_hash
, file
[PATH_MAX
];
5925 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
5927 if (uri
== NULL
|| t
== NULL
)
5930 if (WEBKIT_CHECK_VERSION(1, 4, 0)) {
5931 /* take icon from WebKitIconDatabase */
5932 pb
= webkit_web_view_get_icon_pixbuf(wv
);
5934 xt_icon_from_pixbuf(t
, pb
);
5937 xt_icon_from_name(t
, "text-html");
5939 } else if (WEBKIT_CHECK_VERSION(1, 1, 18)) {
5940 /* download icon to cache dir */
5941 if (t
->icon_request
) {
5942 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5946 /* check to see if we got the icon in cache */
5947 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5948 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5951 if (!stat(file
, &sb
)) {
5952 if (sb
.st_size
> 0) {
5953 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5955 set_favicon_from_file(t
, file
);
5959 /* corrupt icon so trash it */
5960 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5965 /* create download for icon */
5966 t
->icon_request
= webkit_network_request_new(uri
);
5967 if (t
->icon_request
== NULL
) {
5968 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5973 t
->icon_download
= webkit_download_new(t
->icon_request
);
5974 if (t
->icon_download
== NULL
)
5977 /* we have to free icon_dest_uri later */
5978 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5979 webkit_download_set_destination_uri(t
->icon_download
,
5982 if (webkit_download_get_status(t
->icon_download
) ==
5983 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5984 g_object_unref(t
->icon_request
);
5985 g_free(t
->icon_dest_uri
);
5986 t
->icon_request
= NULL
;
5987 t
->icon_dest_uri
= NULL
;
5991 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5992 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5994 webkit_download_start(t
->icon_download
);
5999 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6001 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
6002 struct history
*h
, find
;
6003 const gchar
*s_loading
;
6006 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6007 webkit_web_view_get_load_status(wview
), get_uri(t
) ? get_uri(t
) : "NOTHING");
6010 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6014 switch (webkit_web_view_get_load_status(wview
)) {
6015 case WEBKIT_LOAD_PROVISIONAL
:
6017 abort_favicon_download(t
);
6018 #if GTK_CHECK_VERSION(2, 20, 0)
6019 gtk_widget_show(t
->spinner
);
6020 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6022 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6024 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6026 /* take focus if we are visible */
6032 case WEBKIT_LOAD_COMMITTED
:
6034 if ((uri
= get_uri(t
)) != NULL
) {
6035 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6041 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6044 /* check if js white listing is enabled */
6045 if (enable_js_whitelist
) {
6047 check_and_set_js(uri
, t
);
6053 show_ca_status(t
, uri
);
6055 /* we know enough to autosave the session */
6056 if (session_autosave
) {
6062 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6066 case WEBKIT_LOAD_FINISHED
:
6072 if (!strncmp(uri
, "http://", strlen("http://")) ||
6073 !strncmp(uri
, "https://", strlen("https://")) ||
6074 !strncmp(uri
, "file://", strlen("file://"))) {
6076 h
= RB_FIND(history_list
, &hl
, &find
);
6078 title
= webkit_web_view_get_title(wview
);
6079 set
= title
? title
: uri
;
6080 h
= g_malloc(sizeof *h
);
6081 h
->uri
= g_strdup(uri
);
6082 h
->title
= g_strdup(set
);
6083 RB_INSERT(history_list
, &hl
, h
);
6084 completion_add_uri(h
->uri
);
6085 update_history_tabs(NULL
);
6089 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6090 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6091 case WEBKIT_LOAD_FAILED
:
6094 #if GTK_CHECK_VERSION(2, 20, 0)
6095 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6096 gtk_widget_hide(t
->spinner
);
6098 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
6099 if (s_loading
&& !strcmp(s_loading
, "Loading"))
6100 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6102 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6107 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6109 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6110 webkit_web_view_can_go_back(wview
));
6112 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6113 webkit_web_view_can_go_forward(wview
));
6117 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6119 const gchar
*set
= NULL
, *title
= NULL
;
6121 title
= webkit_web_view_get_title(wview
);
6122 set
= title
? title
: get_uri(t
);
6124 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
6125 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), set
);
6126 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
6128 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6129 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), "(untitled)");
6130 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6135 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6137 run_script(t
, JS_HINTING
);
6141 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6143 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6144 progress
== 100 ? 0 : (double)progress
/ 100);
6145 if (show_url
== 0) {
6146 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
6147 progress
== 100 ? 0 : (double)progress
/ 100);
6152 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6153 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6154 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6157 WebKitWebNavigationReason reason
;
6158 struct domain
*d
= NULL
;
6161 show_oops(NULL
, "webview_npd_cb invalid parameters");
6165 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6167 webkit_network_request_get_uri(request
));
6169 uri
= (char *)webkit_network_request_get_uri(request
);
6171 /* if this is an xtp url, we don't load anything else */
6172 if (parse_xtp_url(t
, uri
))
6175 if (t
->ctrl_click
) {
6177 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6178 webkit_web_policy_decision_ignore(pd
);
6179 return (TRUE
); /* we made the decission */
6183 * This is a little hairy but it comes down to this:
6184 * when we run in whitelist mode we have to assist the browser in
6185 * opening the URL that it would have opened in a new tab.
6187 reason
= webkit_web_navigation_action_get_reason(na
);
6188 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6189 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6190 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6191 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6193 webkit_web_policy_decision_use(pd
);
6194 return (TRUE
); /* we made the decision */
6201 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6204 struct domain
*d
= NULL
;
6206 WebKitWebView
*webview
= NULL
;
6208 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6209 webkit_web_view_get_uri(wv
));
6212 /* open in current tab */
6214 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6215 uri
= webkit_web_view_get_uri(wv
);
6216 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6219 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6221 } else if (enable_scripts
== 1) {
6222 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6230 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6233 struct domain
*d
= NULL
;
6235 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6237 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6238 uri
= webkit_web_view_get_uri(wv
);
6239 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6243 } else if (enable_scripts
== 1)
6250 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6252 /* we can not eat the event without throwing gtk off so defer it */
6254 /* catch middle click */
6255 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6260 /* catch ctrl click */
6261 if (e
->type
== GDK_BUTTON_RELEASE
&&
6262 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6267 return (XT_CB_PASSTHROUGH
);
6271 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6273 struct mime_type
*m
;
6275 m
= find_mime_type(mime_type
);
6283 show_oops(t
, "can't fork mime handler");
6292 execlp(m
->mt_action
, m
->mt_action
,
6293 webkit_network_request_get_uri(request
), (void *)NULL
);
6302 get_mime_type(char *file
)
6304 const char *mime_type
;
6308 if (g_str_has_prefix(file
, "file://"))
6309 file
+= strlen("file://");
6311 gf
= g_file_new_for_path(file
);
6312 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6314 mime_type
= g_file_info_get_content_type(fi
);
6322 run_download_mimehandler(char *mime_type
, char *file
)
6324 struct mime_type
*m
;
6326 m
= find_mime_type(mime_type
);
6332 show_oops(NULL
, "can't fork download mime handler");
6342 if (g_str_has_prefix(file
, "file://"))
6343 file
+= strlen("file://");
6344 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6353 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6356 WebKitDownloadStatus status
;
6357 const gchar
*file
= NULL
, *mime
= NULL
;
6359 if (download
== NULL
)
6361 status
= webkit_download_get_status(download
);
6362 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6365 file
= webkit_download_get_destination_uri(download
);
6368 mime
= get_mime_type((char *)file
);
6372 run_download_mimehandler((char *)mime
, (char *)file
);
6376 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6377 WebKitNetworkRequest
*request
, char *mime_type
,
6378 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6381 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6385 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6386 t
->tab_id
, mime_type
);
6388 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6389 webkit_web_policy_decision_ignore(decision
);
6394 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6395 webkit_web_policy_decision_download(decision
);
6403 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6407 const gchar
*suggested_name
;
6408 gchar
*filename
= NULL
;
6410 struct download
*download_entry
;
6413 if (wk_download
== NULL
|| t
== NULL
) {
6414 show_oops(NULL
, "%s invalid parameters", __func__
);
6418 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6419 if (suggested_name
== NULL
)
6420 return (FALSE
); /* abort download */
6431 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6433 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6434 filename
: suggested_name
);
6436 } while (!stat(uri
+ strlen("file://"), &sb
));
6438 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6439 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6441 webkit_download_set_destination_uri(wk_download
, uri
);
6443 if (webkit_download_get_status(wk_download
) ==
6444 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6445 show_oops(t
, "%s: download failed to start", __func__
);
6447 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6449 /* connect "download first" mime handler */
6450 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6451 G_CALLBACK(download_status_changed_cb
), NULL
);
6453 download_entry
= g_malloc(sizeof(struct download
));
6454 download_entry
->download
= wk_download
;
6455 download_entry
->tab
= t
;
6456 download_entry
->id
= next_download_id
++;
6457 RB_INSERT(download_list
, &downloads
, download_entry
);
6458 /* get from history */
6459 g_object_ref(wk_download
);
6460 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6461 show_oops(t
, "Download of '%s' started...",
6462 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6471 /* sync other download manager tabs */
6472 update_download_tabs(NULL
);
6475 * NOTE: never redirect/render the current tab before this
6476 * function returns. This will cause the download to never start.
6478 return (ret
); /* start download */
6482 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6484 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6487 show_oops(NULL
, "webview_hover_cb");
6492 set_status(t
, uri
, XT_STATUS_LINK
);
6495 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6500 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6502 struct key_binding
*k
;
6504 TAILQ_FOREACH(k
, &kbl
, entry
)
6505 if (e
->keyval
== k
->key
&& (entry
? k
->use_in_entry
: 1)) {
6507 if ((e
->state
& (CTRL
| MOD1
)) == 0)
6508 return (cmd_execute(t
, k
->cmd
));
6509 } else if ((e
->state
& k
->mask
) == k
->mask
) {
6510 return (cmd_execute(t
, k
->cmd
));
6514 return (XT_CB_PASSTHROUGH
);
6518 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6520 char s
[2], buf
[128];
6521 const char *errstr
= NULL
;
6524 /* don't use w directly; use t->whatever instead */
6527 show_oops(NULL
, "wv_keypress_after_cb");
6528 return (XT_CB_PASSTHROUGH
);
6531 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6532 e
->keyval
, e
->state
, t
);
6536 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6538 return (XT_CB_HANDLED
);
6542 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6543 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6545 /* we have a string */
6547 /* we have a number */
6548 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6556 /* XXX unfuck this */
6557 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6558 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6559 /* last input was numerical */
6561 l
= strlen(t
->hint_num
);
6568 t
->hint_num
[l
] = '\0';
6572 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6573 /* last input was alphanumerical */
6575 l
= strlen(t
->hint_buf
);
6582 t
->hint_buf
[l
] = '\0';
6592 /* numerical input */
6593 if (CLEAN(e
->state
) == 0 &&
6594 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6595 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6596 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6597 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6600 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6602 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6605 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6607 t
->hint_mode
= XT_HINT_NUMERICAL
;
6611 /* empty the counter buffer */
6612 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6613 return (XT_CB_HANDLED
);
6616 /* alphanumerical input */
6618 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6619 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6620 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6621 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6622 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6623 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6624 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6627 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6630 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6632 t
->hint_mode
= XT_HINT_ALPHANUM
;
6635 /* empty the counter buffer */
6636 bzero(t
->hint_num
, sizeof t
->hint_num
);
6637 return (XT_CB_HANDLED
);
6640 return (XT_CB_HANDLED
);
6643 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6644 if (CLEAN(e
->state
) == 0 && isdigit(s
[0])) {
6645 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
6649 return (handle_keypress(t
, e
, 0));
6653 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6657 /* Hide buffers, if they are visible, with escape. */
6658 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
6659 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6660 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6662 return (XT_CB_HANDLED
);
6665 return (XT_CB_PASSTHROUGH
);
6669 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6671 const gchar
*c
= gtk_entry_get_text(w
);
6675 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6676 e
->keyval
, e
->state
, t
);
6679 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
6680 return (XT_CB_PASSTHROUGH
);
6683 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6684 e
->keyval
, e
->state
, t
);
6688 if (strlen(c
) == 1) {
6689 webkit_web_view_unmark_text_matches(t
->wv
);
6695 else if (c
[0] == '?')
6701 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6703 /* not found, mark red */
6704 gdk_color_parse(XT_COLOR_RED
, &color
);
6705 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6706 /* unmark and remove selection */
6707 webkit_web_view_unmark_text_matches(t
->wv
);
6708 /* my kingdom for a way to unselect text in webview */
6710 /* found, highlight all */
6711 webkit_web_view_unmark_text_matches(t
->wv
);
6712 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6713 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6714 gdk_color_parse(XT_COLOR_WHITE
, &color
);
6715 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6718 return (XT_CB_PASSTHROUGH
);
6722 match_uri(const gchar
*uri
, const gchar
*key
) {
6725 gboolean match
= FALSE
;
6729 if (!strncmp(key
, uri
, len
))
6732 voffset
= strstr(uri
, "/") + 2;
6733 if (!strncmp(key
, voffset
, len
))
6735 else if (g_str_has_prefix(voffset
, "www.")) {
6736 voffset
= voffset
+ strlen("www.");
6737 if (!strncmp(key
, voffset
, len
))
6746 cmd_getlist(int id
, char *key
)
6751 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
6752 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
6753 if (match_uri(h
->uri
, key
)) {
6754 cmd_status
.list
[c
] = (char *)h
->uri
;
6763 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
6765 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
6766 if(cmds
[i
].level
< dep
)
6768 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
, strlen(key
)))
6769 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
6777 cmd_getnext(int dir
)
6779 cmd_status
.index
+= dir
;
6781 if (cmd_status
.index
< 0)
6782 cmd_status
.index
= cmd_status
.len
- 1;
6783 else if (cmd_status
.index
>= cmd_status
.len
)
6784 cmd_status
.index
= 0;
6786 return cmd_status
.list
[cmd_status
.index
];
6790 cmd_tokenize(char *s
, char *tokens
[])
6794 size_t len
= strlen(s
);
6795 bool blank
= len
== 0 || (len
> 0 && s
[len
-1] == ' ');
6797 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3; tok
= strtok_r(NULL
, " ", &last
), i
++)
6807 cmd_complete(struct tab
*t
, char *str
, int dir
)
6809 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6810 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1, matchcount
= 0;
6811 char *tok
, *match
, *s
= g_strdup(str
);
6813 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
6816 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
6819 for (i
= 0; isdigit(s
[i
]); i
++)
6822 for (; isspace(s
[i
]); i
++)
6827 levels
= cmd_tokenize(s
, tokens
);
6829 for (i
= 0; i
< levels
- 1; i
++) {
6832 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6833 if (cmds
[j
].level
< dep
)
6835 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, strlen(tok
))) {
6838 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
6845 if (matchcount
== 1) {
6846 strlcat(res
, tok
, sizeof res
);
6847 strlcat(res
, " ", sizeof res
);
6857 if (cmd_status
.index
== -1)
6858 cmd_getlist(parent
, tokens
[i
]);
6860 if (cmd_status
.len
> 0) {
6861 match
= cmd_getnext(dir
);
6862 strlcat(res
, match
, sizeof res
);
6863 gtk_entry_set_text(w
, res
);
6864 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6871 cmd_execute(struct tab
*t
, char *str
)
6873 struct cmd
*cmd
= NULL
;
6874 char *tok
, *last
, *s
= g_strdup(str
), *sc
, prefixstr
[4];
6875 int j
, len
, c
= 0, dep
= 0, matchcount
= 0, prefix
= -1;
6876 struct karg arg
= {0, NULL
, -1};
6877 int rv
= XT_CB_PASSTHROUGH
;
6882 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
6888 while (isspace(s
[0]))
6891 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
6892 prefix
= atoi(prefixstr
);
6896 for (tok
= strtok_r(s
, " ", &last
); tok
;
6897 tok
= strtok_r(NULL
, " ", &last
)) {
6899 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6900 if (cmds
[j
].level
< dep
)
6902 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1: strlen(tok
);
6903 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, len
)) {
6907 if (len
== strlen(cmds
[j
].cmd
)) {
6913 if (matchcount
== 1) {
6918 show_oops(t
, "Invalid command: %s", str
);
6927 else if (cmd_prefix
> 0)
6930 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.p
> -1) {
6931 show_oops(t
, "No prefix allowed: %s", str
);
6935 arg
.s
= last
? g_strdup(last
) : g_strdup("");
6936 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
6937 arg
.p
= atoi(arg
.s
);
6940 show_oops(t
, "Zero count");
6942 show_oops(t
, "Trailing characters");
6947 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n", __func__
, arg
.p
, arg
.s
);
6963 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6966 show_oops(NULL
, "entry_key_cb invalid parameters");
6967 return (XT_CB_PASSTHROUGH
);
6970 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6971 e
->keyval
, e
->state
, t
);
6975 if (e
->keyval
== GDK_Escape
) {
6976 /* don't use focus_webview(t) because we want to type :cmds */
6977 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6980 return (handle_keypress(t
, e
, 1));
6984 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6986 int rv
= XT_CB_HANDLED
;
6987 const gchar
*c
= gtk_entry_get_text(w
);
6990 show_oops(NULL
, "cmd_keypress_cb parameters");
6991 return (XT_CB_PASSTHROUGH
);
6994 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6995 e
->keyval
, e
->state
, t
);
6999 e
->keyval
= GDK_Escape
;
7000 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7001 e
->keyval
= GDK_Escape
;
7003 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&& e
->keyval
!= GDK_ISO_Left_Tab
)
7004 cmd_status
.index
= -1;
7006 switch (e
->keyval
) {
7009 cmd_complete(t
, (char *)&c
[1], 1);
7011 case GDK_ISO_Left_Tab
:
7013 cmd_complete(t
, (char *)&c
[1], -1);
7017 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7025 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7026 webkit_web_view_unmark_text_matches(t
->wv
);
7030 rv
= XT_CB_PASSTHROUGH
;
7036 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7039 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7040 return (XT_CB_PASSTHROUGH
);
7042 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7047 if (show_url
== 0 || t
->focus_wv
)
7050 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7052 return (XT_CB_PASSTHROUGH
);
7056 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7059 const gchar
*c
= gtk_entry_get_text(entry
);
7062 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7066 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7073 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7079 if (c
[0] == '/' || c
[0] == '?') {
7080 if (t
->search_text
) {
7081 g_free(t
->search_text
);
7082 t
->search_text
= NULL
;
7085 t
->search_text
= g_strdup(s
);
7087 g_free(global_search
);
7088 global_search
= g_strdup(s
);
7089 t
->search_forward
= c
[0] == '/';
7101 backward_cb(GtkWidget
*w
, struct tab
*t
)
7106 show_oops(NULL
, "backward_cb invalid parameters");
7110 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
7117 forward_cb(GtkWidget
*w
, struct tab
*t
)
7122 show_oops(NULL
, "forward_cb invalid parameters");
7126 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
7128 a
.i
= XT_NAV_FORWARD
;
7133 home_cb(GtkWidget
*w
, struct tab
*t
)
7136 show_oops(NULL
, "home_cb invalid parameters");
7140 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
7146 stop_cb(GtkWidget
*w
, struct tab
*t
)
7148 WebKitWebFrame
*frame
;
7151 show_oops(NULL
, "stop_cb invalid parameters");
7155 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
7157 frame
= webkit_web_view_get_main_frame(t
->wv
);
7158 if (frame
== NULL
) {
7159 show_oops(t
, "stop_cb: no frame");
7163 webkit_web_frame_stop_loading(frame
);
7164 abort_favicon_download(t
);
7168 setup_webkit(struct tab
*t
)
7170 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
7171 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
7172 FALSE
, (char *)NULL
);
7174 warnx("webkit does not have \"enable-dns-prefetching\" property");
7175 g_object_set(G_OBJECT(t
->settings
),
7176 "user-agent", t
->user_agent
, (char *)NULL
);
7177 g_object_set(G_OBJECT(t
->settings
),
7178 "enable-scripts", enable_scripts
, (char *)NULL
);
7179 g_object_set(G_OBJECT(t
->settings
),
7180 "enable-plugins", enable_plugins
, (char *)NULL
);
7181 g_object_set(G_OBJECT(t
->settings
),
7182 "javascript-can-open-windows-automatically", enable_scripts
, (char *)NULL
);
7183 g_object_set(G_OBJECT(t
->settings
),
7184 "enable-html5-database", FALSE
, (char *)NULL
);
7185 g_object_set(G_OBJECT(t
->settings
),
7186 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
7187 g_object_set(G_OBJECT(t
->settings
),
7188 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
7189 g_object_set(G_OBJECT(t
->settings
),
7190 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
7191 g_object_set(G_OBJECT(t
->wv
),
7192 "full-content-zoom", TRUE
, (char *)NULL
);
7193 adjustfont_webkit(t
, XT_FONT_SET
);
7195 webkit_web_view_set_settings(t
->wv
, t
->settings
);
7199 create_browser(struct tab
*t
)
7205 show_oops(NULL
, "create_browser invalid parameters");
7209 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7210 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7211 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7212 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7214 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7215 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7216 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7218 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7219 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7222 t
->settings
= webkit_web_settings_new();
7224 if (user_agent
== NULL
) {
7225 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7227 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7230 t
->user_agent
= g_strdup(user_agent
);
7232 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7244 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7245 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7246 gtk_widget_set_name(w
, "xxxterm");
7247 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7248 g_signal_connect(G_OBJECT(w
), "delete_event",
7249 G_CALLBACK (gtk_main_quit
), NULL
);
7255 create_kiosk_toolbar(struct tab
*t
)
7257 GtkWidget
*toolbar
= NULL
, *b
;
7259 b
= gtk_hbox_new(FALSE
, 0);
7261 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7263 /* backward button */
7264 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7265 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7266 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7267 G_CALLBACK(backward_cb
), t
);
7268 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7270 /* forward button */
7271 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7272 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7273 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7274 G_CALLBACK(forward_cb
), t
);
7275 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7278 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7279 gtk_widget_set_sensitive(t
->gohome
, true);
7280 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7281 G_CALLBACK(home_cb
), t
);
7282 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7284 /* create widgets but don't use them */
7285 t
->uri_entry
= gtk_entry_new();
7286 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7287 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7288 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7294 create_toolbar(struct tab
*t
)
7296 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7298 b
= gtk_hbox_new(FALSE
, 0);
7300 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7303 /* backward button */
7304 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7305 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7306 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7307 G_CALLBACK(backward_cb
), t
);
7308 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7310 /* forward button */
7311 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7312 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7313 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7314 G_CALLBACK(forward_cb
), t
);
7315 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7319 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7320 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7321 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7322 G_CALLBACK(stop_cb
), t
);
7323 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7327 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7328 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7329 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7330 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7331 G_CALLBACK(js_toggle_cb
), t
);
7332 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7335 t
->uri_entry
= gtk_entry_new();
7336 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7337 G_CALLBACK(activate_uri_entry_cb
), t
);
7338 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7339 G_CALLBACK(entry_key_cb
), t
);
7341 eb1
= gtk_hbox_new(FALSE
, 0);
7342 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7343 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7344 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7347 if (fancy_bar
&& search_string
) {
7349 t
->search_entry
= gtk_entry_new();
7350 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7351 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7352 G_CALLBACK(activate_search_entry_cb
), t
);
7353 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7354 G_CALLBACK(entry_key_cb
), t
);
7355 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7356 eb2
= gtk_hbox_new(FALSE
, 0);
7357 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7358 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7360 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7366 create_buffers(struct tab
*t
)
7368 GtkCellRenderer
*renderer
;
7371 view
= gtk_tree_view_new();
7373 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
7375 renderer
= gtk_cell_renderer_text_new();
7376 gtk_tree_view_insert_column_with_attributes
7377 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
7379 renderer
= gtk_cell_renderer_text_new();
7380 gtk_tree_view_insert_column_with_attributes
7381 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
, NULL
);
7383 gtk_tree_view_set_model
7384 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
7390 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
7391 GtkTreeViewColumn
*col
, struct tab
*t
)
7396 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7398 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
, path
)) {
7400 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
7401 set_current_tab(id
- 1);
7407 /* after tab reordering/creation/removal */
7415 TAILQ_FOREACH(t
, &tabs
, entry
) {
7416 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7417 if (t
->tab_id
> maxid
)
7420 gtk_widget_show(t
->tab_elems
.sep
);
7423 curid
= gtk_notebook_get_current_page(notebook
);
7424 TAILQ_FOREACH(t
, &tabs
, entry
) {
7425 if (t
->tab_id
== maxid
) {
7426 gtk_widget_hide(t
->tab_elems
.sep
);
7432 /* after active tab change */
7434 recolor_compact_tabs(void)
7440 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7441 TAILQ_FOREACH(t
, &tabs
, entry
)
7442 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7444 curid
= gtk_notebook_get_current_page(notebook
);
7445 TAILQ_FOREACH(t
, &tabs
, entry
)
7446 if (t
->tab_id
== curid
) {
7447 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
7448 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7454 set_current_tab(int page_num
)
7456 gtk_notebook_set_current_page(notebook
, page_num
);
7457 recolor_compact_tabs();
7461 undo_close_tab_save(struct tab
*t
)
7465 struct undo
*u1
, *u2
;
7467 WebKitWebHistoryItem
*item
;
7469 if ((uri
= get_uri(t
)) == NULL
)
7472 u1
= g_malloc0(sizeof(struct undo
));
7473 u1
->uri
= g_strdup(uri
);
7475 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7477 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
7478 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
7481 /* forward history */
7482 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
7486 u1
->history
= g_list_prepend(u1
->history
,
7487 webkit_web_history_item_copy(item
));
7488 items
= g_list_next(items
);
7493 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
7494 u1
->history
= g_list_prepend(u1
->history
,
7495 webkit_web_history_item_copy(item
));
7499 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
7503 u1
->history
= g_list_prepend(u1
->history
,
7504 webkit_web_history_item_copy(item
));
7505 items
= g_list_next(items
);
7508 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
7510 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
7511 u2
= TAILQ_LAST(&undos
, undo_tailq
);
7512 TAILQ_REMOVE(&undos
, u2
, entry
);
7514 g_list_free(u2
->history
);
7523 delete_tab(struct tab
*t
)
7527 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
7532 TAILQ_REMOVE(&tabs
, t
, entry
);
7534 /* Halt all webkit activity. */
7535 abort_favicon_download(t
);
7536 webkit_web_view_stop_loading(t
->wv
);
7538 /* Save the tab, so we can undo the close. */
7539 undo_close_tab_save(t
);
7541 if (browser_mode
== XT_BM_KIOSK
) {
7542 gtk_widget_destroy(t
->uri_entry
);
7543 gtk_widget_destroy(t
->stop
);
7544 gtk_widget_destroy(t
->js_toggle
);
7547 gtk_widget_destroy(t
->tab_elems
.eventbox
);
7548 gtk_widget_destroy(t
->vbox
);
7550 g_free(t
->user_agent
);
7551 g_free(t
->stylesheet
);
7554 if (TAILQ_EMPTY(&tabs
)) {
7555 if (browser_mode
== XT_BM_KIOSK
)
7556 create_new_tab(home
, NULL
, 1, -1);
7558 create_new_tab(NULL
, NULL
, 1, -1);
7561 /* recreate session */
7562 if (session_autosave
) {
7568 recolor_compact_tabs();
7572 adjustfont_webkit(struct tab
*t
, int adjust
)
7577 show_oops(NULL
, "adjustfont_webkit invalid parameters");
7581 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7582 if (adjust
== XT_FONT_SET
) {
7583 t
->font_size
= default_font_size
;
7584 zoom
= default_zoom_level
;
7585 t
->font_size
+= adjust
;
7586 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
7587 t
->font_size
, (char *)NULL
);
7588 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
7589 &t
->font_size
, (char *)NULL
);
7591 t
->font_size
+= adjust
;
7592 zoom
+= adjust
/25.0;
7597 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
7598 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7602 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
7604 struct tab
*t
= (struct tab
*) data
;
7606 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
7608 switch (event
->button
) {
7610 set_current_tab(t
->tab_id
);
7621 page_reordered_cb(GtkWidget
*nb
, GtkWidget
*eventbox
, guint pn
, gpointer data
)
7623 struct tab
*t
= (struct tab
*) data
;
7625 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
7628 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, t
->tab_id
);
7634 append_tab(struct tab
*t
)
7639 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7640 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
7644 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
7649 WebKitWebHistoryItem
*item
;
7653 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
7655 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
7656 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
7660 t
= g_malloc0(sizeof *t
);
7662 if (title
== NULL
) {
7663 title
= "(untitled)";
7667 t
->vbox
= gtk_vbox_new(FALSE
, 0);
7669 /* label + button for tab */
7670 b
= gtk_hbox_new(FALSE
, 0);
7673 #if GTK_CHECK_VERSION(2, 20, 0)
7674 t
->spinner
= gtk_spinner_new();
7676 t
->label
= gtk_label_new(title
);
7677 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
7678 gtk_widget_set_size_request(t
->label
, 100, 0);
7679 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
7680 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
7681 gtk_widget_set_size_request(b
, 130, 0);
7683 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
7684 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
7685 #if GTK_CHECK_VERSION(2, 20, 0)
7686 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
7690 if (browser_mode
== XT_BM_KIOSK
)
7691 t
->toolbar
= create_kiosk_toolbar(t
);
7693 t
->toolbar
= create_toolbar(t
);
7695 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
7698 t
->browser_win
= create_browser(t
);
7699 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
7701 /* oops message for user feedback */
7702 t
->oops
= gtk_entry_new();
7703 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
7704 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
7705 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
7706 gdk_color_parse(XT_COLOR_RED
, &color
);
7707 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
7708 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
7711 t
->cmd
= gtk_entry_new();
7712 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
7713 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
7714 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
7715 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
7718 t
->statusbar
= gtk_entry_new();
7719 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
7720 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
7721 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
7722 gdk_color_parse(XT_COLOR_BLACK
, &color
);
7723 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7724 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7725 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7726 gtk_widget_modify_font(GTK_WIDGET(t
->statusbar
), statusbar_font
);
7727 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
7730 t
->buffers
= create_buffers(t
);
7731 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
7733 /* xtp meaning is normal by default */
7734 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7736 /* set empty favicon */
7737 xt_icon_from_name(t
, "text-html");
7739 /* and show it all */
7740 gtk_widget_show_all(b
);
7741 gtk_widget_show_all(t
->vbox
);
7743 /* compact tab bar */
7744 t
->tab_elems
.label
= gtk_label_new(title
);
7745 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
7746 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
7747 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
7749 t
->tab_elems
.eventbox
= gtk_event_box_new();
7750 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
7751 t
->tab_elems
.sep
= gtk_vseparator_new();
7753 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
7754 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
7755 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7756 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
7757 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
7758 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
7760 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
, TRUE
, 0);
7761 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
, FALSE
, 0);
7762 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
), t
->tab_elems
.box
);
7764 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
, TRUE
, 0);
7765 gtk_widget_show_all(t
->tab_elems
.eventbox
);
7767 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
7770 id
= position
>= 0 ? position
: gtk_notebook_get_current_page(notebook
) + 1;
7771 if (id
> gtk_notebook_get_n_pages(notebook
))
7774 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7775 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
7776 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, id
);
7781 #if GTK_CHECK_VERSION(2, 20, 0)
7782 /* turn spinner off if we are a new tab without uri */
7784 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7785 gtk_widget_hide(t
->spinner
);
7788 /* make notebook tabs reorderable */
7789 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
7791 /* compact tabs clickable */
7792 g_signal_connect(GTK_OBJECT(t
->tab_elems
.eventbox
),
7793 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
7795 g_signal_connect(GTK_OBJECT(notebook
),
7796 "page_reordered", G_CALLBACK(page_reordered_cb
), t
);
7798 g_object_connect(G_OBJECT(t
->cmd
),
7799 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
7800 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
7801 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
7802 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
7805 /* reuse wv_button_cb to hide oops */
7806 g_object_connect(G_OBJECT(t
->oops
),
7807 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7810 g_signal_connect(t
->buffers
,
7811 "row-activated", G_CALLBACK(row_activated_cb
), t
);
7812 g_object_connect(G_OBJECT(t
->buffers
),
7813 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
7815 g_object_connect(G_OBJECT(t
->wv
),
7816 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
7817 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7818 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
7819 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
7820 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
7821 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7822 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7823 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
7824 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
7825 "signal::event", G_CALLBACK(webview_event_cb
), t
,
7826 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
7827 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
7828 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
7829 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7831 g_signal_connect(t
->wv
,
7832 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
7833 g_signal_connect(t
->wv
,
7834 "notify::title", G_CALLBACK(notify_title_cb
), t
);
7836 /* hijack the unused keys as if we were the browser */
7837 g_object_connect(G_OBJECT(t
->toolbar
),
7838 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7841 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7842 G_CALLBACK(tab_close_cb
), t
);
7848 url_set_visibility();
7849 statusbar_set_visibility();
7852 set_current_tab(t
->tab_id
);
7853 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7858 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7862 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7867 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7868 /* restore the tab's history */
7869 if (u
&& u
->history
) {
7873 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7874 items
= g_list_next(items
);
7877 item
= g_list_nth_data(u
->history
, u
->back
);
7879 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7882 g_list_free(u
->history
);
7884 webkit_web_back_forward_list_clear(t
->bfl
);
7887 recolor_compact_tabs();
7892 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7898 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7900 if (gtk_notebook_get_current_page(notebook
) == -1)
7903 TAILQ_FOREACH(t
, &tabs
, entry
) {
7904 if (t
->tab_id
== pn
) {
7905 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7908 uri
= webkit_web_view_get_title(t
->wv
);
7911 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7917 /* can't use focus_webview here */
7918 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7925 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7932 menuitem_response(struct tab
*t
)
7934 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7938 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
7940 GtkWidget
*menu
, *menu_items
;
7941 GdkEventButton
*bevent
;
7945 if (event
->type
== GDK_BUTTON_PRESS
) {
7946 bevent
= (GdkEventButton
*) event
;
7947 menu
= gtk_menu_new();
7949 TAILQ_FOREACH(ti
, &tabs
, entry
) {
7950 if ((uri
= get_uri(ti
)) == NULL
)
7951 /* XXX make sure there is something to print */
7952 /* XXX add gui pages in here to look purdy */
7954 menu_items
= gtk_menu_item_new_with_label(uri
);
7955 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
7956 gtk_widget_show(menu_items
);
7958 g_signal_connect_swapped((menu_items
),
7959 "activate", G_CALLBACK(menuitem_response
),
7963 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
7964 bevent
->button
, bevent
->time
);
7966 /* unref object so it'll free itself when popped down */
7967 #if !GTK_CHECK_VERSION(3, 0, 0)
7968 /* XXX does not need unref with gtk+3? */
7969 g_object_ref_sink(menu
);
7970 g_object_unref(menu
);
7973 return (TRUE
/* eat event */);
7976 return (FALSE
/* propagate */);
7980 icon_size_map(int icon_size
)
7982 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
7983 icon_size
> GTK_ICON_SIZE_DIALOG
)
7984 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7990 create_button(char *name
, char *stockid
, int size
)
7992 GtkWidget
*button
, *image
;
7996 rcstring
= g_strdup_printf(
7997 "style \"%s-style\"\n"
7999 " GtkWidget::focus-padding = 0\n"
8000 " GtkWidget::focus-line-width = 0\n"
8004 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
8005 gtk_rc_parse_string(rcstring
);
8007 button
= gtk_button_new();
8008 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
8009 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
8011 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
8012 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8013 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
8014 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
8015 gtk_widget_set_name(button
, name
);
8016 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
8022 button_set_stockid(GtkWidget
*button
, char *stockid
)
8026 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
8027 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8028 gtk_button_set_image(GTK_BUTTON(button
), image
);
8032 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
8034 GtkClipboard
*clipboard
;
8035 gchar
*p
= NULL
, *s
= NULL
;
8038 * This code is very aggressive!
8039 * It basically ensures that the primary and regular clipboard are
8040 * always set the same. This obviously messes with standard X protocol
8041 * but those clowns should have come up with something better.
8044 /* XXX make this setting? */
8045 clipboard
= gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
);
8046 p
= gtk_clipboard_wait_for_text(primary
);
8048 DNPRINTF(XT_D_CLIP
, "primary cleaned\n");
8049 p
= gtk_clipboard_wait_for_text(clipboard
);
8051 gtk_clipboard_set_text(primary
, p
, -1);
8053 DNPRINTF(XT_D_CLIP
, "primary got selection\n");
8054 s
= gtk_clipboard_wait_for_text(clipboard
);
8057 * if s and p are the same the string was set by
8058 * clipb_clipboard_cb so do nothing in that case
8059 * to prevent endless loop
8064 gtk_clipboard_set_text(clipboard
, p
, -1);
8074 clipb_clipboard_cb(GtkClipboard
*clipboard
, GdkEvent
*event
, gpointer notused
)
8076 GtkClipboard
*primary
;
8077 gchar
*p
= NULL
, *s
= NULL
;
8079 DNPRINTF(XT_D_CLIP
, "clipboard got content\n");
8081 primary
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
8082 p
= gtk_clipboard_wait_for_text(clipboard
);
8084 s
= gtk_clipboard_wait_for_text(primary
);
8087 * if s and p are the same the string was set by
8088 * clipb_primary_cb so do nothing in that case
8089 * to prevent endless loop and deselection of text
8094 gtk_clipboard_set_text(primary
, p
, -1);
8109 char file
[PATH_MAX
];
8112 vbox
= gtk_vbox_new(FALSE
, 0);
8113 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
8114 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
8115 #if !GTK_CHECK_VERSION(3, 0, 0)
8116 /* XXX seems to be needed with gtk+2 */
8117 gtk_notebook_set_tab_hborder(notebook
, 0);
8118 gtk_notebook_set_tab_vborder(notebook
, 0);
8120 gtk_notebook_set_scrollable(notebook
, TRUE
);
8121 gtk_notebook_set_show_border(notebook
, FALSE
);
8122 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
8124 abtn
= gtk_button_new();
8125 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
8126 gtk_widget_set_size_request(arrow
, -1, -1);
8127 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
8128 gtk_widget_set_size_request(abtn
, -1, 20);
8130 #if GTK_CHECK_VERSION(2, 20, 0)
8131 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
8133 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
8135 /* compact tab bar */
8136 tab_bar
= gtk_hbox_new(TRUE
, 0);
8138 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
8139 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
8140 gtk_widget_set_size_request(vbox
, -1, -1);
8142 g_object_connect(G_OBJECT(notebook
),
8143 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
8145 g_object_connect(G_OBJECT(notebook
),
8146 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
), NULL
,
8148 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
8149 G_CALLBACK(arrow_cb
), NULL
);
8151 main_window
= create_window();
8152 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
8153 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
8156 for (i
= 0; i
< LENGTH(icons
); i
++) {
8157 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
8158 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
8159 l
= g_list_append(l
, pb
);
8161 gtk_window_set_default_icon_list(l
);
8164 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
8165 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
8166 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
)),
8167 "owner-change", G_CALLBACK(clipb_clipboard_cb
), NULL
);
8169 gtk_widget_show_all(abtn
);
8170 gtk_widget_show_all(main_window
);
8171 notebook_tab_set_visibility();
8175 set_hook(void **hook
, char *name
)
8178 errx(1, "set_hook");
8180 if (*hook
== NULL
) {
8181 *hook
= dlsym(RTLD_NEXT
, name
);
8183 errx(1, "can't hook %s", name
);
8187 /* override libsoup soup_cookie_equal because it doesn't look at domain */
8189 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
8191 g_return_val_if_fail(cookie1
, FALSE
);
8192 g_return_val_if_fail(cookie2
, FALSE
);
8194 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
8195 !strcmp (cookie1
->value
, cookie2
->value
) &&
8196 !strcmp (cookie1
->path
, cookie2
->path
) &&
8197 !strcmp (cookie1
->domain
, cookie2
->domain
));
8201 transfer_cookies(void)
8204 SoupCookie
*sc
, *pc
;
8206 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8208 for (;cf
; cf
= cf
->next
) {
8210 sc
= soup_cookie_copy(pc
);
8211 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
8214 soup_cookies_free(cf
);
8218 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
8223 print_cookie("soup_cookie_jar_delete_cookie", c
);
8225 if (cookies_enabled
== 0)
8228 if (jar
== NULL
|| c
== NULL
)
8231 /* find and remove from persistent jar */
8232 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8234 for (;cf
; cf
= cf
->next
) {
8236 if (soup_cookie_equal(ci
, c
)) {
8237 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
8242 soup_cookies_free(cf
);
8244 /* delete from session jar */
8245 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
8249 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
8251 struct domain
*d
= NULL
;
8255 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
8256 jar
, p_cookiejar
, s_cookiejar
);
8258 if (cookies_enabled
== 0)
8261 /* see if we are up and running */
8262 if (p_cookiejar
== NULL
) {
8263 _soup_cookie_jar_add_cookie(jar
, cookie
);
8266 /* disallow p_cookiejar adds, shouldn't happen */
8267 if (jar
== p_cookiejar
)
8271 if (jar
== NULL
|| cookie
== NULL
)
8274 if (enable_cookie_whitelist
&&
8275 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
8277 DNPRINTF(XT_D_COOKIE
,
8278 "soup_cookie_jar_add_cookie: reject %s\n",
8280 if (save_rejected_cookies
) {
8281 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
8282 show_oops(NULL
, "can't open reject cookie file");
8285 fseek(r_cookie_f
, 0, SEEK_END
);
8286 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
8287 cookie
->http_only
? "#HttpOnly_" : "",
8289 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
8291 cookie
->secure
? "TRUE" : "FALSE",
8293 (gulong
)soup_date_to_time_t(cookie
->expires
) :
8300 if (!allow_volatile_cookies
)
8304 if (cookie
->expires
== NULL
&& session_timeout
) {
8305 soup_cookie_set_expires(cookie
,
8306 soup_date_new_from_now(session_timeout
));
8307 print_cookie("modified add cookie", cookie
);
8310 /* see if we are white listed for persistence */
8311 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
8312 /* add to persistent jar */
8313 c
= soup_cookie_copy(cookie
);
8314 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
8315 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
8318 /* add to session jar */
8319 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
8320 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
8326 char file
[PATH_MAX
];
8328 set_hook((void *)&_soup_cookie_jar_add_cookie
,
8329 "soup_cookie_jar_add_cookie");
8330 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
8331 "soup_cookie_jar_delete_cookie");
8333 if (cookies_enabled
== 0)
8337 * the following code is intricate due to overriding several libsoup
8339 * do not alter order of these operations.
8342 /* rejected cookies */
8343 if (save_rejected_cookies
)
8344 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
, XT_REJECT_FILE
);
8346 /* persistent cookies */
8347 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
8348 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
8350 /* session cookies */
8351 s_cookiejar
= soup_cookie_jar_new();
8352 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
8353 cookie_policy
, (void *)NULL
);
8356 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
8360 setup_proxy(char *uri
)
8363 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
8364 soup_uri_free(proxy_uri
);
8368 if (http_proxy
!= uri
) {
8375 http_proxy
= g_strdup(uri
);
8376 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
8377 proxy_uri
= soup_uri_new(http_proxy
);
8378 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
8383 send_cmd_to_socket(char *cmd
)
8386 struct sockaddr_un sa
;
8388 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8389 warnx("%s: socket", __func__
);
8393 sa
.sun_family
= AF_UNIX
;
8394 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8395 work_dir
, XT_SOCKET_FILE
);
8398 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8399 warnx("%s: connect", __func__
);
8403 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
8404 warnx("%s: send", __func__
);
8415 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
8418 char str
[XT_MAX_URL_LENGTH
];
8419 socklen_t t
= sizeof(struct sockaddr_un
);
8420 struct sockaddr_un sa
;
8425 gint fd
= g_io_channel_unix_get_fd(source
);
8427 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
8432 if (getpeereid(s
, &uid
, &gid
) == -1) {
8436 if (uid
!= getuid() || gid
!= getgid()) {
8437 warnx("unauthorized user");
8443 warnx("not a valid user");
8447 n
= recv(s
, str
, sizeof(str
), 0);
8451 tt
= TAILQ_LAST(&tabs
, tab_list
);
8452 cmd_execute(tt
, str
);
8460 struct sockaddr_un sa
;
8462 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8463 warn("is_running: socket");
8467 sa
.sun_family
= AF_UNIX
;
8468 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8469 work_dir
, XT_SOCKET_FILE
);
8472 /* connect to see if there is a listener */
8473 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
8474 rv
= 0; /* not running */
8476 rv
= 1; /* already running */
8487 struct sockaddr_un sa
;
8489 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8490 warn("build_socket: socket");
8494 sa
.sun_family
= AF_UNIX
;
8495 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8496 work_dir
, XT_SOCKET_FILE
);
8499 /* connect to see if there is a listener */
8500 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8501 /* no listener so we will */
8502 unlink(sa
.sun_path
);
8504 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8505 warn("build_socket: bind");
8509 if (listen(s
, 1) == -1) {
8510 warn("build_socket: listen");
8523 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8524 GtkTreeIter
*iter
, struct tab
*t
)
8528 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8536 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8537 GtkTreeIter
*iter
, struct tab
*t
)
8541 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8542 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
8543 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
8550 completion_add_uri(const gchar
*uri
)
8554 /* add uri to list_store */
8555 gtk_list_store_append(completion_model
, &iter
);
8556 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
8560 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
8561 GtkTreeIter
*iter
, gpointer user_data
)
8564 gboolean match
= FALSE
;
8566 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
8572 match
= match_uri(value
, key
);
8579 completion_add(struct tab
*t
)
8581 /* enable completion for tab */
8582 t
->completion
= gtk_entry_completion_new();
8583 gtk_entry_completion_set_text_column(t
->completion
, 0);
8584 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
8585 gtk_entry_completion_set_model(t
->completion
,
8586 GTK_TREE_MODEL(completion_model
));
8587 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
8589 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
8590 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
8591 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
8592 G_CALLBACK(completion_select_cb
), t
);
8593 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
8594 G_CALLBACK(completion_hover_cb
), t
);
8602 if (stat(dir
, &sb
)) {
8603 if (mkdir(dir
, S_IRWXU
) == -1)
8604 err(1, "mkdir %s", dir
);
8606 err(1, "stat %s", dir
);
8608 if (S_ISDIR(sb
.st_mode
) == 0)
8609 errx(1, "%s not a dir", dir
);
8610 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8611 warnx("fixing invalid permissions on %s", dir
);
8612 if (chmod(dir
, S_IRWXU
) == -1)
8613 err(1, "chmod %s", dir
);
8621 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
8627 main(int argc
, char *argv
[])
8630 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
8631 char conf
[PATH_MAX
] = { '\0' };
8632 char file
[PATH_MAX
];
8633 char *env_proxy
= NULL
;
8636 struct sigaction sact
;
8637 GIOChannel
*channel
;
8642 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
8644 /* fiddle with ulimits */
8645 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8648 /* just use them all */
8649 rlp
.rlim_cur
= rlp
.rlim_max
;
8650 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8652 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
8654 else if (rlp
.rlim_cur
<= 256)
8655 warnx("%s requires at least 256 file descriptors",
8659 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
8668 errx(0 , "Version: %s", version
);
8671 strlcpy(conf
, optarg
, sizeof(conf
));
8674 strlcpy(named_session
, optarg
, sizeof(named_session
));
8695 RB_INIT(&downloads
);
8699 TAILQ_INIT(&aliases
);
8705 gnutls_global_init();
8707 /* generate session keys for xtp pages */
8708 generate_xtp_session_key(&dl_session_key
);
8709 generate_xtp_session_key(&hl_session_key
);
8710 generate_xtp_session_key(&cl_session_key
);
8711 generate_xtp_session_key(&fl_session_key
);
8714 gtk_init(&argc
, &argv
);
8715 if (!g_thread_supported())
8716 g_thread_init(NULL
);
8719 bzero(&sact
, sizeof(sact
));
8720 sigemptyset(&sact
.sa_mask
);
8721 sact
.sa_handler
= sigchild
;
8722 sact
.sa_flags
= SA_NOCLDSTOP
;
8723 sigaction(SIGCHLD
, &sact
, NULL
);
8725 /* set download dir */
8726 pwd
= getpwuid(getuid());
8728 errx(1, "invalid user %d", getuid());
8729 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
8731 /* set default string settings */
8732 home
= g_strdup("https://www.cyphertite.com");
8733 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
8734 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
8735 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
8736 cmd_font_name
= g_strdup("monospace normal 9");
8737 statusbar_font_name
= g_strdup("monospace normal 9");
8740 /* read config file */
8741 if (strlen(conf
) == 0)
8742 snprintf(conf
, sizeof conf
, "%s/.%s",
8743 pwd
->pw_dir
, XT_CONF_FILE
);
8744 config_parse(conf
, 0);
8747 cmd_font
= pango_font_description_from_string(cmd_font_name
);
8748 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
8750 /* working directory */
8751 if (strlen(work_dir
) == 0)
8752 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
8753 pwd
->pw_dir
, XT_DIR
);
8756 /* icon cache dir */
8757 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
8761 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
8765 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
8766 work_dir
, XT_SESSIONS_DIR
);
8767 xxx_dir(sessions_dir
);
8769 /* runtime settings that can override config file */
8770 if (runtime_settings
[0] != '\0')
8771 config_parse(runtime_settings
, 1);
8774 if (!strcmp(download_dir
, pwd
->pw_dir
))
8775 strlcat(download_dir
, "/downloads", sizeof download_dir
);
8776 xxx_dir(download_dir
);
8778 /* favorites file */
8779 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
8780 if (stat(file
, &sb
)) {
8781 warnx("favorites file doesn't exist, creating it");
8782 if ((f
= fopen(file
, "w")) == NULL
)
8783 err(1, "favorites");
8788 session
= webkit_get_default_session();
8793 if (stat(ssl_ca_file
, &sb
)) {
8794 warnx("no CA file: %s", ssl_ca_file
);
8795 g_free(ssl_ca_file
);
8798 g_object_set(session
,
8799 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
8800 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
8805 env_proxy
= getenv("http_proxy");
8807 setup_proxy(env_proxy
);
8809 setup_proxy(http_proxy
);
8812 send_cmd_to_socket(argv
[0]);
8816 /* set some connection parameters */
8817 /* XXX webkit 1.4.X overwrites these values! */
8818 /* https://bugs.webkit.org/show_bug.cgi?id=64355 */
8819 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
8820 g_object_set(session
, "max-conns-per-host", max_host_connections
,
8823 /* see if there is already an xxxterm running */
8824 if (single_instance
&& is_running()) {
8826 warnx("already running");
8832 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
8833 send_cmd_to_socket(cmd
);
8843 /* uri completion */
8844 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
8847 buffers_store
= gtk_list_store_new
8848 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
8852 notebook_tab_set_visibility();
8854 if (save_global_history
)
8855 restore_global_history();
8857 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
8858 restore_saved_tabs();
8860 a
.s
= named_session
;
8861 a
.i
= XT_SES_DONOTHING
;
8862 open_tabs(NULL
, &a
);
8866 create_new_tab(argv
[0], NULL
, focus
, -1);
8873 if (TAILQ_EMPTY(&tabs
))
8874 create_new_tab(home
, NULL
, 1, -1);
8877 if ((s
= build_socket()) != -1) {
8878 channel
= g_io_channel_unix_new(s
);
8879 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
8884 gnutls_global_deinit();