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
45 #include <sys/types.h>
47 #if defined(__linux__)
48 #include "linux/util.h"
49 #include "linux/tree.h"
50 #elif defined(__FreeBSD__)
52 #include "freebsd/util.h"
58 #include <sys/queue.h>
60 #include <sys/socket.h>
64 #include <gdk/gdkkeysyms.h>
65 #include <webkit/webkit.h>
66 #include <libsoup/soup.h>
67 #include <gnutls/gnutls.h>
68 #include <JavaScriptCore/JavaScript.h>
69 #include <gnutls/x509.h>
71 #include "javascript.h"
74 javascript.h borrowed from vimprobable2 under the following license:
76 Copyright (c) 2009 Leon Winter
77 Copyright (c) 2009 Hannes Schueller
78 Copyright (c) 2009 Matto Fransen
80 Permission is hereby granted, free of charge, to any person obtaining a copy
81 of this software and associated documentation files (the "Software"), to deal
82 in the Software without restriction, including without limitation the rights
83 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
84 copies of the Software, and to permit persons to whom the Software is
85 furnished to do so, subject to the following conditions:
87 The above copyright notice and this permission notice shall be included in
88 all copies or substantial portions of the Software.
90 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
91 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
92 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
93 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
94 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
95 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
99 static char *version
= "$xxxterm$";
101 /* hooked functions */
102 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
103 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
108 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
109 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
110 #define XT_D_MOVE 0x0001
111 #define XT_D_KEY 0x0002
112 #define XT_D_TAB 0x0004
113 #define XT_D_URL 0x0008
114 #define XT_D_CMD 0x0010
115 #define XT_D_NAV 0x0020
116 #define XT_D_DOWNLOAD 0x0040
117 #define XT_D_CONFIG 0x0080
118 #define XT_D_JS 0x0100
119 #define XT_D_FAVORITE 0x0200
120 #define XT_D_PRINTING 0x0400
121 #define XT_D_COOKIE 0x0800
122 u_int32_t swm_debug
= 0
137 #define DPRINTF(x...)
138 #define DNPRINTF(n,x...)
141 #define LENGTH(x) (sizeof x / sizeof x[0])
142 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
143 ~(GDK_BUTTON1_MASK) & \
144 ~(GDK_BUTTON2_MASK) & \
145 ~(GDK_BUTTON3_MASK) & \
146 ~(GDK_BUTTON4_MASK) & \
158 TAILQ_ENTRY(tab
) entry
;
160 GtkWidget
*tab_content
;
163 GtkWidget
*uri_entry
;
164 GtkWidget
*search_entry
;
166 GtkWidget
*browser_win
;
167 GtkWidget
*statusbar
;
173 GtkWidget
*js_toggle
;
177 WebKitWebHistoryItem
*item
;
178 WebKitWebBackForwardList
*bfl
;
181 WebKitNetworkRequest
*icon_request
;
182 WebKitDownload
*icon_download
;
183 GdkPixbuf
*icon_pixbuf
;
184 gchar
*icon_dest_uri
;
186 /* adjustments for browser */
189 GtkAdjustment
*adjust_h
;
190 GtkAdjustment
*adjust_v
;
196 int xtp_meaning
; /* identifies dls/favorites */
201 #define XT_HINT_NONE (0)
202 #define XT_HINT_NUMERICAL (1)
203 #define XT_HINT_ALPHANUM (2)
212 WebKitWebSettings
*settings
;
216 TAILQ_HEAD(tab_list
, tab
);
219 RB_ENTRY(history
) entry
;
223 RB_HEAD(history_list
, history
);
226 RB_ENTRY(download
) entry
;
228 WebKitDownload
*download
;
231 RB_HEAD(download_list
, download
);
234 RB_ENTRY(domain
) entry
;
236 int handy
; /* app use */
238 RB_HEAD(domain_list
, domain
);
241 TAILQ_ENTRY(undo
) entry
;
244 int back
; /* Keeps track of how many back
245 * history items there are. */
247 TAILQ_HEAD(undo_tailq
, undo
);
249 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
250 int next_download_id
= 1;
258 #define XT_NAME ("XXXTerm")
259 #define XT_DIR (".xxxterm")
260 #define XT_CACHE_DIR ("cache")
261 #define XT_CERT_DIR ("certs/")
262 #define XT_SESSIONS_DIR ("sessions/")
263 #define XT_CONF_FILE ("xxxterm.conf")
264 #define XT_FAVS_FILE ("favorites")
265 #define XT_SAVED_TABS_FILE ("main_session")
266 #define XT_RESTART_TABS_FILE ("restart_tabs")
267 #define XT_SOCKET_FILE ("socket")
268 #define XT_HISTORY_FILE ("history")
269 #define XT_SAVE_SESSION_ID ("SESSION_NAME=")
270 #define XT_CB_HANDLED (TRUE)
271 #define XT_CB_PASSTHROUGH (FALSE)
272 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
273 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
274 #define XT_DLMAN_REFRESH "10"
275 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
276 "td {overflow: hidden;" \
277 " padding: 2px 2px 2px 2px;" \
278 " border: 1px solid black}\n" \
279 "tr:hover {background: #ffff99 ;}\n" \
280 "th {background-color: #cccccc;" \
281 " border: 1px solid black}" \
282 "table {border-spacing: 0; " \
283 " width: 90%%; border: 1px black" \
284 " solid; table-layout: fixed}\n" \
286 " border: 1px solid black;" \
292 " background: green;}" \
294 " font-size: small;" \
295 " text-align: center;}" \
297 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
298 #define XT_MAX_UNDO_CLOSE_TAB (32)
299 #define XT_RESERVED_CHARS "$&+,/:;=?@ \"<>#%%{}|^~[]`"
302 #define SZ_KB ((uint64_t) 1024)
303 #define SZ_MB (SZ_KB * SZ_KB)
304 #define SZ_GB (SZ_KB * SZ_KB * SZ_KB)
305 #define SZ_TB (SZ_KB * SZ_KB * SZ_KB * SZ_KB)
308 * xxxterm "protocol" (xtp)
309 * We use this for managing stuff like downloads and favorites. They
310 * make magical HTML pages in memory which have xxxt:// links in order
311 * to communicate with xxxterm's internals. These links take the format:
312 * xxxt://class/session_key/action/arg
314 * Don't begin xtp class/actions as 0. atoi returns that on error.
316 * Typically we have not put addition of items in this framework, as
317 * adding items is either done via an ex-command or via a keybinding instead.
320 #define XT_XTP_STR "xxxt://"
322 /* XTP classes (xxxt://<class>) */
323 #define XT_XTP_DL 1 /* downloads */
324 #define XT_XTP_HL 2 /* history */
325 #define XT_XTP_CL 3 /* cookies */
326 #define XT_XTP_FL 4 /* favorites */
328 /* XTP download actions */
329 #define XT_XTP_DL_LIST 1
330 #define XT_XTP_DL_CANCEL 2
331 #define XT_XTP_DL_REMOVE 3
333 /* XTP history actions */
334 #define XT_XTP_HL_LIST 1
335 #define XT_XTP_HL_REMOVE 2
337 /* XTP cookie actions */
338 #define XT_XTP_CL_LIST 1
339 #define XT_XTP_CL_REMOVE 2
341 /* XTP cookie actions */
342 #define XT_XTP_FL_LIST 1
343 #define XT_XTP_FL_REMOVE 2
345 /* xtp tab meanings - identifies which tabs have xtp pages in */
346 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
347 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
348 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
349 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
350 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
353 #define XT_MOVE_INVALID (0)
354 #define XT_MOVE_DOWN (1)
355 #define XT_MOVE_UP (2)
356 #define XT_MOVE_BOTTOM (3)
357 #define XT_MOVE_TOP (4)
358 #define XT_MOVE_PAGEDOWN (5)
359 #define XT_MOVE_PAGEUP (6)
360 #define XT_MOVE_HALFDOWN (7)
361 #define XT_MOVE_HALFUP (8)
362 #define XT_MOVE_LEFT (9)
363 #define XT_MOVE_FARLEFT (10)
364 #define XT_MOVE_RIGHT (11)
365 #define XT_MOVE_FARRIGHT (12)
367 #define XT_TAB_LAST (-4)
368 #define XT_TAB_FIRST (-3)
369 #define XT_TAB_PREV (-2)
370 #define XT_TAB_NEXT (-1)
371 #define XT_TAB_INVALID (0)
372 #define XT_TAB_NEW (1)
373 #define XT_TAB_DELETE (2)
374 #define XT_TAB_DELQUIT (3)
375 #define XT_TAB_OPEN (4)
376 #define XT_TAB_UNDO_CLOSE (5)
377 #define XT_TAB_SHOW (6)
378 #define XT_TAB_HIDE (7)
380 #define XT_NAV_INVALID (0)
381 #define XT_NAV_BACK (1)
382 #define XT_NAV_FORWARD (2)
383 #define XT_NAV_RELOAD (3)
384 #define XT_NAV_RELOAD_CACHE (4)
386 #define XT_FOCUS_INVALID (0)
387 #define XT_FOCUS_URI (1)
388 #define XT_FOCUS_SEARCH (2)
390 #define XT_SEARCH_INVALID (0)
391 #define XT_SEARCH_NEXT (1)
392 #define XT_SEARCH_PREV (2)
394 #define XT_PASTE_CURRENT_TAB (0)
395 #define XT_PASTE_NEW_TAB (1)
397 #define XT_FONT_SET (0)
399 #define XT_URL_SHOW (1)
400 #define XT_URL_HIDE (2)
402 #define XT_STATUSBAR_SHOW (1)
403 #define XT_STATUSBAR_HIDE (2)
405 #define XT_WL_TOGGLE (1<<0)
406 #define XT_WL_ENABLE (1<<1)
407 #define XT_WL_DISABLE (1<<2)
408 #define XT_WL_FQDN (1<<3) /* default */
409 #define XT_WL_TOPLEVEL (1<<4)
411 #define XT_CMD_OPEN (0)
412 #define XT_CMD_OPEN_CURRENT (1)
413 #define XT_CMD_TABNEW (2)
414 #define XT_CMD_TABNEW_CURRENT (3)
416 #define XT_STATUS_NOTHING (0)
417 #define XT_STATUS_LINK (1)
418 #define XT_STATUS_URI (2)
420 #define XT_SES_DONOTHING (0)
421 #define XT_SES_CLOSETABS (1)
423 #define XT_COOKIE_NORMAL (0)
424 #define XT_COOKIE_WHITELIST (1)
431 TAILQ_ENTRY(mime_type
) entry
;
433 TAILQ_HEAD(mime_type_list
, mime_type
);
439 TAILQ_ENTRY(alias
) entry
;
441 TAILQ_HEAD(alias_list
, alias
);
443 /* settings that require restart */
444 int tabless
= 0; /* allow only 1 tab */
445 int enable_socket
= 0;
446 int single_instance
= 0; /* only allow one xxxterm to run */
447 int fancy_bar
= 1; /* fancy toolbar */
448 int browser_mode
= XT_COOKIE_NORMAL
;
450 /* runtime settings */
451 int show_tabs
= 1; /* show tabs on notebook */
452 int show_url
= 1; /* show url toolbar on notebook */
453 int show_statusbar
= 0; /* vimperator style status bar */
454 int ctrl_click_focus
= 0; /* ctrl click gets focus */
455 int cookies_enabled
= 1; /* enable cookies */
456 int read_only_cookies
= 0; /* enable to not write cookies */
457 int enable_scripts
= 1;
458 int enable_plugins
= 0;
459 int default_font_size
= 12;
460 int window_height
= 768;
461 int window_width
= 1024;
462 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
463 unsigned refresh_interval
= 10; /* download refresh interval */
464 int enable_cookie_whitelist
= 0;
465 int enable_js_whitelist
= 0;
466 time_t session_timeout
= 3600; /* cookie session timeout */
467 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
468 char *ssl_ca_file
= NULL
;
469 char *resource_dir
= NULL
;
470 gboolean ssl_strict_certs
= FALSE
;
471 int append_next
= 1; /* append tab after current tab */
473 char *search_string
= NULL
;
474 char *http_proxy
= NULL
;
475 char download_dir
[PATH_MAX
];
476 char runtime_settings
[PATH_MAX
]; /* override of settings */
477 int allow_volatile_cookies
= 0;
478 int save_global_history
= 0; /* save global history to disk */
479 char *user_agent
= NULL
;
480 int save_rejected_cookies
= 0;
481 time_t session_autosave
= 0;
482 int guess_search
= 0;
485 int set_download_dir(struct settings
*, char *);
486 int set_runtime_dir(struct settings
*, char *);
487 int set_browser_mode(struct settings
*, char *);
488 int set_cookie_policy(struct settings
*, char *);
489 int add_alias(struct settings
*, char *);
490 int add_mime_type(struct settings
*, char *);
491 int add_cookie_wl(struct settings
*, char *);
492 int add_js_wl(struct settings
*, char *);
493 void button_set_stockid(GtkWidget
*, char *);
494 GtkWidget
* create_button(char *, char *, int);
496 char *get_browser_mode(struct settings
*);
497 char *get_cookie_policy(struct settings
*);
499 char *get_download_dir(struct settings
*);
500 char *get_runtime_dir(struct settings
*);
502 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
503 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
504 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
505 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
508 int (*set
)(struct settings
*, char *);
509 char *(*get
)(struct settings
*);
510 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
513 struct special s_browser_mode
= {
519 struct special s_cookie
= {
525 struct special s_alias
= {
531 struct special s_mime
= {
537 struct special s_js
= {
543 struct special s_cookie_wl
= {
549 struct special s_download_dir
= {
558 #define XT_S_INVALID (0)
562 #define XT_SF_RESTART (1<<0)
563 #define XT_SF_RUNTIME (1<<1)
568 { "append_next", XT_S_INT
, 0, &append_next
, NULL
, NULL
},
569 { "allow_volatile_cookies", XT_S_INT
, 0, &allow_volatile_cookies
, NULL
, NULL
},
570 { "browser_mode", XT_S_INT
, 0, NULL
, NULL
,&s_browser_mode
},
571 { "cookie_policy", XT_S_INT
, 0, NULL
, NULL
,&s_cookie
},
572 { "cookies_enabled", XT_S_INT
, 0, &cookies_enabled
, NULL
, NULL
},
573 { "ctrl_click_focus", XT_S_INT
, 0, &ctrl_click_focus
, NULL
, NULL
},
574 { "default_font_size", XT_S_INT
, 0, &default_font_size
, NULL
, NULL
},
575 { "download_dir", XT_S_STR
, 0, NULL
, NULL
,&s_download_dir
},
576 { "enable_cookie_whitelist", XT_S_INT
, 0, &enable_cookie_whitelist
, NULL
, NULL
},
577 { "enable_js_whitelist", XT_S_INT
, 0, &enable_js_whitelist
, NULL
, NULL
},
578 { "enable_plugins", XT_S_INT
, 0, &enable_plugins
, NULL
, NULL
},
579 { "enable_scripts", XT_S_INT
, 0, &enable_scripts
, NULL
, NULL
},
580 { "enable_socket", XT_S_INT
, XT_SF_RESTART
,&enable_socket
, NULL
, NULL
},
581 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
,&fancy_bar
, NULL
, NULL
},
582 { "home", XT_S_STR
, 0, NULL
, &home
, NULL
},
583 { "http_proxy", XT_S_STR
, 0, NULL
, &http_proxy
, NULL
},
584 { "icon_size", XT_S_INT
, 0, &icon_size
, NULL
, NULL
},
585 { "read_only_cookies", XT_S_INT
, 0, &read_only_cookies
, NULL
, NULL
},
586 { "refresh_interval", XT_S_INT
, 0, &refresh_interval
, NULL
, NULL
},
587 { "resource_dir", XT_S_STR
, 0, NULL
, &resource_dir
, NULL
},
588 { "search_string", XT_S_STR
, 0, NULL
, &search_string
, NULL
},
589 { "save_global_history", XT_S_INT
, XT_SF_RESTART
,&save_global_history
, NULL
, NULL
},
590 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
,&save_rejected_cookies
, NULL
, NULL
},
591 { "session_timeout", XT_S_INT
, 0, &session_timeout
, NULL
, NULL
},
592 { "session_autosave", XT_S_INT
, 0, &session_autosave
, NULL
, NULL
},
593 { "single_instance", XT_S_INT
, XT_SF_RESTART
,&single_instance
, NULL
, NULL
},
594 { "show_tabs", XT_S_INT
, 0, &show_tabs
, NULL
, NULL
},
595 { "show_url", XT_S_INT
, 0, &show_url
, NULL
, NULL
},
596 { "guess_search", XT_S_INT
, 0, &guess_search
, NULL
, NULL
},
597 { "show_statusbar", XT_S_INT
, 0, &show_statusbar
, NULL
, NULL
},
598 { "ssl_ca_file", XT_S_STR
, 0, NULL
, &ssl_ca_file
, NULL
},
599 { "ssl_strict_certs", XT_S_INT
, 0, &ssl_strict_certs
, NULL
, NULL
},
600 { "user_agent", XT_S_STR
, 0, NULL
, &user_agent
, NULL
},
601 { "window_height", XT_S_INT
, 0, &window_height
, NULL
, NULL
},
602 { "window_width", XT_S_INT
, 0, &window_width
, NULL
, NULL
},
604 /* runtime settings */
605 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
606 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
607 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
608 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
612 extern char *__progname
;
615 GtkWidget
*main_window
;
616 GtkNotebook
*notebook
;
617 GtkWidget
*arrow
, *abtn
;
618 struct tab_list tabs
;
619 struct history_list hl
;
620 struct download_list downloads
;
621 struct domain_list c_wl
;
622 struct domain_list js_wl
;
623 struct undo_tailq undos
;
625 int updating_dl_tabs
= 0;
626 int updating_hl_tabs
= 0;
627 int updating_cl_tabs
= 0;
628 int updating_fl_tabs
= 0;
630 uint64_t blocked_cookies
= 0;
631 char named_session
[PATH_MAX
];
632 void update_favicon(struct tab
*);
633 int icon_size_map(int);
638 int saved_errno
, status
;
643 while ((pid
= waitpid(WAIT_ANY
, &status
, WNOHANG
)) != 0) {
647 if (errno
!= ECHILD
) {
649 clog_warn("sigchild: waitpid:");
655 if (WIFEXITED(status
)) {
656 if (WEXITSTATUS(status
) != 0) {
658 clog_warnx("sigchild: child exit status: %d",
659 WEXITSTATUS(status));
664 clog_warnx("sigchild: child is terminated abnormally");
673 load_webkit_string(struct tab
*t
, const char *str
)
675 /* we set this to indicate we want to manually do navaction */
676 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
677 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
681 set_status(struct tab
*t
, gchar
*s
, int status
)
690 type
= g_strdup_printf("Link: %s", s
);
692 t
->status
= g_strdup(gtk_entry_get_text(GTK_ENTRY(t
->statusbar
)));
696 type
= g_strdup_printf("%s", s
);
698 t
->status
= g_strdup(type
);
702 t
->status
= g_strdup(s
);
704 case XT_STATUS_NOTHING
:
709 gtk_entry_set_text(GTK_ENTRY(t
->statusbar
), s
);
715 hide_oops(struct tab
*t
)
717 gtk_widget_hide(t
->oops
);
721 hide_cmd(struct tab
*t
)
723 gtk_widget_hide(t
->cmd
);
727 show_cmd(struct tab
*t
)
729 gtk_widget_hide(t
->oops
);
730 gtk_widget_show(t
->cmd
);
734 show_oops(struct tab
*t
, const char *fmt
, ...)
743 if (vasprintf(&msg
, fmt
, ap
) == -1)
744 errx(1, "show_oops failed");
747 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
748 gtk_widget_hide(t
->cmd
);
749 gtk_widget_show(t
->oops
);
752 /* XXX collapse with show_oops */
754 show_oops_s(const char *fmt
, ...)
758 struct tab
*ti
, *t
= NULL
;
763 TAILQ_FOREACH(ti
, &tabs
, entry
)
764 if (ti
->tab_id
== gtk_notebook_current_page(notebook
)) {
772 if (vasprintf(&msg
, fmt
, ap
) == -1)
773 errx(1, "show_oops_s failed");
776 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
777 gtk_widget_hide(t
->cmd
);
778 gtk_widget_show(t
->oops
);
782 get_as_string(struct settings
*s
)
793 warnx("get_as_string skip %s\n", s
->name
);
794 } else if (s
->type
== XT_S_INT
)
795 r
= g_strdup_printf("%d", *s
->ival
);
796 else if (s
->type
== XT_S_STR
)
797 r
= g_strdup(*s
->sval
);
799 r
= g_strdup_printf("INVALID TYPE");
805 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
810 for (i
= 0; i
< LENGTH(rs
); i
++) {
811 if (rs
[i
].s
&& rs
[i
].s
->walk
)
812 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
814 s
= get_as_string(&rs
[i
]);
815 cb(&rs
[i
], s
, cb_args
);
822 set_browser_mode(struct settings
*s
, char *val
)
824 if (!strcmp(val
, "whitelist")) {
825 browser_mode
= XT_COOKIE_WHITELIST
;
826 allow_volatile_cookies
= 0;
827 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
829 enable_cookie_whitelist
= 1;
830 read_only_cookies
= 0;
831 save_rejected_cookies
= 0;
832 session_timeout
= 3600;
834 enable_js_whitelist
= 1;
835 } else if (!strcmp(val
, "normal")) {
836 browser_mode
= XT_COOKIE_NORMAL
;
837 allow_volatile_cookies
= 0;
838 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
840 enable_cookie_whitelist
= 0;
841 read_only_cookies
= 0;
842 save_rejected_cookies
= 0;
843 session_timeout
= 3600;
845 enable_js_whitelist
= 0;
853 get_browser_mode(struct settings
*s
)
857 if (browser_mode
== XT_COOKIE_WHITELIST
)
858 r
= g_strdup("whitelist");
859 else if (browser_mode
== XT_COOKIE_NORMAL
)
860 r
= g_strdup("normal");
868 set_cookie_policy(struct settings
*s
, char *val
)
870 if (!strcmp(val
, "no3rdparty"))
871 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
872 else if (!strcmp(val
, "accept"))
873 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
874 else if (!strcmp(val
, "reject"))
875 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
883 get_cookie_policy(struct settings
*s
)
887 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
888 r
= g_strdup("no3rdparty");
889 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
890 r
= g_strdup("accept");
891 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
892 r
= g_strdup("reject");
900 get_download_dir(struct settings
*s
)
902 if (download_dir
[0] == '\0')
904 return (g_strdup(download_dir
));
908 set_download_dir(struct settings
*s
, char *val
)
911 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
912 pwd
->pw_dir
, &val
[1]);
914 strlcpy(download_dir
, val
, sizeof download_dir
);
921 * We use these to prevent people putting xxxt:// URLs on
922 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
924 #define XT_XTP_SES_KEY_SZ 8
925 #define XT_XTP_SES_KEY_HEX_FMT \
926 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
927 char *dl_session_key
; /* downloads */
928 char *hl_session_key
; /* history list */
929 char *cl_session_key
; /* cookie list */
930 char *fl_session_key
; /* favorites list */
932 char work_dir
[PATH_MAX
];
933 char certs_dir
[PATH_MAX
];
934 char cache_dir
[PATH_MAX
];
935 char sessions_dir
[PATH_MAX
];
936 char cookie_file
[PATH_MAX
];
937 SoupURI
*proxy_uri
= NULL
;
938 SoupSession
*session
;
939 SoupCookieJar
*s_cookiejar
;
940 SoupCookieJar
*p_cookiejar
;
941 char rc_fname
[PATH_MAX
];
943 struct mime_type_list mtl
;
944 struct alias_list aliases
;
947 void create_new_tab(char *, struct undo
*, int);
948 void delete_tab(struct tab
*);
949 void adjustfont_webkit(struct tab
*, int);
950 int run_script(struct tab
*, char *);
951 int download_rb_cmp(struct download
*, struct download
*);
952 int xtp_page_hl(struct tab
*t
, struct karg
*args
);
953 int xtp_page_dl(struct tab
*t
, struct karg
*args
);
954 int xtp_page_cl(struct tab
*t
, struct karg
*args
);
955 int xtp_page_fl(struct tab
*t
, struct karg
*args
);
958 history_rb_cmp(struct history
*h1
, struct history
*h2
)
960 return (strcmp(h1
->uri
, h2
->uri
));
962 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
965 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
967 return (strcmp(d1
->d
, d2
->d
));
969 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
972 * generate a session key to secure xtp commands.
973 * pass in a ptr to the key in question and it will
974 * be modified in place.
977 generate_xtp_session_key(char **key
)
979 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
986 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
987 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
988 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
989 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
991 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
995 * validate a xtp session key.
999 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
1001 if (strcmp(trusted
, untrusted
) != 0) {
1002 show_oops(t
, "%s: xtp session key mismatch possible spoof",
1011 download_rb_cmp(struct download
*e1
, struct download
*e2
)
1013 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
1015 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
1017 struct valid_url_types
{
1028 valid_url_type(char *url
)
1032 for (i
= 0; i
< LENGTH(vut
); i
++)
1033 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
1040 print_cookie(char *msg
, SoupCookie
*c
)
1046 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
1047 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
1048 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
1049 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
1050 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
1051 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
1052 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
1053 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
1054 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
1055 DNPRINTF(XT_D_COOKIE
, "====================================\n");
1059 walk_alias(struct settings
*s
,
1060 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1065 if (s
== NULL
|| cb
== NULL
) {
1066 show_oops_s("walk_alias invalid parameters");
1070 TAILQ_FOREACH(a
, &aliases
, entry
) {
1071 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
1072 cb(s
, str
, cb_args
);
1078 match_alias(char *url_in
)
1082 char *url_out
= NULL
, *search
, *enc_arg
;
1084 search
= g_strdup(url_in
);
1086 if (strsep(&arg
, " \t") == NULL
) {
1087 show_oops_s("match_alias: NULL URL");
1091 TAILQ_FOREACH(a
, &aliases
, entry
) {
1092 if (!strcmp(search
, a
->a_name
))
1097 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
1100 enc_arg
= soup_uri_encode(arg
, XT_RESERVED_CHARS
);
1101 url_out
= g_strdup_printf(a
->a_uri
, enc_arg
);
1104 url_out
= g_strdup(a
->a_uri
);
1112 guess_url_type(char *url_in
)
1115 char *url_out
= NULL
, *enc_search
= NULL
;
1117 url_out
= match_alias(url_in
);
1118 if (url_out
!= NULL
)
1123 * If there is no dot nor slash in the string and it isn't a
1124 * path to a local file and doesn't resolves to an IP, assume
1125 * that the user wants to search for the string.
1128 if (strchr(url_in
, '.') == NULL
&&
1129 strchr(url_in
, '/') == NULL
&&
1130 stat(url_in
, &sb
) != 0 &&
1131 gethostbyname(url_in
) == NULL
) {
1133 enc_search
= soup_uri_encode(url_in
, XT_RESERVED_CHARS
);
1134 url_out
= g_strdup_printf(search_string
, enc_search
);
1140 /* XXX not sure about this heuristic */
1141 if (stat(url_in
, &sb
) == 0)
1142 url_out
= g_strdup_printf("file://%s", url_in
);
1144 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
1146 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
1152 load_uri(WebKitWebView
*wv
, gchar
*uri
)
1154 gchar
*newuri
= NULL
;
1156 if (uri
== NULL
|| !strlen(uri
))
1159 if (valid_url_type(uri
)) {
1160 newuri
= guess_url_type(uri
);
1164 webkit_web_view_load_uri(wv
, uri
);
1171 add_alias(struct settings
*s
, char *line
)
1174 struct alias
*a
= NULL
;
1176 if (s
== NULL
|| line
== NULL
) {
1177 show_oops_s("add_alias invalid parameters");
1182 a
= g_malloc(sizeof(*a
));
1184 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1185 show_oops_s("add_alias: incomplete alias definition");
1188 if (strlen(alias
) == 0 || strlen(l
) == 0) {
1189 show_oops_s("add_alias: invalid alias definition");
1193 a
->a_name
= g_strdup(alias
);
1194 a
->a_uri
= g_strdup(l
);
1196 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
1198 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
1208 add_mime_type(struct settings
*s
, char *line
)
1212 struct mime_type
*m
= NULL
;
1214 /* XXX this could be smarter */
1217 show_oops_s("add_mime_type invalid parameters");
1222 m
= g_malloc(sizeof(*m
));
1224 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
) {
1225 show_oops_s("add_mime_type: invalid mime_type");
1228 if (mime_type
[strlen(mime_type
) - 1] == '*') {
1229 mime_type
[strlen(mime_type
) - 1] = '\0';
1234 if (strlen(mime_type
) == 0 || strlen(l
) == 0) {
1235 show_oops_s("add_mime_type: invalid mime_type");
1239 m
->mt_type
= g_strdup(mime_type
);
1240 m
->mt_action
= g_strdup(l
);
1242 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1243 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1245 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1255 find_mime_type(char *mime_type
)
1257 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1259 TAILQ_FOREACH(m
, &mtl
, entry
) {
1260 if (m
->mt_default
&&
1261 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1264 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1277 walk_mime_type(struct settings
*s
,
1278 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1280 struct mime_type
*m
;
1283 if (s
== NULL
|| cb
== NULL
)
1284 show_oops_s("walk_mime_type invalid parameters");
1286 TAILQ_FOREACH(m
, &mtl
, entry
) {
1287 str
= g_strdup_printf("%s%s --> %s",
1289 m
->mt_default
? "*" : "",
1291 cb(s
, str
, cb_args
);
1297 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1302 if (str
== NULL
|| wl
== NULL
)
1304 if (strlen(str
) < 2)
1307 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1309 /* treat *.moo.com the same as .moo.com */
1310 if (str
[0] == '*' && str
[1] == '.')
1312 else if (str
[0] == '.')
1317 d
= g_malloc(sizeof *d
);
1319 d
->d
= g_strdup_printf(".%s", str
);
1321 d
->d
= g_strdup(str
);
1324 if (RB_INSERT(domain_list
, wl
, d
))
1327 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1338 add_cookie_wl(struct settings
*s
, char *entry
)
1340 wl_add(entry
, &c_wl
, 1);
1345 walk_cookie_wl(struct settings
*s
,
1346 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1350 if (s
== NULL
|| cb
== NULL
)
1351 show_oops_s("walk_cookie_wl invalid parameters");
1353 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1354 cb(s
, d
->d
, cb_args
);
1358 walk_js_wl(struct settings
*s
,
1359 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1363 if (s
== NULL
|| cb
== NULL
)
1364 show_oops_s("walk_js_wl invalid parameters");
1366 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1367 cb(s
, d
->d
, cb_args
);
1371 add_js_wl(struct settings
*s
, char *entry
)
1373 wl_add(entry
, &js_wl
, 1 /* persistent */);
1378 wl_find(const gchar
*search
, struct domain_list
*wl
)
1381 struct domain
*d
= NULL
, dfind
;
1384 if (search
== NULL
|| wl
== NULL
)
1386 if (strlen(search
) < 2)
1389 if (search
[0] != '.')
1390 s
= g_strdup_printf(".%s", search
);
1392 s
= g_strdup(search
);
1394 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1397 d
= RB_FIND(domain_list
, wl
, &dfind
);
1411 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1417 if (s
== NULL
|| wl
== NULL
)
1420 if (!strncmp(s
, "http://", strlen("http://")))
1421 s
= &s
[strlen("http://")];
1422 else if (!strncmp(s
, "https://", strlen("https://")))
1423 s
= &s
[strlen("https://")];
1428 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1429 /* chop string at first slash */
1430 if (s
[i
] == '/' || s
[i
] == '\0') {
1433 r
= wl_find(ss
, wl
);
1442 get_toplevel_domain(char *domain
)
1449 if (strlen(domain
) < 2)
1452 s
= &domain
[strlen(domain
) - 1];
1453 while (s
!= domain
) {
1470 config_parse(char *filename
, int runtime
)
1473 char *line
, *cp
, *var
, *val
;
1474 size_t len
, lineno
= 0;
1476 char **s
, file
[PATH_MAX
];
1479 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1481 if (filename
== NULL
)
1484 if (runtime
&& runtime_settings
[0] != '\0') {
1485 snprintf(file
, sizeof file
, "%s/%s",
1486 work_dir
, runtime_settings
);
1487 if (stat(file
, &sb
)) {
1488 warnx("runtime file doesn't exist, creating it");
1489 if ((f
= fopen(file
, "w")) == NULL
)
1491 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1495 strlcpy(file
, filename
, sizeof file
);
1497 if ((config
= fopen(file
, "r")) == NULL
) {
1498 warn("config_parse: cannot open %s", filename
);
1503 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1504 if (feof(config
) || ferror(config
))
1508 cp
+= (long)strspn(cp
, WS
);
1509 if (cp
[0] == '\0') {
1515 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1518 cp
+= (long)strspn(cp
, WS
);
1520 if ((val
= strsep(&cp
, "\0")) == NULL
)
1523 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1526 for (i
= 0, handled
= 0; i
< LENGTH(rs
); i
++) {
1527 if (strcmp(var
, rs
[i
].name
))
1531 if (rs
[i
].s
->set(&rs
[i
], val
))
1532 errx(1, "invalid value for %s", var
);
1536 switch (rs
[i
].type
) {
1545 errx(1, "invalid sval for %s",
1554 errx(1, "invalid type for %s", var
);
1559 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1568 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1574 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1578 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1581 JSStringGetUTF8CString(jsref
, s
, l
);
1582 JSStringRelease(jsref
);
1588 disable_hints(struct tab
*t
)
1590 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1591 bzero(t
->hint_num
, sizeof t
->hint_num
);
1592 run_script(t
, "vimprobable_clear()");
1594 t
->hint_mode
= XT_HINT_NONE
;
1598 enable_hints(struct tab
*t
)
1600 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1601 run_script(t
, "vimprobable_show_hints()");
1603 t
->hint_mode
= XT_HINT_NONE
;
1606 #define XT_JS_OPEN ("open;")
1607 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1608 #define XT_JS_FIRE ("fire;")
1609 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1610 #define XT_JS_FOUND ("found;")
1611 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1614 run_script(struct tab
*t
, char *s
)
1616 JSGlobalContextRef ctx
;
1617 WebKitWebFrame
*frame
;
1619 JSValueRef val
, exception
;
1622 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1623 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1625 frame
= webkit_web_view_get_main_frame(t
->wv
);
1626 ctx
= webkit_web_frame_get_global_context(frame
);
1628 str
= JSStringCreateWithUTF8CString(s
);
1629 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1630 NULL
, 0, &exception
);
1631 JSStringRelease(str
);
1633 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1635 es
= js_ref_to_string(ctx
, exception
);
1636 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1640 es
= js_ref_to_string(ctx
, val
);
1641 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1643 /* handle return value right here */
1644 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1646 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1649 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1650 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1651 &es
[XT_JS_FIRE_LEN
]);
1656 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1657 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1668 hint(struct tab
*t
, struct karg
*args
)
1671 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1673 if (t
->hints_on
== 0)
1682 * Doesn't work fully, due to the following bug:
1683 * https://bugs.webkit.org/show_bug.cgi?id=51747
1686 restore_global_history(void)
1688 char file
[PATH_MAX
];
1694 snprintf(file
, sizeof file
, "%s/%s/%s",
1695 pwd
->pw_dir
, XT_DIR
, XT_HISTORY_FILE
);
1697 if ((f
= fopen(file
, "r")) == NULL
) {
1698 warnx("%s: fopen", __func__
);
1703 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1704 if (feof(f
) || ferror(f
))
1707 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1708 if (feof(f
) || ferror(f
)) {
1710 warnx("%s: broken history file\n", __func__
);
1714 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1715 webkit_web_history_item_new_with_data(uri
, title
);
1716 h
= g_malloc(sizeof(struct history
));
1717 h
->uri
= g_strdup(uri
);
1718 h
->title
= g_strdup(title
);
1719 RB_INSERT(history_list
, &hl
, h
);
1721 warnx("%s: failed to restore history\n", __func__
);
1737 save_global_history_to_disk(struct tab
*t
)
1739 char file
[PATH_MAX
];
1743 snprintf(file
, sizeof file
, "%s/%s/%s",
1744 pwd
->pw_dir
, XT_DIR
, XT_HISTORY_FILE
);
1746 if ((f
= fopen(file
, "w")) == NULL
) {
1747 show_oops(t
, "%s: global history file: %s",
1748 __func__
, strerror(errno
));
1752 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1753 if (h
->uri
&& h
->title
)
1754 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
1763 quit(struct tab
*t
, struct karg
*args
)
1765 if (save_global_history
)
1766 save_global_history_to_disk(t
);
1774 open_tabs(struct tab
*t
, struct karg
*a
)
1776 char file
[PATH_MAX
];
1780 struct tab
*ti
, *tt
;
1785 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1786 if ((f
= fopen(file
, "r")) == NULL
)
1789 ti
= TAILQ_LAST(&tabs
, tab_list
);
1792 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1793 if (feof(f
) || ferror(f
))
1796 /* retrieve session name */
1797 if (uri
&& g_str_has_prefix(uri
, XT_SAVE_SESSION_ID
)) {
1798 strlcpy(named_session
,
1799 &uri
[strlen(XT_SAVE_SESSION_ID
)],
1800 sizeof named_session
);
1804 if (uri
&& strlen(uri
))
1805 create_new_tab(uri
, NULL
, 1);
1811 /* close open tabs */
1812 if (a
->i
== XT_SES_CLOSETABS
&& ti
!= NULL
) {
1814 tt
= TAILQ_FIRST(&tabs
);
1833 restore_saved_tabs(void)
1835 char file
[PATH_MAX
];
1836 int unlink_file
= 0;
1841 snprintf(file
, sizeof file
, "%s/%s",
1842 sessions_dir
, XT_RESTART_TABS_FILE
);
1843 if (stat(file
, &sb
) == -1)
1844 a
.s
= XT_SAVED_TABS_FILE
;
1847 a
.s
= XT_RESTART_TABS_FILE
;
1850 a
.i
= XT_SES_DONOTHING
;
1851 rv
= open_tabs(NULL
, &a
);
1860 save_tabs(struct tab
*t
, struct karg
*a
)
1862 char file
[PATH_MAX
];
1865 WebKitWebFrame
*frame
;
1873 snprintf(file
, sizeof file
, "%s/%s",
1874 sessions_dir
, named_session
);
1876 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1878 if ((f
= fopen(file
, "w")) == NULL
) {
1879 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
1883 /* save session name */
1884 fprintf(f
, "%s%s\n", XT_SAVE_SESSION_ID
, named_session
);
1886 /* save tabs, in the order they are arranged in the notebook */
1887 TAILQ_FOREACH(ti
, &tabs
, entry
)
1890 arr
= g_malloc0(len
* sizeof(gchar
*));
1892 TAILQ_FOREACH(ti
, &tabs
, entry
) {
1893 frame
= webkit_web_view_get_main_frame(ti
->wv
);
1894 uri
= webkit_web_frame_get_uri(frame
);
1895 if (uri
&& strlen(uri
) > 0)
1896 arr
[gtk_notebook_page_num(notebook
, ti
->vbox
)] = (gchar
*)uri
;
1899 for (i
= 0; i
< len
; i
++)
1901 fprintf(f
, "%s\n", arr
[i
]);
1910 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
1922 yank_uri(struct tab
*t
, struct karg
*args
)
1924 WebKitWebFrame
*frame
;
1926 GtkClipboard
*clipboard
;
1928 frame
= webkit_web_view_get_main_frame(t
->wv
);
1929 uri
= webkit_web_frame_get_uri(frame
);
1933 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1934 gtk_clipboard_set_text(clipboard
, uri
, -1);
1945 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
1947 struct paste_args
*pap
;
1949 if (data
== NULL
|| text
== NULL
|| !strlen(text
))
1952 pap
= (struct paste_args
*)data
;
1955 case XT_PASTE_CURRENT_TAB
:
1956 load_uri(pap
->t
->wv
, (gchar
*)text
);
1958 case XT_PASTE_NEW_TAB
:
1959 create_new_tab((gchar
*)text
, NULL
, 1);
1967 paste_uri(struct tab
*t
, struct karg
*args
)
1969 GtkClipboard
*clipboard
;
1970 struct paste_args
*pap
;
1972 pap
= g_malloc(sizeof(struct paste_args
));
1977 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1978 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
1984 find_domain(const char *s
, int add_dot
)
1987 char *r
= NULL
, *ss
= NULL
;
1992 if (!strncmp(s
, "http://", strlen("http://")))
1993 s
= &s
[strlen("http://")];
1994 else if (!strncmp(s
, "https://", strlen("https://")))
1995 s
= &s
[strlen("https://")];
2001 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
2002 /* chop string at first slash */
2003 if (ss
[i
] == '/' || ss
[i
] == '\0') {
2006 r
= g_strdup_printf(".%s", ss
);
2017 toggle_cwl(struct tab
*t
, struct karg
*args
)
2019 WebKitWebFrame
*frame
;
2022 char *dom
= NULL
, *dom_toggle
= NULL
;
2028 frame
= webkit_web_view_get_main_frame(t
->wv
);
2029 uri
= (char *)webkit_web_frame_get_uri(frame
);
2030 dom
= find_domain(uri
, 1);
2031 d
= wl_find(dom
, &c_wl
);
2037 if (args
->i
& XT_WL_TOGGLE
)
2039 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2041 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2044 if (args
->i
& XT_WL_TOPLEVEL
)
2045 dom_toggle
= get_toplevel_domain(dom
);
2050 /* enable cookies for domain */
2051 wl_add(dom_toggle
, &c_wl
, 0);
2053 /* disable cookies for domain */
2054 RB_REMOVE(domain_list
, &c_wl
, d
);
2056 webkit_web_view_reload(t
->wv
);
2063 toggle_js(struct tab
*t
, struct karg
*args
)
2066 WebKitWebFrame
*frame
;
2069 char *dom
= NULL
, *dom_toggle
= NULL
;
2074 g_object_get(G_OBJECT(t
->settings
),
2075 "enable-scripts", &es
, (char *)NULL
);
2076 if (args
->i
& XT_WL_TOGGLE
)
2078 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
2080 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
2085 frame
= webkit_web_view_get_main_frame(t
->wv
);
2086 uri
= (char *)webkit_web_frame_get_uri(frame
);
2087 dom
= find_domain(uri
, 1);
2088 if (uri
== NULL
|| dom
== NULL
) {
2089 show_oops(t
, "Can't toggle domain in JavaScript white list");
2093 if (args
->i
& XT_WL_TOPLEVEL
)
2094 dom_toggle
= get_toplevel_domain(dom
);
2099 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
2100 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
2102 d
= wl_find(dom_toggle
, &js_wl
);
2104 RB_REMOVE(domain_list
, &js_wl
, d
);
2105 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
2107 g_object_set(G_OBJECT(t
->settings
),
2108 "enable-scripts", es
, (char *)NULL
);
2109 webkit_web_view_set_settings(t
->wv
, t
->settings
);
2110 webkit_web_view_reload(t
->wv
);
2118 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
2122 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
2127 toggle_src(struct tab
*t
, struct karg
*args
)
2134 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
2135 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
2136 webkit_web_view_reload(t
->wv
);
2142 focus_webview(struct tab
*t
)
2147 /* only grab focus if we are visible */
2148 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
2149 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
2153 focus(struct tab
*t
, struct karg
*args
)
2155 if (t
== NULL
|| args
== NULL
)
2161 if (args
->i
== XT_FOCUS_URI
)
2162 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
2163 else if (args
->i
== XT_FOCUS_SEARCH
)
2164 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
2170 stats(struct tab
*t
, struct karg
*args
)
2172 char *stats
, *s
, line
[64 * 1024];
2173 uint64_t line_count
= 0;
2177 show_oops_s("stats invalid parameters");
2180 if (save_rejected_cookies
) {
2181 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
2183 s
= fgets(line
, sizeof line
, r_cookie_f
);
2184 if (s
== NULL
|| feof(r_cookie_f
) ||
2190 snprintf(line
, sizeof line
,
2191 "<br>Cookies blocked(*) total: %llu", line_count
);
2193 show_oops(t
, "Can't open blocked cookies file: %s",
2197 stats
= g_strdup_printf(XT_DOCTYPE
2200 "<title>Statistics</title>"
2202 "<h1>Statistics</h1>"
2204 "Cookies blocked(*) this session: %llu"
2206 "<p><small><b>*</b> results vary based on settings"
2212 load_webkit_string(t
, stats
);
2219 about(struct tab
*t
, struct karg
*args
)
2224 show_oops_s("about invalid parameters");
2226 about
= g_strdup_printf(XT_DOCTYPE
2229 "<title>About</title>"
2233 "<b>Version: %s</b><p>"
2236 "<li>Marco Peereboom <marco@peereboom.us></li>"
2237 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
2238 "<li>Edd Barrett <vext01@gmail.com> </li>"
2239 "<li>Todd T. Fries <todd@fries.net> </li>"
2241 "Copyrights and licenses can be found on the XXXterm "
2242 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
2248 load_webkit_string(t
, about
);
2255 help(struct tab
*t
, struct karg
*args
)
2260 show_oops_s("help invalid parameters");
2265 "<title>XXXterm</title>"
2266 "<meta http-equiv=\"REFRESH\" content=\"0;"
2267 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
2270 "XXXterm man page <a href=\"http://opensource.conformal.com/"
2271 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
2272 "cgi-bin/man-cgi?xxxterm</a>"
2277 load_webkit_string(t
, help
);
2283 * update all favorite tabs apart from one. Pass NULL if
2284 * you want to update all.
2287 update_favorite_tabs(struct tab
*apart_from
)
2290 if (!updating_fl_tabs
) {
2291 updating_fl_tabs
= 1; /* stop infinite recursion */
2292 TAILQ_FOREACH(t
, &tabs
, entry
)
2293 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2294 && (t
!= apart_from
))
2295 xtp_page_fl(t
, NULL
);
2296 updating_fl_tabs
= 0;
2300 /* show a list of favorites (bookmarks) */
2302 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2304 char file
[PATH_MAX
];
2306 char *uri
= NULL
, *title
= NULL
;
2307 size_t len
, lineno
= 0;
2309 char *header
, *body
, *tmp
, *html
= NULL
;
2311 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2314 warn("%s: bad param", __func__
);
2316 /* mark tab as favorite list */
2317 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2319 /* new session key */
2320 if (!updating_fl_tabs
)
2321 generate_xtp_session_key(&fl_session_key
);
2323 /* open favorites */
2324 snprintf(file
, sizeof file
, "%s/%s/%s",
2325 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
2326 if ((f
= fopen(file
, "r")) == NULL
) {
2327 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2332 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2333 "<title>Favorites</title>\n"
2336 "<h1>Favorites</h1>\n",
2340 body
= g_strdup_printf("<div align='center'><table><tr>"
2341 "<th style='width: 4%%'>#</th><th>Link</th>"
2342 "<th style='width: 15%%'>Remove</th></tr>\n");
2345 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2346 if (feof(f
) || ferror(f
))
2354 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2355 if (feof(f
) || ferror(f
)) {
2356 show_oops(t
, "favorites file corrupt");
2362 body
= g_strdup_printf("%s<tr>"
2364 "<td><a href='%s'>%s</a></td>"
2365 "<td style='text-align: center'>"
2366 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2368 body
, i
, uri
, title
,
2369 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2381 /* if none, say so */
2384 body
= g_strdup_printf("%s<tr>"
2385 "<td colspan='3' style='text-align: center'>"
2386 "No favorites - To add one use the 'favadd' command."
2387 "</td></tr>", body
);
2398 html
= g_strdup_printf("%s%s</table></div></html>",
2400 load_webkit_string(t
, html
);
2403 update_favorite_tabs(t
);
2416 getparams(char *cmd
, char *cmp
)
2421 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2422 rv
= cmd
+ strlen(cmp
);
2425 if (strlen(rv
) == 0)
2434 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2435 size_t cert_count
, char *title
)
2437 gnutls_datum_t cinfo
;
2438 char *tmp
, *header
, *body
, *footer
;
2441 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2442 footer
= g_strdup("</body></html>");
2443 body
= g_strdup("");
2445 for (i
= 0; i
< cert_count
; i
++) {
2446 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2451 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2452 body
, i
, cinfo
.data
);
2453 gnutls_free(cinfo
.data
);
2457 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2461 load_webkit_string(t
, tmp
);
2466 ca_cmd(struct tab
*t
, struct karg
*args
)
2469 int rv
= 1, certs
= 0, certs_read
;
2472 gnutls_x509_crt_t
*c
= NULL
;
2473 char *certs_buf
= NULL
, *s
;
2475 /* yeah yeah stat race */
2476 if (stat(ssl_ca_file
, &sb
)) {
2477 show_oops(t
, "no CA file: %s", ssl_ca_file
);
2481 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2482 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2486 certs_buf
= g_malloc(sb
.st_size
+ 1);
2487 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2488 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2491 certs_buf
[sb
.st_size
] = '\0';
2494 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2496 s
+= strlen("BEGIN CERTIFICATE");
2499 bzero(&dt
, sizeof dt
);
2500 dt
.data
= certs_buf
;
2501 dt
.size
= sb
.st_size
;
2502 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2503 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
,
2504 GNUTLS_X509_FMT_PEM
, 0);
2505 if (certs_read
<= 0) {
2506 show_oops(t
, "No cert(s) available");
2509 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2522 connect_socket_from_uri(char *uri
, char *domain
, size_t domain_sz
)
2525 struct addrinfo hints
, *res
= NULL
, *ai
;
2529 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2532 su
= soup_uri_new(uri
);
2535 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2538 snprintf(port
, sizeof port
, "%d", su
->port
);
2539 bzero(&hints
, sizeof(struct addrinfo
));
2540 hints
.ai_flags
= AI_CANONNAME
;
2541 hints
.ai_family
= AF_UNSPEC
;
2542 hints
.ai_socktype
= SOCK_STREAM
;
2544 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2547 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2548 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2551 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2554 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2558 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2563 strlcpy(domain
, su
->host
, domain_sz
);
2574 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2577 gnutls_deinit(gsession
);
2579 gnutls_certificate_free_credentials(xcred
);
2585 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2586 gnutls_certificate_credentials_t
*xc
)
2588 gnutls_certificate_credentials_t xcred
;
2589 gnutls_session_t gsession
;
2592 if (gs
== NULL
|| xc
== NULL
)
2598 gnutls_certificate_allocate_credentials(&xcred
);
2599 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2600 GNUTLS_X509_FMT_PEM
);
2601 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2602 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2603 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2604 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2605 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2606 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2608 gnutls_error_is_fatal(rv
),
2609 gnutls_strerror_name(rv
));
2610 stop_tls(gsession
, xcred
);
2614 gnutls_credentials_type_t cred
;
2615 cred
= gnutls_auth_get_type(gsession
);
2616 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2617 stop_tls(gsession
, xcred
);
2629 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2633 const gnutls_datum_t
*cl
;
2634 gnutls_x509_crt_t
*all_certs
;
2637 if (certs
== NULL
|| cert_count
== NULL
)
2639 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2641 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2645 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2646 for (i
= 0; i
< len
; i
++) {
2647 gnutls_x509_crt_init(&all_certs
[i
]);
2648 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2649 GNUTLS_X509_FMT_PEM
< 0)) {
2663 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2667 for (i
= 0; i
< cert_count
; i
++)
2668 gnutls_x509_crt_deinit(certs
[i
]);
2673 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2674 size_t cert_count
, char *domain
)
2677 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2682 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2685 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2686 if ((f
= fopen(file
, "w")) == NULL
) {
2687 show_oops(t
, "Can't create cert file %s %s",
2688 file
, strerror(errno
));
2692 for (i
= 0; i
< cert_count
; i
++) {
2693 cert_buf_sz
= sizeof cert_buf
;
2694 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2695 cert_buf
, &cert_buf_sz
)) {
2696 show_oops(t
, "gnutls_x509_crt_export failed");
2699 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2700 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2705 /* not the best spot but oh well */
2706 gdk_color_parse("lightblue", &color
);
2707 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2708 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2709 gdk_color_parse("black", &color
);
2710 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
2716 load_compare_cert(struct tab
*t
, struct karg
*args
)
2718 WebKitWebFrame
*frame
;
2719 char *uri
, domain
[8182], file
[PATH_MAX
];
2720 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2721 int s
= -1, rv
= 1, i
;
2725 gnutls_session_t gsession
;
2726 gnutls_x509_crt_t
*certs
;
2727 gnutls_certificate_credentials_t xcred
;
2732 frame
= webkit_web_view_get_main_frame(t
->wv
);
2733 uri
= (char *)webkit_web_frame_get_uri(frame
);
2734 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2738 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2739 show_oops(t
, "Start TLS failed");
2744 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2745 show_oops(t
, "Can't get connection certificates");
2749 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2750 if ((f
= fopen(file
, "r")) == NULL
)
2753 for (i
= 0; i
< cert_count
; i
++) {
2754 cert_buf_sz
= sizeof cert_buf
;
2755 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2756 cert_buf
, &cert_buf_sz
)) {
2759 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2760 rv
= -1; /* critical */
2763 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
2764 rv
= -1; /* critical */
2773 free_connection_certs(certs
, cert_count
);
2775 /* we close the socket first for speed */
2778 stop_tls(gsession
, xcred
);
2784 cert_cmd(struct tab
*t
, struct karg
*args
)
2786 WebKitWebFrame
*frame
;
2787 char *uri
, *action
, domain
[8182];
2790 gnutls_session_t gsession
;
2791 gnutls_x509_crt_t
*certs
;
2792 gnutls_certificate_credentials_t xcred
;
2797 if ((action
= getparams(args
->s
, "cert")))
2802 frame
= webkit_web_view_get_main_frame(t
->wv
);
2803 uri
= (char *)webkit_web_frame_get_uri(frame
);
2804 if (uri
&& strlen(uri
) == 0) {
2805 show_oops(t
, "Invalid URI");
2808 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
2809 show_oops(t
, "Invalid certidicate URI: %s", uri
);
2814 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2815 show_oops(t
, "Start TLS failed");
2820 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2821 show_oops(t
, "get_connection_certs failed");
2825 if (!strcmp(action
, "show"))
2826 show_certs(t
, certs
, cert_count
, "Certificate Chain");
2827 else if (!strcmp(action
, "save"))
2828 save_certs(t
, certs
, cert_count
, domain
);
2830 show_oops(t
, "Invalid command: %s", action
);
2832 free_connection_certs(certs
, cert_count
);
2834 /* we close the socket first for speed */
2837 stop_tls(gsession
, xcred
);
2843 remove_cookie(int index
)
2849 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
2851 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2853 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
2857 print_cookie("remove cookie", c
);
2858 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
2863 soup_cookies_free(cf
);
2869 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
2872 char *tmp
, *header
, *body
, *footer
;
2873 int p_js
= 0, s_js
= 0;
2875 /* we set this to indicate we want to manually do navaction */
2876 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2878 if (g_str_has_prefix(args
, "show a") ||
2879 !strcmp(args
, "show")) {
2883 } else if (g_str_has_prefix(args
, "show p")) {
2884 /* show persistent */
2886 } else if (g_str_has_prefix(args
, "show s")) {
2892 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
2894 footer
= g_strdup("</body></html>");
2895 body
= g_strdup("");
2900 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
2902 RB_FOREACH(d
, domain_list
, wl
) {
2906 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
2914 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
2916 RB_FOREACH(d
, domain_list
, wl
) {
2920 body
= g_strdup_printf("%s%s", body
, d
->d
);
2925 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2929 load_webkit_string(t
, tmp
);
2935 wl_save(struct tab
*t
, struct karg
*args
, int js
)
2937 char file
[PATH_MAX
];
2939 char *line
= NULL
, *lt
= NULL
;
2941 WebKitWebFrame
*frame
;
2942 char *dom
= NULL
, *uri
, *dom_save
= NULL
;
2949 if (t
== NULL
|| args
== NULL
)
2952 if (runtime_settings
[0] == '\0')
2955 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
2956 if ((f
= fopen(file
, "r+")) == NULL
)
2959 frame
= webkit_web_view_get_main_frame(t
->wv
);
2960 uri
= (char *)webkit_web_frame_get_uri(frame
);
2961 dom
= find_domain(uri
, 1);
2962 if (uri
== NULL
|| dom
== NULL
) {
2963 show_oops(t
, "Can't add domain to %s white list",
2964 js
? "JavaScript" : "cookie");
2968 if (g_str_has_prefix(args
->s
, "save d")) {
2970 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
2971 show_oops(t
, "invalid domain: %s", dom
);
2974 flags
= XT_WL_TOPLEVEL
;
2975 } else if (g_str_has_prefix(args
->s
, "save f") ||
2976 !strcmp(args
->s
, "save")) {
2981 show_oops(t
, "invalid command: %s", args
->s
);
2985 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
2988 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
2991 if (!strcmp(line
, lt
))
2997 fprintf(f
, "%s\n", lt
);
3002 d
= wl_find(dom_save
, &js_wl
);
3005 d
= wl_find(dom_save
, &c_wl
);
3008 /* find and add to persistent jar */
3009 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
3010 for (;cf
; cf
= cf
->next
) {
3012 if (!strcmp(dom_save
, ci
->domain
) ||
3013 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
3014 c
= soup_cookie_copy(ci
);
3015 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
3018 soup_cookies_free(cf
);
3036 cookie_cmd(struct tab
*t
, struct karg
*args
)
3041 if ((cmd
= getparams(args
->s
, "cookie")))
3047 if (g_str_has_prefix(cmd
, "show")) {
3048 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
3049 } else if (g_str_has_prefix(cmd
, "save")) {
3052 } else if (g_str_has_prefix(cmd
, "toggle")) {
3054 if (g_str_has_prefix(cmd
, "toggle d"))
3055 a
.i
|= XT_WL_TOPLEVEL
;
3059 } else if (g_str_has_prefix(cmd
, "delete")) {
3060 show_oops(t
, "'cookie delete' currently unimplemented");
3062 show_oops(t
, "unknown cookie command: %s", cmd
);
3068 js_cmd(struct tab
*t
, struct karg
*args
)
3073 if ((cmd
= getparams(args
->s
, "js")))
3078 if (g_str_has_prefix(cmd
, "show")) {
3079 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
3080 } else if (g_str_has_prefix(cmd
, "save")) {
3083 } else if (g_str_has_prefix(cmd
, "toggle")) {
3085 if (g_str_has_prefix(cmd
, "toggle d"))
3086 a
.i
|= XT_WL_TOPLEVEL
;
3090 } else if (g_str_has_prefix(cmd
, "delete")) {
3091 show_oops(t
, "'js delete' currently unimplemented");
3093 show_oops(t
, "unknown js command: %s", cmd
);
3099 add_favorite(struct tab
*t
, struct karg
*args
)
3101 char file
[PATH_MAX
];
3104 size_t urilen
, linelen
;
3105 WebKitWebFrame
*frame
;
3106 const gchar
*uri
, *title
;
3111 /* don't allow adding of xtp pages to favorites */
3112 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
3113 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
3117 snprintf(file
, sizeof file
, "%s/%s/%s",
3118 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
3119 if ((f
= fopen(file
, "r+")) == NULL
) {
3120 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
3124 title
= webkit_web_view_get_title(t
->wv
);
3125 frame
= webkit_web_view_get_main_frame(t
->wv
);
3126 uri
= webkit_web_frame_get_uri(frame
);
3130 if (title
== NULL
|| uri
== NULL
) {
3131 show_oops(t
, "can't add page to favorites");
3135 urilen
= strlen(uri
);
3138 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
3139 if (linelen
== urilen
&& !strcmp(line
, uri
))
3145 fprintf(f
, "\n%s\n%s", title
, uri
);
3151 update_favorite_tabs(NULL
);
3157 navaction(struct tab
*t
, struct karg
*args
)
3159 WebKitWebHistoryItem
*item
;
3161 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
3162 t
->tab_id
, args
->i
);
3165 if (args
->i
== XT_NAV_BACK
)
3166 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
3168 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
3170 return (XT_CB_PASSTHROUGH
);;
3171 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
3173 return (XT_CB_PASSTHROUGH
);
3178 webkit_web_view_go_back(t
->wv
);
3180 case XT_NAV_FORWARD
:
3181 webkit_web_view_go_forward(t
->wv
);
3184 webkit_web_view_reload(t
->wv
);
3186 case XT_NAV_RELOAD_CACHE
:
3187 webkit_web_view_reload_bypass_cache(t
->wv
);
3190 return (XT_CB_PASSTHROUGH
);
3194 move(struct tab
*t
, struct karg
*args
)
3196 GtkAdjustment
*adjust
;
3197 double pi
, si
, pos
, ps
, upper
, lower
, max
;
3202 case XT_MOVE_BOTTOM
:
3204 case XT_MOVE_PAGEDOWN
:
3205 case XT_MOVE_PAGEUP
:
3206 case XT_MOVE_HALFDOWN
:
3207 case XT_MOVE_HALFUP
:
3208 adjust
= t
->adjust_v
;
3211 adjust
= t
->adjust_h
;
3215 pos
= gtk_adjustment_get_value(adjust
);
3216 ps
= gtk_adjustment_get_page_size(adjust
);
3217 upper
= gtk_adjustment_get_upper(adjust
);
3218 lower
= gtk_adjustment_get_lower(adjust
);
3219 si
= gtk_adjustment_get_step_increment(adjust
);
3220 pi
= gtk_adjustment_get_page_increment(adjust
);
3223 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
3224 "max %f si %f pi %f\n",
3225 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
3226 pos
, ps
, upper
, lower
, max
, si
, pi
);
3232 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3237 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3239 case XT_MOVE_BOTTOM
:
3240 case XT_MOVE_FARRIGHT
:
3241 gtk_adjustment_set_value(adjust
, max
);
3244 case XT_MOVE_FARLEFT
:
3245 gtk_adjustment_set_value(adjust
, lower
);
3247 case XT_MOVE_PAGEDOWN
:
3249 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3251 case XT_MOVE_PAGEUP
:
3253 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3255 case XT_MOVE_HALFDOWN
:
3257 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
3259 case XT_MOVE_HALFUP
:
3261 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
3264 return (XT_CB_PASSTHROUGH
);
3267 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
3269 return (XT_CB_HANDLED
);
3273 url_set_visibility(void)
3277 TAILQ_FOREACH(t
, &tabs
, entry
) {
3278 if (show_url
== 0) {
3279 gtk_widget_hide(t
->toolbar
);
3282 gtk_widget_show(t
->toolbar
);
3287 notebook_tab_set_visibility(GtkNotebook
*notebook
)
3290 gtk_notebook_set_show_tabs(notebook
, FALSE
);
3292 gtk_notebook_set_show_tabs(notebook
, TRUE
);
3296 statusbar_set_visibility(void)
3300 TAILQ_FOREACH(t
, &tabs
, entry
) {
3301 if (show_statusbar
== 0) {
3302 gtk_widget_hide(t
->statusbar
);
3305 gtk_widget_show(t
->statusbar
);
3310 fullscreen(struct tab
*t
, struct karg
*args
)
3312 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3315 return (XT_CB_PASSTHROUGH
);
3318 show_url
= show_tabs
= 1;
3320 show_url
= show_tabs
= 0;
3322 url_set_visibility();
3323 notebook_tab_set_visibility(notebook
);
3325 return (XT_CB_HANDLED
);
3329 statusaction(struct tab
*t
, struct karg
*args
)
3331 int rv
= XT_CB_HANDLED
;
3333 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3336 return (XT_CB_PASSTHROUGH
);
3339 case XT_STATUSBAR_SHOW
:
3340 if (show_statusbar
== 0) {
3342 statusbar_set_visibility();
3345 case XT_STATUSBAR_HIDE
:
3346 if (show_statusbar
== 1) {
3348 statusbar_set_visibility();
3356 urlaction(struct tab
*t
, struct karg
*args
)
3358 int rv
= XT_CB_HANDLED
;
3360 DNPRINTF(XT_D_TAB
, "%s: %p %d\n", __func__
, t
, args
->i
);
3363 return (XT_CB_PASSTHROUGH
);
3367 if (show_url
== 0) {
3369 url_set_visibility();
3373 if (show_url
== 1) {
3375 url_set_visibility();
3383 tabaction(struct tab
*t
, struct karg
*args
)
3385 int rv
= XT_CB_HANDLED
;
3389 DNPRINTF(XT_D_TAB
, "tabaction: %p %d\n", t
, args
->i
);
3392 return (XT_CB_PASSTHROUGH
);
3396 if ((url
= getparams(args
->s
, "tabnew")))
3397 create_new_tab(url
, NULL
, 1);
3399 create_new_tab(NULL
, NULL
, 1);
3404 case XT_TAB_DELQUIT
:
3405 if (gtk_notebook_get_n_pages(notebook
) > 1)
3411 if ((url
= getparams(args
->s
, "open")) ||
3412 ((url
= getparams(args
->s
, "op"))) ||
3413 ((url
= getparams(args
->s
, "o"))))
3416 rv
= XT_CB_PASSTHROUGH
;
3419 load_uri(t
->wv
, url
);
3422 if (show_tabs
== 0) {
3424 notebook_tab_set_visibility(notebook
);
3428 if (show_tabs
== 1) {
3430 notebook_tab_set_visibility(notebook
);
3433 case XT_TAB_UNDO_CLOSE
:
3434 if (undo_count
== 0) {
3435 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3439 u
= TAILQ_FIRST(&undos
);
3440 create_new_tab(u
->uri
, u
, 1);
3442 TAILQ_REMOVE(&undos
, u
, entry
);
3444 /* u->history is freed in create_new_tab() */
3449 rv
= XT_CB_PASSTHROUGH
;
3463 resizetab(struct tab
*t
, struct karg
*args
)
3465 if (t
== NULL
|| args
== NULL
) {
3466 show_oops_s("resizetab invalid parameters");
3467 return (XT_CB_PASSTHROUGH
);
3470 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3471 t
->tab_id
, args
->i
);
3473 adjustfont_webkit(t
, args
->i
);
3475 return (XT_CB_HANDLED
);
3479 movetab(struct tab
*t
, struct karg
*args
)
3484 if (t
== NULL
|| args
== NULL
) {
3485 show_oops_s("movetab invalid parameters");
3486 return (XT_CB_PASSTHROUGH
);
3489 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3490 t
->tab_id
, args
->i
);
3492 if (args
->i
== XT_TAB_INVALID
)
3493 return (XT_CB_PASSTHROUGH
);
3495 if (args
->i
< XT_TAB_INVALID
) {
3496 /* next or previous tab */
3497 if (TAILQ_EMPTY(&tabs
))
3498 return (XT_CB_PASSTHROUGH
);
3502 /* if at the last page, loop around to the first */
3503 if (gtk_notebook_get_current_page(notebook
) ==
3504 gtk_notebook_get_n_pages(notebook
) - 1)
3505 gtk_notebook_set_current_page(notebook
, 0);
3507 gtk_notebook_next_page(notebook
);
3510 /* if at the first page, loop around to the last */
3511 if (gtk_notebook_current_page(notebook
) == 0)
3512 gtk_notebook_set_current_page(notebook
,
3513 gtk_notebook_get_n_pages(notebook
) - 1);
3515 gtk_notebook_prev_page(notebook
);
3518 gtk_notebook_set_current_page(notebook
, 0);
3521 gtk_notebook_set_current_page(notebook
, -1);
3524 return (XT_CB_PASSTHROUGH
);
3527 return (XT_CB_HANDLED
);
3532 if (t
->tab_id
== x
) {
3533 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3534 return (XT_CB_HANDLED
);
3537 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3538 if (tt
->tab_id
== x
) {
3539 gtk_notebook_set_current_page(notebook
, x
);
3540 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3546 return (XT_CB_HANDLED
);
3550 command(struct tab
*t
, struct karg
*args
)
3552 WebKitWebFrame
*frame
;
3553 char *s
= NULL
, *ss
= NULL
;
3557 if (t
== NULL
|| args
== NULL
) {
3558 show_oops_s("command invalid parameters");
3559 return (XT_CB_PASSTHROUGH
);
3578 case XT_CMD_OPEN_CURRENT
:
3581 case XT_CMD_TABNEW_CURRENT
:
3582 if (!s
) /* FALL THROUGH? */
3584 frame
= webkit_web_view_get_main_frame(t
->wv
);
3585 uri
= webkit_web_frame_get_uri(frame
);
3586 if (uri
&& strlen(uri
)) {
3587 ss
= g_strdup_printf("%s%s", s
, uri
);
3592 show_oops(t
, "command: invalid opcode %d", args
->i
);
3593 return (XT_CB_PASSTHROUGH
);
3596 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3598 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3599 gdk_color_parse("white", &color
);
3600 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3602 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3603 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3608 return (XT_CB_HANDLED
);
3612 * Return a new string with a download row (in html)
3613 * appended. Old string is freed.
3616 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3619 WebKitDownloadStatus stat
;
3620 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3622 char cur_sz
[FMT_SCALED_STRSIZE
];
3623 char tot_sz
[FMT_SCALED_STRSIZE
];
3626 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3628 /* All actions wil take this form:
3629 * xxxt://class/seskey
3631 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3632 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3634 stat
= webkit_download_get_status(dl
->download
);
3637 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3638 status_html
= g_strdup_printf("Finished");
3639 cmd_html
= g_strdup_printf(
3640 "<a href='%s%d/%d'>Remove</a>",
3641 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3643 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3644 /* gather size info */
3645 progress
= 100 * webkit_download_get_progress(dl
->download
);
3648 webkit_download_get_current_size(dl
->download
), cur_sz
);
3650 webkit_download_get_total_size(dl
->download
), tot_sz
);
3652 status_html
= g_strdup_printf(
3653 "<div style='width: 100%%' align='center'>"
3654 "<div class='progress-outer'>"
3655 "<div class='progress-inner' style='width: %.2f%%'>"
3656 "</div></div></div>"
3657 "<div class='dlstatus'>%s of %s (%.2f%%)</div>",
3658 progress
, cur_sz
, tot_sz
, progress
);
3660 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3661 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3665 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3666 status_html
= g_strdup_printf("Cancelled");
3667 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3668 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3670 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3671 status_html
= g_strdup_printf("Error!");
3672 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3673 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3675 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3676 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3677 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3678 status_html
= g_strdup_printf("Starting");
3681 show_oops(t
, "%s: unknown download status", __func__
);
3684 new_html
= g_strdup_printf(
3685 "%s\n<tr><td>%s</td><td>%s</td>"
3686 "<td style='text-align:center'>%s</td></tr>\n",
3687 html
, basename(webkit_download_get_uri(dl
->download
)),
3688 status_html
, cmd_html
);
3692 g_free(status_html
);
3703 * update all download tabs apart from one. Pass NULL if
3704 * you want to update all.
3707 update_download_tabs(struct tab
*apart_from
)
3710 if (!updating_dl_tabs
) {
3711 updating_dl_tabs
= 1; /* stop infinite recursion */
3712 TAILQ_FOREACH(t
, &tabs
, entry
)
3713 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3714 && (t
!= apart_from
))
3715 xtp_page_dl(t
, NULL
);
3716 updating_dl_tabs
= 0;
3721 * update all cookie tabs apart from one. Pass NULL if
3722 * you want to update all.
3725 update_cookie_tabs(struct tab
*apart_from
)
3728 if (!updating_cl_tabs
) {
3729 updating_cl_tabs
= 1; /* stop infinite recursion */
3730 TAILQ_FOREACH(t
, &tabs
, entry
)
3731 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3732 && (t
!= apart_from
))
3733 xtp_page_cl(t
, NULL
);
3734 updating_cl_tabs
= 0;
3739 * update all history tabs apart from one. Pass NULL if
3740 * you want to update all.
3743 update_history_tabs(struct tab
*apart_from
)
3747 if (!updating_hl_tabs
) {
3748 updating_hl_tabs
= 1; /* stop infinite recursion */
3749 TAILQ_FOREACH(t
, &tabs
, entry
)
3750 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
3751 && (t
!= apart_from
))
3752 xtp_page_hl(t
, NULL
);
3753 updating_hl_tabs
= 0;
3757 /* cookie management XTP page */
3759 xtp_page_cl(struct tab
*t
, struct karg
*args
)
3761 char *header
, *body
, *footer
, *page
, *tmp
;
3762 int i
= 1; /* all ids start 1 */
3763 GSList
*sc
, *pc
, *pc_start
;
3767 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3770 show_oops_s("%s invalid parameters", __func__
);
3773 /* mark this tab as cookie jar */
3774 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
3776 /* Generate a new session key */
3777 if (!updating_cl_tabs
)
3778 generate_xtp_session_key(&cl_session_key
);
3781 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3782 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
3783 "</head><body><h1>Cookie Jar</h1>\n");
3786 body
= g_strdup_printf("<div align='center'><table><tr>"
3794 "<th>HTTP_only</th>"
3795 "<th>Remove</th></tr>\n");
3797 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
3798 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
3801 for (; sc
; sc
= sc
->next
) {
3805 for (pc
= pc_start
; pc
; pc
= pc
->next
)
3806 if (soup_cookie_equal(pc
->data
, c
)) {
3807 type
= "Session + Persistent";
3812 body
= g_strdup_printf(
3814 "<td style='width: 3%%; text-align: center'>%s</td>"
3815 "<td style='width: 10%%; word-break: break-all'>%s</td>"
3816 "<td style='width: 20%%; word-break: break-all'>%s</td>"
3817 "<td style='width: 10%%; word-break: break-all'>%s</td>"
3818 "<td style='width: 8%%; word-break: break-all'>%s</td>"
3819 "<td style='width: 12%%; word-break: break-all'>%s</td>"
3820 "<td style='width: 3%%; text-align: center'>%d</td>"
3821 "<td style='width: 3%%; text-align: center'>%d</td>"
3822 "<td style='width: 3%%; text-align: center'>"
3823 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3831 soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "",
3846 soup_cookies_free(sc
);
3847 soup_cookies_free(pc
);
3849 /* small message if there are none */
3852 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3853 "colspan='8'>No Cookies</td></tr>\n", body
);
3858 footer
= g_strdup_printf("</table></div></body></html>");
3860 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3866 load_webkit_string(t
, page
);
3867 update_cookie_tabs(t
);
3875 xtp_page_hl(struct tab
*t
, struct karg
*args
)
3877 char *header
, *body
, *footer
, *page
, *tmp
;
3879 int i
= 1; /* all ids start 1 */
3881 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3884 show_oops_s("%s invalid parameters", __func__
);
3888 /* mark this tab as history manager */
3889 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
3891 /* Generate a new session key */
3892 if (!updating_hl_tabs
)
3893 generate_xtp_session_key(&hl_session_key
);
3896 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
3897 "<title>History</title>\n"
3900 "<h1>History</h1>\n",
3904 body
= g_strdup_printf("<div align='center'><table><tr>"
3905 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
3907 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
3909 body
= g_strdup_printf(
3911 "<td><a href='%s'>%s</a></td>"
3913 "<td style='text-align: center'>"
3914 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3915 body
, h
->uri
, h
->uri
, h
->title
,
3916 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
3917 XT_XTP_HL_REMOVE
, i
);
3923 /* small message if there are none */
3926 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3927 "colspan='3'>No History</td></tr>\n", body
);
3932 footer
= g_strdup_printf("</table></div></body></html>");
3934 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3937 * update all history manager tabs as the xtp session
3938 * key has now changed. No need to update the current tab.
3939 * Already did that above.
3941 update_history_tabs(t
);
3947 load_webkit_string(t
, page
);
3954 * Generate a web page detailing the status of any downloads
3957 xtp_page_dl(struct tab
*t
, struct karg
*args
)
3959 struct download
*dl
;
3960 char *header
, *body
, *footer
, *page
, *tmp
;
3964 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
3967 show_oops_s("%s invalid parameters", __func__
);
3970 /* mark as a download manager tab */
3971 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
3974 * Generate a new session key for next page instance.
3975 * This only happens for the top level call to xtp_page_dl()
3976 * in which case updating_dl_tabs is 0.
3978 if (!updating_dl_tabs
)
3979 generate_xtp_session_key(&dl_session_key
);
3981 /* header - with refresh so as to update */
3982 if (refresh_interval
>= 1)
3983 ref
= g_strdup_printf(
3984 "<meta http-equiv='refresh' content='%u"
3985 ";url=%s%d/%s/%d' />\n",
3995 header
= g_strdup_printf(
3997 "<title>Downloads</title>\n%s%s</head>\n",
3998 XT_DOCTYPE XT_HTML_TAG
,
4002 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
4003 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
4004 "</p><table><tr><th style='width: 60%%'>"
4005 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
4006 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
4008 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
4009 body
= xtp_page_dl_row(t
, body
, dl
);
4013 /* message if no downloads in list */
4016 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
4017 " style='text-align: center'>"
4018 "No downloads</td></tr>\n", body
);
4023 footer
= g_strdup_printf("</table></div></body></html>");
4025 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4029 * update all download manager tabs as the xtp session
4030 * key has now changed. No need to update the current tab.
4031 * Already did that above.
4033 update_download_tabs(t
);
4040 load_webkit_string(t
, page
);
4047 search(struct tab
*t
, struct karg
*args
)
4051 if (t
== NULL
|| args
== NULL
) {
4052 show_oops_s("search invalid parameters");
4055 if (t
->search_text
== NULL
) {
4056 if (global_search
== NULL
)
4057 return (XT_CB_PASSTHROUGH
);
4059 t
->search_text
= g_strdup(global_search
);
4060 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
4061 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
4065 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
4066 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
4069 case XT_SEARCH_NEXT
:
4070 d
= t
->search_forward
;
4072 case XT_SEARCH_PREV
:
4073 d
= !t
->search_forward
;
4076 return (XT_CB_PASSTHROUGH
);
4079 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
4081 return (XT_CB_HANDLED
);
4084 struct settings_args
{
4090 print_setting(struct settings
*s
, char *val
, void *cb_args
)
4093 struct settings_args
*sa
= cb_args
;
4098 if (s
->flags
& XT_SF_RUNTIME
)
4104 *sa
->body
= g_strdup_printf(
4106 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
4107 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
4119 set(struct tab
*t
, struct karg
*args
)
4121 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
4123 struct settings_args sa
;
4125 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
4126 bzero(&sa
, sizeof sa
);
4130 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
4131 "\n<head><title>Settings</title>\n"
4132 "</head><body><h1>Settings</h1>\n");
4135 body
= g_strdup_printf("<div align='center'><table><tr>"
4136 "<th align='left'>Setting</th>"
4137 "<th align='left'>Value</th></tr>\n");
4139 settings_walk(print_setting
, &sa
);
4142 /* small message if there are none */
4145 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
4146 "colspan='2'>No settings</td></tr>\n", body
);
4151 footer
= g_strdup_printf("</table></div></body></html>");
4153 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
4159 load_webkit_string(t
, page
);
4161 show_oops(t
, "Invalid command: %s", pars
);
4163 return (XT_CB_PASSTHROUGH
);
4167 session_save(struct tab
*t
, char *filename
, char **ret
)
4173 f
+= strlen("save");
4174 while (*f
== ' ' && *f
!= '\0')
4180 if (f
[0] == '.' || f
[0] == '/')
4184 if (save_tabs(t
, &a
))
4186 strlcpy(named_session
, f
, sizeof named_session
);
4194 session_open(struct tab
*t
, char *filename
, char **ret
)
4200 f
+= strlen("open");
4201 while (*f
== ' ' && *f
!= '\0')
4207 if (f
[0] == '.' || f
[0] == '/')
4211 a
.i
= XT_SES_CLOSETABS
;
4212 if (open_tabs(t
, &a
))
4215 strlcpy(named_session
, f
, sizeof named_session
);
4223 session_delete(struct tab
*t
, char *filename
, char **ret
)
4225 char file
[PATH_MAX
];
4229 f
+= strlen("delete");
4230 while (*f
== ' ' && *f
!= '\0')
4236 if (f
[0] == '.' || f
[0] == '/')
4239 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, f
);
4243 if (!strcmp(f
, named_session
))
4244 strlcpy(named_session
, XT_SAVED_TABS_FILE
,
4245 sizeof named_session
);
4253 session_cmd(struct tab
*t
, struct karg
*args
)
4255 char *action
= NULL
;
4256 char *filename
= NULL
;
4261 if ((action
= getparams(args
->s
, "session")))
4266 if (!strcmp(action
, "show"))
4267 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
4268 XT_SAVED_TABS_FILE
: named_session
);
4269 else if (g_str_has_prefix(action
, "save ")) {
4270 if (session_save(t
, action
, &filename
)) {
4271 show_oops(t
, "Can't save session: %s",
4272 filename
? filename
: "INVALID");
4275 } else if (g_str_has_prefix(action
, "open ")) {
4276 if (session_open(t
, action
, &filename
)) {
4277 show_oops(t
, "Can't open session: %s",
4278 filename
? filename
: "INVALID");
4281 } else if (g_str_has_prefix(action
, "delete ")) {
4282 if (session_delete(t
, action
, &filename
)) {
4283 show_oops(t
, "Can't delete session: %s",
4284 filename
? filename
: "INVALID");
4288 show_oops(t
, "Invalid command: %s", action
);
4290 return (XT_CB_PASSTHROUGH
);
4294 * Make a hardcopy of the page
4297 print_page(struct tab
*t
, struct karg
*args
)
4299 WebKitWebFrame
*frame
;
4301 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
4304 * for now we just call the GTK print box,
4305 * but later we might decide to hook in a command.
4307 frame
= webkit_web_view_get_main_frame(t
->wv
);
4308 webkit_web_frame_print(frame
);
4314 go_home(struct tab
*t
, struct karg
*args
)
4316 load_uri(t
->wv
, home
);
4321 restart(struct tab
*t
, struct karg
*args
)
4325 a
.s
= XT_RESTART_TABS_FILE
;
4327 execvp(start_argv
[0], start_argv
);
4333 #define CTRL GDK_CONTROL_MASK
4334 #define MOD1 GDK_MOD1_MASK
4335 #define SHFT GDK_SHIFT_MASK
4337 /* inherent to GTK not all keys will be caught at all times */
4338 /* XXX sort key bindings */
4339 struct key_bindings
{
4343 int (*func
)(struct tab
*, struct karg
*);
4346 { MOD1
, 0, GDK_j
, xtp_page_cl
, {0} },
4347 { MOD1
, 0, GDK_d
, xtp_page_dl
, {0} },
4348 { MOD1
, 0, GDK_h
, xtp_page_hl
, {0} },
4349 { CTRL
, 0, GDK_p
, print_page
, {0}},
4350 { 0, 0, GDK_slash
, command
, {.i
= '/'} },
4351 { SHFT
, 0, GDK_question
, command
, {.i
= '?'} },
4352 { SHFT
, 0, GDK_colon
, command
, {.i
= ':'} },
4353 { CTRL
, 0, GDK_q
, quit
, {0} },
4354 { MOD1
, 0, GDK_q
, restart
, {0} },
4355 { CTRL
, 0, GDK_j
, toggle_js
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4356 { MOD1
, 0, GDK_c
, toggle_cwl
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
4357 { CTRL
, 0, GDK_s
, toggle_src
, {0} },
4358 { 0, 0, GDK_y
, yank_uri
, {0} },
4359 { 0, 0, GDK_p
, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
} },
4360 { SHFT
, 0, GDK_P
, paste_uri
, {.i
= XT_PASTE_NEW_TAB
} },
4363 { 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
4364 { SHFT
, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
4367 { 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
4368 { 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
4370 /* command aliases (handy when -S flag is used) */
4371 { 0, 0, GDK_F9
, command
, {.i
= XT_CMD_OPEN
} },
4372 { 0, 0, GDK_F10
, command
, {.i
= XT_CMD_OPEN_CURRENT
} },
4373 { 0, 0, GDK_F11
, command
, {.i
= XT_CMD_TABNEW
} },
4374 { 0, 0, GDK_F12
, command
, {.i
= XT_CMD_TABNEW_CURRENT
} },
4377 { 0, 0, GDK_f
, hint
, {.i
= 0} },
4380 { 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
4381 { MOD1
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
4382 { SHFT
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
4383 { MOD1
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
4384 { 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
4385 { CTRL
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
4386 { CTRL
|SHFT
, 0, GDK_R
, navaction
, {.i
= XT_NAV_RELOAD_CACHE
} },
4387 { CTRL
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
4388 { MOD1
, 1, GDK_f
, xtp_page_fl
, {0} },
4390 /* vertical movement */
4391 { 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
4392 { 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
4393 { 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
4394 { 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
4395 { SHFT
, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
4396 { 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
4397 { 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
4398 { 0, 0, GDK_g
, move
, {.i
= XT_MOVE_TOP
} }, /* XXX make this work */
4399 { 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4400 { CTRL
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4401 { CTRL
, 0, GDK_d
, move
, {.i
= XT_MOVE_HALFDOWN
} },
4402 { 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
4403 { 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
4404 { CTRL
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
4405 { CTRL
, 0, GDK_u
, move
, {.i
= XT_MOVE_HALFUP
} },
4406 /* horizontal movement */
4407 { 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
4408 { 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
4409 { 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
4410 { 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
4411 { SHFT
, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
4412 { 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
4415 { CTRL
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
4416 { CTRL
, 1, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
4417 { SHFT
, 0, GDK_U
, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
4418 { CTRL
, 0, GDK_1
, movetab
, {.i
= 1} },
4419 { CTRL
, 0, GDK_2
, movetab
, {.i
= 2} },
4420 { CTRL
, 0, GDK_3
, movetab
, {.i
= 3} },
4421 { CTRL
, 0, GDK_4
, movetab
, {.i
= 4} },
4422 { CTRL
, 0, GDK_5
, movetab
, {.i
= 5} },
4423 { CTRL
, 0, GDK_6
, movetab
, {.i
= 6} },
4424 { CTRL
, 0, GDK_7
, movetab
, {.i
= 7} },
4425 { CTRL
, 0, GDK_8
, movetab
, {.i
= 8} },
4426 { CTRL
, 0, GDK_9
, movetab
, {.i
= 9} },
4427 { CTRL
, 0, GDK_0
, movetab
, {.i
= 10} },
4428 { CTRL
|SHFT
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
4429 { CTRL
|SHFT
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
4430 { CTRL
, 0, GDK_Left
, movetab
, {.i
= XT_TAB_PREV
} },
4431 { CTRL
, 0, GDK_Right
, movetab
, {.i
= XT_TAB_NEXT
} },
4432 { CTRL
, 0, GDK_minus
, resizetab
, {.i
= -1} },
4433 { CTRL
|SHFT
, 0, GDK_plus
, resizetab
, {.i
= 1} },
4434 { CTRL
, 0, GDK_equal
, resizetab
, {.i
= 1} },
4440 int (*func
)(struct tab
*, struct karg
*);
4443 { "q!", 0, quit
, {0} },
4444 { "qa", 0, quit
, {0} },
4445 { "qa!", 0, quit
, {0} },
4446 { "w", 0, save_tabs
, {0} },
4447 { "wq", 0, save_tabs_and_quit
, {0} },
4448 { "wq!", 0, save_tabs_and_quit
, {0} },
4449 { "help", 0, help
, {0} },
4450 { "about", 0, about
, {0} },
4451 { "stats", 0, stats
, {0} },
4452 { "version", 0, about
, {0} },
4453 { "cookies", 0, xtp_page_cl
, {0} },
4454 { "fav", 0, xtp_page_fl
, {0} },
4455 { "favadd", 0, add_favorite
, {0} },
4456 { "js", 2, js_cmd
, {0} },
4457 { "cookie", 2, cookie_cmd
, {0} },
4458 { "cert", 1, cert_cmd
, {0} },
4459 { "ca", 0, ca_cmd
, {0} },
4460 { "dl", 0, xtp_page_dl
, {0} },
4461 { "h", 0, xtp_page_hl
, {0} },
4462 { "hist", 0, xtp_page_hl
, {0} },
4463 { "history", 0, xtp_page_hl
, {0} },
4464 { "home", 0, go_home
, {0} },
4465 { "restart", 0, restart
, {0} },
4466 { "urlhide", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4467 { "urlh", 0, urlaction
, {.i
= XT_URL_HIDE
} },
4468 { "urlshow", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4469 { "urls", 0, urlaction
, {.i
= XT_URL_SHOW
} },
4470 { "statushide", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4471 { "statush", 0, statusaction
, {.i
= XT_STATUSBAR_HIDE
} },
4472 { "statusshow", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4473 { "statuss", 0, statusaction
, {.i
= XT_STATUSBAR_SHOW
} },
4475 { "1", 0, move
, {.i
= XT_MOVE_TOP
} },
4476 { "print", 0, print_page
, {0} },
4479 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4480 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4481 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4482 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4483 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4484 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4485 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4486 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4487 { "tabshow", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4488 { "tabs", 1, tabaction
, {.i
= XT_TAB_SHOW
} },
4489 { "tabhide", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4490 { "tabh", 1, tabaction
, {.i
= XT_TAB_HIDE
} },
4491 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4492 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4493 /* XXX add count to these commands */
4494 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4495 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4496 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4497 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4498 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
4499 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
4500 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
4501 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
4502 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4503 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4506 { "set", 1, set
, {0} },
4507 { "fullscreen", 0, fullscreen
, {0} },
4508 { "f", 0, fullscreen
, {0} },
4511 { "session", 1, session_cmd
, {0} },
4515 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4523 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4525 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
4527 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
4534 * cancel, remove, etc. downloads
4537 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
4539 struct download find
, *d
;
4541 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
4543 /* some commands require a valid download id */
4544 if (cmd
!= XT_XTP_DL_LIST
) {
4545 /* lookup download in question */
4547 d
= RB_FIND(download_list
, &downloads
, &find
);
4550 show_oops(t
, "%s: no such download", __func__
);
4555 /* decide what to do */
4557 case XT_XTP_DL_CANCEL
:
4558 webkit_download_cancel(d
->download
);
4560 case XT_XTP_DL_REMOVE
:
4561 webkit_download_cancel(d
->download
); /* just incase */
4562 g_object_unref(d
->download
);
4563 RB_REMOVE(download_list
, &downloads
, d
);
4565 case XT_XTP_DL_LIST
:
4569 show_oops(t
, "%s: unknown command", __func__
);
4572 xtp_page_dl(t
, NULL
);
4576 * Actions on history, only does one thing for now, but
4577 * we provide the function for future actions
4580 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
4582 struct history
*h
, *next
;
4586 case XT_XTP_HL_REMOVE
:
4587 /* walk backwards, as listed in reverse */
4588 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
4589 next
= RB_PREV(history_list
, &hl
, h
);
4591 RB_REMOVE(history_list
, &hl
, h
);
4592 g_free((gpointer
) h
->title
);
4593 g_free((gpointer
) h
->uri
);
4600 case XT_XTP_HL_LIST
:
4601 /* Nothing - just xtp_page_hl() below */
4604 show_oops(t
, "%s: unknown command", __func__
);
4608 xtp_page_hl(t
, NULL
);
4611 /* remove a favorite */
4613 remove_favorite(struct tab
*t
, int index
)
4615 char file
[PATH_MAX
], *title
, *uri
;
4616 char *new_favs
, *tmp
;
4621 /* open favorites */
4622 snprintf(file
, sizeof file
, "%s/%s/%s",
4623 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
4625 if ((f
= fopen(file
, "r")) == NULL
) {
4626 show_oops(t
, "%s: can't open favorites: %s",
4627 __func__
, strerror(errno
));
4631 /* build a string which will become the new favroites file */
4632 new_favs
= g_strdup_printf("%s", "");
4635 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
4636 if (feof(f
) || ferror(f
))
4638 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
4645 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
4646 if (feof(f
) || ferror(f
)) {
4647 show_oops(t
, "%s: can't parse favorites %s",
4648 __func__
, strerror(errno
));
4653 /* as long as this isn't the one we are deleting add to file */
4656 new_favs
= g_strdup_printf("%s%s\n%s\n",
4657 new_favs
, title
, uri
);
4669 /* write back new favorites file */
4670 if ((f
= fopen(file
, "w")) == NULL
) {
4671 show_oops(t
, "%s: can't open favorites: %s",
4672 __func__
, strerror(errno
));
4676 fwrite(new_favs
, strlen(new_favs
), 1, f
);
4689 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
4692 case XT_XTP_FL_LIST
:
4693 /* nothing, just the below call to xtp_page_fl() */
4695 case XT_XTP_FL_REMOVE
:
4696 remove_favorite(t
, arg
);
4699 show_oops(t
, "%s: invalid favorites command", __func__
);
4703 xtp_page_fl(t
, NULL
);
4707 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
4710 case XT_XTP_CL_LIST
:
4711 /* nothing, just xtp_page_cl() */
4713 case XT_XTP_CL_REMOVE
:
4717 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
4721 xtp_page_cl(t
, NULL
);
4724 /* link an XTP class to it's session key and handler function */
4725 struct xtp_despatch
{
4728 void (*handle_func
)(struct tab
*, uint8_t, int);
4731 struct xtp_despatch xtp_despatches
[] = {
4732 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
4733 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
4734 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
4735 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
4736 { NULL
, NULL
, NULL
}
4740 * is the url xtp protocol? (xxxt://)
4741 * if so, parse and despatch correct bahvior
4744 parse_xtp_url(struct tab
*t
, const char *url
)
4746 char *dup
= NULL
, *p
, *last
;
4747 uint8_t n_tokens
= 0;
4748 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
4749 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
4753 * tokens array meaning:
4755 * tokens[1] = session key
4756 * tokens[2] = action
4757 * tokens[3] = optional argument
4760 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
4762 /*xtp tab meaning is normal unless proven special */
4763 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4765 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
4768 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
4770 /* split out the url */
4771 for ((p
= strtok_r(dup
, "/", &last
)); p
;
4772 (p
= strtok_r(NULL
, "/", &last
))) {
4774 tokens
[n_tokens
++] = p
;
4777 /* should be atleast three fields 'class/seskey/command/arg' */
4781 dsp
= xtp_despatches
;
4782 req_class
= atoi(tokens
[0]);
4783 while (dsp
->xtp_class
!= NULL
) {
4784 if (dsp
->xtp_class
== req_class
) {
4791 /* did we find one atall? */
4792 if (dsp_match
== NULL
) {
4793 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
4797 /* check session key and call despatch function */
4798 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
4799 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
4812 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
4814 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
4816 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
4819 show_oops_s("activate_uri_entry_cb invalid parameters");
4824 show_oops(t
, "activate_uri_entry_cb no uri");
4828 uri
+= strspn(uri
, "\t ");
4830 /* if xxxt:// treat specially */
4831 if (!parse_xtp_url(t
, uri
)) {
4832 load_uri(t
->wv
, (gchar
*)uri
);
4838 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
4840 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
4841 char *newuri
= NULL
;
4844 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
4847 show_oops_s("activate_search_entry_cb invalid parameters");
4851 if (search_string
== NULL
) {
4852 show_oops(t
, "no search_string");
4856 enc_search
= soup_uri_encode(search
, XT_RESERVED_CHARS
);
4857 newuri
= g_strdup_printf(search_string
, enc_search
);
4860 webkit_web_view_load_uri(t
->wv
, newuri
);
4868 check_and_set_js(gchar
*uri
, struct tab
*t
)
4870 struct domain
*d
= NULL
;
4873 if (uri
== NULL
|| t
== NULL
)
4876 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
4881 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
4882 es
? "enable" : "disable", uri
);
4884 g_object_set(G_OBJECT(t
->settings
),
4885 "enable-scripts", es
, (char *)NULL
);
4886 webkit_web_view_set_settings(t
->wv
, t
->settings
);
4888 button_set_stockid(t
->js_toggle
,
4889 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
4893 show_ca_status(struct tab
*t
, const char *uri
)
4895 WebKitWebFrame
*frame
;
4896 WebKitWebDataSource
*source
;
4897 WebKitNetworkRequest
*request
;
4898 SoupMessage
*message
;
4900 gchar
*col_str
= "white";
4903 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
4904 ssl_strict_certs
, ssl_ca_file
, uri
);
4908 if (ssl_ca_file
== NULL
) {
4909 if (g_str_has_prefix(uri
, "http://"))
4911 if (g_str_has_prefix(uri
, "https://")) {
4917 if (g_str_has_prefix(uri
, "http://") ||
4918 !g_str_has_prefix(uri
, "https://"))
4921 frame
= webkit_web_view_get_main_frame(t
->wv
);
4922 source
= webkit_web_frame_get_data_source(frame
);
4923 request
= webkit_web_data_source_get_request(source
);
4924 message
= webkit_network_request_get_message(request
);
4926 if (message
&& (soup_message_get_flags(message
) &
4927 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
4931 r
= load_compare_cert(t
, NULL
);
4933 col_str
= "lightblue";
4942 gdk_color_parse(col_str
, &color
);
4943 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
4945 if (!strcmp(col_str
, "white")) {
4946 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
4948 gdk_color_parse("black", &color
);
4949 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
4952 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
,
4954 gdk_color_parse("black", &color
);
4955 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
,
4962 free_favicon(struct tab
*t
)
4964 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p pix %p\n",
4965 __func__
, t
->icon_download
, t
->icon_request
, t
->icon_pixbuf
);
4967 if (t
->icon_request
)
4968 g_object_unref(t
->icon_request
);
4970 g_object_unref(t
->icon_pixbuf
);
4971 if (t
->icon_dest_uri
)
4972 g_free(t
->icon_dest_uri
);
4974 t
->icon_pixbuf
= NULL
;
4975 t
->icon_request
= NULL
;
4976 t
->icon_dest_uri
= NULL
;
4980 abort_favicon_download(struct tab
*t
)
4982 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p\n", __func__
, t
->icon_download
);
4984 if (t
->icon_download
)
4985 webkit_download_cancel(t
->icon_download
);
4989 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
4990 GTK_ENTRY_ICON_PRIMARY
, "text-html");
4994 set_favicon_from_file(struct tab
*t
, char *file
)
4997 GdkPixbuf
*pixbuf
, *scaled
;
5000 if (t
== NULL
|| file
== NULL
)
5002 if (t
->icon_pixbuf
) {
5003 DNPRINTF(XT_D_DOWNLOAD
, "%s: icon already set\n", __func__
);
5007 if (g_str_has_prefix(file
, "file://"))
5008 file
+= strlen("file://");
5009 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
5011 if (!stat(file
, &sb
)) {
5012 if (sb
.st_size
== 0) {
5013 /* corrupt icon so trash it */
5014 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5017 /* no need to set icon to default here */
5022 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
5023 if (pixbuf
== NULL
) {
5024 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5025 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5029 g_object_get(pixbuf
, "width", &width
, "height", &height
,
5031 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
5032 __func__
, t
->tab_id
, width
, height
);
5034 if (width
> 16 || height
> 16) {
5035 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5036 GDK_INTERP_BILINEAR
);
5037 g_object_unref(pixbuf
);
5041 if (scaled
== NULL
) {
5042 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
5043 GDK_INTERP_BILINEAR
);
5047 t
->icon_pixbuf
= scaled
;
5048 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
5049 GTK_ENTRY_ICON_PRIMARY
, t
->icon_pixbuf
);
5053 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
5056 WebKitDownloadStatus status
= webkit_download_get_status(download
);
5061 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
5062 __func__
, t
->tab_id
, status
);
5065 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
5067 t
->icon_download
= NULL
;
5070 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
5073 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
5076 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
5078 DNPRINTF(XT_D_DOWNLOAD
, "%s: freeing favicon %d\n",
5079 __func__
, t
->tab_id
);
5080 t
->icon_download
= NULL
;
5083 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
5085 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
5086 __func__
, t
->icon_dest_uri
);
5087 set_favicon_from_file(t
, t
->icon_dest_uri
);
5088 /* these will be freed post callback */
5089 t
->icon_request
= NULL
;
5090 t
->icon_download
= NULL
;
5098 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
5100 gchar
*name_hash
, file
[PATH_MAX
];
5103 DNPRINTF(XT_D_DOWNLOAD
, "notify_icon_loaded_cb %s\n", uri
);
5105 if (uri
== NULL
|| t
== NULL
)
5108 if (t
->icon_request
) {
5109 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
5113 /* check to see if we got the icon in cache */
5114 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
5115 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
5118 if (!stat(file
, &sb
)) {
5119 if (sb
.st_size
> 0) {
5120 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
5122 set_favicon_from_file(t
, file
);
5126 /* corrupt icon so trash it */
5127 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
5132 /* create download for icon */
5133 t
->icon_request
= webkit_network_request_new(uri
);
5134 if (t
->icon_request
== NULL
) {
5135 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
5140 t
->icon_download
= webkit_download_new(t
->icon_request
);
5142 /* we have to free icon_dest_uri later */
5143 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
5144 webkit_download_set_destination_uri(t
->icon_download
,
5147 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
5148 G_CALLBACK(favicon_download_status_changed_cb
), t
);
5150 webkit_download_start(t
->icon_download
);
5154 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
5156 WebKitWebFrame
*frame
;
5157 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
5158 struct history
*h
, find
;
5160 const gchar
*s_loading
;
5163 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d\n",
5164 webkit_web_view_get_load_status(wview
));
5167 show_oops_s("notify_load_status_cb invalid paramters");
5171 switch (webkit_web_view_get_load_status(wview
)) {
5172 case WEBKIT_LOAD_PROVISIONAL
:
5174 abort_favicon_download(t
);
5175 #if GTK_CHECK_VERSION(2, 20, 0)
5176 gtk_widget_show(t
->spinner
);
5177 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
5179 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
5181 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
5185 case WEBKIT_LOAD_COMMITTED
:
5187 frame
= webkit_web_view_get_main_frame(wview
);
5188 uri
= webkit_web_frame_get_uri(frame
);
5190 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
5196 set_status(t
, (char *)uri
, XT_STATUS_URI
);
5199 /* check if js white listing is enabled */
5200 if (enable_js_whitelist
) {
5201 frame
= webkit_web_view_get_main_frame(wview
);
5202 uri
= webkit_web_frame_get_uri(frame
);
5203 check_and_set_js((gchar
*)uri
, t
);
5206 show_ca_status(t
, uri
);
5208 /* we know enough to autosave the session */
5209 if (session_autosave
) {
5215 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
5217 title
= webkit_web_view_get_title(wview
);
5218 frame
= webkit_web_view_get_main_frame(wview
);
5219 uri
= webkit_web_frame_get_uri(frame
);
5227 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
5228 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
5231 if (!strncmp(uri
, "http://", strlen("http://")) ||
5232 !strncmp(uri
, "https://", strlen("https://")) ||
5233 !strncmp(uri
, "file://", strlen("file://")))
5238 h
= RB_FIND(history_list
, &hl
, &find
);
5242 h
= g_malloc(sizeof *h
);
5243 h
->uri
= g_strdup(uri
);
5245 h
->title
= g_strdup(title
);
5247 h
->title
= g_strdup(uri
);
5248 RB_INSERT(history_list
, &hl
, h
);
5249 update_history_tabs(NULL
);
5254 case WEBKIT_LOAD_FINISHED
:
5256 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5257 case WEBKIT_LOAD_FAILED
:
5260 #if GTK_CHECK_VERSION(2, 20, 0)
5261 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5262 gtk_widget_hide(t
->spinner
);
5264 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
5265 if (s_loading
&& !strcmp(s_loading
, "Loading"))
5266 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
5268 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
5273 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
5275 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
5276 webkit_web_view_can_go_back(wview
));
5278 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
5279 webkit_web_view_can_go_forward(wview
));
5281 /* take focus if we are visible */
5287 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5289 run_script(t
, JS_HINTING
);
5293 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
5295 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
5296 progress
== 100 ? 0 : (double)progress
/ 100);
5300 webview_nw_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5301 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5302 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5307 show_oops_s("webview_nw_cb invalid paramters");
5311 DNPRINTF(XT_D_NAV
, "webview_nw_cb: %s\n",
5312 webkit_network_request_get_uri(request
));
5314 /* open in current tab */
5315 uri
= (char *)webkit_network_request_get_uri(request
);
5316 webkit_web_view_load_uri(t
->wv
, uri
);
5317 webkit_web_policy_decision_ignore(pd
);
5319 return (TRUE
); /* we made the decission */
5323 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
5324 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
5325 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
5330 show_oops_s("webview_npd_cb invalid parameters");
5334 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
5336 webkit_network_request_get_uri(request
));
5338 uri
= (char *)webkit_network_request_get_uri(request
);
5340 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
5342 create_new_tab(uri
, NULL
, ctrl_click_focus
);
5343 webkit_web_policy_decision_ignore(pd
);
5344 return (TRUE
); /* we made the decission */
5347 webkit_web_policy_decision_use(pd
);
5348 return (TRUE
); /* we made the decission */
5352 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
5354 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
5355 webkit_web_view_get_uri(wv
));
5361 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
5363 /* we can not eat the event without throwing gtk off so defer it */
5365 /* catch middle click */
5366 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
5371 /* catch ctrl click */
5372 if (e
->type
== GDK_BUTTON_RELEASE
&&
5373 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
5378 return (XT_CB_PASSTHROUGH
);
5382 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
5384 struct mime_type
*m
;
5386 m
= find_mime_type(mime_type
);
5392 show_oops(t
, "can't fork mime handler");
5401 execlp(m
->mt_action
, m
->mt_action
,
5402 webkit_network_request_get_uri(request
), (void *)NULL
);
5411 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
5412 WebKitNetworkRequest
*request
, char *mime_type
,
5413 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
5416 show_oops_s("webview_mimetype_cb invalid parameters");
5420 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
5421 t
->tab_id
, mime_type
);
5423 if (run_mimehandler(t
, mime_type
, request
) == 0) {
5424 webkit_web_policy_decision_ignore(decision
);
5429 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
5430 webkit_web_policy_decision_download(decision
);
5438 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
,
5441 const gchar
*filename
;
5443 struct download
*download_entry
;
5446 if (wk_download
== NULL
|| t
== NULL
) {
5447 show_oops_s("%s invalid parameters", __func__
);
5451 filename
= webkit_download_get_suggested_filename(wk_download
);
5452 if (filename
== NULL
)
5453 return (FALSE
); /* abort download */
5455 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
5457 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
5458 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
5460 webkit_download_set_destination_uri(wk_download
, uri
);
5462 if (webkit_download_get_status(wk_download
) ==
5463 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
5464 show_oops(t
, "%s: download failed to start", __func__
);
5466 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
5468 download_entry
= g_malloc(sizeof(struct download
));
5469 download_entry
->download
= wk_download
;
5470 download_entry
->tab
= t
;
5471 download_entry
->id
= next_download_id
++;
5472 RB_INSERT(download_list
, &downloads
, download_entry
);
5473 /* get from history */
5474 g_object_ref(wk_download
);
5475 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
5481 /* sync other download manager tabs */
5482 update_download_tabs(NULL
);
5485 * NOTE: never redirect/render the current tab before this
5486 * function returns. This will cause the download to never start.
5488 return (ret
); /* start download */
5492 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
5494 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
5497 show_oops_s("webview_hover_cb");
5502 set_status(t
, uri
, XT_STATUS_LINK
);
5505 set_status(t
, t
->status
, XT_STATUS_NOTHING
);
5510 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
5513 char s
[2], buf
[128];
5514 const char *errstr
= NULL
;
5517 /* don't use w directly; use t->whatever instead */
5520 show_oops_s("wv_keypress_after_cb");
5521 return (XT_CB_PASSTHROUGH
);
5524 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
5525 e
->keyval
, e
->state
, t
);
5529 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
5531 return (XT_CB_HANDLED
);
5535 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
5536 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
5538 /* we have a string */
5540 /* we have a number */
5541 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
5549 /* XXX unfuck this */
5550 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
5551 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
5552 /* last input was numerical */
5554 l
= strlen(t
->hint_num
);
5561 t
->hint_num
[l
] = '\0';
5565 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
5566 /* last input was alphanumerical */
5568 l
= strlen(t
->hint_buf
);
5575 t
->hint_buf
[l
] = '\0';
5585 /* numerical input */
5586 if (CLEAN(e
->state
) == 0 &&
5587 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
5588 snprintf(s
, sizeof s
, "%c", e
->keyval
);
5589 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
5590 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
5593 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
5595 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
5598 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
5600 t
->hint_mode
= XT_HINT_NUMERICAL
;
5604 /* empty the counter buffer */
5605 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
5606 return (XT_CB_HANDLED
);
5609 /* alphanumerical input */
5611 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
5612 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
5613 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
5614 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
5615 snprintf(s
, sizeof s
, "%c", e
->keyval
);
5616 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
5617 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
5620 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
5623 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
5625 t
->hint_mode
= XT_HINT_ALPHANUM
;
5628 /* empty the counter buffer */
5629 bzero(t
->hint_num
, sizeof t
->hint_num
);
5630 return (XT_CB_HANDLED
);
5633 return (XT_CB_HANDLED
);
5636 for (i
= 0; i
< LENGTH(keys
); i
++)
5637 if (e
->keyval
== keys
[i
].key
&& CLEAN(e
->state
) ==
5639 keys
[i
].func(t
, &keys
[i
].arg
);
5640 return (XT_CB_HANDLED
);
5644 return (XT_CB_PASSTHROUGH
);
5648 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5652 return (XT_CB_PASSTHROUGH
);
5656 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5658 const gchar
*c
= gtk_entry_get_text(w
);
5662 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
5663 e
->keyval
, e
->state
, t
);
5666 show_oops_s("cmd_keyrelease_cb invalid parameters");
5667 return (XT_CB_PASSTHROUGH
);
5670 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
5671 e
->keyval
, e
->state
, t
);
5675 if (strlen(c
) == 1) {
5676 webkit_web_view_unmark_text_matches(t
->wv
);
5682 else if (c
[0] == '?')
5688 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
5690 /* not found, mark red */
5691 gdk_color_parse("red", &color
);
5692 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
5693 /* unmark and remove selection */
5694 webkit_web_view_unmark_text_matches(t
->wv
);
5695 /* my kingdom for a way to unselect text in webview */
5697 /* found, highlight all */
5698 webkit_web_view_unmark_text_matches(t
->wv
);
5699 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
5700 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5701 gdk_color_parse("white", &color
);
5702 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
5705 return (XT_CB_PASSTHROUGH
);
5710 cmd_complete(struct tab
*t
, char *s
)
5713 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
5715 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
5717 for (i
= 0; i
< LENGTH(cmds
); i
++) {
5718 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
5719 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
5721 gtk_entry_set_text(w
, ":");
5722 gtk_entry_append_text(w
, cmds
[i
].cmd
);
5723 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
5733 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5738 show_oops_s("entry_key_cb invalid parameters");
5739 return (XT_CB_PASSTHROUGH
);
5742 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
5743 e
->keyval
, e
->state
, t
);
5747 if (e
->keyval
== GDK_Escape
) {
5748 /* don't use focus_webview(t) because we want to type :cmds */
5749 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5752 for (i
= 0; i
< LENGTH(keys
); i
++)
5753 if (e
->keyval
== keys
[i
].key
&&
5754 CLEAN(e
->state
) == keys
[i
].mask
&&
5755 keys
[i
].use_in_entry
) {
5756 keys
[i
].func(t
, &keys
[i
].arg
);
5757 return (XT_CB_HANDLED
);
5760 return (XT_CB_PASSTHROUGH
);
5764 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5766 int rv
= XT_CB_HANDLED
;
5767 const gchar
*c
= gtk_entry_get_text(w
);
5770 show_oops_s("cmd_keypress_cb parameters");
5771 return (XT_CB_PASSTHROUGH
);
5774 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
5775 e
->keyval
, e
->state
, t
);
5779 e
->keyval
= GDK_Escape
;
5780 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
5781 e
->keyval
= GDK_Escape
;
5783 switch (e
->keyval
) {
5789 if (strchr (c
, ' ')) {
5790 /* par completion */
5791 fprintf(stderr
, "completeme par\n");
5795 cmd_complete(t
, (char *)&c
[1]);
5800 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
5808 if (c
[0] == '/' || c
[0] == '?')
5809 webkit_web_view_unmark_text_matches(t
->wv
);
5813 rv
= XT_CB_PASSTHROUGH
;
5819 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
5822 show_oops_s("cmd_focusout_cb invalid parameters");
5823 return (XT_CB_PASSTHROUGH
);
5825 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d\n", t
->tab_id
);
5830 if (show_url
== 0 || t
->focus_wv
)
5833 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
5835 return (XT_CB_PASSTHROUGH
);
5839 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
5843 const gchar
*c
= gtk_entry_get_text(entry
);
5846 show_oops_s("cmd_activate_cb invalid parameters");
5850 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
5855 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
5861 if (c
[0] == '/' || c
[0] == '?') {
5862 if (t
->search_text
) {
5863 g_free(t
->search_text
);
5864 t
->search_text
= NULL
;
5867 t
->search_text
= g_strdup(s
);
5869 g_free(global_search
);
5870 global_search
= g_strdup(s
);
5871 t
->search_forward
= c
[0] == '/';
5876 for (i
= 0; i
< LENGTH(cmds
); i
++)
5877 if (cmds
[i
].params
) {
5878 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
5879 cmds
[i
].arg
.s
= g_strdup(s
);
5880 goto execute_command
;
5883 if (!strcmp(s
, cmds
[i
].cmd
))
5884 goto execute_command
;
5886 show_oops(t
, "Invalid command: %s", s
);
5893 cmds
[i
].func(t
, &cmds
[i
].arg
);
5896 backward_cb(GtkWidget
*w
, struct tab
*t
)
5901 show_oops_s("backward_cb invalid parameters");
5905 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
5912 forward_cb(GtkWidget
*w
, struct tab
*t
)
5917 show_oops_s("forward_cb invalid parameters");
5921 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
5923 a
.i
= XT_NAV_FORWARD
;
5928 stop_cb(GtkWidget
*w
, struct tab
*t
)
5930 WebKitWebFrame
*frame
;
5933 show_oops_s("stop_cb invalid parameters");
5937 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
5939 frame
= webkit_web_view_get_main_frame(t
->wv
);
5940 if (frame
== NULL
) {
5941 show_oops(t
, "stop_cb: no frame");
5945 webkit_web_frame_stop_loading(frame
);
5946 abort_favicon_download(t
);
5950 setup_webkit(struct tab
*t
)
5952 g_object_set(G_OBJECT(t
->settings
),
5953 "user-agent", t
->user_agent
, (char *)NULL
);
5954 g_object_set(G_OBJECT(t
->settings
),
5955 "enable-scripts", enable_scripts
, (char *)NULL
);
5956 g_object_set(G_OBJECT(t
->settings
),
5957 "enable-plugins", enable_plugins
, (char *)NULL
);
5958 adjustfont_webkit(t
, XT_FONT_SET
);
5960 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5964 create_browser(struct tab
*t
)
5970 show_oops_s("create_browser invalid parameters");
5974 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
5975 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
5976 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
5977 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
5979 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
5980 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
5981 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
5983 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
5984 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
5987 t
->settings
= webkit_web_settings_new();
5989 if (user_agent
== NULL
) {
5990 g_object_get(G_OBJECT(t
->settings
), "user-agent", &strval
,
5992 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
5995 t
->user_agent
= g_strdup(user_agent
);
6008 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
6009 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
6010 gtk_widget_set_name(w
, "xxxterm");
6011 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
6012 g_signal_connect(G_OBJECT(w
), "delete_event",
6013 G_CALLBACK (gtk_main_quit
), NULL
);
6019 create_toolbar(struct tab
*t
)
6021 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
6023 b
= gtk_hbox_new(FALSE
, 0);
6025 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
6028 /* backward button */
6029 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
6030 gtk_widget_set_sensitive(t
->backward
, FALSE
);
6031 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
6032 G_CALLBACK(backward_cb
), t
);
6033 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
6035 /* forward button */
6036 t
->forward
= create_button("GoForward",GTK_STOCK_GO_FORWARD
, 0);
6037 gtk_widget_set_sensitive(t
->forward
, FALSE
);
6038 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
6039 G_CALLBACK(forward_cb
), t
);
6040 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
6044 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
6045 gtk_widget_set_sensitive(t
->stop
, FALSE
);
6046 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
6047 G_CALLBACK(stop_cb
), t
);
6048 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
6052 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
6053 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
6054 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
6055 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
6056 G_CALLBACK(js_toggle_cb
), t
);
6057 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
6060 t
->uri_entry
= gtk_entry_new();
6061 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
6062 G_CALLBACK(activate_uri_entry_cb
), t
);
6063 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
6064 (GCallback
)entry_key_cb
, t
);
6065 eb1
= gtk_hbox_new(FALSE
, 0);
6066 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
6067 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
6068 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
6070 /* set empty favicon */
6071 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
6072 GTK_ENTRY_ICON_PRIMARY
, "text-html");
6075 if (fancy_bar
&& search_string
) {
6077 t
->search_entry
= gtk_entry_new();
6078 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
6079 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
6080 G_CALLBACK(activate_search_entry_cb
), t
);
6081 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
6082 (GCallback
)entry_key_cb
, t
);
6083 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
6084 eb2
= gtk_hbox_new(FALSE
, 0);
6085 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
6086 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
6088 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
6098 TAILQ_FOREACH(t
, &tabs
, entry
)
6099 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
6103 undo_close_tab_save(struct tab
*t
)
6107 struct undo
*u1
, *u2
;
6108 WebKitWebFrame
*frame
;
6110 WebKitWebHistoryItem
*item
;
6112 frame
= webkit_web_view_get_main_frame(t
->wv
);
6113 uri
= webkit_web_frame_get_uri(frame
);
6115 if (uri
&& !strlen(uri
))
6118 u1
= g_malloc0(sizeof(struct undo
));
6119 u1
->uri
= g_strdup(uri
);
6121 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6123 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
6124 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
6127 /* forward history */
6128 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
6132 u1
->history
= g_list_prepend(u1
->history
,
6133 webkit_web_history_item_copy(item
));
6134 items
= g_list_next(items
);
6139 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
6140 u1
->history
= g_list_prepend(u1
->history
,
6141 webkit_web_history_item_copy(item
));
6145 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
6149 u1
->history
= g_list_prepend(u1
->history
,
6150 webkit_web_history_item_copy(item
));
6151 items
= g_list_next(items
);
6154 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
6156 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
6157 u2
= TAILQ_LAST(&undos
, undo_tailq
);
6158 TAILQ_REMOVE(&undos
, u2
, entry
);
6160 g_list_free(u2
->history
);
6169 delete_tab(struct tab
*t
)
6173 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
6178 TAILQ_REMOVE(&tabs
, t
, entry
);
6180 /* halt all webkit activity */
6181 abort_favicon_download(t
);
6182 webkit_web_view_stop_loading(t
->wv
);
6183 undo_close_tab_save(t
);
6185 gtk_widget_destroy(t
->vbox
);
6186 g_free(t
->user_agent
);
6190 if (TAILQ_EMPTY(&tabs
))
6191 create_new_tab(NULL
, NULL
, 1);
6194 /* recreate session */
6195 if (session_autosave
) {
6202 adjustfont_webkit(struct tab
*t
, int adjust
)
6205 show_oops_s("adjustfont_webkit invalid parameters");
6209 if (adjust
== XT_FONT_SET
)
6210 t
->font_size
= default_font_size
;
6212 t
->font_size
+= adjust
;
6213 g_object_set(G_OBJECT(t
->settings
), "default-font-size",
6214 t
->font_size
, (char *)NULL
);
6215 g_object_get(G_OBJECT(t
->settings
), "default-font-size",
6216 &t
->font_size
, (char *)NULL
);
6220 append_tab(struct tab
*t
)
6225 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
6226 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
6230 create_new_tab(char *title
, struct undo
*u
, int focus
)
6233 int load
= 1, id
, notfound
;
6235 WebKitWebHistoryItem
*item
;
6238 PangoFontDescription
*fd
= NULL
;
6240 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
6242 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
6243 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
6247 t
= g_malloc0(sizeof *t
);
6249 if (title
== NULL
) {
6250 title
= "(untitled)";
6254 t
->vbox
= gtk_vbox_new(FALSE
, 0);
6256 /* label + button for tab */
6257 b
= gtk_hbox_new(FALSE
, 0);
6260 #if GTK_CHECK_VERSION(2, 20, 0)
6261 t
->spinner
= gtk_spinner_new ();
6263 t
->label
= gtk_label_new(title
);
6264 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
6265 gtk_widget_set_size_request(t
->label
, 100, 0);
6266 gtk_widget_set_size_request(b
, 130, 0);
6268 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
6269 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
6270 #if GTK_CHECK_VERSION(2, 20, 0)
6271 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
6275 t
->toolbar
= create_toolbar(t
);
6276 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
6279 t
->browser_win
= create_browser(t
);
6280 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
6282 /* oops message for user feedback */
6283 t
->oops
= gtk_entry_new();
6284 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
6285 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
6286 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
6287 gdk_color_parse("red", &color
);
6288 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
6289 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
6292 t
->cmd
= gtk_entry_new();
6293 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
6294 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
6295 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
6298 t
->statusbar
= gtk_entry_new();
6299 gtk_entry_set_inner_border(GTK_ENTRY(t
->statusbar
), NULL
);
6300 gtk_entry_set_has_frame(GTK_ENTRY(t
->statusbar
), FALSE
);
6301 gtk_widget_set_can_focus(GTK_WIDGET(t
->statusbar
), FALSE
);
6302 gdk_color_parse("black", &color
);
6303 gtk_widget_modify_base(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6304 gdk_color_parse("white", &color
);
6305 gtk_widget_modify_text(t
->statusbar
, GTK_STATE_NORMAL
, &color
);
6306 fd
= GTK_WIDGET(t
->statusbar
)->style
->font_desc
;
6307 pango_font_description_set_weight(fd
, PANGO_WEIGHT_SEMIBOLD
);
6308 pango_font_description_set_absolute_size(fd
, 10 * PANGO_SCALE
); /* 10 px font */
6309 gtk_widget_modify_font(t
->statusbar
, fd
);
6310 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->statusbar
, FALSE
, FALSE
, 0);
6312 /* xtp meaning is normal by default */
6313 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
6315 /* and show it all */
6316 gtk_widget_show_all(b
);
6317 gtk_widget_show_all(t
->vbox
);
6319 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
6323 id
= gtk_notebook_get_current_page(notebook
);
6324 TAILQ_FOREACH(tt
, &tabs
, entry
) {
6325 if (tt
->tab_id
== id
) {
6327 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
6328 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
6338 #if GTK_CHECK_VERSION(2, 20, 0)
6339 /* turn spinner off if we are a new tab without uri */
6341 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
6342 gtk_widget_hide(t
->spinner
);
6345 /* make notebook tabs reorderable */
6346 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
6348 g_object_connect(G_OBJECT(t
->cmd
),
6349 "signal::key-press-event", (GCallback
)cmd_keypress_cb
, t
,
6350 "signal::key-release-event", (GCallback
)cmd_keyrelease_cb
, t
,
6351 "signal::focus-out-event", (GCallback
)cmd_focusout_cb
, t
,
6352 "signal::activate", (GCallback
)cmd_activate_cb
, t
,
6355 /* reuse wv_button_cb to hide oops */
6356 g_object_connect(G_OBJECT(t
->oops
),
6357 "signal::button_press_event", (GCallback
)wv_button_cb
, t
,
6360 g_object_connect(G_OBJECT(t
->wv
),
6361 "signal::key-press-event", (GCallback
)wv_keypress_cb
, t
,
6362 "signal-after::key-press-event", (GCallback
)wv_keypress_after_cb
, t
,
6363 "signal::hovering-over-link", (GCallback
)webview_hover_cb
, t
,
6364 "signal::download-requested", (GCallback
)webview_download_cb
, t
,
6365 "signal::mime-type-policy-decision-requested", (GCallback
)webview_mimetype_cb
, t
,
6366 "signal::navigation-policy-decision-requested", (GCallback
)webview_npd_cb
, t
,
6367 "signal::new-window-policy-decision-requested", (GCallback
)webview_nw_cb
, t
,
6368 "signal::create-web-view", (GCallback
)webview_cwv_cb
, t
,
6369 "signal::event", (GCallback
)webview_event_cb
, t
,
6370 "signal::load-finished", (GCallback
)webview_load_finished_cb
, t
,
6371 "signal::load-progress-changed", (GCallback
)webview_progress_changed_cb
, t
,
6372 #if WEBKIT_CHECK_VERSION(1, 1, 18)
6373 "signal::icon-loaded", (GCallback
)notify_icon_loaded_cb
, t
,
6375 "signal::button_press_event", (GCallback
)wv_button_cb
, t
,
6377 g_signal_connect(t
->wv
,
6378 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
6380 /* hijack the unused keys as if we were the browser */
6381 g_object_connect(G_OBJECT(t
->toolbar
),
6382 "signal-after::key-press-event", (GCallback
)wv_keypress_after_cb
, t
,
6385 g_signal_connect(G_OBJECT(bb
), "button_press_event",
6386 G_CALLBACK(tab_close_cb
), t
);
6391 url_set_visibility();
6392 statusbar_set_visibility();
6395 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
6396 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
6401 load_uri(t
->wv
, title
);
6404 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
6409 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
6410 /* restore the tab's history */
6411 if (u
&& u
->history
) {
6415 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
6416 items
= g_list_next(items
);
6419 item
= g_list_nth_data(u
->history
, u
->back
);
6421 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
6424 g_list_free(u
->history
);
6426 webkit_web_back_forward_list_clear(t
->bfl
);
6430 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
6436 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
6438 TAILQ_FOREACH(t
, &tabs
, entry
) {
6439 if (t
->tab_id
== pn
) {
6440 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
6443 uri
= webkit_web_view_get_title(t
->wv
);
6446 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
6458 menuitem_response(struct tab
*t
)
6460 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
6464 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
6466 GtkWidget
*menu
, *menu_items
;
6467 GdkEventButton
*bevent
;
6468 WebKitWebFrame
*frame
;
6472 if (event
->type
== GDK_BUTTON_PRESS
) {
6473 bevent
= (GdkEventButton
*) event
;
6474 menu
= gtk_menu_new();
6476 TAILQ_FOREACH(ti
, &tabs
, entry
) {
6477 frame
= webkit_web_view_get_main_frame(ti
->wv
);
6478 uri
= webkit_web_frame_get_uri(frame
);
6479 /* XXX make sure there is something to print */
6480 /* XXX add gui pages in here to look purdy */
6483 if (strlen(uri
) == 0)
6485 menu_items
= gtk_menu_item_new_with_label(uri
);
6486 gtk_menu_append(GTK_MENU (menu
), menu_items
);
6487 gtk_widget_show(menu_items
);
6489 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
6490 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
6494 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
6495 bevent
->button
, bevent
->time
);
6497 /* unref object so it'll free itself when popped down */
6498 g_object_ref_sink(menu
);
6499 g_object_unref(menu
);
6501 return (TRUE
/* eat event */);
6504 return (FALSE
/* propagate */);
6508 icon_size_map(int icon_size
)
6510 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
6511 icon_size
> GTK_ICON_SIZE_DIALOG
)
6512 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
6518 create_button(char *name
, char *stockid
, int size
)
6520 GtkWidget
*button
, *image
;
6523 rcstring
= g_strdup_printf(
6524 "style \"%s-style\"\n"
6526 " GtkWidget::focus-padding = 0\n"
6527 " GtkWidget::focus-line-width = 0\n"
6531 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
6532 gtk_rc_parse_string(rcstring
);
6534 button
= gtk_button_new();
6535 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
6536 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
6538 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
6539 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
6540 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
6541 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
6542 gtk_widget_set_name(button
, name
);
6543 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
6544 gtk_widget_set_tooltip_text(button
, name
);
6550 button_set_stockid(GtkWidget
*button
, char *stockid
)
6553 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
6554 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
6555 gtk_button_set_image(GTK_BUTTON(button
), image
);
6564 char file
[PATH_MAX
];
6567 vbox
= gtk_vbox_new(FALSE
, 0);
6568 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
6569 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
6570 gtk_notebook_set_tab_hborder(notebook
, 0);
6571 gtk_notebook_set_tab_vborder(notebook
, 0);
6572 gtk_notebook_set_scrollable(notebook
, TRUE
);
6573 notebook_tab_set_visibility(notebook
);
6574 gtk_notebook_set_show_border(notebook
, FALSE
);
6575 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
6577 abtn
= gtk_button_new();
6578 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
6579 gtk_widget_set_size_request(arrow
, -1, -1);
6580 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
6581 gtk_widget_set_size_request(abtn
, -1, 20);
6582 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
6584 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
6585 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
6586 gtk_widget_set_size_request(vbox
, -1, -1);
6588 g_object_connect(G_OBJECT(notebook
),
6589 "signal::switch-page", (GCallback
)notebook_switchpage_cb
, NULL
,
6591 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
6592 G_CALLBACK(arrow_cb
), NULL
);
6594 main_window
= create_window();
6595 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
6596 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
6599 for (i
= 0; i
< LENGTH(icons
); i
++) {
6600 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
6601 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
6602 l
= g_list_append(l
, pb
);
6604 gtk_window_set_default_icon_list(l
);
6606 gtk_widget_show_all(abtn
);
6607 gtk_widget_show_all(main_window
);
6611 set_hook(void **hook
, char *name
)
6614 errx(1, "set_hook");
6616 if (*hook
== NULL
) {
6617 *hook
= dlsym(RTLD_NEXT
, name
);
6619 errx(1, "can't hook %s", name
);
6623 /* override libsoup soup_cookie_equal because it doesn't look at domain */
6625 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
6627 g_return_val_if_fail(cookie1
, FALSE
);
6628 g_return_val_if_fail(cookie2
, FALSE
);
6630 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
6631 !strcmp (cookie1
->value
, cookie2
->value
) &&
6632 !strcmp (cookie1
->path
, cookie2
->path
) &&
6633 !strcmp (cookie1
->domain
, cookie2
->domain
));
6637 transfer_cookies(void)
6640 SoupCookie
*sc
, *pc
;
6642 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
6644 for (;cf
; cf
= cf
->next
) {
6646 sc
= soup_cookie_copy(pc
);
6647 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
6650 soup_cookies_free(cf
);
6654 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
6659 print_cookie("soup_cookie_jar_delete_cookie", c
);
6661 if (cookies_enabled
== 0)
6664 if (jar
== NULL
|| c
== NULL
)
6667 /* find and remove from persistent jar */
6668 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
6670 for (;cf
; cf
= cf
->next
) {
6672 if (soup_cookie_equal(ci
, c
)) {
6673 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
6678 soup_cookies_free(cf
);
6680 /* delete from session jar */
6681 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
6685 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
6687 struct domain
*d
= NULL
;
6691 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
6692 jar
, p_cookiejar
, s_cookiejar
);
6694 if (cookies_enabled
== 0)
6697 /* see if we are up and running */
6698 if (p_cookiejar
== NULL
) {
6699 _soup_cookie_jar_add_cookie(jar
, cookie
);
6702 /* disallow p_cookiejar adds, shouldn't happen */
6703 if (jar
== p_cookiejar
)
6706 if (enable_cookie_whitelist
&&
6707 (d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
6709 DNPRINTF(XT_D_COOKIE
,
6710 "soup_cookie_jar_add_cookie: reject %s\n",
6712 if (save_rejected_cookies
) {
6713 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
)
6714 show_oops_s("can't open reject cookie file");
6715 fseek(r_cookie_f
, 0, SEEK_END
);
6716 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
6717 cookie
->http_only
? "#HttpOnly_" : "",
6719 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
6721 cookie
->secure
? "TRUE" : "FALSE",
6723 (gulong
)soup_date_to_time_t(cookie
->expires
) :
6730 if (!allow_volatile_cookies
)
6734 if (cookie
->expires
== NULL
&& session_timeout
) {
6735 soup_cookie_set_expires(cookie
,
6736 soup_date_new_from_now(session_timeout
));
6737 print_cookie("modified add cookie", cookie
);
6740 /* see if we are white listed for persistence */
6741 if ((d
&& d
->handy
) || (enable_cookie_whitelist
== 0)) {
6742 /* add to persistent jar */
6743 c
= soup_cookie_copy(cookie
);
6744 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
6745 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
6748 /* add to session jar */
6749 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
6750 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
6756 char file
[PATH_MAX
];
6758 set_hook((void *)&_soup_cookie_jar_add_cookie
,
6759 "soup_cookie_jar_add_cookie");
6760 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
6761 "soup_cookie_jar_delete_cookie");
6763 if (cookies_enabled
== 0)
6767 * the following code is intricate due to overriding several libsoup
6769 * do not alter order of these operations.
6772 /* rejected cookies */
6773 if (save_rejected_cookies
)
6774 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
6776 /* persistent cookies */
6777 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
6778 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
6780 /* session cookies */
6781 s_cookiejar
= soup_cookie_jar_new();
6782 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
6783 cookie_policy
, (void *)NULL
);
6786 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
6790 setup_proxy(char *uri
)
6793 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
6794 soup_uri_free(proxy_uri
);
6798 if (http_proxy
!= uri
) {
6805 http_proxy
= g_strdup(uri
);
6806 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
6807 proxy_uri
= soup_uri_new(http_proxy
);
6808 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
6813 send_url_to_socket(char *url
)
6815 int s
, len
, rv
= -1;
6816 struct sockaddr_un sa
;
6818 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6819 warnx("send_url_to_socket: socket");
6823 sa
.sun_family
= AF_UNIX
;
6824 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6825 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6828 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6829 warnx("send_url_to_socket: connect");
6833 if (send(s
, url
, strlen(url
) + 1, 0) == -1) {
6834 warnx("send_url_to_socket: send");
6843 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
6846 char str
[XT_MAX_URL_LENGTH
];
6847 socklen_t t
= sizeof(struct sockaddr_un
);
6848 struct sockaddr_un sa
;
6853 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
6854 warn("socket_watcher: accept");
6858 if (getpeereid(s
, &uid
, &gid
) == -1) {
6859 warn("socket_watcher: getpeereid");
6862 if (uid
!= getuid() || gid
!= getgid()) {
6863 warnx("socket_watcher: unauthorized user");
6869 warnx("socket_watcher: not a valid user");
6873 n
= recv(s
, str
, sizeof(str
), 0);
6877 create_new_tab(str
, NULL
, 1);
6884 struct sockaddr_un sa
;
6886 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6887 warn("is_running: socket");
6891 sa
.sun_family
= AF_UNIX
;
6892 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6893 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6896 /* connect to see if there is a listener */
6897 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
6898 rv
= 0; /* not running */
6900 rv
= 1; /* already running */
6911 struct sockaddr_un sa
;
6913 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6914 warn("build_socket: socket");
6918 sa
.sun_family
= AF_UNIX
;
6919 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6920 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6923 /* connect to see if there is a listener */
6924 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6925 /* no listener so we will */
6926 unlink(sa
.sun_path
);
6928 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6929 warn("build_socket: bind");
6933 if (listen(s
, 1) == -1) {
6934 warn("build_socket: listen");
6950 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
6955 main(int argc
, char *argv
[])
6958 int c
, s
, optn
= 0, focus
= 1;
6959 char conf
[PATH_MAX
] = { '\0' };
6960 char file
[PATH_MAX
];
6961 char *env_proxy
= NULL
;
6964 struct sigaction sact
;
6968 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
6970 while ((c
= getopt(argc
, argv
, "STVf:s:tn")) != -1) {
6979 errx(0 , "Version: %s", version
);
6982 strlcpy(conf
, optarg
, sizeof(conf
));
6985 strlcpy(named_session
, optarg
, sizeof(named_session
));
7004 RB_INIT(&downloads
);
7006 TAILQ_INIT(&aliases
);
7009 gnutls_global_init();
7011 /* generate session keys for xtp pages */
7012 generate_xtp_session_key(&dl_session_key
);
7013 generate_xtp_session_key(&hl_session_key
);
7014 generate_xtp_session_key(&cl_session_key
);
7015 generate_xtp_session_key(&fl_session_key
);
7018 gtk_init(&argc
, &argv
);
7019 if (!g_thread_supported())
7020 g_thread_init(NULL
);
7023 bzero(&sact
, sizeof(sact
));
7024 sigemptyset(&sact
.sa_mask
);
7025 sact
.sa_handler
= sigchild
;
7026 sact
.sa_flags
= SA_NOCLDSTOP
;
7027 sigaction(SIGCHLD
, &sact
, NULL
);
7029 /* set download dir */
7030 pwd
= getpwuid(getuid());
7032 errx(1, "invalid user %d", getuid());
7033 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
7035 /* set default string settings */
7036 home
= g_strdup("http://www.peereboom.us");
7037 search_string
= g_strdup("https://ssl.scroogle.org/cgi-bin/nbbwssl.cgi?Gw=%s");
7038 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
7039 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
7041 /* read config file */
7042 if (strlen(conf
) == 0)
7043 snprintf(conf
, sizeof conf
, "%s/.%s",
7044 pwd
->pw_dir
, XT_CONF_FILE
);
7045 config_parse(conf
, 0);
7047 /* working directory */
7048 snprintf(work_dir
, sizeof work_dir
, "%s/%s", pwd
->pw_dir
, XT_DIR
);
7049 if (stat(work_dir
, &sb
)) {
7050 if (mkdir(work_dir
, S_IRWXU
) == -1)
7051 err(1, "mkdir work_dir");
7052 if (stat(work_dir
, &sb
))
7053 err(1, "stat work_dir");
7055 if (S_ISDIR(sb
.st_mode
) == 0)
7056 errx(1, "%s not a dir", work_dir
);
7057 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7058 warnx("fixing invalid permissions on %s", work_dir
);
7059 if (chmod(work_dir
, S_IRWXU
) == -1)
7063 /* icon cache dir */
7064 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s/%s",
7065 pwd
->pw_dir
, XT_DIR
, XT_CACHE_DIR
);
7066 if (stat(cache_dir
, &sb
)) {
7067 if (mkdir(cache_dir
, S_IRWXU
) == -1)
7068 err(1, "mkdir cache_dir");
7069 if (stat(cache_dir
, &sb
))
7070 err(1, "stat cache_dir");
7072 if (S_ISDIR(sb
.st_mode
) == 0)
7073 errx(1, "%s not a dir", cache_dir
);
7074 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7075 warnx("fixing invalid permissions on %s", cache_dir
);
7076 if (chmod(cache_dir
, S_IRWXU
) == -1)
7081 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s/%s",
7082 pwd
->pw_dir
, XT_DIR
, XT_CERT_DIR
);
7083 if (stat(certs_dir
, &sb
)) {
7084 if (mkdir(certs_dir
, S_IRWXU
) == -1)
7085 err(1, "mkdir certs_dir");
7086 if (stat(certs_dir
, &sb
))
7087 err(1, "stat certs_dir");
7089 if (S_ISDIR(sb
.st_mode
) == 0)
7090 errx(1, "%s not a dir", certs_dir
);
7091 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7092 warnx("fixing invalid permissions on %s", certs_dir
);
7093 if (chmod(certs_dir
, S_IRWXU
) == -1)
7098 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s/%s",
7099 pwd
->pw_dir
, XT_DIR
, XT_SESSIONS_DIR
);
7100 if (stat(sessions_dir
, &sb
)) {
7101 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
7102 err(1, "mkdir sessions_dir");
7103 if (stat(sessions_dir
, &sb
))
7104 err(1, "stat sessions_dir");
7106 if (S_ISDIR(sb
.st_mode
) == 0)
7107 errx(1, "%s not a dir", sessions_dir
);
7108 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7109 warnx("fixing invalid permissions on %s", sessions_dir
);
7110 if (chmod(sessions_dir
, S_IRWXU
) == -1)
7113 /* runtime settings that can override config file */
7114 if (runtime_settings
[0] != '\0')
7115 config_parse(runtime_settings
, 1);
7118 if (!strcmp(download_dir
, pwd
->pw_dir
))
7119 strlcat(download_dir
, "/downloads", sizeof download_dir
);
7120 if (stat(download_dir
, &sb
)) {
7121 if (mkdir(download_dir
, S_IRWXU
) == -1)
7122 err(1, "mkdir download_dir");
7123 if (stat(download_dir
, &sb
))
7124 err(1, "stat download_dir");
7126 if (S_ISDIR(sb
.st_mode
) == 0)
7127 errx(1, "%s not a dir", download_dir
);
7128 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
7129 warnx("fixing invalid permissions on %s", download_dir
);
7130 if (chmod(download_dir
, S_IRWXU
) == -1)
7134 /* favorites file */
7135 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
7136 if (stat(file
, &sb
)) {
7137 warnx("favorites file doesn't exist, creating it");
7138 if ((f
= fopen(file
, "w")) == NULL
)
7139 err(1, "favorites");
7144 session
= webkit_get_default_session();
7149 if (stat(ssl_ca_file
, &sb
)) {
7150 warn("no CA file: %s", ssl_ca_file
);
7151 g_free(ssl_ca_file
);
7154 g_object_set(session
,
7155 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
7156 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
7161 env_proxy
= getenv("http_proxy");
7163 setup_proxy(env_proxy
);
7165 setup_proxy(http_proxy
);
7167 /* see if there is already an xxxterm running */
7168 if (single_instance
&& is_running()) {
7170 warnx("already running");
7175 send_url_to_socket(argv
[0]);
7186 if (save_global_history
)
7187 restore_global_history();
7189 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
7190 restore_saved_tabs();
7192 a
.s
= named_session
;
7193 a
.i
= XT_SES_DONOTHING
;
7194 open_tabs(NULL
, &a
);
7198 create_new_tab(argv
[0], NULL
, focus
);
7205 if (TAILQ_EMPTY(&tabs
))
7206 create_new_tab(home
, NULL
, 1);
7209 if ((s
= build_socket()) != -1)
7210 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
7214 gnutls_global_deinit();