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>
63 #include <gdk/gdkkeysyms.h>
65 #if GTK_CHECK_VERSION(3,0,0)
66 /* we still use GDK_* instead of GDK_KEY_* */
67 #include <gdk/gdkkeysyms-compat.h>
70 #include <webkit/webkit.h>
71 #include <libsoup/soup.h>
72 #include <gnutls/gnutls.h>
73 #include <JavaScriptCore/JavaScript.h>
74 #include <gnutls/x509.h>
76 #include "javascript.h"
79 javascript.h borrowed from vimprobable2 under the following license:
81 Copyright (c) 2009 Leon Winter
82 Copyright (c) 2009 Hannes Schueller
83 Copyright (c) 2009 Matto Fransen
85 Permission is hereby granted, free of charge, to any person obtaining a copy
86 of this software and associated documentation files (the "Software"), to deal
87 in the Software without restriction, including without limitation the rights
88 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
89 copies of the Software, and to permit persons to whom the Software is
90 furnished to do so, subject to the following conditions:
92 The above copyright notice and this permission notice shall be included in
93 all copies or substantial portions of the Software.
95 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
96 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
97 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
98 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
99 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
100 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
104 static char *version
= "$xxxterm$";
106 /* hooked functions */
107 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
108 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
113 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
114 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
115 #define XT_D_MOVE 0x0001
116 #define XT_D_KEY 0x0002
117 #define XT_D_TAB 0x0004
118 #define XT_D_URL 0x0008
119 #define XT_D_CMD 0x0010
120 #define XT_D_NAV 0x0020
121 #define XT_D_DOWNLOAD 0x0040
122 #define XT_D_CONFIG 0x0080
123 #define XT_D_JS 0x0100
124 #define XT_D_FAVORITE 0x0200
125 #define XT_D_PRINTING 0x0400
126 #define XT_D_COOKIE 0x0800
127 #define XT_D_KEYBINDING 0x1000
128 #define XT_D_CLIP 0x2000
129 u_int32_t swm_debug
= 0
146 #define DPRINTF(x...)
147 #define DNPRINTF(n,x...)
150 #define LENGTH(x) (sizeof x / sizeof x[0])
151 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
152 ~(GDK_BUTTON1_MASK) & \
153 ~(GDK_BUTTON2_MASK) & \
154 ~(GDK_BUTTON3_MASK) & \
155 ~(GDK_BUTTON4_MASK) & \
167 TAILQ_ENTRY(tab
) entry
;
169 GtkWidget
*tab_content
;
172 GtkWidget
*uri_entry
;
173 GtkWidget
*search_entry
;
175 GtkWidget
*browser_win
;
176 GtkWidget
*statusbar
;
183 GtkWidget
*js_toggle
;
184 GtkEntryCompletion
*completion
;
188 WebKitWebHistoryItem
*item
;
189 WebKitWebBackForwardList
*bfl
;
192 WebKitNetworkRequest
*icon_request
;
193 WebKitDownload
*icon_download
;
194 gchar
*icon_dest_uri
;
196 /* adjustments for browser */
199 GtkAdjustment
*adjust_h
;
200 GtkAdjustment
*adjust_v
;
206 int xtp_meaning
; /* identifies dls/favorites */
211 #define XT_HINT_NONE (0)
212 #define XT_HINT_NUMERICAL (1)
213 #define XT_HINT_ALPHANUM (2)
217 /* custom stylesheet */
226 WebKitWebSettings
*settings
;
230 TAILQ_HEAD(tab_list
, tab
);
233 RB_ENTRY(history
) entry
;
237 RB_HEAD(history_list
, history
);
240 RB_ENTRY(download
) entry
;
242 WebKitDownload
*download
;
245 RB_HEAD(download_list
, download
);
248 RB_ENTRY(domain
) entry
;
250 int handy
; /* app use */
252 RB_HEAD(domain_list
, domain
);
255 TAILQ_ENTRY(undo
) entry
;
258 int back
; /* Keeps track of how many back
259 * history items there are. */
261 TAILQ_HEAD(undo_tailq
, undo
);
263 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
264 int next_download_id
= 1;
273 #define XT_NAME ("XXXTerm")
274 #define XT_DIR (".xxxterm")
275 #define XT_CACHE_DIR ("cache")
276 #define XT_CERT_DIR ("certs/")
277 #define XT_SESSIONS_DIR ("sessions/")
278 #define XT_CONF_FILE ("xxxterm.conf")
279 #define XT_FAVS_FILE ("favorites")
280 #define XT_SAVED_TABS_FILE ("main_session")
281 #define XT_RESTART_TABS_FILE ("restart_tabs")
282 #define XT_SOCKET_FILE ("socket")
283 #define XT_HISTORY_FILE ("history")
284 #define XT_REJECT_FILE ("rejected.txt")
285 #define XT_COOKIE_FILE ("cookies.txt")
286 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
287 #define XT_CB_HANDLED (TRUE)
288 #define XT_CB_PASSTHROUGH (FALSE)
289 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
290 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
291 #define XT_DLMAN_REFRESH "10"
292 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
293 "td{overflow: hidden;" \
294 " padding: 2px 2px 2px 2px;" \
295 " border: 1px solid black;" \
296 " vertical-align:top;" \
297 " word-wrap: break-word}\n" \
298 "tr:hover{background: #ffff99}\n" \
299 "th{background-color: #cccccc;" \
300 " border: 1px solid black}\n" \
301 "table{width: 100%%;" \
302 " border: 1px black solid;" \
303 " border-collapse:collapse}\n" \
305 "border: 1px solid black;" \
308 ".progress-inner{float: left;" \
310 " background: green}\n" \
311 ".dlstatus{font-size: small;" \
312 " text-align: center}\n" \
314 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
315 #define XT_MAX_UNDO_CLOSE_TAB (32)
316 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
317 #define XT_PRINT_EXTRA_MARGIN 10
320 #define XT_COLOR_RED "#cc0000"
321 #define XT_COLOR_YELLOW "#ffff66"
322 #define XT_COLOR_BLUE "lightblue"
323 #define XT_COLOR_GREEN "#99ff66"
324 #define XT_COLOR_WHITE "white"
325 #define XT_COLOR_BLACK "black"
328 * xxxterm "protocol" (xtp)
329 * We use this for managing stuff like downloads and favorites. They
330 * make magical HTML pages in memory which have xxxt:// links in order
331 * to communicate with xxxterm's internals. These links take the format:
332 * xxxt://class/session_key/action/arg
334 * Don't begin xtp class/actions as 0. atoi returns that on error.
336 * Typically we have not put addition of items in this framework, as
337 * adding items is either done via an ex-command or via a keybinding instead.
340 #define XT_XTP_STR "xxxt://"
342 /* XTP classes (xxxt://<class>) */
343 #define XT_XTP_INVALID 0 /* invalid */
344 #define XT_XTP_DL 1 /* downloads */
345 #define XT_XTP_HL 2 /* history */
346 #define XT_XTP_CL 3 /* cookies */
347 #define XT_XTP_FL 4 /* favorites */
349 /* XTP download actions */
350 #define XT_XTP_DL_LIST 1
351 #define XT_XTP_DL_CANCEL 2
352 #define XT_XTP_DL_REMOVE 3
354 /* XTP history actions */
355 #define XT_XTP_HL_LIST 1
356 #define XT_XTP_HL_REMOVE 2
358 /* XTP cookie actions */
359 #define XT_XTP_CL_LIST 1
360 #define XT_XTP_CL_REMOVE 2
362 /* XTP cookie actions */
363 #define XT_XTP_FL_LIST 1
364 #define XT_XTP_FL_REMOVE 2
366 /* xtp tab meanings - identifies which tabs have xtp pages in */
367 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
368 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
369 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
370 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
371 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
374 #define XT_MOVE_INVALID (0)
375 #define XT_MOVE_DOWN (1)
376 #define XT_MOVE_UP (2)
377 #define XT_MOVE_BOTTOM (3)
378 #define XT_MOVE_TOP (4)
379 #define XT_MOVE_PAGEDOWN (5)
380 #define XT_MOVE_PAGEUP (6)
381 #define XT_MOVE_HALFDOWN (7)
382 #define XT_MOVE_HALFUP (8)
383 #define XT_MOVE_LEFT (9)
384 #define XT_MOVE_FARLEFT (10)
385 #define XT_MOVE_RIGHT (11)
386 #define XT_MOVE_FARRIGHT (12)
388 #define XT_TAB_LAST (-4)
389 #define XT_TAB_FIRST (-3)
390 #define XT_TAB_PREV (-2)
391 #define XT_TAB_NEXT (-1)
392 #define XT_TAB_INVALID (0)
393 #define XT_TAB_NEW (1)
394 #define XT_TAB_DELETE (2)
395 #define XT_TAB_DELQUIT (3)
396 #define XT_TAB_OPEN (4)
397 #define XT_TAB_UNDO_CLOSE (5)
398 #define XT_TAB_SHOW (6)
399 #define XT_TAB_HIDE (7)
401 #define XT_NAV_INVALID (0)
402 #define XT_NAV_BACK (1)
403 #define XT_NAV_FORWARD (2)
404 #define XT_NAV_RELOAD (3)
405 #define XT_NAV_RELOAD_CACHE (4)
407 #define XT_FOCUS_INVALID (0)
408 #define XT_FOCUS_URI (1)
409 #define XT_FOCUS_SEARCH (2)
411 #define XT_SEARCH_INVALID (0)
412 #define XT_SEARCH_NEXT (1)
413 #define XT_SEARCH_PREV (2)
415 #define XT_PASTE_CURRENT_TAB (0)
416 #define XT_PASTE_NEW_TAB (1)
418 #define XT_FONT_SET (0)
420 #define XT_URL_SHOW (1)
421 #define XT_URL_HIDE (2)
423 #define XT_STATUSBAR_SHOW (1)
424 #define XT_STATUSBAR_HIDE (2)
426 #define XT_WL_TOGGLE (1<<0)
427 #define XT_WL_ENABLE (1<<1)
428 #define XT_WL_DISABLE (1<<2)
429 #define XT_WL_FQDN (1<<3) /* default */
430 #define XT_WL_TOPLEVEL (1<<4)
431 #define XT_WL_PERSISTENT (1<<5)
432 #define XT_WL_SESSION (1<<6)
433 #define XT_WL_RELOAD (1<<7)
435 #define XT_SHOW (1<<7)
436 #define XT_DELETE (1<<8)
437 #define XT_SAVE (1<<9)
438 #define XT_OPEN (1<<10)
440 #define XT_CMD_OPEN (0)
441 #define XT_CMD_OPEN_CURRENT (1)
442 #define XT_CMD_TABNEW (2)
443 #define XT_CMD_TABNEW_CURRENT (3)
445 #define XT_STATUS_NOTHING (0)
446 #define XT_STATUS_LINK (1)
447 #define XT_STATUS_URI (2)
448 #define XT_STATUS_LOADING (3)
450 #define XT_SES_DONOTHING (0)
451 #define XT_SES_CLOSETABS (1)
453 #define XT_BM_NORMAL (0)
454 #define XT_BM_WHITELIST (1)
455 #define XT_BM_KIOSK (2)
457 #define XT_PREFIX (1<<0)
458 #define XT_USERARG (1<<1)
459 #define XT_URLARG (1<<2)
460 #define XT_INTARG (1<<3)
468 TAILQ_ENTRY(mime_type
) entry
;
470 TAILQ_HEAD(mime_type_list
, mime_type
);
476 TAILQ_ENTRY(alias
) entry
;
478 TAILQ_HEAD(alias_list
, alias
);
480 /* settings that require restart */
481 int tabless
= 0; /* allow only 1 tab */
482 int enable_socket
= 0;
483 int single_instance
= 0; /* only allow one xxxterm to run */
484 int fancy_bar
= 1; /* fancy toolbar */
485 int browser_mode
= XT_BM_NORMAL
;
487 /* runtime settings */
488 int show_tabs
= 1; /* show tabs on notebook */
489 int show_url
= 1; /* show url toolbar on notebook */
490 int show_statusbar
= 0; /* vimperator style status bar */
491 int ctrl_click_focus
= 0; /* ctrl click gets focus */
492 int cookies_enabled
= 1; /* enable cookies */
493 int read_only_cookies
= 0; /* enable to not write cookies */
494 int enable_scripts
= 1;
495 int enable_plugins
= 0;
496 int default_font_size
= 12;
497 gfloat default_zoom_level
= 1.0;
498 int window_height
= 768;
499 int window_width
= 1024;
500 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
501 unsigned refresh_interval
= 10; /* download refresh interval */
502 int enable_cookie_whitelist
= 0;
503 int enable_js_whitelist
= 0;
504 time_t session_timeout
= 3600; /* cookie session timeout */
505 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
506 char *ssl_ca_file
= NULL
;
507 char *resource_dir
= NULL
;
508 gboolean ssl_strict_certs
= FALSE
;
509 int append_next
= 1; /* append tab after current tab */
511 char *search_string
= NULL
;
512 char *http_proxy
= NULL
;
513 char download_dir
[PATH_MAX
];
514 char runtime_settings
[PATH_MAX
]; /* override of settings */
515 int allow_volatile_cookies
= 0;
516 int save_global_history
= 0; /* save global history to disk */
517 char *user_agent
= NULL
;
518 int save_rejected_cookies
= 0;
519 time_t session_autosave
= 0;
520 int guess_search
= 0;
521 int dns_prefetch
= FALSE
;
522 gint max_connections
= 25;
523 gint max_host_connections
= 5;
524 gint enable_spell_checking
= 0;
525 char *spell_check_languages
= NULL
;
529 int set_download_dir(struct settings
*, char *);
530 int set_work_dir(struct settings
*, char *);
531 int set_runtime_dir(struct settings
*, char *);
532 int set_browser_mode(struct settings
*, char *);
533 int set_cookie_policy(struct settings
*, char *);
534 int add_alias(struct settings
*, char *);
535 int add_mime_type(struct settings
*, char *);
536 int add_cookie_wl(struct settings
*, char *);
537 int add_js_wl(struct settings
*, char *);
538 int add_kb(struct settings
*, char *);
539 void button_set_stockid(GtkWidget
*, char *);
540 GtkWidget
* create_button(char *, char *, int);
542 char *get_browser_mode(struct settings
*);
543 char *get_cookie_policy(struct settings
*);
545 char *get_download_dir(struct settings
*);
546 char *get_work_dir(struct settings
*);
547 char *get_runtime_dir(struct settings
*);
549 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
550 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
551 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
552 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
553 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
555 void recalc_tabs(void);
558 int (*set
)(struct settings
*, char *);
559 char *(*get
)(struct settings
*);
560 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
563 struct special s_browser_mode
= {
569 struct special s_cookie
= {
575 struct special s_alias
= {
581 struct special s_mime
= {
587 struct special s_js
= {
593 struct special s_kb
= {
599 struct special s_cookie_wl
= {
605 struct special s_download_dir
= {
611 struct special s_work_dir
= {
620 #define XT_S_INVALID (0)
623 #define XT_S_FLOAT (3)
625 #define XT_SF_RESTART (1<<0)
626 #define XT_SF_RUNTIME (1<<1)
632 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
633 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
634 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
635 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
636 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
637 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
638 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
639 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
640 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
641 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
642 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
643 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
644 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
645 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
646 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
647 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
648 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
649 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
650 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
651 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
652 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
653 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
654 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
655 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
656 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
657 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
658 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
659 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
660 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
661 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
662 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
663 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
664 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
665 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
666 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
667 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
668 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
669 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
670 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
671 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
672 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
674 /* runtime settings */
675 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
676 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
677 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
678 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
679 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
682 int about(struct tab
*, struct karg
*);
683 int blank(struct tab
*, struct karg
*);
684 int ca_cmd(struct tab
*, struct karg
*);
685 int cookie_show_wl(struct tab
*, struct karg
*);
686 int js_show_wl(struct tab
*, struct karg
*);
687 int help(struct tab
*, struct karg
*);
688 int set(struct tab
*, struct karg
*);
689 int stats(struct tab
*, struct karg
*);
690 int marco(struct tab
*, struct karg
*);
691 const char * marco_message(int *);
692 int xtp_page_cl(struct tab
*, struct karg
*);
693 int xtp_page_dl(struct tab
*, struct karg
*);
694 int xtp_page_fl(struct tab
*, struct karg
*);
695 int xtp_page_hl(struct tab
*, struct karg
*);
696 void xt_icon_from_file(struct tab
*, char *);
698 #define XT_URI_ABOUT ("about:")
699 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
700 #define XT_URI_ABOUT_ABOUT ("about")
701 #define XT_URI_ABOUT_BLANK ("blank")
702 #define XT_URI_ABOUT_CERTS ("certs") /* XXX NOT YET */
703 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
704 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
705 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
706 #define XT_URI_ABOUT_FAVORITES ("favorites")
707 #define XT_URI_ABOUT_HELP ("help")
708 #define XT_URI_ABOUT_HISTORY ("history")
709 #define XT_URI_ABOUT_JSWL ("jswl")
710 #define XT_URI_ABOUT_SET ("set")
711 #define XT_URI_ABOUT_STATS ("stats")
712 #define XT_URI_ABOUT_MARCO ("marco")
716 int (*func
)(struct tab
*, struct karg
*);
718 { XT_URI_ABOUT_ABOUT
, about
},
719 { XT_URI_ABOUT_BLANK
, blank
},
720 { XT_URI_ABOUT_CERTS
, ca_cmd
},
721 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
722 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
723 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
724 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
725 { XT_URI_ABOUT_HELP
, help
},
726 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
727 { XT_URI_ABOUT_JSWL
, js_show_wl
},
728 { XT_URI_ABOUT_SET
, set
},
729 { XT_URI_ABOUT_STATS
, stats
},
730 { XT_URI_ABOUT_MARCO
, marco
},
734 extern char *__progname
;
737 GtkWidget
*main_window
;
738 GtkNotebook
*notebook
;
739 GtkWidget
*arrow
, *abtn
;
740 struct tab_list tabs
;
741 struct history_list hl
;
742 struct download_list downloads
;
743 struct domain_list c_wl
;
744 struct domain_list js_wl
;
745 struct undo_tailq undos
;
746 struct keybinding_list kbl
;
748 int updating_dl_tabs
= 0;
749 int updating_hl_tabs
= 0;
750 int updating_cl_tabs
= 0;
751 int updating_fl_tabs
= 0;
753 uint64_t blocked_cookies
= 0;
754 char named_session
[PATH_MAX
];
755 int icon_size_map(int);
757 GtkListStore
*completion_model
;
758 void completion_add(struct tab
*);
759 void completion_add_uri(const gchar
*);
760 void xxx_dir(char *);
765 int saved_errno
, status
;
770 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
774 if (errno
!= ECHILD
) {
776 clog_warn("sigchild: waitpid:");
782 if (WIFEXITED(status
)) {
783 if (WEXITSTATUS(status
) != 0) {
785 clog_warnx("sigchild: child exit status: %d",
786 WEXITSTATUS(status));
791 clog_warnx("sigchild: child is terminated abnormally");
800 is_g_object_setting(GObject
*o
, char *str
)
802 guint n_props
= 0, i
;
803 GParamSpec
**proplist
;
805 if (! G_IS_OBJECT(o
))
808 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
811 for (i
=0; i
< n_props
; i
++) {
812 if (! strcmp(proplist
[i
]->name
, str
))
819 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
823 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
825 "<title>%s</title>\n"
834 addstyles
? XT_PAGE_STYLE
: "",
843 * Display a web page from a HTML string in memory, rather than from a URL
846 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
851 /* we set this to indicate we want to manually do navaction */
853 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
855 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "");
856 #if GTK_CHECK_VERSION(2, 20, 0)
857 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
858 gtk_widget_hide(t
->spinner
);
862 uri
= g_strdup_printf("%s%s", XT_URI_ABOUT
, title
);
863 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
866 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
867 xt_icon_from_file(t
, file
);
872 set_status(struct tab
*t
, gchar
*s
, int status
)
880 case XT_STATUS_LOADING
:
881 type
= g_strdup_printf("Loading: %s", s
);
885 type
= g_strdup_printf("Link: %s", s
);
887 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
891 type
= g_strdup_printf("%s", s
);
893 t
->status
= g_strdup(type
);
897 t
->status
= g_strdup(s
);
899 case XT_STATUS_NOTHING
:
904 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
910 hide_oops(struct tab
*t
)
912 gtk_widget_hide(t
->oops
);
916 hide_cmd(struct tab
*t
)
918 gtk_widget_hide(t
->cmd
);
922 show_cmd(struct tab
*t
)
924 gtk_widget_hide(t
->oops
);
925 gtk_widget_show(t
->cmd
);
929 show_oops(struct tab
*t
, const char *fmt
, ...)
938 if (vasprintf(&msg
, fmt
, ap
) == -1)
939 errx(1, "show_oops failed");
942 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
943 gtk_widget_hide(t
->cmd
);
944 gtk_widget_show(t
->oops
);
947 /* XXX collapse with show_oops */
949 show_oops_s(const char *fmt
, ...)
953 struct tab
*ti
, *t
= NULL
;
958 TAILQ_FOREACH(ti
, &tabs
, entry
)
959 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
967 if (vasprintf(&msg
, fmt
, ap
) == -1)
968 errx(1, "show_oops_s failed");
971 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
972 gtk_widget_hide(t
->cmd
);
973 gtk_widget_show(t
->oops
);
977 get_as_string(struct settings
*s
)
988 warnx("get_as_string skip %s\n", s
->name
);
989 } else if (s
->type
== XT_S_INT
)
990 r
= g_strdup_printf("%d", *s
->ival
);
991 else if (s
->type
== XT_S_STR
)
992 r
= g_strdup(*s
->sval
);
993 else if (s
->type
== XT_S_FLOAT
)
994 r
= g_strdup_printf("%f", *s
->fval
);
996 r
= g_strdup_printf("INVALID TYPE");
1002 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1007 for (i
= 0; i
< LENGTH(rs
); i
++) {
1008 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1009 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1011 s
= get_as_string(&rs
[i
]);
1012 cb(&rs
[i
], s
, cb_args
);
1019 set_browser_mode(struct settings
*s
, char *val
)
1021 if (!strcmp(val
, "whitelist")) {
1022 browser_mode
= XT_BM_WHITELIST
;
1023 allow_volatile_cookies
= 0;
1024 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1025 cookies_enabled
= 1;
1026 enable_cookie_whitelist
= 1;
1027 read_only_cookies
= 0;
1028 save_rejected_cookies
= 0;
1029 session_timeout
= 3600;
1031 enable_js_whitelist
= 1;
1032 } else if (!strcmp(val
, "normal")) {
1033 browser_mode
= XT_BM_NORMAL
;
1034 allow_volatile_cookies
= 0;
1035 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1036 cookies_enabled
= 1;
1037 enable_cookie_whitelist
= 0;
1038 read_only_cookies
= 0;
1039 save_rejected_cookies
= 0;
1040 session_timeout
= 3600;
1042 enable_js_whitelist
= 0;
1043 } else if (!strcmp(val
, "kiosk")) {
1044 browser_mode
= XT_BM_KIOSK
;
1045 allow_volatile_cookies
= 0;
1046 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1047 cookies_enabled
= 1;
1048 enable_cookie_whitelist
= 0;
1049 read_only_cookies
= 0;
1050 save_rejected_cookies
= 0;
1051 session_timeout
= 3600;
1053 enable_js_whitelist
= 0;
1063 get_browser_mode(struct settings
*s
)
1067 if (browser_mode
== XT_BM_WHITELIST
)
1068 r
= g_strdup("whitelist");
1069 else if (browser_mode
== XT_BM_NORMAL
)
1070 r
= g_strdup("normal");
1071 else if (browser_mode
== XT_BM_KIOSK
)
1072 r
= g_strdup("kiosk");
1080 set_cookie_policy(struct settings
*s
, char *val
)
1082 if (!strcmp(val
, "no3rdparty"))
1083 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1084 else if (!strcmp(val
, "accept"))
1085 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1086 else if (!strcmp(val
, "reject"))
1087 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1095 get_cookie_policy(struct settings
*s
)
1099 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1100 r
= g_strdup("no3rdparty");
1101 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1102 r
= g_strdup("accept");
1103 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1104 r
= g_strdup("reject");
1112 get_download_dir(struct settings
*s
)
1114 if (download_dir
[0] == '\0')
1116 return (g_strdup(download_dir
));
1120 set_download_dir(struct settings
*s
, char *val
)
1123 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1124 pwd
->pw_dir
, &val
[1]);
1126 strlcpy(download_dir
, val
, sizeof download_dir
);
1133 * We use these to prevent people putting xxxt:// URLs on
1134 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1136 #define XT_XTP_SES_KEY_SZ 8
1137 #define XT_XTP_SES_KEY_HEX_FMT \
1138 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1139 char *dl_session_key
; /* downloads */
1140 char *hl_session_key
; /* history list */
1141 char *cl_session_key
; /* cookie list */
1142 char *fl_session_key
; /* favorites list */
1144 char work_dir
[PATH_MAX
];
1145 char certs_dir
[PATH_MAX
];
1146 char cache_dir
[PATH_MAX
];
1147 char sessions_dir
[PATH_MAX
];
1148 char cookie_file
[PATH_MAX
];
1149 SoupURI
*proxy_uri
= NULL
;
1150 SoupSession
*session
;
1151 SoupCookieJar
*s_cookiejar
;
1152 SoupCookieJar
*p_cookiejar
;
1153 char rc_fname
[PATH_MAX
];
1155 struct mime_type_list mtl
;
1156 struct alias_list aliases
;
1159 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1160 void delete_tab(struct tab
*);
1161 void adjustfont_webkit(struct tab
*, int);
1162 int run_script(struct tab
*, char *);
1163 int download_rb_cmp(struct download
*, struct download
*);
1164 gboolean
cmd_execute(struct tab
*t
, char *str
);
1167 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1169 return (strcmp(h1
->uri
, h2
->uri
));
1171 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1174 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1176 return (strcmp(d1
->d
, d2
->d
));
1178 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1181 get_work_dir(struct settings
*s
)
1183 if (work_dir
[0] == '\0')
1185 return (g_strdup(work_dir
));
1189 set_work_dir(struct settings
*s
, char *val
)
1192 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1193 pwd
->pw_dir
, &val
[1]);
1195 strlcpy(work_dir
, val
, sizeof work_dir
);
1201 * generate a session key to secure xtp commands.
1202 * pass in a ptr to the key in question and it will
1203 * be modified in place.
1206 generate_xtp_session_key(char **key
)
1208 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1214 /* make a new one */
1215 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1216 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1217 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1218 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1220 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1224 * validate a xtp session key.
1228 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1230 if (strcmp(trusted
, untrusted
) != 0) {
1231 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1240 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1242 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1244 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1246 struct valid_url_types
{
1257 valid_url_type(char *url
)
1261 for (i
= 0; i
< LENGTH(vut
); i
++)
1262 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1269 print_cookie(char *msg
, SoupCookie
*c
)
1275 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1276 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1277 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1278 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1279 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1280 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1281 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1282 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1283 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1284 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1288 walk_alias(struct settings
*s
,
1289 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1294 if (s
== NULL
|| cb
== NULL
) {
1295 show_oops_s("walk_alias invalid parameters");
1299 TAILQ_FOREACH(a
, &aliases
, entry
) {
1300 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1301 cb(s
, str
, cb_args
);
1307 match_alias(char *url_in
)
1311 char *url_out
= NULL
, *search
, *enc_arg
;
1313 search
= g_strdup(url_in
);
1315 if (strsep(&arg
, " \t") == NULL
) {
1316 show_oops_s("match_alias: NULL URL");
1320 TAILQ_FOREACH(a
, &aliases
, entry
) {
1321 if (!strcmp(search
, a
->a_name
))
1326 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1329 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1330 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1333 url_out
= g_strdup(a
->a_uri
);
1341 guess_url_type(char *url_in
)
1344 char *url_out
= NULL
, *enc_search
= NULL
;
1346 url_out
= match_alias(url_in
);
1347 if (url_out
!= NULL
)
1352 * If there is no dot nor slash in the string and it isn't a
1353 * path to a local file and doesn't resolves to an IP, assume
1354 * that the user wants to search for the string.
1357 if (strchr(url_in
, '.') == NULL
&&
1358 strchr(url_in
, '/') == NULL
&&
1359 stat(url_in
, &sb
) != 0 &&
1360 gethostbyname(url_in
) == NULL
) {
1362 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1363 url_out
= g_strdup_printf(search_string
, enc_search
);
1369 /* XXX not sure about this heuristic */
1370 if (stat(url_in
, &sb
) == 0)
1371 url_out
= g_strdup_printf("file://%s", url_in
);
1373 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1375 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1381 load_uri(struct tab
*t
, gchar
*uri
)
1384 gchar
*newuri
= NULL
;
1390 /* Strip leading spaces. */
1391 while(*uri
&& isspace(*uri
))
1394 if (strlen(uri
) == 0) {
1399 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1400 for (i
= 0; i
< LENGTH(about_list
); i
++)
1401 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1402 bzero(&args
, sizeof args
);
1403 about_list
[i
].func(t
, &args
);
1404 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1408 show_oops(t
, "invalid about page");
1412 if (valid_url_type(uri
)) {
1413 newuri
= guess_url_type(uri
);
1417 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1418 webkit_web_view_load_uri(t
->wv
, uri
);
1425 get_uri(WebKitWebView
*wv
)
1429 uri
= webkit_web_view_get_uri(wv
);
1431 if (uri
&& strlen(uri
) > 0)
1438 add_alias(struct settings
*s
, char *line
)
1441 struct alias
*a
= NULL
;
1443 if (s
== NULL
|| line
== NULL
) {
1444 show_oops_s("add_alias invalid parameters");
1449 a
= g_malloc(sizeof(*a
));
1451 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1452 show_oops_s("add_alias: incomplete alias definition");
1455 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1456 show_oops_s("add_alias: invalid alias definition");
1460 a
->a_name
= g_strdup(alias
);
1461 a
->a_uri
= g_strdup(l
);
1463 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1465 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1475 add_mime_type(struct settings
*s
, char *line
)
1479 struct mime_type
*m
= NULL
;
1480 int downloadfirst
= 0;
1482 /* XXX this could be smarter */
1484 if (line
== NULL
&& strlen(line
) == 0) {
1485 show_oops_s("add_mime_type invalid parameters");
1494 m
= g_malloc(sizeof(*m
));
1496 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1497 show_oops_s("add_mime_type: invalid mime_type");
1500 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1501 mime_type
[strlen(mime_type
) - 1] = '\0';
1506 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1507 show_oops_s("add_mime_type: invalid mime_type");
1511 m
->mt_type
= g_strdup(mime_type
);
1512 m
->mt_action
= g_strdup(l
);
1513 m
->mt_download
= downloadfirst
;
1515 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1516 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1518 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1528 find_mime_type(char *mime_type
)
1530 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1532 TAILQ_FOREACH(m
, &mtl
, entry
) {
1533 if (m
->mt_default
&&
1534 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1537 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1550 walk_mime_type(struct settings
*s
,
1551 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1553 struct mime_type
*m
;
1556 if (s
== NULL
|| cb
== NULL
)
1557 show_oops_s("walk_mime_type invalid parameters");
1559 TAILQ_FOREACH(m
, &mtl
, entry
) {
1560 str
= g_strdup_printf("%s%s --> %s",
1562 m
->mt_default
? "*" : "",
1564 cb(s
, str
, cb_args
);
1570 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1575 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1578 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1580 /* treat *.moo.com the same as .moo.com */
1581 if (str
[0] == '*' && str
[1] == '.')
1583 else if (str
[0] == '.')
1588 d
= g_malloc(sizeof *d
);
1590 d
->d
= g_strdup_printf(".%s", str
);
1592 d
->d
= g_strdup(str
);
1595 if (RB_INSERT(domain_list
, wl
, d
))
1598 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1609 add_cookie_wl(struct settings
*s
, char *entry
)
1611 wl_add(entry
, &c_wl
, 1);
1616 walk_cookie_wl(struct settings
*s
,
1617 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1621 if (s
== NULL
|| cb
== NULL
) {
1622 show_oops_s("walk_cookie_wl invalid parameters");
1626 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1627 cb(s
, d
->d
, cb_args
);
1631 walk_js_wl(struct settings
*s
,
1632 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1636 if (s
== NULL
|| cb
== NULL
) {
1637 show_oops_s("walk_js_wl invalid parameters");
1641 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1642 cb(s
, d
->d
, cb_args
);
1646 add_js_wl(struct settings
*s
, char *entry
)
1648 wl_add(entry
, &js_wl
, 1 /* persistent */);
1653 wl_find(const gchar
*search
, struct domain_list
*wl
)
1656 struct domain
*d
= NULL
, dfind
;
1659 if (search
== NULL
|| wl
== NULL
)
1661 if (strlen(search
) < 2)
1664 if (search
[0] != '.')
1665 s
= g_strdup_printf(".%s", search
);
1667 s
= g_strdup(search
);
1669 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1672 d
= RB_FIND(domain_list
, wl
, &dfind
);
1686 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1692 if (s
== NULL
|| wl
== NULL
)
1695 if (!strncmp(s
, "http://", strlen("http://")))
1696 s
= &s
[strlen("http://")];
1697 else if (!strncmp(s
, "https://", strlen("https://")))
1698 s
= &s
[strlen("https://")];
1703 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1704 /* chop string at first slash */
1705 if (s
[i
] == '/' || s
[i
] == '\0') {
1708 r
= wl_find(ss
, wl
);
1717 get_toplevel_domain(char *domain
)
1724 if (strlen(domain
) < 2)
1727 s
= &domain
[strlen(domain
) - 1];
1728 while (s
!= domain
) {
1744 settings_add(char *var
, char *val
)
1751 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
1752 if (strcmp(var
, rs
[i
].name
))
1756 if (rs
[i
].s
->set(&rs
[i
], val
))
1757 errx(1, "invalid value for %s: %s", var
, val
);
1761 switch (rs
[i
].type
) {
1770 errx(1, "invalid sval for %s",
1784 errx(1, "invalid type for %s", var
);
1793 config_parse(char *filename
, int runtime
)
1796 char *line
, *cp
, *var
, *val
;
1797 size_t len
, lineno
= 0;
1799 char file
[PATH_MAX
];
1802 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1804 if (filename
== NULL
)
1807 if (runtime
&& runtime_settings
[0] != '\0') {
1808 snprintf(file
, sizeof file
, "%s/%s",
1809 work_dir
, runtime_settings
);
1810 if (stat(file
, &sb
)) {
1811 warnx("runtime file doesn't exist, creating it");
1812 if ((f
= fopen(file
, "w")) == NULL
)
1814 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1818 strlcpy(file
, filename
, sizeof file
);
1820 if ((config
= fopen(file
, "r")) == NULL
) {
1821 warn("config_parse: cannot open %s", filename
);
1826 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1827 if (feof(config
) || ferror(config
))
1831 cp
+= (long)strspn(cp
, WS
);
1832 if (cp
[0] == '\0') {
1838 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1839 errx(1, "invalid config file entry: %s", line
);
1841 cp
+= (long)strspn(cp
, WS
);
1843 if ((val
= strsep(&cp
, "\0")) == NULL
)
1846 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1847 handled
= settings_add(var
, val
);
1849 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1858 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1864 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1868 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1871 JSStringGetUTF8CString(jsref
, s
, l
);
1872 JSStringRelease(jsref
);
1878 disable_hints(struct tab
*t
)
1880 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1881 bzero(t
->hint_num
, sizeof t
->hint_num
);
1882 run_script(t
, "vimprobable_clear()");
1884 t
->hint_mode
= XT_HINT_NONE
;
1888 enable_hints(struct tab
*t
)
1890 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1891 run_script(t
, "vimprobable_show_hints()");
1893 t
->hint_mode
= XT_HINT_NONE
;
1896 #define XT_JS_OPEN ("open;")
1897 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1898 #define XT_JS_FIRE ("fire;")
1899 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1900 #define XT_JS_FOUND ("found;")
1901 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1904 run_script(struct tab
*t
, char *s
)
1906 JSGlobalContextRef ctx
;
1907 WebKitWebFrame
*frame
;
1909 JSValueRef val
, exception
;
1912 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1913 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1915 frame
= webkit_web_view_get_main_frame(t
->wv
);
1916 ctx
= webkit_web_frame_get_global_context(frame
);
1918 str
= JSStringCreateWithUTF8CString(s
);
1919 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1920 NULL
, 0, &exception
);
1921 JSStringRelease(str
);
1923 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1925 es
= js_ref_to_string(ctx
, exception
);
1926 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1930 es
= js_ref_to_string(ctx
, val
);
1931 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1933 /* handle return value right here */
1934 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1936 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1939 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1940 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1941 &es
[XT_JS_FIRE_LEN
]);
1946 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1947 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1958 hint(struct tab
*t
, struct karg
*args
)
1961 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1963 if (t
->hints_on
== 0)
1972 apply_style(struct tab
*t
)
1974 g_object_set(G_OBJECT(t
->settings
),
1975 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
1979 userstyle(struct tab
*t
, struct karg
*args
)
1981 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
1985 g_object_set(G_OBJECT(t
->settings
),
1986 "user-stylesheet-uri", NULL
, (char *)NULL
);
1995 * Doesn't work fully, due to the following bug:
1996 * https://bugs.webkit.org/show_bug.cgi?id=51747
1999 restore_global_history(void)
2001 char file
[PATH_MAX
];
2006 const char delim
[3] = {'\\', '\\', '\0'};
2008 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2010 if ((f
= fopen(file
, "r")) == NULL
) {
2011 warnx("%s: fopen", __func__
);
2016 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2017 if (feof(f
) || ferror(f
))
2020 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2021 if (feof(f
) || ferror(f
)) {
2023 warnx("%s: broken history file\n", __func__
);
2027 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2028 webkit_web_history_item_new_with_data(uri
, title
);
2029 h
= g_malloc(sizeof(struct history
));
2030 h
->uri
= g_strdup(uri
);
2031 h
->title
= g_strdup(title
);
2032 RB_INSERT(history_list
, &hl
, h
);
2033 completion_add_uri(h
->uri
);
2035 warnx("%s: failed to restore history\n", __func__
);
2051 save_global_history_to_disk(struct tab
*t
)
2053 char file
[PATH_MAX
];
2057 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2059 if ((f
= fopen(file
, "w")) == NULL
) {
2060 show_oops(t
, "%s: global history file: %s",
2061 __func__
, strerror(errno
));
2065 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2066 if (h
->uri
&& h
->title
)
2067 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2076 quit(struct tab
*t
, struct karg
*args
)
2078 if (save_global_history
)
2079 save_global_history_to_disk(t
);
2087 open_tabs(struct tab
*t
, struct karg
*a
)
2089 char file
[PATH_MAX
];
2093 struct tab
*ti
, *tt
;
2098 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2099 if ((f
= fopen(file
, "r")) == NULL
)
2102 ti
= TAILQ_LAST(&tabs
, tab_list
);
2105 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2106 if (feof(f
) || ferror(f
))
2109 /* retrieve session name */
2110 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2111 strlcpy(named_session
,
2112 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2113 sizeof named_session
);
2117 if (uri
&& strlen(uri
))
2118 create_new_tab(uri
, NULL
, 1, -1);
2124 /* close open tabs */
2125 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2127 tt
= TAILQ_FIRST(&tabs
);
2146 restore_saved_tabs(void)
2148 char file
[PATH_MAX
];
2149 int unlink_file
= 0;
2154 snprintf(file
, sizeof file
, "%s/%s",
2155 sessions_dir
, XT_RESTART_TABS_FILE
);
2156 if (stat(file
, &sb
) == -1)
2157 a
.s
= XT_SAVED_TABS_FILE
;
2160 a
.s
= XT_RESTART_TABS_FILE
;
2163 a
.i
= XT_SES_DONOTHING
;
2164 rv
= open_tabs(NULL
, &a
);
2173 save_tabs(struct tab
*t
, struct karg
*a
)
2175 char file
[PATH_MAX
];
2180 const gchar
**arr
= NULL
;
2185 snprintf(file
, sizeof file
, "%s/%s",
2186 sessions_dir
, named_session
);
2188 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2190 if ((f
= fopen(file
, "w")) == NULL
) {
2191 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2195 /* save session name */
2196 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2198 /* save tabs, in the order they are arranged in the notebook */
2199 TAILQ_FOREACH(ti
, &tabs
, entry
)
2202 arr
= g_malloc0(len
* sizeof(gchar
*));
2204 TAILQ_FOREACH(ti
, &tabs
, entry
) {
2205 if ((uri
= get_uri(ti
->wv
)) != NULL
)
2206 arr
[gtk_notebook_page_num(notebook
, ti
->vbox
)] = uri
;
2209 for (i
= 0; i
< len
; i
++)
2211 fprintf(f
, "%s\n", arr
[i
]);
2214 /* try and make sure this gets to disk NOW. XXX Backup first? */
2215 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2216 show_oops(t
, "May not have managed to save session: %s",
2226 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2238 yank_uri(struct tab
*t
, struct karg
*args
)
2241 GtkClipboard
*clipboard
;
2243 if ((uri
= get_uri(t
->wv
)) == NULL
)
2246 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2247 gtk_clipboard_set_text(clipboard
, uri
, -1);
2253 paste_uri(struct tab
*t
, struct karg
*args
)
2255 GtkClipboard
*clipboard
;
2256 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2258 gchar
*p
= NULL
, *uri
;
2260 /* try primary clipboard first */
2261 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2262 p
= gtk_clipboard_wait_for_text(clipboard
);
2264 /* if it failed get whatever text is in cut_buffer0 */
2266 if (gdk_property_get(gdk_get_default_root_window(),
2268 gdk_atom_intern("STRING", FALSE
),
2270 65536 /* picked out of my butt */,
2276 /* yes sir, we need to NUL the string */
2282 while(*uri
&& isspace(*uri
))
2284 if (strlen(uri
) == 0) {
2285 show_oops(t
, "empty paste buffer");
2288 if (valid_url_type(uri
)) {
2289 /* we can be clever and paste this in search box */
2290 show_oops(t
, "not a valid URL");
2294 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2296 else if (args
->i
== XT_PASTE_NEW_TAB
)
2297 create_new_tab(uri
, NULL
, 1, -1);
2308 find_domain(const gchar
*s
, int add_dot
)
2311 char *r
= NULL
, *ss
= NULL
;
2316 if (!strncmp(s
, "http://", strlen("http://")))
2317 s
= &s
[strlen("http://")];
2318 else if (!strncmp(s
, "https://", strlen("https://")))
2319 s
= &s
[strlen("https://")];
2325 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2326 /* chop string at first slash */
2327 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2330 r
= g_strdup_printf(".%s", ss
);
2341 toggle_cwl(struct tab
*t
, struct karg
*args
)
2345 char *dom
= NULL
, *dom_toggle
= NULL
;
2351 uri
= get_uri(t
->wv
);
2352 dom
= find_domain(uri
, 1);
2353 d
= wl_find(dom
, &c_wl
);
2360 if (args
->i
& XT_WL_TOGGLE
)
2362 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2364 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2367 if (args
->i
& XT_WL_TOPLEVEL
)
2368 dom_toggle
= get_toplevel_domain(dom
);
2373 /* enable cookies for domain */
2374 wl_add(dom_toggle
, &c_wl
, 0);
2376 /* disable cookies for domain */
2377 RB_REMOVE(domain_list
, &c_wl
, d
);
2379 if (args
->i
& XT_WL_RELOAD
)
2380 webkit_web_view_reload(t
->wv
);
2387 toggle_js(struct tab
*t
, struct karg
*args
)
2392 char *dom
= NULL
, *dom_toggle
= NULL
;
2397 g_object_get(G_OBJECT(t
->settings
),
2398 "enable-scripts", &es
, (char *)NULL
);
2399 if (args
->i
& XT_WL_TOGGLE
)
2401 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2403 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2408 uri
= get_uri(t
->wv
);
2409 dom
= find_domain(uri
, 1);
2411 if (uri
== NULL
|| dom
== NULL
) {
2412 show_oops(t
, "Can't toggle domain in JavaScript white list");
2416 if (args
->i
& XT_WL_TOPLEVEL
)
2417 dom_toggle
= get_toplevel_domain(dom
);
2422 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2423 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2425 d
= wl_find(dom_toggle
, &js_wl
);
2427 RB_REMOVE(domain_list
, &js_wl
, d
);
2428 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2430 g_object_set(G_OBJECT(t
->settings
),
2431 "enable-scripts", es
, (char *)NULL
);
2432 g_object_set(G_OBJECT(t
->settings
),
2433 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2434 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2436 if (args
->i
& XT_WL_RELOAD
)
2437 webkit_web_view_reload(t
->wv
);
2445 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2449 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2452 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2457 toggle_src(struct tab
*t
, struct karg
*args
)
2464 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2465 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2466 webkit_web_view_reload(t
->wv
);
2472 focus_webview(struct tab
*t
)
2477 /* only grab focus if we are visible */
2478 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2479 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2483 focus(struct tab
*t
, struct karg
*args
)
2485 if (t
== NULL
|| args
== NULL
)
2491 if (args
->i
== XT_FOCUS_URI
)
2492 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2493 else if (args
->i
== XT_FOCUS_SEARCH
)
2494 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2500 stats(struct tab
*t
, struct karg
*args
)
2502 char *page
, *body
, *s
, line
[64 * 1024];
2503 uint64_t line_count
= 0;
2507 show_oops_s("stats invalid parameters");
2510 if (save_rejected_cookies
) {
2511 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2513 s
= fgets(line
, sizeof line
, r_cookie_f
);
2514 if (s
== NULL
|| feof(r_cookie_f
) ||
2520 snprintf(line
, sizeof line
,
2521 "<br/>Cookies blocked(*) total: %llu", line_count
);
2523 show_oops(t
, "Can't open blocked cookies file: %s",
2527 body
= g_strdup_printf(
2528 "Cookies blocked(*) this session: %llu"
2530 "<p><small><b>*</b> results vary based on settings</small></p>",
2534 page
= get_html_page("Statistics", body
, "", 0);
2537 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2544 marco(struct tab
*t
, struct karg
*args
)
2546 char *page
, line
[64 * 1024];
2550 show_oops_s("marco invalid parameters");
2553 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2555 page
= get_html_page("Marco Sez...", line
, "", 0);
2557 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2564 blank(struct tab
*t
, struct karg
*args
)
2567 show_oops_s("blank invalid parameters");
2569 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2574 about(struct tab
*t
, struct karg
*args
)
2579 show_oops_s("about invalid parameters");
2581 body
= g_strdup_printf("<b>Version: %s</b><p>"
2584 "<li>Marco Peereboom <marco@peereboom.us></li>"
2585 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2586 "<li>Edd Barrett <vext01@gmail.com> </li>"
2587 "<li>Todd T. Fries <todd@fries.net> </li>"
2588 "<li>Raphael Graf <r@undefined.ch> </li>"
2590 "Copyrights and licenses can be found on the XXXterm "
2591 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2595 page
= get_html_page("About", body
, "", 0);
2598 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2605 help(struct tab
*t
, struct karg
*args
)
2607 char *page
, *head
, *body
;
2610 show_oops_s("help invalid parameters");
2612 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2613 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2615 body
= "XXXterm man page <a href=\"http://opensource.conformal.com/"
2616 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2617 "cgi-bin/man-cgi?xxxterm</a>";
2619 page
= get_html_page("XXXterm", body
, head
, FALSE
);
2621 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2628 * update all favorite tabs apart from one. Pass NULL if
2629 * you want to update all.
2632 update_favorite_tabs(struct tab
*apart_from
)
2635 if (!updating_fl_tabs
) {
2636 updating_fl_tabs
= 1; /* stop infinite recursion */
2637 TAILQ_FOREACH(t
, &tabs
, entry
)
2638 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2639 && (t
!= apart_from
))
2640 xtp_page_fl(t
, NULL
);
2641 updating_fl_tabs
= 0;
2645 /* show a list of favorites (bookmarks) */
2647 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2649 char file
[PATH_MAX
];
2651 char *uri
= NULL
, *title
= NULL
;
2652 size_t len
, lineno
= 0;
2654 char *body
, *tmp
, *page
= NULL
;
2655 const char delim
[3] = {'\\', '\\', '\0'};
2657 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2660 warn("%s: bad param", __func__
);
2662 /* mark tab as favorite list */
2663 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2665 /* new session key */
2666 if (!updating_fl_tabs
)
2667 generate_xtp_session_key(&fl_session_key
);
2669 /* open favorites */
2670 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2671 if ((f
= fopen(file
, "r")) == NULL
) {
2672 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2677 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
2678 "<th style='width: 40px'>#</th><th>Link</th>"
2679 "<th style='width: 40px'>Rm</th></tr>\n");
2682 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2683 if (feof(f
) || ferror(f
))
2691 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2692 if (feof(f
) || ferror(f
)) {
2693 show_oops(t
, "favorites file corrupt");
2699 body
= g_strdup_printf("%s<tr>"
2701 "<td><a href='%s'>%s</a></td>"
2702 "<td style='text-align: center'>"
2703 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2705 body
, i
, uri
, title
,
2706 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2718 /* if none, say so */
2721 body
= g_strdup_printf("%s<tr>"
2722 "<td colspan='3' style='text-align: center'>"
2723 "No favorites - To add one use the 'favadd' command."
2724 "</td></tr>", body
);
2729 body
= g_strdup_printf("%s</table>", body
);
2739 page
= get_html_page("Favorites", body
, "", 1);
2740 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
2744 update_favorite_tabs(t
);
2753 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2754 size_t cert_count
, char *title
)
2756 gnutls_datum_t cinfo
;
2760 body
= g_strdup("");
2762 for (i
= 0; i
< cert_count
; i
++) {
2763 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2768 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2769 body
, i
, cinfo
.data
);
2770 gnutls_free(cinfo
.data
);
2774 tmp
= get_html_page(title
, body
, "", 0);
2777 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
2782 ca_cmd(struct tab
*t
, struct karg
*args
)
2785 int rv
= 1, certs
= 0, certs_read
;
2788 gnutls_x509_crt_t
*c
= NULL
;
2789 char *certs_buf
= NULL
, *s
;
2791 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2792 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
2796 if (fstat(fileno(f
), &sb
) == -1) {
2797 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
2801 certs_buf
= g_malloc(sb
.st_size
+ 1);
2802 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2803 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2806 certs_buf
[sb
.st_size
] = '\0';
2809 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2811 s
+= strlen("BEGIN CERTIFICATE");
2814 bzero(&dt
, sizeof dt
);
2815 dt
.data
= certs_buf
;
2816 dt
.size
= sb
.st_size
;
2817 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2818 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
,
2819 GNUTLS_X509_FMT_PEM
, 0);
2820 if (certs_read
<= 0) {
2821 show_oops(t
, "No cert(s) available");
2824 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2837 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2840 struct addrinfo hints
, *res
= NULL
, *ai
;
2844 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2847 su
= soup_uri_new(uri
);
2850 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2853 snprintf(port
, sizeof port
, "%d", su
->port
);
2854 bzero(&hints
, sizeof(struct addrinfo
));
2855 hints
.ai_flags
= AI_CANONNAME
;
2856 hints
.ai_family
= AF_UNSPEC
;
2857 hints
.ai_socktype
= SOCK_STREAM
;
2859 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2862 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2863 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2866 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2869 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2873 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2878 strlcpy(domain
, su
->host
, domain_sz
);
2889 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2892 gnutls_deinit(gsession
);
2894 gnutls_certificate_free_credentials(xcred
);
2900 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2901 gnutls_certificate_credentials_t
*xc
)
2903 gnutls_certificate_credentials_t xcred
;
2904 gnutls_session_t gsession
;
2907 if (gs
== NULL
|| xc
== NULL
)
2913 gnutls_certificate_allocate_credentials(&xcred
);
2914 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2915 GNUTLS_X509_FMT_PEM
);
2916 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2917 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2918 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2919 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2920 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2921 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2923 gnutls_error_is_fatal(rv
),
2924 gnutls_strerror_name(rv
));
2925 stop_tls(gsession
, xcred
);
2929 gnutls_credentials_type_t cred
;
2930 cred
= gnutls_auth_get_type(gsession
);
2931 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2932 stop_tls(gsession
, xcred
);
2944 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2948 const gnutls_datum_t
*cl
;
2949 gnutls_x509_crt_t
*all_certs
;
2952 if (certs
== NULL
|| cert_count
== NULL
)
2954 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2956 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2960 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2961 for (i
= 0; i
< len
; i
++) {
2962 gnutls_x509_crt_init(&all_certs
[i
]);
2963 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2964 GNUTLS_X509_FMT_PEM
< 0)) {
2978 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2982 for (i
= 0; i
< cert_count
; i
++)
2983 gnutls_x509_crt_deinit(certs
[i
]);
2988 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2989 size_t cert_count
, char *domain
)
2992 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2997 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3000 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3001 if ((f
= fopen(file
, "w")) == NULL
) {
3002 show_oops(t
, "Can't create cert file %s %s",
3003 file
, strerror(errno
));
3007 for (i
= 0; i
< cert_count
; i
++) {
3008 cert_buf_sz
= sizeof cert_buf
;
3009 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3010 cert_buf
, &cert_buf_sz
)) {
3011 show_oops(t
, "gnutls_x509_crt_export failed");
3014 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3015 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3020 /* not the best spot but oh well */
3021 gdk_color_parse("lightblue", &color
);
3022 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3023 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
3024 gdk_color_parse(XT_COLOR_BLACK
, &color
);
3025 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
3031 load_compare_cert(struct tab
*t
, struct karg
*args
)
3034 char domain
[8182], file
[PATH_MAX
];
3035 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3036 int s
= -1, rv
= 1, i
;
3040 gnutls_session_t gsession
;
3041 gnutls_x509_crt_t
*certs
;
3042 gnutls_certificate_credentials_t xcred
;
3047 if ((uri
= get_uri(t
->wv
)) == NULL
)
3050 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
3054 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3055 show_oops(t
, "Start TLS failed");
3060 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3061 show_oops(t
, "Can't get connection certificates");
3065 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3066 if ((f
= fopen(file
, "r")) == NULL
)
3069 for (i
= 0; i
< cert_count
; i
++) {
3070 cert_buf_sz
= sizeof cert_buf
;
3071 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3072 cert_buf
, &cert_buf_sz
)) {
3075 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3076 rv
= -1; /* critical */
3079 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3080 rv
= -1; /* critical */
3089 free_connection_certs(certs
, cert_count
);
3091 /* we close the socket first for speed */
3094 stop_tls(gsession
, xcred
);
3100 cert_cmd(struct tab
*t
, struct karg
*args
)
3106 gnutls_session_t gsession
;
3107 gnutls_x509_crt_t
*certs
;
3108 gnutls_certificate_credentials_t xcred
;
3113 if (ssl_ca_file
== NULL
) {
3114 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3118 if ((uri
= get_uri(t
->wv
)) == NULL
) {
3119 show_oops(t
, "Invalid URI");
3123 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3124 show_oops(t
, "Invalid certificate URI: %s", uri
);
3129 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3130 show_oops(t
, "Start TLS failed");
3135 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3136 show_oops(t
, "get_connection_certs failed");
3140 if (args
->i
& XT_SHOW
)
3141 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3142 else if (args
->i
& XT_SAVE
)
3143 save_certs(t
, certs
, cert_count
, domain
);
3145 free_connection_certs(certs
, cert_count
);
3147 /* we close the socket first for speed */
3150 stop_tls(gsession
, xcred
);
3156 remove_cookie(int index
)
3162 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3164 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3166 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3170 print_cookie("remove cookie", c
);
3171 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3176 soup_cookies_free(cf
);
3182 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3187 body
= g_strdup("");
3190 if (args
->i
& XT_WL_PERSISTENT
) {
3192 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3194 RB_FOREACH(d
, domain_list
, wl
) {
3198 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3204 if (args
->i
& XT_WL_SESSION
) {
3206 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3208 RB_FOREACH(d
, domain_list
, wl
) {
3212 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3217 tmp
= get_html_page(title
, body
, "", 0);
3220 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3222 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3228 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3230 char file
[PATH_MAX
];
3232 char *line
= NULL
, *lt
= NULL
;
3235 char *dom
= NULL
, *dom_save
= NULL
;
3241 if (t
== NULL
|| args
== NULL
)
3244 if (runtime_settings
[0] == '\0')
3247 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3248 if ((f
= fopen(file
, "r+")) == NULL
)
3251 uri
= get_uri(t
->wv
);
3252 dom
= find_domain(uri
, 1);
3253 if (uri
== NULL
|| dom
== NULL
) {
3254 show_oops(t
, "Can't add domain to %s white list",
3255 js
? "JavaScript" : "cookie");
3259 if (args
->i
& XT_WL_TOPLEVEL
) {
3261 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3262 show_oops(t
, "invalid domain: %s", dom
);
3265 } else if (args
->i
& XT_WL_FQDN
) {
3271 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3274 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3277 if (!strcmp(line
, lt
))
3283 fprintf(f
, "%s\n", lt
);
3288 d
= wl_find(dom_save
, &js_wl
);
3290 settings_add("js_wl", dom_save
);
3291 d
= wl_find(dom_save
, &js_wl
);
3295 d
= wl_find(dom_save
, &c_wl
);
3297 settings_add("cookie_wl", dom_save
);
3298 d
= wl_find(dom_save
, &c_wl
);
3302 /* find and add to persistent jar */
3303 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3304 for (;cf
; cf
= cf
->next
) {
3306 if (!strcmp(dom_save
, ci
->domain
) ||
3307 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3308 c
= soup_cookie_copy(ci
);
3309 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3312 soup_cookies_free(cf
);
3330 js_show_wl(struct tab
*t
, struct karg
*args
)
3332 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3333 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3339 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3341 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3342 wl_show(t
, args
, "Cookie White List", &c_wl
);
3348 cookie_cmd(struct tab
*t
, struct karg
*args
)
3350 if (args
->i
& XT_SHOW
)
3351 wl_show(t
, args
, "Cookie White List", &c_wl
);
3352 else if (args
->i
& XT_WL_TOGGLE
) {
3353 args
->i
|= XT_WL_RELOAD
;
3354 toggle_cwl(t
, args
);
3355 } else if (args
->i
& XT_SAVE
) {
3356 args
->i
|= XT_WL_RELOAD
;
3357 wl_save(t
, args
, 0);
3358 } else if (args
->i
& XT_DELETE
)
3359 show_oops(t
, "'cookie delete' currently unimplemented");
3365 js_cmd(struct tab
*t
, struct karg
*args
)
3367 if (args
->i
& XT_SHOW
)
3368 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3369 else if (args
->i
& XT_SAVE
) {
3370 args
->i
|= XT_WL_RELOAD
;
3371 wl_save(t
, args
, 1);
3372 } else if (args
->i
& XT_WL_TOGGLE
) {
3373 args
->i
|= XT_WL_RELOAD
;
3375 } else if (args
->i
& XT_DELETE
)
3376 show_oops(t
, "'js delete' currently unimplemented");
3382 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3384 js_toggle_cb(t
->js_toggle
, t
);
3390 add_favorite(struct tab
*t
, struct karg
*args
)
3392 char file
[PATH_MAX
];
3395 size_t urilen
, linelen
;
3396 const gchar
*uri
, *title
;
3401 /* don't allow adding of xtp pages to favorites */
3402 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3403 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3407 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3408 if ((f
= fopen(file
, "r+")) == NULL
) {
3409 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3413 title
= webkit_web_view_get_title(t
->wv
);
3414 uri
= get_uri(t
->wv
);
3419 if (title
== NULL
|| uri
== NULL
) {
3420 show_oops(t
, "can't add page to favorites");
3424 urilen
= strlen(uri
);
3427 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3428 if (feof(f
) || ferror(f
))
3431 if (linelen
== urilen
&& !strcmp(line
, uri
))
3438 fprintf(f
, "\n%s\n%s", title
, uri
);
3444 update_favorite_tabs(NULL
);
3450 navaction(struct tab
*t
, struct karg
*args
)
3452 WebKitWebHistoryItem
*item
;
3454 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3455 t
->tab_id
, args
->i
);
3458 if (args
->i
== XT_NAV_BACK
)
3459 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3461 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3463 return (XT_CB_PASSTHROUGH
);
3464 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3466 return (XT_CB_PASSTHROUGH
);
3471 webkit_web_view_go_back(t
->wv
);
3473 case XT_NAV_FORWARD
:
3474 webkit_web_view_go_forward(t
->wv
);
3477 webkit_web_view_reload(t
->wv
);
3479 case XT_NAV_RELOAD_CACHE
:
3480 webkit_web_view_reload_bypass_cache(t
->wv
);
3483 return (XT_CB_PASSTHROUGH
);
3487 move(struct tab
*t
, struct karg
*args
)
3489 GtkAdjustment
*adjust
;
3490 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3495 case XT_MOVE_BOTTOM
:
3497 case XT_MOVE_PAGEDOWN
:
3498 case XT_MOVE_PAGEUP
:
3499 case XT_MOVE_HALFDOWN
:
3500 case XT_MOVE_HALFUP
:
3501 adjust
= t
->adjust_v
;
3504 adjust
= t
->adjust_h
;
3508 pos
= gtk_adjustment_get_value(adjust
);
3509 ps
= gtk_adjustment_get_page_size(adjust
);
3510 upper
= gtk_adjustment_get_upper(adjust
);
3511 lower
= gtk_adjustment_get_lower(adjust
);
3512 si
= gtk_adjustment_get_step_increment(adjust
);
3513 pi
= gtk_adjustment_get_page_increment(adjust
);
3516 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3517 "max %f si %f pi %f\n",
3518 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3519 pos
, ps
, upper
, lower
, max
, si
, pi
);
3525 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3530 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3532 case XT_MOVE_BOTTOM
:
3533 case XT_MOVE_FARRIGHT
:
3534 gtk_adjustment_set_value(adjust
, max
);
3537 case XT_MOVE_FARLEFT
:
3538 gtk_adjustment_set_value(adjust
, lower
);
3540 case XT_MOVE_PAGEDOWN
:
3542 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3544 case XT_MOVE_PAGEUP
:
3546 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3548 case XT_MOVE_HALFDOWN
:
3550 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3552 case XT_MOVE_HALFUP
:
3554 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3557 return (XT_CB_PASSTHROUGH
);
3560 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3562 return (XT_CB_HANDLED
);
3566 url_set_visibility(void)
3570 TAILQ_FOREACH(t
, &tabs
, entry
) {
3571 if (show_url
== 0) {
3572 gtk_widget_hide(t
->toolbar
);
3575 gtk_widget_show(t
->toolbar
);
3580 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3583 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3585 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3589 statusbar_set_visibility(void)
3593 TAILQ_FOREACH(t
, &tabs
, entry
) {
3594 if (show_statusbar
== 0) {
3595 gtk_widget_hide(t
->statusbar
);
3598 gtk_widget_show(t
->statusbar
);
3603 url_set(struct tab
*t
, int enable_url_entry
)
3608 show_url
= enable_url_entry
;
3610 if (enable_url_entry
) {
3611 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3612 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3613 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3615 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3616 GTK_ENTRY_ICON_PRIMARY
);
3618 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3619 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3620 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3621 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3627 fullscreen(struct tab
*t
, struct karg
*args
)
3629 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3632 return (XT_CB_PASSTHROUGH
);
3634 if (show_url
== 0) {
3642 url_set_visibility();
3643 notebook_tab_set_visibility(notebook
);
3645 return (XT_CB_HANDLED
);
3649 statusaction(struct tab
*t
, struct karg
*args
)
3651 int rv
= XT_CB_HANDLED
;
3653 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3656 return (XT_CB_PASSTHROUGH
);
3659 case XT_STATUSBAR_SHOW
:
3660 if (show_statusbar
== 0) {
3662 statusbar_set_visibility();
3665 case XT_STATUSBAR_HIDE
:
3666 if (show_statusbar
== 1) {
3668 statusbar_set_visibility();
3676 urlaction(struct tab
*t
, struct karg
*args
)
3678 int rv
= XT_CB_HANDLED
;
3680 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3683 return (XT_CB_PASSTHROUGH
);
3687 if (show_url
== 0) {
3689 url_set_visibility();
3693 if (show_url
== 1) {
3695 url_set_visibility();
3703 tabaction(struct tab
*t
, struct karg
*args
)
3705 int rv
= XT_CB_HANDLED
;
3706 char *url
= args
->s
;
3710 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3713 return (XT_CB_PASSTHROUGH
);
3717 if (strlen(url
) > 0)
3718 create_new_tab(url
, NULL
, 1, args
->p
);
3720 create_new_tab(NULL
, NULL
, 1, args
->p
);
3726 TAILQ_FOREACH(tt
, &tabs
, entry
)
3727 if (tt
->tab_id
== args
->p
- 1) {
3733 case XT_TAB_DELQUIT
:
3734 if (gtk_notebook_get_n_pages(notebook
) > 1)
3740 if (strlen(url
) > 0)
3743 rv
= XT_CB_PASSTHROUGH
;
3749 if (show_tabs
== 0) {
3751 notebook_tab_set_visibility(notebook
);
3755 if (show_tabs
== 1) {
3757 notebook_tab_set_visibility(notebook
);
3760 case XT_TAB_UNDO_CLOSE
:
3761 if (undo_count
== 0) {
3762 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3766 u
= TAILQ_FIRST(&undos
);
3767 create_new_tab(u
->uri
, u
, 1, -1);
3769 TAILQ_REMOVE(&undos
, u
, entry
);
3771 /* u->history is freed in create_new_tab() */
3776 rv
= XT_CB_PASSTHROUGH
;
3790 resizetab(struct tab
*t
, struct karg
*args
)
3792 if (t
== NULL
|| args
== NULL
) {
3793 show_oops_s("resizetab invalid parameters");
3794 return (XT_CB_PASSTHROUGH
);
3797 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3798 t
->tab_id
, args
->i
);
3800 adjustfont_webkit(t
, args
->i
);
3802 return (XT_CB_HANDLED
);
3806 movetab(struct tab
*t
, struct karg
*args
)
3810 if (t
== NULL
|| args
== NULL
) {
3811 show_oops_s("movetab invalid parameters");
3812 return (XT_CB_PASSTHROUGH
);
3815 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3816 t
->tab_id
, args
->i
);
3818 if (args
->i
>= XT_TAB_INVALID
)
3819 return (XT_CB_PASSTHROUGH
);
3821 if (TAILQ_EMPTY(&tabs
))
3822 return (XT_CB_PASSTHROUGH
);
3824 n
= gtk_notebook_get_n_pages(notebook
);
3825 dest
= gtk_notebook_get_current_page(notebook
);
3830 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
3839 dest
-= args
->p
% n
;
3852 return (XT_CB_PASSTHROUGH
);
3855 if (dest
< 0 || dest
>= n
)
3856 return (XT_CB_PASSTHROUGH
);
3857 if (t
->tab_id
== dest
) {
3858 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3859 return (XT_CB_HANDLED
);
3862 gtk_notebook_set_current_page(notebook
, dest
);
3864 return (XT_CB_HANDLED
);
3870 command(struct tab
*t
, struct karg
*args
)
3872 char *s
= NULL
, *ss
= NULL
;
3876 if (t
== NULL
|| args
== NULL
) {
3877 show_oops_s("command invalid parameters");
3878 return (XT_CB_PASSTHROUGH
);
3889 if (cmd_prefix
== 0)
3892 ss
= g_strdup_printf(":%d", cmd_prefix
);
3903 case XT_CMD_OPEN_CURRENT
:
3906 case XT_CMD_TABNEW_CURRENT
:
3907 if (!s
) /* FALL THROUGH? */
3909 if ((uri
= get_uri(t
->wv
)) != NULL
) {
3910 ss
= g_strdup_printf("%s%s", s
, uri
);
3915 show_oops(t
, "command: invalid opcode %d", args
->i
);
3916 return (XT_CB_PASSTHROUGH
);
3919 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3921 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3922 gdk_color_parse(XT_COLOR_WHITE
, &color
);
3923 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3925 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3926 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3931 return (XT_CB_HANDLED
);
3935 * Return a new string with a download row (in html)
3936 * appended. Old string is freed.
3939 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3942 WebKitDownloadStatus stat
;
3943 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3945 char cur_sz
[FMT_SCALED_STRSIZE
];
3946 char tot_sz
[FMT_SCALED_STRSIZE
];
3949 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3951 /* All actions wil take this form:
3952 * xxxt://class/seskey
3954 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3955 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3957 stat
= webkit_download_get_status(dl
->download
);
3960 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3961 status_html
= g_strdup_printf("Finished");
3962 cmd_html
= g_strdup_printf(
3963 "<a href='%s%d/%d'>Remove</a>",
3964 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3966 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3967 /* gather size info */
3968 progress
= 100 * webkit_download_get_progress(dl
->download
);
3971 webkit_download_get_current_size(dl
->download
), cur_sz
);
3973 webkit_download_get_total_size(dl
->download
), tot_sz
);
3975 status_html
= g_strdup_printf(
3976 "<div style='width: 100%%' align='center'>"
3977 "<div class='progress-outer'>"
3978 "<div class='progress-inner' style='width: %.2f%%'>"
3979 "</div></div></div>"
3980 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
3981 progress
, cur_sz
, tot_sz
, progress
);
3983 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3984 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3988 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3989 status_html
= g_strdup_printf("Cancelled");
3990 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3991 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3993 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3994 status_html
= g_strdup_printf("Error!");
3995 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3996 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3998 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3999 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4000 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4001 status_html
= g_strdup_printf("Starting");
4004 show_oops(t
, "%s: unknown download status", __func__
);
4007 new_html
= g_strdup_printf(
4008 "%s\n<tr><td>%s</td><td>%s</td>"
4009 "<td style='text-align:center'>%s</td></tr>\n",
4010 html
, basename(webkit_download_get_destination_uri(dl
->download
)),
4011 status_html
, cmd_html
);
4015 g_free(status_html
);
4026 * update all download tabs apart from one. Pass NULL if
4027 * you want to update all.
4030 update_download_tabs(struct tab
*apart_from
)
4033 if (!updating_dl_tabs
) {
4034 updating_dl_tabs
= 1; /* stop infinite recursion */
4035 TAILQ_FOREACH(t
, &tabs
, entry
)
4036 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4037 && (t
!= apart_from
))
4038 xtp_page_dl(t
, NULL
);
4039 updating_dl_tabs
= 0;
4044 * update all cookie tabs apart from one. Pass NULL if
4045 * you want to update all.
4048 update_cookie_tabs(struct tab
*apart_from
)
4051 if (!updating_cl_tabs
) {
4052 updating_cl_tabs
= 1; /* stop infinite recursion */
4053 TAILQ_FOREACH(t
, &tabs
, entry
)
4054 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4055 && (t
!= apart_from
))
4056 xtp_page_cl(t
, NULL
);
4057 updating_cl_tabs
= 0;
4062 * update all history tabs apart from one. Pass NULL if
4063 * you want to update all.
4066 update_history_tabs(struct tab
*apart_from
)
4070 if (!updating_hl_tabs
) {
4071 updating_hl_tabs
= 1; /* stop infinite recursion */
4072 TAILQ_FOREACH(t
, &tabs
, entry
)
4073 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4074 && (t
!= apart_from
))
4075 xtp_page_hl(t
, NULL
);
4076 updating_hl_tabs
= 0;
4080 /* cookie management XTP page */
4082 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4084 char *body
, *page
, *tmp
;
4085 int i
= 1; /* all ids start 1 */
4086 GSList
*sc
, *pc
, *pc_start
;
4088 char *type
, *table_headers
;
4089 char *last_domain
= strdup("");
4091 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4094 show_oops_s("%s invalid parameters", __func__
);
4097 /* mark this tab as cookie jar */
4098 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
4100 /* Generate a new session key */
4101 if (!updating_cl_tabs
)
4102 generate_xtp_session_key(&cl_session_key
);
4105 table_headers
= g_strdup_printf("<table><tr>"
4108 "<th style='width:200px'>Value</th>"
4112 "<th>HTTP<br />only</th>"
4113 "<th style='width:40px'>Rm</th></tr>\n");
4115 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4116 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4120 for (; sc
; sc
= sc
->next
) {
4123 if (strcmp(last_domain
, c
->domain
) != 0) {
4126 last_domain
= strdup(c
->domain
);
4130 body
= g_strdup_printf("%s</table>"
4132 body
, c
->domain
, table_headers
);
4136 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4137 c
->domain
, table_headers
);
4142 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4143 if (soup_cookie_equal(pc
->data
, c
)) {
4144 type
= "Session + Persistent";
4149 body
= g_strdup_printf(
4152 "<td style='word-wrap:normal'>%s</td>"
4154 " <textarea rows='4'>%s</textarea>"
4160 "<td style='text-align:center'>"
4161 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4168 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4183 soup_cookies_free(sc
);
4184 soup_cookies_free(pc
);
4186 /* small message if there are none */
4188 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4189 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4192 body
= g_strdup_printf("%s</table>", body
);
4195 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4197 g_free(table_headers
);
4198 g_free(last_domain
);
4200 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4201 update_cookie_tabs(t
);
4209 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4211 char *body
, *page
, *tmp
;
4213 int i
= 1; /* all ids start 1 */
4215 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4218 show_oops_s("%s invalid parameters", __func__
);
4222 /* mark this tab as history manager */
4223 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
4225 /* Generate a new session key */
4226 if (!updating_hl_tabs
)
4227 generate_xtp_session_key(&hl_session_key
);
4230 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4231 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4233 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4235 body
= g_strdup_printf(
4237 "<td><a href='%s'>%s</a></td>"
4239 "<td style='text-align: center'>"
4240 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4241 body
, h
->uri
, h
->uri
, h
->title
,
4242 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4243 XT_XTP_HL_REMOVE
, i
);
4249 /* small message if there are none */
4252 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4253 "colspan='3'>No History</td></tr>\n", body
);
4258 body
= g_strdup_printf("%s</table>", body
);
4261 page
= get_html_page("History", body
, "", TRUE
);
4265 * update all history manager tabs as the xtp session
4266 * key has now changed. No need to update the current tab.
4267 * Already did that above.
4269 update_history_tabs(t
);
4271 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4278 * Generate a web page detailing the status of any downloads
4281 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4283 struct download
*dl
;
4284 char *body
, *page
, *tmp
;
4288 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4291 show_oops_s("%s invalid parameters", __func__
);
4294 /* mark as a download manager tab */
4295 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
4298 * Generate a new session key for next page instance.
4299 * This only happens for the top level call to xtp_page_dl()
4300 * in which case updating_dl_tabs is 0.
4302 if (!updating_dl_tabs
)
4303 generate_xtp_session_key(&dl_session_key
);
4305 /* header - with refresh so as to update */
4306 if (refresh_interval
>= 1)
4307 ref
= g_strdup_printf(
4308 "<meta http-equiv='refresh' content='%u"
4309 ";url=%s%d/%s/%d' />\n",
4318 body
= g_strdup_printf("<div align='center'>"
4319 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4320 "</p><table><tr><th style='width: 60%%'>"
4321 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4322 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4324 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4325 body
= xtp_page_dl_row(t
, body
, dl
);
4329 /* message if no downloads in list */
4332 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4333 " style='text-align: center'>"
4334 "No downloads</td></tr>\n", body
);
4339 body
= g_strdup_printf("%s</table></div>", body
);
4342 page
= get_html_page("Downloads", body
, ref
, 1);
4347 * update all download manager tabs as the xtp session
4348 * key has now changed. No need to update the current tab.
4349 * Already did that above.
4351 update_download_tabs(t
);
4353 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4360 search(struct tab
*t
, struct karg
*args
)
4364 if (t
== NULL
|| args
== NULL
) {
4365 show_oops_s("search invalid parameters");
4368 if (t
->search_text
== NULL
) {
4369 if (global_search
== NULL
)
4370 return (XT_CB_PASSTHROUGH
);
4372 t
->search_text
= g_strdup(global_search
);
4373 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4374 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4378 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4379 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4382 case XT_SEARCH_NEXT
:
4383 d
= t
->search_forward
;
4385 case XT_SEARCH_PREV
:
4386 d
= !t
->search_forward
;
4389 return (XT_CB_PASSTHROUGH
);
4392 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4394 return (XT_CB_HANDLED
);
4397 struct settings_args
{
4403 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4406 struct settings_args
*sa
= cb_args
;
4411 if (s
->flags
& XT_SF_RUNTIME
)
4417 *sa
->body
= g_strdup_printf(
4419 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4420 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4432 set(struct tab
*t
, struct karg
*args
)
4434 char *body
, *page
, *tmp
;
4436 struct settings_args sa
;
4438 bzero(&sa
, sizeof sa
);
4442 body
= g_strdup_printf("<div align='center'><table><tr>"
4443 "<th align='left'>Setting</th>"
4444 "<th align='left'>Value</th></tr>\n");
4446 settings_walk(print_setting
, &sa
);
4449 /* small message if there are none */
4452 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4453 "colspan='2'>No settings</td></tr>\n", body
);
4458 body
= g_strdup_printf("%s</table></div>", body
);
4461 page
= get_html_page("Settings", body
, "", 0);
4465 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4469 return (XT_CB_PASSTHROUGH
);
4473 session_save(struct tab
*t
, char *filename
)
4478 if (strlen(filename
) == 0)
4481 if (filename
[0] == '.' || filename
[0] == '/')
4485 if (save_tabs(t
, &a
))
4487 strlcpy(named_session
, filename
, sizeof named_session
);
4495 session_open(struct tab
*t
, char *filename
)
4500 if (strlen(filename
) == 0)
4503 if (filename
[0] == '.' || filename
[0] == '/')
4507 a
.i
= XT_SES_CLOSETABS
;
4508 if (open_tabs(t
, &a
))
4511 strlcpy(named_session
, filename
, sizeof named_session
);
4519 session_delete(struct tab
*t
, char *filename
)
4521 char file
[PATH_MAX
];
4524 if (strlen(filename
) == 0)
4527 if (filename
[0] == '.' || filename
[0] == '/')
4530 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4534 if (!strcmp(filename
, named_session
))
4535 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4536 sizeof named_session
);
4544 session_cmd(struct tab
*t
, struct karg
*args
)
4546 char *filename
= args
->s
;
4551 if (args
->i
& XT_SHOW
)
4552 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4553 XT_SAVED_TABS_FILE
: named_session
);
4554 else if (args
->i
& XT_SAVE
) {
4555 if (session_save(t
, filename
)) {
4556 show_oops(t
, "Can't save session: %s",
4557 filename
? filename
: "INVALID");
4560 } else if (args
->i
& XT_OPEN
) {
4561 if (session_open(t
, filename
)) {
4562 show_oops(t
, "Can't open session: %s",
4563 filename
? filename
: "INVALID");
4566 } else if (args
->i
& XT_DELETE
) {
4567 if (session_delete(t
, filename
)) {
4568 show_oops(t
, "Can't delete session: %s",
4569 filename
? filename
: "INVALID");
4574 return (XT_CB_PASSTHROUGH
);
4578 * Make a hardcopy of the page
4581 print_page(struct tab
*t
, struct karg
*args
)
4583 WebKitWebFrame
*frame
;
4585 GtkPrintOperation
*op
;
4586 GtkPrintOperationAction action
;
4587 GtkPrintOperationResult print_res
;
4588 GError
*g_err
= NULL
;
4589 int marg_l
, marg_r
, marg_t
, marg_b
;
4591 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4593 ps
= gtk_page_setup_new();
4594 op
= gtk_print_operation_new();
4595 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4596 frame
= webkit_web_view_get_main_frame(t
->wv
);
4598 /* the default margins are too small, so we will bump them */
4599 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4600 XT_PRINT_EXTRA_MARGIN
;
4601 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4602 XT_PRINT_EXTRA_MARGIN
;
4603 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4604 XT_PRINT_EXTRA_MARGIN
;
4605 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4606 XT_PRINT_EXTRA_MARGIN
;
4609 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4610 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4611 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4612 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4614 gtk_print_operation_set_default_page_setup(op
, ps
);
4616 /* this appears to free 'op' and 'ps' */
4617 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4619 /* check it worked */
4620 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4621 show_oops_s("can't print: %s", g_err
->message
);
4622 g_error_free (g_err
);
4630 go_home(struct tab
*t
, struct karg
*args
)
4637 restart(struct tab
*t
, struct karg
*args
)
4641 a
.s
= XT_RESTART_TABS_FILE
;
4643 execvp(start_argv
[0], start_argv
);
4649 #define CTRL GDK_CONTROL_MASK
4650 #define MOD1 GDK_MOD1_MASK
4651 #define SHFT GDK_SHIFT_MASK
4653 /* inherent to GTK not all keys will be caught at all times */
4654 /* XXX sort key bindings */
4655 struct key_binding
{
4660 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4662 { "cookiejar", MOD1
, 0, GDK_j
},
4663 { "downloadmgr", MOD1
, 0, GDK_d
},
4664 { "history", MOD1
, 0, GDK_h
},
4665 { "print", CTRL
, 0, GDK_p
},
4666 { "search", 0, 0, GDK_slash
},
4667 { "searchb", 0, 0, GDK_question
},
4668 { "command", 0, 0, GDK_colon
},
4669 { "qa", CTRL
, 0, GDK_q
},
4670 { "restart", MOD1
, 0, GDK_q
},
4671 { "js toggle", CTRL
, 0, GDK_j
},
4672 { "cookie toggle", MOD1
, 0, GDK_c
},
4673 { "togglesrc", CTRL
, 0, GDK_s
},
4674 { "yankuri", 0, 0, GDK_y
},
4675 { "pasteuricur", 0, 0, GDK_p
},
4676 { "pasteurinew", 0, 0, GDK_P
},
4677 { "toplevel toggle", 0, 0, GDK_F4
},
4678 { "help", 0, 0, GDK_F1
},
4681 { "searchnext", 0, 0, GDK_n
},
4682 { "searchprevious", 0, 0, GDK_N
},
4685 { "focusaddress", 0, 0, GDK_F6
},
4686 { "focussearch", 0, 0, GDK_F7
},
4689 { "hinting", 0, 0, GDK_f
},
4691 /* custom stylesheet */
4692 { "userstyle", 0, 0, GDK_i
},
4695 { "goback", 0, 0, GDK_BackSpace
},
4696 { "goback", MOD1
, 0, GDK_Left
},
4697 { "goforward", SHFT
, 0, GDK_BackSpace
},
4698 { "goforward", MOD1
, 0, GDK_Right
},
4699 { "reload", 0, 0, GDK_F5
},
4700 { "reload", CTRL
, 0, GDK_r
},
4701 { "reloadforce", CTRL
, 0, GDK_R
},
4702 { "reload", CTRL
, 0, GDK_l
},
4703 { "favorites", MOD1
, 1, GDK_f
},
4705 /* vertical movement */
4706 { "scrolldown", 0, 0, GDK_j
},
4707 { "scrolldown", 0, 0, GDK_Down
},
4708 { "scrollup", 0, 0, GDK_Up
},
4709 { "scrollup", 0, 0, GDK_k
},
4710 { "scrollbottom", 0, 0, GDK_G
},
4711 { "scrollbottom", 0, 0, GDK_End
},
4712 { "scrolltop", 0, 0, GDK_Home
},
4713 { "scrolltop", 0, 0, GDK_g
},
4714 { "scrollpagedown", 0, 0, GDK_space
},
4715 { "scrollpagedown", CTRL
, 0, GDK_f
},
4716 { "scrollhalfdown", CTRL
, 0, GDK_d
},
4717 { "scrollpagedown", 0, 0, GDK_Page_Down
},
4718 { "scrollpageup", 0, 0, GDK_Page_Up
},
4719 { "scrollpageup", CTRL
, 0, GDK_b
},
4720 { "scrollhalfup", CTRL
, 0, GDK_u
},
4721 /* horizontal movement */
4722 { "scrollright", 0, 0, GDK_l
},
4723 { "scrollright", 0, 0, GDK_Right
},
4724 { "scrollleft", 0, 0, GDK_Left
},
4725 { "scrollleft", 0, 0, GDK_h
},
4726 { "scrollfarright", 0, 0, GDK_dollar
},
4727 { "scrollfarleft", 0, 0, GDK_0
},
4730 { "tabnew", CTRL
, 0, GDK_t
},
4731 { "999tabnew", CTRL
, 0, GDK_T
},
4732 { "tabclose", CTRL
, 1, GDK_w
},
4733 { "tabundoclose", 0, 0, GDK_U
},
4734 { "tabnext 1", CTRL
, 0, GDK_1
},
4735 { "tabnext 2", CTRL
, 0, GDK_2
},
4736 { "tabnext 3", CTRL
, 0, GDK_3
},
4737 { "tabnext 4", CTRL
, 0, GDK_4
},
4738 { "tabnext 5", CTRL
, 0, GDK_5
},
4739 { "tabnext 6", CTRL
, 0, GDK_6
},
4740 { "tabnext 7", CTRL
, 0, GDK_7
},
4741 { "tabnext 8", CTRL
, 0, GDK_8
},
4742 { "tabnext 9", CTRL
, 0, GDK_9
},
4743 { "tabnext 10", CTRL
, 0, GDK_0
},
4744 { "tabfirst", CTRL
, 0, GDK_less
},
4745 { "tablast", CTRL
, 0, GDK_greater
},
4746 { "tabprevious", CTRL
, 0, GDK_Left
},
4747 { "tabnext", CTRL
, 0, GDK_Right
},
4748 { "focusout", CTRL
, 0, GDK_minus
},
4749 { "focusin", CTRL
, 0, GDK_plus
},
4750 { "focusin", CTRL
, 0, GDK_equal
},
4752 /* command aliases (handy when -S flag is used) */
4753 { "promptopen", 0, 0, GDK_F9
},
4754 { "promptopencurrent", 0, 0, GDK_F10
},
4755 { "prompttabnew", 0, 0, GDK_F11
},
4756 { "prompttabnewcurrent",0, 0, GDK_F12
},
4758 TAILQ_HEAD(keybinding_list
, key_binding
);
4761 walk_kb(struct settings
*s
,
4762 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4764 struct key_binding
*k
;
4767 if (s
== NULL
|| cb
== NULL
) {
4768 show_oops_s("walk_kb invalid parameters");
4772 TAILQ_FOREACH(k
, &kbl
, entry
) {
4778 if (gdk_keyval_name(k
->key
) == NULL
)
4781 strlcat(str
, k
->cmd
, sizeof str
);
4782 strlcat(str
, ",", sizeof str
);
4784 if (k
->mask
& GDK_SHIFT_MASK
)
4785 strlcat(str
, "S-", sizeof str
);
4786 if (k
->mask
& GDK_CONTROL_MASK
)
4787 strlcat(str
, "C-", sizeof str
);
4788 if (k
->mask
& GDK_MOD1_MASK
)
4789 strlcat(str
, "M1-", sizeof str
);
4790 if (k
->mask
& GDK_MOD2_MASK
)
4791 strlcat(str
, "M2-", sizeof str
);
4792 if (k
->mask
& GDK_MOD3_MASK
)
4793 strlcat(str
, "M3-", sizeof str
);
4794 if (k
->mask
& GDK_MOD4_MASK
)
4795 strlcat(str
, "M4-", sizeof str
);
4796 if (k
->mask
& GDK_MOD5_MASK
)
4797 strlcat(str
, "M5-", sizeof str
);
4799 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4800 cb(s
, str
, cb_args
);
4805 init_keybindings(void)
4808 struct key_binding
*k
;
4810 for (i
= 0; i
< LENGTH(keys
); i
++) {
4811 k
= g_malloc0(sizeof *k
);
4812 k
->cmd
= keys
[i
].cmd
;
4813 k
->mask
= keys
[i
].mask
;
4814 k
->use_in_entry
= keys
[i
].use_in_entry
;
4815 k
->key
= keys
[i
].key
;
4816 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4818 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4819 k
->cmd
? k
->cmd
: "unnamed key");
4824 keybinding_clearall(void)
4826 struct key_binding
*k
, *next
;
4828 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
4829 next
= TAILQ_NEXT(k
, entry
);
4833 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4834 k
->cmd
? k
->cmd
: "unnamed key");
4835 TAILQ_REMOVE(&kbl
, k
, entry
);
4841 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
4843 struct key_binding
*k
;
4844 guint keyval
, mask
= 0;
4847 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
4849 /* Keys which are to be used in entry have been prefixed with an
4850 * exclamation mark. */
4854 /* find modifier keys */
4855 if (strstr(key
, "S-"))
4856 mask
|= GDK_SHIFT_MASK
;
4857 if (strstr(key
, "C-"))
4858 mask
|= GDK_CONTROL_MASK
;
4859 if (strstr(key
, "M1-"))
4860 mask
|= GDK_MOD1_MASK
;
4861 if (strstr(key
, "M2-"))
4862 mask
|= GDK_MOD2_MASK
;
4863 if (strstr(key
, "M3-"))
4864 mask
|= GDK_MOD3_MASK
;
4865 if (strstr(key
, "M4-"))
4866 mask
|= GDK_MOD4_MASK
;
4867 if (strstr(key
, "M5-"))
4868 mask
|= GDK_MOD5_MASK
;
4871 for (i
= strlen(key
) - 1; i
> 0; i
--)
4875 /* validate keyname */
4876 keyval
= gdk_keyval_from_name(key
);
4877 if (keyval
== GDK_VoidSymbol
) {
4878 warnx("invalid keybinding name %s", key
);
4881 /* must run this test too, gtk+ doesn't handle 10 for example */
4882 if (gdk_keyval_name(keyval
) == NULL
) {
4883 warnx("invalid keybinding name %s", key
);
4887 /* Remove eventual dupes. */
4888 TAILQ_FOREACH(k
, &kbl
, entry
)
4889 if (k
->key
== keyval
&& k
->mask
== mask
) {
4890 TAILQ_REMOVE(&kbl
, k
, entry
);
4896 k
= g_malloc0(sizeof *k
);
4897 k
->cmd
= g_strdup(cmd
);
4899 k
->use_in_entry
= use_in_entry
;
4902 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
4907 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
4908 k
->cmd
, gdk_keyval_name(keyval
));
4910 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4916 add_kb(struct settings
*s
, char *entry
)
4920 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
4922 /* clearall is special */
4923 if (!strcmp(entry
, "clearall")) {
4924 keybinding_clearall();
4928 kb
= strstr(entry
, ",");
4934 return (keybinding_add(entry
, key
, key
[0] == '!'));
4940 int (*func
)(struct tab
*, struct karg
*);
4944 { "command", 0, command
, ':', 0 },
4945 { "search", 0, command
, '/', 0 },
4946 { "searchb", 0, command
, '?', 0 },
4947 { "togglesrc", 0, toggle_src
, 0, 0 },
4949 /* yanking and pasting */
4950 { "yankuri", 0, yank_uri
, 0, 0 },
4951 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
4952 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
4953 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
4956 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
4957 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
4960 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
4961 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
4964 { "hinting", 0, hint
, 0, 0 },
4966 /* custom stylesheet */
4967 { "userstyle", 0, userstyle
, 0, 0 },
4970 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
4971 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
4972 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
4973 { "reloadforce", 0, navaction
, XT_NAV_RELOAD_CACHE
, 0 },
4975 /* vertical movement */
4976 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
4977 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
4978 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
4979 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
4980 { "1", 0, move
, XT_MOVE_TOP
, 0 },
4981 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
4982 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
4983 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
4984 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
4985 /* horizontal movement */
4986 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
4987 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
4988 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
4989 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
4992 { "favorites", 0, xtp_page_fl
, 0, 0 },
4993 { "fav", 0, xtp_page_fl
, 0, 0 },
4994 { "favadd", 0, add_favorite
, 0, 0 },
4996 { "qall", 0, quit
, 0, 0 },
4997 { "quitall", 0, quit
, 0, 0 },
4998 { "w", 0, save_tabs
, 0, 0 },
4999 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5000 { "help", 0, help
, 0, 0 },
5001 { "about", 0, about
, 0, 0 },
5002 { "stats", 0, stats
, 0, 0 },
5003 { "version", 0, about
, 0, 0 },
5006 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5007 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5008 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5009 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5010 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5011 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5012 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5013 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5014 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5015 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5016 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5018 /* cookie command */
5019 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5020 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5021 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5022 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5023 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5024 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5025 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5026 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5027 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5028 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5029 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5031 /* toplevel (domain) command */
5032 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5033 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5036 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5039 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5040 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5041 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5043 { "ca", 0, ca_cmd
, 0, 0 },
5044 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5045 { "dl", 0, xtp_page_dl
, 0, 0 },
5046 { "h", 0, xtp_page_hl
, 0, 0 },
5047 { "history", 0, xtp_page_hl
, 0, 0 },
5048 { "home", 0, go_home
, 0, 0 },
5049 { "restart", 0, restart
, 0, 0 },
5050 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5051 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5052 { "statushide", 0, statusaction
, XT_STATUSBAR_HIDE
, 0 },
5053 { "statusshow", 0, statusaction
, XT_STATUSBAR_SHOW
, 0 },
5055 { "print", 0, print_page
, 0, 0 },
5058 { "focusin", 0, resizetab
, 1, 0 },
5059 { "focusout", 0, resizetab
, -1, 0 },
5060 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5061 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5062 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5063 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5064 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5065 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5066 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5067 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5068 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5069 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5070 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5071 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5072 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5073 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5075 /* command aliases (handy when -S flag is used) */
5076 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5077 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5078 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5079 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5082 { "set", 0, set
, 0, 0 },
5083 { "fullscreen", 0, fullscreen
, 0, 0 },
5084 { "f", 0, fullscreen
, 0, 0 },
5087 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5088 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5089 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5090 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5091 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5098 } cmd_status
= {-1, 0};
5101 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5107 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5113 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5115 a
.i
= XT_NAV_FORWARD
;
5125 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5127 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5129 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5136 * cancel, remove, etc. downloads
5139 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5141 struct download find
, *d
= NULL
;
5143 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5145 /* some commands require a valid download id */
5146 if (cmd
!= XT_XTP_DL_LIST
) {
5147 /* lookup download in question */
5149 d
= RB_FIND(download_list
, &downloads
, &find
);
5152 show_oops(t
, "%s: no such download", __func__
);
5157 /* decide what to do */
5159 case XT_XTP_DL_CANCEL
:
5160 webkit_download_cancel(d
->download
);
5162 case XT_XTP_DL_REMOVE
:
5163 webkit_download_cancel(d
->download
); /* just incase */
5164 g_object_unref(d
->download
);
5165 RB_REMOVE(download_list
, &downloads
, d
);
5167 case XT_XTP_DL_LIST
:
5171 show_oops(t
, "%s: unknown command", __func__
);
5174 xtp_page_dl(t
, NULL
);
5178 * Actions on history, only does one thing for now, but
5179 * we provide the function for future actions
5182 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5184 struct history
*h
, *next
;
5188 case XT_XTP_HL_REMOVE
:
5189 /* walk backwards, as listed in reverse */
5190 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5191 next
= RB_PREV(history_list
, &hl
, h
);
5193 RB_REMOVE(history_list
, &hl
, h
);
5194 g_free((gpointer
) h
->title
);
5195 g_free((gpointer
) h
->uri
);
5202 case XT_XTP_HL_LIST
:
5203 /* Nothing - just xtp_page_hl() below */
5206 show_oops(t
, "%s: unknown command", __func__
);
5210 xtp_page_hl(t
, NULL
);
5213 /* remove a favorite */
5215 remove_favorite(struct tab
*t
, int index
)
5217 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5218 char *new_favs
, *tmp
;
5223 /* open favorites */
5224 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5226 if ((f
= fopen(file
, "r")) == NULL
) {
5227 show_oops(t
, "%s: can't open favorites: %s",
5228 __func__
, strerror(errno
));
5232 /* build a string which will become the new favroites file */
5233 new_favs
= g_strdup("");
5236 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5237 if (feof(f
) || ferror(f
))
5239 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5246 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5247 if (feof(f
) || ferror(f
)) {
5248 show_oops(t
, "%s: can't parse favorites %s",
5249 __func__
, strerror(errno
));
5254 /* as long as this isn't the one we are deleting add to file */
5257 new_favs
= g_strdup_printf("%s%s\n%s\n",
5258 new_favs
, title
, uri
);
5270 /* write back new favorites file */
5271 if ((f
= fopen(file
, "w")) == NULL
) {
5272 show_oops(t
, "%s: can't open favorites: %s",
5273 __func__
, strerror(errno
));
5277 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5290 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5293 case XT_XTP_FL_LIST
:
5294 /* nothing, just the below call to xtp_page_fl() */
5296 case XT_XTP_FL_REMOVE
:
5297 remove_favorite(t
, arg
);
5300 show_oops(t
, "%s: invalid favorites command", __func__
);
5304 xtp_page_fl(t
, NULL
);
5308 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5311 case XT_XTP_CL_LIST
:
5312 /* nothing, just xtp_page_cl() */
5314 case XT_XTP_CL_REMOVE
:
5318 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5322 xtp_page_cl(t
, NULL
);
5325 /* link an XTP class to it's session key and handler function */
5326 struct xtp_despatch
{
5329 void (*handle_func
)(struct tab
*, uint8_t, int);
5332 struct xtp_despatch xtp_despatches
[] = {
5333 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5334 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5335 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5336 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5337 { XT_XTP_INVALID
, NULL
, NULL
}
5341 * is the url xtp protocol? (xxxt://)
5342 * if so, parse and despatch correct bahvior
5345 parse_xtp_url(struct tab
*t
, const char *url
)
5347 char *dup
= NULL
, *p
, *last
;
5348 uint8_t n_tokens
= 0;
5349 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5350 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5355 * tokens array meaning:
5357 * tokens[1] = session key
5358 * tokens[2] = action
5359 * tokens[3] = optional argument
5362 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5364 /*xtp tab meaning is normal unless proven special */
5365 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5367 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5370 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5372 /* split out the url */
5373 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5374 (p
= strtok_r(NULL
, "/", &last
))) {
5376 tokens
[n_tokens
++] = p
;
5379 /* should be atleast three fields 'class/seskey/command/arg' */
5383 dsp
= xtp_despatches
;
5384 req_class
= atoi(tokens
[0]);
5385 while (dsp
->xtp_class
) {
5386 if (dsp
->xtp_class
== req_class
) {
5393 /* did we find one atall? */
5394 if (dsp_match
== NULL
) {
5395 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5399 /* check session key and call despatch function */
5400 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5401 ret
= TRUE
; /* all is well, this was a valid xtp request */
5402 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5415 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5417 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5419 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5422 show_oops_s("activate_uri_entry_cb invalid parameters");
5427 show_oops(t
, "activate_uri_entry_cb no uri");
5431 uri
+= strspn(uri
, "\t ");
5433 /* if xxxt:// treat specially */
5434 if (parse_xtp_url(t
, uri
))
5437 /* otherwise continue to load page normally */
5438 load_uri(t
, (gchar
*)uri
);
5443 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5445 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5446 char *newuri
= NULL
;
5449 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5452 show_oops_s("activate_search_entry_cb invalid parameters");
5456 if (search_string
== NULL
) {
5457 show_oops(t
, "no search_string");
5461 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5462 newuri
= g_strdup_printf(search_string
, enc_search
);
5465 webkit_web_view_load_uri(t
->wv
, newuri
);
5473 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5475 struct domain
*d
= NULL
;
5478 if (uri
== NULL
|| t
== NULL
)
5481 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5486 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5487 es
? "enable" : "disable", uri
);
5489 g_object_set(G_OBJECT(t
->settings
),
5490 "enable-scripts", es
, (char *)NULL
);
5491 g_object_set(G_OBJECT(t
->settings
),
5492 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5493 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5495 button_set_stockid(t
->js_toggle
,
5496 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5500 show_ca_status(struct tab
*t
, const char *uri
)
5502 WebKitWebFrame
*frame
;
5503 WebKitWebDataSource
*source
;
5504 WebKitNetworkRequest
*request
;
5505 SoupMessage
*message
;
5507 gchar
*col_str
= XT_COLOR_WHITE
;
5510 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5511 ssl_strict_certs
, ssl_ca_file
, uri
);
5515 if (ssl_ca_file
== NULL
) {
5516 if (g_str_has_prefix(uri
, "http://"))
5518 if (g_str_has_prefix(uri
, "https://")) {
5519 col_str
= XT_COLOR_RED
;
5524 if (g_str_has_prefix(uri
, "http://") ||
5525 !g_str_has_prefix(uri
, "https://"))
5528 frame
= webkit_web_view_get_main_frame(t
->wv
);
5529 source
= webkit_web_frame_get_data_source(frame
);
5530 request
= webkit_web_data_source_get_request(source
);
5531 message
= webkit_network_request_get_message(request
);
5533 if (message
&& (soup_message_get_flags(message
) &
5534 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5535 col_str
= XT_COLOR_GREEN
;
5538 r
= load_compare_cert(t
, NULL
);
5540 col_str
= XT_COLOR_BLUE
;
5542 col_str
= XT_COLOR_YELLOW
;
5544 col_str
= XT_COLOR_RED
;
5549 gdk_color_parse(col_str
, &color
);
5550 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5552 if (!strcmp(col_str
, XT_COLOR_WHITE
)) {
5553 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5555 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5556 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5559 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5561 gdk_color_parse(XT_COLOR_BLACK
, &color
);
5562 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5569 free_favicon(struct tab
*t
)
5571 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
5572 __func__
, t
->icon_download
, t
->icon_request
);
5574 if (t
->icon_request
)
5575 g_object_unref(t
->icon_request
);
5576 if (t
->icon_dest_uri
)
5577 g_free(t
->icon_dest_uri
);
5579 t
->icon_request
= NULL
;
5580 t
->icon_dest_uri
= NULL
;
5584 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5586 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5587 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5589 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5590 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5592 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5593 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5597 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
5599 GdkPixbuf
*pb_scaled
;
5601 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
5602 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16, GDK_INTERP_BILINEAR
);
5606 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5607 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5609 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5610 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5612 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5613 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5615 if (pb_scaled
!= pb
)
5616 g_object_unref(pb_scaled
);
5620 xt_icon_from_file(struct tab
*t
, char *file
)
5624 if (g_str_has_prefix(file
, "file://"))
5625 file
+= strlen("file://");
5627 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5629 xt_icon_from_pixbuf(t
, pb
);
5632 xt_icon_from_name(t
, "text-html");
5636 is_valid_icon(char *file
)
5639 const char *mime_type
;
5643 gf
= g_file_new_for_path(file
);
5644 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5646 mime_type
= g_file_info_get_content_type(fi
);
5647 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5648 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5649 g_strcmp0(mime_type
, "image/png") == 0 ||
5650 g_strcmp0(mime_type
, "image/gif") == 0 ||
5651 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5659 set_favicon_from_file(struct tab
*t
, char *file
)
5663 if (t
== NULL
|| file
== NULL
)
5666 if (g_str_has_prefix(file
, "file://"))
5667 file
+= strlen("file://");
5668 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5670 if (!stat(file
, &sb
)) {
5671 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5672 /* corrupt icon so trash it */
5673 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5676 /* no need to set icon to default here */
5680 xt_icon_from_file(t
, file
);
5684 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5687 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5688 struct tab
*tt
= NULL
, *t
= NULL
;
5691 * find the webview instead of passing in the tab as it could have been
5692 * deleted from underneath us.
5694 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5703 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5704 __func__
, t
->tab_id
, status
);
5707 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5709 t
->icon_download
= NULL
;
5712 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5715 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5718 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5720 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5721 __func__
, t
->tab_id
);
5722 t
->icon_download
= NULL
;
5725 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5728 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5729 __func__
, t
->icon_dest_uri
);
5730 set_favicon_from_file(t
, t
->icon_dest_uri
);
5731 /* these will be freed post callback */
5732 t
->icon_request
= NULL
;
5733 t
->icon_download
= NULL
;
5741 abort_favicon_download(struct tab
*t
)
5743 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5745 if (!WEBKIT_CHECK_VERSION(1, 4, 0)) {
5746 if (t
->icon_download
) {
5747 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
5748 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5749 webkit_download_cancel(t
->icon_download
);
5750 t
->icon_download
= NULL
;
5755 xt_icon_from_name(t
, "text-html");
5759 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5762 gchar
*name_hash
, file
[PATH_MAX
];
5765 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
5767 if (uri
== NULL
|| t
== NULL
)
5770 if (WEBKIT_CHECK_VERSION(1, 4, 0)) {
5771 /* take icon from WebKitIconDatabase */
5772 pb
= webkit_web_view_get_icon_pixbuf(wv
);
5774 xt_icon_from_pixbuf(t
, pb
);
5777 xt_icon_from_name(t
, "text-html");
5779 } else if (WEBKIT_CHECK_VERSION(1, 1, 18)) {
5780 /* download icon to cache dir */
5781 if (t
->icon_request
) {
5782 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5786 /* check to see if we got the icon in cache */
5787 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5788 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5791 if (!stat(file
, &sb
)) {
5792 if (sb
.st_size
> 0) {
5793 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5795 set_favicon_from_file(t
, file
);
5799 /* corrupt icon so trash it */
5800 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5805 /* create download for icon */
5806 t
->icon_request
= webkit_network_request_new(uri
);
5807 if (t
->icon_request
== NULL
) {
5808 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5813 t
->icon_download
= webkit_download_new(t
->icon_request
);
5814 if (t
->icon_download
== NULL
) {
5815 fprintf(stderr
, "%s: icon_download", __func__
);
5819 /* we have to free icon_dest_uri later */
5820 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5821 webkit_download_set_destination_uri(t
->icon_download
,
5824 if (webkit_download_get_status(t
->icon_download
) ==
5825 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5826 fprintf(stderr
, "%s: download failed to start", __func__
);
5827 g_object_unref(t
->icon_request
);
5828 g_free(t
->icon_dest_uri
);
5829 t
->icon_request
= NULL
;
5830 t
->icon_dest_uri
= NULL
;
5834 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5835 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
5837 webkit_download_start(t
->icon_download
);
5842 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5844 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5845 struct history
*h
, find
;
5846 const gchar
*s_loading
;
5849 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
5850 webkit_web_view_get_load_status(wview
), get_uri(wview
) ? get_uri(wview
) : "NOTHING");
5853 show_oops_s("notify_load_status_cb invalid paramters");
5857 switch (webkit_web_view_get_load_status(wview
)) {
5858 case WEBKIT_LOAD_PROVISIONAL
:
5860 abort_favicon_download(t
);
5861 #if GTK_CHECK_VERSION(2, 20, 0)
5862 gtk_widget_show(t
->spinner
);
5863 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5865 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5867 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5869 /* take focus if we are visible */
5875 case WEBKIT_LOAD_COMMITTED
:
5877 if ((uri
= get_uri(wview
)) != NULL
) {
5878 if (strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
))
5879 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5885 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
5888 /* check if js white listing is enabled */
5889 if (enable_js_whitelist
) {
5890 uri
= get_uri(wview
);
5891 check_and_set_js(uri
, t
);
5897 show_ca_status(t
, uri
);
5899 /* we know enough to autosave the session */
5900 if (session_autosave
) {
5906 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
5910 case WEBKIT_LOAD_FINISHED
:
5912 uri
= get_uri(wview
);
5916 if (!strncmp(uri
, "http://", strlen("http://")) ||
5917 !strncmp(uri
, "https://", strlen("https://")) ||
5918 !strncmp(uri
, "file://", strlen("file://"))) {
5920 h
= RB_FIND(history_list
, &hl
, &find
);
5922 title
= webkit_web_view_get_title(wview
);
5923 set
= title
? title
: uri
;
5924 h
= g_malloc(sizeof *h
);
5925 h
->uri
= g_strdup(uri
);
5926 h
->title
= g_strdup(set
);
5927 RB_INSERT(history_list
, &hl
, h
);
5928 completion_add_uri(h
->uri
);
5929 update_history_tabs(NULL
);
5933 set_status(t
, (char *)uri
, XT_STATUS_URI
);
5934 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5935 case WEBKIT_LOAD_FAILED
:
5938 #if GTK_CHECK_VERSION(2, 20, 0)
5939 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5940 gtk_widget_hide(t
->spinner
);
5942 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
5943 if (s_loading
&& !strcmp(s_loading
, "Loading"))
5944 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5946 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
5951 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
5953 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
5954 webkit_web_view_can_go_back(wview
));
5956 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
5957 webkit_web_view_can_go_forward(wview
));
5961 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5963 const gchar
*set
= NULL
, *title
= NULL
;
5965 title
= webkit_web_view_get_title(wview
);
5966 set
= title
? title
: get_uri(wview
);
5968 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
5969 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
5971 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5972 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
5977 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5979 run_script(t
, JS_HINTING
);
5983 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
5985 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
5986 progress
== 100 ? 0 : (double)progress
/ 100);
5987 if (show_url
== 0) {
5988 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
5989 progress
== 100 ? 0 : (double)progress
/ 100);
5994 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5995 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5996 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5999 WebKitWebNavigationReason reason
;
6000 struct domain
*d
= NULL
;
6003 show_oops_s("webview_npd_cb invalid parameters");
6007 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6009 webkit_network_request_get_uri(request
));
6011 uri
= (char *)webkit_network_request_get_uri(request
);
6013 /* if this is an xtp url, we don't load anything else */
6014 if (parse_xtp_url(t
, uri
))
6017 if (t
->ctrl_click
) {
6019 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6020 webkit_web_policy_decision_ignore(pd
);
6021 return (TRUE
); /* we made the decission */
6025 * This is a little hairy but it comes down to this:
6026 * when we run in whitelist mode we have to assist the browser in
6027 * opening the URL that it would have opened in a new tab.
6029 reason
= webkit_web_navigation_action_get_reason(na
);
6030 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6031 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6032 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6035 webkit_web_policy_decision_use(pd
);
6036 return (TRUE
); /* we made the decission */
6043 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6046 struct domain
*d
= NULL
;
6048 WebKitWebView
*webview
= NULL
;
6050 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6051 webkit_web_view_get_uri(wv
));
6054 /* open in current tab */
6056 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6057 uri
= webkit_web_view_get_uri(wv
);
6058 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6061 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6063 } else if (enable_scripts
== 1) {
6064 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6072 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6075 struct domain
*d
= NULL
;
6077 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6079 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6080 uri
= webkit_web_view_get_uri(wv
);
6081 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6085 } else if (enable_scripts
== 1)
6092 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6094 /* we can not eat the event without throwing gtk off so defer it */
6096 /* catch middle click */
6097 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6102 /* catch ctrl click */
6103 if (e
->type
== GDK_BUTTON_RELEASE
&&
6104 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6109 return (XT_CB_PASSTHROUGH
);
6113 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6115 struct mime_type
*m
;
6117 m
= find_mime_type(mime_type
);
6125 show_oops(t
, "can't fork mime handler");
6134 execlp(m
->mt_action
, m
->mt_action
,
6135 webkit_network_request_get_uri(request
), (void *)NULL
);
6144 get_mime_type(char *file
)
6146 const char *mime_type
;
6150 if (g_str_has_prefix(file
, "file://"))
6151 file
+= strlen("file://");
6153 gf
= g_file_new_for_path(file
);
6154 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6156 mime_type
= g_file_info_get_content_type(fi
);
6164 run_download_mimehandler(char *mime_type
, char *file
)
6166 struct mime_type
*m
;
6168 m
= find_mime_type(mime_type
);
6174 show_oops_s("can't fork download mime handler");
6183 if (g_str_has_prefix(file
, "file://"))
6184 file
+= strlen("file://");
6185 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6194 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6197 WebKitDownloadStatus status
;
6198 const gchar
*file
= NULL
, *mime
= NULL
;
6200 if (download
== NULL
)
6202 status
= webkit_download_get_status(download
);
6203 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6206 file
= webkit_download_get_destination_uri(download
);
6209 mime
= get_mime_type((char *)file
);
6213 run_download_mimehandler((char *)mime
, (char *)file
);
6217 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6218 WebKitNetworkRequest
*request
, char *mime_type
,
6219 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6222 show_oops_s("webview_mimetype_cb invalid parameters");
6226 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6227 t
->tab_id
, mime_type
);
6229 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6230 webkit_web_policy_decision_ignore(decision
);
6235 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6236 webkit_web_policy_decision_download(decision
);
6244 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6247 const gchar
*filename
;
6249 struct download
*download_entry
;
6252 if (wk_download
== NULL
|| t
== NULL
) {
6253 show_oops_s("%s invalid parameters", __func__
);
6257 filename
= webkit_download_get_suggested_filename(wk_download
);
6258 if (filename
== NULL
)
6259 return (FALSE
); /* abort download */
6261 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
6263 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6264 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6266 webkit_download_set_destination_uri(wk_download
, uri
);
6268 if (webkit_download_get_status(wk_download
) ==
6269 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6270 show_oops(t
, "%s: download failed to start", __func__
);
6272 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6274 /* connect "download first" mime handler */
6275 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6276 G_CALLBACK(download_status_changed_cb
), NULL
);
6278 download_entry
= g_malloc(sizeof(struct download
));
6279 download_entry
->download
= wk_download
;
6280 download_entry
->tab
= t
;
6281 download_entry
->id
= next_download_id
++;
6282 RB_INSERT(download_list
, &downloads
, download_entry
);
6283 /* get from history */
6284 g_object_ref(wk_download
);
6285 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6286 show_oops(t
, "Download of '%s' started...",
6287 basename(webkit_download_get_destination_uri(wk_download
)));
6293 /* sync other download manager tabs */
6294 update_download_tabs(NULL
);
6297 * NOTE: never redirect/render the current tab before this
6298 * function returns. This will cause the download to never start.
6300 return (ret
); /* start download */
6304 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6306 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6309 show_oops_s("webview_hover_cb");
6314 set_status(t
, uri
, XT_STATUS_LINK
);
6317 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6322 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6324 struct key_binding
*k
;
6326 TAILQ_FOREACH(k
, &kbl
, entry
)
6327 if (e
->keyval
== k
->key
&& (entry
? k
->use_in_entry
: 1)) {
6329 if ((e
->state
& (CTRL
| MOD1
)) == 0)
6330 return (cmd_execute(t
, k
->cmd
));
6331 } else if ((e
->state
& k
->mask
) == k
->mask
) {
6332 return (cmd_execute(t
, k
->cmd
));
6336 return (XT_CB_PASSTHROUGH
);
6340 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
6342 char s
[2], buf
[128];
6343 const char *errstr
= NULL
;
6346 /* don't use w directly; use t->whatever instead */
6349 show_oops_s("wv_keypress_after_cb");
6350 return (XT_CB_PASSTHROUGH
);
6353 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
6354 e
->keyval
, e
->state
, t
);
6358 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
6360 return (XT_CB_HANDLED
);
6364 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
6365 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6367 /* we have a string */
6369 /* we have a number */
6370 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
6378 /* XXX unfuck this */
6379 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
6380 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
6381 /* last input was numerical */
6383 l
= strlen(t
->hint_num
);
6390 t
->hint_num
[l
] = '\0';
6394 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
6395 /* last input was alphanumerical */
6397 l
= strlen(t
->hint_buf
);
6404 t
->hint_buf
[l
] = '\0';
6414 /* numerical input */
6415 if (CLEAN(e
->state
) == 0 &&
6416 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
6417 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6418 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
6419 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
6422 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
6424 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
6427 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
6429 t
->hint_mode
= XT_HINT_NUMERICAL
;
6433 /* empty the counter buffer */
6434 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
6435 return (XT_CB_HANDLED
);
6438 /* alphanumerical input */
6440 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
6441 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
6442 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
6443 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
6444 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6445 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
6446 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
6449 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
6452 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
6454 t
->hint_mode
= XT_HINT_ALPHANUM
;
6457 /* empty the counter buffer */
6458 bzero(t
->hint_num
, sizeof t
->hint_num
);
6459 return (XT_CB_HANDLED
);
6462 return (XT_CB_HANDLED
);
6465 snprintf(s
, sizeof s
, "%c", e
->keyval
);
6466 if (CLEAN(e
->state
) == 0 && isdigit(s
[0])) {
6467 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
6471 return (handle_keypress(t
, e
, 0));
6475 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6479 return (XT_CB_PASSTHROUGH
);
6483 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6485 const gchar
*c
= gtk_entry_get_text(w
);
6489 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6490 e
->keyval
, e
->state
, t
);
6493 show_oops_s("cmd_keyrelease_cb invalid parameters");
6494 return (XT_CB_PASSTHROUGH
);
6497 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
6498 e
->keyval
, e
->state
, t
);
6502 if (strlen(c
) == 1) {
6503 webkit_web_view_unmark_text_matches(t
->wv
);
6509 else if (c
[0] == '?')
6515 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6517 /* not found, mark red */
6518 gdk_color_parse(XT_COLOR_RED
, &color
);
6519 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6520 /* unmark and remove selection */
6521 webkit_web_view_unmark_text_matches(t
->wv
);
6522 /* my kingdom for a way to unselect text in webview */
6524 /* found, highlight all */
6525 webkit_web_view_unmark_text_matches(t
->wv
);
6526 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6527 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6528 gdk_color_parse(XT_COLOR_WHITE
, &color
);
6529 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6532 return (XT_CB_PASSTHROUGH
);
6536 match_uri(const gchar
*uri
, const gchar
*key
) {
6539 gboolean match
= FALSE
;
6543 if (!strncmp(key
, uri
, len
))
6546 voffset
= strstr(uri
, "/") + 2;
6547 if (!strncmp(key
, voffset
, len
))
6549 else if (g_str_has_prefix(voffset
, "www.")) {
6550 voffset
= voffset
+ strlen("www.");
6551 if (!strncmp(key
, voffset
, len
))
6560 cmd_getlist(int id
, char *key
)
6565 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
6566 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
6567 if (match_uri(h
->uri
, key
)) {
6568 cmd_status
.list
[c
] = (char *)h
->uri
;
6577 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
6579 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
6580 if(cmds
[i
].level
< dep
)
6582 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
, strlen(key
)))
6583 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
6591 cmd_getnext(int dir
)
6593 cmd_status
.index
+= dir
;
6595 if (cmd_status
.index
< 0)
6596 cmd_status
.index
= cmd_status
.len
- 1;
6597 else if (cmd_status
.index
>= cmd_status
.len
)
6598 cmd_status
.index
= 0;
6600 return cmd_status
.list
[cmd_status
.index
];
6604 cmd_tokenize(char *s
, char *tokens
[])
6608 size_t len
= strlen(s
);
6609 bool blank
= len
== 0 || (len
> 0 && s
[len
-1] == ' ');
6611 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3; tok
= strtok_r(NULL
, " ", &last
), i
++)
6621 cmd_complete(struct tab
*t
, char *str
, int dir
)
6623 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6624 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1, matchcount
= 0;
6625 char *tok
, *match
, *s
= g_strdup(str
);
6627 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
6630 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
6633 for (i
= 0; isdigit(s
[i
]); i
++)
6636 for (; isspace(s
[i
]); i
++)
6641 levels
= cmd_tokenize(s
, tokens
);
6643 for (i
= 0; i
< levels
- 1; i
++) {
6646 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6647 if (cmds
[j
].level
< dep
)
6649 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, strlen(tok
))) {
6652 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
6659 if (matchcount
== 1) {
6660 strlcat(res
, tok
, sizeof res
);
6661 strlcat(res
, " ", sizeof res
);
6671 if (cmd_status
.index
== -1)
6672 cmd_getlist(parent
, tokens
[i
]);
6674 if (cmd_status
.len
> 0) {
6675 match
= cmd_getnext(dir
);
6676 strlcat(res
, match
, sizeof res
);
6677 gtk_entry_set_text(w
, res
);
6678 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6685 cmd_execute(struct tab
*t
, char *str
)
6687 struct cmd
*cmd
= NULL
;
6688 char *tok
, *last
, *s
= g_strdup(str
), *sc
, prefixstr
[4];
6689 int j
, len
, c
= 0, dep
= 0, matchcount
= 0, prefix
= -1;
6690 struct karg arg
= {0, NULL
, -1};
6691 int rv
= XT_CB_PASSTHROUGH
;
6696 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
6702 while (isspace(s
[0]))
6705 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
6706 prefix
= atoi(prefixstr
);
6710 for (tok
= strtok_r(s
, " ", &last
); tok
;
6711 tok
= strtok_r(NULL
, " ", &last
)) {
6713 for (j
= c
; j
< LENGTH(cmds
); j
++) {
6714 if (cmds
[j
].level
< dep
)
6716 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1: strlen(tok
);
6717 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
, len
)) {
6721 if (len
== strlen(cmds
[j
].cmd
)) {
6727 if (matchcount
== 1) {
6732 show_oops(t
, "Invalid command: %s", str
);
6741 else if (cmd_prefix
> 0)
6744 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.p
> -1) {
6745 show_oops(t
, "No prefix allowed: %s", str
);
6749 arg
.s
= last
? g_strdup(last
) : g_strdup("");
6750 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
6751 arg
.p
= atoi(arg
.s
);
6754 show_oops(t
, "Zero count");
6756 show_oops(t
, "Trailing characters");
6761 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n", __func__
, arg
.p
, arg
.s
);
6777 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6780 show_oops_s("entry_key_cb invalid parameters");
6781 return (XT_CB_PASSTHROUGH
);
6784 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6785 e
->keyval
, e
->state
, t
);
6789 if (e
->keyval
== GDK_Escape
) {
6790 /* don't use focus_webview(t) because we want to type :cmds */
6791 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6794 return (handle_keypress(t
, e
, 1));
6798 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6800 int rv
= XT_CB_HANDLED
;
6801 const gchar
*c
= gtk_entry_get_text(w
);
6804 show_oops_s("cmd_keypress_cb parameters");
6805 return (XT_CB_PASSTHROUGH
);
6808 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6809 e
->keyval
, e
->state
, t
);
6813 e
->keyval
= GDK_Escape
;
6814 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6815 e
->keyval
= GDK_Escape
;
6817 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&& e
->keyval
!= GDK_ISO_Left_Tab
)
6818 cmd_status
.index
= -1;
6820 switch (e
->keyval
) {
6823 cmd_complete(t
, (char *)&c
[1], 1);
6825 case GDK_ISO_Left_Tab
:
6827 cmd_complete(t
, (char *)&c
[1], -1);
6831 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
6839 if (c
[0] == '/' || c
[0] == '?')
6840 webkit_web_view_unmark_text_matches(t
->wv
);
6844 rv
= XT_CB_PASSTHROUGH
;
6850 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6853 show_oops_s("cmd_focusout_cb invalid parameters");
6854 return (XT_CB_PASSTHROUGH
);
6856 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
6861 if (show_url
== 0 || t
->focus_wv
)
6864 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6866 return (XT_CB_PASSTHROUGH
);
6870 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6873 const gchar
*c
= gtk_entry_get_text(entry
);
6876 show_oops_s("cmd_activate_cb invalid parameters");
6880 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
6887 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6893 if (c
[0] == '/' || c
[0] == '?') {
6894 if (t
->search_text
) {
6895 g_free(t
->search_text
);
6896 t
->search_text
= NULL
;
6899 t
->search_text
= g_strdup(s
);
6901 g_free(global_search
);
6902 global_search
= g_strdup(s
);
6903 t
->search_forward
= c
[0] == '/';
6915 backward_cb(GtkWidget
*w
, struct tab
*t
)
6920 show_oops_s("backward_cb invalid parameters");
6924 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
6931 forward_cb(GtkWidget
*w
, struct tab
*t
)
6936 show_oops_s("forward_cb invalid parameters");
6940 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
6942 a
.i
= XT_NAV_FORWARD
;
6947 home_cb(GtkWidget
*w
, struct tab
*t
)
6950 show_oops_s("home_cb invalid parameters");
6954 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
6960 stop_cb(GtkWidget
*w
, struct tab
*t
)
6962 WebKitWebFrame
*frame
;
6965 show_oops_s("stop_cb invalid parameters");
6969 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
6971 frame
= webkit_web_view_get_main_frame(t
->wv
);
6972 if (frame
== NULL
) {
6973 show_oops(t
, "stop_cb: no frame");
6977 webkit_web_frame_stop_loading(frame
);
6978 abort_favicon_download(t
);
6982 setup_webkit(struct tab
*t
)
6984 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
6985 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
6986 FALSE
, (char *)NULL
);
6988 warnx("webkit does not have \"enable-dns-prefetching\" property");
6989 g_object_set(G_OBJECT(t
->settings
),
6990 "user-agent", t
->user_agent
, (char *)NULL
);
6991 g_object_set(G_OBJECT(t
->settings
),
6992 "enable-scripts", enable_scripts
, (char *)NULL
);
6993 g_object_set(G_OBJECT(t
->settings
),
6994 "enable-plugins", enable_plugins
, (char *)NULL
);
6995 g_object_set(G_OBJECT(t
->settings
),
6996 "javascript-can-open-windows-automatically", enable_scripts
, (char *)NULL
);
6997 g_object_set(G_OBJECT(t
->settings
),
6998 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
6999 g_object_set(G_OBJECT(t
->settings
),
7000 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
7001 g_object_set(G_OBJECT(t
->wv
),
7002 "full-content-zoom", TRUE
, (char *)NULL
);
7003 adjustfont_webkit(t
, XT_FONT_SET
);
7005 webkit_web_view_set_settings(t
->wv
, t
->settings
);
7009 create_browser(struct tab
*t
)
7015 show_oops_s("create_browser invalid parameters");
7019 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7020 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7021 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7022 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7024 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7025 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7026 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7028 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7029 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7032 t
->settings
= webkit_web_settings_new();
7034 if (user_agent
== NULL
) {
7035 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7037 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7040 t
->user_agent
= g_strdup(user_agent
);
7042 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7054 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7055 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7056 gtk_widget_set_name(w
, "xxxterm");
7057 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7058 g_signal_connect(G_OBJECT(w
), "delete_event",
7059 G_CALLBACK (gtk_main_quit
), NULL
);
7065 create_kiosk_toolbar(struct tab
*t
)
7067 GtkWidget
*toolbar
= NULL
, *b
;
7069 b
= gtk_hbox_new(FALSE
, 0);
7071 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7073 /* backward button */
7074 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7075 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7076 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7077 G_CALLBACK(backward_cb
), t
);
7078 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7080 /* forward button */
7081 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7082 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7083 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7084 G_CALLBACK(forward_cb
), t
);
7085 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7088 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7089 gtk_widget_set_sensitive(t
->gohome
, true);
7090 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7091 G_CALLBACK(home_cb
), t
);
7092 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7094 /* create widgets but don't use them */
7095 t
->uri_entry
= gtk_entry_new();
7096 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7097 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7098 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7104 create_toolbar(struct tab
*t
)
7106 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7108 b
= gtk_hbox_new(FALSE
, 0);
7110 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7113 /* backward button */
7114 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7115 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7116 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7117 G_CALLBACK(backward_cb
), t
);
7118 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7120 /* forward button */
7121 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7122 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7123 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7124 G_CALLBACK(forward_cb
), t
);
7125 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7129 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7130 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7131 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7132 G_CALLBACK(stop_cb
), t
);
7133 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7137 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7138 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7139 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7140 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7141 G_CALLBACK(js_toggle_cb
), t
);
7142 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7145 t
->uri_entry
= gtk_entry_new();
7146 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7147 G_CALLBACK(activate_uri_entry_cb
), t
);
7148 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7149 G_CALLBACK(entry_key_cb
), t
);
7151 eb1
= gtk_hbox_new(FALSE
, 0);
7152 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7153 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7154 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7157 if (fancy_bar
&& search_string
) {
7159 t
->search_entry
= gtk_entry_new();
7160 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7161 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7162 G_CALLBACK(activate_search_entry_cb
), t
);
7163 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7164 G_CALLBACK(entry_key_cb
), t
);
7165 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7166 eb2
= gtk_hbox_new(FALSE
, 0);
7167 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7168 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7170 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7180 TAILQ_FOREACH(t
, &tabs
, entry
)
7181 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7185 undo_close_tab_save(struct tab
*t
)
7189 struct undo
*u1
, *u2
;
7191 WebKitWebHistoryItem
*item
;
7193 if ((uri
= get_uri(t
->wv
)) == NULL
)
7196 u1
= g_malloc0(sizeof(struct undo
));
7197 u1
->uri
= g_strdup(uri
);
7199 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7201 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
7202 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
7205 /* forward history */
7206 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
7210 u1
->history
= g_list_prepend(u1
->history
,
7211 webkit_web_history_item_copy(item
));
7212 items
= g_list_next(items
);
7217 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
7218 u1
->history
= g_list_prepend(u1
->history
,
7219 webkit_web_history_item_copy(item
));
7223 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
7227 u1
->history
= g_list_prepend(u1
->history
,
7228 webkit_web_history_item_copy(item
));
7229 items
= g_list_next(items
);
7232 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
7234 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
7235 u2
= TAILQ_LAST(&undos
, undo_tailq
);
7236 TAILQ_REMOVE(&undos
, u2
, entry
);
7238 g_list_free(u2
->history
);
7247 delete_tab(struct tab
*t
)
7251 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
7256 TAILQ_REMOVE(&tabs
, t
, entry
);
7258 /* halt all webkit activity */
7259 abort_favicon_download(t
);
7260 webkit_web_view_stop_loading(t
->wv
);
7261 undo_close_tab_save(t
);
7263 if (browser_mode
== XT_BM_KIOSK
) {
7264 gtk_widget_destroy(t
->uri_entry
);
7265 gtk_widget_destroy(t
->stop
);
7266 gtk_widget_destroy(t
->js_toggle
);
7269 gtk_widget_destroy(t
->vbox
);
7270 g_free(t
->user_agent
);
7271 g_free(t
->stylesheet
);
7274 if (TAILQ_EMPTY(&tabs
)) {
7275 if (browser_mode
== XT_BM_KIOSK
)
7276 create_new_tab(home
, NULL
, 1, -1);
7278 create_new_tab(NULL
, NULL
, 1, -1);
7281 /* recreate session */
7282 if (session_autosave
) {
7289 adjustfont_webkit(struct tab
*t
, int adjust
)
7294 show_oops_s("adjustfont_webkit invalid parameters");
7298 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7299 if (adjust
== XT_FONT_SET
) {
7300 t
->font_size
= default_font_size
;
7301 zoom
= default_zoom_level
;
7302 t
->font_size
+= adjust
;
7303 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
7304 t
->font_size
, (char *)NULL
);
7305 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
7306 &t
->font_size
, (char *)NULL
);
7308 t
->font_size
+= adjust
;
7309 zoom
+= adjust
/25.0;
7314 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
7315 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
7319 append_tab(struct tab
*t
)
7324 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7325 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
7329 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
7334 WebKitWebHistoryItem
*item
;
7338 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
7340 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
7341 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
7345 t
= g_malloc0(sizeof *t
);
7347 if (title
== NULL
) {
7348 title
= "(untitled)";
7352 t
->vbox
= gtk_vbox_new(FALSE
, 0);
7354 /* label + button for tab */
7355 b
= gtk_hbox_new(FALSE
, 0);
7358 #if GTK_CHECK_VERSION(2, 20, 0)
7359 t
->spinner
= gtk_spinner_new ();
7361 t
->label
= gtk_label_new(title
);
7362 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
7363 gtk_widget_set_size_request(t
->label
, 100, 0);
7364 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
7365 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
7366 gtk_widget_set_size_request(b
, 130, 0);
7368 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
7369 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
7370 #if GTK_CHECK_VERSION(2, 20, 0)
7371 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
7375 if (browser_mode
== XT_BM_KIOSK
)
7376 t
->toolbar
= create_kiosk_toolbar(t
);
7378 t
->toolbar
= create_toolbar(t
);
7380 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
7383 t
->browser_win
= create_browser(t
);
7384 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
7386 /* oops message for user feedback */
7387 t
->oops
= gtk_entry_new();
7388 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
7389 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
7390 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
7391 gdk_color_parse(XT_COLOR_RED
, &color
);
7392 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
7393 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
7396 t
->cmd
= gtk_entry_new();
7397 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
7398 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
7399 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
7402 t
->statusbar
= gtk_entry_new();
7403 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
7404 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
7405 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
7406 gdk_color_parse(XT_COLOR_BLACK
, &color
);
7407 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7408 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7409 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
7410 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
7412 /* xtp meaning is normal by default */
7413 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
7415 /* set empty favicon */
7416 xt_icon_from_name(t
, "text-html");
7418 /* and show it all */
7419 gtk_widget_show_all(b
);
7420 gtk_widget_show_all(t
->vbox
);
7422 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
7425 id
= position
>= 0 ? position
: gtk_notebook_get_current_page(notebook
) + 1;
7426 if (id
> gtk_notebook_get_n_pages(notebook
))
7429 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
7430 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
7435 #if GTK_CHECK_VERSION(2, 20, 0)
7436 /* turn spinner off if we are a new tab without uri */
7438 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
7439 gtk_widget_hide(t
->spinner
);
7442 /* make notebook tabs reorderable */
7443 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
7445 g_object_connect(G_OBJECT(t
->cmd
),
7446 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
7447 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
7448 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
7449 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
7452 /* reuse wv_button_cb to hide oops */
7453 g_object_connect(G_OBJECT(t
->oops
),
7454 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7457 g_object_connect(G_OBJECT(t
->wv
),
7458 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
7459 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7460 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
7461 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
7462 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
7463 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7464 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
7465 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
7466 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
7467 "signal::event", G_CALLBACK(webview_event_cb
), t
,
7468 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
7469 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
7470 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
7471 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
7473 g_signal_connect(t
->wv
,
7474 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
7475 g_signal_connect(t
->wv
,
7476 "notify::title", G_CALLBACK(notify_title_cb
), t
);
7478 /* hijack the unused keys as if we were the browser */
7479 g_object_connect(G_OBJECT(t
->toolbar
),
7480 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
7483 g_signal_connect(G_OBJECT(bb
), "button_press_event",
7484 G_CALLBACK(tab_close_cb
), t
);
7489 url_set_visibility();
7490 statusbar_set_visibility();
7493 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7494 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
7499 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
7503 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7508 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
7509 /* restore the tab's history */
7510 if (u
&& u
->history
) {
7514 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
7515 items
= g_list_next(items
);
7518 item
= g_list_nth_data(u
->history
, u
->back
);
7520 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
7523 g_list_free(u
->history
);
7525 webkit_web_back_forward_list_clear(t
->bfl
);
7531 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7537 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
7539 if (gtk_notebook_get_current_page(notebook
) == -1)
7542 TAILQ_FOREACH(t
, &tabs
, entry
) {
7543 if (t
->tab_id
== pn
) {
7544 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
7547 uri
= webkit_web_view_get_title(t
->wv
);
7550 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
7556 /* can't use focus_webview here */
7557 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7564 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
7571 menuitem_response(struct tab
*t
)
7573 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
7577 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
7579 GtkWidget
*menu
, *menu_items
;
7580 GdkEventButton
*bevent
;
7584 if (event
->type
== GDK_BUTTON_PRESS
) {
7585 bevent
= (GdkEventButton
*) event
;
7586 menu
= gtk_menu_new();
7588 TAILQ_FOREACH(ti
, &tabs
, entry
) {
7589 if ((uri
= get_uri(ti
->wv
)) == NULL
)
7590 /* XXX make sure there is something to print */
7591 /* XXX add gui pages in here to look purdy */
7593 menu_items
= gtk_menu_item_new_with_label(uri
);
7594 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
7595 gtk_widget_show(menu_items
);
7597 g_signal_connect_swapped((menu_items
),
7598 "activate", G_CALLBACK(menuitem_response
),
7602 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
7603 bevent
->button
, bevent
->time
);
7605 /* unref object so it'll free itself when popped down */
7606 #if !GTK_CHECK_VERSION(3, 0, 0)
7607 /* XXX does not need unref with gtk+3? */
7608 g_object_ref_sink(menu
);
7609 g_object_unref(menu
);
7612 return (TRUE
/* eat event */);
7615 return (FALSE
/* propagate */);
7619 icon_size_map(int icon_size
)
7621 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
7622 icon_size
> GTK_ICON_SIZE_DIALOG
)
7623 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
7629 create_button(char *name
, char *stockid
, int size
)
7631 GtkWidget
*button
, *image
;
7635 rcstring
= g_strdup_printf(
7636 "style \"%s-style\"\n"
7638 " GtkWidget::focus-padding = 0\n"
7639 " GtkWidget::focus-line-width = 0\n"
7643 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
7644 gtk_rc_parse_string(rcstring
);
7646 button
= gtk_button_new();
7647 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
7648 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
7650 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
7651 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7652 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
7653 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
7654 gtk_widget_set_name(button
, name
);
7655 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
7661 button_set_stockid(GtkWidget
*button
, char *stockid
)
7665 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
7666 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
7667 gtk_button_set_image(GTK_BUTTON(button
), image
);
7671 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
7673 GtkClipboard
*clipboard
;
7674 gchar
*p
= NULL
, *s
= NULL
;
7677 * This code is very aggressive!
7678 * It basically ensures that the primary and regular clipboard are
7679 * always set the same. This obviously messes with standard X protocol
7680 * but those clowns should have come up with something better.
7683 /* XXX make this setting? */
7684 clipboard
= gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
);
7685 p
= gtk_clipboard_wait_for_text(primary
);
7687 DNPRINTF(XT_D_CLIP
, "primary cleaned\n");
7688 p
= gtk_clipboard_wait_for_text(clipboard
);
7690 gtk_clipboard_set_text(primary
, p
, -1);
7692 DNPRINTF(XT_D_CLIP
, "primary got selection\n");
7693 s
= gtk_clipboard_wait_for_text(clipboard
);
7696 * if s and p are the same the string was set by
7697 * clipb_clipboard_cb so do nothing in that case
7698 * to prevent endless loop
7703 gtk_clipboard_set_text(clipboard
, p
, -1);
7713 clipb_clipboard_cb(GtkClipboard
*clipboard
, GdkEvent
*event
, gpointer notused
)
7715 GtkClipboard
*primary
;
7716 gchar
*p
= NULL
, *s
= NULL
;
7718 DNPRINTF(XT_D_CLIP
, "clipboard got content\n");
7720 primary
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
7721 p
= gtk_clipboard_wait_for_text(clipboard
);
7723 s
= gtk_clipboard_wait_for_text(primary
);
7726 * if s and p are the same the string was set by
7727 * clipb_primary_cb so do nothing in that case
7728 * to prevent endless loop and deselection of text
7733 gtk_clipboard_set_text(primary
, p
, -1);
7748 char file
[PATH_MAX
];
7751 vbox
= gtk_vbox_new(FALSE
, 0);
7752 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
7753 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
7754 #if !GTK_CHECK_VERSION(3, 0, 0)
7755 /* XXX seems to be needed with gtk+2 */
7756 gtk_notebook_set_tab_hborder(notebook
, 0);
7757 gtk_notebook_set_tab_vborder(notebook
, 0);
7759 gtk_notebook_set_scrollable(notebook
, TRUE
);
7760 notebook_tab_set_visibility(notebook
);
7761 gtk_notebook_set_show_border(notebook
, FALSE
);
7762 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
7764 abtn
= gtk_button_new();
7765 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
7766 gtk_widget_set_size_request(arrow
, -1, -1);
7767 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
7768 gtk_widget_set_size_request(abtn
, -1, 20);
7770 #if GTK_CHECK_VERSION(2, 20, 0)
7771 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
7773 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
7774 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
7775 gtk_widget_set_size_request(vbox
, -1, -1);
7777 g_object_connect(G_OBJECT(notebook
),
7778 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
7780 g_object_connect(G_OBJECT(notebook
),
7781 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
), NULL
,
7783 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
7784 G_CALLBACK(arrow_cb
), NULL
);
7786 main_window
= create_window();
7787 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
7788 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
7791 for (i
= 0; i
< LENGTH(icons
); i
++) {
7792 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
7793 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
7794 l
= g_list_append(l
, pb
);
7796 gtk_window_set_default_icon_list(l
);
7799 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
7800 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
7801 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
)),
7802 "owner-change", G_CALLBACK(clipb_clipboard_cb
), NULL
);
7804 gtk_widget_show_all(abtn
);
7805 gtk_widget_show_all(main_window
);
7809 set_hook(void **hook
, char *name
)
7812 errx(1, "set_hook");
7814 if (*hook
== NULL
) {
7815 *hook
= dlsym(RTLD_NEXT
, name
);
7817 errx(1, "can't hook %s", name
);
7821 /* override libsoup soup_cookie_equal because it doesn't look at domain */
7823 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
7825 g_return_val_if_fail(cookie1
, FALSE
);
7826 g_return_val_if_fail(cookie2
, FALSE
);
7828 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
7829 !strcmp (cookie1
->value
, cookie2
->value
) &&
7830 !strcmp (cookie1
->path
, cookie2
->path
) &&
7831 !strcmp (cookie1
->domain
, cookie2
->domain
));
7835 transfer_cookies(void)
7838 SoupCookie
*sc
, *pc
;
7840 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7842 for (;cf
; cf
= cf
->next
) {
7844 sc
= soup_cookie_copy(pc
);
7845 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
7848 soup_cookies_free(cf
);
7852 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
7857 print_cookie("soup_cookie_jar_delete_cookie", c
);
7859 if (cookies_enabled
== 0)
7862 if (jar
== NULL
|| c
== NULL
)
7865 /* find and remove from persistent jar */
7866 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
7868 for (;cf
; cf
= cf
->next
) {
7870 if (soup_cookie_equal(ci
, c
)) {
7871 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
7876 soup_cookies_free(cf
);
7878 /* delete from session jar */
7879 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
7883 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
7885 struct domain
*d
= NULL
;
7889 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
7890 jar
, p_cookiejar
, s_cookiejar
);
7892 if (cookies_enabled
== 0)
7895 /* see if we are up and running */
7896 if (p_cookiejar
== NULL
) {
7897 _soup_cookie_jar_add_cookie(jar
, cookie
);
7900 /* disallow p_cookiejar adds, shouldn't happen */
7901 if (jar
== p_cookiejar
)
7905 if (jar
== NULL
|| cookie
== NULL
)
7908 if (enable_cookie_whitelist
&&
7909 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
7911 DNPRINTF(XT_D_COOKIE
,
7912 "soup_cookie_jar_add_cookie: reject %s\n",
7914 if (save_rejected_cookies
) {
7915 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
7916 show_oops_s("can't open reject cookie file");
7919 fseek(r_cookie_f
, 0, SEEK_END
);
7920 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
7921 cookie
->http_only
? "#HttpOnly_" : "",
7923 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
7925 cookie
->secure
? "TRUE" : "FALSE",
7927 (gulong
)soup_date_to_time_t(cookie
->expires
) :
7934 if (!allow_volatile_cookies
)
7938 if (cookie
->expires
== NULL
&& session_timeout
) {
7939 soup_cookie_set_expires(cookie
,
7940 soup_date_new_from_now(session_timeout
));
7941 print_cookie("modified add cookie", cookie
);
7944 /* see if we are white listed for persistence */
7945 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
7946 /* add to persistent jar */
7947 c
= soup_cookie_copy(cookie
);
7948 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
7949 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
7952 /* add to session jar */
7953 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
7954 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
7960 char file
[PATH_MAX
];
7962 set_hook((void *)&_soup_cookie_jar_add_cookie
,
7963 "soup_cookie_jar_add_cookie");
7964 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
7965 "soup_cookie_jar_delete_cookie");
7967 if (cookies_enabled
== 0)
7971 * the following code is intricate due to overriding several libsoup
7973 * do not alter order of these operations.
7976 /* rejected cookies */
7977 if (save_rejected_cookies
)
7978 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
, XT_REJECT_FILE
);
7980 /* persistent cookies */
7981 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
7982 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
7984 /* session cookies */
7985 s_cookiejar
= soup_cookie_jar_new();
7986 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
7987 cookie_policy
, (void *)NULL
);
7990 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
7994 setup_proxy(char *uri
)
7997 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
7998 soup_uri_free(proxy_uri
);
8002 if (http_proxy
!= uri
) {
8009 http_proxy
= g_strdup(uri
);
8010 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
8011 proxy_uri
= soup_uri_new(http_proxy
);
8012 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
8017 send_cmd_to_socket(char *cmd
)
8020 struct sockaddr_un sa
;
8022 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8023 warnx("%s: socket", __func__
);
8027 sa
.sun_family
= AF_UNIX
;
8028 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8029 work_dir
, XT_SOCKET_FILE
);
8032 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8033 warnx("%s: connect", __func__
);
8037 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
8038 warnx("%s: send", __func__
);
8049 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
8052 char str
[XT_MAX_URL_LENGTH
];
8053 socklen_t t
= sizeof(struct sockaddr_un
);
8054 struct sockaddr_un sa
;
8059 gint fd
= g_io_channel_unix_get_fd(source
);
8061 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
8066 if (getpeereid(s
, &uid
, &gid
) == -1) {
8070 if (uid
!= getuid() || gid
!= getgid()) {
8071 warnx("unauthorized user");
8077 warnx("not a valid user");
8081 n
= recv(s
, str
, sizeof(str
), 0);
8085 tt
= TAILQ_LAST(&tabs
, tab_list
);
8086 cmd_execute(tt
, str
);
8094 struct sockaddr_un sa
;
8096 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8097 warn("is_running: socket");
8101 sa
.sun_family
= AF_UNIX
;
8102 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8103 work_dir
, XT_SOCKET_FILE
);
8106 /* connect to see if there is a listener */
8107 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
8108 rv
= 0; /* not running */
8110 rv
= 1; /* already running */
8121 struct sockaddr_un sa
;
8123 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
8124 warn("build_socket: socket");
8128 sa
.sun_family
= AF_UNIX
;
8129 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
8130 work_dir
, XT_SOCKET_FILE
);
8133 /* connect to see if there is a listener */
8134 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8135 /* no listener so we will */
8136 unlink(sa
.sun_path
);
8138 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
8139 warn("build_socket: bind");
8143 if (listen(s
, 1) == -1) {
8144 warn("build_socket: listen");
8157 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8158 GtkTreeIter
*iter
, struct tab
*t
)
8162 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8170 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
8171 GtkTreeIter
*iter
, struct tab
*t
)
8175 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
8176 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
8177 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
8184 completion_add_uri(const gchar
*uri
)
8188 /* add uri to list_store */
8189 gtk_list_store_append(completion_model
, &iter
);
8190 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
8194 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
8195 GtkTreeIter
*iter
, gpointer user_data
)
8198 gboolean match
= FALSE
;
8200 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
8206 match
= match_uri(value
, key
);
8213 completion_add(struct tab
*t
)
8215 /* enable completion for tab */
8216 t
->completion
= gtk_entry_completion_new();
8217 gtk_entry_completion_set_text_column(t
->completion
, 0);
8218 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
8219 gtk_entry_completion_set_model(t
->completion
,
8220 GTK_TREE_MODEL(completion_model
));
8221 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
8223 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
8224 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
8225 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
8226 G_CALLBACK(completion_select_cb
), t
);
8227 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
8228 G_CALLBACK(completion_hover_cb
), t
);
8236 if (stat(dir
, &sb
)) {
8237 if (mkdir(dir
, S_IRWXU
) == -1)
8238 err(1, "mkdir %s", dir
);
8240 err(1, "stat %s", dir
);
8242 if (S_ISDIR(sb
.st_mode
) == 0)
8243 errx(1, "%s not a dir", dir
);
8244 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
8245 warnx("fixing invalid permissions on %s", dir
);
8246 if (chmod(dir
, S_IRWXU
) == -1)
8247 err(1, "chmod %s", dir
);
8255 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
8261 main(int argc
, char *argv
[])
8264 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
8265 char conf
[PATH_MAX
] = { '\0' };
8266 char file
[PATH_MAX
];
8267 char *env_proxy
= NULL
;
8270 struct sigaction sact
;
8271 gchar
*priority
= g_strdup("NORMAL");
8272 GIOChannel
*channel
;
8276 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
8278 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
8287 errx(0 , "Version: %s", version
);
8290 strlcpy(conf
, optarg
, sizeof(conf
));
8293 strlcpy(named_session
, optarg
, sizeof(named_session
));
8314 RB_INIT(&downloads
);
8318 TAILQ_INIT(&aliases
);
8324 gnutls_global_init();
8326 /* generate session keys for xtp pages */
8327 generate_xtp_session_key(&dl_session_key
);
8328 generate_xtp_session_key(&hl_session_key
);
8329 generate_xtp_session_key(&cl_session_key
);
8330 generate_xtp_session_key(&fl_session_key
);
8333 gtk_init(&argc
, &argv
);
8334 if (!g_thread_supported())
8335 g_thread_init(NULL
);
8338 bzero(&sact
, sizeof(sact
));
8339 sigemptyset(&sact
.sa_mask
);
8340 sact
.sa_handler
= sigchild
;
8341 sact
.sa_flags
= SA_NOCLDSTOP
;
8342 sigaction(SIGCHLD
, &sact
, NULL
);
8344 /* set download dir */
8345 pwd
= getpwuid(getuid());
8347 errx(1, "invalid user %d", getuid());
8348 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
8350 /* set default string settings */
8351 home
= g_strdup("https://www.cyphertite.com");
8352 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
8353 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
8354 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
8356 /* read config file */
8357 if (strlen(conf
) == 0)
8358 snprintf(conf
, sizeof conf
, "%s/.%s",
8359 pwd
->pw_dir
, XT_CONF_FILE
);
8360 config_parse(conf
, 0);
8362 /* working directory */
8363 if (strlen(work_dir
) == 0)
8364 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
8365 pwd
->pw_dir
, XT_DIR
);
8368 /* icon cache dir */
8369 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
8373 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
8377 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
8378 work_dir
, XT_SESSIONS_DIR
);
8379 xxx_dir(sessions_dir
);
8381 /* runtime settings that can override config file */
8382 if (runtime_settings
[0] != '\0')
8383 config_parse(runtime_settings
, 1);
8386 if (!strcmp(download_dir
, pwd
->pw_dir
))
8387 strlcat(download_dir
, "/downloads", sizeof download_dir
);
8388 xxx_dir(download_dir
);
8390 /* favorites file */
8391 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
8392 if (stat(file
, &sb
)) {
8393 warnx("favorites file doesn't exist, creating it");
8394 if ((f
= fopen(file
, "w")) == NULL
)
8395 err(1, "favorites");
8400 session
= webkit_get_default_session();
8401 /* XXX ssl-priority property not quite available yet */
8402 if (is_g_object_setting(G_OBJECT(session
), "ssl-priority"))
8403 g_object_set(G_OBJECT(session
), "ssl-priority", priority
,
8406 warnx("session does not have \"ssl-priority\" property");
8411 if (stat(ssl_ca_file
, &sb
)) {
8412 warnx("no CA file: %s", ssl_ca_file
);
8413 g_free(ssl_ca_file
);
8416 g_object_set(session
,
8417 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
8418 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
8423 env_proxy
= getenv("http_proxy");
8425 setup_proxy(env_proxy
);
8427 setup_proxy(http_proxy
);
8430 send_cmd_to_socket(argv
[0]);
8434 /* set some connection parameters */
8435 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
8436 g_object_set(session
, "max-conns-per-host", max_host_connections
,
8439 /* see if there is already an xxxterm running */
8440 if (single_instance
&& is_running()) {
8442 warnx("already running");
8448 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
8449 send_cmd_to_socket(cmd
);
8459 /* uri completion */
8460 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
8465 if (save_global_history
)
8466 restore_global_history();
8468 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
8469 restore_saved_tabs();
8471 a
.s
= named_session
;
8472 a
.i
= XT_SES_DONOTHING
;
8473 open_tabs(NULL
, &a
);
8477 create_new_tab(argv
[0], NULL
, focus
, -1);
8484 if (TAILQ_EMPTY(&tabs
))
8485 create_new_tab(home
, NULL
, 1, -1);
8488 if ((s
= build_socket()) != -1) {
8489 channel
= g_io_channel_unix_new(s
);
8490 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
8495 gnutls_global_deinit();