3 * Copyright (c) 2010, 2011 Marco Peereboom <marco@peereboom.us>
4 * Copyright (c) 2011 Stevan Andjelkovic <stevan@student.chalmers.se>
5 * Copyright (c) 2010 Edd Barrett <vext01@gmail.com>
6 * Copyright (c) 2011 Todd T. Fries <todd@fries.net>
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * inverse color browsing
26 * multi letter commands
27 * pre and post counts for commands
28 * autocompletion on various inputs
29 * create privacy browsing
30 * - 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>
61 #include <sys/socket.h>
65 #include <gdk/gdkkeysyms.h>
66 #include <webkit/webkit.h>
67 #include <libsoup/soup.h>
68 #include <gnutls/gnutls.h>
69 #include <JavaScriptCore/JavaScript.h>
70 #include <gnutls/x509.h>
72 #include "javascript.h"
75 javascript.h borrowed from vimprobable2 under the following license:
77 Copyright (c) 2009 Leon Winter
78 Copyright (c) 2009 Hannes Schueller
79 Copyright (c) 2009 Matto Fransen
81 Permission is hereby granted, free of charge, to any person obtaining a copy
82 of this software and associated documentation files (the "Software"), to deal
83 in the Software without restriction, including without limitation the rights
84 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
85 copies of the Software, and to permit persons to whom the Software is
86 furnished to do so, subject to the following conditions:
88 The above copyright notice and this permission notice shall be included in
89 all copies or substantial portions of the Software.
91 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
92 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
93 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
94 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
95 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
96 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
100 static char *version
= "$xxxterm$";
102 /* hooked functions */
103 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
104 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
109 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
110 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
111 #define XT_D_MOVE 0x0001
112 #define XT_D_KEY 0x0002
113 #define XT_D_TAB 0x0004
114 #define XT_D_URL 0x0008
115 #define XT_D_CMD 0x0010
116 #define XT_D_NAV 0x0020
117 #define XT_D_DOWNLOAD 0x0040
118 #define XT_D_CONFIG 0x0080
119 #define XT_D_JS 0x0100
120 #define XT_D_FAVORITE 0x0200
121 #define XT_D_PRINTING 0x0400
122 #define XT_D_COOKIE 0x0800
123 #define XT_D_KEYBINDING 0x1000
124 u_int32_t swm_debug
= 0
139 #define DPRINTF(x...)
140 #define DNPRINTF(n,x...)
143 #define LENGTH(x) (sizeof x / sizeof x[0])
144 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
145 ~(GDK_BUTTON1_MASK) & \
146 ~(GDK_BUTTON2_MASK) & \
147 ~(GDK_BUTTON3_MASK) & \
148 ~(GDK_BUTTON4_MASK) & \
160 TAILQ_ENTRY(tab
) entry
;
162 GtkWidget
*tab_content
;
165 GtkWidget
*uri_entry
;
166 GtkWidget
*search_entry
;
168 GtkWidget
*browser_win
;
169 GtkWidget
*statusbar
;
175 GtkWidget
*js_toggle
;
179 WebKitWebHistoryItem
*item
;
180 WebKitWebBackForwardList
*bfl
;
183 WebKitNetworkRequest
*icon_request
;
184 WebKitDownload
*icon_download
;
185 GdkPixbuf
*icon_pixbuf
;
186 gchar
*icon_dest_uri
;
188 /* adjustments for browser */
191 GtkAdjustment
*adjust_h
;
192 GtkAdjustment
*adjust_v
;
198 int xtp_meaning
; /* identifies dls/favorites */
203 #define XT_HINT_NONE (0)
204 #define XT_HINT_NUMERICAL (1)
205 #define XT_HINT_ALPHANUM (2)
214 WebKitWebSettings
*settings
;
218 TAILQ_HEAD(tab_list
, tab
);
221 RB_ENTRY(history
) entry
;
225 RB_HEAD(history_list
, history
);
228 RB_ENTRY(download
) entry
;
230 WebKitDownload
*download
;
233 RB_HEAD(download_list
, download
);
236 RB_ENTRY(domain
) entry
;
238 int handy
; /* app use */
240 RB_HEAD(domain_list
, domain
);
243 TAILQ_ENTRY(undo
) entry
;
246 int back
; /* Keeps track of how many back
247 * history items there are. */
249 TAILQ_HEAD(undo_tailq
, undo
);
251 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
252 int next_download_id
= 1;
260 #define XT_NAME ("XXXTerm")
261 #define XT_DIR (".xxxterm")
262 #define XT_CACHE_DIR ("cache")
263 #define XT_CERT_DIR ("certs/")
264 #define XT_SESSIONS_DIR ("sessions/")
265 #define XT_CONF_FILE ("xxxterm.conf")
266 #define XT_FAVS_FILE ("favorites")
267 #define XT_SAVED_TABS_FILE ("main_session")
268 #define XT_RESTART_TABS_FILE ("restart_tabs")
269 #define XT_SOCKET_FILE ("socket")
270 #define XT_HISTORY_FILE ("history")
271 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
272 #define XT_CB_HANDLED (TRUE)
273 #define XT_CB_PASSTHROUGH (FALSE)
274 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
275 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
276 #define XT_DLMAN_REFRESH "10"
277 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
278 "td {overflow: hidden;" \
279 " padding: 2px 2px 2px 2px;" \
280 " border: 1px solid black}\n" \
281 "tr:hover {background: #ffff99 ;}\n" \
282 "th {background-color: #cccccc;" \
283 " border: 1px solid black}" \
284 "table {border-spacing: 0; " \
286 " border: 1px black solid;}\n" \
288 " border: 1px solid black;" \
294 " background: green;}" \
296 " font-size: small;" \
297 " text-align: center;}" \
299 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
300 #define XT_MAX_UNDO_CLOSE_TAB (32)
301 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
304 #define SZ_KB ((uint64_t) 1024)
305 #define SZ_MB (SZ_KB * SZ_KB)
306 #define SZ_GB (SZ_KB * SZ_KB * SZ_KB)
307 #define SZ_TB (SZ_KB * SZ_KB * SZ_KB * SZ_KB)
310 * xxxterm "protocol" (xtp)
311 * We use this for managing stuff like downloads and favorites. They
312 * make magical HTML pages in memory which have xxxt:// links in order
313 * to communicate with xxxterm's internals. These links take the format:
314 * xxxt://class/session_key/action/arg
316 * Don't begin xtp class/actions as 0. atoi returns that on error.
318 * Typically we have not put addition of items in this framework, as
319 * adding items is either done via an ex-command or via a keybinding instead.
322 #define XT_XTP_STR "xxxt://"
324 /* XTP classes (xxxt://<class>) */
325 #define XT_XTP_DL 1 /* downloads */
326 #define XT_XTP_HL 2 /* history */
327 #define XT_XTP_CL 3 /* cookies */
328 #define XT_XTP_FL 4 /* favorites */
330 /* XTP download actions */
331 #define XT_XTP_DL_LIST 1
332 #define XT_XTP_DL_CANCEL 2
333 #define XT_XTP_DL_REMOVE 3
335 /* XTP history actions */
336 #define XT_XTP_HL_LIST 1
337 #define XT_XTP_HL_REMOVE 2
339 /* XTP cookie actions */
340 #define XT_XTP_CL_LIST 1
341 #define XT_XTP_CL_REMOVE 2
343 /* XTP cookie actions */
344 #define XT_XTP_FL_LIST 1
345 #define XT_XTP_FL_REMOVE 2
347 /* xtp tab meanings - identifies which tabs have xtp pages in */
348 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
349 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
350 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
351 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
352 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
355 #define XT_MOVE_INVALID (0)
356 #define XT_MOVE_DOWN (1)
357 #define XT_MOVE_UP (2)
358 #define XT_MOVE_BOTTOM (3)
359 #define XT_MOVE_TOP (4)
360 #define XT_MOVE_PAGEDOWN (5)
361 #define XT_MOVE_PAGEUP (6)
362 #define XT_MOVE_HALFDOWN (7)
363 #define XT_MOVE_HALFUP (8)
364 #define XT_MOVE_LEFT (9)
365 #define XT_MOVE_FARLEFT (10)
366 #define XT_MOVE_RIGHT (11)
367 #define XT_MOVE_FARRIGHT (12)
369 #define XT_TAB_LAST (-4)
370 #define XT_TAB_FIRST (-3)
371 #define XT_TAB_PREV (-2)
372 #define XT_TAB_NEXT (-1)
373 #define XT_TAB_INVALID (0)
374 #define XT_TAB_NEW (1)
375 #define XT_TAB_DELETE (2)
376 #define XT_TAB_DELQUIT (3)
377 #define XT_TAB_OPEN (4)
378 #define XT_TAB_UNDO_CLOSE (5)
379 #define XT_TAB_SHOW (6)
380 #define XT_TAB_HIDE (7)
382 #define XT_NAV_INVALID (0)
383 #define XT_NAV_BACK (1)
384 #define XT_NAV_FORWARD (2)
385 #define XT_NAV_RELOAD (3)
386 #define XT_NAV_RELOAD_CACHE (4)
388 #define XT_FOCUS_INVALID (0)
389 #define XT_FOCUS_URI (1)
390 #define XT_FOCUS_SEARCH (2)
392 #define XT_SEARCH_INVALID (0)
393 #define XT_SEARCH_NEXT (1)
394 #define XT_SEARCH_PREV (2)
396 #define XT_PASTE_CURRENT_TAB (0)
397 #define XT_PASTE_NEW_TAB (1)
399 #define XT_FONT_SET (0)
401 #define XT_URL_SHOW (1)
402 #define XT_URL_HIDE (2)
404 #define XT_STATUSBAR_SHOW (1)
405 #define XT_STATUSBAR_HIDE (2)
407 #define XT_WL_TOGGLE (1<<0)
408 #define XT_WL_ENABLE (1<<1)
409 #define XT_WL_DISABLE (1<<2)
410 #define XT_WL_FQDN (1<<3) /* default */
411 #define XT_WL_TOPLEVEL (1<<4)
413 #define XT_CMD_OPEN (0)
414 #define XT_CMD_OPEN_CURRENT (1)
415 #define XT_CMD_TABNEW (2)
416 #define XT_CMD_TABNEW_CURRENT (3)
418 #define XT_STATUS_NOTHING (0)
419 #define XT_STATUS_LINK (1)
420 #define XT_STATUS_URI (2)
421 #define XT_STATUS_LOADING (3)
423 #define XT_SES_DONOTHING (0)
424 #define XT_SES_CLOSETABS (1)
426 #define XT_COOKIE_NORMAL (0)
427 #define XT_COOKIE_WHITELIST (1)
434 TAILQ_ENTRY(mime_type
) entry
;
436 TAILQ_HEAD(mime_type_list
, mime_type
);
442 TAILQ_ENTRY(alias
) entry
;
444 TAILQ_HEAD(alias_list
, alias
);
446 /* settings that require restart */
447 int tabless
= 0; /* allow only 1 tab */
448 int enable_socket
= 0;
449 int single_instance
= 0; /* only allow one xxxterm to run */
450 int fancy_bar
= 1; /* fancy toolbar */
451 int browser_mode
= XT_COOKIE_NORMAL
;
453 /* runtime settings */
454 int show_tabs
= 1; /* show tabs on notebook */
455 int show_url
= 1; /* show url toolbar on notebook */
456 int show_statusbar
= 0; /* vimperator style status bar */
457 int ctrl_click_focus
= 0; /* ctrl click gets focus */
458 int cookies_enabled
= 1; /* enable cookies */
459 int read_only_cookies
= 0; /* enable to not write cookies */
460 int enable_scripts
= 1;
461 int enable_plugins
= 0;
462 int default_font_size
= 12;
463 int window_height
= 768;
464 int window_width
= 1024;
465 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
466 unsigned refresh_interval
= 10; /* download refresh interval */
467 int enable_cookie_whitelist
= 0;
468 int enable_js_whitelist
= 0;
469 time_t session_timeout
= 3600; /* cookie session timeout */
470 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
471 char *ssl_ca_file
= NULL
;
472 char *resource_dir
= NULL
;
473 gboolean ssl_strict_certs
= FALSE
;
474 int append_next
= 1; /* append tab after current tab */
476 char *search_string
= NULL
;
477 char *http_proxy
= NULL
;
478 char download_dir
[PATH_MAX
];
479 char runtime_settings
[PATH_MAX
]; /* override of settings */
480 int allow_volatile_cookies
= 0;
481 int save_global_history
= 0; /* save global history to disk */
482 char *user_agent
= NULL
;
483 int save_rejected_cookies
= 0;
484 time_t session_autosave
= 0;
485 int guess_search
= 0;
489 int set_download_dir(struct settings
*, char *);
490 int set_work_dir(struct settings
*, char *);
491 int set_runtime_dir(struct settings
*, char *);
492 int set_browser_mode(struct settings
*, char *);
493 int set_cookie_policy(struct settings
*, char *);
494 int add_alias(struct settings
*, char *);
495 int add_mime_type(struct settings
*, char *);
496 int add_cookie_wl(struct settings
*, char *);
497 int add_js_wl(struct settings
*, char *);
498 int add_kb(struct settings
*, char *);
499 void button_set_stockid(GtkWidget
*, char *);
500 GtkWidget
* create_button(char *, char *, int);
502 char *get_browser_mode(struct settings
*);
503 char *get_cookie_policy(struct settings
*);
505 char *get_download_dir(struct settings
*);
506 char *get_work_dir(struct settings
*);
507 char *get_runtime_dir(struct settings
*);
509 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
510 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
511 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
512 void walk_kb(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
513 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
516 int (*set
)(struct settings
*, char *);
517 char *(*get
)(struct settings
*);
518 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
521 struct special s_browser_mode
= {
527 struct special s_cookie
= {
533 struct special s_alias
= {
539 struct special s_mime
= {
545 struct special s_js
= {
551 struct special s_kb
= {
557 struct special s_cookie_wl
= {
563 struct special s_download_dir
= {
569 struct special s_work_dir
= {
578 #define XT_S_INVALID (0)
582 #define XT_SF_RESTART (1<<0)
583 #define XT_SF_RUNTIME (1<<1)
588 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
589 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
590 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
591 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
592 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
593 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
594 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
595 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
596 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
597 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
598 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
599 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
600 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
601 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
602 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
603 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
604 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
605 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
606 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
607 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
608 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
609 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
610 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
611 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
612 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
613 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
614 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
615 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
616 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
617 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
618 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
619 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
620 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
621 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
622 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
623 { "work_dir", XT_S_STR
, 0, NULL
, NULL
,&s_work_dir
},
625 /* runtime settings */
626 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
627 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
628 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
629 { "keybinding", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_kb
},
630 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
634 extern char *__progname
;
637 GtkWidget
*main_window
;
638 GtkNotebook
*notebook
;
639 GtkWidget
*arrow
, *abtn
;
640 struct tab_list tabs
;
641 struct history_list hl
;
642 struct download_list downloads
;
643 struct domain_list c_wl
;
644 struct domain_list js_wl
;
645 struct undo_tailq undos
;
646 struct keybinding_list kbl
;
648 int updating_dl_tabs
= 0;
649 int updating_hl_tabs
= 0;
650 int updating_cl_tabs
= 0;
651 int updating_fl_tabs
= 0;
653 uint64_t blocked_cookies
= 0;
654 char named_session
[PATH_MAX
];
655 void update_favicon(struct tab
*);
656 int icon_size_map(int);
661 int saved_errno
, status
;
666 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
670 if (errno
!= ECHILD
) {
672 clog_warn("sigchild: waitpid:");
678 if (WIFEXITED(status
)) {
679 if (WEXITSTATUS(status
) != 0) {
681 clog_warnx("sigchild: child exit status: %d",
682 WEXITSTATUS(status));
687 clog_warnx("sigchild: child is terminated abnormally");
696 load_webkit_string(struct tab
*t
, const char *str
)
698 /* we set this to indicate we want to manually do navaction */
699 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
700 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
704 set_status(struct tab
*t
, gchar
*s
, int status
)
712 case XT_STATUS_LOADING
:
713 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), s
);
714 type
= g_strdup_printf("Loading: %s", s
);
718 type
= g_strdup_printf("Link: %s", s
);
720 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
724 type
= g_strdup_printf("%s", s
);
726 t
->status
= g_strdup(type
);
730 t
->status
= g_strdup(s
);
732 case XT_STATUS_NOTHING
:
737 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
743 hide_oops(struct tab
*t
)
745 gtk_widget_hide(t
->oops
);
749 hide_cmd(struct tab
*t
)
751 gtk_widget_hide(t
->cmd
);
755 show_cmd(struct tab
*t
)
757 gtk_widget_hide(t
->oops
);
758 gtk_widget_show(t
->cmd
);
762 show_oops(struct tab
*t
, const char *fmt
, ...)
771 if (vasprintf(&msg
, fmt
, ap
) == -1)
772 errx(1, "show_oops failed");
775 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
776 gtk_widget_hide(t
->cmd
);
777 gtk_widget_show(t
->oops
);
780 /* XXX collapse with show_oops */
782 show_oops_s(const char *fmt
, ...)
786 struct tab
*ti
, *t
= NULL
;
791 TAILQ_FOREACH(ti
, &tabs
, entry
)
792 if (ti
->tab_id
== gtk_notebook_current_page(notebook
)) {
800 if (vasprintf(&msg
, fmt
, ap
) == -1)
801 errx(1, "show_oops_s failed");
804 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
805 gtk_widget_hide(t
->cmd
);
806 gtk_widget_show(t
->oops
);
810 get_as_string(struct settings
*s
)
821 warnx("get_as_string skip %s\n", s
->name
);
822 } else if (s
->type
== XT_S_INT
)
823 r
= g_strdup_printf("%d", *s
->ival
);
824 else if (s
->type
== XT_S_STR
)
825 r
= g_strdup(*s
->sval
);
827 r
= g_strdup_printf("INVALID TYPE");
833 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
838 for (i
= 0; i
< LENGTH(rs
); i
++) {
839 if (rs
[i
].s
&& rs
[i
].s
->walk
)
840 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
842 s
= get_as_string(&rs
[i
]);
843 cb(&rs
[i
], s
, cb_args
);
850 set_browser_mode(struct settings
*s
, char *val
)
852 if (!strcmp(val
, "whitelist")) {
853 browser_mode
= XT_COOKIE_WHITELIST
;
854 allow_volatile_cookies
= 0;
855 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
857 enable_cookie_whitelist
= 1;
858 read_only_cookies
= 0;
859 save_rejected_cookies
= 0;
860 session_timeout
= 3600;
862 enable_js_whitelist
= 1;
863 } else if (!strcmp(val
, "normal")) {
864 browser_mode
= XT_COOKIE_NORMAL
;
865 allow_volatile_cookies
= 0;
866 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
868 enable_cookie_whitelist
= 0;
869 read_only_cookies
= 0;
870 save_rejected_cookies
= 0;
871 session_timeout
= 3600;
873 enable_js_whitelist
= 0;
881 get_browser_mode(struct settings
*s
)
885 if (browser_mode
== XT_COOKIE_WHITELIST
)
886 r
= g_strdup("whitelist");
887 else if (browser_mode
== XT_COOKIE_NORMAL
)
888 r
= g_strdup("normal");
896 set_cookie_policy(struct settings
*s
, char *val
)
898 if (!strcmp(val
, "no3rdparty"))
899 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
900 else if (!strcmp(val
, "accept"))
901 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
902 else if (!strcmp(val
, "reject"))
903 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
911 get_cookie_policy(struct settings
*s
)
915 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
916 r
= g_strdup("no3rdparty");
917 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
918 r
= g_strdup("accept");
919 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
920 r
= g_strdup("reject");
928 get_download_dir(struct settings
*s
)
930 if (download_dir
[0] == '\0')
932 return (g_strdup(download_dir
));
936 set_download_dir(struct settings
*s
, char *val
)
939 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
940 pwd
->pw_dir
, &val
[1]);
942 strlcpy(download_dir
, val
, sizeof download_dir
);
949 * We use these to prevent people putting xxxt:// URLs on
950 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
952 #define XT_XTP_SES_KEY_SZ 8
953 #define XT_XTP_SES_KEY_HEX_FMT \
954 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
955 char *dl_session_key
; /* downloads */
956 char *hl_session_key
; /* history list */
957 char *cl_session_key
; /* cookie list */
958 char *fl_session_key
; /* favorites list */
960 char work_dir
[PATH_MAX
];
961 char certs_dir
[PATH_MAX
];
962 char cache_dir
[PATH_MAX
];
963 char sessions_dir
[PATH_MAX
];
964 char cookie_file
[PATH_MAX
];
965 SoupURI
*proxy_uri
= NULL
;
966 SoupSession
*session
;
967 SoupCookieJar
*s_cookiejar
;
968 SoupCookieJar
*p_cookiejar
;
969 char rc_fname
[PATH_MAX
];
971 struct mime_type_list mtl
;
972 struct alias_list aliases
;
975 void create_new_tab(char *, struct undo
*, int);
976 void delete_tab(struct tab
*);
977 void adjustfont_webkit(struct tab
*, int);
978 int run_script(struct tab
*, char *);
979 int download_rb_cmp(struct download
*, struct download
*);
980 int xtp_page_hl(struct tab
*t
, struct karg
*args
);
981 int xtp_page_dl(struct tab
*t
, struct karg
*args
);
982 int xtp_page_cl(struct tab
*t
, struct karg
*args
);
983 int xtp_page_fl(struct tab
*t
, struct karg
*args
);
986 history_rb_cmp(struct history
*h1
, struct history
*h2
)
988 return (strcmp(h1
->uri
, h2
->uri
));
990 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
993 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
995 return (strcmp(d1
->d
, d2
->d
));
997 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
1000 get_work_dir(struct settings
*s
)
1002 if (work_dir
[0] == '\0')
1004 return (g_strdup(work_dir
));
1008 set_work_dir(struct settings
*s
, char *val
)
1011 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
1012 pwd
->pw_dir
, &val
[1]);
1014 strlcpy(work_dir
, val
, sizeof work_dir
);
1020 * generate a session key to secure xtp commands.
1021 * pass in a ptr to the key in question and it will
1022 * be modified in place.
1025 generate_xtp_session_key(char **key
)
1027 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
1033 /* make a new one */
1034 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
1035 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
1036 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
1037 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
1039 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
1043 * validate a xtp session key.
1047 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1049 if (strcmp(trusted
, untrusted
) != 0) {
1050 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1059 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1061 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1063 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1065 struct valid_url_types
{
1076 valid_url_type(char *url
)
1080 for (i
= 0; i
< LENGTH(vut
); i
++)
1081 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1088 print_cookie(char *msg
, SoupCookie
*c
)
1094 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1095 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1096 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1097 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1098 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1099 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1100 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1101 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1102 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1103 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1107 walk_alias(struct settings
*s
,
1108 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1113 if (s
== NULL
|| cb
== NULL
) {
1114 show_oops_s("walk_alias invalid parameters");
1118 TAILQ_FOREACH(a
, &aliases
, entry
) {
1119 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1120 cb(s
, str
, cb_args
);
1126 match_alias(char *url_in
)
1130 char *url_out
= NULL
, *search
, *enc_arg
;
1132 search
= g_strdup(url_in
);
1134 if (strsep(&arg
, " \t") == NULL
) {
1135 show_oops_s("match_alias: NULL URL");
1139 TAILQ_FOREACH(a
, &aliases
, entry
) {
1140 if (!strcmp(search
, a
->a_name
))
1145 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1148 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1149 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1152 url_out
= g_strdup(a
->a_uri
);
1160 guess_url_type(char *url_in
)
1163 char *url_out
= NULL
, *enc_search
= NULL
;
1165 url_out
= match_alias(url_in
);
1166 if (url_out
!= NULL
)
1171 * If there is no dot nor slash in the string and it isn't a
1172 * path to a local file and doesn't resolves to an IP, assume
1173 * that the user wants to search for the string.
1176 if (strchr(url_in
, '.') == NULL
&&
1177 strchr(url_in
, '/') == NULL
&&
1178 stat(url_in
, &sb
) != 0 &&
1179 gethostbyname(url_in
) == NULL
) {
1181 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1182 url_out
= g_strdup_printf(search_string
, enc_search
);
1188 /* XXX not sure about this heuristic */
1189 if (stat(url_in
, &sb
) == 0)
1190 url_out
= g_strdup_printf("file://%s", url_in
);
1192 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1194 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1200 load_uri(struct tab
*t
, gchar
*uri
)
1202 gchar
*newuri
= NULL
;
1204 if (uri
== NULL
|| !strlen(uri
))
1207 /* Strip leading spaces. */
1208 while(*uri
&& isspace(*uri
))
1214 if (valid_url_type(uri
)) {
1215 newuri
= guess_url_type(uri
);
1219 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
1220 webkit_web_view_load_uri(t
->wv
, uri
);
1227 get_uri(WebKitWebView
*wv
)
1229 WebKitWebFrame
*frame
;
1232 frame
= webkit_web_view_get_main_frame(wv
);
1233 uri
= webkit_web_frame_get_uri(frame
);
1235 if (uri
&& strlen(uri
) > 0)
1242 add_alias(struct settings
*s
, char *line
)
1245 struct alias
*a
= NULL
;
1247 if (s
== NULL
|| line
== NULL
) {
1248 show_oops_s("add_alias invalid parameters");
1253 a
= g_malloc(sizeof(*a
));
1255 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1256 show_oops_s("add_alias: incomplete alias definition");
1259 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1260 show_oops_s("add_alias: invalid alias definition");
1264 a
->a_name
= g_strdup(alias
);
1265 a
->a_uri
= g_strdup(l
);
1267 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1269 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1279 add_mime_type(struct settings
*s
, char *line
)
1283 struct mime_type
*m
= NULL
;
1285 /* XXX this could be smarter */
1288 show_oops_s("add_mime_type invalid parameters");
1293 m
= g_malloc(sizeof(*m
));
1295 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1296 show_oops_s("add_mime_type: invalid mime_type");
1299 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1300 mime_type
[strlen(mime_type
) - 1] = '\0';
1305 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1306 show_oops_s("add_mime_type: invalid mime_type");
1310 m
->mt_type
= g_strdup(mime_type
);
1311 m
->mt_action
= g_strdup(l
);
1313 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1314 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1316 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1326 find_mime_type(char *mime_type
)
1328 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1330 TAILQ_FOREACH(m
, &mtl
, entry
) {
1331 if (m
->mt_default
&&
1332 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1335 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1348 walk_mime_type(struct settings
*s
,
1349 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1351 struct mime_type
*m
;
1354 if (s
== NULL
|| cb
== NULL
)
1355 show_oops_s("walk_mime_type invalid parameters");
1357 TAILQ_FOREACH(m
, &mtl
, entry
) {
1358 str
= g_strdup_printf("%s%s --> %s",
1360 m
->mt_default
? "*" : "",
1362 cb(s
, str
, cb_args
);
1368 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1373 if (str
== NULL
|| wl
== NULL
)
1375 if (strlen(str
) < 2)
1378 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1380 /* treat *.moo.com the same as .moo.com */
1381 if (str
[0] == '*' && str
[1] == '.')
1383 else if (str
[0] == '.')
1388 d
= g_malloc(sizeof *d
);
1390 d
->d
= g_strdup_printf(".%s", str
);
1392 d
->d
= g_strdup(str
);
1395 if (RB_INSERT(domain_list
, wl
, d
))
1398 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1409 add_cookie_wl(struct settings
*s
, char *entry
)
1411 wl_add(entry
, &c_wl
, 1);
1416 walk_cookie_wl(struct settings
*s
,
1417 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1421 if (s
== NULL
|| cb
== NULL
) {
1422 show_oops_s("walk_cookie_wl invalid parameters");
1426 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1427 cb(s
, d
->d
, cb_args
);
1431 walk_js_wl(struct settings
*s
,
1432 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1436 if (s
== NULL
|| cb
== NULL
) {
1437 show_oops_s("walk_js_wl invalid parameters");
1441 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1442 cb(s
, d
->d
, cb_args
);
1446 add_js_wl(struct settings
*s
, char *entry
)
1448 wl_add(entry
, &js_wl
, 1 /* persistent */);
1453 wl_find(const gchar
*search
, struct domain_list
*wl
)
1456 struct domain
*d
= NULL
, dfind
;
1459 if (search
== NULL
|| wl
== NULL
)
1461 if (strlen(search
) < 2)
1464 if (search
[0] != '.')
1465 s
= g_strdup_printf(".%s", search
);
1467 s
= g_strdup(search
);
1469 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1472 d
= RB_FIND(domain_list
, wl
, &dfind
);
1486 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1492 if (s
== NULL
|| wl
== NULL
)
1495 if (!strncmp(s
, "http://", strlen("http://")))
1496 s
= &s
[strlen("http://")];
1497 else if (!strncmp(s
, "https://", strlen("https://")))
1498 s
= &s
[strlen("https://")];
1503 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1504 /* chop string at first slash */
1505 if (s
[i
] == '/' || s
[i
] == '\0') {
1508 r
= wl_find(ss
, wl
);
1517 get_toplevel_domain(char *domain
)
1524 if (strlen(domain
) < 2)
1527 s
= &domain
[strlen(domain
) - 1];
1528 while (s
!= domain
) {
1545 config_parse(char *filename
, int runtime
)
1548 char *line
, *cp
, *var
, *val
;
1549 size_t len
, lineno
= 0;
1551 char **s
, file
[PATH_MAX
];
1554 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1556 if (filename
== NULL
)
1559 if (runtime
&& runtime_settings
[0] != '\0') {
1560 snprintf(file
, sizeof file
, "%s/%s",
1561 work_dir
, runtime_settings
);
1562 if (stat(file
, &sb
)) {
1563 warnx("runtime file doesn't exist, creating it");
1564 if ((f
= fopen(file
, "w")) == NULL
)
1566 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1570 strlcpy(file
, filename
, sizeof file
);
1572 if ((config
= fopen(file
, "r")) == NULL
) {
1573 warn("config_parse: cannot open %s", filename
);
1578 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1579 if (feof(config
) || ferror(config
))
1583 cp
+= (long)strspn(cp
, WS
);
1584 if (cp
[0] == '\0') {
1590 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1591 errx(1, "invalid config file entry: %s", line
);
1593 cp
+= (long)strspn(cp
, WS
);
1595 if ((val
= strsep(&cp
, "\0")) == NULL
)
1598 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1601 for (i
= 0, handled
= 0; i
< LENGTH(rs
); i
++) {
1602 if (strcmp(var
, rs
[i
].name
))
1606 if (rs
[i
].s
->set(&rs
[i
], val
))
1607 errx(1, "invalid value for %s", var
);
1611 switch (rs
[i
].type
) {
1620 errx(1, "invalid sval for %s",
1629 errx(1, "invalid type for %s", var
);
1634 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1643 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1649 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1653 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1656 JSStringGetUTF8CString(jsref
, s
, l
);
1657 JSStringRelease(jsref
);
1663 disable_hints(struct tab
*t
)
1665 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1666 bzero(t
->hint_num
, sizeof t
->hint_num
);
1667 run_script(t
, "vimprobable_clear()");
1669 t
->hint_mode
= XT_HINT_NONE
;
1673 enable_hints(struct tab
*t
)
1675 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1676 run_script(t
, "vimprobable_show_hints()");
1678 t
->hint_mode
= XT_HINT_NONE
;
1681 #define XT_JS_OPEN ("open;")
1682 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1683 #define XT_JS_FIRE ("fire;")
1684 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1685 #define XT_JS_FOUND ("found;")
1686 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1689 run_script(struct tab
*t
, char *s
)
1691 JSGlobalContextRef ctx
;
1692 WebKitWebFrame
*frame
;
1694 JSValueRef val
, exception
;
1697 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1698 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1700 frame
= webkit_web_view_get_main_frame(t
->wv
);
1701 ctx
= webkit_web_frame_get_global_context(frame
);
1703 str
= JSStringCreateWithUTF8CString(s
);
1704 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1705 NULL
, 0, &exception
);
1706 JSStringRelease(str
);
1708 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1710 es
= js_ref_to_string(ctx
, exception
);
1711 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1715 es
= js_ref_to_string(ctx
, val
);
1716 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1718 /* handle return value right here */
1719 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1721 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1724 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1725 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1726 &es
[XT_JS_FIRE_LEN
]);
1731 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1732 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1743 hint(struct tab
*t
, struct karg
*args
)
1746 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1748 if (t
->hints_on
== 0)
1757 * Doesn't work fully, due to the following bug:
1758 * https://bugs.webkit.org/show_bug.cgi?id=51747
1761 restore_global_history(void)
1763 char file
[PATH_MAX
];
1769 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1771 if ((f
= fopen(file
, "r")) == NULL
) {
1772 warnx("%s: fopen", __func__
);
1777 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1778 if (feof(f
) || ferror(f
))
1781 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1782 if (feof(f
) || ferror(f
)) {
1784 warnx("%s: broken history file\n", __func__
);
1788 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1789 webkit_web_history_item_new_with_data(uri
, title
);
1790 h
= g_malloc(sizeof(struct history
));
1791 h
->uri
= g_strdup(uri
);
1792 h
->title
= g_strdup(title
);
1793 RB_INSERT(history_list
, &hl
, h
);
1795 warnx("%s: failed to restore history\n", __func__
);
1811 save_global_history_to_disk(struct tab
*t
)
1813 char file
[PATH_MAX
];
1817 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_HISTORY_FILE
);
1819 if ((f
= fopen(file
, "w")) == NULL
) {
1820 show_oops(t
, "%s: global history file: %s",
1821 __func__
, strerror(errno
));
1825 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1826 if (h
->uri
&& h
->title
)
1827 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
1836 quit(struct tab
*t
, struct karg
*args
)
1838 if (save_global_history
)
1839 save_global_history_to_disk(t
);
1847 open_tabs(struct tab
*t
, struct karg
*a
)
1849 char file
[PATH_MAX
];
1853 struct tab
*ti
, *tt
;
1858 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1859 if ((f
= fopen(file
, "r")) == NULL
)
1862 ti
= TAILQ_LAST(&tabs
, tab_list
);
1865 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1866 if (feof(f
) || ferror(f
))
1869 /* retrieve session name */
1870 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
1871 strlcpy(named_session
,
1872 &uri
[strlen(XT_SAVE_SESSION_ID
)],
1873 sizeof named_session
);
1877 if (uri
&& strlen(uri
))
1878 create_new_tab(uri
, NULL
, 1);
1884 /* close open tabs */
1885 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
1887 tt
= TAILQ_FIRST(&tabs
);
1906 restore_saved_tabs(void)
1908 char file
[PATH_MAX
];
1909 int unlink_file
= 0;
1914 snprintf(file
, sizeof file
, "%s/%s",
1915 sessions_dir
, XT_RESTART_TABS_FILE
);
1916 if (stat(file
, &sb
) == -1)
1917 a
.s
= XT_SAVED_TABS_FILE
;
1920 a
.s
= XT_RESTART_TABS_FILE
;
1923 a
.i
= XT_SES_DONOTHING
;
1924 rv
= open_tabs(NULL
, &a
);
1933 save_tabs(struct tab
*t
, struct karg
*a
)
1935 char file
[PATH_MAX
];
1940 const gchar
**arr
= NULL
;
1945 snprintf(file
, sizeof file
, "%s/%s",
1946 sessions_dir
, named_session
);
1948 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1950 if ((f
= fopen(file
, "w")) == NULL
) {
1951 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
1955 /* save session name */
1956 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
1958 /* save tabs, in the order they are arranged in the notebook */
1959 TAILQ_FOREACH(ti
, &tabs
, entry
)
1962 arr
= g_malloc0(len
* sizeof(gchar
*));
1964 TAILQ_FOREACH(ti
, &tabs
, entry
) {
1965 if ((uri
= get_uri(ti
->wv
)) != NULL
)
1966 arr
[gtk_notebook_page_num(notebook
, ti
->vbox
)] = uri
;
1969 for (i
= 0; i
< len
; i
++)
1971 fprintf(f
, "%s\n", arr
[i
]);
1980 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
1992 yank_uri(struct tab
*t
, struct karg
*args
)
1995 GtkClipboard
*clipboard
;
1997 if ((uri
= get_uri(t
->wv
)) == NULL
)
2000 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2001 gtk_clipboard_set_text(clipboard
, uri
, -1);
2012 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
2014 struct paste_args
*pap
;
2016 if (data
== NULL
|| text
== NULL
|| !strlen(text
))
2019 pap
= (struct paste_args
*)data
;
2022 case XT_PASTE_CURRENT_TAB
:
2023 load_uri(pap
->t
, (gchar
*)text
);
2025 case XT_PASTE_NEW_TAB
:
2026 create_new_tab((gchar
*)text
, NULL
, 1);
2034 paste_uri(struct tab
*t
, struct karg
*args
)
2036 GtkClipboard
*clipboard
;
2037 struct paste_args
*pap
;
2039 pap
= g_malloc(sizeof(struct paste_args
));
2044 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
2045 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
2051 find_domain(const gchar
*s
, int add_dot
)
2054 char *r
= NULL
, *ss
= NULL
;
2059 if (!strncmp(s
, "http://", strlen("http://")))
2060 s
= &s
[strlen("http://")];
2061 else if (!strncmp(s
, "https://", strlen("https://")))
2062 s
= &s
[strlen("https://")];
2068 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2069 /* chop string at first slash */
2070 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2073 r
= g_strdup_printf(".%s", ss
);
2084 toggle_cwl(struct tab
*t
, struct karg
*args
)
2088 char *dom
= NULL
, *dom_toggle
= NULL
;
2094 if ((uri
= get_uri(t
->wv
)) == NULL
)
2097 dom
= find_domain(uri
, 1);
2098 d
= wl_find(dom
, &c_wl
);
2104 if (args
->i
& XT_WL_TOGGLE
)
2106 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2108 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2111 if (args
->i
& XT_WL_TOPLEVEL
)
2112 dom_toggle
= get_toplevel_domain(dom
);
2117 /* enable cookies for domain */
2118 wl_add(dom_toggle
, &c_wl
, 0);
2120 /* disable cookies for domain */
2121 RB_REMOVE(domain_list
, &c_wl
, d
);
2123 webkit_web_view_reload(t
->wv
);
2130 toggle_js(struct tab
*t
, struct karg
*args
)
2135 char *dom
= NULL
, *dom_toggle
= NULL
;
2140 g_object_get(G_OBJECT(t
->settings
),
2141 "enable-scripts", &es
, (char *)NULL
);
2142 if (args
->i
& XT_WL_TOGGLE
)
2144 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2146 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2151 if ((uri
= get_uri(t
->wv
)) == NULL
)
2154 dom
= find_domain(uri
, 1);
2155 if (uri
== NULL
|| dom
== NULL
) {
2156 show_oops(t
, "Can't toggle domain in JavaScript white list");
2160 if (args
->i
& XT_WL_TOPLEVEL
)
2161 dom_toggle
= get_toplevel_domain(dom
);
2166 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2167 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2169 d
= wl_find(dom_toggle
, &js_wl
);
2171 RB_REMOVE(domain_list
, &js_wl
, d
);
2172 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2174 g_object_set(G_OBJECT(t
->settings
),
2175 "enable-scripts", es
, (char *)NULL
);
2176 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2177 webkit_web_view_reload(t
->wv
);
2185 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2189 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
2194 toggle_src(struct tab
*t
, struct karg
*args
)
2201 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2202 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2203 webkit_web_view_reload(t
->wv
);
2209 focus_webview(struct tab
*t
)
2214 /* only grab focus if we are visible */
2215 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2216 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2220 focus(struct tab
*t
, struct karg
*args
)
2222 if (t
== NULL
|| args
== NULL
)
2228 if (args
->i
== XT_FOCUS_URI
)
2229 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2230 else if (args
->i
== XT_FOCUS_SEARCH
)
2231 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2237 stats(struct tab
*t
, struct karg
*args
)
2239 char *stats
, *s
, line
[64 * 1024];
2240 uint64_t line_count
= 0;
2244 show_oops_s("stats invalid parameters");
2247 if (save_rejected_cookies
) {
2248 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2250 s
= fgets(line
, sizeof line
, r_cookie_f
);
2251 if (s
== NULL
|| feof(r_cookie_f
) ||
2257 snprintf(line
, sizeof line
,
2258 "<br>Cookies blocked(*) total: %llu", line_count
);
2260 show_oops(t
, "Can't open blocked cookies file: %s",
2264 stats
= g_strdup_printf(XT_DOCTYPE
2267 "<title>Statistics</title>"
2269 "<h1>Statistics</h1>"
2271 "Cookies blocked(*) this session: %llu"
2273 "<p><small><b>*</b> results vary based on settings"
2279 load_webkit_string(t
, stats
);
2286 about(struct tab
*t
, struct karg
*args
)
2291 show_oops_s("about invalid parameters");
2293 about
= g_strdup_printf(XT_DOCTYPE
2296 "<title>About</title>"
2300 "<b>Version: %s</b><p>"
2303 "<li>Marco Peereboom <marco@peereboom.us></li>"
2304 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2305 "<li>Edd Barrett <vext01@gmail.com> </li>"
2306 "<li>Todd T. Fries <todd@fries.net> </li>"
2308 "Copyrights and licenses can be found on the XXXterm "
2309 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
2315 load_webkit_string(t
, about
);
2322 help(struct tab
*t
, struct karg
*args
)
2327 show_oops_s("help invalid parameters");
2332 "<title>XXXterm</title>"
2333 "<meta http-equiv=\"REFRESH\" content=\"0;"
2334 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2337 "XXXterm man page <a href=\"http://opensource.conformal.com/"
2338 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2339 "cgi-bin/man-cgi?xxxterm</a>"
2344 load_webkit_string(t
, help
);
2350 * update all favorite tabs apart from one. Pass NULL if
2351 * you want to update all.
2354 update_favorite_tabs(struct tab
*apart_from
)
2357 if (!updating_fl_tabs
) {
2358 updating_fl_tabs
= 1; /* stop infinite recursion */
2359 TAILQ_FOREACH(t
, &tabs
, entry
)
2360 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2361 && (t
!= apart_from
))
2362 xtp_page_fl(t
, NULL
);
2363 updating_fl_tabs
= 0;
2367 /* show a list of favorites (bookmarks) */
2369 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2371 char file
[PATH_MAX
];
2373 char *uri
= NULL
, *title
= NULL
;
2374 size_t len
, lineno
= 0;
2376 char *header
, *body
, *tmp
, *html
= NULL
;
2378 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2381 warn("%s: bad param", __func__
);
2383 /* mark tab as favorite list */
2384 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2386 /* new session key */
2387 if (!updating_fl_tabs
)
2388 generate_xtp_session_key(&fl_session_key
);
2390 /* open favorites */
2391 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
2392 if ((f
= fopen(file
, "r")) == NULL
) {
2393 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2398 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2399 "<title>Favorites</title>\n"
2402 "<h1>Favorites</h1>\n",
2406 body
= g_strdup_printf("<div align='center'><table><tr>"
2407 "<th style='width: 4%%'>#</th><th>Link</th>"
2408 "<th style='width: 15%%'>Remove</th></tr>\n");
2411 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2412 if (feof(f
) || ferror(f
))
2420 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2421 if (feof(f
) || ferror(f
)) {
2422 show_oops(t
, "favorites file corrupt");
2428 body
= g_strdup_printf("%s<tr>"
2430 "<td><a href='%s'>%s</a></td>"
2431 "<td style='text-align: center'>"
2432 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2434 body
, i
, uri
, title
,
2435 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2447 /* if none, say so */
2450 body
= g_strdup_printf("%s<tr>"
2451 "<td colspan='3' style='text-align: center'>"
2452 "No favorites - To add one use the 'favadd' command."
2453 "</td></tr>", body
);
2464 html
= g_strdup_printf("%s%s</table></div></html>",
2466 load_webkit_string(t
, html
);
2469 update_favorite_tabs(t
);
2482 getparams(char *cmd
, char *cmp
)
2487 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2488 rv
= cmd
+ strlen(cmp
);
2491 if (strlen(rv
) == 0)
2500 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2501 size_t cert_count
, char *title
)
2503 gnutls_datum_t cinfo
;
2504 char *tmp
, *header
, *body
, *footer
;
2507 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2508 footer
= g_strdup("</body></html>");
2509 body
= g_strdup("");
2511 for (i
= 0; i
< cert_count
; i
++) {
2512 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2517 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2518 body
, i
, cinfo
.data
);
2519 gnutls_free(cinfo
.data
);
2523 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2527 load_webkit_string(t
, tmp
);
2532 ca_cmd(struct tab
*t
, struct karg
*args
)
2535 int rv
= 1, certs
= 0, certs_read
;
2538 gnutls_x509_crt_t
*c
= NULL
;
2539 char *certs_buf
= NULL
, *s
;
2541 /* yeah yeah stat race */
2542 if (stat(ssl_ca_file
, &sb
)) {
2543 show_oops(t
, "no CA file: %s", ssl_ca_file
);
2547 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2548 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2552 certs_buf
= g_malloc(sb
.st_size
+ 1);
2553 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2554 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2557 certs_buf
[sb
.st_size
] = '\0';
2560 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2562 s
+= strlen("BEGIN CERTIFICATE");
2565 bzero(&dt
, sizeof dt
);
2566 dt
.data
= certs_buf
;
2567 dt
.size
= sb
.st_size
;
2568 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2569 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
,
2570 GNUTLS_X509_FMT_PEM
, 0);
2571 if (certs_read
<= 0) {
2572 show_oops(t
, "No cert(s) available");
2575 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2588 connect_socket_from_uri(const gchar
*uri
, char *domain
, size_t domain_sz
)
2591 struct addrinfo hints
, *res
= NULL
, *ai
;
2595 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2598 su
= soup_uri_new(uri
);
2601 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2604 snprintf(port
, sizeof port
, "%d", su
->port
);
2605 bzero(&hints
, sizeof(struct addrinfo
));
2606 hints
.ai_flags
= AI_CANONNAME
;
2607 hints
.ai_family
= AF_UNSPEC
;
2608 hints
.ai_socktype
= SOCK_STREAM
;
2610 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2613 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2614 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2617 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2620 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2624 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2629 strlcpy(domain
, su
->host
, domain_sz
);
2640 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2643 gnutls_deinit(gsession
);
2645 gnutls_certificate_free_credentials(xcred
);
2651 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2652 gnutls_certificate_credentials_t
*xc
)
2654 gnutls_certificate_credentials_t xcred
;
2655 gnutls_session_t gsession
;
2658 if (gs
== NULL
|| xc
== NULL
)
2664 gnutls_certificate_allocate_credentials(&xcred
);
2665 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2666 GNUTLS_X509_FMT_PEM
);
2667 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2668 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2669 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2670 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2671 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2672 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2674 gnutls_error_is_fatal(rv
),
2675 gnutls_strerror_name(rv
));
2676 stop_tls(gsession
, xcred
);
2680 gnutls_credentials_type_t cred
;
2681 cred
= gnutls_auth_get_type(gsession
);
2682 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2683 stop_tls(gsession
, xcred
);
2695 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2699 const gnutls_datum_t
*cl
;
2700 gnutls_x509_crt_t
*all_certs
;
2703 if (certs
== NULL
|| cert_count
== NULL
)
2705 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2707 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2711 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2712 for (i
= 0; i
< len
; i
++) {
2713 gnutls_x509_crt_init(&all_certs
[i
]);
2714 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2715 GNUTLS_X509_FMT_PEM
< 0)) {
2729 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2733 for (i
= 0; i
< cert_count
; i
++)
2734 gnutls_x509_crt_deinit(certs
[i
]);
2739 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2740 size_t cert_count
, char *domain
)
2743 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2748 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2751 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2752 if ((f
= fopen(file
, "w")) == NULL
) {
2753 show_oops(t
, "Can't create cert file %s %s",
2754 file
, strerror(errno
));
2758 for (i
= 0; i
< cert_count
; i
++) {
2759 cert_buf_sz
= sizeof cert_buf
;
2760 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2761 cert_buf
, &cert_buf_sz
)) {
2762 show_oops(t
, "gnutls_x509_crt_export failed");
2765 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2766 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2771 /* not the best spot but oh well */
2772 gdk_color_parse("lightblue", &color
);
2773 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2774 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2775 gdk_color_parse("black", &color
);
2776 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2782 load_compare_cert(struct tab
*t
, struct karg
*args
)
2785 char domain
[8182], file
[PATH_MAX
];
2786 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2787 int s
= -1, rv
= 1, i
;
2791 gnutls_session_t gsession
;
2792 gnutls_x509_crt_t
*certs
;
2793 gnutls_certificate_credentials_t xcred
;
2798 if ((uri
= get_uri(t
->wv
)) == NULL
)
2801 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2805 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2806 show_oops(t
, "Start TLS failed");
2811 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2812 show_oops(t
, "Can't get connection certificates");
2816 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2817 if ((f
= fopen(file
, "r")) == NULL
)
2820 for (i
= 0; i
< cert_count
; i
++) {
2821 cert_buf_sz
= sizeof cert_buf
;
2822 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2823 cert_buf
, &cert_buf_sz
)) {
2826 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2827 rv
= -1; /* critical */
2830 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
2831 rv
= -1; /* critical */
2840 free_connection_certs(certs
, cert_count
);
2842 /* we close the socket first for speed */
2845 stop_tls(gsession
, xcred
);
2851 cert_cmd(struct tab
*t
, struct karg
*args
)
2854 char *action
, domain
[8182];
2857 gnutls_session_t gsession
;
2858 gnutls_x509_crt_t
*certs
;
2859 gnutls_certificate_credentials_t xcred
;
2864 if ((action
= getparams(args
->s
, "cert")))
2869 if ((uri
= get_uri(t
->wv
)) == NULL
) {
2870 show_oops(t
, "Invalid URI");
2874 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
2875 show_oops(t
, "Invalid certidicate URI: %s", uri
);
2880 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2881 show_oops(t
, "Start TLS failed");
2886 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2887 show_oops(t
, "get_connection_certs failed");
2891 if (!strcmp(action
, "show"))
2892 show_certs(t
, certs
, cert_count
, "Certificate Chain");
2893 else if (!strcmp(action
, "save"))
2894 save_certs(t
, certs
, cert_count
, domain
);
2896 show_oops(t
, "Invalid command: %s", action
);
2898 free_connection_certs(certs
, cert_count
);
2900 /* we close the socket first for speed */
2903 stop_tls(gsession
, xcred
);
2909 remove_cookie(int index
)
2915 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
2917 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2919 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
2923 print_cookie("remove cookie", c
);
2924 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
2929 soup_cookies_free(cf
);
2935 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
2938 char *tmp
, *header
, *body
, *footer
;
2939 int p_js
= 0, s_js
= 0;
2941 /* we set this to indicate we want to manually do navaction */
2942 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2944 if (g_str_has_prefix(args
, "show a") ||
2945 !strcmp(args
, "show")) {
2949 } else if (g_str_has_prefix(args
, "show p")) {
2950 /* show persistent */
2952 } else if (g_str_has_prefix(args
, "show s")) {
2958 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
2960 footer
= g_strdup("</body></html>");
2961 body
= g_strdup("");
2966 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
2968 RB_FOREACH(d
, domain_list
, wl
) {
2972 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
2980 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
2982 RB_FOREACH(d
, domain_list
, wl
) {
2986 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
2991 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2995 load_webkit_string(t
, tmp
);
3001 wl_save(struct tab
*t
, struct karg
*args
, int js
)
3003 char file
[PATH_MAX
];
3005 char *line
= NULL
, *lt
= NULL
;
3008 char *dom
= NULL
, *dom_save
= NULL
;
3015 if (t
== NULL
|| args
== NULL
)
3018 if (runtime_settings
[0] == '\0')
3021 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
3022 if ((f
= fopen(file
, "r+")) == NULL
)
3025 uri
= get_uri(t
->wv
);
3026 dom
= find_domain(uri
, 1);
3027 if (uri
== NULL
|| dom
== NULL
) {
3028 show_oops(t
, "Can't add domain to %s white list",
3029 js
? "JavaScript" : "cookie");
3033 if (g_str_has_prefix(args
->s
, "save d")) {
3035 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
3036 show_oops(t
, "invalid domain: %s", dom
);
3039 flags
= XT_WL_TOPLEVEL
;
3040 } else if (g_str_has_prefix(args
->s
, "save f") ||
3041 !strcmp(args
->s
, "save")) {
3046 show_oops(t
, "invalid command: %s", args
->s
);
3050 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
3053 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3056 if (!strcmp(line
, lt
))
3062 fprintf(f
, "%s\n", lt
);
3067 d
= wl_find(dom_save
, &js_wl
);
3070 d
= wl_find(dom_save
, &c_wl
);
3073 /* find and add to persistent jar */
3074 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3075 for (;cf
; cf
= cf
->next
) {
3077 if (!strcmp(dom_save
, ci
->domain
) ||
3078 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3079 c
= soup_cookie_copy(ci
);
3080 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3083 soup_cookies_free(cf
);
3101 cookie_cmd(struct tab
*t
, struct karg
*args
)
3106 if ((cmd
= getparams(args
->s
, "cookie")))
3112 if (g_str_has_prefix(cmd
, "show")) {
3113 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
3114 } else if (g_str_has_prefix(cmd
, "save")) {
3117 } else if (g_str_has_prefix(cmd
, "toggle")) {
3119 if (g_str_has_prefix(cmd
, "toggle d"))
3120 a
.i
|= XT_WL_TOPLEVEL
;
3124 } else if (g_str_has_prefix(cmd
, "delete")) {
3125 show_oops(t
, "'cookie delete' currently unimplemented");
3127 show_oops(t
, "unknown cookie command: %s", cmd
);
3133 js_cmd(struct tab
*t
, struct karg
*args
)
3138 if ((cmd
= getparams(args
->s
, "js")))
3143 if (g_str_has_prefix(cmd
, "show")) {
3144 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
3145 } else if (g_str_has_prefix(cmd
, "save")) {
3148 } else if (g_str_has_prefix(cmd
, "toggle")) {
3150 if (g_str_has_prefix(cmd
, "toggle d"))
3151 a
.i
|= XT_WL_TOPLEVEL
;
3155 } else if (g_str_has_prefix(cmd
, "delete")) {
3156 show_oops(t
, "'js delete' currently unimplemented");
3158 show_oops(t
, "unknown js command: %s", cmd
);
3164 add_favorite(struct tab
*t
, struct karg
*args
)
3166 char file
[PATH_MAX
];
3169 size_t urilen
, linelen
;
3170 const gchar
*uri
, *title
;
3175 /* don't allow adding of xtp pages to favorites */
3176 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3177 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3181 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
3182 if ((f
= fopen(file
, "r+")) == NULL
) {
3183 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3187 title
= webkit_web_view_get_title(t
->wv
);
3188 uri
= get_uri(t
->wv
);
3193 if (title
== NULL
|| uri
== NULL
) {
3194 show_oops(t
, "can't add page to favorites");
3198 urilen
= strlen(uri
);
3201 if ((line
= fparseln(f
, &linelen
, NULL
, NULL
, 0)) == NULL
)
3202 if (feof(f
) || ferror(f
))
3205 if (linelen
== urilen
&& !strcmp(line
, uri
))
3212 fprintf(f
, "\n%s\n%s", title
, uri
);
3218 update_favorite_tabs(NULL
);
3224 navaction(struct tab
*t
, struct karg
*args
)
3226 WebKitWebHistoryItem
*item
;
3228 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3229 t
->tab_id
, args
->i
);
3232 if (args
->i
== XT_NAV_BACK
)
3233 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3235 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3237 return (XT_CB_PASSTHROUGH
);
3238 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3240 return (XT_CB_PASSTHROUGH
);
3245 webkit_web_view_go_back(t
->wv
);
3247 case XT_NAV_FORWARD
:
3248 webkit_web_view_go_forward(t
->wv
);
3251 webkit_web_view_reload(t
->wv
);
3253 case XT_NAV_RELOAD_CACHE
:
3254 webkit_web_view_reload_bypass_cache(t
->wv
);
3257 return (XT_CB_PASSTHROUGH
);
3261 move(struct tab
*t
, struct karg
*args
)
3263 GtkAdjustment
*adjust
;
3264 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3269 case XT_MOVE_BOTTOM
:
3271 case XT_MOVE_PAGEDOWN
:
3272 case XT_MOVE_PAGEUP
:
3273 case XT_MOVE_HALFDOWN
:
3274 case XT_MOVE_HALFUP
:
3275 adjust
= t
->adjust_v
;
3278 adjust
= t
->adjust_h
;
3282 pos
= gtk_adjustment_get_value(adjust
);
3283 ps
= gtk_adjustment_get_page_size(adjust
);
3284 upper
= gtk_adjustment_get_upper(adjust
);
3285 lower
= gtk_adjustment_get_lower(adjust
);
3286 si
= gtk_adjustment_get_step_increment(adjust
);
3287 pi
= gtk_adjustment_get_page_increment(adjust
);
3290 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3291 "max %f si %f pi %f\n",
3292 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3293 pos
, ps
, upper
, lower
, max
, si
, pi
);
3299 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3304 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3306 case XT_MOVE_BOTTOM
:
3307 case XT_MOVE_FARRIGHT
:
3308 gtk_adjustment_set_value(adjust
, max
);
3311 case XT_MOVE_FARLEFT
:
3312 gtk_adjustment_set_value(adjust
, lower
);
3314 case XT_MOVE_PAGEDOWN
:
3316 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3318 case XT_MOVE_PAGEUP
:
3320 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3322 case XT_MOVE_HALFDOWN
:
3324 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3326 case XT_MOVE_HALFUP
:
3328 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3331 return (XT_CB_PASSTHROUGH
);
3334 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3336 return (XT_CB_HANDLED
);
3340 url_set_visibility(void)
3344 TAILQ_FOREACH(t
, &tabs
, entry
) {
3345 if (show_url
== 0) {
3346 gtk_widget_hide(t
->toolbar
);
3349 gtk_widget_show(t
->toolbar
);
3354 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3357 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3359 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3363 statusbar_set_visibility(void)
3367 TAILQ_FOREACH(t
, &tabs
, entry
) {
3368 if (show_statusbar
== 0) {
3369 gtk_widget_hide(t
->statusbar
);
3372 gtk_widget_show(t
->statusbar
);
3377 url_set(struct tab
*t
, int enable_url_entry
)
3382 show_url
= enable_url_entry
;
3384 if (enable_url_entry
) {
3385 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
3386 GTK_ENTRY_ICON_PRIMARY
, NULL
);
3387 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
), 0);
3389 pixbuf
= gtk_entry_get_icon_pixbuf(GTK_ENTRY(t
->uri_entry
),
3390 GTK_ENTRY_ICON_PRIMARY
);
3392 gtk_entry_get_progress_fraction(GTK_ENTRY(t
->uri_entry
));
3393 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
3394 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
3395 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
3401 fullscreen(struct tab
*t
, struct karg
*args
)
3403 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3406 return (XT_CB_PASSTHROUGH
);
3408 if (show_url
== 0) {
3416 url_set_visibility();
3417 notebook_tab_set_visibility(notebook
);
3419 return (XT_CB_HANDLED
);
3423 statusaction(struct tab
*t
, struct karg
*args
)
3425 int rv
= XT_CB_HANDLED
;
3427 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3430 return (XT_CB_PASSTHROUGH
);
3433 case XT_STATUSBAR_SHOW
:
3434 if (show_statusbar
== 0) {
3436 statusbar_set_visibility();
3439 case XT_STATUSBAR_HIDE
:
3440 if (show_statusbar
== 1) {
3442 statusbar_set_visibility();
3450 urlaction(struct tab
*t
, struct karg
*args
)
3452 int rv
= XT_CB_HANDLED
;
3454 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3457 return (XT_CB_PASSTHROUGH
);
3461 if (show_url
== 0) {
3463 url_set_visibility();
3467 if (show_url
== 1) {
3469 url_set_visibility();
3477 tabaction(struct tab
*t
, struct karg
*args
)
3479 int rv
= XT_CB_HANDLED
;
3483 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3486 return (XT_CB_PASSTHROUGH
);
3490 if ((url
= getparams(args
->s
, "tabnew")))
3491 create_new_tab(url
, NULL
, 1);
3493 create_new_tab(NULL
, NULL
, 1);
3498 case XT_TAB_DELQUIT
:
3499 if (gtk_notebook_get_n_pages(notebook
) > 1)
3505 if ((url
= getparams(args
->s
, "open")) ||
3506 ((url
= getparams(args
->s
, "op"))) ||
3507 ((url
= getparams(args
->s
, "o"))))
3510 rv
= XT_CB_PASSTHROUGH
;
3516 if (show_tabs
== 0) {
3518 notebook_tab_set_visibility(notebook
);
3522 if (show_tabs
== 1) {
3524 notebook_tab_set_visibility(notebook
);
3527 case XT_TAB_UNDO_CLOSE
:
3528 if (undo_count
== 0) {
3529 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3533 u
= TAILQ_FIRST(&undos
);
3534 create_new_tab(u
->uri
, u
, 1);
3536 TAILQ_REMOVE(&undos
, u
, entry
);
3538 /* u->history is freed in create_new_tab() */
3543 rv
= XT_CB_PASSTHROUGH
;
3557 resizetab(struct tab
*t
, struct karg
*args
)
3559 if (t
== NULL
|| args
== NULL
) {
3560 show_oops_s("resizetab invalid parameters");
3561 return (XT_CB_PASSTHROUGH
);
3564 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3565 t
->tab_id
, args
->i
);
3567 adjustfont_webkit(t
, args
->i
);
3569 return (XT_CB_HANDLED
);
3573 movetab(struct tab
*t
, struct karg
*args
)
3578 if (t
== NULL
|| args
== NULL
) {
3579 show_oops_s("movetab invalid parameters");
3580 return (XT_CB_PASSTHROUGH
);
3583 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3584 t
->tab_id
, args
->i
);
3586 if (args
->i
== XT_TAB_INVALID
)
3587 return (XT_CB_PASSTHROUGH
);
3589 if (args
->i
< XT_TAB_INVALID
) {
3590 /* next or previous tab */
3591 if (TAILQ_EMPTY(&tabs
))
3592 return (XT_CB_PASSTHROUGH
);
3596 /* if at the last page, loop around to the first */
3597 if (gtk_notebook_get_current_page(notebook
) ==
3598 gtk_notebook_get_n_pages(notebook
) - 1)
3599 gtk_notebook_set_current_page(notebook
, 0);
3601 gtk_notebook_next_page(notebook
);
3604 /* if at the first page, loop around to the last */
3605 if (gtk_notebook_current_page(notebook
) == 0)
3606 gtk_notebook_set_current_page(notebook
,
3607 gtk_notebook_get_n_pages(notebook
) - 1);
3609 gtk_notebook_prev_page(notebook
);
3612 gtk_notebook_set_current_page(notebook
, 0);
3615 gtk_notebook_set_current_page(notebook
, -1);
3618 return (XT_CB_PASSTHROUGH
);
3621 return (XT_CB_HANDLED
);
3626 if (t
->tab_id
== x
) {
3627 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3628 return (XT_CB_HANDLED
);
3631 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3632 if (tt
->tab_id
== x
) {
3633 gtk_notebook_set_current_page(notebook
, x
);
3634 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3640 return (XT_CB_HANDLED
);
3644 command(struct tab
*t
, struct karg
*args
)
3646 char *s
= NULL
, *ss
= NULL
;
3650 if (t
== NULL
|| args
== NULL
) {
3651 show_oops_s("command invalid parameters");
3652 return (XT_CB_PASSTHROUGH
);
3671 case XT_CMD_OPEN_CURRENT
:
3674 case XT_CMD_TABNEW_CURRENT
:
3675 if (!s
) /* FALL THROUGH? */
3677 if ((uri
= get_uri(t
->wv
)) != NULL
) {
3678 ss
= g_strdup_printf("%s%s", s
, uri
);
3683 show_oops(t
, "command: invalid opcode %d", args
->i
);
3684 return (XT_CB_PASSTHROUGH
);
3687 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3689 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3690 gdk_color_parse("white", &color
);
3691 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3693 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3694 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3699 return (XT_CB_HANDLED
);
3703 * Return a new string with a download row (in html)
3704 * appended. Old string is freed.
3707 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3710 WebKitDownloadStatus stat
;
3711 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3713 char cur_sz
[FMT_SCALED_STRSIZE
];
3714 char tot_sz
[FMT_SCALED_STRSIZE
];
3717 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3719 /* All actions wil take this form:
3720 * xxxt://class/seskey
3722 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3723 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3725 stat
= webkit_download_get_status(dl
->download
);
3728 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3729 status_html
= g_strdup_printf("Finished");
3730 cmd_html
= g_strdup_printf(
3731 "<a href='%s%d/%d'>Remove</a>",
3732 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3734 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3735 /* gather size info */
3736 progress
= 100 * webkit_download_get_progress(dl
->download
);
3739 webkit_download_get_current_size(dl
->download
), cur_sz
);
3741 webkit_download_get_total_size(dl
->download
), tot_sz
);
3743 status_html
= g_strdup_printf(
3744 "<div style='width: 100%%' align='center'>"
3745 "<div class='progress-outer'>"
3746 "<div class='progress-inner' style='width: %.2f%%'>"
3747 "</div></div></div>"
3748 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
3749 progress
, cur_sz
, tot_sz
, progress
);
3751 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3752 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3756 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3757 status_html
= g_strdup_printf("Cancelled");
3758 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3759 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3761 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3762 status_html
= g_strdup_printf("Error!");
3763 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3764 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3766 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3767 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3768 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3769 status_html
= g_strdup_printf("Starting");
3772 show_oops(t
, "%s: unknown download status", __func__
);
3775 new_html
= g_strdup_printf(
3776 "%s\n<tr><td>%s</td><td>%s</td>"
3777 "<td style='text-align:center'>%s</td></tr>\n",
3778 html
, basename(webkit_download_get_uri(dl
->download
)),
3779 status_html
, cmd_html
);
3783 g_free(status_html
);
3794 * update all download tabs apart from one. Pass NULL if
3795 * you want to update all.
3798 update_download_tabs(struct tab
*apart_from
)
3801 if (!updating_dl_tabs
) {
3802 updating_dl_tabs
= 1; /* stop infinite recursion */
3803 TAILQ_FOREACH(t
, &tabs
, entry
)
3804 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3805 && (t
!= apart_from
))
3806 xtp_page_dl(t
, NULL
);
3807 updating_dl_tabs
= 0;
3812 * update all cookie tabs apart from one. Pass NULL if
3813 * you want to update all.
3816 update_cookie_tabs(struct tab
*apart_from
)
3819 if (!updating_cl_tabs
) {
3820 updating_cl_tabs
= 1; /* stop infinite recursion */
3821 TAILQ_FOREACH(t
, &tabs
, entry
)
3822 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3823 && (t
!= apart_from
))
3824 xtp_page_cl(t
, NULL
);
3825 updating_cl_tabs
= 0;
3830 * update all history tabs apart from one. Pass NULL if
3831 * you want to update all.
3834 update_history_tabs(struct tab
*apart_from
)
3838 if (!updating_hl_tabs
) {
3839 updating_hl_tabs
= 1; /* stop infinite recursion */
3840 TAILQ_FOREACH(t
, &tabs
, entry
)
3841 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
3842 && (t
!= apart_from
))
3843 xtp_page_hl(t
, NULL
);
3844 updating_hl_tabs
= 0;
3848 /* cookie management XTP page */
3850 xtp_page_cl(struct tab
*t
, struct karg
*args
)
3852 char *header
, *body
, *footer
, *page
, *tmp
;
3853 int i
= 1; /* all ids start 1 */
3854 GSList
*sc
, *pc
, *pc_start
;
3856 char *type
, *table_headers
;
3857 char *last_domain
= strdup("");
3859 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3862 show_oops_s("%s invalid parameters", __func__
);
3865 /* mark this tab as cookie jar */
3866 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
3868 /* Generate a new session key */
3869 if (!updating_cl_tabs
)
3870 generate_xtp_session_key(&cl_session_key
);
3873 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3874 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
3875 "</head><body><h1>Cookie Jar</h1>\n");
3878 table_headers
= g_strdup_printf("<div align='center'><table><tr>"
3885 "<th>HTTP<br />only</th>"
3886 "<th>Rm</th></tr>\n");
3888 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
3889 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
3893 for (; sc
; sc
= sc
->next
) {
3896 if (strcmp(last_domain
, c
->domain
) != 0) {
3899 last_domain
= strdup(c
->domain
);
3903 body
= g_strdup_printf("%s</table></div>"
3905 body
, c
->domain
, table_headers
);
3909 body
= g_strdup_printf("<h2>%s</h2>%s\n",
3910 c
->domain
, table_headers
);
3915 for (pc
= pc_start
; pc
; pc
= pc
->next
)
3916 if (soup_cookie_equal(pc
->data
, c
)) {
3917 type
= "Session + Persistent";
3922 body
= g_strdup_printf(
3924 "<td style='width: text-align: center'>%s</td>"
3925 "<td style='width: 1px'>%s</td>"
3926 "<td style='width=70%%;overflow: visible'>"
3927 " <textarea rows='4'>%s</textarea>"
3931 "<td style='width: 1px; text-align: center'>%d</td>"
3932 "<td style='width: 1px; text-align: center'>%d</td>"
3933 "<td style='width: 1px; text-align: center'>"
3934 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3941 soup_date_to_string(c
->expires
, SOUP_DATE_COOKIE
) : "",
3956 soup_cookies_free(sc
);
3957 soup_cookies_free(pc
);
3959 /* small message if there are none */
3961 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3962 "colspan='8'>No Cookies</td></tr>\n", table_headers
);
3966 footer
= g_strdup_printf("</table></div></body></html>");
3968 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3973 g_free(table_headers
);
3974 g_free(last_domain
);
3976 load_webkit_string(t
, page
);
3977 update_cookie_tabs(t
);
3985 xtp_page_hl(struct tab
*t
, struct karg
*args
)
3987 char *header
, *body
, *footer
, *page
, *tmp
;
3989 int i
= 1; /* all ids start 1 */
3991 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3994 show_oops_s("%s invalid parameters", __func__
);
3998 /* mark this tab as history manager */
3999 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
4001 /* Generate a new session key */
4002 if (!updating_hl_tabs
)
4003 generate_xtp_session_key(&hl_session_key
);
4006 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
4007 "<title>History</title>\n"
4010 "<h1>History</h1>\n",
4014 body
= g_strdup_printf("<div align='center'><table><tr>"
4015 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
4017 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
4019 body
= g_strdup_printf(
4021 "<td><a href='%s'>%s</a></td>"
4023 "<td style='text-align: center'>"
4024 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
4025 body
, h
->uri
, h
->uri
, h
->title
,
4026 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
4027 XT_XTP_HL_REMOVE
, i
);
4033 /* small message if there are none */
4036 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4037 "colspan='3'>No History</td></tr>\n", body
);
4042 footer
= g_strdup_printf("</table></div></body></html>");
4044 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4047 * update all history manager tabs as the xtp session
4048 * key has now changed. No need to update the current tab.
4049 * Already did that above.
4051 update_history_tabs(t
);
4057 load_webkit_string(t
, page
);
4064 * Generate a web page detailing the status of any downloads
4067 xtp_page_dl(struct tab
*t
, struct karg
*args
)
4069 struct download
*dl
;
4070 char *header
, *body
, *footer
, *page
, *tmp
;
4074 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
4077 show_oops_s("%s invalid parameters", __func__
);
4080 /* mark as a download manager tab */
4081 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
4084 * Generate a new session key for next page instance.
4085 * This only happens for the top level call to xtp_page_dl()
4086 * in which case updating_dl_tabs is 0.
4088 if (!updating_dl_tabs
)
4089 generate_xtp_session_key(&dl_session_key
);
4091 /* header - with refresh so as to update */
4092 if (refresh_interval
>= 1)
4093 ref
= g_strdup_printf(
4094 "<meta http-equiv='refresh' content='%u"
4095 ";url=%s%d/%s/%d' />\n",
4105 header
= g_strdup_printf(
4107 "<title>Downloads</title>\n%s%s</head>\n",
4108 XT_DOCTYPE XT_HTML_TAG
,
4112 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
4113 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4114 "</p><table><tr><th style='width: 60%%'>"
4115 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4116 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4118 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4119 body
= xtp_page_dl_row(t
, body
, dl
);
4123 /* message if no downloads in list */
4126 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4127 " style='text-align: center'>"
4128 "No downloads</td></tr>\n", body
);
4133 footer
= g_strdup_printf("</table></div></body></html>");
4135 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4139 * update all download manager tabs as the xtp session
4140 * key has now changed. No need to update the current tab.
4141 * Already did that above.
4143 update_download_tabs(t
);
4150 load_webkit_string(t
, page
);
4157 search(struct tab
*t
, struct karg
*args
)
4161 if (t
== NULL
|| args
== NULL
) {
4162 show_oops_s("search invalid parameters");
4165 if (t
->search_text
== NULL
) {
4166 if (global_search
== NULL
)
4167 return (XT_CB_PASSTHROUGH
);
4169 t
->search_text
= g_strdup(global_search
);
4170 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4171 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4175 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4176 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4179 case XT_SEARCH_NEXT
:
4180 d
= t
->search_forward
;
4182 case XT_SEARCH_PREV
:
4183 d
= !t
->search_forward
;
4186 return (XT_CB_PASSTHROUGH
);
4189 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4191 return (XT_CB_HANDLED
);
4194 struct settings_args
{
4200 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4203 struct settings_args
*sa
= cb_args
;
4208 if (s
->flags
& XT_SF_RUNTIME
)
4214 *sa
->body
= g_strdup_printf(
4216 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
4217 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
4229 set(struct tab
*t
, struct karg
*args
)
4231 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
4233 struct settings_args sa
;
4235 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
4236 bzero(&sa
, sizeof sa
);
4240 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4241 "\n<head><title>Settings</title>\n"
4242 "</head><body><h1>Settings</h1>\n");
4245 body
= g_strdup_printf("<div align='center'><table><tr>"
4246 "<th align='left'>Setting</th>"
4247 "<th align='left'>Value</th></tr>\n");
4249 settings_walk(print_setting
, &sa
);
4252 /* small message if there are none */
4255 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4256 "colspan='2'>No settings</td></tr>\n", body
);
4261 footer
= g_strdup_printf("</table></div></body></html>");
4263 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4269 load_webkit_string(t
, page
);
4271 show_oops(t
, "Invalid command: %s", pars
);
4273 return (XT_CB_PASSTHROUGH
);
4277 session_save(struct tab
*t
, char *filename
, char **ret
)
4283 f
+= strlen("save");
4284 while (*f
== ' ' && *f
!= '\0')
4290 if (f
[0] == '.' || f
[0] == '/')
4294 if (save_tabs(t
, &a
))
4296 strlcpy(named_session
, f
, sizeof named_session
);
4304 session_open(struct tab
*t
, char *filename
, char **ret
)
4310 f
+= strlen("open");
4311 while (*f
== ' ' && *f
!= '\0')
4317 if (f
[0] == '.' || f
[0] == '/')
4321 a
.i
= XT_SES_CLOSETABS
;
4322 if (open_tabs(t
, &a
))
4325 strlcpy(named_session
, f
, sizeof named_session
);
4333 session_delete(struct tab
*t
, char *filename
, char **ret
)
4335 char file
[PATH_MAX
];
4339 f
+= strlen("delete");
4340 while (*f
== ' ' && *f
!= '\0')
4346 if (f
[0] == '.' || f
[0] == '/')
4349 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, f
);
4353 if (!strcmp(f
, named_session
))
4354 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4355 sizeof named_session
);
4363 session_cmd(struct tab
*t
, struct karg
*args
)
4365 char *action
= NULL
;
4366 char *filename
= NULL
;
4371 if ((action
= getparams(args
->s
, "session")))
4376 if (!strcmp(action
, "show"))
4377 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4378 XT_SAVED_TABS_FILE
: named_session
);
4379 else if (g_str_has_prefix(action
, "save ")) {
4380 if (session_save(t
, action
, &filename
)) {
4381 show_oops(t
, "Can't save session: %s",
4382 filename
? filename
: "INVALID");
4385 } else if (g_str_has_prefix(action
, "open ")) {
4386 if (session_open(t
, action
, &filename
)) {
4387 show_oops(t
, "Can't open session: %s",
4388 filename
? filename
: "INVALID");
4391 } else if (g_str_has_prefix(action
, "delete ")) {
4392 if (session_delete(t
, action
, &filename
)) {
4393 show_oops(t
, "Can't delete session: %s",
4394 filename
? filename
: "INVALID");
4398 show_oops(t
, "Invalid command: %s", action
);
4400 return (XT_CB_PASSTHROUGH
);
4404 * Make a hardcopy of the page
4407 print_page(struct tab
*t
, struct karg
*args
)
4409 WebKitWebFrame
*frame
;
4411 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4414 * for now we just call the GTK print box,
4415 * but later we might decide to hook in a command.
4417 frame
= webkit_web_view_get_main_frame(t
->wv
);
4418 webkit_web_frame_print(frame
);
4424 go_home(struct tab
*t
, struct karg
*args
)
4431 restart(struct tab
*t
, struct karg
*args
)
4435 a
.s
= XT_RESTART_TABS_FILE
;
4437 execvp(start_argv
[0], start_argv
);
4443 #define CTRL GDK_CONTROL_MASK
4444 #define MOD1 GDK_MOD1_MASK
4445 #define SHFT GDK_SHIFT_MASK
4447 /* inherent to GTK not all keys will be caught at all times */
4448 /* XXX sort key bindings */
4449 struct key_binding
{
4454 int (*func
)(struct tab
*, struct karg
*);
4456 TAILQ_ENTRY(key_binding
) entry
; /* in bss so no need to init */
4458 { "cookiejar", MOD1
, 0, GDK_j
, xtp_page_cl
, {0} },
4459 { "downloadmgr", MOD1
, 0, GDK_d
, xtp_page_dl
, {0} },
4460 { "history", MOD1
, 0, GDK_h
, xtp_page_hl
, {0} },
4461 { "print", CTRL
, 0, GDK_p
, print_page
, {0}},
4462 { NULL
, 0, 0, GDK_slash
, command
, {.i
= '/'} },
4463 { NULL
, 0, 0, GDK_question
, command
, {.i
= '?'} },
4464 { NULL
, 0, 0, GDK_colon
, command
, {.i
= ':'} },
4465 { "quit", CTRL
, 0, GDK_q
, quit
, {0} },
4466 { "restart", MOD1
, 0, GDK_q
, restart
, {0} },
4467 { "togglejs", CTRL
, 0, GDK_j
, toggle_js
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4468 { "togglecookie", MOD1
, 0, GDK_c
, toggle_cwl
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4469 { "togglesrc", CTRL
, 0, GDK_s
, toggle_src
, {0} },
4470 { "yankuri", 0, 0, GDK_y
, yank_uri
, {0} },
4471 { "pasteuricur", 0, 0, GDK_p
, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
} },
4472 { "pasteurinew", 0, 0, GDK_P
, paste_uri
, {.i
= XT_PASTE_NEW_TAB
} },
4475 { "searchnext", 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
4476 { "searchprev", 0, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
4479 { "focusaddress", 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
4480 { "focussearch", 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
4482 /* command aliases (handy when -S flag is used) */
4483 { NULL
, 0, 0, GDK_F9
, command
, {.i
= XT_CMD_OPEN
} },
4484 { NULL
, 0, 0, GDK_F10
, command
, {.i
= XT_CMD_OPEN_CURRENT
} },
4485 { NULL
, 0, 0, GDK_F11
, command
, {.i
= XT_CMD_TABNEW
} },
4486 { NULL
, 0, 0, GDK_F12
, command
, {.i
= XT_CMD_TABNEW_CURRENT
} },
4489 { "hinting", 0, 0, GDK_f
, hint
, {.i
= 0} },
4492 { "goback", 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
4493 { "goback", MOD1
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
4494 { "goforward", SHFT
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
4495 { "goforward", MOD1
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
4496 { "reload", 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
4497 { "reload", CTRL
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
4498 { "reloadforce", CTRL
, 0, GDK_R
, navaction
, {.i
= XT_NAV_RELOAD_CACHE
} },
4499 { "reload" , CTRL
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
4500 { "favorites", MOD1
, 1, GDK_f
, xtp_page_fl
, {0} },
4502 /* vertical movement */
4503 { "scrolldown", 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
4504 { "scrolldown", 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
4505 { "scrollup", 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
4506 { "scrollup", 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
4507 { "scrollbottom", 0, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
4508 { "scrollbottom", 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
4509 { "scrolltop", 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
4510 { "scrolltop", 0, 0, GDK_g
, move
, {.i
= XT_MOVE_TOP
} },
4511 { "scrollpagedown", 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4512 { "scrollpagedown", CTRL
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4513 { "scrollhalfdown", CTRL
, 0, GDK_d
, move
, {.i
= XT_MOVE_HALFDOWN
} },
4514 { "scrollpagedown", 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4515 { "scrollpageup", 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
4516 { "scrollpageup", CTRL
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
4517 { "scrollhalfup", CTRL
, 0, GDK_u
, move
, {.i
= XT_MOVE_HALFUP
} },
4518 /* horizontal movement */
4519 { "scrollright", 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
4520 { "scrollright", 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
4521 { "scrollleft", 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
4522 { "scrollleft", 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
4523 { "scrollfarright", 0, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
4524 { "scrollfarleft", 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
4527 { "tabnew", CTRL
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
4528 { "tabclose", CTRL
, 1, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
4529 { "tabundoclose", 0, 0, GDK_U
, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
4530 { "tabgoto1", CTRL
, 0, GDK_1
, movetab
, {.i
= 1} },
4531 { "tabgoto2", CTRL
, 0, GDK_2
, movetab
, {.i
= 2} },
4532 { "tabgoto3", CTRL
, 0, GDK_3
, movetab
, {.i
= 3} },
4533 { "tabgoto4", CTRL
, 0, GDK_4
, movetab
, {.i
= 4} },
4534 { "tabgoto5", CTRL
, 0, GDK_5
, movetab
, {.i
= 5} },
4535 { "tabgoto6", CTRL
, 0, GDK_6
, movetab
, {.i
= 6} },
4536 { "tabgoto7", CTRL
, 0, GDK_7
, movetab
, {.i
= 7} },
4537 { "tabgoto8", CTRL
, 0, GDK_8
, movetab
, {.i
= 8} },
4538 { "tabgoto9", CTRL
, 0, GDK_9
, movetab
, {.i
= 9} },
4539 { "tabgoto10", CTRL
, 0, GDK_0
, movetab
, {.i
= 10} },
4540 { "tabgotofirst", CTRL
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
4541 { "tabgotolast", CTRL
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
4542 { "tabgotoprev", CTRL
, 0, GDK_Left
, movetab
, {.i
= XT_TAB_PREV
} },
4543 { "tabgotonext", CTRL
, 0, GDK_Right
, movetab
, {.i
= XT_TAB_NEXT
} },
4544 { "focusout", CTRL
, 0, GDK_minus
, resizetab
, {.i
= -1} },
4545 { "focusin", CTRL
, 0, GDK_plus
, resizetab
, {.i
= 1} },
4546 { "focusin", CTRL
, 0, GDK_equal
, resizetab
, {.i
= 1} },
4548 TAILQ_HEAD(keybinding_list
, key_binding
);
4551 walk_kb(struct settings
*s
,
4552 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
4554 struct key_binding
*k
;
4557 if (s
== NULL
|| cb
== NULL
) {
4558 show_oops_s("walk_kb invalid parameters");
4562 TAILQ_FOREACH(k
, &kbl
, entry
) {
4563 if (k
->name
== NULL
)
4567 strlcat(str
, k
->name
, sizeof str
);
4568 strlcat(str
, ",", sizeof str
);
4570 if (k
->mask
& GDK_SHIFT_MASK
)
4571 strlcat(str
, "S+", sizeof str
);
4572 if (k
->mask
& GDK_CONTROL_MASK
)
4573 strlcat(str
, "C+", sizeof str
);
4574 if (k
->mask
& GDK_MOD1_MASK
)
4575 strlcat(str
, "M1+", sizeof str
);
4576 if (k
->mask
& GDK_MOD2_MASK
)
4577 strlcat(str
, "M2+", sizeof str
);
4578 if (k
->mask
& GDK_MOD3_MASK
)
4579 strlcat(str
, "M3+", sizeof str
);
4580 if (k
->mask
& GDK_MOD4_MASK
)
4581 strlcat(str
, "M4+", sizeof str
);
4582 if (k
->mask
& GDK_MOD5_MASK
)
4583 strlcat(str
, "M5+", sizeof str
);
4585 strlcat(str
, gdk_keyval_name(k
->key
), sizeof str
);
4586 cb(s
, str
, cb_args
);
4590 init_keybindings(void)
4593 struct key_binding
*k
;
4595 for (i
= 0; i
< LENGTH(keys
); i
++) {
4596 k
= g_malloc0(sizeof *k
);
4597 k
->name
= keys
[i
].name
;
4598 k
->mask
= keys
[i
].mask
;
4599 k
->use_in_entry
= keys
[i
].use_in_entry
;
4600 k
->key
= keys
[i
].key
;
4601 k
->func
= keys
[i
].func
;
4602 bcopy(&keys
[i
].arg
, &k
->arg
, sizeof k
->arg
);
4603 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4605 DNPRINTF(XT_D_KEYBINDING
, "init_keybindings: added: %s\n",
4606 k
->name
? k
->name
: "unnamed key");
4611 keybinding_clearall(void)
4613 struct key_binding
*k
, *next
;
4615 for (k
= TAILQ_FIRST(&kbl
); k
!= TAILQ_LAST(&kbl
, keybinding_list
);
4617 next
= TAILQ_NEXT(k
, entry
);
4618 if (k
->name
== NULL
)
4621 DNPRINTF(XT_D_KEYBINDING
, "keybinding_clearall: %s\n",
4622 k
->name
? k
->name
: "unnamed key");
4623 TAILQ_REMOVE(&kbl
, k
, entry
);
4629 keybinding_add(char *kb
, struct key_binding
*orig
)
4631 struct key_binding
*k
;
4632 char *name
, *value
, *s
;
4633 guint keyval
, mask
= 0;
4636 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s\n", kb
);
4638 s
= strstr(kb
, ",");
4646 /* find modifier keys */
4647 if (strstr(value
, "S+"))
4648 mask
|= GDK_SHIFT_MASK
;
4649 if (strstr(value
, "C+"))
4650 mask
|= GDK_CONTROL_MASK
;
4651 if (strstr(value
, "M1+"))
4652 mask
|= GDK_MOD1_MASK
;
4653 if (strstr(value
, "M2+"))
4654 mask
|= GDK_MOD2_MASK
;
4655 if (strstr(value
, "M3+"))
4656 mask
|= GDK_MOD3_MASK
;
4657 if (strstr(value
, "M4+"))
4658 mask
|= GDK_MOD4_MASK
;
4659 if (strstr(value
, "M5+"))
4660 mask
|= GDK_MOD5_MASK
;
4663 for (i
= strlen(value
) - 1; i
> 0; i
--)
4664 if (value
[i
] == '+')
4665 value
= &value
[i
+ 1];
4667 /* validate keyname */
4668 keyval
= gdk_keyval_from_name(value
);
4669 if (keyval
== GDK_VoidSymbol
) {
4670 warnx("invalid keybinding name %s", value
);
4675 k
= g_malloc0(sizeof *k
);
4678 k
->use_in_entry
= orig
->use_in_entry
;
4680 k
->func
= orig
->func
;
4681 bcopy(&orig
->arg
, &k
->arg
, sizeof k
->arg
);
4683 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: %s 0x%x %d 0x%x\n",
4688 DNPRINTF(XT_D_KEYBINDING
, "keybinding_add: adding: %s %s\n",
4689 name
, gdk_keyval_name(keyval
));
4691 TAILQ_INSERT_HEAD(&kbl
, k
, entry
);
4697 add_kb(struct settings
*s
, char *entry
)
4701 DNPRINTF(XT_D_KEYBINDING
, "add_kb: %s\n", entry
);
4703 /* clearall is special */
4704 if (!strcmp(entry
, "clearall")) {
4705 keybinding_clearall();
4709 /* make sure it is a valid keybinding */
4710 for (i
= 0; i
< LENGTH(keys
); i
++)
4711 if (keys
[i
].name
&& !strncmp(entry
, keys
[i
].name
,
4712 strlen(keys
[i
].name
))) {
4713 DNPRINTF(XT_D_KEYBINDING
, "%s 0x%x %d 0x%x\n",
4716 keys
[i
].use_in_entry
,
4719 return (keybinding_add(entry
, &keys
[i
]));
4728 int (*func
)(struct tab
*, struct karg
*);
4731 { "q!", 0, quit
, {0} },
4732 { "qa", 0, quit
, {0} },
4733 { "qa!", 0, quit
, {0} },
4734 { "w", 0, save_tabs
, {0} },
4735 { "wq", 0, save_tabs_and_quit
, {0} },
4736 { "wq!", 0, save_tabs_and_quit
, {0} },
4737 { "help", 0, help
, {0} },
4738 { "about", 0, about
, {0} },
4739 { "stats", 0, stats
, {0} },
4740 { "version", 0, about
, {0} },
4741 { "cookies", 0, xtp_page_cl
, {0} },
4742 { "fav", 0, xtp_page_fl
, {0} },
4743 { "favadd", 0, add_favorite
, {0} },
4744 { "js", 2, js_cmd
, {0} },
4745 { "cookie", 2, cookie_cmd
, {0} },
4746 { "cert", 1, cert_cmd
, {0} },
4747 { "ca", 0, ca_cmd
, {0} },
4748 { "dl", 0, xtp_page_dl
, {0} },
4749 { "h", 0, xtp_page_hl
, {0} },
4750 { "hist", 0, xtp_page_hl
, {0} },
4751 { "history", 0, xtp_page_hl
, {0} },
4752 { "home", 0, go_home
, {0} },
4753 { "restart", 0, restart
, {0} },
4754 { "urlhide", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4755 { "urlh", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4756 { "urlshow", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4757 { "urls", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4758 { "statushide", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4759 { "statush", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4760 { "statusshow", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4761 { "statuss", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4763 { "1", 0, move
, {.i
= XT_MOVE_TOP
} },
4764 { "print", 0, print_page
, {0} },
4767 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4768 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4769 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4770 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4771 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4772 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4773 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4774 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4775 { "tabshow", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4776 { "tabs", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4777 { "tabhide", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4778 { "tabh", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4779 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4780 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4781 /* XXX add count to these commands */
4782 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4783 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4784 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4785 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4786 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
4787 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
4788 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
4789 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
4790 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4791 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4794 { "set", 1, set
, {0} },
4795 { "fullscreen", 0, fullscreen
, {0} },
4796 { "f", 0, fullscreen
, {0} },
4799 { "session", 1, session_cmd
, {0} },
4803 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4811 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4813 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
4815 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
4822 * cancel, remove, etc. downloads
4825 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
4827 struct download find
, *d
;
4829 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
4831 /* some commands require a valid download id */
4832 if (cmd
!= XT_XTP_DL_LIST
) {
4833 /* lookup download in question */
4835 d
= RB_FIND(download_list
, &downloads
, &find
);
4838 show_oops(t
, "%s: no such download", __func__
);
4843 /* decide what to do */
4845 case XT_XTP_DL_CANCEL
:
4846 webkit_download_cancel(d
->download
);
4848 case XT_XTP_DL_REMOVE
:
4849 webkit_download_cancel(d
->download
); /* just incase */
4850 g_object_unref(d
->download
);
4851 RB_REMOVE(download_list
, &downloads
, d
);
4853 case XT_XTP_DL_LIST
:
4857 show_oops(t
, "%s: unknown command", __func__
);
4860 xtp_page_dl(t
, NULL
);
4864 * Actions on history, only does one thing for now, but
4865 * we provide the function for future actions
4868 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
4870 struct history
*h
, *next
;
4874 case XT_XTP_HL_REMOVE
:
4875 /* walk backwards, as listed in reverse */
4876 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
4877 next
= RB_PREV(history_list
, &hl
, h
);
4879 RB_REMOVE(history_list
, &hl
, h
);
4880 g_free((gpointer
) h
->title
);
4881 g_free((gpointer
) h
->uri
);
4888 case XT_XTP_HL_LIST
:
4889 /* Nothing - just xtp_page_hl() below */
4892 show_oops(t
, "%s: unknown command", __func__
);
4896 xtp_page_hl(t
, NULL
);
4899 /* remove a favorite */
4901 remove_favorite(struct tab
*t
, int index
)
4903 char file
[PATH_MAX
], *title
, *uri
;
4904 char *new_favs
, *tmp
;
4909 /* open favorites */
4910 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
4912 if ((f
= fopen(file
, "r")) == NULL
) {
4913 show_oops(t
, "%s: can't open favorites: %s",
4914 __func__
, strerror(errno
));
4918 /* build a string which will become the new favroites file */
4919 new_favs
= g_strdup_printf("%s", "");
4922 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
4923 if (feof(f
) || ferror(f
))
4925 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
4932 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
4933 if (feof(f
) || ferror(f
)) {
4934 show_oops(t
, "%s: can't parse favorites %s",
4935 __func__
, strerror(errno
));
4940 /* as long as this isn't the one we are deleting add to file */
4943 new_favs
= g_strdup_printf("%s%s\n%s\n",
4944 new_favs
, title
, uri
);
4956 /* write back new favorites file */
4957 if ((f
= fopen(file
, "w")) == NULL
) {
4958 show_oops(t
, "%s: can't open favorites: %s",
4959 __func__
, strerror(errno
));
4963 fwrite(new_favs
, strlen(new_favs
), 1, f
);
4976 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
4979 case XT_XTP_FL_LIST
:
4980 /* nothing, just the below call to xtp_page_fl() */
4982 case XT_XTP_FL_REMOVE
:
4983 remove_favorite(t
, arg
);
4986 show_oops(t
, "%s: invalid favorites command", __func__
);
4990 xtp_page_fl(t
, NULL
);
4994 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
4997 case XT_XTP_CL_LIST
:
4998 /* nothing, just xtp_page_cl() */
5000 case XT_XTP_CL_REMOVE
:
5004 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
5008 xtp_page_cl(t
, NULL
);
5011 /* link an XTP class to it's session key and handler function */
5012 struct xtp_despatch
{
5015 void (*handle_func
)(struct tab
*, uint8_t, int);
5018 struct xtp_despatch xtp_despatches
[] = {
5019 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
5020 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
5021 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
5022 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
5023 { NULL
, NULL
, NULL
}
5027 * is the url xtp protocol? (xxxt://)
5028 * if so, parse and despatch correct bahvior
5031 parse_xtp_url(struct tab
*t
, const char *url
)
5033 char *dup
= NULL
, *p
, *last
;
5034 uint8_t n_tokens
= 0;
5035 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
5036 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
5040 * tokens array meaning:
5042 * tokens[1] = session key
5043 * tokens[2] = action
5044 * tokens[3] = optional argument
5047 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
5049 /*xtp tab meaning is normal unless proven special */
5050 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5052 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
5055 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
5057 /* split out the url */
5058 for ((p
= strtok_r(dup
, "/", &last
)); p
;
5059 (p
= strtok_r(NULL
, "/", &last
))) {
5061 tokens
[n_tokens
++] = p
;
5064 /* should be atleast three fields 'class/seskey/command/arg' */
5068 dsp
= xtp_despatches
;
5069 req_class
= atoi(tokens
[0]);
5070 while (dsp
->xtp_class
!= NULL
) {
5071 if (dsp
->xtp_class
== req_class
) {
5078 /* did we find one atall? */
5079 if (dsp_match
== NULL
) {
5080 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
5084 /* check session key and call despatch function */
5085 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
5086 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
5099 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5101 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
5103 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
5106 show_oops_s("activate_uri_entry_cb invalid parameters");
5111 show_oops(t
, "activate_uri_entry_cb no uri");
5115 uri
+= strspn(uri
, "\t ");
5117 /* if xxxt:// treat specially */
5118 if (!parse_xtp_url(t
, uri
)) {
5119 load_uri(t
, (gchar
*)uri
);
5125 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
5127 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
5128 char *newuri
= NULL
;
5131 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
5134 show_oops_s("activate_search_entry_cb invalid parameters");
5138 if (search_string
== NULL
) {
5139 show_oops(t
, "no search_string");
5143 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
5144 newuri
= g_strdup_printf(search_string
, enc_search
);
5147 webkit_web_view_load_uri(t
->wv
, newuri
);
5155 check_and_set_js(const gchar
*uri
, struct tab
*t
)
5157 struct domain
*d
= NULL
;
5160 if (uri
== NULL
|| t
== NULL
)
5163 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
5168 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
5169 es
? "enable" : "disable", uri
);
5171 g_object_set(G_OBJECT(t
->settings
),
5172 "enable-scripts", es
, (char *)NULL
);
5173 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5175 button_set_stockid(t
->js_toggle
,
5176 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
5180 show_ca_status(struct tab
*t
, const char *uri
)
5182 WebKitWebFrame
*frame
;
5183 WebKitWebDataSource
*source
;
5184 WebKitNetworkRequest
*request
;
5185 SoupMessage
*message
;
5187 gchar
*col_str
= "white";
5190 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
5191 ssl_strict_certs
, ssl_ca_file
, uri
);
5195 if (ssl_ca_file
== NULL
) {
5196 if (g_str_has_prefix(uri
, "http://"))
5198 if (g_str_has_prefix(uri
, "https://")) {
5204 if (g_str_has_prefix(uri
, "http://") ||
5205 !g_str_has_prefix(uri
, "https://"))
5208 frame
= webkit_web_view_get_main_frame(t
->wv
);
5209 source
= webkit_web_frame_get_data_source(frame
);
5210 request
= webkit_web_data_source_get_request(source
);
5211 message
= webkit_network_request_get_message(request
);
5213 if (message
&& (soup_message_get_flags(message
) &
5214 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
5218 r
= load_compare_cert(t
, NULL
);
5220 col_str
= "lightblue";
5229 gdk_color_parse(col_str
, &color
);
5230 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
5232 if (!strcmp(col_str
, "white")) {
5233 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5235 gdk_color_parse("black", &color
);
5236 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5239 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
5241 gdk_color_parse("black", &color
);
5242 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
5249 free_favicon(struct tab
*t
)
5251 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p pix %p\n",
5252 __func__
, t
->icon_download
, t
->icon_request
, t
->icon_pixbuf
);
5254 if (t
->icon_request
)
5255 g_object_unref(t
->icon_request
);
5257 g_object_unref(t
->icon_pixbuf
);
5258 if (t
->icon_dest_uri
)
5259 g_free(t
->icon_dest_uri
);
5261 t
->icon_pixbuf
= NULL
;
5262 t
->icon_request
= NULL
;
5263 t
->icon_dest_uri
= NULL
;
5267 xt_icon_from_name(struct tab
*t
, gchar
*name
)
5269 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5270 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5271 if (show_url
== 0) {
5272 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5273 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5275 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5276 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5282 xt_icon_from_pixbuf(struct tab
*t
, GdkPixbuf
*pixbuf
)
5284 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5285 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5286 if (show_url
== 0) {
5287 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->statusbar
),
5288 GTK_ENTRY_ICON_PRIMARY
, pixbuf
);
5290 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->statusbar
),
5291 GTK_ENTRY_ICON_PRIMARY
, NULL
);
5296 abort_favicon_download(struct tab
*t
)
5298 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
5300 if (t
->icon_download
)
5301 webkit_download_cancel(t
->icon_download
);
5305 xt_icon_from_name(t
, "text-html");
5309 set_favicon_from_file(struct tab
*t
, char *file
)
5312 GdkPixbuf
*pixbuf
, *scaled
;
5315 if (t
== NULL
|| file
== NULL
)
5317 if (t
->icon_pixbuf
) {
5318 DNPRINTF(XT_D_DOWNLOAD
, "%s: icon already set\n", __func__
);
5322 if (g_str_has_prefix(file
, "file://"))
5323 file
+= strlen("file://");
5324 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5326 if (!stat(file
, &sb
)) {
5327 if (sb
.st_size
== 0) {
5328 /* corrupt icon so trash it */
5329 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5332 /* no need to set icon to default here */
5337 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
5338 if (pixbuf
== NULL
) {
5339 xt_icon_from_name(t
, "text-html");
5343 g_object_get(pixbuf
, "width", &width
, "height", &height
,
5345 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
5346 __func__
, t
->tab_id
, width
, height
);
5348 if (width
> 16 || height
> 16) {
5349 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5350 GDK_INTERP_BILINEAR
);
5351 g_object_unref(pixbuf
);
5355 if (scaled
== NULL
) {
5356 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5357 GDK_INTERP_BILINEAR
);
5361 t
->icon_pixbuf
= scaled
;
5362 xt_icon_from_pixbuf(t
, t
->icon_pixbuf
);
5366 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5369 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5374 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5375 __func__
, t
->tab_id
, status
);
5378 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5380 t
->icon_download
= NULL
;
5383 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5386 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5389 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5391 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5392 __func__
, t
->tab_id
);
5393 t
->icon_download
= NULL
;
5396 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5398 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5399 __func__
, t
->icon_dest_uri
);
5400 set_favicon_from_file(t
, t
->icon_dest_uri
);
5401 /* these will be freed post callback */
5402 t
->icon_request
= NULL
;
5403 t
->icon_download
= NULL
;
5411 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5413 gchar
*name_hash
, file
[PATH_MAX
];
5416 DNPRINTF(XT_D_DOWNLOAD
, "notify_icon_loaded_cb %s\n", uri
);
5418 if (uri
== NULL
|| t
== NULL
)
5421 if (t
->icon_request
) {
5422 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5426 /* check to see if we got the icon in cache */
5427 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5428 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5431 if (!stat(file
, &sb
)) {
5432 if (sb
.st_size
> 0) {
5433 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5435 set_favicon_from_file(t
, file
);
5439 /* corrupt icon so trash it */
5440 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5445 /* create download for icon */
5446 t
->icon_request
= webkit_network_request_new(uri
);
5447 if (t
->icon_request
== NULL
) {
5448 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5453 t
->icon_download
= webkit_download_new(t
->icon_request
);
5455 /* we have to free icon_dest_uri later */
5456 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5457 webkit_download_set_destination_uri(t
->icon_download
,
5460 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5461 G_CALLBACK(favicon_download_status_changed_cb
), t
);
5463 webkit_download_start(t
->icon_download
);
5467 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5469 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5470 struct history
*h
, find
;
5472 const gchar
*s_loading
;
5475 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d\n",
5476 webkit_web_view_get_load_status(wview
));
5479 show_oops_s("notify_load_status_cb invalid paramters");
5483 switch (webkit_web_view_get_load_status(wview
)) {
5484 case WEBKIT_LOAD_PROVISIONAL
:
5486 abort_favicon_download(t
);
5487 #if GTK_CHECK_VERSION(2, 20, 0)
5488 gtk_widget_show(t
->spinner
);
5489 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5491 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5493 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5497 case WEBKIT_LOAD_COMMITTED
:
5499 if ((uri
= get_uri(wview
)) != NULL
) {
5500 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5506 set_status(t
, (char *)uri
, XT_STATUS_LOADING
);
5509 /* check if js white listing is enabled */
5510 if (enable_js_whitelist
) {
5511 uri
= get_uri(wview
);
5512 check_and_set_js(uri
, t
);
5515 show_ca_status(t
, uri
);
5517 /* we know enough to autosave the session */
5518 if (session_autosave
) {
5524 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
5526 title
= webkit_web_view_get_title(wview
);
5527 uri
= get_uri(wview
);
5535 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
5536 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
5539 if (!strncmp(uri
, "http://", strlen("http://")) ||
5540 !strncmp(uri
, "https://", strlen("https://")) ||
5541 !strncmp(uri
, "file://", strlen("file://")))
5546 h
= RB_FIND(history_list
, &hl
, &find
);
5550 h
= g_malloc(sizeof *h
);
5551 h
->uri
= g_strdup(uri
);
5553 h
->title
= g_strdup(title
);
5555 h
->title
= g_strdup(uri
);
5556 RB_INSERT(history_list
, &hl
, h
);
5557 update_history_tabs(NULL
);
5562 case WEBKIT_LOAD_FINISHED
:
5564 uri
= get_uri(wview
);
5565 set_status(t
, (char *)uri
, XT_STATUS_URI
);
5566 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5567 case WEBKIT_LOAD_FAILED
:
5570 #if GTK_CHECK_VERSION(2, 20, 0)
5571 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5572 gtk_widget_hide(t
->spinner
);
5574 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
5575 if (s_loading
&& !strcmp(s_loading
, "Loading"))
5576 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5578 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
5583 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
5585 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
5586 webkit_web_view_can_go_back(wview
));
5588 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
5589 webkit_web_view_can_go_forward(wview
));
5591 /* take focus if we are visible */
5597 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5599 run_script(t
, JS_HINTING
);
5603 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
5605 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
5606 progress
== 100 ? 0 : (double)progress
/ 100);
5607 if (show_url
== 0) {
5608 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->statusbar
),
5609 progress
== 100 ? 0 : (double)progress
/ 100);
5614 webview_nw_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5615 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5616 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5621 show_oops_s("webview_nw_cb invalid paramters");
5625 DNPRINTF(XT_D_NAV
, "webview_nw_cb: %s\n",
5626 webkit_network_request_get_uri(request
));
5628 /* open in current tab */
5629 uri
= (char *)webkit_network_request_get_uri(request
);
5630 webkit_web_view_load_uri(t
->wv
, uri
);
5631 webkit_web_policy_decision_ignore(pd
);
5633 return (TRUE
); /* we made the decission */
5637 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5638 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5639 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5644 show_oops_s("webview_npd_cb invalid parameters");
5648 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
5650 webkit_network_request_get_uri(request
));
5652 uri
= (char *)webkit_network_request_get_uri(request
);
5654 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
5656 create_new_tab(uri
, NULL
, ctrl_click_focus
);
5657 webkit_web_policy_decision_ignore(pd
);
5658 return (TRUE
); /* we made the decission */
5661 webkit_web_policy_decision_use(pd
);
5662 return (TRUE
); /* we made the decission */
5666 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5668 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
5669 webkit_web_view_get_uri(wv
));
5675 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
5677 /* we can not eat the event without throwing gtk off so defer it */
5679 /* catch middle click */
5680 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
5685 /* catch ctrl click */
5686 if (e
->type
== GDK_BUTTON_RELEASE
&&
5687 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
5692 return (XT_CB_PASSTHROUGH
);
5696 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
5698 struct mime_type
*m
;
5700 m
= find_mime_type(mime_type
);
5706 show_oops(t
, "can't fork mime handler");
5715 execlp(m
->mt_action
, m
->mt_action
,
5716 webkit_network_request_get_uri(request
), (void *)NULL
);
5725 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
5726 WebKitNetworkRequest
*request
, char *mime_type
,
5727 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
5730 show_oops_s("webview_mimetype_cb invalid parameters");
5734 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
5735 t
->tab_id
, mime_type
);
5737 if (run_mimehandler(t
, mime_type
, request
) == 0) {
5738 webkit_web_policy_decision_ignore(decision
);
5743 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
5744 webkit_web_policy_decision_download(decision
);
5752 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
5755 const gchar
*filename
;
5757 struct download
*download_entry
;
5760 if (wk_download
== NULL
|| t
== NULL
) {
5761 show_oops_s("%s invalid parameters", __func__
);
5765 filename
= webkit_download_get_suggested_filename(wk_download
);
5766 if (filename
== NULL
)
5767 return (FALSE
); /* abort download */
5769 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
5771 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
5772 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
5774 webkit_download_set_destination_uri(wk_download
, uri
);
5776 if (webkit_download_get_status(wk_download
) ==
5777 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5778 show_oops(t
, "%s: download failed to start", __func__
);
5780 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
5782 download_entry
= g_malloc(sizeof(struct download
));
5783 download_entry
->download
= wk_download
;
5784 download_entry
->tab
= t
;
5785 download_entry
->id
= next_download_id
++;
5786 RB_INSERT(download_list
, &downloads
, download_entry
);
5787 /* get from history */
5788 g_object_ref(wk_download
);
5789 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
5795 /* sync other download manager tabs */
5796 update_download_tabs(NULL
);
5799 * NOTE: never redirect/render the current tab before this
5800 * function returns. This will cause the download to never start.
5802 return (ret
); /* start download */
5806 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
5808 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
5811 show_oops_s("webview_hover_cb");
5816 set_status(t
, uri
, XT_STATUS_LINK
);
5819 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
5824 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
5826 char s
[2], buf
[128];
5827 const char *errstr
= NULL
;
5830 /* don't use w directly; use t->whatever instead */
5833 show_oops_s("wv_keypress_after_cb");
5834 return (XT_CB_PASSTHROUGH
);
5837 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
5838 e
->keyval
, e
->state
, t
);
5842 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
5844 return (XT_CB_HANDLED
);
5848 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
5849 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
5851 /* we have a string */
5853 /* we have a number */
5854 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
5862 /* XXX unfuck this */
5863 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
5864 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
5865 /* last input was numerical */
5867 l
= strlen(t
->hint_num
);
5874 t
->hint_num
[l
] = '\0';
5878 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
5879 /* last input was alphanumerical */
5881 l
= strlen(t
->hint_buf
);
5888 t
->hint_buf
[l
] = '\0';
5898 /* numerical input */
5899 if (CLEAN(e
->state
) == 0 &&
5900 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
5901 snprintf(s
, sizeof s
, "%c", e
->keyval
);
5902 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
5903 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
5906 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
5908 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
5911 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
5913 t
->hint_mode
= XT_HINT_NUMERICAL
;
5917 /* empty the counter buffer */
5918 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
5919 return (XT_CB_HANDLED
);
5922 /* alphanumerical input */
5924 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
5925 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
5926 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
5927 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
5928 snprintf(s
, sizeof s
, "%c", e
->keyval
);
5929 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
5930 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
5933 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
5936 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
5938 t
->hint_mode
= XT_HINT_ALPHANUM
;
5941 /* empty the counter buffer */
5942 bzero(t
->hint_num
, sizeof t
->hint_num
);
5943 return (XT_CB_HANDLED
);
5946 return (XT_CB_HANDLED
);
5949 struct key_binding
*k
;
5950 TAILQ_FOREACH(k
, &kbl
, entry
)
5951 if (e
->keyval
== k
->key
) {
5953 if ((e
->state
& (CTRL
| MOD1
)) == 0) {
5954 k
->func(t
, &k
->arg
);
5955 return (XT_CB_HANDLED
);
5958 else if ((e
->state
& k
->mask
) == k
->mask
) {
5959 k
->func(t
, &k
->arg
);
5960 return (XT_CB_HANDLED
);
5964 return (XT_CB_PASSTHROUGH
);
5968 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5972 return (XT_CB_PASSTHROUGH
);
5976 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5978 const gchar
*c
= gtk_entry_get_text(w
);
5982 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
5983 e
->keyval
, e
->state
, t
);
5986 show_oops_s("cmd_keyrelease_cb invalid parameters");
5987 return (XT_CB_PASSTHROUGH
);
5990 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
5991 e
->keyval
, e
->state
, t
);
5995 if (strlen(c
) == 1) {
5996 webkit_web_view_unmark_text_matches(t
->wv
);
6002 else if (c
[0] == '?')
6008 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
6010 /* not found, mark red */
6011 gdk_color_parse("red", &color
);
6012 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6013 /* unmark and remove selection */
6014 webkit_web_view_unmark_text_matches(t
->wv
);
6015 /* my kingdom for a way to unselect text in webview */
6017 /* found, highlight all */
6018 webkit_web_view_unmark_text_matches(t
->wv
);
6019 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
6020 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
6021 gdk_color_parse("white", &color
);
6022 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
6025 return (XT_CB_PASSTHROUGH
);
6030 cmd_complete(struct tab
*t
, char *s
)
6033 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
6035 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
6037 for (i
= 0; i
< LENGTH(cmds
); i
++) {
6038 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
6039 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
6041 gtk_entry_set_text(w
, ":");
6042 gtk_entry_append_text(w
, cmds
[i
].cmd
);
6043 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
6053 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6056 show_oops_s("entry_key_cb invalid parameters");
6057 return (XT_CB_PASSTHROUGH
);
6060 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
6061 e
->keyval
, e
->state
, t
);
6065 if (e
->keyval
== GDK_Escape
) {
6066 /* don't use focus_webview(t) because we want to type :cmds */
6067 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
6070 struct key_binding
*k
;
6071 TAILQ_FOREACH(k
, &kbl
, entry
)
6072 if (e
->keyval
== k
->key
&& k
->use_in_entry
) {
6074 if ((e
->state
& (CTRL
| MOD1
)) == 0) {
6075 k
->func(t
, &k
->arg
);
6076 return (XT_CB_HANDLED
);
6079 else if ((e
->state
& k
->mask
) == k
->mask
) {
6080 k
->func(t
, &k
->arg
);
6081 return (XT_CB_HANDLED
);
6085 return (XT_CB_PASSTHROUGH
);
6089 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
6091 int rv
= XT_CB_HANDLED
;
6092 const gchar
*c
= gtk_entry_get_text(w
);
6095 show_oops_s("cmd_keypress_cb parameters");
6096 return (XT_CB_PASSTHROUGH
);
6099 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
6100 e
->keyval
, e
->state
, t
);
6104 e
->keyval
= GDK_Escape
;
6105 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6106 e
->keyval
= GDK_Escape
;
6108 switch (e
->keyval
) {
6114 if (strchr (c
, ' ')) {
6115 /* par completion */
6116 fprintf(stderr
, "completeme par\n");
6120 cmd_complete(t
, (char *)&c
[1]);
6125 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
6133 if (c
[0] == '/' || c
[0] == '?')
6134 webkit_web_view_unmark_text_matches(t
->wv
);
6138 rv
= XT_CB_PASSTHROUGH
;
6144 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
6147 show_oops_s("cmd_focusout_cb invalid parameters");
6148 return (XT_CB_PASSTHROUGH
);
6150 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
6155 if (show_url
== 0 || t
->focus_wv
)
6158 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6160 return (XT_CB_PASSTHROUGH
);
6164 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
6168 const gchar
*c
= gtk_entry_get_text(entry
);
6171 show_oops_s("cmd_activate_cb invalid parameters");
6175 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
6180 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
6186 if (c
[0] == '/' || c
[0] == '?') {
6187 if (t
->search_text
) {
6188 g_free(t
->search_text
);
6189 t
->search_text
= NULL
;
6192 t
->search_text
= g_strdup(s
);
6194 g_free(global_search
);
6195 global_search
= g_strdup(s
);
6196 t
->search_forward
= c
[0] == '/';
6201 for (i
= 0; i
< LENGTH(cmds
); i
++)
6202 if (cmds
[i
].params
) {
6203 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
6204 cmds
[i
].arg
.s
= g_strdup(s
);
6205 goto execute_command
;
6208 if (!strcmp(s
, cmds
[i
].cmd
))
6209 goto execute_command
;
6211 show_oops(t
, "Invalid command: %s", s
);
6218 cmds
[i
].func(t
, &cmds
[i
].arg
);
6221 backward_cb(GtkWidget
*w
, struct tab
*t
)
6226 show_oops_s("backward_cb invalid parameters");
6230 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
6237 forward_cb(GtkWidget
*w
, struct tab
*t
)
6242 show_oops_s("forward_cb invalid parameters");
6246 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
6248 a
.i
= XT_NAV_FORWARD
;
6253 stop_cb(GtkWidget
*w
, struct tab
*t
)
6255 WebKitWebFrame
*frame
;
6258 show_oops_s("stop_cb invalid parameters");
6262 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
6264 frame
= webkit_web_view_get_main_frame(t
->wv
);
6265 if (frame
== NULL
) {
6266 show_oops(t
, "stop_cb: no frame");
6270 webkit_web_frame_stop_loading(frame
);
6271 abort_favicon_download(t
);
6275 setup_webkit(struct tab
*t
)
6277 g_object_set(G_OBJECT(t
->settings
),
6278 "user-agent", t
->user_agent
, (char *)NULL
);
6279 g_object_set(G_OBJECT(t
->settings
),
6280 "enable-scripts", enable_scripts
, (char *)NULL
);
6281 g_object_set(G_OBJECT(t
->settings
),
6282 "enable-plugins", enable_plugins
, (char *)NULL
);
6283 adjustfont_webkit(t
, XT_FONT_SET
);
6285 webkit_web_view_set_settings(t
->wv
, t
->settings
);
6289 create_browser(struct tab
*t
)
6295 show_oops_s("create_browser invalid parameters");
6299 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
6300 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
6301 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
6302 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
6304 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
6305 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
6306 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
6308 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
6309 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
6312 t
->settings
= webkit_web_settings_new();
6314 if (user_agent
== NULL
) {
6315 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
6317 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
6320 t
->user_agent
= g_strdup(user_agent
);
6333 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
6334 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
6335 gtk_widget_set_name(w
, "xxxterm");
6336 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
6337 g_signal_connect(G_OBJECT(w
), "delete_event",
6338 G_CALLBACK (gtk_main_quit
), NULL
);
6344 create_toolbar(struct tab
*t
)
6346 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
6348 b
= gtk_hbox_new(FALSE
, 0);
6350 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
6353 /* backward button */
6354 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
6355 gtk_widget_set_sensitive(t
->backward
, FALSE
);
6356 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
6357 G_CALLBACK(backward_cb
), t
);
6358 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
6360 /* forward button */
6361 t
->forward
= create_button("GoForward",GTK_STOCK_GO_FORWARD
, 0);
6362 gtk_widget_set_sensitive(t
->forward
, FALSE
);
6363 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
6364 G_CALLBACK(forward_cb
), t
);
6365 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
6369 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
6370 gtk_widget_set_sensitive(t
->stop
, FALSE
);
6371 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
6372 G_CALLBACK(stop_cb
), t
);
6373 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
6377 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
6378 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
6379 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
6380 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
6381 G_CALLBACK(js_toggle_cb
), t
);
6382 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
6385 t
->uri_entry
= gtk_entry_new();
6386 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
6387 G_CALLBACK(activate_uri_entry_cb
), t
);
6388 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
6389 G_CALLBACK(entry_key_cb
), t
);
6390 eb1
= gtk_hbox_new(FALSE
, 0);
6391 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
6392 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
6393 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
6396 if (fancy_bar
&& search_string
) {
6398 t
->search_entry
= gtk_entry_new();
6399 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
6400 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
6401 G_CALLBACK(activate_search_entry_cb
), t
);
6402 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
6403 G_CALLBACK(entry_key_cb
), t
);
6404 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
6405 eb2
= gtk_hbox_new(FALSE
, 0);
6406 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
6407 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
6409 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
6419 TAILQ_FOREACH(t
, &tabs
, entry
)
6420 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
6424 undo_close_tab_save(struct tab
*t
)
6428 struct undo
*u1
, *u2
;
6430 WebKitWebHistoryItem
*item
;
6432 if ((uri
= get_uri(t
->wv
)) == NULL
)
6435 u1
= g_malloc0(sizeof(struct undo
));
6436 u1
->uri
= g_strdup(uri
);
6438 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6440 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
6441 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
6444 /* forward history */
6445 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
6449 u1
->history
= g_list_prepend(u1
->history
,
6450 webkit_web_history_item_copy(item
));
6451 items
= g_list_next(items
);
6456 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
6457 u1
->history
= g_list_prepend(u1
->history
,
6458 webkit_web_history_item_copy(item
));
6462 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
6466 u1
->history
= g_list_prepend(u1
->history
,
6467 webkit_web_history_item_copy(item
));
6468 items
= g_list_next(items
);
6471 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
6473 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
6474 u2
= TAILQ_LAST(&undos
, undo_tailq
);
6475 TAILQ_REMOVE(&undos
, u2
, entry
);
6477 g_list_free(u2
->history
);
6486 delete_tab(struct tab
*t
)
6490 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
6495 TAILQ_REMOVE(&tabs
, t
, entry
);
6497 /* halt all webkit activity */
6498 abort_favicon_download(t
);
6499 webkit_web_view_stop_loading(t
->wv
);
6500 undo_close_tab_save(t
);
6502 gtk_widget_destroy(t
->vbox
);
6503 g_free(t
->user_agent
);
6507 if (TAILQ_EMPTY(&tabs
))
6508 create_new_tab(NULL
, NULL
, 1);
6511 /* recreate session */
6512 if (session_autosave
) {
6519 adjustfont_webkit(struct tab
*t
, int adjust
)
6522 show_oops_s("adjustfont_webkit invalid parameters");
6526 if (adjust
== XT_FONT_SET
)
6527 t
->font_size
= default_font_size
;
6529 t
->font_size
+= adjust
;
6530 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
6531 t
->font_size
, (char *)NULL
);
6532 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
6533 &t
->font_size
, (char *)NULL
);
6537 append_tab(struct tab
*t
)
6542 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
6543 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
6547 create_new_tab(char *title
, struct undo
*u
, int focus
)
6550 int load
= 1, id
, notfound
;
6552 WebKitWebHistoryItem
*item
;
6555 PangoFontDescription
*fd
= NULL
;
6557 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
6559 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
6560 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
6564 t
= g_malloc0(sizeof *t
);
6566 if (title
== NULL
) {
6567 title
= "(untitled)";
6571 t
->vbox
= gtk_vbox_new(FALSE
, 0);
6573 /* label + button for tab */
6574 b
= gtk_hbox_new(FALSE
, 0);
6577 #if GTK_CHECK_VERSION(2, 20, 0)
6578 t
->spinner
= gtk_spinner_new ();
6580 t
->label
= gtk_label_new(title
);
6581 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
6582 gtk_widget_set_size_request(t
->label
, 100, 0);
6583 gtk_widget_set_size_request(b
, 130, 0);
6585 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
6586 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
6587 #if GTK_CHECK_VERSION(2, 20, 0)
6588 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
6592 t
->toolbar
= create_toolbar(t
);
6593 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
6596 t
->browser_win
= create_browser(t
);
6597 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
6599 /* oops message for user feedback */
6600 t
->oops
= gtk_entry_new();
6601 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
6602 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
6603 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
6604 gdk_color_parse("red", &color
);
6605 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
6606 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
6609 t
->cmd
= gtk_entry_new();
6610 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
6611 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
6612 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
6615 t
->statusbar
= gtk_entry_new();
6616 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
6617 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
6618 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
6619 gdk_color_parse("black", &color
);
6620 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6621 gdk_color_parse("white", &color
);
6622 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6623 fd
= GTK_WIDGET(t
->statusbar
)->style
->font_desc
;
6624 pango_font_description_set_weight(fd
, PANGO_WEIGHT_SEMIBOLD
);
6625 pango_font_description_set_absolute_size(fd
, 10 * PANGO_SCALE
); /* 10 px font */
6626 gtk_widget_modify_font(t
->statusbar
, fd
);
6627 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
6629 /* xtp meaning is normal by default */
6630 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6632 /* set empty favicon */
6633 xt_icon_from_name(t
, "text-html");
6635 /* and show it all */
6636 gtk_widget_show_all(b
);
6637 gtk_widget_show_all(t
->vbox
);
6639 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
6643 id
= gtk_notebook_get_current_page(notebook
);
6644 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6645 if (tt
->tab_id
== id
) {
6647 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
6648 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
6658 #if GTK_CHECK_VERSION(2, 20, 0)
6659 /* turn spinner off if we are a new tab without uri */
6661 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6662 gtk_widget_hide(t
->spinner
);
6665 /* make notebook tabs reorderable */
6666 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
6668 g_object_connect(G_OBJECT(t
->cmd
),
6669 "signal::key-press-event", G_CALLBACK(cmd_keypress_cb
), t
,
6670 "signal::key-release-event", G_CALLBACK(cmd_keyrelease_cb
), t
,
6671 "signal::focus-out-event", G_CALLBACK(cmd_focusout_cb
), t
,
6672 "signal::activate", G_CALLBACK(cmd_activate_cb
), t
,
6675 /* reuse wv_button_cb to hide oops */
6676 g_object_connect(G_OBJECT(t
->oops
),
6677 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
6680 g_object_connect(G_OBJECT(t
->wv
),
6681 "signal::key-press-event", G_CALLBACK(wv_keypress_cb
), t
,
6682 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
6683 "signal::hovering-over-link", G_CALLBACK(webview_hover_cb
), t
,
6684 "signal::download-requested", G_CALLBACK(webview_download_cb
), t
,
6685 "signal::mime-type-policy-decision-requested", G_CALLBACK(webview_mimetype_cb
), t
,
6686 "signal::navigation-policy-decision-requested", G_CALLBACK(webview_npd_cb
), t
,
6687 "signal::new-window-policy-decision-requested", G_CALLBACK(webview_nw_cb
), t
,
6688 "signal::create-web-view", G_CALLBACK(webview_cwv_cb
), t
,
6689 "signal::event", G_CALLBACK(webview_event_cb
), t
,
6690 "signal::load-finished", G_CALLBACK(webview_load_finished_cb
), t
,
6691 "signal::load-progress-changed", G_CALLBACK(webview_progress_changed_cb
), t
,
6692 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6693 "signal::icon-loaded", G_CALLBACK(notify_icon_loaded_cb
), t
,
6695 "signal::button_press_event", G_CALLBACK(wv_button_cb
), t
,
6697 g_signal_connect(t
->wv
,
6698 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
6700 /* hijack the unused keys as if we were the browser */
6701 g_object_connect(G_OBJECT(t
->toolbar
),
6702 "signal-after::key-press-event", G_CALLBACK(wv_keypress_after_cb
), t
,
6705 g_signal_connect(G_OBJECT(bb
), "button_press_event",
6706 G_CALLBACK(tab_close_cb
), t
);
6711 url_set_visibility();
6712 statusbar_set_visibility();
6715 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
6716 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
6721 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), title
);
6725 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6730 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6731 /* restore the tab's history */
6732 if (u
&& u
->history
) {
6736 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
6737 items
= g_list_next(items
);
6740 item
= g_list_nth_data(u
->history
, u
->back
);
6742 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
6745 g_list_free(u
->history
);
6747 webkit_web_back_forward_list_clear(t
->bfl
);
6751 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
6757 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
6759 TAILQ_FOREACH(t
, &tabs
, entry
) {
6760 if (t
->tab_id
== pn
) {
6761 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
6764 uri
= webkit_web_view_get_title(t
->wv
);
6767 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
6779 menuitem_response(struct tab
*t
)
6781 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
6785 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
6787 GtkWidget
*menu
, *menu_items
;
6788 GdkEventButton
*bevent
;
6792 if (event
->type
== GDK_BUTTON_PRESS
) {
6793 bevent
= (GdkEventButton
*) event
;
6794 menu
= gtk_menu_new();
6796 TAILQ_FOREACH(ti
, &tabs
, entry
) {
6797 if ((uri
= get_uri(ti
->wv
)) == NULL
)
6798 /* XXX make sure there is something to print */
6799 /* XXX add gui pages in here to look purdy */
6801 menu_items
= gtk_menu_item_new_with_label(uri
);
6802 gtk_menu_append(GTK_MENU (menu
), menu_items
);
6803 gtk_widget_show(menu_items
);
6805 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
6806 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
6810 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
6811 bevent
->button
, bevent
->time
);
6813 /* unref object so it'll free itself when popped down */
6814 g_object_ref_sink(menu
);
6815 g_object_unref(menu
);
6817 return (TRUE
/* eat event */);
6820 return (FALSE
/* propagate */);
6824 icon_size_map(int icon_size
)
6826 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
6827 icon_size
> GTK_ICON_SIZE_DIALOG
)
6828 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
6834 create_button(char *name
, char *stockid
, int size
)
6836 GtkWidget
*button
, *image
;
6839 rcstring
= g_strdup_printf(
6840 "style \"%s-style\"\n"
6842 " GtkWidget::focus-padding = 0\n"
6843 " GtkWidget::focus-line-width = 0\n"
6847 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
6848 gtk_rc_parse_string(rcstring
);
6850 button
= gtk_button_new();
6851 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
6852 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
6854 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
6855 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
6856 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
6857 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
6858 gtk_widget_set_name(button
, name
);
6859 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
6860 gtk_widget_set_tooltip_text(button
, name
);
6866 button_set_stockid(GtkWidget
*button
, char *stockid
)
6869 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
6870 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
6871 gtk_button_set_image(GTK_BUTTON(button
), image
);
6880 char file
[PATH_MAX
];
6883 vbox
= gtk_vbox_new(FALSE
, 0);
6884 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
6885 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
6886 gtk_notebook_set_tab_hborder(notebook
, 0);
6887 gtk_notebook_set_tab_vborder(notebook
, 0);
6888 gtk_notebook_set_scrollable(notebook
, TRUE
);
6889 notebook_tab_set_visibility(notebook
);
6890 gtk_notebook_set_show_border(notebook
, FALSE
);
6891 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
6893 abtn
= gtk_button_new();
6894 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
6895 gtk_widget_set_size_request(arrow
, -1, -1);
6896 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
6897 gtk_widget_set_size_request(abtn
, -1, 20);
6898 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
6900 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
6901 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
6902 gtk_widget_set_size_request(vbox
, -1, -1);
6904 g_object_connect(G_OBJECT(notebook
),
6905 "signal::switch-page", G_CALLBACK(notebook_switchpage_cb
), NULL
,
6907 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
6908 G_CALLBACK(arrow_cb
), NULL
);
6910 main_window
= create_window();
6911 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
6912 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6915 for (i
= 0; i
< LENGTH(icons
); i
++) {
6916 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
6917 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6918 l
= g_list_append(l
, pb
);
6920 gtk_window_set_default_icon_list(l
);
6922 gtk_widget_show_all(abtn
);
6923 gtk_widget_show_all(main_window
);
6927 set_hook(void **hook
, char *name
)
6930 errx(1, "set_hook");
6932 if (*hook
== NULL
) {
6933 *hook
= dlsym(RTLD_NEXT
, name
);
6935 errx(1, "can't hook %s", name
);
6939 /* override libsoup soup_cookie_equal because it doesn't look at domain */
6941 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
6943 g_return_val_if_fail(cookie1
, FALSE
);
6944 g_return_val_if_fail(cookie2
, FALSE
);
6946 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
6947 !strcmp (cookie1
->value
, cookie2
->value
) &&
6948 !strcmp (cookie1
->path
, cookie2
->path
) &&
6949 !strcmp (cookie1
->domain
, cookie2
->domain
));
6953 transfer_cookies(void)
6956 SoupCookie
*sc
, *pc
;
6958 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
6960 for (;cf
; cf
= cf
->next
) {
6962 sc
= soup_cookie_copy(pc
);
6963 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
6966 soup_cookies_free(cf
);
6970 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
6975 print_cookie("soup_cookie_jar_delete_cookie", c
);
6977 if (cookies_enabled
== 0)
6980 if (jar
== NULL
|| c
== NULL
)
6983 /* find and remove from persistent jar */
6984 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
6986 for (;cf
; cf
= cf
->next
) {
6988 if (soup_cookie_equal(ci
, c
)) {
6989 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
6994 soup_cookies_free(cf
);
6996 /* delete from session jar */
6997 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
7001 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
7003 struct domain
*d
= NULL
;
7007 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
7008 jar
, p_cookiejar
, s_cookiejar
);
7010 if (cookies_enabled
== 0)
7013 /* see if we are up and running */
7014 if (p_cookiejar
== NULL
) {
7015 _soup_cookie_jar_add_cookie(jar
, cookie
);
7018 /* disallow p_cookiejar adds, shouldn't happen */
7019 if (jar
== p_cookiejar
)
7022 if (enable_cookie_whitelist
&&
7023 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
7025 DNPRINTF(XT_D_COOKIE
,
7026 "soup_cookie_jar_add_cookie: reject %s\n",
7028 if (save_rejected_cookies
) {
7029 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
) {
7030 show_oops_s("can't open reject cookie file");
7033 fseek(r_cookie_f
, 0, SEEK_END
);
7034 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
7035 cookie
->http_only
? "#HttpOnly_" : "",
7037 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
7039 cookie
->secure
? "TRUE" : "FALSE",
7041 (gulong
)soup_date_to_time_t(cookie
->expires
) :
7048 if (!allow_volatile_cookies
)
7052 if (cookie
->expires
== NULL
&& session_timeout
) {
7053 soup_cookie_set_expires(cookie
,
7054 soup_date_new_from_now(session_timeout
));
7055 print_cookie("modified add cookie", cookie
);
7058 /* see if we are white listed for persistence */
7059 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
7060 /* add to persistent jar */
7061 c
= soup_cookie_copy(cookie
);
7062 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
7063 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
7066 /* add to session jar */
7067 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
7068 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
7074 char file
[PATH_MAX
];
7076 set_hook((void *)&_soup_cookie_jar_add_cookie
,
7077 "soup_cookie_jar_add_cookie");
7078 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
7079 "soup_cookie_jar_delete_cookie");
7081 if (cookies_enabled
== 0)
7085 * the following code is intricate due to overriding several libsoup
7087 * do not alter order of these operations.
7090 /* rejected cookies */
7091 if (save_rejected_cookies
)
7092 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
7094 /* persistent cookies */
7095 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
7096 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
7098 /* session cookies */
7099 s_cookiejar
= soup_cookie_jar_new();
7100 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
7101 cookie_policy
, (void *)NULL
);
7104 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
7108 setup_proxy(char *uri
)
7111 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
7112 soup_uri_free(proxy_uri
);
7116 if (http_proxy
!= uri
) {
7123 http_proxy
= g_strdup(uri
);
7124 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
7125 proxy_uri
= soup_uri_new(http_proxy
);
7126 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
7131 send_url_to_socket(char *url
)
7133 int s
, len
, rv
= -1;
7134 struct sockaddr_un sa
;
7136 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7137 warnx("send_url_to_socket: socket");
7141 sa
.sun_family
= AF_UNIX
;
7142 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7143 work_dir
, XT_SOCKET_FILE
);
7146 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7147 warnx("send_url_to_socket: connect");
7151 if (send(s
, url
, strlen(url
) + 1, 0) == -1) {
7152 warnx("send_url_to_socket: send");
7161 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
7164 char str
[XT_MAX_URL_LENGTH
];
7165 socklen_t t
= sizeof(struct sockaddr_un
);
7166 struct sockaddr_un sa
;
7171 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
7172 warn("socket_watcher: accept");
7176 if (getpeereid(s
, &uid
, &gid
) == -1) {
7177 warn("socket_watcher: getpeereid");
7180 if (uid
!= getuid() || gid
!= getgid()) {
7181 warnx("socket_watcher: unauthorized user");
7187 warnx("socket_watcher: not a valid user");
7191 n
= recv(s
, str
, sizeof(str
), 0);
7195 create_new_tab(str
, NULL
, 1);
7202 struct sockaddr_un sa
;
7204 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7205 warn("is_running: socket");
7209 sa
.sun_family
= AF_UNIX
;
7210 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7211 work_dir
, XT_SOCKET_FILE
);
7214 /* connect to see if there is a listener */
7215 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
7216 rv
= 0; /* not running */
7218 rv
= 1; /* already running */
7229 struct sockaddr_un sa
;
7231 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
7232 warn("build_socket: socket");
7236 sa
.sun_family
= AF_UNIX
;
7237 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s",
7238 work_dir
, XT_SOCKET_FILE
);
7241 /* connect to see if there is a listener */
7242 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7243 /* no listener so we will */
7244 unlink(sa
.sun_path
);
7246 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
7247 warn("build_socket: bind");
7251 if (listen(s
, 1) == -1) {
7252 warn("build_socket: listen");
7268 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
7273 main(int argc
, char *argv
[])
7276 int c
, s
, optn
= 0, focus
= 1;
7277 char conf
[PATH_MAX
] = { '\0' };
7278 char file
[PATH_MAX
];
7279 char *env_proxy
= NULL
;
7282 struct sigaction sact
;
7286 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
7288 while ((c
= getopt(argc
, argv
, "STVf:s:tn")) != -1) {
7297 errx(0 , "Version: %s", version
);
7300 strlcpy(conf
, optarg
, sizeof(conf
));
7303 strlcpy(named_session
, optarg
, sizeof(named_session
));
7321 RB_INIT(&downloads
);
7325 TAILQ_INIT(&aliases
);
7331 gnutls_global_init();
7333 /* generate session keys for xtp pages */
7334 generate_xtp_session_key(&dl_session_key
);
7335 generate_xtp_session_key(&hl_session_key
);
7336 generate_xtp_session_key(&cl_session_key
);
7337 generate_xtp_session_key(&fl_session_key
);
7340 gtk_init(&argc
, &argv
);
7341 if (!g_thread_supported())
7342 g_thread_init(NULL
);
7345 bzero(&sact
, sizeof(sact
));
7346 sigemptyset(&sact
.sa_mask
);
7347 sact
.sa_handler
= sigchild
;
7348 sact
.sa_flags
= SA_NOCLDSTOP
;
7349 sigaction(SIGCHLD
, &sact
, NULL
);
7351 /* set download dir */
7352 pwd
= getpwuid(getuid());
7354 errx(1, "invalid user %d", getuid());
7355 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
7357 /* set default string settings */
7358 home
= g_strdup("http://www.peereboom.us");
7359 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
7360 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
7361 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
7363 /* read config file */
7364 if (strlen(conf
) == 0)
7365 snprintf(conf
, sizeof conf
, "%s/.%s",
7366 pwd
->pw_dir
, XT_CONF_FILE
);
7367 config_parse(conf
, 0);
7369 /* working directory */
7370 if (strlen(work_dir
) == 0)
7371 snprintf(work_dir
, sizeof work_dir
, "%s/%s",
7372 pwd
->pw_dir
, XT_DIR
);
7373 if (stat(work_dir
, &sb
)) {
7374 if (mkdir(work_dir
, S_IRWXU
) == -1)
7375 err(1, "mkdir work_dir");
7376 if (stat(work_dir
, &sb
))
7377 err(1, "stat work_dir");
7379 if (S_ISDIR(sb
.st_mode
) == 0)
7380 errx(1, "%s not a dir", work_dir
);
7381 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7382 warnx("fixing invalid permissions on %s", work_dir
);
7383 if (chmod(work_dir
, S_IRWXU
) == -1)
7387 /* icon cache dir */
7388 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s", work_dir
, XT_CACHE_DIR
);
7389 if (stat(cache_dir
, &sb
)) {
7390 if (mkdir(cache_dir
, S_IRWXU
) == -1)
7391 err(1, "mkdir cache_dir");
7392 if (stat(cache_dir
, &sb
))
7393 err(1, "stat cache_dir");
7395 if (S_ISDIR(sb
.st_mode
) == 0)
7396 errx(1, "%s not a dir", cache_dir
);
7397 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7398 warnx("fixing invalid permissions on %s", cache_dir
);
7399 if (chmod(cache_dir
, S_IRWXU
) == -1)
7404 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s", work_dir
, XT_CERT_DIR
);
7405 if (stat(certs_dir
, &sb
)) {
7406 if (mkdir(certs_dir
, S_IRWXU
) == -1)
7407 err(1, "mkdir certs_dir");
7408 if (stat(certs_dir
, &sb
))
7409 err(1, "stat certs_dir");
7411 if (S_ISDIR(sb
.st_mode
) == 0)
7412 errx(1, "%s not a dir", certs_dir
);
7413 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7414 warnx("fixing invalid permissions on %s", certs_dir
);
7415 if (chmod(certs_dir
, S_IRWXU
) == -1)
7420 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s",
7421 work_dir
, XT_SESSIONS_DIR
);
7422 if (stat(sessions_dir
, &sb
)) {
7423 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
7424 err(1, "mkdir sessions_dir");
7425 if (stat(sessions_dir
, &sb
))
7426 err(1, "stat sessions_dir");
7428 if (S_ISDIR(sb
.st_mode
) == 0)
7429 errx(1, "%s not a dir", sessions_dir
);
7430 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7431 warnx("fixing invalid permissions on %s", sessions_dir
);
7432 if (chmod(sessions_dir
, S_IRWXU
) == -1)
7435 /* runtime settings that can override config file */
7436 if (runtime_settings
[0] != '\0')
7437 config_parse(runtime_settings
, 1);
7440 if (!strcmp(download_dir
, pwd
->pw_dir
))
7441 strlcat(download_dir
, "/downloads", sizeof download_dir
);
7442 if (stat(download_dir
, &sb
)) {
7443 if (mkdir(download_dir
, S_IRWXU
) == -1)
7444 err(1, "mkdir download_dir");
7445 if (stat(download_dir
, &sb
))
7446 err(1, "stat download_dir");
7448 if (S_ISDIR(sb
.st_mode
) == 0)
7449 errx(1, "%s not a dir", download_dir
);
7450 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7451 warnx("fixing invalid permissions on %s", download_dir
);
7452 if (chmod(download_dir
, S_IRWXU
) == -1)
7456 /* favorites file */
7457 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
7458 if (stat(file
, &sb
)) {
7459 warnx("favorites file doesn't exist, creating it");
7460 if ((f
= fopen(file
, "w")) == NULL
)
7461 err(1, "favorites");
7466 session
= webkit_get_default_session();
7471 if (stat(ssl_ca_file
, &sb
)) {
7472 warn("no CA file: %s", ssl_ca_file
);
7473 g_free(ssl_ca_file
);
7476 g_object_set(session
,
7477 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
7478 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
7483 env_proxy
= getenv("http_proxy");
7485 setup_proxy(env_proxy
);
7487 setup_proxy(http_proxy
);
7489 /* see if there is already an xxxterm running */
7490 if (single_instance
&& is_running()) {
7492 warnx("already running");
7497 send_url_to_socket(argv
[0]);
7508 if (save_global_history
)
7509 restore_global_history();
7511 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
7512 restore_saved_tabs();
7514 a
.s
= named_session
;
7515 a
.i
= XT_SES_DONOTHING
;
7516 open_tabs(NULL
, &a
);
7520 create_new_tab(argv
[0], NULL
, focus
);
7527 if (TAILQ_EMPTY(&tabs
))
7528 create_new_tab(home
, NULL
, 1);
7531 if ((s
= build_socket()) != -1)
7532 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
7536 gnutls_global_deinit();