3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010, 2011 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
7 * Copyright (c) 2011 Raphael Graf <r@undefined.ch>
8 * Copyright (c) 2011 Michal Mazurek <akfaew@jasminek.net>
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 * multi letter commands
26 * pre and post counts for commands
27 * autocompletion on various inputs
28 * create privacy browsing
29 * - encrypted local data
46 #include <sys/types.h>
48 #if defined(__linux__)
49 #include "linux/util.h"
50 #include "linux/tree.h"
51 #elif defined(__FreeBSD__)
53 #include "freebsd/util.h"
59 #include <sys/queue.h>
60 #include <sys/resource.h>
61 #include <sys/socket.h>
67 #include <gdk/gdkkeysyms.h>
69 #if GTK_CHECK_VERSION(3,0,0)
70 /* we still use GDK_* instead of GDK_KEY_* */
71 #include <gdk/gdkkeysyms-compat.h>
74 #include <webkit/webkit.h>
75 #include <libsoup/soup.h>
76 #include <gnutls/gnutls.h>
77 #include <JavaScriptCore/JavaScript.h>
78 #include <gnutls/x509.h>
80 #include "javascript.h"
83 javascript.h borrowed from vimprobable2 under the following license:
85 Copyright (c) 2009 Leon Winter
86 Copyright (c) 2009 Hannes Schueller
87 Copyright (c) 2009 Matto Fransen
89 Permission is hereby granted, free of charge, to any person obtaining a copy
90 of this software and associated documentation files (the "Software"), to deal
91 in the Software without restriction, including without limitation the rights
92 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
93 copies of the Software, and to permit persons to whom the Software is
94 furnished to do so, subject to the following conditions:
96 The above copyright notice and this permission notice shall be included in
97 all copies or substantial portions of the Software.
99 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
100 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
101 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
102 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
103 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
104 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
108 static char *version
= "$xxxterm$";
110 /* hooked functions */
111 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
112 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
117 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
118 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
119 #define XT_D_MOVE 0x0001
120 #define XT_D_KEY 0x0002
121 #define XT_D_TAB 0x0004
122 #define XT_D_URL 0x0008
123 #define XT_D_CMD 0x0010
124 #define XT_D_NAV 0x0020
125 #define XT_D_DOWNLOAD 0x0040
126 #define XT_D_CONFIG 0x0080
127 #define XT_D_JS 0x0100
128 #define XT_D_FAVORITE 0x0200
129 #define XT_D_PRINTING 0x0400
130 #define XT_D_COOKIE 0x0800
131 #define XT_D_KEYBINDING 0x1000
132 #define XT_D_CLIP 0x2000
133 #define XT_D_BUFFERCMD 0x4000
134 u_int32_t swm_debug
= 0
152 #define DPRINTF(x...)
153 #define DNPRINTF(n,x...)
156 #define LENGTH(x) (sizeof x / sizeof x[0])
157 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
158 ~(GDK_BUTTON1_MASK) & \
159 ~(GDK_BUTTON2_MASK) & \
160 ~(GDK_BUTTON3_MASK) & \
161 ~(GDK_BUTTON4_MASK) & \
164 #define XT_NOMARKS (('z' - 'a' + 1) * 2 + 10)
175 TAILQ_ENTRY(tab
) entry
;
177 GtkWidget
*tab_content
;
186 GtkWidget
*uri_entry
;
187 GtkWidget
*search_entry
;
189 GtkWidget
*browser_win
;
190 GtkWidget
*statusbar_box
;
192 GtkWidget
*statusbar
;
193 GtkWidget
*buffercmd
;
204 GtkWidget
*js_toggle
;
205 GtkEntryCompletion
*completion
;
209 WebKitWebHistoryItem
*item
;
210 WebKitWebBackForwardList
*bfl
;
213 WebKitNetworkRequest
*icon_request
;
214 WebKitDownload
*icon_download
;
215 gchar
*icon_dest_uri
;
217 /* adjustments for browser */
220 GtkAdjustment
*adjust_h
;
221 GtkAdjustment
*adjust_v
;
227 int xtp_meaning
; /* identifies dls/favorites */
233 #define XT_HINT_NONE (0)
234 #define XT_HINT_NUMERICAL (1)
235 #define XT_HINT_ALPHANUM (2)
239 /* custom stylesheet */
248 WebKitWebSettings
*settings
;
252 double mark
[XT_NOMARKS
];
254 TAILQ_HEAD(tab_list
, tab
);
257 RB_ENTRY(history
) entry
;
261 RB_HEAD(history_list
, history
);
264 RB_ENTRY(download
) entry
;
266 WebKitDownload
*download
;
269 RB_HEAD(download_list
, download
);
272 RB_ENTRY(domain
) entry
;
274 int handy
; /* app use */
276 RB_HEAD(domain_list
, domain
);
279 TAILQ_ENTRY(undo
) entry
;
282 int back
; /* Keeps track of how many back
283 * history items there are. */
285 TAILQ_HEAD(undo_tailq
, undo
);
287 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
288 int next_download_id
= 1;
297 #define XT_NAME ("XXXTerm")
298 #define XT_DIR (".xxxterm")
299 #define XT_CACHE_DIR ("cache")
300 #define XT_CERT_DIR ("certs/")
301 #define XT_SESSIONS_DIR ("sessions/")
302 #define XT_CONF_FILE ("xxxterm.conf")
303 #define XT_FAVS_FILE ("favorites")
304 #define XT_QMARKS_FILE ("quickmarks")
305 #define XT_SAVED_TABS_FILE ("main_session")
306 #define XT_RESTART_TABS_FILE ("restart_tabs")
307 #define XT_SOCKET_FILE ("socket")
308 #define XT_HISTORY_FILE ("history")
309 #define XT_REJECT_FILE ("rejected.txt")
310 #define XT_COOKIE_FILE ("cookies.txt")
311 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
312 #define XT_CB_HANDLED (TRUE)
313 #define XT_CB_PASSTHROUGH (FALSE)
314 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>\n"
315 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
316 #define XT_DLMAN_REFRESH "10"
317 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
318 "td{overflow: hidden;" \
319 " padding: 2px 2px 2px 2px;" \
320 " border: 1px solid black;" \
321 " vertical-align:top;" \
322 " word-wrap: break-word}\n" \
323 "tr:hover{background: #ffff99}\n" \
324 "th{background-color: #cccccc;" \
325 " border: 1px solid black}\n" \
326 "table{width: 100%%;" \
327 " border: 1px black solid;" \
328 " border-collapse:collapse}\n" \
330 "border: 1px solid black;" \
333 ".progress-inner{float: left;" \
335 " background: green}\n" \
336 ".dlstatus{font-size: small;" \
337 " text-align: center}\n" \
339 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
340 #define XT_MAX_UNDO_CLOSE_TAB (32)
341 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
342 #define XT_PRINT_EXTRA_MARGIN 10
343 #define XT_INVALID_MARK (-1) /* XXX this is a double, maybe use something else, like a nan */
346 #define XT_COLOR_RED "#cc0000"
347 #define XT_COLOR_YELLOW "#ffff66"
348 #define XT_COLOR_BLUE "lightblue"
349 #define XT_COLOR_GREEN "#99ff66"
350 #define XT_COLOR_WHITE "white"
351 #define XT_COLOR_BLACK "black"
353 #define XT_COLOR_CT_BACKGROUND "#000000"
354 #define XT_COLOR_CT_INACTIVE "#dddddd"
355 #define XT_COLOR_CT_ACTIVE "#bbbb00"
356 #define XT_COLOR_CT_SEPARATOR "#555555"
358 #define XT_COLOR_SB_SEPARATOR "#555555"
360 #define XT_PROTO_DELIM "://"
363 * xxxterm "protocol" (xtp)
364 * We use this for managing stuff like downloads and favorites. They
365 * make magical HTML pages in memory which have xxxt:// links in order
366 * to communicate with xxxterm's internals. These links take the format:
367 * xxxt://class/session_key/action/arg
369 * Don't begin xtp class/actions as 0. atoi returns that on error.
371 * Typically we have not put addition of items in this framework, as
372 * adding items is either done via an ex-command or via a keybinding instead.
375 #define XT_XTP_STR "xxxt://"
377 /* XTP classes (xxxt://<class>) */
378 #define XT_XTP_INVALID 0 /* invalid */
379 #define XT_XTP_DL 1 /* downloads */
380 #define XT_XTP_HL 2 /* history */
381 #define XT_XTP_CL 3 /* cookies */
382 #define XT_XTP_FL 4 /* favorites */
384 /* XTP download actions */
385 #define XT_XTP_DL_LIST 1
386 #define XT_XTP_DL_CANCEL 2
387 #define XT_XTP_DL_REMOVE 3
389 /* XTP history actions */
390 #define XT_XTP_HL_LIST 1
391 #define XT_XTP_HL_REMOVE 2
393 /* XTP cookie actions */
394 #define XT_XTP_CL_LIST 1
395 #define XT_XTP_CL_REMOVE 2
397 /* XTP cookie actions */
398 #define XT_XTP_FL_LIST 1
399 #define XT_XTP_FL_REMOVE 2
402 #define XT_MOVE_INVALID (0)
403 #define XT_MOVE_DOWN (1)
404 #define XT_MOVE_UP (2)
405 #define XT_MOVE_BOTTOM (3)
406 #define XT_MOVE_TOP (4)
407 #define XT_MOVE_PAGEDOWN (5)
408 #define XT_MOVE_PAGEUP (6)
409 #define XT_MOVE_HALFDOWN (7)
410 #define XT_MOVE_HALFUP (8)
411 #define XT_MOVE_LEFT (9)
412 #define XT_MOVE_FARLEFT (10)
413 #define XT_MOVE_RIGHT (11)
414 #define XT_MOVE_FARRIGHT (12)
415 #define XT_MOVE_PERCENT (13)
417 #define XT_QMARK_SET (0)
418 #define XT_QMARK_OPEN (1)
419 #define XT_QMARK_TAB (2)
421 #define XT_MARK_SET (0)
422 #define XT_MARK_GOTO (1)
424 #define XT_TAB_LAST (-4)
425 #define XT_TAB_FIRST (-3)
426 #define XT_TAB_PREV (-2)
427 #define XT_TAB_NEXT (-1)
428 #define XT_TAB_INVALID (0)
429 #define XT_TAB_NEW (1)
430 #define XT_TAB_DELETE (2)
431 #define XT_TAB_DELQUIT (3)
432 #define XT_TAB_OPEN (4)
433 #define XT_TAB_UNDO_CLOSE (5)
434 #define XT_TAB_SHOW (6)
435 #define XT_TAB_HIDE (7)
436 #define XT_TAB_NEXTSTYLE (8)
438 #define XT_NAV_INVALID (0)
439 #define XT_NAV_BACK (1)
440 #define XT_NAV_FORWARD (2)
441 #define XT_NAV_RELOAD (3)
443 #define XT_FOCUS_INVALID (0)
444 #define XT_FOCUS_URI (1)
445 #define XT_FOCUS_SEARCH (2)
447 #define XT_SEARCH_INVALID (0)
448 #define XT_SEARCH_NEXT (1)
449 #define XT_SEARCH_PREV (2)
451 #define XT_PASTE_CURRENT_TAB (0)
452 #define XT_PASTE_NEW_TAB (1)
454 #define XT_ZOOM_IN (-1)
455 #define XT_ZOOM_OUT (-2)
456 #define XT_ZOOM_NORMAL (100)
458 #define XT_URL_SHOW (1)
459 #define XT_URL_HIDE (2)
461 #define XT_WL_TOGGLE (1<<0)
462 #define XT_WL_ENABLE (1<<1)
463 #define XT_WL_DISABLE (1<<2)
464 #define XT_WL_FQDN (1<<3) /* default */
465 #define XT_WL_TOPLEVEL (1<<4)
466 #define XT_WL_PERSISTENT (1<<5)
467 #define XT_WL_SESSION (1<<6)
468 #define XT_WL_RELOAD (1<<7)
470 #define XT_SHOW (1<<7)
471 #define XT_DELETE (1<<8)
472 #define XT_SAVE (1<<9)
473 #define XT_OPEN (1<<10)
475 #define XT_CMD_OPEN (0)
476 #define XT_CMD_OPEN_CURRENT (1)
477 #define XT_CMD_TABNEW (2)
478 #define XT_CMD_TABNEW_CURRENT (3)
480 #define XT_STATUS_NOTHING (0)
481 #define XT_STATUS_LINK (1)
482 #define XT_STATUS_URI (2)
483 #define XT_STATUS_LOADING (3)
485 #define XT_SES_DONOTHING (0)
486 #define XT_SES_CLOSETABS (1)
488 #define XT_BM_NORMAL (0)
489 #define XT_BM_WHITELIST (1)
490 #define XT_BM_KIOSK (2)
492 #define XT_PREFIX (1<<0)
493 #define XT_USERARG (1<<1)
494 #define XT_URLARG (1<<2)
495 #define XT_INTARG (1<<3)
497 #define XT_TABS_NORMAL 0
498 #define XT_TABS_COMPACT 1
506 TAILQ_ENTRY(mime_type
) entry
;
508 TAILQ_HEAD(mime_type_list
, mime_type
);
514 TAILQ_ENTRY(alias
) entry
;
516 TAILQ_HEAD(alias_list
, alias
);
518 /* settings that require restart */
519 int tabless
= 0; /* allow only 1 tab */
520 int enable_socket
= 0;
521 int single_instance
= 0; /* only allow one xxxterm to run */
522 int fancy_bar
= 1; /* fancy toolbar */
523 int browser_mode
= XT_BM_NORMAL
;
524 int enable_localstorage
= 0;
525 char *statusbar_elems
= NULL
;
527 /* runtime settings */
528 int show_tabs
= 1; /* show tabs on notebook */
529 int tab_style
= XT_TABS_NORMAL
; /* tab bar style */
530 int show_url
= 1; /* show url toolbar on notebook */
531 int show_statusbar
= 0; /* vimperator style status bar */
532 int ctrl_click_focus
= 0; /* ctrl click gets focus */
533 int cookies_enabled
= 1; /* enable cookies */
534 int read_only_cookies
= 0; /* enable to not write cookies */
535 int enable_scripts
= 1;
536 int enable_plugins
= 0;
537 gfloat default_zoom_level
= 1.0;
538 char default_script
[PATH_MAX
];
539 int window_height
= 768;
540 int window_width
= 1024;
541 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
542 int refresh_interval
= 10; /* download refresh interval */
543 int enable_cookie_whitelist
= 0;
544 int enable_js_whitelist
= 0;
545 int session_timeout
= 3600; /* cookie session timeout */
546 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
547 char *ssl_ca_file
= NULL
;
548 char *resource_dir
= NULL
;
549 gboolean ssl_strict_certs
= FALSE
;
550 int append_next
= 1; /* append tab after current tab */
552 char *search_string
= NULL
;
553 char *http_proxy
= NULL
;
554 char download_dir
[PATH_MAX
];
555 char runtime_settings
[PATH_MAX
]; /* override of settings */
556 int allow_volatile_cookies
= 0;
557 int save_global_history
= 0; /* save global history to disk */
558 char *user_agent
= NULL
;
559 int save_rejected_cookies
= 0;
560 int session_autosave
= 0;
561 int guess_search
= 0;
562 int dns_prefetch
= FALSE
;
563 gint max_connections
= 25;
564 gint max_host_connections
= 5;
565 gint enable_spell_checking
= 0;
566 char *spell_check_languages
= NULL
;
568 char *cmd_font_name
= NULL
;
569 char *oops_font_name
= NULL
;
570 char *statusbar_font_name
= NULL
;
571 char *tabbar_font_name
= NULL
;
572 PangoFontDescription
*cmd_font
;
573 PangoFontDescription
*oops_font
;
574 PangoFontDescription
*statusbar_font
;
575 PangoFontDescription
*tabbar_font
;
576 char *qmarks
[XT_NOMARKS
];
578 int btn_down
; /* M1 down in any wv */
582 int set_browser_mode(struct settings
*, char *);
583 int set_cookie_policy(struct settings
*, char *);
584 int set_download_dir(struct settings
*, char *);
585 int set_default_script(struct settings
*, char *);
586 int set_runtime_dir(struct settings
*, char *);
587 int set_tab_style(struct settings
*, char *);
588 int set_work_dir(struct settings
*, char *);
589 int add_alias(struct settings
*, char *);
590 int add_mime_type(struct settings
*, char *);
591 int add_cookie_wl(struct settings
*, char *);
592 int add_js_wl(struct settings
*, char *);
593 int add_kb(struct settings
*, char *);
594 void button_set_stockid(GtkWidget
*, char *);
595 GtkWidget
* create_button(char *, char *, int);
597 char *get_browser_mode(struct settings
*);
598 char *get_cookie_policy(struct settings
*);
599 char *get_download_dir(struct settings
*);
600 char *get_default_script(struct settings
*);
601 char *get_runtime_dir(struct settings
*);
602 char *get_tab_style(struct settings
*);
603 char *get_work_dir(struct settings
*);
605 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
606 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
607 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
608 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
609 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
611 void recalc_tabs(void);
612 void recolor_compact_tabs(void);
613 void set_current_tab(int page_num
);
614 gboolean
update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
);
615 void marks_clear(struct tab
*t
);
618 int (*set
)(struct settings
*, char *);
619 char *(*get
)(struct settings
*);
620 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
623 struct special s_browser_mode
= {
629 struct special s_cookie
= {
635 struct special s_alias
= {
641 struct special s_mime
= {
647 struct special s_js
= {
653 struct special s_kb
= {
659 struct special s_cookie_wl
= {
665 struct special s_default_script
= {
671 struct special s_download_dir
= {
677 struct special s_work_dir
= {
683 struct special s_tab_style
= {
692 #define XT_S_INVALID (0)
695 #define XT_S_FLOAT (3)
697 #define XT_SF_RESTART (1<<0)
698 #define XT_SF_RUNTIME (1<<1)
704 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
705 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
706 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
707 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
708 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
709 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
710 { "default_zoom_level", XT_S_FLOAT
, 0, NULL
, NULL
, NULL
, &default_zoom_level
},
711 { "default_script", XT_S_STR
, 0, NULL
, NULL
,&s_default_script
},
712 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
713 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
714 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
715 { "enable_localstorage", XT_S_INT
, 0, &enable_localstorage
, NULL
, NULL
},
716 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
717 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
718 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
719 { "enable_spell_checking", XT_S_INT
, 0, &enable_spell_checking
, NULL
, NULL
},
720 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
721 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
722 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
723 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
724 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
725 { "max_connections", XT_S_INT
, XT_SF_RESTART
,&max_connections
, NULL
, NULL
},
726 { "max_host_connections", XT_S_INT
, XT_SF_RESTART
,&max_host_connections
, NULL
, NULL
},
727 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
728 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
729 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
730 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
731 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
732 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
733 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
734 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
735 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
736 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
737 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
738 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
739 { "spell_check_languages", XT_S_STR
, 0, NULL
, &spell_check_languages
, NULL
},
740 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
741 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
742 { "statusbar_elems", XT_S_STR
, 0, NULL
, &statusbar_elems
, NULL
},
743 { "tab_style", XT_S_STR
, 0, NULL
, NULL
,&s_tab_style
},
744 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
745 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
746 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
747 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
750 { "cmd_font", XT_S_STR
, 0, NULL
, &cmd_font_name
, NULL
},
751 { "oops_font", XT_S_STR
, 0, NULL
, &oops_font_name
, NULL
},
752 { "statusbar_font", XT_S_STR
, 0, NULL
, &statusbar_font_name
, NULL
},
753 { "tabbar_font", XT_S_STR
, 0, NULL
, &tabbar_font_name
, NULL
},
755 /* runtime settings */
756 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
757 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
758 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
759 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
760 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
763 int about(struct tab
*, struct karg
*);
764 int blank(struct tab
*, struct karg
*);
765 int ca_cmd(struct tab
*, struct karg
*);
766 int cookie_show_wl(struct tab
*, struct karg
*);
767 int js_show_wl(struct tab
*, struct karg
*);
768 int help(struct tab
*, struct karg
*);
769 int set(struct tab
*, struct karg
*);
770 int stats(struct tab
*, struct karg
*);
771 int marco(struct tab
*, struct karg
*);
772 const char * marco_message(int *);
773 int xtp_page_cl(struct tab
*, struct karg
*);
774 int xtp_page_dl(struct tab
*, struct karg
*);
775 int xtp_page_fl(struct tab
*, struct karg
*);
776 int xtp_page_hl(struct tab
*, struct karg
*);
777 void xt_icon_from_file(struct tab
*, char *);
778 const gchar
*get_uri(struct tab
*);
779 const gchar
*get_title(struct tab
*, bool);
781 #define XT_URI_ABOUT ("about:")
782 #define XT_URI_ABOUT_LEN (strlen(XT_URI_ABOUT))
783 #define XT_URI_ABOUT_ABOUT ("about")
784 #define XT_URI_ABOUT_BLANK ("blank")
785 #define XT_URI_ABOUT_CERTS ("certs")
786 #define XT_URI_ABOUT_COOKIEWL ("cookiewl")
787 #define XT_URI_ABOUT_COOKIEJAR ("cookiejar")
788 #define XT_URI_ABOUT_DOWNLOADS ("downloads")
789 #define XT_URI_ABOUT_FAVORITES ("favorites")
790 #define XT_URI_ABOUT_HELP ("help")
791 #define XT_URI_ABOUT_HISTORY ("history")
792 #define XT_URI_ABOUT_JSWL ("jswl")
793 #define XT_URI_ABOUT_SET ("set")
794 #define XT_URI_ABOUT_STATS ("stats")
795 #define XT_URI_ABOUT_MARCO ("marco")
799 int (*func
)(struct tab
*, struct karg
*);
801 { XT_URI_ABOUT_ABOUT
, about
},
802 { XT_URI_ABOUT_BLANK
, blank
},
803 { XT_URI_ABOUT_CERTS
, ca_cmd
},
804 { XT_URI_ABOUT_COOKIEWL
, cookie_show_wl
},
805 { XT_URI_ABOUT_COOKIEJAR
, xtp_page_cl
},
806 { XT_URI_ABOUT_DOWNLOADS
, xtp_page_dl
},
807 { XT_URI_ABOUT_FAVORITES
, xtp_page_fl
},
808 { XT_URI_ABOUT_HELP
, help
},
809 { XT_URI_ABOUT_HISTORY
, xtp_page_hl
},
810 { XT_URI_ABOUT_JSWL
, js_show_wl
},
811 { XT_URI_ABOUT_SET
, set
},
812 { XT_URI_ABOUT_STATS
, stats
},
813 { XT_URI_ABOUT_MARCO
, marco
},
816 /* xtp tab meanings - identifies which tabs have xtp pages in (corresponding to about_list indices) */
817 #define XT_XTP_TAB_MEANING_NORMAL -1 /* normal url */
818 #define XT_XTP_TAB_MEANING_BL 1 /* about:blank in this tab */
819 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
820 #define XT_XTP_TAB_MEANING_DL 5 /* download manager in this tab */
821 #define XT_XTP_TAB_MEANING_FL 6 /* favorite manager in this tab */
822 #define XT_XTP_TAB_MEANING_HL 8 /* history manager in this tab */
825 extern char *__progname
;
828 GtkWidget
*main_window
;
829 GtkNotebook
*notebook
;
831 GtkWidget
*arrow
, *abtn
;
832 struct tab_list tabs
;
833 struct history_list hl
;
834 struct download_list downloads
;
835 struct domain_list c_wl
;
836 struct domain_list js_wl
;
837 struct undo_tailq undos
;
838 struct keybinding_list kbl
;
840 int updating_dl_tabs
= 0;
841 int updating_hl_tabs
= 0;
842 int updating_cl_tabs
= 0;
843 int updating_fl_tabs
= 0;
845 uint64_t blocked_cookies
= 0;
846 char named_session
[PATH_MAX
];
847 int icon_size_map(int);
849 GtkListStore
*completion_model
;
850 void completion_add(struct tab
*);
851 void completion_add_uri(const gchar
*);
852 GtkListStore
*buffers_store
;
853 void xxx_dir(char *);
855 /* marks and quickmarks array storage.
856 * first a-z, then A-Z, then 0-9 */
863 if (i
>= 0 && i
<= 'z' - 'a')
867 if (i
>= 0 && i
<= 'Z' - 'A')
882 if (m
>= 'a' && m
<= 'z')
883 return ret
+ m
- 'a';
885 ret
+= 'z' - 'a' + 1;
886 if (m
>= 'A' && m
<= 'Z')
887 return ret
+ m
- 'A';
889 ret
+= 'Z' - 'A' + 1;
890 if (m
>= '0' && m
<= '9')
891 return ret
+ m
- '0';
900 int saved_errno
, status
;
905 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
909 if (errno
!= ECHILD
) {
911 clog_warn("sigchild: waitpid:");
917 if (WIFEXITED(status
)) {
918 if (WEXITSTATUS(status
) != 0) {
920 clog_warnx("sigchild: child exit status: %d",
921 WEXITSTATUS(status));
926 clog_warnx("sigchild: child is terminated abnormally");
935 is_g_object_setting(GObject
*o
, char *str
)
937 guint n_props
= 0, i
;
938 GParamSpec
**proplist
;
940 if (! G_IS_OBJECT(o
))
943 proplist
= g_object_class_list_properties(G_OBJECT_GET_CLASS(o
),
946 for (i
=0; i
< n_props
; i
++) {
947 if (! strcmp(proplist
[i
]->name
, str
))
954 get_html_page(gchar
*title
, gchar
*body
, gchar
*head
, bool addstyles
)
958 r
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
960 "<title>%s</title>\n"
969 addstyles
? XT_PAGE_STYLE
: "",
978 * Display a web page from a HTML string in memory, rather than from a URL
981 load_webkit_string(struct tab
*t
, const char *str
, gchar
*title
)
986 /* we set this to indicate we want to manually do navaction */
988 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
990 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
992 /* set t->xtp_meaning */
993 for (i
= 0; i
< LENGTH(about_list
); i
++)
994 if (!strcmp(title
, about_list
[i
].name
)) {
999 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, "file://");
1000 #if GTK_CHECK_VERSION(2, 20, 0)
1001 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
1002 gtk_widget_hide(t
->spinner
);
1004 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[0]);
1005 xt_icon_from_file(t
, file
);
1010 get_current_tab(void)
1014 TAILQ_FOREACH(t
, &tabs
, entry
) {
1015 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
1019 warnx("%s: no current tab", __func__
);
1025 set_status(struct tab
*t
, gchar
*s
, int status
)
1033 case XT_STATUS_LOADING
:
1034 type
= g_strdup_printf("Loading: %s", s
);
1037 case XT_STATUS_LINK
:
1038 type
= g_strdup_printf("Link: %s", s
);
1040 t
->status
= g_strdup(gtk_entry_get_text(
1041 GTK_ENTRY(t
->sbe
.statusbar
)));
1045 type
= g_strdup_printf("%s", s
);
1047 t
->status
= g_strdup(type
);
1051 t
->status
= g_strdup(s
);
1053 case XT_STATUS_NOTHING
:
1058 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.statusbar
), s
);
1064 hide_cmd(struct tab
*t
)
1066 gtk_widget_hide(t
->cmd
);
1070 show_cmd(struct tab
*t
)
1072 gtk_widget_hide(t
->oops
);
1073 gtk_widget_show(t
->cmd
);
1077 hide_buffers(struct tab
*t
)
1079 gtk_widget_hide(t
->buffers
);
1080 gtk_list_store_clear(buffers_store
);
1090 sort_tabs_by_page_num(struct tab
***stabs
)
1095 num_tabs
= gtk_notebook_get_n_pages(notebook
);
1097 *stabs
= g_malloc0(num_tabs
* sizeof(struct tab
*));
1099 TAILQ_FOREACH(t
, &tabs
, entry
)
1100 (*stabs
)[gtk_notebook_page_num(notebook
, t
->vbox
)] = t
;
1106 buffers_make_list(void)
1109 const gchar
*title
= NULL
;
1111 struct tab
**stabs
= NULL
;
1113 num_tabs
= sort_tabs_by_page_num(&stabs
);
1115 for (i
= 0; i
< num_tabs
; i
++)
1117 gtk_list_store_append(buffers_store
, &iter
);
1118 title
= get_title(stabs
[i
], FALSE
);
1119 gtk_list_store_set(buffers_store
, &iter
,
1120 COL_ID
, i
+ 1, /* Enumerate the tabs starting from 1
1130 show_buffers(struct tab
*t
)
1132 buffers_make_list();
1133 gtk_widget_show(t
->buffers
);
1134 gtk_widget_grab_focus(GTK_WIDGET(t
->buffers
));
1138 toggle_buffers(struct tab
*t
)
1140 if (gtk_widget_get_visible(t
->buffers
))
1147 buffers(struct tab
*t
, struct karg
*args
)
1155 hide_oops(struct tab
*t
)
1157 gtk_widget_hide(t
->oops
);
1161 show_oops(struct tab
*at
, const char *fmt
, ...)
1165 struct tab
*t
= NULL
;
1171 if ((t
= get_current_tab()) == NULL
)
1177 if (vasprintf(&msg
, fmt
, ap
) == -1)
1178 errx(1, "show_oops failed");
1181 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
1182 gtk_widget_hide(t
->cmd
);
1183 gtk_widget_show(t
->oops
);
1187 get_as_string(struct settings
*s
)
1198 warnx("get_as_string skip %s\n", s
->name
);
1199 } else if (s
->type
== XT_S_INT
)
1200 r
= g_strdup_printf("%d", *s
->ival
);
1201 else if (s
->type
== XT_S_STR
)
1202 r
= g_strdup(*s
->sval
);
1203 else if (s
->type
== XT_S_FLOAT
)
1204 r
= g_strdup_printf("%f", *s
->fval
);
1206 r
= g_strdup_printf("INVALID TYPE");
1212 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1217 for (i
= 0; i
< LENGTH(rs
); i
++) {
1218 if (rs
[i
].s
&& rs
[i
].s
->walk
)
1219 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
1221 s
= get_as_string(&rs
[i
]);
1222 cb(&rs
[i
], s
, cb_args
);
1229 set_browser_mode(struct settings
*s
, char *val
)
1231 if (!strcmp(val
, "whitelist")) {
1232 browser_mode
= XT_BM_WHITELIST
;
1233 allow_volatile_cookies
= 0;
1234 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1235 cookies_enabled
= 1;
1236 enable_cookie_whitelist
= 1;
1237 read_only_cookies
= 0;
1238 save_rejected_cookies
= 0;
1239 session_timeout
= 3600;
1241 enable_js_whitelist
= 1;
1242 enable_localstorage
= 0;
1243 } else if (!strcmp(val
, "normal")) {
1244 browser_mode
= XT_BM_NORMAL
;
1245 allow_volatile_cookies
= 0;
1246 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1247 cookies_enabled
= 1;
1248 enable_cookie_whitelist
= 0;
1249 read_only_cookies
= 0;
1250 save_rejected_cookies
= 0;
1251 session_timeout
= 3600;
1253 enable_js_whitelist
= 0;
1254 enable_localstorage
= 1;
1255 } else if (!strcmp(val
, "kiosk")) {
1256 browser_mode
= XT_BM_KIOSK
;
1257 allow_volatile_cookies
= 0;
1258 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1259 cookies_enabled
= 1;
1260 enable_cookie_whitelist
= 0;
1261 read_only_cookies
= 0;
1262 save_rejected_cookies
= 0;
1263 session_timeout
= 3600;
1265 enable_js_whitelist
= 0;
1266 enable_localstorage
= 1;
1276 get_browser_mode(struct settings
*s
)
1280 if (browser_mode
== XT_BM_WHITELIST
)
1281 r
= g_strdup("whitelist");
1282 else if (browser_mode
== XT_BM_NORMAL
)
1283 r
= g_strdup("normal");
1284 else if (browser_mode
== XT_BM_KIOSK
)
1285 r
= g_strdup("kiosk");
1293 set_cookie_policy(struct settings
*s
, char *val
)
1295 if (!strcmp(val
, "no3rdparty"))
1296 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
1297 else if (!strcmp(val
, "accept"))
1298 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
1299 else if (!strcmp(val
, "reject"))
1300 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
1308 get_cookie_policy(struct settings
*s
)
1312 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
1313 r
= g_strdup("no3rdparty");
1314 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
1315 r
= g_strdup("accept");
1316 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
1317 r
= g_strdup("reject");
1325 get_default_script(struct settings
*s
)
1327 if (default_script
[0] == '\0')
1329 return (g_strdup(default_script
));
1333 set_default_script(struct settings
*s
, char *val
)
1336 snprintf(default_script
, sizeof default_script
, "%s/%s",
1337 pwd
->pw_dir
, &val
[1]);
1339 strlcpy(default_script
, val
, sizeof default_script
);
1345 get_download_dir(struct settings
*s
)
1347 if (download_dir
[0] == '\0')
1349 return (g_strdup(download_dir
));
1353 set_download_dir(struct settings
*s
, char *val
)
1356 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
1357 pwd
->pw_dir
, &val
[1]);
1359 strlcpy(download_dir
, val
, sizeof download_dir
);
1366 * We use these to prevent people putting xxxt:// URLs on
1367 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
1369 #define XT_XTP_SES_KEY_SZ 8
1370 #define XT_XTP_SES_KEY_HEX_FMT \
1371 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
1372 char *dl_session_key
; /* downloads */
1373 char *hl_session_key
; /* history list */
1374 char *cl_session_key
; /* cookie list */
1375 char *fl_session_key
; /* favorites list */
1377 char work_dir
[PATH_MAX
];
1378 char certs_dir
[PATH_MAX
];
1379 char cache_dir
[PATH_MAX
];
1380 char sessions_dir
[PATH_MAX
];
1381 char cookie_file
[PATH_MAX
];
1382 SoupURI
*proxy_uri
= NULL
;
1383 SoupSession
*session
;
1384 SoupCookieJar
*s_cookiejar
;
1385 SoupCookieJar
*p_cookiejar
;
1386 char rc_fname
[PATH_MAX
];
1388 struct mime_type_list mtl
;
1389 struct alias_list aliases
;
1392 struct tab
*create_new_tab(char *, struct undo
*, int, int);
1393 void delete_tab(struct tab
*);
1394 void setzoom_webkit(struct tab
*, int);
1395 int run_script(struct tab
*, char *);
1396 int download_rb_cmp(struct download
*, struct download
*);
1397 gboolean
cmd_execute(struct tab
*t
, char *str
);
1400 history_rb_cmp(struct history
*h1
, struct history
*h2
)
1402 return (strcmp(h1
->uri
, h2
->uri
));
1404 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
1407 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
1409 return (strcmp(d1
->d
, d2
->d
));
1411 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1414 get_work_dir(struct settings
*s
)
1416 if (work_dir
[0] == '\0')
1418 return (g_strdup(work_dir
));
1422 set_work_dir(struct settings
*s
, char *val
)
1425 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1426 pwd
->pw_dir
, &val
[1]);
1428 strlcpy(work_dir
, val
, sizeof work_dir
);
1434 get_tab_style(struct settings
*s
)
1436 if (tab_style
== XT_TABS_NORMAL
)
1437 return (g_strdup("normal"));
1439 return (g_strdup("compact"));
1443 set_tab_style(struct settings
*s
, char *val
)
1445 if (!strcmp(val
, "normal"))
1446 tab_style
= XT_TABS_NORMAL
;
1447 else if (!strcmp(val
, "compact"))
1448 tab_style
= XT_TABS_COMPACT
;
1456 * generate a session key to secure xtp commands.
1457 * pass in a ptr to the key in question and it will
1458 * be modified in place.
1461 generate_xtp_session_key(char **key
)
1463 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1469 /* make a new one */
1470 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1471 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1472 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1473 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1475 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1479 * validate a xtp session key.
1483 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1485 if (strcmp(trusted
, untrusted
) != 0) {
1486 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1495 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1497 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1499 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1501 struct valid_url_types
{
1512 valid_url_type(char *url
)
1516 for (i
= 0; i
< LENGTH(vut
); i
++)
1517 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1524 print_cookie(char *msg
, SoupCookie
*c
)
1530 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1531 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1532 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1533 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1534 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1535 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1536 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1537 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1538 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1539 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1543 walk_alias(struct settings
*s
,
1544 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1549 if (s
== NULL
|| cb
== NULL
) {
1550 show_oops(NULL
, "walk_alias invalid parameters");
1554 TAILQ_FOREACH(a
, &aliases
, entry
) {
1555 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1556 cb(s
, str
, cb_args
);
1562 match_alias(char *url_in
)
1566 char *url_out
= NULL
, *search
, *enc_arg
;
1568 search
= g_strdup(url_in
);
1570 if (strsep(&arg
, " \t") == NULL
) {
1571 show_oops(NULL
, "match_alias: NULL URL");
1575 TAILQ_FOREACH(a
, &aliases
, entry
) {
1576 if (!strcmp(search
, a
->a_name
))
1581 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1584 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1585 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1588 url_out
= g_strdup_printf(a
->a_uri
, "");
1596 guess_url_type(char *url_in
)
1599 char *url_out
= NULL
, *enc_search
= NULL
;
1601 url_out
= match_alias(url_in
);
1602 if (url_out
!= NULL
)
1607 * If there is no dot nor slash in the string and it isn't a
1608 * path to a local file and doesn't resolves to an IP, assume
1609 * that the user wants to search for the string.
1612 if (strchr(url_in
, '.') == NULL
&&
1613 strchr(url_in
, '/') == NULL
&&
1614 stat(url_in
, &sb
) != 0 &&
1615 gethostbyname(url_in
) == NULL
) {
1617 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1618 url_out
= g_strdup_printf(search_string
, enc_search
);
1624 /* XXX not sure about this heuristic */
1625 if (stat(url_in
, &sb
) == 0)
1626 url_out
= g_strdup_printf("file://%s", url_in
);
1628 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1630 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1636 load_uri(struct tab
*t
, gchar
*uri
)
1639 gchar
*newuri
= NULL
;
1645 /* Strip leading spaces. */
1646 while (*uri
&& isspace(*uri
))
1649 if (strlen(uri
) == 0) {
1654 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
1656 if (!strncmp(uri
, XT_URI_ABOUT
, XT_URI_ABOUT_LEN
)) {
1657 for (i
= 0; i
< LENGTH(about_list
); i
++)
1658 if (!strcmp(&uri
[XT_URI_ABOUT_LEN
], about_list
[i
].name
)) {
1659 bzero(&args
, sizeof args
);
1660 about_list
[i
].func(t
, &args
);
1661 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
),
1665 show_oops(t
, "invalid about page");
1669 if (valid_url_type(uri
)) {
1670 newuri
= guess_url_type(uri
);
1674 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1676 webkit_web_view_load_uri(t
->wv
, uri
);
1683 get_uri(struct tab
*t
)
1685 const gchar
*uri
= NULL
;
1687 if (webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
)
1689 if (t
->xtp_meaning
== XT_XTP_TAB_MEANING_NORMAL
) {
1690 uri
= webkit_web_view_get_uri(t
->wv
);
1692 /* use tmp_uri to make sure it is g_freed */
1695 t
->tmp_uri
=g_strdup_printf("%s%s", XT_URI_ABOUT
,
1696 about_list
[t
->xtp_meaning
].name
);
1703 get_title(struct tab
*t
, bool window
)
1705 const gchar
*set
= NULL
, *title
= NULL
;
1706 WebKitLoadStatus status
= webkit_web_view_get_load_status(t
->wv
);
1708 if (status
== WEBKIT_LOAD_PROVISIONAL
|| status
== WEBKIT_LOAD_FAILED
||
1709 t
->xtp_meaning
== XT_XTP_TAB_MEANING_BL
)
1712 title
= webkit_web_view_get_title(t
->wv
);
1713 if ((set
= title
? title
: get_uri(t
)))
1717 set
= window
? XT_NAME
: "(untitled)";
1723 add_alias(struct settings
*s
, char *line
)
1726 struct alias
*a
= NULL
;
1728 if (s
== NULL
|| line
== NULL
) {
1729 show_oops(NULL
, "add_alias invalid parameters");
1734 a
= g_malloc(sizeof(*a
));
1736 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1737 show_oops(NULL
, "add_alias: incomplete alias definition");
1740 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1741 show_oops(NULL
, "add_alias: invalid alias definition");
1745 a
->a_name
= g_strdup(alias
);
1746 a
->a_uri
= g_strdup(l
);
1748 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1750 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1760 add_mime_type(struct settings
*s
, char *line
)
1764 struct mime_type
*m
= NULL
;
1765 int downloadfirst
= 0;
1767 /* XXX this could be smarter */
1769 if (line
== NULL
|| strlen(line
) == 0) {
1770 show_oops(NULL
, "add_mime_type invalid parameters");
1779 m
= g_malloc(sizeof(*m
));
1781 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1782 show_oops(NULL
, "add_mime_type: invalid mime_type");
1785 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1786 mime_type
[strlen(mime_type
) - 1] = '\0';
1791 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1792 show_oops(NULL
, "add_mime_type: invalid mime_type");
1796 m
->mt_type
= g_strdup(mime_type
);
1797 m
->mt_action
= g_strdup(l
);
1798 m
->mt_download
= downloadfirst
;
1800 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1801 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1803 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1813 find_mime_type(char *mime_type
)
1815 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1817 TAILQ_FOREACH(m
, &mtl
, entry
) {
1818 if (m
->mt_default
&&
1819 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1822 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1835 walk_mime_type(struct settings
*s
,
1836 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1838 struct mime_type
*m
;
1841 if (s
== NULL
|| cb
== NULL
) {
1842 show_oops(NULL
, "walk_mime_type invalid parameters");
1846 TAILQ_FOREACH(m
, &mtl
, entry
) {
1847 str
= g_strdup_printf("%s%s --> %s",
1849 m
->mt_default
? "*" : "",
1851 cb(s
, str
, cb_args
);
1857 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1862 if (str
== NULL
|| wl
== NULL
|| strlen(str
) < 2)
1865 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1867 /* treat *.moo.com the same as .moo.com */
1868 if (str
[0] == '*' && str
[1] == '.')
1870 else if (str
[0] == '.')
1875 d
= g_malloc(sizeof *d
);
1877 d
->d
= g_strdup_printf(".%s", str
);
1879 d
->d
= g_strdup(str
);
1882 if (RB_INSERT(domain_list
, wl
, d
))
1885 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1896 add_cookie_wl(struct settings
*s
, char *entry
)
1898 wl_add(entry
, &c_wl
, 1);
1903 walk_cookie_wl(struct settings
*s
,
1904 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1908 if (s
== NULL
|| cb
== NULL
) {
1909 show_oops(NULL
, "walk_cookie_wl invalid parameters");
1913 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1914 cb(s
, d
->d
, cb_args
);
1918 walk_js_wl(struct settings
*s
,
1919 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1923 if (s
== NULL
|| cb
== NULL
) {
1924 show_oops(NULL
, "walk_js_wl invalid parameters");
1928 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1929 cb(s
, d
->d
, cb_args
);
1933 add_js_wl(struct settings
*s
, char *entry
)
1935 wl_add(entry
, &js_wl
, 1 /* persistent */);
1940 wl_find(const gchar
*search
, struct domain_list
*wl
)
1943 struct domain
*d
= NULL
, dfind
;
1946 if (search
== NULL
|| wl
== NULL
)
1948 if (strlen(search
) < 2)
1951 if (search
[0] != '.')
1952 s
= g_strdup_printf(".%s", search
);
1954 s
= g_strdup(search
);
1956 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1959 d
= RB_FIND(domain_list
, wl
, &dfind
);
1973 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1979 if (s
== NULL
|| wl
== NULL
)
1982 if (!strncmp(s
, "http://", strlen("http://")))
1983 s
= &s
[strlen("http://")];
1984 else if (!strncmp(s
, "https://", strlen("https://")))
1985 s
= &s
[strlen("https://")];
1990 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1991 /* chop string at first slash */
1992 if (s
[i
] == '/' || s
[i
] == '\0') {
1995 r
= wl_find(ss
, wl
);
2004 settings_add(char *var
, char *val
)
2011 for (i
= 0, rv
= 0; i
< LENGTH(rs
); i
++) {
2012 if (strcmp(var
, rs
[i
].name
))
2016 if (rs
[i
].s
->set(&rs
[i
], val
))
2017 errx(1, "invalid value for %s: %s", var
, val
);
2021 switch (rs
[i
].type
) {
2030 errx(1, "invalid sval for %s",
2044 errx(1, "invalid type for %s", var
);
2053 config_parse(char *filename
, int runtime
)
2056 char *line
, *cp
, *var
, *val
;
2057 size_t len
, lineno
= 0;
2059 char file
[PATH_MAX
];
2062 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
2064 if (filename
== NULL
)
2067 if (runtime
&& runtime_settings
[0] != '\0') {
2068 snprintf(file
, sizeof file
, "%s/%s",
2069 work_dir
, runtime_settings
);
2070 if (stat(file
, &sb
)) {
2071 warnx("runtime file doesn't exist, creating it");
2072 if ((f
= fopen(file
, "w")) == NULL
)
2074 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
2078 strlcpy(file
, filename
, sizeof file
);
2080 if ((config
= fopen(file
, "r")) == NULL
) {
2081 warn("config_parse: cannot open %s", filename
);
2086 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
2087 if (feof(config
) || ferror(config
))
2091 cp
+= (long)strspn(cp
, WS
);
2092 if (cp
[0] == '\0') {
2098 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
2099 errx(1, "invalid config file entry: %s", line
);
2101 cp
+= (long)strspn(cp
, WS
);
2103 if ((val
= strsep(&cp
, "\0")) == NULL
)
2106 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n", var
, val
);
2107 handled
= settings_add(var
, val
);
2109 errx(1, "invalid conf file entry: %s=%s", var
, val
);
2118 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
2124 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
2128 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
2131 JSStringGetUTF8CString(jsref
, s
, l
);
2132 JSStringRelease(jsref
);
2138 disable_hints(struct tab
*t
)
2140 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2141 bzero(t
->hint_num
, sizeof t
->hint_num
);
2142 run_script(t
, "vimprobable_clear()");
2144 t
->hint_mode
= XT_HINT_NONE
;
2148 enable_hints(struct tab
*t
)
2150 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
2151 run_script(t
, "vimprobable_show_hints()");
2153 t
->hint_mode
= XT_HINT_NONE
;
2156 #define XT_JS_OPEN ("open;")
2157 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
2158 #define XT_JS_FIRE ("fire;")
2159 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
2160 #define XT_JS_FOUND ("found;")
2161 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
2164 run_script(struct tab
*t
, char *s
)
2166 JSGlobalContextRef ctx
;
2167 WebKitWebFrame
*frame
;
2169 JSValueRef val
, exception
;
2172 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
2173 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
2175 frame
= webkit_web_view_get_main_frame(t
->wv
);
2176 ctx
= webkit_web_frame_get_global_context(frame
);
2178 str
= JSStringCreateWithUTF8CString(s
);
2179 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
2180 NULL
, 0, &exception
);
2181 JSStringRelease(str
);
2183 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
2185 es
= js_ref_to_string(ctx
, exception
);
2186 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
2190 es
= js_ref_to_string(ctx
, val
);
2191 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
2193 /* handle return value right here */
2194 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
2197 load_uri(t
, &es
[XT_JS_OPEN_LEN
]);
2200 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
2201 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
2202 &es
[XT_JS_FIRE_LEN
]);
2207 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
2208 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
2219 hint(struct tab
*t
, struct karg
*args
)
2222 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
2224 if (t
->hints_on
== 0)
2233 apply_style(struct tab
*t
)
2235 g_object_set(G_OBJECT(t
->settings
),
2236 "user-stylesheet-uri", t
->stylesheet
, (char *)NULL
);
2240 userstyle(struct tab
*t
, struct karg
*args
)
2242 DNPRINTF(XT_D_JS
, "userstyle: tab %d\n", t
->tab_id
);
2246 g_object_set(G_OBJECT(t
->settings
),
2247 "user-stylesheet-uri", NULL
, (char *)NULL
);
2256 * Doesn't work fully, due to the following bug:
2257 * https://bugs.webkit.org/show_bug.cgi?id=51747
2260 restore_global_history(void)
2262 char file
[PATH_MAX
];
2267 const char delim
[3] = {'\\', '\\', '\0'};
2269 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2271 if ((f
= fopen(file
, "r")) == NULL
) {
2272 warnx("%s: fopen", __func__
);
2277 if ((uri
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2278 if (feof(f
) || ferror(f
))
2281 if ((title
= fparseln(f
, NULL
, NULL
, delim
, 0)) == NULL
)
2282 if (feof(f
) || ferror(f
)) {
2284 warnx("%s: broken history file\n", __func__
);
2288 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
2289 webkit_web_history_item_new_with_data(uri
, title
);
2290 h
= g_malloc(sizeof(struct history
));
2291 h
->uri
= g_strdup(uri
);
2292 h
->title
= g_strdup(title
);
2293 RB_INSERT(history_list
, &hl
, h
);
2294 completion_add_uri(h
->uri
);
2296 warnx("%s: failed to restore history\n", __func__
);
2312 save_global_history_to_disk(struct tab
*t
)
2314 char file
[PATH_MAX
];
2318 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
2320 if ((f
= fopen(file
, "w")) == NULL
) {
2321 show_oops(t
, "%s: global history file: %s",
2322 __func__
, strerror(errno
));
2326 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
2327 if (h
->uri
&& h
->title
)
2328 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
2337 quit(struct tab
*t
, struct karg
*args
)
2339 if (save_global_history
)
2340 save_global_history_to_disk(t
);
2348 open_tabs(struct tab
*t
, struct karg
*a
)
2350 char file
[PATH_MAX
];
2354 struct tab
*ti
, *tt
;
2359 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2360 if ((f
= fopen(file
, "r")) == NULL
)
2363 ti
= TAILQ_LAST(&tabs
, tab_list
);
2366 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
2367 if (feof(f
) || ferror(f
))
2370 /* retrieve session name */
2371 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
2372 strlcpy(named_session
,
2373 &uri
[strlen(XT_SAVE_SESSION_ID
)],
2374 sizeof named_session
);
2378 if (uri
&& strlen(uri
))
2379 create_new_tab(uri
, NULL
, 1, -1);
2385 /* close open tabs */
2386 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
2388 tt
= TAILQ_FIRST(&tabs
);
2408 restore_saved_tabs(void)
2410 char file
[PATH_MAX
];
2411 int unlink_file
= 0;
2416 snprintf(file
, sizeof file
, "%s/%s",
2417 sessions_dir
, XT_RESTART_TABS_FILE
);
2418 if (stat(file
, &sb
) == -1)
2419 a
.s
= XT_SAVED_TABS_FILE
;
2422 a
.s
= XT_RESTART_TABS_FILE
;
2425 a
.i
= XT_SES_DONOTHING
;
2426 rv
= open_tabs(NULL
, &a
);
2435 save_tabs(struct tab
*t
, struct karg
*a
)
2437 char file
[PATH_MAX
];
2439 int num_tabs
= 0, i
;
2440 struct tab
**stabs
= NULL
;
2445 snprintf(file
, sizeof file
, "%s/%s",
2446 sessions_dir
, named_session
);
2448 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
2450 if ((f
= fopen(file
, "w")) == NULL
) {
2451 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
2455 /* save session name */
2456 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
2458 /* Save tabs, in the order they are arranged in the notebook. */
2459 num_tabs
= sort_tabs_by_page_num(&stabs
);
2461 for (i
= 0; i
< num_tabs
; i
++)
2462 if (stabs
[i
] && get_uri(stabs
[i
]) != NULL
)
2463 fprintf(f
, "%s\n", get_uri(stabs
[i
]));
2467 /* try and make sure this gets to disk NOW. XXX Backup first? */
2468 if (fflush(f
) != 0 || fsync(fileno(f
)) != 0) {
2469 show_oops(t
, "May not have managed to save session: %s",
2479 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
2491 run_page_script(struct tab
*t
, struct karg
*args
)
2494 char *tmp
, script
[PATH_MAX
];
2496 tmp
= args
->s
!= NULL
&& strlen(args
->s
) > 0 ? args
->s
: default_script
;
2497 if (tmp
[0] == '\0') {
2498 show_oops(t
, "no script specified");
2502 if ((uri
= get_uri(t
)) == NULL
) {
2503 show_oops(t
, "tab is empty, not running script");
2508 snprintf(script
, sizeof script
, "%s/%s",
2509 pwd
->pw_dir
, &tmp
[1]);
2511 strlcpy(script
, tmp
, sizeof script
);
2515 show_oops(t
, "can't fork to run script");
2525 execlp(script
, script
, uri
, (void *)NULL
);
2535 yank_uri(struct tab
*t
, struct karg
*args
)
2538 GtkClipboard
*clipboard
;
2540 if ((uri
= get_uri(t
)) == NULL
)
2543 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2544 gtk_clipboard_set_text(clipboard
, uri
, -1);
2550 paste_uri(struct tab
*t
, struct karg
*args
)
2552 GtkClipboard
*clipboard
;
2553 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
2555 gchar
*p
= NULL
, *uri
;
2557 /* try primary clipboard first */
2558 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2559 p
= gtk_clipboard_wait_for_text(clipboard
);
2561 /* if it failed get whatever text is in cut_buffer0 */
2563 if (gdk_property_get(gdk_get_default_root_window(),
2565 gdk_atom_intern("STRING", FALSE
),
2567 1024 * 1024 /* picked out of my butt */,
2573 /* yes sir, we need to NUL the string */
2579 while (*uri
&& isspace(*uri
))
2581 if (strlen(uri
) == 0) {
2582 show_oops(t
, "empty paste buffer");
2585 if (guess_search
== 0 && valid_url_type(uri
)) {
2586 /* we can be clever and paste this in search box */
2587 show_oops(t
, "not a valid URL");
2591 if (args
->i
== XT_PASTE_CURRENT_TAB
)
2593 else if (args
->i
== XT_PASTE_NEW_TAB
)
2594 create_new_tab(uri
, NULL
, 1, -1);
2605 find_domain(const gchar
*s
, int toplevel
)
2613 uri
= soup_uri_new(s
);
2615 if (uri
== NULL
|| !SOUP_URI_VALID_FOR_HTTP(uri
)) {
2619 if (toplevel
&& !isdigit(uri
->host
[strlen(uri
->host
) - 1])) {
2620 if ((p
= strrchr(uri
->host
, '.')) != NULL
) {
2621 while(--p
>= uri
->host
&& *p
!= '.');
2628 if (uri
->port
== 80)
2629 ret
= g_strdup_printf(".%s", p
);
2631 ret
= g_strdup_printf(".%s:%d", p
, uri
->port
);
2639 toggle_cwl(struct tab
*t
, struct karg
*args
)
2650 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2652 if (uri
== NULL
|| dom
== NULL
||
2653 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2654 show_oops(t
, "Can't toggle domain in cookie white list");
2657 d
= wl_find(dom
, &c_wl
);
2664 if (args
->i
& XT_WL_TOGGLE
)
2666 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2668 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2672 /* enable cookies for domain */
2673 wl_add(dom
, &c_wl
, 0);
2675 /* disable cookies for domain */
2676 RB_REMOVE(domain_list
, &c_wl
, d
);
2678 if (args
->i
& XT_WL_RELOAD
)
2679 webkit_web_view_reload(t
->wv
);
2687 toggle_js(struct tab
*t
, struct karg
*args
)
2697 g_object_get(G_OBJECT(t
->settings
),
2698 "enable-scripts", &es
, (char *)NULL
);
2699 if (args
->i
& XT_WL_TOGGLE
)
2701 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2703 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2709 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
2711 if (uri
== NULL
|| dom
== NULL
||
2712 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
2713 show_oops(t
, "Can't toggle domain in JavaScript white list");
2718 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2719 wl_add(dom
, &js_wl
, 0 /* session */);
2721 d
= wl_find(dom
, &js_wl
);
2723 RB_REMOVE(domain_list
, &js_wl
, d
);
2724 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2726 g_object_set(G_OBJECT(t
->settings
),
2727 "enable-scripts", es
, (char *)NULL
);
2728 g_object_set(G_OBJECT(t
->settings
),
2729 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
2730 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2732 if (args
->i
& XT_WL_RELOAD
)
2733 webkit_web_view_reload(t
->wv
);
2741 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2745 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
;
2748 a
.i
= XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
;
2753 toggle_src(struct tab
*t
, struct karg
*args
)
2760 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2761 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2762 webkit_web_view_reload(t
->wv
);
2768 focus_webview(struct tab
*t
)
2773 /* only grab focus if we are visible */
2774 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2775 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2779 focus(struct tab
*t
, struct karg
*args
)
2781 if (t
== NULL
|| args
== NULL
)
2787 if (args
->i
== XT_FOCUS_URI
)
2788 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2789 else if (args
->i
== XT_FOCUS_SEARCH
)
2790 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2796 stats(struct tab
*t
, struct karg
*args
)
2798 char *page
, *body
, *s
, line
[64 * 1024];
2799 uint64_t line_count
= 0;
2803 show_oops(NULL
, "stats invalid parameters");
2806 if (save_rejected_cookies
) {
2807 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2809 s
= fgets(line
, sizeof line
, r_cookie_f
);
2810 if (s
== NULL
|| feof(r_cookie_f
) ||
2816 snprintf(line
, sizeof line
,
2817 "<br/>Cookies blocked(*) total: %llu", line_count
);
2819 show_oops(t
, "Can't open blocked cookies file: %s",
2823 body
= g_strdup_printf(
2824 "Cookies blocked(*) this session: %llu"
2826 "<p><small><b>*</b> results vary based on settings</small></p>",
2830 page
= get_html_page("Statistics", body
, "", 0);
2833 load_webkit_string(t
, page
, XT_URI_ABOUT_STATS
);
2840 marco(struct tab
*t
, struct karg
*args
)
2842 char *page
, line
[64 * 1024];
2846 show_oops(NULL
, "marco invalid parameters");
2849 snprintf(line
, sizeof line
, "%s", marco_message(&len
));
2851 page
= get_html_page("Marco Sez...", line
, "", 0);
2853 load_webkit_string(t
, page
, XT_URI_ABOUT_MARCO
);
2860 blank(struct tab
*t
, struct karg
*args
)
2863 show_oops(NULL
, "blank invalid parameters");
2865 load_webkit_string(t
, "", XT_URI_ABOUT_BLANK
);
2870 about(struct tab
*t
, struct karg
*args
)
2875 show_oops(NULL
, "about invalid parameters");
2877 body
= g_strdup_printf("<b>Version: %s</b><p>"
2880 "<li>Marco Peereboom <marco@peereboom.us></li>"
2881 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2882 "<li>Edd Barrett <vext01@gmail.com> </li>"
2883 "<li>Todd T. Fries <todd@fries.net> </li>"
2884 "<li>Raphael Graf <r@undefined.ch> </li>"
2886 "Copyrights and licenses can be found on the XXXTerm "
2887 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>",
2891 page
= get_html_page("About", body
, "", 0);
2894 load_webkit_string(t
, page
, XT_URI_ABOUT_ABOUT
);
2901 help(struct tab
*t
, struct karg
*args
)
2903 char *page
, *head
, *body
;
2906 show_oops(NULL
, "help invalid parameters");
2908 head
= "<meta http-equiv=\"REFRESH\" content=\"0;"
2909 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2911 body
= "XXXTerm man page <a href=\"http://opensource.conformal.com/"
2912 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2913 "cgi-bin/man-cgi?xxxterm</a>";
2915 page
= get_html_page(XT_NAME
, body
, head
, FALSE
);
2917 load_webkit_string(t
, page
, XT_URI_ABOUT_HELP
);
2924 * update all favorite tabs apart from one. Pass NULL if
2925 * you want to update all.
2928 update_favorite_tabs(struct tab
*apart_from
)
2931 if (!updating_fl_tabs
) {
2932 updating_fl_tabs
= 1; /* stop infinite recursion */
2933 TAILQ_FOREACH(t
, &tabs
, entry
)
2934 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2935 && (t
!= apart_from
))
2936 xtp_page_fl(t
, NULL
);
2937 updating_fl_tabs
= 0;
2941 /* show a list of favorites (bookmarks) */
2943 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2945 char file
[PATH_MAX
];
2947 char *uri
= NULL
, *title
= NULL
;
2948 size_t len
, lineno
= 0;
2950 char *body
, *tmp
, *page
= NULL
;
2951 const char delim
[3] = {'\\', '\\', '\0'};
2953 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2956 warn("%s: bad param", __func__
);
2958 /* new session key */
2959 if (!updating_fl_tabs
)
2960 generate_xtp_session_key(&fl_session_key
);
2962 /* open favorites */
2963 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2964 if ((f
= fopen(file
, "r")) == NULL
) {
2965 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2970 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
2971 "<th style='width: 40px'>#</th><th>Link</th>"
2972 "<th style='width: 40px'>Rm</th></tr>\n");
2975 if ((title
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2976 if (feof(f
) || ferror(f
))
2978 if (strlen(title
) == 0 || title
[0] == '#') {
2984 if ((uri
= fparseln(f
, &len
, &lineno
, delim
, 0)) == NULL
)
2985 if (feof(f
) || ferror(f
)) {
2986 show_oops(t
, "favorites file corrupt");
2992 body
= g_strdup_printf("%s<tr>"
2994 "<td><a href='%s'>%s</a></td>"
2995 "<td style='text-align: center'>"
2996 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2998 body
, i
, uri
, title
,
2999 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
3011 /* if none, say so */
3014 body
= g_strdup_printf("%s<tr>"
3015 "<td colspan='3' style='text-align: center'>"
3016 "No favorites - To add one use the 'favadd' command."
3017 "</td></tr>", body
);
3022 body
= g_strdup_printf("%s</table>", body
);
3032 page
= get_html_page("Favorites", body
, "", 1);
3033 load_webkit_string(t
, page
, XT_URI_ABOUT_FAVORITES
);
3037 update_favorite_tabs(t
);
3046 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3047 size_t cert_count
, char *title
)
3049 gnutls_datum_t cinfo
;
3053 body
= g_strdup("");
3055 for (i
= 0; i
< cert_count
; i
++) {
3056 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
3061 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
3062 body
, i
, cinfo
.data
);
3063 gnutls_free(cinfo
.data
);
3067 tmp
= get_html_page(title
, body
, "", 0);
3070 load_webkit_string(t
, tmp
, XT_URI_ABOUT_CERTS
);
3075 ca_cmd(struct tab
*t
, struct karg
*args
)
3078 int rv
= 1, certs
= 0, certs_read
;
3081 gnutls_x509_crt_t
*c
= NULL
;
3082 char *certs_buf
= NULL
, *s
;
3084 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
3085 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3089 if (fstat(fileno(f
), &sb
) == -1) {
3090 show_oops(t
, "Can't stat CA file: %s", ssl_ca_file
);
3094 certs_buf
= g_malloc(sb
.st_size
+ 1);
3095 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
3096 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
3099 certs_buf
[sb
.st_size
] = '\0';
3102 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
3104 s
+= strlen("BEGIN CERTIFICATE");
3107 bzero(&dt
, sizeof dt
);
3108 dt
.data
= (unsigned char *)certs_buf
;
3109 dt
.size
= sb
.st_size
;
3110 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
3111 certs_read
= gnutls_x509_crt_list_import(c
, (unsigned int *)&certs
, &dt
,
3112 GNUTLS_X509_FMT_PEM
, 0);
3113 if (certs_read
<= 0) {
3114 show_oops(t
, "No cert(s) available");
3117 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
3130 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
3133 struct addrinfo hints
, *res
= NULL
, *ai
;
3137 if (uri
&& !g_str_has_prefix(uri
, "https://"))
3140 su
= soup_uri_new(uri
);
3143 if (!SOUP_URI_VALID_FOR_HTTP(su
))
3146 snprintf(port
, sizeof port
, "%d", su
->port
);
3147 bzero(&hints
, sizeof(struct addrinfo
));
3148 hints
.ai_flags
= AI_CANONNAME
;
3149 hints
.ai_family
= AF_UNSPEC
;
3150 hints
.ai_socktype
= SOCK_STREAM
;
3152 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
3155 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
3156 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
3159 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
3162 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
3166 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
3171 strlcpy(domain
, su
->host
, domain_sz
);
3182 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
3185 gnutls_deinit(gsession
);
3187 gnutls_certificate_free_credentials(xcred
);
3193 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
3194 gnutls_certificate_credentials_t
*xc
)
3196 gnutls_certificate_credentials_t xcred
;
3197 gnutls_session_t gsession
;
3200 if (gs
== NULL
|| xc
== NULL
)
3206 gnutls_certificate_allocate_credentials(&xcred
);
3207 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
3208 GNUTLS_X509_FMT_PEM
);
3209 gnutls_init(&gsession
, GNUTLS_CLIENT
);
3210 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
3211 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
3212 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
3213 if ((rv
= gnutls_handshake(gsession
)) < 0) {
3214 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
3216 gnutls_error_is_fatal(rv
),
3217 gnutls_strerror_name(rv
));
3218 stop_tls(gsession
, xcred
);
3222 gnutls_credentials_type_t cred
;
3223 cred
= gnutls_auth_get_type(gsession
);
3224 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
3225 stop_tls(gsession
, xcred
);
3237 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
3241 const gnutls_datum_t
*cl
;
3242 gnutls_x509_crt_t
*all_certs
;
3245 if (certs
== NULL
|| cert_count
== NULL
)
3247 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
3249 cl
= gnutls_certificate_get_peers(gsession
, &len
);
3253 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
3254 for (i
= 0; i
< len
; i
++) {
3255 gnutls_x509_crt_init(&all_certs
[i
]);
3256 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
3257 GNUTLS_X509_FMT_PEM
< 0)) {
3271 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
3275 for (i
= 0; i
< cert_count
; i
++)
3276 gnutls_x509_crt_deinit(certs
[i
]);
3281 statusbar_modify_attr(struct tab
*t
, const char *text
, const char *base
)
3283 GdkColor c_text
, c_base
;
3285 gdk_color_parse(text
, &c_text
);
3286 gdk_color_parse(base
, &c_base
);
3288 gtk_widget_modify_text(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_text
);
3289 gtk_widget_modify_text(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_text
);
3290 gtk_widget_modify_text(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_text
);
3291 gtk_widget_modify_text(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_text
);
3293 gtk_widget_modify_base(t
->sbe
.statusbar
, GTK_STATE_NORMAL
, &c_base
);
3294 gtk_widget_modify_base(t
->sbe
.buffercmd
, GTK_STATE_NORMAL
, &c_base
);
3295 gtk_widget_modify_base(t
->sbe
.zoom
, GTK_STATE_NORMAL
, &c_base
);
3296 gtk_widget_modify_base(t
->sbe
.position
, GTK_STATE_NORMAL
, &c_base
);
3300 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
3301 size_t cert_count
, char *domain
)
3304 char cert_buf
[64 * 1024], file
[PATH_MAX
];
3309 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
3312 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3313 if ((f
= fopen(file
, "w")) == NULL
) {
3314 show_oops(t
, "Can't create cert file %s %s",
3315 file
, strerror(errno
));
3319 for (i
= 0; i
< cert_count
; i
++) {
3320 cert_buf_sz
= sizeof cert_buf
;
3321 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3322 cert_buf
, &cert_buf_sz
)) {
3323 show_oops(t
, "gnutls_x509_crt_export failed");
3326 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3327 show_oops(t
, "Can't write certs: %s", strerror(errno
));
3332 /* not the best spot but oh well */
3333 gdk_color_parse("lightblue", &color
);
3334 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
3335 statusbar_modify_attr(t
, XT_COLOR_BLACK
, "lightblue");
3341 load_compare_cert(struct tab
*t
, struct karg
*args
)
3344 char domain
[8182], file
[PATH_MAX
];
3345 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
3346 int s
= -1, rv
= 1, i
;
3350 gnutls_session_t gsession
;
3351 gnutls_x509_crt_t
*certs
;
3352 gnutls_certificate_credentials_t xcred
;
3357 if ((uri
= get_uri(t
)) == NULL
)
3360 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
3364 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3365 show_oops(t
, "Start TLS failed");
3370 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3371 show_oops(t
, "Can't get connection certificates");
3375 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
3376 if ((f
= fopen(file
, "r")) == NULL
)
3379 for (i
= 0; i
< cert_count
; i
++) {
3380 cert_buf_sz
= sizeof cert_buf
;
3381 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
3382 cert_buf
, &cert_buf_sz
)) {
3385 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
3386 rv
= -1; /* critical */
3389 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
3390 rv
= -1; /* critical */
3399 free_connection_certs(certs
, cert_count
);
3401 /* we close the socket first for speed */
3404 stop_tls(gsession
, xcred
);
3410 cert_cmd(struct tab
*t
, struct karg
*args
)
3416 gnutls_session_t gsession
;
3417 gnutls_x509_crt_t
*certs
;
3418 gnutls_certificate_credentials_t xcred
;
3423 if (ssl_ca_file
== NULL
) {
3424 show_oops(t
, "Can't open CA file: %s", ssl_ca_file
);
3428 if ((uri
= get_uri(t
)) == NULL
) {
3429 show_oops(t
, "Invalid URI");
3433 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
3434 show_oops(t
, "Invalid certificate URI: %s", uri
);
3439 if (start_tls(t
, s
, &gsession
, &xcred
)) {
3440 show_oops(t
, "Start TLS failed");
3445 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
3446 show_oops(t
, "get_connection_certs failed");
3450 if (args
->i
& XT_SHOW
)
3451 show_certs(t
, certs
, cert_count
, "Certificate Chain");
3452 else if (args
->i
& XT_SAVE
)
3453 save_certs(t
, certs
, cert_count
, domain
);
3455 free_connection_certs(certs
, cert_count
);
3457 /* we close the socket first for speed */
3460 stop_tls(gsession
, xcred
);
3466 remove_cookie(int index
)
3472 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
3474 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3476 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
3480 print_cookie("remove cookie", c
);
3481 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
3486 soup_cookies_free(cf
);
3492 wl_show(struct tab
*t
, struct karg
*args
, char *title
, struct domain_list
*wl
)
3497 body
= g_strdup("");
3500 if (args
->i
& XT_WL_PERSISTENT
) {
3502 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
3504 RB_FOREACH(d
, domain_list
, wl
) {
3508 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3514 if (args
->i
& XT_WL_SESSION
) {
3516 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
3518 RB_FOREACH(d
, domain_list
, wl
) {
3522 body
= g_strdup_printf("%s%s<br/>", body
, d
->d
);
3527 tmp
= get_html_page(title
, body
, "", 0);
3530 load_webkit_string(t
, tmp
, XT_URI_ABOUT_JSWL
);
3532 load_webkit_string(t
, tmp
, XT_URI_ABOUT_COOKIEWL
);
3538 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3540 char file
[PATH_MAX
];
3542 char *line
= NULL
, *lt
= NULL
, *dom
= NULL
;
3550 if (t
== NULL
|| args
== NULL
)
3553 if (runtime_settings
[0] == '\0')
3556 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3557 if ((f
= fopen(file
, "r+")) == NULL
)
3561 dom
= find_domain(uri
, args
->i
& XT_WL_TOPLEVEL
);
3562 if (uri
== NULL
|| dom
== NULL
||
3563 webkit_web_view_get_load_status(t
->wv
) == WEBKIT_LOAD_FAILED
) {
3564 show_oops(t
, "Can't add domain to %s white list",
3565 js
? "JavaScript" : "cookie");
3569 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom
);
3572 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3575 if (!strcmp(line
, lt
))
3581 fprintf(f
, "%s\n", lt
);
3586 d
= wl_find(dom
, &js_wl
);
3588 settings_add("js_wl", dom
);
3589 d
= wl_find(dom
, &js_wl
);
3593 d
= wl_find(dom
, &c_wl
);
3595 settings_add("cookie_wl", dom
);
3596 d
= wl_find(dom
, &c_wl
);
3600 /* find and add to persistent jar */
3601 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3602 for (;cf
; cf
= cf
->next
) {
3604 if (!strcmp(dom
, ci
->domain
) ||
3605 !strcmp(&dom
[1], ci
->domain
)) /* deal with leading . */ {
3606 c
= soup_cookie_copy(ci
);
3607 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3610 soup_cookies_free(cf
);
3628 js_show_wl(struct tab
*t
, struct karg
*args
)
3630 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3631 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3637 cookie_show_wl(struct tab
*t
, struct karg
*args
)
3639 args
->i
= XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
;
3640 wl_show(t
, args
, "Cookie White List", &c_wl
);
3646 cookie_cmd(struct tab
*t
, struct karg
*args
)
3648 if (args
->i
& XT_SHOW
)
3649 wl_show(t
, args
, "Cookie White List", &c_wl
);
3650 else if (args
->i
& XT_WL_TOGGLE
) {
3651 args
->i
|= XT_WL_RELOAD
;
3652 toggle_cwl(t
, args
);
3653 } else if (args
->i
& XT_SAVE
) {
3654 args
->i
|= XT_WL_RELOAD
;
3655 wl_save(t
, args
, 0);
3656 } else if (args
->i
& XT_DELETE
)
3657 show_oops(t
, "'cookie delete' currently unimplemented");
3663 js_cmd(struct tab
*t
, struct karg
*args
)
3665 if (args
->i
& XT_SHOW
)
3666 wl_show(t
, args
, "JavaScript White List", &js_wl
);
3667 else if (args
->i
& XT_SAVE
) {
3668 args
->i
|= XT_WL_RELOAD
;
3669 wl_save(t
, args
, 1);
3670 } else if (args
->i
& XT_WL_TOGGLE
) {
3671 args
->i
|= XT_WL_RELOAD
;
3673 } else if (args
->i
& XT_DELETE
)
3674 show_oops(t
, "'js delete' currently unimplemented");
3680 toplevel_cmd(struct tab
*t
, struct karg
*args
)
3682 js_toggle_cb(t
->js_toggle
, t
);
3688 add_favorite(struct tab
*t
, struct karg
*args
)
3690 char file
[PATH_MAX
];
3693 size_t urilen
, linelen
;
3694 const gchar
*uri
, *title
;
3699 /* don't allow adding of xtp pages to favorites */
3700 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3701 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3705 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3706 if ((f
= fopen(file
, "r+")) == NULL
) {
3707 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3711 title
= get_title(t
, FALSE
);
3714 if (title
== NULL
|| uri
== NULL
) {
3715 show_oops(t
, "can't add page to favorites");
3719 urilen
= strlen(uri
);
3722 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3723 if (feof(f
) || ferror(f
))
3726 if (linelen
== urilen
&& !strcmp(line
, uri
))
3733 fprintf(f
, "\n%s\n%s", title
, uri
);
3739 update_favorite_tabs(NULL
);
3745 navaction(struct tab
*t
, struct karg
*args
)
3747 WebKitWebHistoryItem
*item
;
3749 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3750 t
->tab_id
, args
->i
);
3752 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
3755 if (args
->i
== XT_NAV_BACK
)
3756 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3758 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3760 return (XT_CB_PASSTHROUGH
);
3761 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3763 return (XT_CB_PASSTHROUGH
);
3769 item
= webkit_web_back_forward_list_get_back_item(t
->bfl
);
3771 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3773 case XT_NAV_FORWARD
:
3775 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3777 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3780 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3782 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
3785 return (XT_CB_PASSTHROUGH
);
3789 move(struct tab
*t
, struct karg
*args
)
3791 GtkAdjustment
*adjust
;
3792 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3797 case XT_MOVE_BOTTOM
:
3799 case XT_MOVE_PAGEDOWN
:
3800 case XT_MOVE_PAGEUP
:
3801 case XT_MOVE_HALFDOWN
:
3802 case XT_MOVE_HALFUP
:
3803 case XT_MOVE_PERCENT
:
3804 adjust
= t
->adjust_v
;
3807 adjust
= t
->adjust_h
;
3811 pos
= gtk_adjustment_get_value(adjust
);
3812 ps
= gtk_adjustment_get_page_size(adjust
);
3813 upper
= gtk_adjustment_get_upper(adjust
);
3814 lower
= gtk_adjustment_get_lower(adjust
);
3815 si
= gtk_adjustment_get_step_increment(adjust
);
3816 pi
= gtk_adjustment_get_page_increment(adjust
);
3819 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3820 "max %f si %f pi %f\n",
3821 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3822 pos
, ps
, upper
, lower
, max
, si
, pi
);
3828 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3833 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3835 case XT_MOVE_BOTTOM
:
3836 case XT_MOVE_FARRIGHT
:
3837 gtk_adjustment_set_value(adjust
, max
);
3840 case XT_MOVE_FARLEFT
:
3841 gtk_adjustment_set_value(adjust
, lower
);
3843 case XT_MOVE_PAGEDOWN
:
3845 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3847 case XT_MOVE_PAGEUP
:
3849 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3851 case XT_MOVE_HALFDOWN
:
3853 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3855 case XT_MOVE_HALFUP
:
3857 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3859 case XT_MOVE_PERCENT
:
3863 percent
= atoi(args
->s
) / 100.0;
3864 pos
= max
* percent
;
3865 if (pos
< 0.0 || pos
> max
)
3868 gtk_adjustment_set_value(adjust
, pos
);
3872 return (XT_CB_PASSTHROUGH
);
3875 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3877 return (XT_CB_HANDLED
);
3881 url_set_visibility(void)
3885 TAILQ_FOREACH(t
, &tabs
, entry
)
3886 if (show_url
== 0) {
3887 gtk_widget_hide(t
->toolbar
);
3890 gtk_widget_show(t
->toolbar
);
3894 notebook_tab_set_visibility(void)
3896 if (show_tabs
== 0) {
3897 gtk_widget_hide(tab_bar
);
3898 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3900 if (tab_style
== XT_TABS_NORMAL
) {
3901 gtk_widget_hide(tab_bar
);
3902 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3903 } else if (tab_style
== XT_TABS_COMPACT
) {
3904 gtk_widget_show(tab_bar
);
3905 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3911 statusbar_set_visibility(void)
3915 TAILQ_FOREACH(t
, &tabs
, entry
)
3916 if (show_statusbar
== 0) {
3917 gtk_widget_hide(t
->statusbar_box
);
3920 gtk_widget_show(t
->statusbar_box
);
3924 url_set(struct tab
*t
, int enable_url_entry
)
3929 show_url
= enable_url_entry
;
3931 if (enable_url_entry
) {
3932 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
3933 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3934 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
), 0);
3936 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3937 GTK_ENTRY_ICON_PRIMARY
);
3939 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3940 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
3941 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3942 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
3948 fullscreen(struct tab
*t
, struct karg
*args
)
3950 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3953 return (XT_CB_PASSTHROUGH
);
3955 if (show_url
== 0) {
3963 url_set_visibility();
3964 notebook_tab_set_visibility();
3966 return (XT_CB_HANDLED
);
3970 statustoggle(struct tab
*t
, struct karg
*args
)
3972 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3974 if (show_statusbar
== 1) {
3976 statusbar_set_visibility();
3977 } else if (show_statusbar
== 0) {
3979 statusbar_set_visibility();
3981 return (XT_CB_HANDLED
);
3985 urlaction(struct tab
*t
, struct karg
*args
)
3987 int rv
= XT_CB_HANDLED
;
3989 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3992 return (XT_CB_PASSTHROUGH
);
3996 if (show_url
== 0) {
3998 url_set_visibility();
4002 if (show_url
== 1) {
4004 url_set_visibility();
4012 tabaction(struct tab
*t
, struct karg
*args
)
4014 int rv
= XT_CB_HANDLED
;
4015 char *url
= args
->s
;
4019 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
4022 return (XT_CB_PASSTHROUGH
);
4026 if (strlen(url
) > 0)
4027 create_new_tab(url
, NULL
, 1, args
->p
);
4029 create_new_tab(NULL
, NULL
, 1, args
->p
);
4035 TAILQ_FOREACH(tt
, &tabs
, entry
)
4036 if (tt
->tab_id
== args
->p
- 1) {
4041 case XT_TAB_DELQUIT
:
4042 if (gtk_notebook_get_n_pages(notebook
) > 1)
4048 if (strlen(url
) > 0)
4051 rv
= XT_CB_PASSTHROUGH
;
4057 if (show_tabs
== 0) {
4059 notebook_tab_set_visibility();
4063 if (show_tabs
== 1) {
4065 notebook_tab_set_visibility();
4068 case XT_TAB_NEXTSTYLE
:
4069 if (tab_style
== XT_TABS_NORMAL
) {
4070 tab_style
= XT_TABS_COMPACT
;
4071 recolor_compact_tabs();
4074 tab_style
= XT_TABS_NORMAL
;
4075 notebook_tab_set_visibility();
4077 case XT_TAB_UNDO_CLOSE
:
4078 if (undo_count
== 0) {
4079 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close",
4084 u
= TAILQ_FIRST(&undos
);
4085 create_new_tab(u
->uri
, u
, 1, -1);
4087 TAILQ_REMOVE(&undos
, u
, entry
);
4089 /* u->history is freed in create_new_tab() */
4094 rv
= XT_CB_PASSTHROUGH
;
4108 resizetab(struct tab
*t
, struct karg
*args
)
4110 if (t
== NULL
|| args
== NULL
) {
4111 show_oops(NULL
, "resizetab invalid parameters");
4112 return (XT_CB_PASSTHROUGH
);
4115 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
4116 t
->tab_id
, args
->i
);
4118 setzoom_webkit(t
, args
->i
);
4120 return (XT_CB_HANDLED
);
4124 movetab(struct tab
*t
, struct karg
*args
)
4128 if (t
== NULL
|| args
== NULL
) {
4129 show_oops(NULL
, "movetab invalid parameters");
4130 return (XT_CB_PASSTHROUGH
);
4133 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
4134 t
->tab_id
, args
->i
);
4136 if (args
->i
>= XT_TAB_INVALID
)
4137 return (XT_CB_PASSTHROUGH
);
4139 if (TAILQ_EMPTY(&tabs
))
4140 return (XT_CB_PASSTHROUGH
);
4142 n
= gtk_notebook_get_n_pages(notebook
);
4143 dest
= gtk_notebook_get_current_page(notebook
);
4148 dest
= dest
== n
- 1 ? 0 : dest
+ 1;
4157 dest
-= args
->p
% n
;
4170 return (XT_CB_PASSTHROUGH
);
4173 if (dest
< 0 || dest
>= n
)
4174 return (XT_CB_PASSTHROUGH
);
4175 if (t
->tab_id
== dest
) {
4176 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
4177 return (XT_CB_HANDLED
);
4180 set_current_tab(dest
);
4182 return (XT_CB_HANDLED
);
4188 command(struct tab
*t
, struct karg
*args
)
4190 char *s
= NULL
, *ss
= NULL
;
4194 if (t
== NULL
|| args
== NULL
) {
4195 show_oops(NULL
, "command invalid parameters");
4196 return (XT_CB_PASSTHROUGH
);
4207 if (cmd_prefix
== 0)
4210 ss
= g_strdup_printf(":%d", cmd_prefix
);
4221 case XT_CMD_OPEN_CURRENT
:
4224 case XT_CMD_TABNEW_CURRENT
:
4225 if (!s
) /* FALL THROUGH? */
4227 if ((uri
= get_uri(t
)) != NULL
) {
4228 ss
= g_strdup_printf("%s%s", s
, uri
);
4233 show_oops(t
, "command: invalid opcode %d", args
->i
);
4234 return (XT_CB_PASSTHROUGH
);
4237 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
4239 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
4240 gdk_color_parse(XT_COLOR_WHITE
, &color
);
4241 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
4243 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
4244 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
4249 return (XT_CB_HANDLED
);
4253 * Return a new string with a download row (in html)
4254 * appended. Old string is freed.
4257 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
4260 WebKitDownloadStatus stat
;
4261 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
4263 char cur_sz
[FMT_SCALED_STRSIZE
];
4264 char tot_sz
[FMT_SCALED_STRSIZE
];
4267 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
4269 /* All actions wil take this form:
4270 * xxxt://class/seskey
4272 xtp_prefix
= g_strdup_printf("%s%d/%s/",
4273 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
4275 stat
= webkit_download_get_status(dl
->download
);
4278 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4279 status_html
= g_strdup_printf("Finished");
4280 cmd_html
= g_strdup_printf(
4281 "<a href='%s%d/%d'>Remove</a>",
4282 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4284 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4285 /* gather size info */
4286 progress
= 100 * webkit_download_get_progress(dl
->download
);
4289 webkit_download_get_current_size(dl
->download
), cur_sz
);
4291 webkit_download_get_total_size(dl
->download
), tot_sz
);
4293 status_html
= g_strdup_printf(
4294 "<div style='width: 100%%' align='center'>"
4295 "<div class='progress-outer'>"
4296 "<div class='progress-inner' style='width: %.2f%%'>"
4297 "</div></div></div>"
4298 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
4299 progress
, cur_sz
, tot_sz
, progress
);
4301 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4302 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4306 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4307 status_html
= g_strdup_printf("Cancelled");
4308 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4309 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4311 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4312 status_html
= g_strdup_printf("Error!");
4313 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
4314 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
4316 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4317 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
4318 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
4319 status_html
= g_strdup_printf("Starting");
4322 show_oops(t
, "%s: unknown download status", __func__
);
4325 new_html
= g_strdup_printf(
4326 "%s\n<tr><td>%s</td><td>%s</td>"
4327 "<td style='text-align:center'>%s</td></tr>\n",
4328 html
, basename((char *)webkit_download_get_destination_uri(dl
->download
)),
4329 status_html
, cmd_html
);
4333 g_free(status_html
);
4344 * update all download tabs apart from one. Pass NULL if
4345 * you want to update all.
4348 update_download_tabs(struct tab
*apart_from
)
4351 if (!updating_dl_tabs
) {
4352 updating_dl_tabs
= 1; /* stop infinite recursion */
4353 TAILQ_FOREACH(t
, &tabs
, entry
)
4354 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
4355 && (t
!= apart_from
))
4356 xtp_page_dl(t
, NULL
);
4357 updating_dl_tabs
= 0;
4362 * update all cookie tabs apart from one. Pass NULL if
4363 * you want to update all.
4366 update_cookie_tabs(struct tab
*apart_from
)
4369 if (!updating_cl_tabs
) {
4370 updating_cl_tabs
= 1; /* stop infinite recursion */
4371 TAILQ_FOREACH(t
, &tabs
, entry
)
4372 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
4373 && (t
!= apart_from
))
4374 xtp_page_cl(t
, NULL
);
4375 updating_cl_tabs
= 0;
4380 * update all history tabs apart from one. Pass NULL if
4381 * you want to update all.
4384 update_history_tabs(struct tab
*apart_from
)
4388 if (!updating_hl_tabs
) {
4389 updating_hl_tabs
= 1; /* stop infinite recursion */
4390 TAILQ_FOREACH(t
, &tabs
, entry
)
4391 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
4392 && (t
!= apart_from
))
4393 xtp_page_hl(t
, NULL
);
4394 updating_hl_tabs
= 0;
4398 /* cookie management XTP page */
4400 xtp_page_cl(struct tab
*t
, struct karg
*args
)
4402 char *body
, *page
, *tmp
;
4403 int i
= 1; /* all ids start 1 */
4404 GSList
*sc
, *pc
, *pc_start
;
4406 char *type
, *table_headers
, *last_domain
;
4408 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4411 show_oops(NULL
, "%s invalid parameters", __func__
);
4415 /* Generate a new session key */
4416 if (!updating_cl_tabs
)
4417 generate_xtp_session_key(&cl_session_key
);
4420 table_headers
= g_strdup_printf("<table><tr>"
4423 "<th style='width:200px'>Value</th>"
4427 "<th>HTTP<br />only</th>"
4428 "<th style='width:40px'>Rm</th></tr>\n");
4430 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
4431 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
4435 last_domain
= strdup("");
4436 for (; sc
; sc
= sc
->next
) {
4439 if (strcmp(last_domain
, c
->domain
) != 0) {
4442 last_domain
= strdup(c
->domain
);
4446 body
= g_strdup_printf("%s</table>"
4448 body
, c
->domain
, table_headers
);
4452 body
= g_strdup_printf("<h2>%s</h2>%s\n",
4453 c
->domain
, table_headers
);
4458 for (pc
= pc_start
; pc
; pc
= pc
->next
)
4459 if (soup_cookie_equal(pc
->data
, c
)) {
4460 type
= "Session + Persistent";
4465 body
= g_strdup_printf(
4468 "<td style='word-wrap:normal'>%s</td>"
4470 " <textarea rows='4'>%s</textarea>"
4476 "<td style='text-align:center'>"
4477 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4484 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
4499 soup_cookies_free(sc
);
4500 soup_cookies_free(pc
);
4502 /* small message if there are none */
4504 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4505 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
4508 body
= g_strdup_printf("%s</table>", body
);
4511 page
= get_html_page("Cookie Jar", body
, "", TRUE
);
4513 g_free(table_headers
);
4514 g_free(last_domain
);
4516 load_webkit_string(t
, page
, XT_URI_ABOUT_COOKIEJAR
);
4517 update_cookie_tabs(t
);
4525 xtp_page_hl(struct tab
*t
, struct karg
*args
)
4527 char *body
, *page
, *tmp
;
4529 int i
= 1; /* all ids start 1 */
4531 DNPRINTF(XT_D_CMD
, "%s", __func__
);
4534 show_oops(NULL
, "%s invalid parameters", __func__
);
4538 /* Generate a new session key */
4539 if (!updating_hl_tabs
)
4540 generate_xtp_session_key(&hl_session_key
);
4543 body
= g_strdup_printf("<table style='table-layout:fixed'><tr>"
4544 "<th>URI</th><th>Title</th><th style='width: 40px'>Rm</th></tr>\n");
4546 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4548 body
= g_strdup_printf(
4550 "<td><a href='%s'>%s</a></td>"
4552 "<td style='text-align: center'>"
4553 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4554 body
, h
->uri
, h
->uri
, h
->title
,
4555 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4556 XT_XTP_HL_REMOVE
, i
);
4562 /* small message if there are none */
4565 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4566 "colspan='3'>No History</td></tr>\n", body
);
4571 body
= g_strdup_printf("%s</table>", body
);
4574 page
= get_html_page("History", body
, "", TRUE
);
4578 * update all history manager tabs as the xtp session
4579 * key has now changed. No need to update the current tab.
4580 * Already did that above.
4582 update_history_tabs(t
);
4584 load_webkit_string(t
, page
, XT_URI_ABOUT_HISTORY
);
4591 * Generate a web page detailing the status of any downloads
4594 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4596 struct download
*dl
;
4597 char *body
, *page
, *tmp
;
4601 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4604 show_oops(NULL
, "%s invalid parameters", __func__
);
4609 * Generate a new session key for next page instance.
4610 * This only happens for the top level call to xtp_page_dl()
4611 * in which case updating_dl_tabs is 0.
4613 if (!updating_dl_tabs
)
4614 generate_xtp_session_key(&dl_session_key
);
4616 /* header - with refresh so as to update */
4617 if (refresh_interval
>= 1)
4618 ref
= g_strdup_printf(
4619 "<meta http-equiv='refresh' content='%u"
4620 ";url=%s%d/%s/%d' />\n",
4629 body
= g_strdup_printf("<div align='center'>"
4630 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4631 "</p><table><tr><th style='width: 60%%'>"
4632 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4633 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4635 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4636 body
= xtp_page_dl_row(t
, body
, dl
);
4640 /* message if no downloads in list */
4643 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4644 " style='text-align: center'>"
4645 "No downloads</td></tr>\n", body
);
4650 body
= g_strdup_printf("%s</table></div>", body
);
4653 page
= get_html_page("Downloads", body
, ref
, 1);
4658 * update all download manager tabs as the xtp session
4659 * key has now changed. No need to update the current tab.
4660 * Already did that above.
4662 update_download_tabs(t
);
4664 load_webkit_string(t
, page
, XT_URI_ABOUT_DOWNLOADS
);
4671 search(struct tab
*t
, struct karg
*args
)
4675 if (t
== NULL
|| args
== NULL
) {
4676 show_oops(NULL
, "search invalid parameters");
4679 if (t
->search_text
== NULL
) {
4680 if (global_search
== NULL
)
4681 return (XT_CB_PASSTHROUGH
);
4683 t
->search_text
= g_strdup(global_search
);
4684 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4685 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4689 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4690 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4693 case XT_SEARCH_NEXT
:
4694 d
= t
->search_forward
;
4696 case XT_SEARCH_PREV
:
4697 d
= !t
->search_forward
;
4700 return (XT_CB_PASSTHROUGH
);
4703 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4705 return (XT_CB_HANDLED
);
4708 struct settings_args
{
4714 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4717 struct settings_args
*sa
= cb_args
;
4722 if (s
->flags
& XT_SF_RUNTIME
)
4728 *sa
->body
= g_strdup_printf(
4730 "<td style='background-color: %s; width: 10%%;word-break:break-all'>%s</td>"
4731 "<td style='background-color: %s; width: 20%%;word-break:break-all'>%s</td>",
4743 set(struct tab
*t
, struct karg
*args
)
4745 char *body
, *page
, *tmp
;
4747 struct settings_args sa
;
4749 bzero(&sa
, sizeof sa
);
4753 body
= g_strdup_printf("<div align='center'><table><tr>"
4754 "<th align='left'>Setting</th>"
4755 "<th align='left'>Value</th></tr>\n");
4757 settings_walk(print_setting
, &sa
);
4760 /* small message if there are none */
4763 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4764 "colspan='2'>No settings</td></tr>\n", body
);
4769 body
= g_strdup_printf("%s</table></div>", body
);
4772 page
= get_html_page("Settings", body
, "", 0);
4776 load_webkit_string(t
, page
, XT_URI_ABOUT_SET
);
4780 return (XT_CB_PASSTHROUGH
);
4784 session_save(struct tab
*t
, char *filename
)
4789 if (strlen(filename
) == 0)
4792 if (filename
[0] == '.' || filename
[0] == '/')
4796 if (save_tabs(t
, &a
))
4798 strlcpy(named_session
, filename
, sizeof named_session
);
4806 session_open(struct tab
*t
, char *filename
)
4811 if (strlen(filename
) == 0)
4814 if (filename
[0] == '.' || filename
[0] == '/')
4818 a
.i
= XT_SES_CLOSETABS
;
4819 if (open_tabs(t
, &a
))
4822 strlcpy(named_session
, filename
, sizeof named_session
);
4830 session_delete(struct tab
*t
, char *filename
)
4832 char file
[PATH_MAX
];
4835 if (strlen(filename
) == 0)
4838 if (filename
[0] == '.' || filename
[0] == '/')
4841 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, filename
);
4845 if (!strcmp(filename
, named_session
))
4846 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4847 sizeof named_session
);
4855 session_cmd(struct tab
*t
, struct karg
*args
)
4857 char *filename
= args
->s
;
4862 if (args
->i
& XT_SHOW
)
4863 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4864 XT_SAVED_TABS_FILE
: named_session
);
4865 else if (args
->i
& XT_SAVE
) {
4866 if (session_save(t
, filename
)) {
4867 show_oops(t
, "Can't save session: %s",
4868 filename
? filename
: "INVALID");
4871 } else if (args
->i
& XT_OPEN
) {
4872 if (session_open(t
, filename
)) {
4873 show_oops(t
, "Can't open session: %s",
4874 filename
? filename
: "INVALID");
4877 } else if (args
->i
& XT_DELETE
) {
4878 if (session_delete(t
, filename
)) {
4879 show_oops(t
, "Can't delete session: %s",
4880 filename
? filename
: "INVALID");
4885 return (XT_CB_PASSTHROUGH
);
4889 * Make a hardcopy of the page
4892 print_page(struct tab
*t
, struct karg
*args
)
4894 WebKitWebFrame
*frame
;
4896 GtkPrintOperation
*op
;
4897 GtkPrintOperationAction action
;
4898 GtkPrintOperationResult print_res
;
4899 GError
*g_err
= NULL
;
4900 int marg_l
, marg_r
, marg_t
, marg_b
;
4902 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4904 ps
= gtk_page_setup_new();
4905 op
= gtk_print_operation_new();
4906 action
= GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG
;
4907 frame
= webkit_web_view_get_main_frame(t
->wv
);
4909 /* the default margins are too small, so we will bump them */
4910 marg_l
= gtk_page_setup_get_left_margin(ps
, GTK_UNIT_MM
) +
4911 XT_PRINT_EXTRA_MARGIN
;
4912 marg_r
= gtk_page_setup_get_right_margin(ps
, GTK_UNIT_MM
) +
4913 XT_PRINT_EXTRA_MARGIN
;
4914 marg_t
= gtk_page_setup_get_top_margin(ps
, GTK_UNIT_MM
) +
4915 XT_PRINT_EXTRA_MARGIN
;
4916 marg_b
= gtk_page_setup_get_bottom_margin(ps
, GTK_UNIT_MM
) +
4917 XT_PRINT_EXTRA_MARGIN
;
4920 gtk_page_setup_set_left_margin(ps
, marg_l
, GTK_UNIT_MM
);
4921 gtk_page_setup_set_right_margin(ps
, marg_r
, GTK_UNIT_MM
);
4922 gtk_page_setup_set_top_margin(ps
, marg_t
, GTK_UNIT_MM
);
4923 gtk_page_setup_set_bottom_margin(ps
, marg_b
, GTK_UNIT_MM
);
4925 gtk_print_operation_set_default_page_setup(op
, ps
);
4927 /* this appears to free 'op' and 'ps' */
4928 print_res
= webkit_web_frame_print_full(frame
, op
, action
, &g_err
);
4930 /* check it worked */
4931 if (print_res
== GTK_PRINT_OPERATION_RESULT_ERROR
) {
4932 show_oops(NULL
, "can't print: %s", g_err
->message
);
4933 g_error_free (g_err
);
4941 go_home(struct tab
*t
, struct karg
*args
)
4948 restart(struct tab
*t
, struct karg
*args
)
4952 a
.s
= XT_RESTART_TABS_FILE
;
4954 execvp(start_argv
[0], start_argv
);
4960 #define CTRL GDK_CONTROL_MASK
4961 #define MOD1 GDK_MOD1_MASK
4962 #define SHFT GDK_SHIFT_MASK
4964 /* inherent to GTK not all keys will be caught at all times */
4965 /* XXX sort key bindings */
4966 struct key_binding
{
4971 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4973 { "cookiejar", MOD1
, 0, GDK_j
},
4974 { "downloadmgr", MOD1
, 0, GDK_d
},
4975 { "history", MOD1
, 0, GDK_h
},
4976 { "print", CTRL
, 0, GDK_p
},
4977 { "search", 0, 0, GDK_slash
},
4978 { "searchb", 0, 0, GDK_question
},
4979 { "statustoggle", CTRL
, 0, GDK_n
},
4980 { "command", 0, 0, GDK_colon
},
4981 { "qa", CTRL
, 0, GDK_q
},
4982 { "restart", MOD1
, 0, GDK_q
},
4983 { "js toggle", CTRL
, 0, GDK_j
},
4984 { "cookie toggle", MOD1
, 0, GDK_c
},
4985 { "togglesrc", CTRL
, 0, GDK_s
},
4986 { "yankuri", 0, 0, GDK_y
},
4987 { "pasteuricur", 0, 0, GDK_p
},
4988 { "pasteurinew", 0, 0, GDK_P
},
4989 { "toplevel toggle", 0, 0, GDK_F4
},
4990 { "help", 0, 0, GDK_F1
},
4991 { "run_script", MOD1
, 0, GDK_r
},
4994 { "searchnext", 0, 0, GDK_n
},
4995 { "searchprevious", 0, 0, GDK_N
},
4998 { "focusaddress", 0, 0, GDK_F6
},
4999 { "focussearch", 0, 0, GDK_F7
},
5002 { "hinting", 0, 0, GDK_f
},
5004 /* custom stylesheet */
5005 { "userstyle", 0, 0, GDK_i
},
5008 { "goback", 0, 0, GDK_BackSpace
},
5009 { "goback", MOD1
, 0, GDK_Left
},
5010 { "goforward", SHFT
, 0, GDK_BackSpace
},
5011 { "goforward", MOD1
, 0, GDK_Right
},
5012 { "reload", 0, 0, GDK_F5
},
5013 { "reload", CTRL
, 0, GDK_r
},
5014 { "reload", CTRL
, 0, GDK_l
},
5015 { "favorites", MOD1
, 1, GDK_f
},
5017 /* vertical movement */
5018 { "scrolldown", 0, 0, GDK_j
},
5019 { "scrolldown", 0, 0, GDK_Down
},
5020 { "scrollup", 0, 0, GDK_Up
},
5021 { "scrollup", 0, 0, GDK_k
},
5022 { "scrollbottom", 0, 0, GDK_G
},
5023 { "scrollbottom", 0, 0, GDK_End
},
5024 { "scrolltop", 0, 0, GDK_Home
},
5025 { "scrollpagedown", 0, 0, GDK_space
},
5026 { "scrollpagedown", CTRL
, 0, GDK_f
},
5027 { "scrollhalfdown", CTRL
, 0, GDK_d
},
5028 { "scrollpagedown", 0, 0, GDK_Page_Down
},
5029 { "scrollpageup", 0, 0, GDK_Page_Up
},
5030 { "scrollpageup", CTRL
, 0, GDK_b
},
5031 { "scrollhalfup", CTRL
, 0, GDK_u
},
5032 /* horizontal movement */
5033 { "scrollright", 0, 0, GDK_l
},
5034 { "scrollright", 0, 0, GDK_Right
},
5035 { "scrollleft", 0, 0, GDK_Left
},
5036 { "scrollleft", 0, 0, GDK_h
},
5037 { "scrollfarright", 0, 0, GDK_dollar
},
5038 { "scrollfarleft", 0, 0, GDK_0
},
5041 { "tabnew", CTRL
, 0, GDK_t
},
5042 { "999tabnew", CTRL
, 0, GDK_T
},
5043 { "tabclose", CTRL
, 1, GDK_w
},
5044 { "tabundoclose", 0, 0, GDK_U
},
5045 { "tabnext 1", CTRL
, 0, GDK_1
},
5046 { "tabnext 2", CTRL
, 0, GDK_2
},
5047 { "tabnext 3", CTRL
, 0, GDK_3
},
5048 { "tabnext 4", CTRL
, 0, GDK_4
},
5049 { "tabnext 5", CTRL
, 0, GDK_5
},
5050 { "tabnext 6", CTRL
, 0, GDK_6
},
5051 { "tabnext 7", CTRL
, 0, GDK_7
},
5052 { "tabnext 8", CTRL
, 0, GDK_8
},
5053 { "tabnext 9", CTRL
, 0, GDK_9
},
5054 { "tabfirst", CTRL
, 0, GDK_less
},
5055 { "tablast", CTRL
, 0, GDK_greater
},
5056 { "tabprevious", CTRL
, 0, GDK_Left
},
5057 { "tabnext", CTRL
, 0, GDK_Right
},
5058 { "focusout", CTRL
, 0, GDK_minus
},
5059 { "focusin", CTRL
, 0, GDK_plus
},
5060 { "focusin", CTRL
, 0, GDK_equal
},
5061 { "focusreset", CTRL
, 0, GDK_0
},
5063 /* command aliases (handy when -S flag is used) */
5064 { "promptopen", 0, 0, GDK_F9
},
5065 { "promptopencurrent", 0, 0, GDK_F10
},
5066 { "prompttabnew", 0, 0, GDK_F11
},
5067 { "prompttabnewcurrent",0, 0, GDK_F12
},
5069 TAILQ_HEAD(keybinding_list
, key_binding
);
5072 walk_kb(struct settings
*s
,
5073 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
5075 struct key_binding
*k
;
5078 if (s
== NULL
|| cb
== NULL
) {
5079 show_oops(NULL
, "walk_kb invalid parameters");
5083 TAILQ_FOREACH(k
, &kbl
, entry
) {
5089 if (gdk_keyval_name(k
->key
) == NULL
)
5092 strlcat(str
, k
->cmd
, sizeof str
);
5093 strlcat(str
, ",", sizeof str
);
5095 if (k
->mask
& GDK_SHIFT_MASK
)
5096 strlcat(str
, "S-", sizeof str
);
5097 if (k
->mask
& GDK_CONTROL_MASK
)
5098 strlcat(str
, "C-", sizeof str
);
5099 if (k
->mask
& GDK_MOD1_MASK
)
5100 strlcat(str
, "M1-", sizeof str
);
5101 if (k
->mask
& GDK_MOD2_MASK
)
5102 strlcat(str
, "M2-", sizeof str
);
5103 if (k
->mask
& GDK_MOD3_MASK
)
5104 strlcat(str
, "M3-", sizeof str
);
5105 if (k
->mask
& GDK_MOD4_MASK
)
5106 strlcat(str
, "M4-", sizeof str
);
5107 if (k
->mask
& GDK_MOD5_MASK
)
5108 strlcat(str
, "M5-", sizeof str
);
5110 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
5111 cb(s
, str
, cb_args
);
5116 init_keybindings(void)
5119 struct key_binding
*k
;
5121 for (i
= 0; i
< LENGTH(keys
); i
++) {
5122 k
= g_malloc0(sizeof *k
);
5123 k
->cmd
= keys
[i
].cmd
;
5124 k
->mask
= keys
[i
].mask
;
5125 k
->use_in_entry
= keys
[i
].use_in_entry
;
5126 k
->key
= keys
[i
].key
;
5127 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5129 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
5130 k
->cmd
? k
->cmd
: "unnamed key");
5135 keybinding_clearall(void)
5137 struct key_binding
*k
, *next
;
5139 for (k
= TAILQ_FIRST(&kbl
); k
; k
= next
) {
5140 next
= TAILQ_NEXT(k
, entry
);
5144 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
5145 k
->cmd
? k
->cmd
: "unnamed key");
5146 TAILQ_REMOVE(&kbl
, k
, entry
);
5152 keybinding_add(char *cmd
, char *key
, int use_in_entry
)
5154 struct key_binding
*k
;
5155 guint keyval
, mask
= 0;
5158 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s %s\n", cmd
, key
);
5160 /* Keys which are to be used in entry have been prefixed with an
5161 * exclamation mark. */
5165 /* find modifier keys */
5166 if (strstr(key
, "S-"))
5167 mask
|= GDK_SHIFT_MASK
;
5168 if (strstr(key
, "C-"))
5169 mask
|= GDK_CONTROL_MASK
;
5170 if (strstr(key
, "M1-"))
5171 mask
|= GDK_MOD1_MASK
;
5172 if (strstr(key
, "M2-"))
5173 mask
|= GDK_MOD2_MASK
;
5174 if (strstr(key
, "M3-"))
5175 mask
|= GDK_MOD3_MASK
;
5176 if (strstr(key
, "M4-"))
5177 mask
|= GDK_MOD4_MASK
;
5178 if (strstr(key
, "M5-"))
5179 mask
|= GDK_MOD5_MASK
;
5182 for (i
= strlen(key
) - 1; i
> 0; i
--)
5186 /* validate keyname */
5187 keyval
= gdk_keyval_from_name(key
);
5188 if (keyval
== GDK_VoidSymbol
) {
5189 warnx("invalid keybinding name %s", key
);
5192 /* must run this test too, gtk+ doesn't handle 10 for example */
5193 if (gdk_keyval_name(keyval
) == NULL
) {
5194 warnx("invalid keybinding name %s", key
);
5198 /* Remove eventual dupes. */
5199 TAILQ_FOREACH(k
, &kbl
, entry
)
5200 if (k
->key
== keyval
&& k
->mask
== mask
) {
5201 TAILQ_REMOVE(&kbl
, k
, entry
);
5207 k
= g_malloc0(sizeof *k
);
5208 k
->cmd
= g_strdup(cmd
);
5210 k
->use_in_entry
= use_in_entry
;
5213 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
5218 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
5219 k
->cmd
, gdk_keyval_name(keyval
));
5221 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
5227 add_kb(struct settings
*s
, char *entry
)
5231 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
5233 /* clearall is special */
5234 if (!strcmp(entry
, "clearall")) {
5235 keybinding_clearall();
5239 kb
= strstr(entry
, ",");
5245 return (keybinding_add(entry
, key
, key
[0] == '!'));
5251 int (*func
)(struct tab
*, struct karg
*);
5255 { "command", 0, command
, ':', 0 },
5256 { "search", 0, command
, '/', 0 },
5257 { "searchb", 0, command
, '?', 0 },
5258 { "togglesrc", 0, toggle_src
, 0, 0 },
5260 /* yanking and pasting */
5261 { "yankuri", 0, yank_uri
, 0, 0 },
5262 /* XXX: pasteuri{cur,new} do not work from the cmd_entry? */
5263 { "pasteuricur", 0, paste_uri
, XT_PASTE_CURRENT_TAB
, 0 },
5264 { "pasteurinew", 0, paste_uri
, XT_PASTE_NEW_TAB
, 0 },
5267 { "searchnext", 0, search
, XT_SEARCH_NEXT
, 0 },
5268 { "searchprevious", 0, search
, XT_SEARCH_PREV
, 0 },
5271 { "focusaddress", 0, focus
, XT_FOCUS_URI
, 0 },
5272 { "focussearch", 0, focus
, XT_FOCUS_SEARCH
, 0 },
5275 { "hinting", 0, hint
, 0, 0 },
5277 /* custom stylesheet */
5278 { "userstyle", 0, userstyle
, 0, 0 },
5281 { "goback", 0, navaction
, XT_NAV_BACK
, 0 },
5282 { "goforward", 0, navaction
, XT_NAV_FORWARD
, 0 },
5283 { "reload", 0, navaction
, XT_NAV_RELOAD
, 0 },
5285 /* vertical movement */
5286 { "scrolldown", 0, move
, XT_MOVE_DOWN
, 0 },
5287 { "scrollup", 0, move
, XT_MOVE_UP
, 0 },
5288 { "scrollbottom", 0, move
, XT_MOVE_BOTTOM
, 0 },
5289 { "scrolltop", 0, move
, XT_MOVE_TOP
, 0 },
5290 { "1", 0, move
, XT_MOVE_TOP
, 0 },
5291 { "scrollhalfdown", 0, move
, XT_MOVE_HALFDOWN
, 0 },
5292 { "scrollhalfup", 0, move
, XT_MOVE_HALFUP
, 0 },
5293 { "scrollpagedown", 0, move
, XT_MOVE_PAGEDOWN
, 0 },
5294 { "scrollpageup", 0, move
, XT_MOVE_PAGEUP
, 0 },
5295 /* horizontal movement */
5296 { "scrollright", 0, move
, XT_MOVE_RIGHT
, 0 },
5297 { "scrollleft", 0, move
, XT_MOVE_LEFT
, 0 },
5298 { "scrollfarright", 0, move
, XT_MOVE_FARRIGHT
, 0 },
5299 { "scrollfarleft", 0, move
, XT_MOVE_FARLEFT
, 0 },
5301 { "favorites", 0, xtp_page_fl
, 0, 0 },
5302 { "fav", 0, xtp_page_fl
, 0, 0 },
5303 { "favadd", 0, add_favorite
, 0, 0 },
5305 { "qall", 0, quit
, 0, 0 },
5306 { "quitall", 0, quit
, 0, 0 },
5307 { "w", 0, save_tabs
, 0, 0 },
5308 { "wq", 0, save_tabs_and_quit
, 0, 0 },
5309 { "help", 0, help
, 0, 0 },
5310 { "about", 0, about
, 0, 0 },
5311 { "stats", 0, stats
, 0, 0 },
5312 { "version", 0, about
, 0, 0 },
5315 { "js", 0, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5316 { "save", 1, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5317 { "domain", 2, js_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5318 { "fqdn", 2, js_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5319 { "show", 1, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5320 { "all", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5321 { "persistent", 2, js_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5322 { "session", 2, js_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5323 { "toggle", 1, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5324 { "domain", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5325 { "fqdn", 2, js_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5327 /* cookie command */
5328 { "cookie", 0, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5329 { "save", 1, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5330 { "domain", 2, cookie_cmd
, XT_SAVE
| XT_WL_TOPLEVEL
, 0 },
5331 { "fqdn", 2, cookie_cmd
, XT_SAVE
| XT_WL_FQDN
, 0 },
5332 { "show", 1, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5333 { "all", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
| XT_WL_SESSION
, 0 },
5334 { "persistent", 2, cookie_cmd
, XT_SHOW
| XT_WL_PERSISTENT
, 0 },
5335 { "session", 2, cookie_cmd
, XT_SHOW
| XT_WL_SESSION
, 0 },
5336 { "toggle", 1, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5337 { "domain", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
, 0 },
5338 { "fqdn", 2, cookie_cmd
, XT_WL_TOGGLE
| XT_WL_FQDN
, 0 },
5340 /* toplevel (domain) command */
5341 { "toplevel", 0, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5342 { "toggle", 1, toplevel_cmd
, XT_WL_TOGGLE
| XT_WL_TOPLEVEL
| XT_WL_RELOAD
, 0 },
5345 { "cookiejar", 0, xtp_page_cl
, 0, 0 },
5348 { "cert", 0, cert_cmd
, XT_SHOW
, 0 },
5349 { "save", 1, cert_cmd
, XT_SAVE
, 0 },
5350 { "show", 1, cert_cmd
, XT_SHOW
, 0 },
5352 { "ca", 0, ca_cmd
, 0, 0 },
5353 { "downloadmgr", 0, xtp_page_dl
, 0, 0 },
5354 { "dl", 0, xtp_page_dl
, 0, 0 },
5355 { "h", 0, xtp_page_hl
, 0, 0 },
5356 { "history", 0, xtp_page_hl
, 0, 0 },
5357 { "home", 0, go_home
, 0, 0 },
5358 { "restart", 0, restart
, 0, 0 },
5359 { "urlhide", 0, urlaction
, XT_URL_HIDE
, 0 },
5360 { "urlshow", 0, urlaction
, XT_URL_SHOW
, 0 },
5361 { "statustoggle", 0, statustoggle
, 0, 0 },
5362 { "run_script", 0, run_page_script
, 0, XT_USERARG
},
5364 { "print", 0, print_page
, 0, 0 },
5367 { "focusin", 0, resizetab
, XT_ZOOM_IN
, 0 },
5368 { "focusout", 0, resizetab
, XT_ZOOM_OUT
, 0 },
5369 { "focusreset", 0, resizetab
, XT_ZOOM_NORMAL
, 0 },
5370 { "q", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5371 { "quit", 0, tabaction
, XT_TAB_DELQUIT
, 0 },
5372 { "open", 0, tabaction
, XT_TAB_OPEN
, XT_URLARG
},
5373 { "tabclose", 0, tabaction
, XT_TAB_DELETE
, XT_PREFIX
| XT_INTARG
},
5374 { "tabedit", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5375 { "tabfirst", 0, movetab
, XT_TAB_FIRST
, 0 },
5376 { "tabhide", 0, tabaction
, XT_TAB_HIDE
, 0 },
5377 { "tablast", 0, movetab
, XT_TAB_LAST
, 0 },
5378 { "tabnew", 0, tabaction
, XT_TAB_NEW
, XT_PREFIX
| XT_URLARG
},
5379 { "tabnext", 0, movetab
, XT_TAB_NEXT
, XT_PREFIX
| XT_INTARG
},
5380 { "tabnextstyle", 0, tabaction
, XT_TAB_NEXTSTYLE
, 0 },
5381 { "tabprevious", 0, movetab
, XT_TAB_PREV
, XT_PREFIX
| XT_INTARG
},
5382 { "tabrewind", 0, movetab
, XT_TAB_FIRST
, 0 },
5383 { "tabshow", 0, tabaction
, XT_TAB_SHOW
, 0 },
5384 { "tabundoclose", 0, tabaction
, XT_TAB_UNDO_CLOSE
, 0 },
5385 { "buffers", 0, buffers
, 0, 0 },
5386 { "ls", 0, buffers
, 0, 0 },
5387 { "tabs", 0, buffers
, 0, 0 },
5389 /* command aliases (handy when -S flag is used) */
5390 { "promptopen", 0, command
, XT_CMD_OPEN
, 0 },
5391 { "promptopencurrent", 0, command
, XT_CMD_OPEN_CURRENT
, 0 },
5392 { "prompttabnew", 0, command
, XT_CMD_TABNEW
, 0 },
5393 { "prompttabnewcurrent",0, command
, XT_CMD_TABNEW_CURRENT
, 0 },
5396 { "set", 0, set
, 0, 0 },
5397 { "fullscreen", 0, fullscreen
, 0, 0 },
5398 { "f", 0, fullscreen
, 0, 0 },
5401 { "session", 0, session_cmd
, XT_SHOW
, 0 },
5402 { "delete", 1, session_cmd
, XT_DELETE
, XT_USERARG
},
5403 { "open", 1, session_cmd
, XT_OPEN
, XT_USERARG
},
5404 { "save", 1, session_cmd
, XT_SAVE
, XT_USERARG
},
5405 { "show", 1, session_cmd
, XT_SHOW
, 0 },
5412 } cmd_status
= {-1, 0};
5415 wv_release_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5418 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 1)
5425 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5432 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5434 else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 8 /* btn 4 */) {
5440 } else if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 9 /* btn 5 */) {
5442 a
.i
= XT_NAV_FORWARD
;
5452 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
5454 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
5456 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
5463 * cancel, remove, etc. downloads
5466 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
5468 struct download find
, *d
= NULL
;
5470 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
5472 /* some commands require a valid download id */
5473 if (cmd
!= XT_XTP_DL_LIST
) {
5474 /* lookup download in question */
5476 d
= RB_FIND(download_list
, &downloads
, &find
);
5479 show_oops(t
, "%s: no such download", __func__
);
5484 /* decide what to do */
5486 case XT_XTP_DL_CANCEL
:
5487 webkit_download_cancel(d
->download
);
5489 case XT_XTP_DL_REMOVE
:
5490 webkit_download_cancel(d
->download
); /* just incase */
5491 g_object_unref(d
->download
);
5492 RB_REMOVE(download_list
, &downloads
, d
);
5494 case XT_XTP_DL_LIST
:
5498 show_oops(t
, "%s: unknown command", __func__
);
5501 xtp_page_dl(t
, NULL
);
5505 * Actions on history, only does one thing for now, but
5506 * we provide the function for future actions
5509 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
5511 struct history
*h
, *next
;
5515 case XT_XTP_HL_REMOVE
:
5516 /* walk backwards, as listed in reverse */
5517 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
5518 next
= RB_PREV(history_list
, &hl
, h
);
5520 RB_REMOVE(history_list
, &hl
, h
);
5521 g_free((gpointer
) h
->title
);
5522 g_free((gpointer
) h
->uri
);
5529 case XT_XTP_HL_LIST
:
5530 /* Nothing - just xtp_page_hl() below */
5533 show_oops(t
, "%s: unknown command", __func__
);
5537 xtp_page_hl(t
, NULL
);
5540 /* remove a favorite */
5542 remove_favorite(struct tab
*t
, int index
)
5544 char file
[PATH_MAX
], *title
, *uri
= NULL
;
5545 char *new_favs
, *tmp
;
5550 /* open favorites */
5551 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
5553 if ((f
= fopen(file
, "r")) == NULL
) {
5554 show_oops(t
, "%s: can't open favorites: %s",
5555 __func__
, strerror(errno
));
5559 /* build a string which will become the new favroites file */
5560 new_favs
= g_strdup("");
5563 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
5564 if (feof(f
) || ferror(f
))
5566 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
5573 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
5574 if (feof(f
) || ferror(f
)) {
5575 show_oops(t
, "%s: can't parse favorites %s",
5576 __func__
, strerror(errno
));
5581 /* as long as this isn't the one we are deleting add to file */
5584 new_favs
= g_strdup_printf("%s%s\n%s\n",
5585 new_favs
, title
, uri
);
5597 /* write back new favorites file */
5598 if ((f
= fopen(file
, "w")) == NULL
) {
5599 show_oops(t
, "%s: can't open favorites: %s",
5600 __func__
, strerror(errno
));
5604 fwrite(new_favs
, strlen(new_favs
), 1, f
);
5617 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
5620 case XT_XTP_FL_LIST
:
5621 /* nothing, just the below call to xtp_page_fl() */
5623 case XT_XTP_FL_REMOVE
:
5624 remove_favorite(t
, arg
);
5627 show_oops(t
, "%s: invalid favorites command", __func__
);
5631 xtp_page_fl(t
, NULL
);
5635 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
5638 case XT_XTP_CL_LIST
:
5639 /* nothing, just xtp_page_cl() */
5641 case XT_XTP_CL_REMOVE
:
5645 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5649 xtp_page_cl(t
, NULL
);
5652 /* link an XTP class to it's session key and handler function */
5653 struct xtp_despatch
{
5656 void (*handle_func
)(struct tab
*, uint8_t, int);
5659 struct xtp_despatch xtp_despatches
[] = {
5660 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5661 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5662 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5663 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5664 { XT_XTP_INVALID
, NULL
, NULL
}
5668 * is the url xtp protocol? (xxxt://)
5669 * if so, parse and despatch correct bahvior
5672 parse_xtp_url(struct tab
*t
, const char *url
)
5674 char *dup
= NULL
, *p
, *last
;
5675 uint8_t n_tokens
= 0;
5676 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5677 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5682 * tokens array meaning:
5684 * tokens[1] = session key
5685 * tokens[2] = action
5686 * tokens[3] = optional argument
5689 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5691 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5694 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5696 /* split out the url */
5697 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5698 (p
= strtok_r(NULL
, "/", &last
))) {
5700 tokens
[n_tokens
++] = p
;
5703 /* should be atleast three fields 'class/seskey/command/arg' */
5707 dsp
= xtp_despatches
;
5708 req_class
= atoi(tokens
[0]);
5709 while (dsp
->xtp_class
) {
5710 if (dsp
->xtp_class
== req_class
) {
5717 /* did we find one atall? */
5718 if (dsp_match
== NULL
) {
5719 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5723 /* check session key and call despatch function */
5724 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5725 ret
= TRUE
; /* all is well, this was a valid xtp request */
5726 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5739 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5741 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5743 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5746 show_oops(NULL
, "activate_uri_entry_cb invalid parameters");
5751 show_oops(t
, "activate_uri_entry_cb no uri");
5755 uri
+= strspn(uri
, "\t ");
5757 /* if xxxt:// treat specially */
5758 if (parse_xtp_url(t
, uri
))
5761 /* otherwise continue to load page normally */
5762 load_uri(t
, (gchar
*)uri
);
5767 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5769 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5770 char *newuri
= NULL
;
5773 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5776 show_oops(NULL
, "activate_search_entry_cb invalid parameters");
5780 if (search_string
== NULL
) {
5781 show_oops(t
, "no search_string");
5785 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5787 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5788 newuri
= g_strdup_printf(search_string
, enc_search
);
5792 webkit_web_view_load_uri(t
->wv
, newuri
);
5800 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5802 struct domain
*d
= NULL
;
5805 if (uri
== NULL
|| t
== NULL
)
5808 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5813 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5814 es
? "enable" : "disable", uri
);
5816 g_object_set(G_OBJECT(t
->settings
),
5817 "enable-scripts", es
, (char *)NULL
);
5818 g_object_set(G_OBJECT(t
->settings
),
5819 "javascript-can-open-windows-automatically", es
, (char *)NULL
);
5820 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5822 button_set_stockid(t
->js_toggle
,
5823 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5827 show_ca_status(struct tab
*t
, const char *uri
)
5829 WebKitWebFrame
*frame
;
5830 WebKitWebDataSource
*source
;
5831 WebKitNetworkRequest
*request
;
5832 SoupMessage
*message
;
5834 gchar
*col_str
= XT_COLOR_WHITE
;
5837 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5838 ssl_strict_certs
, ssl_ca_file
, uri
);
5842 if (ssl_ca_file
== NULL
) {
5843 if (g_str_has_prefix(uri
, "http://"))
5845 if (g_str_has_prefix(uri
, "https://")) {
5846 col_str
= XT_COLOR_RED
;
5851 if (g_str_has_prefix(uri
, "http://") ||
5852 !g_str_has_prefix(uri
, "https://"))
5855 frame
= webkit_web_view_get_main_frame(t
->wv
);
5856 source
= webkit_web_frame_get_data_source(frame
);
5857 request
= webkit_web_data_source_get_request(source
);
5858 message
= webkit_network_request_get_message(request
);
5860 if (message
&& (soup_message_get_flags(message
) &
5861 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5862 col_str
= XT_COLOR_GREEN
;
5864 /* see if we need to override green */
5865 r
= load_compare_cert(t
, NULL
);
5867 col_str
= XT_COLOR_BLUE
;
5870 r
= load_compare_cert(t
, NULL
);
5872 col_str
= XT_COLOR_BLUE
;
5874 col_str
= XT_COLOR_YELLOW
;
5876 col_str
= XT_COLOR_RED
;
5881 gdk_color_parse(col_str
, &color
);
5882 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5884 if (!strcmp(col_str
, XT_COLOR_WHITE
))
5885 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
5887 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
5892 free_favicon(struct tab
*t
)
5894 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
5895 __func__
, t
->icon_download
, t
->icon_request
);
5897 if (t
->icon_request
)
5898 g_object_unref(t
->icon_request
);
5899 if (t
->icon_dest_uri
)
5900 g_free(t
->icon_dest_uri
);
5902 t
->icon_request
= NULL
;
5903 t
->icon_dest_uri
= NULL
;
5907 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5909 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5910 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5912 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5913 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5915 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5916 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5920 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
5922 GdkPixbuf
*pb_scaled
;
5924 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
5925 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
5926 GDK_INTERP_BILINEAR
);
5930 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5931 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5933 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
5934 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5936 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5937 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5939 if (pb_scaled
!= pb
)
5940 g_object_unref(pb_scaled
);
5944 xt_icon_from_file(struct tab
*t
, char *file
)
5948 if (g_str_has_prefix(file
, "file://"))
5949 file
+= strlen("file://");
5951 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5953 xt_icon_from_pixbuf(t
, pb
);
5956 xt_icon_from_name(t
, "text-html");
5960 is_valid_icon(char *file
)
5963 const char *mime_type
;
5967 gf
= g_file_new_for_path(file
);
5968 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5970 mime_type
= g_file_info_get_content_type(fi
);
5971 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5972 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5973 g_strcmp0(mime_type
, "image/png") == 0 ||
5974 g_strcmp0(mime_type
, "image/gif") == 0 ||
5975 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5983 set_favicon_from_file(struct tab
*t
, char *file
)
5987 if (t
== NULL
|| file
== NULL
)
5990 if (g_str_has_prefix(file
, "file://"))
5991 file
+= strlen("file://");
5992 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5994 if (!stat(file
, &sb
)) {
5995 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5996 /* corrupt icon so trash it */
5997 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6000 /* no need to set icon to default here */
6004 xt_icon_from_file(t
, file
);
6008 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6011 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6012 struct tab
*tt
= NULL
, *t
= NULL
;
6015 * find the webview instead of passing in the tab as it could have been
6016 * deleted from underneath us.
6018 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6027 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6028 __func__
, t
->tab_id
, status
);
6031 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6033 t
->icon_download
= NULL
;
6036 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6039 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6042 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6044 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6045 __func__
, t
->tab_id
);
6046 t
->icon_download
= NULL
;
6049 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6052 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6053 __func__
, t
->icon_dest_uri
);
6054 set_favicon_from_file(t
, t
->icon_dest_uri
);
6055 /* these will be freed post callback */
6056 t
->icon_request
= NULL
;
6057 t
->icon_download
= NULL
;
6065 abort_favicon_download(struct tab
*t
)
6067 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6069 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6070 if (t
->icon_download
) {
6071 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6072 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6073 webkit_download_cancel(t
->icon_download
);
6074 t
->icon_download
= NULL
;
6079 xt_icon_from_name(t
, "text-html");
6083 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6085 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6087 if (uri
== NULL
|| t
== NULL
)
6090 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6091 /* take icon from WebKitIconDatabase */
6094 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6096 xt_icon_from_pixbuf(t
, pb
);
6099 xt_icon_from_name(t
, "text-html");
6100 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6101 /* download icon to cache dir */
6102 gchar
*name_hash
, file
[PATH_MAX
];
6105 if (t
->icon_request
) {
6106 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6110 /* check to see if we got the icon in cache */
6111 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6112 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6115 if (!stat(file
, &sb
)) {
6116 if (sb
.st_size
> 0) {
6117 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6119 set_favicon_from_file(t
, file
);
6123 /* corrupt icon so trash it */
6124 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6129 /* create download for icon */
6130 t
->icon_request
= webkit_network_request_new(uri
);
6131 if (t
->icon_request
== NULL
) {
6132 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6137 t
->icon_download
= webkit_download_new(t
->icon_request
);
6138 if (t
->icon_download
== NULL
)
6141 /* we have to free icon_dest_uri later */
6142 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6143 webkit_download_set_destination_uri(t
->icon_download
,
6146 if (webkit_download_get_status(t
->icon_download
) ==
6147 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6148 g_object_unref(t
->icon_request
);
6149 g_free(t
->icon_dest_uri
);
6150 t
->icon_request
= NULL
;
6151 t
->icon_dest_uri
= NULL
;
6155 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6156 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6158 webkit_download_start(t
->icon_download
);
6163 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6165 const gchar
*uri
= NULL
, *title
= NULL
;
6166 struct history
*h
, find
;
6169 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6170 webkit_web_view_get_load_status(wview
),
6171 get_uri(t
) ? get_uri(t
) : "NOTHING");
6174 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6178 switch (webkit_web_view_get_load_status(wview
)) {
6179 case WEBKIT_LOAD_PROVISIONAL
:
6181 abort_favicon_download(t
);
6182 #if GTK_CHECK_VERSION(2, 20, 0)
6183 gtk_widget_show(t
->spinner
);
6184 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6186 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6188 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6190 /* take focus if we are visible */
6196 case WEBKIT_LOAD_COMMITTED
:
6201 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6207 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6209 /* check if js white listing is enabled */
6210 if (enable_js_whitelist
) {
6211 check_and_set_js(uri
, t
);
6218 /* we know enough to autosave the session */
6219 if (session_autosave
) {
6224 show_ca_status(t
, uri
);
6227 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6231 case WEBKIT_LOAD_FINISHED
:
6237 if (!strncmp(uri
, "http://", strlen("http://")) ||
6238 !strncmp(uri
, "https://", strlen("https://")) ||
6239 !strncmp(uri
, "file://", strlen("file://"))) {
6241 h
= RB_FIND(history_list
, &hl
, &find
);
6243 title
= get_title(t
, FALSE
);
6244 h
= g_malloc(sizeof *h
);
6245 h
->uri
= g_strdup(uri
);
6246 h
->title
= g_strdup(title
);
6247 RB_INSERT(history_list
, &hl
, h
);
6248 completion_add_uri(h
->uri
);
6249 update_history_tabs(NULL
);
6253 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6254 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6255 case WEBKIT_LOAD_FAILED
:
6258 #if GTK_CHECK_VERSION(2, 20, 0)
6259 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6260 gtk_widget_hide(t
->spinner
);
6263 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6268 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6270 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6271 webkit_web_view_can_go_back(wview
));
6273 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6274 webkit_web_view_can_go_forward(wview
));
6278 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6279 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6283 t
->tmp_uri
= g_strdup(uri
);
6284 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6285 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6286 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6287 set_status(t
, uri
, XT_STATUS_NOTHING
);
6293 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6295 const gchar
*title
= NULL
, *win_title
= NULL
;
6297 title
= get_title(t
, FALSE
);
6298 win_title
= get_title(t
, TRUE
);
6299 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6300 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6301 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6302 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6306 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6308 run_script(t
, JS_HINTING
);
6312 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6314 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6315 progress
== 100 ? 0 : (double)progress
/ 100);
6316 if (show_url
== 0) {
6317 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6318 progress
== 100 ? 0 : (double)progress
/ 100);
6321 update_statusbar_position(NULL
, NULL
);
6325 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6326 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6327 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6330 WebKitWebNavigationReason reason
;
6331 struct domain
*d
= NULL
;
6334 show_oops(NULL
, "webview_npd_cb invalid parameters");
6338 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6340 webkit_network_request_get_uri(request
));
6342 uri
= (char *)webkit_network_request_get_uri(request
);
6344 /* if this is an xtp url, we don't load anything else */
6345 if (parse_xtp_url(t
, uri
))
6348 if (t
->ctrl_click
) {
6350 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6351 webkit_web_policy_decision_ignore(pd
);
6352 return (TRUE
); /* we made the decission */
6356 * This is a little hairy but it comes down to this:
6357 * when we run in whitelist mode we have to assist the browser in
6358 * opening the URL that it would have opened in a new tab.
6360 reason
= webkit_web_navigation_action_get_reason(na
);
6361 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6362 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6363 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6364 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6366 webkit_web_policy_decision_use(pd
);
6367 return (TRUE
); /* we made the decision */
6374 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6377 struct domain
*d
= NULL
;
6379 WebKitWebView
*webview
= NULL
;
6381 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6382 webkit_web_view_get_uri(wv
));
6385 /* open in current tab */
6387 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6388 uri
= webkit_web_view_get_uri(wv
);
6389 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6392 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6394 } else if (enable_scripts
== 1) {
6395 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6403 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6406 struct domain
*d
= NULL
;
6408 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6410 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6411 uri
= webkit_web_view_get_uri(wv
);
6412 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6416 } else if (enable_scripts
== 1)
6423 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6425 /* we can not eat the event without throwing gtk off so defer it */
6427 /* catch middle click */
6428 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6433 /* catch ctrl click */
6434 if (e
->type
== GDK_BUTTON_RELEASE
&&
6435 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6440 return (XT_CB_PASSTHROUGH
);
6444 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6446 struct mime_type
*m
;
6448 m
= find_mime_type(mime_type
);
6456 show_oops(t
, "can't fork mime handler");
6466 execlp(m
->mt_action
, m
->mt_action
,
6467 webkit_network_request_get_uri(request
), (void *)NULL
);
6476 get_mime_type(char *file
)
6478 const char *mime_type
;
6482 if (g_str_has_prefix(file
, "file://"))
6483 file
+= strlen("file://");
6485 gf
= g_file_new_for_path(file
);
6486 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6488 mime_type
= g_file_info_get_content_type(fi
);
6496 run_download_mimehandler(char *mime_type
, char *file
)
6498 struct mime_type
*m
;
6500 m
= find_mime_type(mime_type
);
6506 show_oops(NULL
, "can't fork download mime handler");
6516 if (g_str_has_prefix(file
, "file://"))
6517 file
+= strlen("file://");
6518 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6527 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6530 WebKitDownloadStatus status
;
6531 const gchar
*file
= NULL
, *mime
= NULL
;
6533 if (download
== NULL
)
6535 status
= webkit_download_get_status(download
);
6536 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6539 file
= webkit_download_get_destination_uri(download
);
6542 mime
= get_mime_type((char *)file
);
6546 run_download_mimehandler((char *)mime
, (char *)file
);
6550 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6551 WebKitNetworkRequest
*request
, char *mime_type
,
6552 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6555 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6559 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6560 t
->tab_id
, mime_type
);
6562 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6563 webkit_web_policy_decision_ignore(decision
);
6568 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6569 webkit_web_policy_decision_download(decision
);
6577 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6581 const gchar
*suggested_name
;
6582 gchar
*filename
= NULL
;
6584 struct download
*download_entry
;
6587 if (wk_download
== NULL
|| t
== NULL
) {
6588 show_oops(NULL
, "%s invalid parameters", __func__
);
6592 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6593 if (suggested_name
== NULL
)
6594 return (FALSE
); /* abort download */
6605 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6607 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6608 filename
: suggested_name
);
6610 } while (!stat(uri
+ strlen("file://"), &sb
));
6612 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6613 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6615 webkit_download_set_destination_uri(wk_download
, uri
);
6617 if (webkit_download_get_status(wk_download
) ==
6618 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6619 show_oops(t
, "%s: download failed to start", __func__
);
6621 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6623 /* connect "download first" mime handler */
6624 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6625 G_CALLBACK(download_status_changed_cb
), NULL
);
6627 download_entry
= g_malloc(sizeof(struct download
));
6628 download_entry
->download
= wk_download
;
6629 download_entry
->tab
= t
;
6630 download_entry
->id
= next_download_id
++;
6631 RB_INSERT(download_list
, &downloads
, download_entry
);
6632 /* get from history */
6633 g_object_ref(wk_download
);
6634 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6635 show_oops(t
, "Download of '%s' started...",
6636 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6645 /* sync other download manager tabs */
6646 update_download_tabs(NULL
);
6649 * NOTE: never redirect/render the current tab before this
6650 * function returns. This will cause the download to never start.
6652 return (ret
); /* start download */
6656 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6658 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6661 show_oops(NULL
, "webview_hover_cb");
6666 set_status(t
, uri
, XT_STATUS_LINK
);
6669 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6674 mark(struct tab
*t
, struct karg
*arg
)
6680 if ((index
= marktoindex(mark
)) == -1)
6683 if (arg
->i
== XT_MARK_SET
)
6684 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
6685 else if (arg
->i
== XT_MARK_GOTO
) {
6686 if (t
->mark
[index
] == XT_INVALID_MARK
) {
6687 show_oops(t
, "mark '%c' does not exist", mark
);
6690 /* XXX t->mark[index] can be bigger than the maximum if ajax or
6691 something changes the document size */
6692 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
6699 marks_clear(struct tab
*t
)
6703 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
6704 t
->mark
[i
] = XT_INVALID_MARK
;
6710 char file
[PATH_MAX
];
6711 char *line
= NULL
, *p
, mark
;
6716 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
6717 if ((f
= fopen(file
, "r+")) == NULL
) {
6718 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
6722 for (i
= 1; ; i
++) {
6723 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
6724 if (feof(f
) || ferror(f
))
6726 if (strlen(line
) == 0 || line
[0] == '#') {
6732 p
= strtok(line
, " \t");
6734 if (p
== NULL
|| strlen(p
) != 1 ||
6735 (index
= marktoindex(*p
)) == -1) {
6736 warnx("corrupt quickmarks file, line %d", i
);
6741 p
= strtok(NULL
, " \t");
6742 if (qmarks
[index
] != NULL
)
6743 g_free(qmarks
[index
]);
6744 qmarks
[index
] = g_strdup(p
);
6755 char file
[PATH_MAX
];
6759 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
6760 if ((f
= fopen(file
, "r+")) == NULL
) {
6761 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
6765 for (i
= 0; i
< XT_NOMARKS
; i
++)
6766 if (qmarks
[i
] != NULL
)
6767 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
6775 qmark(struct tab
*t
, struct karg
*arg
)
6780 mark
= arg
->s
[strlen(arg
->s
)-1];
6781 index
= marktoindex(mark
);
6787 if (qmarks
[index
] != NULL
)
6788 g_free(qmarks
[index
]);
6790 qmarks_load(); /* sync if multiple instances */
6791 qmarks
[index
] = g_strdup(get_uri(t
));
6795 if (qmarks
[index
] != NULL
)
6796 load_uri(t
, qmarks
[index
]);
6798 show_oops(t
, "quickmark \"%c\" does not exist",
6804 if (qmarks
[index
] != NULL
)
6805 create_new_tab(qmarks
[index
], NULL
, 1, -1);
6807 show_oops(t
, "quickmark \"%c\" does not exist",
6818 go_up(struct tab
*t
, struct karg
*args
)
6824 levels
= atoi(args
->s
);
6828 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
6829 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
6831 tmp
+= strlen(XT_PROTO_DELIM
);
6833 /* if an uri starts with a slash, leave it alone (for file:///) */
6840 p
= strrchr(tmp
, '/');
6854 gototab(struct tab
*t
, struct karg
*args
)
6857 struct karg arg
= {0, NULL
, -1};
6859 tab
= atoi(args
->s
);
6861 arg
.i
= XT_TAB_NEXT
;
6870 zoom_amount(struct tab
*t
, struct karg
*arg
)
6872 struct karg narg
= {0, NULL
, -1};
6874 narg
.i
= atoi(arg
->s
);
6875 resizetab(t
, &narg
);
6880 /* buffer commands receive the regex that triggered them in arg.s */
6884 int (*func
)(struct tab
*, struct karg
*);
6888 { "^[0-9]*gu$", go_up
, 0 },
6889 { "^gg$", move
, XT_MOVE_TOP
},
6890 { "^gG$", move
, XT_MOVE_BOTTOM
},
6891 { "^[0-9]+%$", move
, XT_MOVE_PERCENT
},
6892 { "^gh$", go_home
, 0 },
6893 { "^m[a-zA-Z0-9]$", mark
, XT_MARK_SET
},
6894 { "^[`'][a-zA-Z0-9]$", mark
, XT_MARK_GOTO
},
6895 { "^[0-9]+t$", gototab
, 0 },
6896 { "^M[a-zA-Z0-9]$", qmark
, XT_QMARK_SET
},
6897 { "^go[a-zA-Z0-9]$", qmark
, XT_QMARK_OPEN
},
6898 { "^gn[a-zA-Z0-9]$", qmark
, XT_QMARK_TAB
},
6899 { "^ZR$", restart
, 0 },
6900 { "^ZZ$", quit
, 0 },
6901 { "^zi$", resizetab
, XT_ZOOM_IN
},
6902 { "^zo$", resizetab
, XT_ZOOM_OUT
},
6903 { "^z0$", resizetab
, XT_ZOOM_NORMAL
},
6904 { "^[0-9]+Z$", zoom_amount
, 0 },
6908 buffercmd_init(void)
6912 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
6913 regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
6918 buffercmd_abort(struct tab
*t
)
6922 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
6923 for (i
= 0; i
< LENGTH(bcmd
); i
++)
6926 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
6927 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
6931 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
6933 struct karg arg
= {0, NULL
, -1};
6936 arg
.s
= g_strdup(bcmd
);
6938 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
6939 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
6949 buffercmd_addkey(struct tab
*t
, guint keyval
)
6953 if (keyval
== GDK_Escape
) {
6955 return (XT_CB_HANDLED
);
6958 /* key with modifier or non-ascii character */
6959 if (!isascii(keyval
))
6960 return (XT_CB_PASSTHROUGH
);
6962 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
6963 "to buffer \"%s\"\n", keyval
, bcmd
);
6965 for (i
= 0; i
< LENGTH(bcmd
); i
++)
6966 if (bcmd
[i
] == '\0') {
6971 /* buffer full, ignore input */
6972 if (i
== LENGTH(bcmd
)) {
6973 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
6974 return (XT_CB_HANDLED
);
6977 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
6979 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
6980 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
6981 (size_t) 0, NULL
, 0) == 0) {
6982 buffercmd_execute(t
, &buffercmds
[i
]);
6986 return (XT_CB_HANDLED
);
6990 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6992 struct key_binding
*k
;
6994 /* handle keybindings if buffercmd is empty.
6995 if not empty, allow commands like C-n */
6996 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
6997 TAILQ_FOREACH(k
, &kbl
, entry
)
6998 if (e
->keyval
== k
->key
6999 && (entry
? k
->use_in_entry
: 1)) {
7001 if ((e
->state
& (CTRL
| MOD1
)) == 0)
7002 return (cmd_execute(t
, k
->cmd
));
7003 } else if ((e
->state
& k
->mask
) == k
->mask
) {
7004 return (cmd_execute(t
, k
->cmd
));
7008 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7009 return buffercmd_addkey(t
, e
->keyval
);
7011 return (XT_CB_PASSTHROUGH
);
7015 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7017 char s
[2], buf
[128];
7018 const char *errstr
= NULL
;
7020 /* don't use w directly; use t->whatever instead */
7023 show_oops(NULL
, "wv_keypress_after_cb");
7024 return (XT_CB_PASSTHROUGH
);
7027 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7028 e
->keyval
, e
->state
, t
);
7032 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7034 return (XT_CB_HANDLED
);
7038 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7040 /* we have a string */
7042 /* we have a number */
7043 snprintf(buf
, sizeof buf
,
7044 "vimprobable_fire(%s)", t
->hint_num
);
7051 /* XXX unfuck this */
7052 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7053 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7054 /* last input was numerical */
7056 l
= strlen(t
->hint_num
);
7063 t
->hint_num
[l
] = '\0';
7067 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7068 /* last input was alphanumerical */
7070 l
= strlen(t
->hint_buf
);
7077 t
->hint_buf
[l
] = '\0';
7087 /* numerical input */
7088 if (CLEAN(e
->state
) == 0 &&
7089 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7090 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7091 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7092 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7093 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7097 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7098 "invalid link number\n");
7101 snprintf(buf
, sizeof buf
,
7102 "vimprobable_update_hints(%s)",
7104 t
->hint_mode
= XT_HINT_NUMERICAL
;
7108 /* empty the counter buffer */
7109 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7110 return (XT_CB_HANDLED
);
7113 /* alphanumerical input */
7114 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7115 e
->keyval
<= GDK_z
) ||
7116 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7117 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7118 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7119 e
->keyval
<= GDK_9
) ||
7120 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7121 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7122 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7123 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7124 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7125 " %s\n", t
->hint_buf
);
7127 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7130 snprintf(buf
, sizeof buf
,
7131 "vimprobable_show_hints('%s')", t
->hint_buf
);
7132 t
->hint_mode
= XT_HINT_ALPHANUM
;
7135 /* empty the counter buffer */
7136 bzero(t
->hint_num
, sizeof t
->hint_num
);
7137 return (XT_CB_HANDLED
);
7140 return (XT_CB_HANDLED
);
7143 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7144 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7145 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7148 return (handle_keypress(t
, e
, 0));
7152 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7156 /* Hide buffers, if they are visible, with escape. */
7157 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7158 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7159 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7161 return (XT_CB_HANDLED
);
7164 return (XT_CB_PASSTHROUGH
);
7168 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7170 const gchar
*c
= gtk_entry_get_text(w
);
7174 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7175 e
->keyval
, e
->state
, t
);
7178 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7179 return (XT_CB_PASSTHROUGH
);
7182 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7183 e
->keyval
, e
->state
, t
);
7187 if (strlen(c
) == 1) {
7188 webkit_web_view_unmark_text_matches(t
->wv
);
7194 else if (c
[0] == '?')
7200 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
7202 /* not found, mark red */
7203 gdk_color_parse(XT_COLOR_RED
, &color
);
7204 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7205 /* unmark and remove selection */
7206 webkit_web_view_unmark_text_matches(t
->wv
);
7207 /* my kingdom for a way to unselect text in webview */
7209 /* found, highlight all */
7210 webkit_web_view_unmark_text_matches(t
->wv
);
7211 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7212 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7213 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7214 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7217 return (XT_CB_PASSTHROUGH
);
7221 match_uri(const gchar
*uri
, const gchar
*key
) {
7224 gboolean match
= FALSE
;
7228 if (!strncmp(key
, uri
, len
))
7231 voffset
= strstr(uri
, "/") + 2;
7232 if (!strncmp(key
, voffset
, len
))
7234 else if (g_str_has_prefix(voffset
, "www.")) {
7235 voffset
= voffset
+ strlen("www.");
7236 if (!strncmp(key
, voffset
, len
))
7245 cmd_getlist(int id
, char *key
)
7250 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
7251 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7252 if (match_uri(h
->uri
, key
)) {
7253 cmd_status
.list
[c
] = (char *)h
->uri
;
7262 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7264 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7265 if (cmds
[i
].level
< dep
)
7267 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7269 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7277 cmd_getnext(int dir
)
7279 cmd_status
.index
+= dir
;
7281 if (cmd_status
.index
< 0)
7282 cmd_status
.index
= cmd_status
.len
- 1;
7283 else if (cmd_status
.index
>= cmd_status
.len
)
7284 cmd_status
.index
= 0;
7286 return cmd_status
.list
[cmd_status
.index
];
7290 cmd_tokenize(char *s
, char *tokens
[])
7294 size_t len
= strlen(s
);
7297 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7298 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7299 tok
= strtok_r(NULL
, " ", &last
), i
++)
7309 cmd_complete(struct tab
*t
, char *str
, int dir
)
7311 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
7312 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
7314 char *tok
, *match
, *s
= g_strdup(str
);
7316 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
7319 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
7322 for (i
= 0; isdigit(s
[i
]); i
++)
7325 for (; isspace(s
[i
]); i
++)
7330 levels
= cmd_tokenize(s
, tokens
);
7332 for (i
= 0; i
< levels
- 1; i
++) {
7335 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7336 if (cmds
[j
].level
< dep
)
7338 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
7342 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
7349 if (matchcount
== 1) {
7350 strlcat(res
, tok
, sizeof res
);
7351 strlcat(res
, " ", sizeof res
);
7361 if (cmd_status
.index
== -1)
7362 cmd_getlist(parent
, tokens
[i
]);
7364 if (cmd_status
.len
> 0) {
7365 match
= cmd_getnext(dir
);
7366 strlcat(res
, match
, sizeof res
);
7367 gtk_entry_set_text(w
, res
);
7368 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
7375 cmd_execute(struct tab
*t
, char *str
)
7377 struct cmd
*cmd
= NULL
;
7378 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
7380 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
7381 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
7382 struct karg arg
= {0, NULL
, -1};
7387 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
7393 while (isspace(s
[0]))
7396 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
7397 prefix
= atoi(prefixstr
);
7401 for (tok
= strtok_r(s
, " ", &last
); tok
;
7402 tok
= strtok_r(NULL
, " ", &last
)) {
7404 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7405 if (cmds
[j
].level
< dep
)
7407 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
7409 if (cmds
[j
].level
== dep
&&
7410 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
7414 if (len
== strlen(cmds
[j
].cmd
)) {
7420 if (matchcount
== 1) {
7425 show_oops(t
, "Invalid command: %s", str
);
7434 else if (cmd_prefix
> 0)
7437 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.p
> -1) {
7438 show_oops(t
, "No prefix allowed: %s", str
);
7442 arg
.s
= last
? g_strdup(last
) : g_strdup("");
7443 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
7444 arg
.p
= atoi(arg
.s
);
7447 show_oops(t
, "Zero count");
7449 show_oops(t
, "Trailing characters");
7454 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n", __func__
, arg
.p
, arg
.s
);
7470 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7473 show_oops(NULL
, "entry_key_cb invalid parameters");
7474 return (XT_CB_PASSTHROUGH
);
7477 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
7478 e
->keyval
, e
->state
, t
);
7482 if (e
->keyval
== GDK_Escape
) {
7483 /* don't use focus_webview(t) because we want to type :cmds */
7484 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7487 return (handle_keypress(t
, e
, 1));
7491 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7493 int rv
= XT_CB_HANDLED
;
7494 const gchar
*c
= gtk_entry_get_text(w
);
7497 show_oops(NULL
, "cmd_keypress_cb parameters");
7498 return (XT_CB_PASSTHROUGH
);
7501 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
7502 e
->keyval
, e
->state
, t
);
7506 e
->keyval
= GDK_Escape
;
7507 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7508 e
->keyval
= GDK_Escape
;
7510 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
7511 e
->keyval
!= GDK_ISO_Left_Tab
)
7512 cmd_status
.index
= -1;
7514 switch (e
->keyval
) {
7517 cmd_complete(t
, (char *)&c
[1], 1);
7519 case GDK_ISO_Left_Tab
:
7521 cmd_complete(t
, (char *)&c
[1], -1);
7525 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7533 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7534 webkit_web_view_unmark_text_matches(t
->wv
);
7538 rv
= XT_CB_PASSTHROUGH
;
7544 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7547 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7548 return (XT_CB_PASSTHROUGH
);
7550 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7555 if (show_url
== 0 || t
->focus_wv
)
7558 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7560 return (XT_CB_PASSTHROUGH
);
7564 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7567 const gchar
*c
= gtk_entry_get_text(entry
);
7570 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7574 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7581 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7587 if (c
[0] == '/' || c
[0] == '?') {
7588 if (t
->search_text
) {
7589 g_free(t
->search_text
);
7590 t
->search_text
= NULL
;
7593 t
->search_text
= g_strdup(s
);
7595 g_free(global_search
);
7596 global_search
= g_strdup(s
);
7597 t
->search_forward
= c
[0] == '/';
7609 backward_cb(GtkWidget
*w
, struct tab
*t
)
7614 show_oops(NULL
, "backward_cb invalid parameters");
7618 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
7625 forward_cb(GtkWidget
*w
, struct tab
*t
)
7630 show_oops(NULL
, "forward_cb invalid parameters");
7634 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
7636 a
.i
= XT_NAV_FORWARD
;
7641 home_cb(GtkWidget
*w
, struct tab
*t
)
7644 show_oops(NULL
, "home_cb invalid parameters");
7648 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
7654 stop_cb(GtkWidget
*w
, struct tab
*t
)
7656 WebKitWebFrame
*frame
;
7659 show_oops(NULL
, "stop_cb invalid parameters");
7663 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
7665 frame
= webkit_web_view_get_main_frame(t
->wv
);
7666 if (frame
== NULL
) {
7667 show_oops(t
, "stop_cb: no frame");
7671 webkit_web_frame_stop_loading(frame
);
7672 abort_favicon_download(t
);
7676 setup_webkit(struct tab
*t
)
7678 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
7679 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
7680 FALSE
, (char *)NULL
);
7682 warnx("webkit does not have \"enable-dns-prefetching\" property");
7683 g_object_set(G_OBJECT(t
->settings
),
7684 "user-agent", t
->user_agent
, (char *)NULL
);
7685 g_object_set(G_OBJECT(t
->settings
),
7686 "enable-scripts", enable_scripts
, (char *)NULL
);
7687 g_object_set(G_OBJECT(t
->settings
),
7688 "enable-plugins", enable_plugins
, (char *)NULL
);
7689 g_object_set(G_OBJECT(t
->settings
),
7690 "javascript-can-open-windows-automatically", enable_scripts
,
7692 g_object_set(G_OBJECT(t
->settings
),
7693 "enable-html5-database", FALSE
, (char *)NULL
);
7694 g_object_set(G_OBJECT(t
->settings
),
7695 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
7696 g_object_set(G_OBJECT(t
->settings
),
7697 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
7698 g_object_set(G_OBJECT(t
->settings
),
7699 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
7700 g_object_set(G_OBJECT(t
->wv
),
7701 "full-content-zoom", TRUE
, (char *)NULL
);
7703 webkit_web_view_set_settings(t
->wv
, t
->settings
);
7707 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
7709 struct tab
*ti
, *t
= NULL
;
7710 gdouble view_size
, value
, max
;
7713 TAILQ_FOREACH(ti
, &tabs
, entry
)
7714 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
7722 if (adjustment
== NULL
)
7723 adjustment
= gtk_scrolled_window_get_vadjustment(
7724 GTK_SCROLLED_WINDOW(t
->browser_win
));
7726 view_size
= gtk_adjustment_get_page_size(adjustment
);
7727 value
= gtk_adjustment_get_value(adjustment
);
7728 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
7731 position
= g_strdup("All");
7732 else if (value
== max
)
7733 position
= g_strdup("Bot");
7734 else if (value
== 0)
7735 position
= g_strdup("Top");
7737 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
7739 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
7746 create_browser(struct tab
*t
)
7750 GtkAdjustment
*adjustment
;
7753 show_oops(NULL
, "create_browser invalid parameters");
7757 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7758 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7759 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7760 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7762 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7763 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7764 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7766 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7767 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7770 t
->settings
= webkit_web_settings_new();
7772 if (user_agent
== NULL
) {
7773 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7775 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7778 t
->user_agent
= g_strdup(user_agent
);
7780 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7783 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
7784 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
7785 G_CALLBACK(update_statusbar_position
), NULL
);
7797 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7798 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7799 gtk_widget_set_name(w
, "xxxterm");
7800 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7801 g_signal_connect(G_OBJECT(w
), "delete_event",
7802 G_CALLBACK (gtk_main_quit
), NULL
);
7808 create_kiosk_toolbar(struct tab
*t
)
7810 GtkWidget
*toolbar
= NULL
, *b
;
7812 b
= gtk_hbox_new(FALSE
, 0);
7814 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7816 /* backward button */
7817 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7818 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7819 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7820 G_CALLBACK(backward_cb
), t
);
7821 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7823 /* forward button */
7824 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7825 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7826 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7827 G_CALLBACK(forward_cb
), t
);
7828 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7831 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7832 gtk_widget_set_sensitive(t
->gohome
, true);
7833 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7834 G_CALLBACK(home_cb
), t
);
7835 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7837 /* create widgets but don't use them */
7838 t
->uri_entry
= gtk_entry_new();
7839 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7840 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7841 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7847 create_toolbar(struct tab
*t
)
7849 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7851 b
= gtk_hbox_new(FALSE
, 0);
7853 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7855 /* backward button */
7856 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7857 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7858 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7859 G_CALLBACK(backward_cb
), t
);
7860 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7862 /* forward button */
7863 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7864 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7865 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7866 G_CALLBACK(forward_cb
), t
);
7867 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7871 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7872 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7873 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7874 G_CALLBACK(stop_cb
), t
);
7875 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7879 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7880 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7881 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7882 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7883 G_CALLBACK(js_toggle_cb
), t
);
7884 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7886 t
->uri_entry
= gtk_entry_new();
7887 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7888 G_CALLBACK(activate_uri_entry_cb
), t
);
7889 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7890 G_CALLBACK(entry_key_cb
), t
);
7892 eb1
= gtk_hbox_new(FALSE
, 0);
7893 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7894 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7895 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7898 if (search_string
) {
7900 t
->search_entry
= gtk_entry_new();
7901 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7902 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7903 G_CALLBACK(activate_search_entry_cb
), t
);
7904 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7905 G_CALLBACK(entry_key_cb
), t
);
7906 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7907 eb2
= gtk_hbox_new(FALSE
, 0);
7908 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7909 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7911 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7918 create_buffers(struct tab
*t
)
7920 GtkCellRenderer
*renderer
;
7923 view
= gtk_tree_view_new();
7925 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
7927 renderer
= gtk_cell_renderer_text_new();
7928 gtk_tree_view_insert_column_with_attributes
7929 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
7931 renderer
= gtk_cell_renderer_text_new();
7932 gtk_tree_view_insert_column_with_attributes
7933 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
7936 gtk_tree_view_set_model
7937 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
7943 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
7944 GtkTreeViewColumn
*col
, struct tab
*t
)
7949 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7951 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
7954 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
7955 set_current_tab(id
- 1);
7961 /* after tab reordering/creation/removal */
7968 TAILQ_FOREACH(t
, &tabs
, entry
) {
7969 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7970 if (t
->tab_id
> maxid
)
7973 gtk_widget_show(t
->tab_elems
.sep
);
7976 TAILQ_FOREACH(t
, &tabs
, entry
) {
7977 if (t
->tab_id
== maxid
) {
7978 gtk_widget_hide(t
->tab_elems
.sep
);
7984 /* after active tab change */
7986 recolor_compact_tabs(void)
7992 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7993 TAILQ_FOREACH(t
, &tabs
, entry
)
7994 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
7997 curid
= gtk_notebook_get_current_page(notebook
);
7998 TAILQ_FOREACH(t
, &tabs
, entry
)
7999 if (t
->tab_id
== curid
) {
8000 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
8001 gtk_widget_modify_fg(t
->tab_elems
.label
,
8002 GTK_STATE_NORMAL
, &color
);
8008 set_current_tab(int page_num
)
8010 buffercmd_abort(get_current_tab());
8011 gtk_notebook_set_current_page(notebook
, page_num
);
8012 recolor_compact_tabs();
8016 undo_close_tab_save(struct tab
*t
)
8020 struct undo
*u1
, *u2
;
8022 WebKitWebHistoryItem
*item
;
8024 if ((uri
= get_uri(t
)) == NULL
)
8027 u1
= g_malloc0(sizeof(struct undo
));
8028 u1
->uri
= g_strdup(uri
);
8030 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8032 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8033 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8036 /* forward history */
8037 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8041 u1
->history
= g_list_prepend(u1
->history
,
8042 webkit_web_history_item_copy(item
));
8043 items
= g_list_next(items
);
8048 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8049 u1
->history
= g_list_prepend(u1
->history
,
8050 webkit_web_history_item_copy(item
));
8054 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8058 u1
->history
= g_list_prepend(u1
->history
,
8059 webkit_web_history_item_copy(item
));
8060 items
= g_list_next(items
);
8063 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8065 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8066 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8067 TAILQ_REMOVE(&undos
, u2
, entry
);
8069 g_list_free(u2
->history
);
8078 delete_tab(struct tab
*t
)
8082 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8087 TAILQ_REMOVE(&tabs
, t
, entry
);
8089 /* Halt all webkit activity. */
8090 abort_favicon_download(t
);
8091 webkit_web_view_stop_loading(t
->wv
);
8093 /* Save the tab, so we can undo the close. */
8094 undo_close_tab_save(t
);
8096 if (browser_mode
== XT_BM_KIOSK
) {
8097 gtk_widget_destroy(t
->uri_entry
);
8098 gtk_widget_destroy(t
->stop
);
8099 gtk_widget_destroy(t
->js_toggle
);
8102 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8103 gtk_widget_destroy(t
->vbox
);
8105 g_free(t
->user_agent
);
8106 g_free(t
->stylesheet
);
8110 if (TAILQ_EMPTY(&tabs
)) {
8111 if (browser_mode
== XT_BM_KIOSK
)
8112 create_new_tab(home
, NULL
, 1, -1);
8114 create_new_tab(NULL
, NULL
, 1, -1);
8117 /* recreate session */
8118 if (session_autosave
) {
8124 recolor_compact_tabs();
8128 update_statusbar_zoom(struct tab
*t
)
8131 char s
[16] = { '\0' };
8133 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8134 if ((zoom
<= 0.99 || zoom
>= 1.01))
8135 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8136 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8140 setzoom_webkit(struct tab
*t
, int adjust
)
8142 #define XT_ZOOMPERCENT 0.04
8147 show_oops(NULL
, "setzoom_webkit invalid parameters");
8151 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8152 if (adjust
== XT_ZOOM_IN
)
8153 zoom
+= XT_ZOOMPERCENT
;
8154 else if (adjust
== XT_ZOOM_OUT
)
8155 zoom
-= XT_ZOOMPERCENT
;
8156 else if (adjust
> 0)
8157 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8159 show_oops(t
, "setzoom_webkit invalid zoom value");
8163 if (zoom
< XT_ZOOMPERCENT
)
8164 zoom
= XT_ZOOMPERCENT
;
8165 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8166 update_statusbar_zoom(t
);
8170 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8172 struct tab
*t
= (struct tab
*) data
;
8174 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8176 switch (event
->button
) {
8178 set_current_tab(t
->tab_id
);
8189 append_tab(struct tab
*t
)
8194 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8195 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8199 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
8204 WebKitWebHistoryItem
*item
;
8208 int sbe_p
= 0, sbe_b
= 0,
8211 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
8213 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
8214 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
8218 t
= g_malloc0(sizeof *t
);
8220 if (title
== NULL
) {
8221 title
= "(untitled)";
8225 t
->vbox
= gtk_vbox_new(FALSE
, 0);
8227 /* label + button for tab */
8228 b
= gtk_hbox_new(FALSE
, 0);
8231 #if GTK_CHECK_VERSION(2, 20, 0)
8232 t
->spinner
= gtk_spinner_new();
8234 t
->label
= gtk_label_new(title
);
8235 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
8236 gtk_widget_set_size_request(t
->label
, 100, 0);
8237 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
8238 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
8239 gtk_widget_set_size_request(b
, 130, 0);
8241 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
8242 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
8243 #if GTK_CHECK_VERSION(2, 20, 0)
8244 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
8248 if (browser_mode
== XT_BM_KIOSK
) {
8249 t
->toolbar
= create_kiosk_toolbar(t
);
8250 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
8253 t
->toolbar
= create_toolbar(t
);
8255 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
8263 t
->browser_win
= create_browser(t
);
8264 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
8266 /* oops message for user feedback */
8267 t
->oops
= gtk_entry_new();
8268 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
8269 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
8270 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
8271 gdk_color_parse(XT_COLOR_RED
, &color
);
8272 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
8273 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
8274 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
8277 t
->cmd
= gtk_entry_new();
8278 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
8279 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
8280 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
8281 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
8284 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
8286 t
->sbe
.statusbar
= gtk_entry_new();
8287 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
8288 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
8289 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
8290 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
8292 /* create these widgets only if specified in statusbar_elems */
8293 t
->sbe
.position
= gtk_entry_new();
8294 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.position
), NULL
);
8295 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.position
), FALSE
);
8296 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.position
), FALSE
);
8297 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.position
), statusbar_font
);
8298 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.position
), 1.0);
8299 gtk_widget_set_size_request(t
->sbe
.position
, 40, -1);
8301 t
->sbe
.zoom
= gtk_entry_new();
8302 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.zoom
), NULL
);
8303 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.zoom
), FALSE
);
8304 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.zoom
), FALSE
);
8305 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.zoom
), statusbar_font
);
8306 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.zoom
), 1.0);
8307 gtk_widget_set_size_request(t
->sbe
.zoom
, 40, -1);
8309 t
->sbe
.buffercmd
= gtk_entry_new();
8310 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.buffercmd
), NULL
);
8311 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.buffercmd
), FALSE
);
8312 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.buffercmd
), FALSE
);
8313 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.buffercmd
), statusbar_font
);
8314 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.buffercmd
), 1.0);
8315 gtk_widget_set_size_request(t
->sbe
.buffercmd
, 60, -1);
8317 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
8319 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
8322 /* gtk widgets cannot be added to a box twice. sbe_* variables
8323 make sure of this */
8324 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
8328 GtkWidget
*sep
= gtk_vseparator_new();
8330 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
8331 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
8332 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
8333 FALSE
, FALSE
, FALSE
);
8338 warnx("flag \"%c\" specified more than "
8339 "once in statusbar_elems\n", *p
);
8343 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8344 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
8348 warnx("flag \"%c\" specified more than "
8349 "once in statusbar_elems\n", *p
);
8353 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8354 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
8358 warnx("flag \"%c\" specified more than "
8359 "once in statusbar_elems\n", *p
);
8363 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8364 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
8367 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
8372 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
8375 t
->buffers
= create_buffers(t
);
8376 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
8378 /* xtp meaning is normal by default */
8379 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
8381 /* set empty favicon */
8382 xt_icon_from_name(t
, "text-html");
8384 /* and show it all */
8385 gtk_widget_show_all(b
);
8386 gtk_widget_show_all(t
->vbox
);
8388 /* compact tab bar */
8389 t
->tab_elems
.label
= gtk_label_new(title
);
8390 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
8391 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
8392 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
8393 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
8395 t
->tab_elems
.eventbox
= gtk_event_box_new();
8396 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
8397 t
->tab_elems
.sep
= gtk_vseparator_new();
8399 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
8400 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
8401 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8402 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
8403 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
8404 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
8406 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
8408 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
8410 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
8413 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
8415 gtk_widget_show_all(t
->tab_elems
.eventbox
);
8417 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
8420 id
= position
>= 0 ? position
:
8421 gtk_notebook_get_current_page(notebook
) + 1;
8422 if (id
> gtk_notebook_get_n_pages(notebook
))
8425 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8426 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
8427 gtk_box_reorder_child(GTK_BOX(tab_bar
),
8428 t
->tab_elems
.eventbox
, id
);
8433 #if GTK_CHECK_VERSION(2, 20, 0)
8434 /* turn spinner off if we are a new tab without uri */
8436 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
8437 gtk_widget_hide(t
->spinner
);
8440 /* make notebook tabs reorderable */
8441 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
8443 /* compact tabs clickable */
8444 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
8445 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
8447 g_object_connect(G_OBJECT(t
->cmd
),
8448 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
8449 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
8450 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
8451 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
8454 /* reuse wv_button_cb to hide oops */
8455 g_object_connect(G_OBJECT(t
->oops
),
8456 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8459 g_signal_connect(t
->buffers
,
8460 "row-activated", G_CALLBACK(row_activated_cb
), t
);
8461 g_object_connect(G_OBJECT(t
->buffers
),
8462 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
8464 g_object_connect(G_OBJECT(t
->wv
),
8465 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
8466 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8467 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
8468 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
8469 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
8470 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8471 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8472 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
8473 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
8474 "signal::event", G_CALLBACK(webview_event_cb
), t
,
8475 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
8476 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
8477 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
8478 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8479 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
8481 g_signal_connect(t
->wv
,
8482 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
8483 g_signal_connect(t
->wv
,
8484 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
8485 g_signal_connect(t
->wv
,
8486 "notify::title", G_CALLBACK(notify_title_cb
), t
);
8488 /* hijack the unused keys as if we were the browser */
8489 g_object_connect(G_OBJECT(t
->toolbar
),
8490 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8493 g_signal_connect(G_OBJECT(bb
), "button_press_event",
8494 G_CALLBACK(tab_close_cb
), t
);
8500 url_set_visibility();
8501 statusbar_set_visibility();
8504 set_current_tab(t
->tab_id
);
8505 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
8510 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
8514 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8519 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8520 /* restore the tab's history */
8521 if (u
&& u
->history
) {
8525 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
8526 items
= g_list_next(items
);
8529 item
= g_list_nth_data(u
->history
, u
->back
);
8531 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
8534 g_list_free(u
->history
);
8536 webkit_web_back_forward_list_clear(t
->bfl
);
8538 recolor_compact_tabs();
8543 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8549 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
8551 if (gtk_notebook_get_current_page(notebook
) == -1)
8554 TAILQ_FOREACH(t
, &tabs
, entry
) {
8555 if (t
->tab_id
== pn
) {
8556 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
8559 uri
= get_title(t
, TRUE
);
8560 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
8566 /* can't use focus_webview here */
8567 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8574 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8577 struct tab
*t
= NULL
, *tt
;
8581 TAILQ_FOREACH(tt
, &tabs
, entry
)
8582 if (tt
->tab_id
== pn
) {
8587 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
8589 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
8594 menuitem_response(struct tab
*t
)
8596 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
8600 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
8602 GtkWidget
*menu
, *menu_items
;
8603 GdkEventButton
*bevent
;
8607 if (event
->type
== GDK_BUTTON_PRESS
) {
8608 bevent
= (GdkEventButton
*) event
;
8609 menu
= gtk_menu_new();
8611 TAILQ_FOREACH(ti
, &tabs
, entry
) {
8612 if ((uri
= get_uri(ti
)) == NULL
)
8613 /* XXX make sure there is something to print */
8614 /* XXX add gui pages in here to look purdy */
8616 menu_items
= gtk_menu_item_new_with_label(uri
);
8617 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
8618 gtk_widget_show(menu_items
);
8620 g_signal_connect_swapped((menu_items
),
8621 "activate", G_CALLBACK(menuitem_response
),
8625 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
8626 bevent
->button
, bevent
->time
);
8628 /* unref object so it'll free itself when popped down */
8629 #if !GTK_CHECK_VERSION(3, 0, 0)
8630 /* XXX does not need unref with gtk+3? */
8631 g_object_ref_sink(menu
);
8632 g_object_unref(menu
);
8635 return (TRUE
/* eat event */);
8638 return (FALSE
/* propagate */);
8642 icon_size_map(int icon_size
)
8644 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
8645 icon_size
> GTK_ICON_SIZE_DIALOG
)
8646 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
8652 create_button(char *name
, char *stockid
, int size
)
8654 GtkWidget
*button
, *image
;
8658 rcstring
= g_strdup_printf(
8659 "style \"%s-style\"\n"
8661 " GtkWidget::focus-padding = 0\n"
8662 " GtkWidget::focus-line-width = 0\n"
8666 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
8667 gtk_rc_parse_string(rcstring
);
8669 button
= gtk_button_new();
8670 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
8671 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
8673 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
8674 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8675 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
8676 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
8677 gtk_widget_set_name(button
, name
);
8678 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
8684 button_set_stockid(GtkWidget
*button
, char *stockid
)
8688 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
8689 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8690 gtk_button_set_image(GTK_BUTTON(button
), image
);
8694 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
8697 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
8701 * xterm doesn't play nice with clipboards because it clears the
8702 * primary when clicked. We rely on primary being set to properly
8703 * handle middle mouse button clicks (paste). So when someone clears
8704 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
8705 * other application behavior (as in DON'T clear primary).
8708 p
= gtk_clipboard_wait_for_text(primary
);
8710 if (gdk_property_get(gdk_get_default_root_window(),
8712 gdk_atom_intern("STRING", FALSE
),
8714 1024 * 1024 /* picked out of my butt */,
8720 /* yes sir, we need to NUL the string */
8722 gtk_clipboard_set_text(primary
, p
, -1);
8736 char file
[PATH_MAX
];
8739 vbox
= gtk_vbox_new(FALSE
, 0);
8740 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
8741 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
8742 #if !GTK_CHECK_VERSION(3, 0, 0)
8743 /* XXX seems to be needed with gtk+2 */
8744 gtk_notebook_set_tab_hborder(notebook
, 0);
8745 gtk_notebook_set_tab_vborder(notebook
, 0);
8747 gtk_notebook_set_scrollable(notebook
, TRUE
);
8748 gtk_notebook_set_show_border(notebook
, FALSE
);
8749 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
8751 abtn
= gtk_button_new();
8752 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
8753 gtk_widget_set_size_request(arrow
, -1, -1);
8754 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
8755 gtk_widget_set_size_request(abtn
, -1, 20);
8757 #if GTK_CHECK_VERSION(2, 20, 0)
8758 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
8760 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
8762 /* compact tab bar */
8763 tab_bar
= gtk_hbox_new(TRUE
, 0);
8765 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
8766 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
8767 gtk_widget_set_size_request(vbox
, -1, -1);
8769 g_object_connect(G_OBJECT(notebook
),
8770 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
8772 g_object_connect(G_OBJECT(notebook
),
8773 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
8774 NULL
, (char *)NULL
);
8775 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
8776 G_CALLBACK(arrow_cb
), NULL
);
8778 main_window
= create_window();
8779 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
8782 for (i
= 0; i
< LENGTH(icons
); i
++) {
8783 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
8784 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
8785 l
= g_list_append(l
, pb
);
8787 gtk_window_set_default_icon_list(l
);
8790 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
8791 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
8793 gtk_widget_show_all(abtn
);
8794 gtk_widget_show_all(main_window
);
8795 notebook_tab_set_visibility();
8799 set_hook(void **hook
, char *name
)
8802 errx(1, "set_hook");
8804 if (*hook
== NULL
) {
8805 *hook
= dlsym(RTLD_NEXT
, name
);
8807 errx(1, "can't hook %s", name
);
8811 /* override libsoup soup_cookie_equal because it doesn't look at domain */
8813 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
8815 g_return_val_if_fail(cookie1
, FALSE
);
8816 g_return_val_if_fail(cookie2
, FALSE
);
8818 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
8819 !strcmp (cookie1
->value
, cookie2
->value
) &&
8820 !strcmp (cookie1
->path
, cookie2
->path
) &&
8821 !strcmp (cookie1
->domain
, cookie2
->domain
));
8825 transfer_cookies(void)
8828 SoupCookie
*sc
, *pc
;
8830 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8832 for (;cf
; cf
= cf
->next
) {
8834 sc
= soup_cookie_copy(pc
);
8835 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
8838 soup_cookies_free(cf
);
8842 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
8847 print_cookie("soup_cookie_jar_delete_cookie", c
);
8849 if (cookies_enabled
== 0)
8852 if (jar
== NULL
|| c
== NULL
)
8855 /* find and remove from persistent jar */
8856 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8858 for (;cf
; cf
= cf
->next
) {
8860 if (soup_cookie_equal(ci
, c
)) {
8861 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
8866 soup_cookies_free(cf
);
8868 /* delete from session jar */
8869 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
8873 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
8875 struct domain
*d
= NULL
;
8879 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
8880 jar
, p_cookiejar
, s_cookiejar
);
8882 if (cookies_enabled
== 0)
8885 /* see if we are up and running */
8886 if (p_cookiejar
== NULL
) {
8887 _soup_cookie_jar_add_cookie(jar
, cookie
);
8890 /* disallow p_cookiejar adds, shouldn't happen */
8891 if (jar
== p_cookiejar
)
8895 if (jar
== NULL
|| cookie
== NULL
)
8898 if (enable_cookie_whitelist
&&
8899 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
8901 DNPRINTF(XT_D_COOKIE
,
8902 "soup_cookie_jar_add_cookie: reject %s\n",
8904 if (save_rejected_cookies
) {
8905 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
8906 show_oops(NULL
, "can't open reject cookie file");
8909 fseek(r_cookie_f
, 0, SEEK_END
);
8910 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
8911 cookie
->http_only
? "#HttpOnly_" : "",
8913 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
8915 cookie
->secure
? "TRUE" : "FALSE",
8917 (gulong
)soup_date_to_time_t(cookie
->expires
) :
8924 if (!allow_volatile_cookies
)
8928 if (cookie
->expires
== NULL
&& session_timeout
) {
8929 soup_cookie_set_expires(cookie
,
8930 soup_date_new_from_now(session_timeout
));
8931 print_cookie("modified add cookie", cookie
);
8934 /* see if we are white listed for persistence */
8935 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
8936 /* add to persistent jar */
8937 c
= soup_cookie_copy(cookie
);
8938 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
8939 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
8942 /* add to session jar */
8943 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
8944 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
8950 char file
[PATH_MAX
];
8952 set_hook((void *)&_soup_cookie_jar_add_cookie
,
8953 "soup_cookie_jar_add_cookie");
8954 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
8955 "soup_cookie_jar_delete_cookie");
8957 if (cookies_enabled
== 0)
8961 * the following code is intricate due to overriding several libsoup
8963 * do not alter order of these operations.
8966 /* rejected cookies */
8967 if (save_rejected_cookies
)
8968 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
8971 /* persistent cookies */
8972 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
8973 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
8975 /* session cookies */
8976 s_cookiejar
= soup_cookie_jar_new();
8977 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
8978 cookie_policy
, (void *)NULL
);
8981 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
8985 setup_proxy(char *uri
)
8988 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
8989 soup_uri_free(proxy_uri
);
8993 if (http_proxy
!= uri
) {
9000 http_proxy
= g_strdup(uri
);
9001 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
9002 proxy_uri
= soup_uri_new(http_proxy
);
9003 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
9008 send_cmd_to_socket(char *cmd
)
9011 struct sockaddr_un sa
;
9013 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9014 warnx("%s: socket", __func__
);
9018 sa
.sun_family
= AF_UNIX
;
9019 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9020 work_dir
, XT_SOCKET_FILE
);
9023 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9024 warnx("%s: connect", __func__
);
9028 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9029 warnx("%s: send", __func__
);
9040 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9043 char str
[XT_MAX_URL_LENGTH
];
9044 socklen_t t
= sizeof(struct sockaddr_un
);
9045 struct sockaddr_un sa
;
9050 gint fd
= g_io_channel_unix_get_fd(source
);
9052 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9057 if (getpeereid(s
, &uid
, &gid
) == -1) {
9061 if (uid
!= getuid() || gid
!= getgid()) {
9062 warnx("unauthorized user");
9068 warnx("not a valid user");
9072 n
= recv(s
, str
, sizeof(str
), 0);
9076 tt
= TAILQ_LAST(&tabs
, tab_list
);
9077 cmd_execute(tt
, str
);
9085 struct sockaddr_un sa
;
9087 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9088 warn("is_running: socket");
9092 sa
.sun_family
= AF_UNIX
;
9093 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9094 work_dir
, XT_SOCKET_FILE
);
9097 /* connect to see if there is a listener */
9098 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9099 rv
= 0; /* not running */
9101 rv
= 1; /* already running */
9112 struct sockaddr_un sa
;
9114 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9115 warn("build_socket: socket");
9119 sa
.sun_family
= AF_UNIX
;
9120 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9121 work_dir
, XT_SOCKET_FILE
);
9124 /* connect to see if there is a listener */
9125 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9126 /* no listener so we will */
9127 unlink(sa
.sun_path
);
9129 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9130 warn("build_socket: bind");
9134 if (listen(s
, 1) == -1) {
9135 warn("build_socket: listen");
9148 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9149 GtkTreeIter
*iter
, struct tab
*t
)
9153 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9161 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9162 GtkTreeIter
*iter
, struct tab
*t
)
9166 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9167 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9168 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
9175 completion_add_uri(const gchar
*uri
)
9179 /* add uri to list_store */
9180 gtk_list_store_append(completion_model
, &iter
);
9181 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
9185 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
9186 GtkTreeIter
*iter
, gpointer user_data
)
9189 gboolean match
= FALSE
;
9191 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
9197 match
= match_uri(value
, key
);
9204 completion_add(struct tab
*t
)
9206 /* enable completion for tab */
9207 t
->completion
= gtk_entry_completion_new();
9208 gtk_entry_completion_set_text_column(t
->completion
, 0);
9209 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
9210 gtk_entry_completion_set_model(t
->completion
,
9211 GTK_TREE_MODEL(completion_model
));
9212 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
9214 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
9215 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
9216 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
9217 G_CALLBACK(completion_select_cb
), t
);
9218 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
9219 G_CALLBACK(completion_hover_cb
), t
);
9227 if (stat(dir
, &sb
)) {
9228 if (mkdir(dir
, S_IRWXU
) == -1)
9229 err(1, "mkdir %s", dir
);
9231 err(1, "stat %s", dir
);
9233 if (S_ISDIR(sb
.st_mode
) == 0)
9234 errx(1, "%s not a dir", dir
);
9235 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
9236 warnx("fixing invalid permissions on %s", dir
);
9237 if (chmod(dir
, S_IRWXU
) == -1)
9238 err(1, "chmod %s", dir
);
9246 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
9252 main(int argc
, char *argv
[])
9255 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
9256 char conf
[PATH_MAX
] = { '\0' };
9257 char file
[PATH_MAX
];
9258 char *env_proxy
= NULL
;
9262 struct sigaction sact
;
9263 GIOChannel
*channel
;
9268 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
9270 /* fiddle with ulimits */
9271 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9274 /* just use them all */
9275 rlp
.rlim_cur
= rlp
.rlim_max
;
9276 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9278 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9280 else if (rlp
.rlim_cur
<= 256)
9281 warnx("%s requires at least 256 file descriptors",
9285 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
9294 errx(0 , "Version: %s", version
);
9297 strlcpy(conf
, optarg
, sizeof(conf
));
9300 strlcpy(named_session
, optarg
, sizeof(named_session
));
9321 RB_INIT(&downloads
);
9325 TAILQ_INIT(&aliases
);
9331 gnutls_global_init();
9333 /* generate session keys for xtp pages */
9334 generate_xtp_session_key(&dl_session_key
);
9335 generate_xtp_session_key(&hl_session_key
);
9336 generate_xtp_session_key(&cl_session_key
);
9337 generate_xtp_session_key(&fl_session_key
);
9340 gtk_init(&argc
, &argv
);
9341 if (!g_thread_supported())
9342 g_thread_init(NULL
);
9345 bzero(&sact
, sizeof(sact
));
9346 sigemptyset(&sact
.sa_mask
);
9347 sact
.sa_handler
= sigchild
;
9348 sact
.sa_flags
= SA_NOCLDSTOP
;
9349 sigaction(SIGCHLD
, &sact
, NULL
);
9351 /* set download dir */
9352 pwd
= getpwuid(getuid());
9354 errx(1, "invalid user %d", getuid());
9355 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
9357 /* compile buffer command regexes */
9360 /* set default string settings */
9361 home
= g_strdup("https://www.cyphertite.com");
9362 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
9363 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
9364 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
9365 cmd_font_name
= g_strdup("monospace normal 9");
9366 oops_font_name
= g_strdup("monospace normal 9");
9367 statusbar_font_name
= g_strdup("monospace normal 9");
9368 tabbar_font_name
= g_strdup("monospace normal 9");
9369 statusbar_elems
= g_strdup("BP");
9371 /* read config file */
9372 if (strlen(conf
) == 0)
9373 snprintf(conf
, sizeof conf
, "%s/.%s",
9374 pwd
->pw_dir
, XT_CONF_FILE
);
9375 config_parse(conf
, 0);
9378 cmd_font
= pango_font_description_from_string(cmd_font_name
);
9379 oops_font
= pango_font_description_from_string(oops_font_name
);
9380 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
9381 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
9383 /* working directory */
9384 if (strlen(work_dir
) == 0)
9385 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
9386 pwd
->pw_dir
, XT_DIR
);
9389 /* icon cache dir */
9390 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
9394 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
9398 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
9399 work_dir
, XT_SESSIONS_DIR
);
9400 xxx_dir(sessions_dir
);
9402 /* runtime settings that can override config file */
9403 if (runtime_settings
[0] != '\0')
9404 config_parse(runtime_settings
, 1);
9407 if (!strcmp(download_dir
, pwd
->pw_dir
))
9408 strlcat(download_dir
, "/downloads", sizeof download_dir
);
9409 xxx_dir(download_dir
);
9411 /* favorites file */
9412 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
9413 if (stat(file
, &sb
)) {
9414 warnx("favorites file doesn't exist, creating it");
9415 if ((f
= fopen(file
, "w")) == NULL
)
9416 err(1, "favorites");
9420 /* quickmarks file */
9421 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
9422 if (stat(file
, &sb
)) {
9423 warnx("quickmarks file doesn't exist, creating it");
9424 if ((f
= fopen(file
, "w")) == NULL
)
9425 err(1, "quickmarks");
9430 session
= webkit_get_default_session();
9435 if (stat(ssl_ca_file
, &sb
)) {
9436 warnx("no CA file: %s", ssl_ca_file
);
9437 g_free(ssl_ca_file
);
9440 g_object_set(session
,
9441 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
9442 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
9447 env_proxy
= getenv("http_proxy");
9449 setup_proxy(env_proxy
);
9451 setup_proxy(http_proxy
);
9454 send_cmd_to_socket(argv
[0]);
9458 /* set some connection parameters */
9459 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
9460 g_object_set(session
, "max-conns-per-host", max_host_connections
,
9463 /* see if there is already an xxxterm running */
9464 if (single_instance
&& is_running()) {
9466 warnx("already running");
9471 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
9472 send_cmd_to_socket(cmd
);
9482 /* uri completion */
9483 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
9486 buffers_store
= gtk_list_store_new
9487 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
9493 notebook_tab_set_visibility();
9495 if (save_global_history
)
9496 restore_global_history();
9498 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
9499 restore_saved_tabs();
9501 a
.s
= named_session
;
9502 a
.i
= XT_SES_DONOTHING
;
9503 open_tabs(NULL
, &a
);
9507 create_new_tab(argv
[0], NULL
, focus
, -1);
9514 if (TAILQ_EMPTY(&tabs
))
9515 create_new_tab(home
, NULL
, 1, -1);
9518 if ((s
= build_socket()) != -1) {
9519 channel
= g_io_channel_unix_new(s
);
9520 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
9525 gnutls_global_deinit();