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
;
5865 r
= load_compare_cert(t
, NULL
);
5867 col_str
= XT_COLOR_BLUE
;
5869 col_str
= XT_COLOR_YELLOW
;
5871 col_str
= XT_COLOR_RED
;
5876 gdk_color_parse(col_str
, &color
);
5877 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5879 if (!strcmp(col_str
, XT_COLOR_WHITE
))
5880 statusbar_modify_attr(t
, col_str
, XT_COLOR_BLACK
);
5882 statusbar_modify_attr(t
, XT_COLOR_BLACK
, col_str
);
5887 free_favicon(struct tab
*t
)
5889 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p\n",
5890 __func__
, t
->icon_download
, t
->icon_request
);
5892 if (t
->icon_request
)
5893 g_object_unref(t
->icon_request
);
5894 if (t
->icon_dest_uri
)
5895 g_free(t
->icon_dest_uri
);
5897 t
->icon_request
= NULL
;
5898 t
->icon_dest_uri
= NULL
;
5902 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5904 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5905 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5907 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5908 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5910 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5911 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5915 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pb
)
5917 GdkPixbuf
*pb_scaled
;
5919 if (gdk_pixbuf_get_width(pb
) > 16 || gdk_pixbuf_get_height(pb
) > 16)
5920 pb_scaled
= gdk_pixbuf_scale_simple(pb
, 16, 16,
5921 GDK_INTERP_BILINEAR
);
5925 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5926 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5928 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->sbe
.statusbar
),
5929 GTK_ENTRY_ICON_PRIMARY
, pb_scaled
);
5931 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->sbe
.statusbar
),
5932 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5934 if (pb_scaled
!= pb
)
5935 g_object_unref(pb_scaled
);
5939 xt_icon_from_file(struct tab
*t
, char *file
)
5943 if (g_str_has_prefix(file
, "file://"))
5944 file
+= strlen("file://");
5946 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5948 xt_icon_from_pixbuf(t
, pb
);
5951 xt_icon_from_name(t
, "text-html");
5955 is_valid_icon(char *file
)
5958 const char *mime_type
;
5962 gf
= g_file_new_for_path(file
);
5963 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
5965 mime_type
= g_file_info_get_content_type(fi
);
5966 valid
= g_strcmp0(mime_type
, "image/x-ico") == 0 ||
5967 g_strcmp0(mime_type
, "image/vnd.microsoft.icon") == 0 ||
5968 g_strcmp0(mime_type
, "image/png") == 0 ||
5969 g_strcmp0(mime_type
, "image/gif") == 0 ||
5970 g_strcmp0(mime_type
, "application/octet-stream") == 0;
5978 set_favicon_from_file(struct tab
*t
, char *file
)
5982 if (t
== NULL
|| file
== NULL
)
5985 if (g_str_has_prefix(file
, "file://"))
5986 file
+= strlen("file://");
5987 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5989 if (!stat(file
, &sb
)) {
5990 if (sb
.st_size
== 0 || !is_valid_icon(file
)) {
5991 /* corrupt icon so trash it */
5992 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5995 /* no need to set icon to default here */
5999 xt_icon_from_file(t
, file
);
6003 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6006 WebKitDownloadStatus status
= webkit_download_get_status(download
);
6007 struct tab
*tt
= NULL
, *t
= NULL
;
6010 * find the webview instead of passing in the tab as it could have been
6011 * deleted from underneath us.
6013 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6022 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
6023 __func__
, t
->tab_id
, status
);
6026 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
6028 t
->icon_download
= NULL
;
6031 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
6034 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
6037 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
6039 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
6040 __func__
, t
->tab_id
);
6041 t
->icon_download
= NULL
;
6044 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
6047 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
6048 __func__
, t
->icon_dest_uri
);
6049 set_favicon_from_file(t
, t
->icon_dest_uri
);
6050 /* these will be freed post callback */
6051 t
->icon_request
= NULL
;
6052 t
->icon_download
= NULL
;
6060 abort_favicon_download(struct tab
*t
)
6062 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
6064 #if !WEBKIT_CHECK_VERSION(1, 4, 0)
6065 if (t
->icon_download
) {
6066 g_signal_handlers_disconnect_by_func(G_OBJECT(t
->icon_download
),
6067 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6068 webkit_download_cancel(t
->icon_download
);
6069 t
->icon_download
= NULL
;
6074 xt_icon_from_name(t
, "text-html");
6078 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
6080 DNPRINTF(XT_D_DOWNLOAD
, "%s %s\n", __func__
, uri
);
6082 if (uri
== NULL
|| t
== NULL
)
6085 #if WEBKIT_CHECK_VERSION(1, 4, 0)
6086 /* take icon from WebKitIconDatabase */
6089 pb
= webkit_web_view_get_icon_pixbuf(wv
);
6091 xt_icon_from_pixbuf(t
, pb
);
6094 xt_icon_from_name(t
, "text-html");
6095 #elif WEBKIT_CHECK_VERSION(1, 1, 18)
6096 /* download icon to cache dir */
6097 gchar
*name_hash
, file
[PATH_MAX
];
6100 if (t
->icon_request
) {
6101 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
6105 /* check to see if we got the icon in cache */
6106 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
6107 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
6110 if (!stat(file
, &sb
)) {
6111 if (sb
.st_size
> 0) {
6112 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
6114 set_favicon_from_file(t
, file
);
6118 /* corrupt icon so trash it */
6119 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
6124 /* create download for icon */
6125 t
->icon_request
= webkit_network_request_new(uri
);
6126 if (t
->icon_request
== NULL
) {
6127 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
6132 t
->icon_download
= webkit_download_new(t
->icon_request
);
6133 if (t
->icon_download
== NULL
)
6136 /* we have to free icon_dest_uri later */
6137 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
6138 webkit_download_set_destination_uri(t
->icon_download
,
6141 if (webkit_download_get_status(t
->icon_download
) ==
6142 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6143 g_object_unref(t
->icon_request
);
6144 g_free(t
->icon_dest_uri
);
6145 t
->icon_request
= NULL
;
6146 t
->icon_dest_uri
= NULL
;
6150 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
6151 G_CALLBACK(favicon_download_status_changed_cb
), t
->wv
);
6153 webkit_download_start(t
->icon_download
);
6158 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6160 const gchar
*uri
= NULL
, *title
= NULL
;
6161 struct history
*h
, find
;
6164 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d %s\n",
6165 webkit_web_view_get_load_status(wview
),
6166 get_uri(t
) ? get_uri(t
) : "NOTHING");
6169 show_oops(NULL
, "notify_load_status_cb invalid parameters");
6173 switch (webkit_web_view_get_load_status(wview
)) {
6174 case WEBKIT_LOAD_PROVISIONAL
:
6176 abort_favicon_download(t
);
6177 #if GTK_CHECK_VERSION(2, 20, 0)
6178 gtk_widget_show(t
->spinner
);
6179 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
6181 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
6183 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
6185 /* take focus if we are visible */
6191 case WEBKIT_LOAD_COMMITTED
:
6196 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6202 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
6204 /* check if js white listing is enabled */
6205 if (enable_js_whitelist
) {
6206 check_and_set_js(uri
, t
);
6212 show_ca_status(t
, uri
);
6214 /* we know enough to autosave the session */
6215 if (session_autosave
) {
6221 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
6225 case WEBKIT_LOAD_FINISHED
:
6231 if (!strncmp(uri
, "http://", strlen("http://")) ||
6232 !strncmp(uri
, "https://", strlen("https://")) ||
6233 !strncmp(uri
, "file://", strlen("file://"))) {
6235 h
= RB_FIND(history_list
, &hl
, &find
);
6237 title
= get_title(t
, FALSE
);
6238 h
= g_malloc(sizeof *h
);
6239 h
->uri
= g_strdup(uri
);
6240 h
->title
= g_strdup(title
);
6241 RB_INSERT(history_list
, &hl
, h
);
6242 completion_add_uri(h
->uri
);
6243 update_history_tabs(NULL
);
6247 set_status(t
, (char *)uri
, XT_STATUS_URI
);
6248 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6249 case WEBKIT_LOAD_FAILED
:
6252 #if GTK_CHECK_VERSION(2, 20, 0)
6253 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6254 gtk_widget_hide(t
->spinner
);
6257 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
6262 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
6264 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
6265 webkit_web_view_can_go_back(wview
));
6267 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
6268 webkit_web_view_can_go_forward(wview
));
6272 notify_load_error_cb(WebKitWebView
* wview
, WebKitWebFrame
*web_frame
,
6273 gchar
*uri
, gpointer web_error
,struct tab
*t
)
6277 t
->tmp_uri
= g_strdup(uri
);
6278 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
6279 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
6280 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6281 set_status(t
, uri
, XT_STATUS_NOTHING
);
6287 notify_title_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
6289 const gchar
*title
= NULL
, *win_title
= NULL
;
6291 title
= get_title(t
, FALSE
);
6292 win_title
= get_title(t
, TRUE
);
6293 gtk_label_set_text(GTK_LABEL(t
->label
), title
);
6294 gtk_label_set_text(GTK_LABEL(t
->tab_elems
.label
), title
);
6295 if (t
->tab_id
== gtk_notebook_get_current_page(notebook
))
6296 gtk_window_set_title(GTK_WINDOW(main_window
), win_title
);
6300 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6302 run_script(t
, JS_HINTING
);
6306 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
6308 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
6309 progress
== 100 ? 0 : (double)progress
/ 100);
6310 if (show_url
== 0) {
6311 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->sbe
.statusbar
),
6312 progress
== 100 ? 0 : (double)progress
/ 100);
6315 update_statusbar_position(NULL
, NULL
);
6319 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
6320 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
6321 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
6324 WebKitWebNavigationReason reason
;
6325 struct domain
*d
= NULL
;
6328 show_oops(NULL
, "webview_npd_cb invalid parameters");
6332 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
6334 webkit_network_request_get_uri(request
));
6336 uri
= (char *)webkit_network_request_get_uri(request
);
6338 /* if this is an xtp url, we don't load anything else */
6339 if (parse_xtp_url(t
, uri
))
6342 if (t
->ctrl_click
) {
6344 create_new_tab(uri
, NULL
, ctrl_click_focus
, -1);
6345 webkit_web_policy_decision_ignore(pd
);
6346 return (TRUE
); /* we made the decission */
6350 * This is a little hairy but it comes down to this:
6351 * when we run in whitelist mode we have to assist the browser in
6352 * opening the URL that it would have opened in a new tab.
6354 reason
= webkit_web_navigation_action_get_reason(na
);
6355 if (reason
== WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED
) {
6356 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6357 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1)
6358 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6360 webkit_web_policy_decision_use(pd
);
6361 return (TRUE
); /* we made the decision */
6368 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
6371 struct domain
*d
= NULL
;
6373 WebKitWebView
*webview
= NULL
;
6375 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
6376 webkit_web_view_get_uri(wv
));
6379 /* open in current tab */
6381 } else if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6382 uri
= webkit_web_view_get_uri(wv
);
6383 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6386 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6388 } else if (enable_scripts
== 1) {
6389 tt
= create_new_tab(NULL
, NULL
, 1, -1);
6397 webview_closewv_cb(WebKitWebView
*wv
, struct tab
*t
)
6400 struct domain
*d
= NULL
;
6402 DNPRINTF(XT_D_NAV
, "webview_close_cb: %d\n", t
->tab_id
);
6404 if (enable_scripts
== 0 && enable_cookie_whitelist
== 1) {
6405 uri
= webkit_web_view_get_uri(wv
);
6406 if (uri
&& (d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
6410 } else if (enable_scripts
== 1)
6417 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
6419 /* we can not eat the event without throwing gtk off so defer it */
6421 /* catch middle click */
6422 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
6427 /* catch ctrl click */
6428 if (e
->type
== GDK_BUTTON_RELEASE
&&
6429 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
6434 return (XT_CB_PASSTHROUGH
);
6438 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
6440 struct mime_type
*m
;
6442 m
= find_mime_type(mime_type
);
6450 show_oops(t
, "can't fork mime handler");
6460 execlp(m
->mt_action
, m
->mt_action
,
6461 webkit_network_request_get_uri(request
), (void *)NULL
);
6470 get_mime_type(char *file
)
6472 const char *mime_type
;
6476 if (g_str_has_prefix(file
, "file://"))
6477 file
+= strlen("file://");
6479 gf
= g_file_new_for_path(file
);
6480 fi
= g_file_query_info(gf
, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
, 0,
6482 mime_type
= g_file_info_get_content_type(fi
);
6490 run_download_mimehandler(char *mime_type
, char *file
)
6492 struct mime_type
*m
;
6494 m
= find_mime_type(mime_type
);
6500 show_oops(NULL
, "can't fork download mime handler");
6510 if (g_str_has_prefix(file
, "file://"))
6511 file
+= strlen("file://");
6512 execlp(m
->mt_action
, m
->mt_action
, file
, (void *)NULL
);
6521 download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
6524 WebKitDownloadStatus status
;
6525 const gchar
*file
= NULL
, *mime
= NULL
;
6527 if (download
== NULL
)
6529 status
= webkit_download_get_status(download
);
6530 if (status
!= WEBKIT_DOWNLOAD_STATUS_FINISHED
)
6533 file
= webkit_download_get_destination_uri(download
);
6536 mime
= get_mime_type((char *)file
);
6540 run_download_mimehandler((char *)mime
, (char *)file
);
6544 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
6545 WebKitNetworkRequest
*request
, char *mime_type
,
6546 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
6549 show_oops(NULL
, "webview_mimetype_cb invalid parameters");
6553 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
6554 t
->tab_id
, mime_type
);
6556 if (run_mimehandler(t
, mime_type
, request
) == 0) {
6557 webkit_web_policy_decision_ignore(decision
);
6562 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
6563 webkit_web_policy_decision_download(decision
);
6571 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
6575 const gchar
*suggested_name
;
6576 gchar
*filename
= NULL
;
6578 struct download
*download_entry
;
6581 if (wk_download
== NULL
|| t
== NULL
) {
6582 show_oops(NULL
, "%s invalid parameters", __func__
);
6586 suggested_name
= webkit_download_get_suggested_filename(wk_download
);
6587 if (suggested_name
== NULL
)
6588 return (FALSE
); /* abort download */
6599 filename
= g_strdup_printf("%d%s", i
, suggested_name
);
6601 uri
= g_strdup_printf("file://%s/%s", download_dir
, i
?
6602 filename
: suggested_name
);
6604 } while (!stat(uri
+ strlen("file://"), &sb
));
6606 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
6607 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
6609 webkit_download_set_destination_uri(wk_download
, uri
);
6611 if (webkit_download_get_status(wk_download
) ==
6612 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
6613 show_oops(t
, "%s: download failed to start", __func__
);
6615 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
6617 /* connect "download first" mime handler */
6618 g_signal_connect(G_OBJECT(wk_download
), "notify::status",
6619 G_CALLBACK(download_status_changed_cb
), NULL
);
6621 download_entry
= g_malloc(sizeof(struct download
));
6622 download_entry
->download
= wk_download
;
6623 download_entry
->tab
= t
;
6624 download_entry
->id
= next_download_id
++;
6625 RB_INSERT(download_list
, &downloads
, download_entry
);
6626 /* get from history */
6627 g_object_ref(wk_download
);
6628 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
6629 show_oops(t
, "Download of '%s' started...",
6630 basename((char *)webkit_download_get_destination_uri(wk_download
)));
6639 /* sync other download manager tabs */
6640 update_download_tabs(NULL
);
6643 * NOTE: never redirect/render the current tab before this
6644 * function returns. This will cause the download to never start.
6646 return (ret
); /* start download */
6650 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
6652 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
6655 show_oops(NULL
, "webview_hover_cb");
6660 set_status(t
, uri
, XT_STATUS_LINK
);
6663 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
6668 mark(struct tab
*t
, struct karg
*arg
)
6674 if ((index
= marktoindex(mark
)) == -1)
6677 if (arg
->i
== XT_MARK_SET
)
6678 t
->mark
[index
] = gtk_adjustment_get_value(t
->adjust_v
);
6679 else if (arg
->i
== XT_MARK_GOTO
) {
6680 if (t
->mark
[index
] == XT_INVALID_MARK
) {
6681 show_oops(t
, "mark '%c' does not exist", mark
);
6684 /* XXX t->mark[index] can be bigger than the maximum if ajax or
6685 something changes the document size */
6686 gtk_adjustment_set_value(t
->adjust_v
, t
->mark
[index
]);
6693 marks_clear(struct tab
*t
)
6697 for (i
= 0; i
< LENGTH(t
->mark
); i
++)
6698 t
->mark
[i
] = XT_INVALID_MARK
;
6704 char file
[PATH_MAX
];
6705 char *line
= NULL
, *p
, mark
;
6710 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
6711 if ((f
= fopen(file
, "r+")) == NULL
) {
6712 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
6716 for (i
= 1; ; i
++) {
6717 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
6718 if (feof(f
) || ferror(f
))
6720 if (strlen(line
) == 0 || line
[0] == '#') {
6726 p
= strtok(line
, " \t");
6728 if (p
== NULL
|| strlen(p
) != 1 ||
6729 (index
= marktoindex(*p
)) == -1) {
6730 warnx("corrupt quickmarks file, line %d", i
);
6735 p
= strtok(NULL
, " \t");
6736 if (qmarks
[index
] != NULL
)
6737 g_free(qmarks
[index
]);
6738 qmarks
[index
] = g_strdup(p
);
6749 char file
[PATH_MAX
];
6753 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
6754 if ((f
= fopen(file
, "r+")) == NULL
) {
6755 show_oops(NULL
, "Can't open quickmarks file: %s", strerror(errno
));
6759 for (i
= 0; i
< XT_NOMARKS
; i
++)
6760 if (qmarks
[i
] != NULL
)
6761 fprintf(f
, "%c %s\n", indextomark(i
), qmarks
[i
]);
6769 qmark(struct tab
*t
, struct karg
*arg
)
6774 mark
= arg
->s
[strlen(arg
->s
)-1];
6775 index
= marktoindex(mark
);
6781 if (qmarks
[index
] != NULL
)
6782 g_free(qmarks
[index
]);
6784 qmarks_load(); /* sync if multiple instances */
6785 qmarks
[index
] = g_strdup(get_uri(t
));
6789 if (qmarks
[index
] != NULL
)
6790 load_uri(t
, qmarks
[index
]);
6792 show_oops(t
, "quickmark \"%c\" does not exist",
6798 if (qmarks
[index
] != NULL
)
6799 create_new_tab(qmarks
[index
], NULL
, 1, -1);
6801 show_oops(t
, "quickmark \"%c\" does not exist",
6812 go_up(struct tab
*t
, struct karg
*args
)
6818 levels
= atoi(args
->s
);
6822 uri
= g_strdup(webkit_web_view_get_uri(t
->wv
));
6823 if ((tmp
= strstr(uri
, XT_PROTO_DELIM
)) == NULL
)
6825 tmp
+= strlen(XT_PROTO_DELIM
);
6827 /* if an uri starts with a slash, leave it alone (for file:///) */
6834 p
= strrchr(tmp
, '/');
6848 gototab(struct tab
*t
, struct karg
*args
)
6851 struct karg arg
= {0, NULL
, -1};
6853 tab
= atoi(args
->s
);
6855 arg
.i
= XT_TAB_NEXT
;
6864 zoom_amount(struct tab
*t
, struct karg
*arg
)
6866 struct karg narg
= {0, NULL
, -1};
6868 narg
.i
= atoi(arg
->s
);
6869 resizetab(t
, &narg
);
6874 /* buffer commands receive the regex that triggered them in arg.s */
6878 int (*func
)(struct tab
*, struct karg
*);
6882 { "^[0-9]*gu$", go_up
, 0 },
6883 { "^gg$", move
, XT_MOVE_TOP
},
6884 { "^gG$", move
, XT_MOVE_BOTTOM
},
6885 { "^[0-9]+%$", move
, XT_MOVE_PERCENT
},
6886 { "^gh$", go_home
, 0 },
6887 { "^m[a-zA-Z0-9]$", mark
, XT_MARK_SET
},
6888 { "^[`'][a-zA-Z0-9]$", mark
, XT_MARK_GOTO
},
6889 { "^[0-9]+t$", gototab
, 0 },
6890 { "^M[a-zA-Z0-9]$", qmark
, XT_QMARK_SET
},
6891 { "^go[a-zA-Z0-9]$", qmark
, XT_QMARK_OPEN
},
6892 { "^gn[a-zA-Z0-9]$", qmark
, XT_QMARK_TAB
},
6893 { "^ZR$", restart
, 0 },
6894 { "^ZZ$", quit
, 0 },
6895 { "^zi$", resizetab
, XT_ZOOM_IN
},
6896 { "^zo$", resizetab
, XT_ZOOM_OUT
},
6897 { "^z0$", resizetab
, XT_ZOOM_NORMAL
},
6898 { "^[0-9]+Z$", zoom_amount
, 0 },
6902 buffercmd_init(void)
6906 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
6907 regcomp(&buffercmds
[i
].cregex
, buffercmds
[i
].regex
,
6912 buffercmd_abort(struct tab
*t
)
6916 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_abort: clearing buffer\n");
6917 for (i
= 0; i
< LENGTH(bcmd
); i
++)
6920 cmd_prefix
= 0; /* clear prefix for non-buffer commands */
6921 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
6925 buffercmd_execute(struct tab
*t
, struct buffercmd
*cmd
)
6927 struct karg arg
= {0, NULL
, -1};
6930 arg
.s
= g_strdup(bcmd
);
6932 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_execute: buffer \"%s\" "
6933 "matches regex \"%s\", executing\n", bcmd
, cmd
->regex
);
6943 buffercmd_addkey(struct tab
*t
, guint keyval
)
6947 if (keyval
== GDK_Escape
) {
6949 return (XT_CB_HANDLED
);
6952 /* key with modifier or non-ascii character */
6953 if (!isascii(keyval
))
6954 return (XT_CB_PASSTHROUGH
);
6956 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: adding key \"%c\" "
6957 "to buffer \"%s\"\n", keyval
, bcmd
);
6959 for (i
= 0; i
< LENGTH(bcmd
); i
++)
6960 if (bcmd
[i
] == '\0') {
6965 /* buffer full, ignore input */
6966 if (i
== LENGTH(bcmd
)) {
6967 DNPRINTF(XT_D_BUFFERCMD
, "buffercmd_addkey: buffer full\n");
6968 return (XT_CB_HANDLED
);
6971 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.buffercmd
), bcmd
);
6973 for (i
= 0; i
< LENGTH(buffercmds
); i
++)
6974 if (regexec(&buffercmds
[i
].cregex
, bcmd
,
6975 (size_t) 0, NULL
, 0) == 0) {
6976 buffercmd_execute(t
, &buffercmds
[i
]);
6980 return (XT_CB_HANDLED
);
6984 handle_keypress(struct tab
*t
, GdkEventKey
*e
, int entry
)
6986 struct key_binding
*k
;
6988 /* handle keybindings if buffercmd is empty.
6989 if not empty, allow commands like C-n */
6990 if (bcmd
[0] == '\0' || ((e
->state
& (CTRL
| MOD1
)) != 0))
6991 TAILQ_FOREACH(k
, &kbl
, entry
)
6992 if (e
->keyval
== k
->key
6993 && (entry
? k
->use_in_entry
: 1)) {
6995 if ((e
->state
& (CTRL
| MOD1
)) == 0)
6996 return (cmd_execute(t
, k
->cmd
));
6997 } else if ((e
->state
& k
->mask
) == k
->mask
) {
6998 return (cmd_execute(t
, k
->cmd
));
7002 if (!entry
&& ((e
->state
& (CTRL
| MOD1
)) == 0))
7003 return buffercmd_addkey(t
, e
->keyval
);
7005 return (XT_CB_PASSTHROUGH
);
7009 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
7011 char s
[2], buf
[128];
7012 const char *errstr
= NULL
;
7014 /* don't use w directly; use t->whatever instead */
7017 show_oops(NULL
, "wv_keypress_after_cb");
7018 return (XT_CB_PASSTHROUGH
);
7021 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
7022 e
->keyval
, e
->state
, t
);
7026 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7028 return (XT_CB_HANDLED
);
7032 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
7034 /* we have a string */
7036 /* we have a number */
7037 snprintf(buf
, sizeof buf
,
7038 "vimprobable_fire(%s)", t
->hint_num
);
7045 /* XXX unfuck this */
7046 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
7047 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
7048 /* last input was numerical */
7050 l
= strlen(t
->hint_num
);
7057 t
->hint_num
[l
] = '\0';
7061 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
7062 /* last input was alphanumerical */
7064 l
= strlen(t
->hint_buf
);
7071 t
->hint_buf
[l
] = '\0';
7081 /* numerical input */
7082 if (CLEAN(e
->state
) == 0 &&
7083 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
7084 (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
7085 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7086 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
7087 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: num %s\n",
7091 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: "
7092 "invalid link number\n");
7095 snprintf(buf
, sizeof buf
,
7096 "vimprobable_update_hints(%s)",
7098 t
->hint_mode
= XT_HINT_NUMERICAL
;
7102 /* empty the counter buffer */
7103 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
7104 return (XT_CB_HANDLED
);
7107 /* alphanumerical input */
7108 if ((CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&&
7109 e
->keyval
<= GDK_z
) ||
7110 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&&
7111 e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
7112 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&&
7113 e
->keyval
<= GDK_9
) ||
7114 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) &&
7115 (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
7116 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7117 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
7118 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical"
7119 " %s\n", t
->hint_buf
);
7121 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
7124 snprintf(buf
, sizeof buf
,
7125 "vimprobable_show_hints('%s')", t
->hint_buf
);
7126 t
->hint_mode
= XT_HINT_ALPHANUM
;
7129 /* empty the counter buffer */
7130 bzero(t
->hint_num
, sizeof t
->hint_num
);
7131 return (XT_CB_HANDLED
);
7134 return (XT_CB_HANDLED
);
7137 snprintf(s
, sizeof s
, "%c", e
->keyval
);
7138 if (CLEAN(e
->state
) == 0 && isdigit(s
[0]))
7139 cmd_prefix
= 10 * cmd_prefix
+ atoi(s
);
7142 return (handle_keypress(t
, e
, 0));
7146 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7150 /* Hide buffers, if they are visible, with escape. */
7151 if (gtk_widget_get_visible(GTK_WIDGET(t
->buffers
)) &&
7152 CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
7153 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7155 return (XT_CB_HANDLED
);
7158 return (XT_CB_PASSTHROUGH
);
7162 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7164 const gchar
*c
= gtk_entry_get_text(w
);
7168 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7169 e
->keyval
, e
->state
, t
);
7172 show_oops(NULL
, "cmd_keyrelease_cb invalid parameters");
7173 return (XT_CB_PASSTHROUGH
);
7176 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
7177 e
->keyval
, e
->state
, t
);
7181 if (strlen(c
) == 1) {
7182 webkit_web_view_unmark_text_matches(t
->wv
);
7188 else if (c
[0] == '?')
7194 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
7196 /* not found, mark red */
7197 gdk_color_parse(XT_COLOR_RED
, &color
);
7198 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7199 /* unmark and remove selection */
7200 webkit_web_view_unmark_text_matches(t
->wv
);
7201 /* my kingdom for a way to unselect text in webview */
7203 /* found, highlight all */
7204 webkit_web_view_unmark_text_matches(t
->wv
);
7205 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
7206 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
7207 gdk_color_parse(XT_COLOR_WHITE
, &color
);
7208 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
7211 return (XT_CB_PASSTHROUGH
);
7215 match_uri(const gchar
*uri
, const gchar
*key
) {
7218 gboolean match
= FALSE
;
7222 if (!strncmp(key
, uri
, len
))
7225 voffset
= strstr(uri
, "/") + 2;
7226 if (!strncmp(key
, voffset
, len
))
7228 else if (g_str_has_prefix(voffset
, "www.")) {
7229 voffset
= voffset
+ strlen("www.");
7230 if (!strncmp(key
, voffset
, len
))
7239 cmd_getlist(int id
, char *key
)
7244 if (id
>= 0 && (cmds
[id
].type
& XT_URLARG
)) {
7245 RB_FOREACH_REVERSE(h
, history_list
, &hl
)
7246 if (match_uri(h
->uri
, key
)) {
7247 cmd_status
.list
[c
] = (char *)h
->uri
;
7256 dep
= (id
== -1) ? 0 : cmds
[id
].level
+ 1;
7258 for (i
= id
+ 1; i
< LENGTH(cmds
); i
++) {
7259 if (cmds
[i
].level
< dep
)
7261 if (cmds
[i
].level
== dep
&& !strncmp(key
, cmds
[i
].cmd
,
7263 cmd_status
.list
[c
++] = cmds
[i
].cmd
;
7271 cmd_getnext(int dir
)
7273 cmd_status
.index
+= dir
;
7275 if (cmd_status
.index
< 0)
7276 cmd_status
.index
= cmd_status
.len
- 1;
7277 else if (cmd_status
.index
>= cmd_status
.len
)
7278 cmd_status
.index
= 0;
7280 return cmd_status
.list
[cmd_status
.index
];
7284 cmd_tokenize(char *s
, char *tokens
[])
7288 size_t len
= strlen(s
);
7291 blank
= len
== 0 || (len
> 0 && s
[len
- 1] == ' ');
7292 for (tok
= strtok_r(s
, " ", &last
); tok
&& i
< 3;
7293 tok
= strtok_r(NULL
, " ", &last
), i
++)
7303 cmd_complete(struct tab
*t
, char *str
, int dir
)
7305 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
7306 int i
, j
, levels
, c
= 0, dep
= 0, parent
= -1;
7308 char *tok
, *match
, *s
= g_strdup(str
);
7310 char res
[XT_MAX_URL_LENGTH
+ 32] = ":";
7313 DNPRINTF(XT_D_CMD
, "%s: complete %s\n", __func__
, str
);
7316 for (i
= 0; isdigit(s
[i
]); i
++)
7319 for (; isspace(s
[i
]); i
++)
7324 levels
= cmd_tokenize(s
, tokens
);
7326 for (i
= 0; i
< levels
- 1; i
++) {
7329 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7330 if (cmds
[j
].level
< dep
)
7332 if (cmds
[j
].level
== dep
&& !strncmp(tok
, cmds
[j
].cmd
,
7336 if (strlen(tok
) == strlen(cmds
[j
].cmd
)) {
7343 if (matchcount
== 1) {
7344 strlcat(res
, tok
, sizeof res
);
7345 strlcat(res
, " ", sizeof res
);
7355 if (cmd_status
.index
== -1)
7356 cmd_getlist(parent
, tokens
[i
]);
7358 if (cmd_status
.len
> 0) {
7359 match
= cmd_getnext(dir
);
7360 strlcat(res
, match
, sizeof res
);
7361 gtk_entry_set_text(w
, res
);
7362 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
7369 cmd_execute(struct tab
*t
, char *str
)
7371 struct cmd
*cmd
= NULL
;
7372 char *tok
, *last
, *s
= g_strdup(str
), *sc
;
7374 int j
, len
, c
= 0, dep
= 0, matchcount
= 0;
7375 int prefix
= -1, rv
= XT_CB_PASSTHROUGH
;
7376 struct karg arg
= {0, NULL
, -1};
7381 for (j
= 0; j
<3 && isdigit(s
[j
]); j
++)
7387 while (isspace(s
[0]))
7390 if (strlen(s
) > 0 && strlen(prefixstr
) > 0)
7391 prefix
= atoi(prefixstr
);
7395 for (tok
= strtok_r(s
, " ", &last
); tok
;
7396 tok
= strtok_r(NULL
, " ", &last
)) {
7398 for (j
= c
; j
< LENGTH(cmds
); j
++) {
7399 if (cmds
[j
].level
< dep
)
7401 len
= (tok
[strlen(tok
) - 1] == '!') ? strlen(tok
) - 1 :
7403 if (cmds
[j
].level
== dep
&&
7404 !strncmp(tok
, cmds
[j
].cmd
, len
)) {
7408 if (len
== strlen(cmds
[j
].cmd
)) {
7414 if (matchcount
== 1) {
7419 show_oops(t
, "Invalid command: %s", str
);
7428 else if (cmd_prefix
> 0)
7431 if (j
> 0 && !(cmd
->type
& XT_PREFIX
) && arg
.p
> -1) {
7432 show_oops(t
, "No prefix allowed: %s", str
);
7436 arg
.s
= last
? g_strdup(last
) : g_strdup("");
7437 if (cmd
->type
& XT_INTARG
&& last
&& strlen(last
) > 0) {
7438 arg
.p
= atoi(arg
.s
);
7441 show_oops(t
, "Zero count");
7443 show_oops(t
, "Trailing characters");
7448 DNPRINTF(XT_D_CMD
, "%s: prefix %d arg %s\n", __func__
, arg
.p
, arg
.s
);
7464 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7467 show_oops(NULL
, "entry_key_cb invalid parameters");
7468 return (XT_CB_PASSTHROUGH
);
7471 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
7472 e
->keyval
, e
->state
, t
);
7476 if (e
->keyval
== GDK_Escape
) {
7477 /* don't use focus_webview(t) because we want to type :cmds */
7478 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7481 return (handle_keypress(t
, e
, 1));
7485 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
7487 int rv
= XT_CB_HANDLED
;
7488 const gchar
*c
= gtk_entry_get_text(w
);
7491 show_oops(NULL
, "cmd_keypress_cb parameters");
7492 return (XT_CB_PASSTHROUGH
);
7495 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
7496 e
->keyval
, e
->state
, t
);
7500 e
->keyval
= GDK_Escape
;
7501 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7502 e
->keyval
= GDK_Escape
;
7504 if (e
->keyval
!= GDK_Tab
&& e
->keyval
!= GDK_Shift_L
&&
7505 e
->keyval
!= GDK_ISO_Left_Tab
)
7506 cmd_status
.index
= -1;
7508 switch (e
->keyval
) {
7511 cmd_complete(t
, (char *)&c
[1], 1);
7513 case GDK_ISO_Left_Tab
:
7515 cmd_complete(t
, (char *)&c
[1], -1);
7519 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
7527 if (c
!= NULL
&& (c
[0] == '/' || c
[0] == '?'))
7528 webkit_web_view_unmark_text_matches(t
->wv
);
7532 rv
= XT_CB_PASSTHROUGH
;
7538 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
7541 show_oops(NULL
, "cmd_focusout_cb invalid parameters");
7542 return (XT_CB_PASSTHROUGH
);
7544 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
7549 if (show_url
== 0 || t
->focus_wv
)
7552 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
7554 return (XT_CB_PASSTHROUGH
);
7558 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
7561 const gchar
*c
= gtk_entry_get_text(entry
);
7564 show_oops(NULL
, "cmd_activate_cb invalid parameters");
7568 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
7575 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
7581 if (c
[0] == '/' || c
[0] == '?') {
7582 if (t
->search_text
) {
7583 g_free(t
->search_text
);
7584 t
->search_text
= NULL
;
7587 t
->search_text
= g_strdup(s
);
7589 g_free(global_search
);
7590 global_search
= g_strdup(s
);
7591 t
->search_forward
= c
[0] == '/';
7603 backward_cb(GtkWidget
*w
, struct tab
*t
)
7608 show_oops(NULL
, "backward_cb invalid parameters");
7612 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
7619 forward_cb(GtkWidget
*w
, struct tab
*t
)
7624 show_oops(NULL
, "forward_cb invalid parameters");
7628 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
7630 a
.i
= XT_NAV_FORWARD
;
7635 home_cb(GtkWidget
*w
, struct tab
*t
)
7638 show_oops(NULL
, "home_cb invalid parameters");
7642 DNPRINTF(XT_D_NAV
, "home_cb: tab %d\n", t
->tab_id
);
7648 stop_cb(GtkWidget
*w
, struct tab
*t
)
7650 WebKitWebFrame
*frame
;
7653 show_oops(NULL
, "stop_cb invalid parameters");
7657 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
7659 frame
= webkit_web_view_get_main_frame(t
->wv
);
7660 if (frame
== NULL
) {
7661 show_oops(t
, "stop_cb: no frame");
7665 webkit_web_frame_stop_loading(frame
);
7666 abort_favicon_download(t
);
7670 setup_webkit(struct tab
*t
)
7672 if (is_g_object_setting(G_OBJECT(t
->settings
), "enable-dns-prefetching"))
7673 g_object_set(G_OBJECT(t
->settings
), "enable-dns-prefetching",
7674 FALSE
, (char *)NULL
);
7676 warnx("webkit does not have \"enable-dns-prefetching\" property");
7677 g_object_set(G_OBJECT(t
->settings
),
7678 "user-agent", t
->user_agent
, (char *)NULL
);
7679 g_object_set(G_OBJECT(t
->settings
),
7680 "enable-scripts", enable_scripts
, (char *)NULL
);
7681 g_object_set(G_OBJECT(t
->settings
),
7682 "enable-plugins", enable_plugins
, (char *)NULL
);
7683 g_object_set(G_OBJECT(t
->settings
),
7684 "javascript-can-open-windows-automatically", enable_scripts
,
7686 g_object_set(G_OBJECT(t
->settings
),
7687 "enable-html5-database", FALSE
, (char *)NULL
);
7688 g_object_set(G_OBJECT(t
->settings
),
7689 "enable-html5-local-storage", enable_localstorage
, (char *)NULL
);
7690 g_object_set(G_OBJECT(t
->settings
),
7691 "enable_spell_checking", enable_spell_checking
, (char *)NULL
);
7692 g_object_set(G_OBJECT(t
->settings
),
7693 "spell_checking_languages", spell_check_languages
, (char *)NULL
);
7694 g_object_set(G_OBJECT(t
->wv
),
7695 "full-content-zoom", TRUE
, (char *)NULL
);
7697 webkit_web_view_set_settings(t
->wv
, t
->settings
);
7701 update_statusbar_position(GtkAdjustment
* adjustment
, gpointer data
)
7703 struct tab
*ti
, *t
= NULL
;
7704 gdouble view_size
, value
, max
;
7707 TAILQ_FOREACH(ti
, &tabs
, entry
)
7708 if (ti
->tab_id
== gtk_notebook_get_current_page(notebook
)) {
7716 if (adjustment
== NULL
)
7717 adjustment
= gtk_scrolled_window_get_vadjustment(
7718 GTK_SCROLLED_WINDOW(t
->browser_win
));
7720 view_size
= gtk_adjustment_get_page_size(adjustment
);
7721 value
= gtk_adjustment_get_value(adjustment
);
7722 max
= gtk_adjustment_get_upper(adjustment
) - view_size
;
7725 position
= g_strdup("All");
7726 else if (value
== max
)
7727 position
= g_strdup("Bot");
7728 else if (value
== 0)
7729 position
= g_strdup("Top");
7731 position
= g_strdup_printf("%d%%", (int) ((value
/ max
) * 100));
7733 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.position
), position
);
7740 create_browser(struct tab
*t
)
7744 GtkAdjustment
*adjustment
;
7747 show_oops(NULL
, "create_browser invalid parameters");
7751 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
7752 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
7753 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
7754 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
7756 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
7757 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
7758 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
7760 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
7761 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
7764 t
->settings
= webkit_web_settings_new();
7766 if (user_agent
== NULL
) {
7767 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
7769 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
7772 t
->user_agent
= g_strdup(user_agent
);
7774 t
->stylesheet
= g_strdup_printf("file://%s/style.css", resource_dir
);
7777 gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(w
));
7778 g_signal_connect(G_OBJECT(adjustment
), "value-changed",
7779 G_CALLBACK(update_statusbar_position
), NULL
);
7791 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
7792 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
7793 gtk_widget_set_name(w
, "xxxterm");
7794 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
7795 g_signal_connect(G_OBJECT(w
), "delete_event",
7796 G_CALLBACK (gtk_main_quit
), NULL
);
7802 create_kiosk_toolbar(struct tab
*t
)
7804 GtkWidget
*toolbar
= NULL
, *b
;
7806 b
= gtk_hbox_new(FALSE
, 0);
7808 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7810 /* backward button */
7811 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7812 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7813 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7814 G_CALLBACK(backward_cb
), t
);
7815 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, TRUE
, TRUE
, 0);
7817 /* forward button */
7818 t
->forward
= create_button("Forward", GTK_STOCK_GO_FORWARD
, 0);
7819 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7820 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7821 G_CALLBACK(forward_cb
), t
);
7822 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, TRUE
, TRUE
, 0);
7825 t
->gohome
= create_button("Home", GTK_STOCK_HOME
, 0);
7826 gtk_widget_set_sensitive(t
->gohome
, true);
7827 g_signal_connect(G_OBJECT(t
->gohome
), "clicked",
7828 G_CALLBACK(home_cb
), t
);
7829 gtk_box_pack_start(GTK_BOX(b
), t
->gohome
, TRUE
, TRUE
, 0);
7831 /* create widgets but don't use them */
7832 t
->uri_entry
= gtk_entry_new();
7833 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7834 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7835 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7841 create_toolbar(struct tab
*t
)
7843 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
7845 b
= gtk_hbox_new(FALSE
, 0);
7847 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
7849 /* backward button */
7850 t
->backward
= create_button("Back", GTK_STOCK_GO_BACK
, 0);
7851 gtk_widget_set_sensitive(t
->backward
, FALSE
);
7852 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
7853 G_CALLBACK(backward_cb
), t
);
7854 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
7856 /* forward button */
7857 t
->forward
= create_button("Forward",GTK_STOCK_GO_FORWARD
, 0);
7858 gtk_widget_set_sensitive(t
->forward
, FALSE
);
7859 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
7860 G_CALLBACK(forward_cb
), t
);
7861 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
7865 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
7866 gtk_widget_set_sensitive(t
->stop
, FALSE
);
7867 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
7868 G_CALLBACK(stop_cb
), t
);
7869 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
7873 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
7874 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
7875 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
7876 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
7877 G_CALLBACK(js_toggle_cb
), t
);
7878 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
7880 t
->uri_entry
= gtk_entry_new();
7881 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
7882 G_CALLBACK(activate_uri_entry_cb
), t
);
7883 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
7884 G_CALLBACK(entry_key_cb
), t
);
7886 eb1
= gtk_hbox_new(FALSE
, 0);
7887 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
7888 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
7889 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
7892 if (search_string
) {
7894 t
->search_entry
= gtk_entry_new();
7895 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
7896 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
7897 G_CALLBACK(activate_search_entry_cb
), t
);
7898 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
7899 G_CALLBACK(entry_key_cb
), t
);
7900 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
7901 eb2
= gtk_hbox_new(FALSE
, 0);
7902 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
7903 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
7905 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
7912 create_buffers(struct tab
*t
)
7914 GtkCellRenderer
*renderer
;
7917 view
= gtk_tree_view_new();
7919 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view
), FALSE
);
7921 renderer
= gtk_cell_renderer_text_new();
7922 gtk_tree_view_insert_column_with_attributes
7923 (GTK_TREE_VIEW(view
), -1, "Id", renderer
, "text", COL_ID
, NULL
);
7925 renderer
= gtk_cell_renderer_text_new();
7926 gtk_tree_view_insert_column_with_attributes
7927 (GTK_TREE_VIEW(view
), -1, "Title", renderer
, "text", COL_TITLE
,
7930 gtk_tree_view_set_model
7931 (GTK_TREE_VIEW(view
), GTK_TREE_MODEL(buffers_store
));
7937 row_activated_cb(GtkTreeView
*view
, GtkTreePath
*path
,
7938 GtkTreeViewColumn
*col
, struct tab
*t
)
7943 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
7945 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(buffers_store
), &iter
,
7948 (GTK_TREE_MODEL(buffers_store
), &iter
, COL_ID
, &id
, -1);
7949 set_current_tab(id
- 1);
7955 /* after tab reordering/creation/removal */
7962 TAILQ_FOREACH(t
, &tabs
, entry
) {
7963 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
7964 if (t
->tab_id
> maxid
)
7967 gtk_widget_show(t
->tab_elems
.sep
);
7970 TAILQ_FOREACH(t
, &tabs
, entry
) {
7971 if (t
->tab_id
== maxid
) {
7972 gtk_widget_hide(t
->tab_elems
.sep
);
7978 /* after active tab change */
7980 recolor_compact_tabs(void)
7986 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
7987 TAILQ_FOREACH(t
, &tabs
, entry
)
7988 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
,
7991 curid
= gtk_notebook_get_current_page(notebook
);
7992 TAILQ_FOREACH(t
, &tabs
, entry
)
7993 if (t
->tab_id
== curid
) {
7994 gdk_color_parse(XT_COLOR_CT_ACTIVE
, &color
);
7995 gtk_widget_modify_fg(t
->tab_elems
.label
,
7996 GTK_STATE_NORMAL
, &color
);
8002 set_current_tab(int page_num
)
8004 buffercmd_abort(get_current_tab());
8005 gtk_notebook_set_current_page(notebook
, page_num
);
8006 recolor_compact_tabs();
8010 undo_close_tab_save(struct tab
*t
)
8014 struct undo
*u1
, *u2
;
8016 WebKitWebHistoryItem
*item
;
8018 if ((uri
= get_uri(t
)) == NULL
)
8021 u1
= g_malloc0(sizeof(struct undo
));
8022 u1
->uri
= g_strdup(uri
);
8024 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8026 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
8027 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
8030 /* forward history */
8031 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
8035 u1
->history
= g_list_prepend(u1
->history
,
8036 webkit_web_history_item_copy(item
));
8037 items
= g_list_next(items
);
8042 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
8043 u1
->history
= g_list_prepend(u1
->history
,
8044 webkit_web_history_item_copy(item
));
8048 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
8052 u1
->history
= g_list_prepend(u1
->history
,
8053 webkit_web_history_item_copy(item
));
8054 items
= g_list_next(items
);
8057 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
8059 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
8060 u2
= TAILQ_LAST(&undos
, undo_tailq
);
8061 TAILQ_REMOVE(&undos
, u2
, entry
);
8063 g_list_free(u2
->history
);
8072 delete_tab(struct tab
*t
)
8076 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
8081 TAILQ_REMOVE(&tabs
, t
, entry
);
8083 /* Halt all webkit activity. */
8084 abort_favicon_download(t
);
8085 webkit_web_view_stop_loading(t
->wv
);
8087 /* Save the tab, so we can undo the close. */
8088 undo_close_tab_save(t
);
8090 if (browser_mode
== XT_BM_KIOSK
) {
8091 gtk_widget_destroy(t
->uri_entry
);
8092 gtk_widget_destroy(t
->stop
);
8093 gtk_widget_destroy(t
->js_toggle
);
8096 gtk_widget_destroy(t
->tab_elems
.eventbox
);
8097 gtk_widget_destroy(t
->vbox
);
8099 g_free(t
->user_agent
);
8100 g_free(t
->stylesheet
);
8104 if (TAILQ_EMPTY(&tabs
)) {
8105 if (browser_mode
== XT_BM_KIOSK
)
8106 create_new_tab(home
, NULL
, 1, -1);
8108 create_new_tab(NULL
, NULL
, 1, -1);
8111 /* recreate session */
8112 if (session_autosave
) {
8118 recolor_compact_tabs();
8122 update_statusbar_zoom(struct tab
*t
)
8125 char s
[16] = { '\0' };
8127 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8128 if ((zoom
<= 0.99 || zoom
>= 1.01))
8129 snprintf(s
, sizeof s
, "%d%%", (int)(zoom
* 100));
8130 gtk_entry_set_text(GTK_ENTRY(t
->sbe
.zoom
), s
);
8134 setzoom_webkit(struct tab
*t
, int adjust
)
8136 #define XT_ZOOMPERCENT 0.04
8141 show_oops(NULL
, "setzoom_webkit invalid parameters");
8145 g_object_get(G_OBJECT(t
->wv
), "zoom-level", &zoom
, (char *)NULL
);
8146 if (adjust
== XT_ZOOM_IN
)
8147 zoom
+= XT_ZOOMPERCENT
;
8148 else if (adjust
== XT_ZOOM_OUT
)
8149 zoom
-= XT_ZOOMPERCENT
;
8150 else if (adjust
> 0)
8151 zoom
= default_zoom_level
+ adjust
/ 100.0 - 1.0;
8153 show_oops(t
, "setzoom_webkit invalid zoom value");
8157 if (zoom
< XT_ZOOMPERCENT
)
8158 zoom
= XT_ZOOMPERCENT
;
8159 g_object_set(G_OBJECT(t
->wv
), "zoom-level", zoom
, (char *)NULL
);
8160 update_statusbar_zoom(t
);
8164 tab_clicked_cb(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
8166 struct tab
*t
= (struct tab
*) data
;
8168 DNPRINTF(XT_D_TAB
, "tab_clicked_cb: tab: %d\n", t
->tab_id
);
8170 switch (event
->button
) {
8172 set_current_tab(t
->tab_id
);
8183 append_tab(struct tab
*t
)
8188 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8189 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
8193 create_new_tab(char *title
, struct undo
*u
, int focus
, int position
)
8198 WebKitWebHistoryItem
*item
;
8202 int sbe_p
= 0, sbe_b
= 0,
8205 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
8207 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
8208 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
8212 t
= g_malloc0(sizeof *t
);
8214 if (title
== NULL
) {
8215 title
= "(untitled)";
8219 t
->vbox
= gtk_vbox_new(FALSE
, 0);
8221 /* label + button for tab */
8222 b
= gtk_hbox_new(FALSE
, 0);
8225 #if GTK_CHECK_VERSION(2, 20, 0)
8226 t
->spinner
= gtk_spinner_new();
8228 t
->label
= gtk_label_new(title
);
8229 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
8230 gtk_widget_set_size_request(t
->label
, 100, 0);
8231 gtk_label_set_max_width_chars(GTK_LABEL(t
->label
), 20);
8232 gtk_label_set_ellipsize(GTK_LABEL(t
->label
), PANGO_ELLIPSIZE_END
);
8233 gtk_widget_set_size_request(b
, 130, 0);
8235 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
8236 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
8237 #if GTK_CHECK_VERSION(2, 20, 0)
8238 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
8242 if (browser_mode
== XT_BM_KIOSK
) {
8243 t
->toolbar
= create_kiosk_toolbar(t
);
8244 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
,
8247 t
->toolbar
= create_toolbar(t
);
8249 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
,
8257 t
->browser_win
= create_browser(t
);
8258 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
8260 /* oops message for user feedback */
8261 t
->oops
= gtk_entry_new();
8262 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
8263 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
8264 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
8265 gdk_color_parse(XT_COLOR_RED
, &color
);
8266 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
8267 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
8268 gtk_widget_modify_font(GTK_WIDGET(t
->oops
), oops_font
);
8271 t
->cmd
= gtk_entry_new();
8272 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
8273 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
8274 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
8275 gtk_widget_modify_font(GTK_WIDGET(t
->cmd
), cmd_font
);
8278 t
->statusbar_box
= gtk_hbox_new(FALSE
, 0);
8280 t
->sbe
.statusbar
= gtk_entry_new();
8281 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.statusbar
), NULL
);
8282 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.statusbar
), FALSE
);
8283 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.statusbar
), FALSE
);
8284 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.statusbar
), statusbar_font
);
8286 /* create these widgets only if specified in statusbar_elems */
8287 t
->sbe
.position
= gtk_entry_new();
8288 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.position
), NULL
);
8289 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.position
), FALSE
);
8290 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.position
), FALSE
);
8291 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.position
), statusbar_font
);
8292 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.position
), 1.0);
8293 gtk_widget_set_size_request(t
->sbe
.position
, 40, -1);
8295 t
->sbe
.zoom
= gtk_entry_new();
8296 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.zoom
), NULL
);
8297 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.zoom
), FALSE
);
8298 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.zoom
), FALSE
);
8299 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.zoom
), statusbar_font
);
8300 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.zoom
), 1.0);
8301 gtk_widget_set_size_request(t
->sbe
.zoom
, 40, -1);
8303 t
->sbe
.buffercmd
= gtk_entry_new();
8304 gtk_entry_set_inner_border(GTK_ENTRY(t
->sbe
.buffercmd
), NULL
);
8305 gtk_entry_set_has_frame(GTK_ENTRY(t
->sbe
.buffercmd
), FALSE
);
8306 gtk_widget_set_can_focus(GTK_WIDGET(t
->sbe
.buffercmd
), FALSE
);
8307 gtk_widget_modify_font(GTK_WIDGET(t
->sbe
.buffercmd
), statusbar_font
);
8308 gtk_entry_set_alignment(GTK_ENTRY(t
->sbe
.buffercmd
), 1.0);
8309 gtk_widget_set_size_request(t
->sbe
.buffercmd
, 60, -1);
8311 statusbar_modify_attr(t
, XT_COLOR_WHITE
, XT_COLOR_BLACK
);
8313 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), t
->sbe
.statusbar
, TRUE
,
8316 /* gtk widgets cannot be added to a box twice. sbe_* variables
8317 make sure of this */
8318 for (p
= statusbar_elems
; *p
!= '\0'; p
++) {
8322 GtkWidget
*sep
= gtk_vseparator_new();
8324 gdk_color_parse(XT_COLOR_SB_SEPARATOR
, &color
);
8325 gtk_widget_modify_bg(sep
, GTK_STATE_NORMAL
, &color
);
8326 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
), sep
,
8327 FALSE
, FALSE
, FALSE
);
8332 warnx("flag \"%c\" specified more than "
8333 "once in statusbar_elems\n", *p
);
8337 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8338 t
->sbe
.position
, FALSE
, FALSE
, FALSE
);
8342 warnx("flag \"%c\" specified more than "
8343 "once in statusbar_elems\n", *p
);
8347 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8348 t
->sbe
.buffercmd
, FALSE
, FALSE
, FALSE
);
8352 warnx("flag \"%c\" specified more than "
8353 "once in statusbar_elems\n", *p
);
8357 gtk_box_pack_start(GTK_BOX(t
->statusbar_box
),
8358 t
->sbe
.zoom
, FALSE
, FALSE
, FALSE
);
8361 warnx("illegal flag \"%c\" in statusbar_elems\n", *p
);
8366 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar_box
, FALSE
, FALSE
, 0);
8369 t
->buffers
= create_buffers(t
);
8370 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->buffers
, FALSE
, FALSE
, 0);
8372 /* xtp meaning is normal by default */
8373 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
8375 /* set empty favicon */
8376 xt_icon_from_name(t
, "text-html");
8378 /* and show it all */
8379 gtk_widget_show_all(b
);
8380 gtk_widget_show_all(t
->vbox
);
8382 /* compact tab bar */
8383 t
->tab_elems
.label
= gtk_label_new(title
);
8384 gtk_label_set_width_chars(GTK_LABEL(t
->tab_elems
.label
), 1.0);
8385 gtk_misc_set_alignment(GTK_MISC(t
->tab_elems
.label
), 0.0, 0.0);
8386 gtk_misc_set_padding(GTK_MISC(t
->tab_elems
.label
), 4.0, 4.0);
8387 gtk_widget_modify_font(GTK_WIDGET(t
->tab_elems
.label
), tabbar_font
);
8389 t
->tab_elems
.eventbox
= gtk_event_box_new();
8390 t
->tab_elems
.box
= gtk_hbox_new(FALSE
, 0);
8391 t
->tab_elems
.sep
= gtk_vseparator_new();
8393 gdk_color_parse(XT_COLOR_CT_BACKGROUND
, &color
);
8394 gtk_widget_modify_bg(t
->tab_elems
.eventbox
, GTK_STATE_NORMAL
, &color
);
8395 gdk_color_parse(XT_COLOR_CT_INACTIVE
, &color
);
8396 gtk_widget_modify_fg(t
->tab_elems
.label
, GTK_STATE_NORMAL
, &color
);
8397 gdk_color_parse(XT_COLOR_CT_SEPARATOR
, &color
);
8398 gtk_widget_modify_bg(t
->tab_elems
.sep
, GTK_STATE_NORMAL
, &color
);
8400 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.label
, TRUE
,
8402 gtk_box_pack_start(GTK_BOX(t
->tab_elems
.box
), t
->tab_elems
.sep
, FALSE
,
8404 gtk_container_add(GTK_CONTAINER(t
->tab_elems
.eventbox
),
8407 gtk_box_pack_start(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
, TRUE
,
8409 gtk_widget_show_all(t
->tab_elems
.eventbox
);
8411 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
8414 id
= position
>= 0 ? position
:
8415 gtk_notebook_get_current_page(notebook
) + 1;
8416 if (id
> gtk_notebook_get_n_pages(notebook
))
8419 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
8420 gtk_notebook_insert_page(notebook
, t
->vbox
, b
, id
);
8421 gtk_box_reorder_child(GTK_BOX(tab_bar
),
8422 t
->tab_elems
.eventbox
, id
);
8427 #if GTK_CHECK_VERSION(2, 20, 0)
8428 /* turn spinner off if we are a new tab without uri */
8430 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
8431 gtk_widget_hide(t
->spinner
);
8434 /* make notebook tabs reorderable */
8435 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
8437 /* compact tabs clickable */
8438 g_signal_connect(G_OBJECT(t
->tab_elems
.eventbox
),
8439 "button_press_event", G_CALLBACK(tab_clicked_cb
), t
);
8441 g_object_connect(G_OBJECT(t
->cmd
),
8442 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
8443 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
8444 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
8445 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
8448 /* reuse wv_button_cb to hide oops */
8449 g_object_connect(G_OBJECT(t
->oops
),
8450 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8453 g_signal_connect(t
->buffers
,
8454 "row-activated", G_CALLBACK(row_activated_cb
), t
);
8455 g_object_connect(G_OBJECT(t
->buffers
),
8456 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
, NULL
);
8458 g_object_connect(G_OBJECT(t
->wv
),
8459 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
8460 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8461 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
8462 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
8463 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
8464 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8465 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
8466 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
8467 "signal::close-web-view", G_CALLBACK(webview_closewv_cb
), t
,
8468 "signal::event", G_CALLBACK(webview_event_cb
), t
,
8469 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
8470 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
8471 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
8472 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
8473 "signal::button_release_event", G_CALLBACK(wv_release_button_cb
), t
,
8475 g_signal_connect(t
->wv
,
8476 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
8477 g_signal_connect(t
->wv
,
8478 "load-error", G_CALLBACK(notify_load_error_cb
), t
);
8479 g_signal_connect(t
->wv
,
8480 "notify::title", G_CALLBACK(notify_title_cb
), t
);
8482 /* hijack the unused keys as if we were the browser */
8483 g_object_connect(G_OBJECT(t
->toolbar
),
8484 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
8487 g_signal_connect(G_OBJECT(bb
), "button_press_event",
8488 G_CALLBACK(tab_close_cb
), t
);
8494 url_set_visibility();
8495 statusbar_set_visibility();
8498 set_current_tab(t
->tab_id
);
8499 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
8504 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
8508 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
8513 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
8514 /* restore the tab's history */
8515 if (u
&& u
->history
) {
8519 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
8520 items
= g_list_next(items
);
8523 item
= g_list_nth_data(u
->history
, u
->back
);
8525 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
8528 g_list_free(u
->history
);
8530 webkit_web_back_forward_list_clear(t
->bfl
);
8532 recolor_compact_tabs();
8537 notebook_switchpage_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8543 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
8545 if (gtk_notebook_get_current_page(notebook
) == -1)
8548 TAILQ_FOREACH(t
, &tabs
, entry
) {
8549 if (t
->tab_id
== pn
) {
8550 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
8553 uri
= get_title(t
, TRUE
);
8554 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
8560 /* can't use focus_webview here */
8561 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
8568 notebook_pagereordered_cb(GtkNotebook
*nb
, GtkWidget
*nbp
, guint pn
,
8571 struct tab
*t
= NULL
, *tt
;
8575 TAILQ_FOREACH(tt
, &tabs
, entry
)
8576 if (tt
->tab_id
== pn
) {
8581 DNPRINTF(XT_D_TAB
, "page_reordered_cb: tab: %d\n", t
->tab_id
);
8583 gtk_box_reorder_child(GTK_BOX(tab_bar
), t
->tab_elems
.eventbox
,
8588 menuitem_response(struct tab
*t
)
8590 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
8594 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
8596 GtkWidget
*menu
, *menu_items
;
8597 GdkEventButton
*bevent
;
8601 if (event
->type
== GDK_BUTTON_PRESS
) {
8602 bevent
= (GdkEventButton
*) event
;
8603 menu
= gtk_menu_new();
8605 TAILQ_FOREACH(ti
, &tabs
, entry
) {
8606 if ((uri
= get_uri(ti
)) == NULL
)
8607 /* XXX make sure there is something to print */
8608 /* XXX add gui pages in here to look purdy */
8610 menu_items
= gtk_menu_item_new_with_label(uri
);
8611 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), menu_items
);
8612 gtk_widget_show(menu_items
);
8614 g_signal_connect_swapped((menu_items
),
8615 "activate", G_CALLBACK(menuitem_response
),
8619 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
8620 bevent
->button
, bevent
->time
);
8622 /* unref object so it'll free itself when popped down */
8623 #if !GTK_CHECK_VERSION(3, 0, 0)
8624 /* XXX does not need unref with gtk+3? */
8625 g_object_ref_sink(menu
);
8626 g_object_unref(menu
);
8629 return (TRUE
/* eat event */);
8632 return (FALSE
/* propagate */);
8636 icon_size_map(int icon_size
)
8638 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
8639 icon_size
> GTK_ICON_SIZE_DIALOG
)
8640 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
8646 create_button(char *name
, char *stockid
, int size
)
8648 GtkWidget
*button
, *image
;
8652 rcstring
= g_strdup_printf(
8653 "style \"%s-style\"\n"
8655 " GtkWidget::focus-padding = 0\n"
8656 " GtkWidget::focus-line-width = 0\n"
8660 "widget \"*.%s\" style \"%s-style\"", name
, name
, name
);
8661 gtk_rc_parse_string(rcstring
);
8663 button
= gtk_button_new();
8664 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
8665 gtk_icon_size
= icon_size_map(size
? size
: icon_size
);
8667 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
8668 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8669 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
8670 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
8671 gtk_widget_set_name(button
, name
);
8672 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
8678 button_set_stockid(GtkWidget
*button
, char *stockid
)
8682 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
8683 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
8684 gtk_button_set_image(GTK_BUTTON(button
), image
);
8688 clipb_primary_cb(GtkClipboard
*primary
, GdkEvent
*event
, gpointer notused
)
8691 GdkAtom atom
= gdk_atom_intern("CUT_BUFFER0", FALSE
);
8695 * xterm doesn't play nice with clipboards because it clears the
8696 * primary when clicked. We rely on primary being set to properly
8697 * handle middle mouse button clicks (paste). So when someone clears
8698 * primary copy whatever is in CUT_BUFFER0 into primary to simualte
8699 * other application behavior (as in DON'T clear primary).
8702 p
= gtk_clipboard_wait_for_text(primary
);
8704 if (gdk_property_get(gdk_get_default_root_window(),
8706 gdk_atom_intern("STRING", FALSE
),
8708 1024 * 1024 /* picked out of my butt */,
8714 /* yes sir, we need to NUL the string */
8716 gtk_clipboard_set_text(primary
, p
, -1);
8730 char file
[PATH_MAX
];
8733 vbox
= gtk_vbox_new(FALSE
, 0);
8734 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
8735 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
8736 #if !GTK_CHECK_VERSION(3, 0, 0)
8737 /* XXX seems to be needed with gtk+2 */
8738 gtk_notebook_set_tab_hborder(notebook
, 0);
8739 gtk_notebook_set_tab_vborder(notebook
, 0);
8741 gtk_notebook_set_scrollable(notebook
, TRUE
);
8742 gtk_notebook_set_show_border(notebook
, FALSE
);
8743 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
8745 abtn
= gtk_button_new();
8746 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
8747 gtk_widget_set_size_request(arrow
, -1, -1);
8748 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
8749 gtk_widget_set_size_request(abtn
, -1, 20);
8751 #if GTK_CHECK_VERSION(2, 20, 0)
8752 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
8754 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
8756 /* compact tab bar */
8757 tab_bar
= gtk_hbox_new(TRUE
, 0);
8759 gtk_box_pack_start(GTK_BOX(vbox
), tab_bar
, FALSE
, FALSE
, 0);
8760 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
8761 gtk_widget_set_size_request(vbox
, -1, -1);
8763 g_object_connect(G_OBJECT(notebook
),
8764 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
8766 g_object_connect(G_OBJECT(notebook
),
8767 "signal::page-reordered", G_CALLBACK(notebook_pagereordered_cb
),
8768 NULL
, (char *)NULL
);
8769 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
8770 G_CALLBACK(arrow_cb
), NULL
);
8772 main_window
= create_window();
8773 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
8776 for (i
= 0; i
< LENGTH(icons
); i
++) {
8777 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
8778 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
8779 l
= g_list_append(l
, pb
);
8781 gtk_window_set_default_icon_list(l
);
8784 g_signal_connect(G_OBJECT(gtk_clipboard_get(GDK_SELECTION_PRIMARY
)),
8785 "owner-change", G_CALLBACK(clipb_primary_cb
), NULL
);
8787 gtk_widget_show_all(abtn
);
8788 gtk_widget_show_all(main_window
);
8789 notebook_tab_set_visibility();
8793 set_hook(void **hook
, char *name
)
8796 errx(1, "set_hook");
8798 if (*hook
== NULL
) {
8799 *hook
= dlsym(RTLD_NEXT
, name
);
8801 errx(1, "can't hook %s", name
);
8805 /* override libsoup soup_cookie_equal because it doesn't look at domain */
8807 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
8809 g_return_val_if_fail(cookie1
, FALSE
);
8810 g_return_val_if_fail(cookie2
, FALSE
);
8812 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
8813 !strcmp (cookie1
->value
, cookie2
->value
) &&
8814 !strcmp (cookie1
->path
, cookie2
->path
) &&
8815 !strcmp (cookie1
->domain
, cookie2
->domain
));
8819 transfer_cookies(void)
8822 SoupCookie
*sc
, *pc
;
8824 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8826 for (;cf
; cf
= cf
->next
) {
8828 sc
= soup_cookie_copy(pc
);
8829 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
8832 soup_cookies_free(cf
);
8836 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
8841 print_cookie("soup_cookie_jar_delete_cookie", c
);
8843 if (cookies_enabled
== 0)
8846 if (jar
== NULL
|| c
== NULL
)
8849 /* find and remove from persistent jar */
8850 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
8852 for (;cf
; cf
= cf
->next
) {
8854 if (soup_cookie_equal(ci
, c
)) {
8855 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
8860 soup_cookies_free(cf
);
8862 /* delete from session jar */
8863 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
8867 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
8869 struct domain
*d
= NULL
;
8873 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
8874 jar
, p_cookiejar
, s_cookiejar
);
8876 if (cookies_enabled
== 0)
8879 /* see if we are up and running */
8880 if (p_cookiejar
== NULL
) {
8881 _soup_cookie_jar_add_cookie(jar
, cookie
);
8884 /* disallow p_cookiejar adds, shouldn't happen */
8885 if (jar
== p_cookiejar
)
8889 if (jar
== NULL
|| cookie
== NULL
)
8892 if (enable_cookie_whitelist
&&
8893 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
8895 DNPRINTF(XT_D_COOKIE
,
8896 "soup_cookie_jar_add_cookie: reject %s\n",
8898 if (save_rejected_cookies
) {
8899 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
8900 show_oops(NULL
, "can't open reject cookie file");
8903 fseek(r_cookie_f
, 0, SEEK_END
);
8904 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
8905 cookie
->http_only
? "#HttpOnly_" : "",
8907 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
8909 cookie
->secure
? "TRUE" : "FALSE",
8911 (gulong
)soup_date_to_time_t(cookie
->expires
) :
8918 if (!allow_volatile_cookies
)
8922 if (cookie
->expires
== NULL
&& session_timeout
) {
8923 soup_cookie_set_expires(cookie
,
8924 soup_date_new_from_now(session_timeout
));
8925 print_cookie("modified add cookie", cookie
);
8928 /* see if we are white listed for persistence */
8929 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
8930 /* add to persistent jar */
8931 c
= soup_cookie_copy(cookie
);
8932 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
8933 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
8936 /* add to session jar */
8937 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
8938 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
8944 char file
[PATH_MAX
];
8946 set_hook((void *)&_soup_cookie_jar_add_cookie
,
8947 "soup_cookie_jar_add_cookie");
8948 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
8949 "soup_cookie_jar_delete_cookie");
8951 if (cookies_enabled
== 0)
8955 * the following code is intricate due to overriding several libsoup
8957 * do not alter order of these operations.
8960 /* rejected cookies */
8961 if (save_rejected_cookies
)
8962 snprintf(rc_fname
, sizeof file
, "%s/%s", work_dir
,
8965 /* persistent cookies */
8966 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_COOKIE_FILE
);
8967 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
8969 /* session cookies */
8970 s_cookiejar
= soup_cookie_jar_new();
8971 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
8972 cookie_policy
, (void *)NULL
);
8975 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
8979 setup_proxy(char *uri
)
8982 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
8983 soup_uri_free(proxy_uri
);
8987 if (http_proxy
!= uri
) {
8994 http_proxy
= g_strdup(uri
);
8995 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
8996 proxy_uri
= soup_uri_new(http_proxy
);
8997 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
9002 send_cmd_to_socket(char *cmd
)
9005 struct sockaddr_un sa
;
9007 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9008 warnx("%s: socket", __func__
);
9012 sa
.sun_family
= AF_UNIX
;
9013 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9014 work_dir
, XT_SOCKET_FILE
);
9017 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9018 warnx("%s: connect", __func__
);
9022 if (send(s
, cmd
, strlen(cmd
) + 1, 0) == -1) {
9023 warnx("%s: send", __func__
);
9034 socket_watcher(GIOChannel
*source
, GIOCondition condition
, gpointer data
)
9037 char str
[XT_MAX_URL_LENGTH
];
9038 socklen_t t
= sizeof(struct sockaddr_un
);
9039 struct sockaddr_un sa
;
9044 gint fd
= g_io_channel_unix_get_fd(source
);
9046 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
9051 if (getpeereid(s
, &uid
, &gid
) == -1) {
9055 if (uid
!= getuid() || gid
!= getgid()) {
9056 warnx("unauthorized user");
9062 warnx("not a valid user");
9066 n
= recv(s
, str
, sizeof(str
), 0);
9070 tt
= TAILQ_LAST(&tabs
, tab_list
);
9071 cmd_execute(tt
, str
);
9079 struct sockaddr_un sa
;
9081 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9082 warn("is_running: socket");
9086 sa
.sun_family
= AF_UNIX
;
9087 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9088 work_dir
, XT_SOCKET_FILE
);
9091 /* connect to see if there is a listener */
9092 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
9093 rv
= 0; /* not running */
9095 rv
= 1; /* already running */
9106 struct sockaddr_un sa
;
9108 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
9109 warn("build_socket: socket");
9113 sa
.sun_family
= AF_UNIX
;
9114 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
9115 work_dir
, XT_SOCKET_FILE
);
9118 /* connect to see if there is a listener */
9119 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9120 /* no listener so we will */
9121 unlink(sa
.sun_path
);
9123 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
9124 warn("build_socket: bind");
9128 if (listen(s
, 1) == -1) {
9129 warn("build_socket: listen");
9142 completion_select_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9143 GtkTreeIter
*iter
, struct tab
*t
)
9147 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9155 completion_hover_cb(GtkEntryCompletion
*widget
, GtkTreeModel
*model
,
9156 GtkTreeIter
*iter
, struct tab
*t
)
9160 gtk_tree_model_get(model
, iter
, 0, &value
, -1);
9161 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), value
);
9162 gtk_editable_set_position(GTK_EDITABLE(t
->uri_entry
), -1);
9169 completion_add_uri(const gchar
*uri
)
9173 /* add uri to list_store */
9174 gtk_list_store_append(completion_model
, &iter
);
9175 gtk_list_store_set(completion_model
, &iter
, 0, uri
, -1);
9179 completion_match(GtkEntryCompletion
*completion
, const gchar
*key
,
9180 GtkTreeIter
*iter
, gpointer user_data
)
9183 gboolean match
= FALSE
;
9185 gtk_tree_model_get(GTK_TREE_MODEL(completion_model
), iter
, 0, &value
,
9191 match
= match_uri(value
, key
);
9198 completion_add(struct tab
*t
)
9200 /* enable completion for tab */
9201 t
->completion
= gtk_entry_completion_new();
9202 gtk_entry_completion_set_text_column(t
->completion
, 0);
9203 gtk_entry_set_completion(GTK_ENTRY(t
->uri_entry
), t
->completion
);
9204 gtk_entry_completion_set_model(t
->completion
,
9205 GTK_TREE_MODEL(completion_model
));
9206 gtk_entry_completion_set_match_func(t
->completion
, completion_match
,
9208 gtk_entry_completion_set_minimum_key_length(t
->completion
, 1);
9209 gtk_entry_completion_set_inline_selection(t
->completion
, TRUE
);
9210 g_signal_connect(G_OBJECT (t
->completion
), "match-selected",
9211 G_CALLBACK(completion_select_cb
), t
);
9212 g_signal_connect(G_OBJECT (t
->completion
), "cursor-on-match",
9213 G_CALLBACK(completion_hover_cb
), t
);
9221 if (stat(dir
, &sb
)) {
9222 if (mkdir(dir
, S_IRWXU
) == -1)
9223 err(1, "mkdir %s", dir
);
9225 err(1, "stat %s", dir
);
9227 if (S_ISDIR(sb
.st_mode
) == 0)
9228 errx(1, "%s not a dir", dir
);
9229 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
9230 warnx("fixing invalid permissions on %s", dir
);
9231 if (chmod(dir
, S_IRWXU
) == -1)
9232 err(1, "chmod %s", dir
);
9240 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
9246 main(int argc
, char *argv
[])
9249 int c
, s
, optn
= 0, opte
= 0, focus
= 1;
9250 char conf
[PATH_MAX
] = { '\0' };
9251 char file
[PATH_MAX
];
9252 char *env_proxy
= NULL
;
9256 struct sigaction sact
;
9257 GIOChannel
*channel
;
9262 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
9264 /* fiddle with ulimits */
9265 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9268 /* just use them all */
9269 rlp
.rlim_cur
= rlp
.rlim_max
;
9270 if (setrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9272 if (getrlimit(RLIMIT_NOFILE
, &rlp
) == -1)
9274 else if (rlp
.rlim_cur
<= 256)
9275 warnx("%s requires at least 256 file descriptors",
9279 while ((c
= getopt(argc
, argv
, "STVf:s:tne")) != -1) {
9288 errx(0 , "Version: %s", version
);
9291 strlcpy(conf
, optarg
, sizeof(conf
));
9294 strlcpy(named_session
, optarg
, sizeof(named_session
));
9315 RB_INIT(&downloads
);
9319 TAILQ_INIT(&aliases
);
9325 gnutls_global_init();
9327 /* generate session keys for xtp pages */
9328 generate_xtp_session_key(&dl_session_key
);
9329 generate_xtp_session_key(&hl_session_key
);
9330 generate_xtp_session_key(&cl_session_key
);
9331 generate_xtp_session_key(&fl_session_key
);
9334 gtk_init(&argc
, &argv
);
9335 if (!g_thread_supported())
9336 g_thread_init(NULL
);
9339 bzero(&sact
, sizeof(sact
));
9340 sigemptyset(&sact
.sa_mask
);
9341 sact
.sa_handler
= sigchild
;
9342 sact
.sa_flags
= SA_NOCLDSTOP
;
9343 sigaction(SIGCHLD
, &sact
, NULL
);
9345 /* set download dir */
9346 pwd
= getpwuid(getuid());
9348 errx(1, "invalid user %d", getuid());
9349 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
9351 /* compile buffer command regexes */
9354 /* set default string settings */
9355 home
= g_strdup("https://www.cyphertite.com");
9356 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
9357 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
9358 strlcpy(runtime_settings
, "runtime", sizeof runtime_settings
);
9359 cmd_font_name
= g_strdup("monospace normal 9");
9360 oops_font_name
= g_strdup("monospace normal 9");
9361 statusbar_font_name
= g_strdup("monospace normal 9");
9362 tabbar_font_name
= g_strdup("monospace normal 9");
9363 statusbar_elems
= g_strdup("BP");
9365 /* read config file */
9366 if (strlen(conf
) == 0)
9367 snprintf(conf
, sizeof conf
, "%s/.%s",
9368 pwd
->pw_dir
, XT_CONF_FILE
);
9369 config_parse(conf
, 0);
9372 cmd_font
= pango_font_description_from_string(cmd_font_name
);
9373 oops_font
= pango_font_description_from_string(oops_font_name
);
9374 statusbar_font
= pango_font_description_from_string(statusbar_font_name
);
9375 tabbar_font
= pango_font_description_from_string(tabbar_font_name
);
9377 /* working directory */
9378 if (strlen(work_dir
) == 0)
9379 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
9380 pwd
->pw_dir
, XT_DIR
);
9383 /* icon cache dir */
9384 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
9388 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
9392 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
9393 work_dir
, XT_SESSIONS_DIR
);
9394 xxx_dir(sessions_dir
);
9396 /* runtime settings that can override config file */
9397 if (runtime_settings
[0] != '\0')
9398 config_parse(runtime_settings
, 1);
9401 if (!strcmp(download_dir
, pwd
->pw_dir
))
9402 strlcat(download_dir
, "/downloads", sizeof download_dir
);
9403 xxx_dir(download_dir
);
9405 /* favorites file */
9406 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
9407 if (stat(file
, &sb
)) {
9408 warnx("favorites file doesn't exist, creating it");
9409 if ((f
= fopen(file
, "w")) == NULL
)
9410 err(1, "favorites");
9414 /* quickmarks file */
9415 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_QMARKS_FILE
);
9416 if (stat(file
, &sb
)) {
9417 warnx("quickmarks file doesn't exist, creating it");
9418 if ((f
= fopen(file
, "w")) == NULL
)
9419 err(1, "quickmarks");
9424 session
= webkit_get_default_session();
9429 if (stat(ssl_ca_file
, &sb
)) {
9430 warnx("no CA file: %s", ssl_ca_file
);
9431 g_free(ssl_ca_file
);
9434 g_object_set(session
,
9435 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
9436 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
9441 env_proxy
= getenv("http_proxy");
9443 setup_proxy(env_proxy
);
9445 setup_proxy(http_proxy
);
9448 send_cmd_to_socket(argv
[0]);
9452 /* set some connection parameters */
9453 g_object_set(session
, "max-conns", max_connections
, (char *)NULL
);
9454 g_object_set(session
, "max-conns-per-host", max_host_connections
,
9457 /* see if there is already an xxxterm running */
9458 if (single_instance
&& is_running()) {
9460 warnx("already running");
9465 cmd
= g_strdup_printf("%s %s", "tabnew", argv
[0]);
9466 send_cmd_to_socket(cmd
);
9476 /* uri completion */
9477 completion_model
= gtk_list_store_new(1, G_TYPE_STRING
);
9480 buffers_store
= gtk_list_store_new
9481 (NUM_COLS
, G_TYPE_UINT
, G_TYPE_STRING
);
9487 notebook_tab_set_visibility();
9489 if (save_global_history
)
9490 restore_global_history();
9492 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
9493 restore_saved_tabs();
9495 a
.s
= named_session
;
9496 a
.i
= XT_SES_DONOTHING
;
9497 open_tabs(NULL
, &a
);
9501 create_new_tab(argv
[0], NULL
, focus
, -1);
9508 if (TAILQ_EMPTY(&tabs
))
9509 create_new_tab(home
, NULL
, 1, -1);
9512 if ((s
= build_socket()) != -1) {
9513 channel
= g_io_channel_unix_new(s
);
9514 g_io_add_watch(channel
, G_IO_IN
, socket_watcher
, NULL
);
9519 gnutls_global_deinit();