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>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 * inverse color browsing
25 * multi letter commands
26 * pre and post counts for commands
27 * autocompletion on various inputs
28 * create privacy browsing
29 * - encrypted local data
44 #include "linux/tree.h"
48 #include <sys/queue.h>
49 #include <sys/types.h>
51 #include <sys/socket.h>
55 #include <gdk/gdkkeysyms.h>
56 #include <webkit/webkit.h>
57 #include <libsoup/soup.h>
58 #include <gnutls/gnutls.h>
59 #include <JavaScriptCore/JavaScript.h>
60 #include <gnutls/x509.h>
62 #include "javascript.h"
65 javascript.h borrowed from vimprobable2 under the following license:
67 Copyright (c) 2009 Leon Winter
68 Copyright (c) 2009 Hannes Schueller
69 Copyright (c) 2009 Matto Fransen
71 Permission is hereby granted, free of charge, to any person obtaining a copy
72 of this software and associated documentation files (the "Software"), to deal
73 in the Software without restriction, including without limitation the rights
74 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
75 copies of the Software, and to permit persons to whom the Software is
76 furnished to do so, subject to the following conditions:
78 The above copyright notice and this permission notice shall be included in
79 all copies or substantial portions of the Software.
81 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
82 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
83 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
84 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
85 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
86 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
90 static char *version
= "$xxxterm$";
92 /* hooked functions */
93 void (*_soup_cookie_jar_add_cookie
)(SoupCookieJar
*, SoupCookie
*);
94 void (*_soup_cookie_jar_delete_cookie
)(SoupCookieJar
*,
99 #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0)
100 #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0)
101 #define XT_D_MOVE 0x0001
102 #define XT_D_KEY 0x0002
103 #define XT_D_TAB 0x0004
104 #define XT_D_URL 0x0008
105 #define XT_D_CMD 0x0010
106 #define XT_D_NAV 0x0020
107 #define XT_D_DOWNLOAD 0x0040
108 #define XT_D_CONFIG 0x0080
109 #define XT_D_JS 0x0100
110 #define XT_D_FAVORITE 0x0200
111 #define XT_D_PRINTING 0x0400
112 #define XT_D_COOKIE 0x0800
113 u_int32_t swm_debug
= 0
128 #define DPRINTF(x...)
129 #define DNPRINTF(n,x...)
132 #define LENGTH(x) (sizeof x / sizeof x[0])
133 #define CLEAN(mask) (mask & ~(GDK_MOD2_MASK) & \
134 ~(GDK_BUTTON1_MASK) & \
135 ~(GDK_BUTTON2_MASK) & \
136 ~(GDK_BUTTON3_MASK) & \
137 ~(GDK_BUTTON4_MASK) & \
149 TAILQ_ENTRY(tab
) entry
;
151 GtkWidget
*tab_content
;
154 GtkWidget
*uri_entry
;
155 GtkWidget
*search_entry
;
157 GtkWidget
*browser_win
;
163 GtkWidget
*js_toggle
;
164 GdkPixbuf
*icon_pixbuf
;
167 WebKitDownload
*icon_download
;
170 WebKitWebHistoryItem
*item
;
171 WebKitWebBackForwardList
*bfl
;
173 /* adjustments for browser */
176 GtkAdjustment
*adjust_h
;
177 GtkAdjustment
*adjust_v
;
183 int xtp_meaning
; /* identifies dls/favorites */
188 #define XT_HINT_NONE (0)
189 #define XT_HINT_NUMERICAL (1)
190 #define XT_HINT_ALPHANUM (2)
199 WebKitWebSettings
*settings
;
203 TAILQ_HEAD(tab_list
, tab
);
206 RB_ENTRY(history
) entry
;
210 RB_HEAD(history_list
, history
);
213 RB_ENTRY(download
) entry
;
215 WebKitDownload
*download
;
218 RB_HEAD(download_list
, download
);
221 RB_ENTRY(domain
) entry
;
223 int handy
; /* app use */
225 RB_HEAD(domain_list
, domain
);
228 TAILQ_ENTRY(undo
) entry
;
231 int back
; /* Keeps track of how many back
232 * history items there are. */
234 TAILQ_HEAD(undo_tailq
, undo
);
236 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
237 int next_download_id
= 1;
245 #define XT_NAME ("XXXTerm")
246 #define XT_DIR (".xxxterm")
247 #define XT_CACHE_DIR ("cache")
248 #define XT_CERT_DIR ("certs/")
249 #define XT_SESSIONS_DIR ("sessions/")
250 #define XT_CONF_FILE ("xxxterm.conf")
251 #define XT_FAVS_FILE ("favorites")
252 #define XT_SAVED_TABS_FILE ("main_session")
253 #define XT_RESTART_TABS_FILE ("restart_tabs")
254 #define XT_SOCKET_FILE ("socket")
255 #define XT_HISTORY_FILE ("history")
256 #define XT_CB_HANDLED (TRUE)
257 #define XT_CB_PASSTHROUGH (FALSE)
258 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
259 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
260 #define XT_DLMAN_REFRESH "10"
261 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
262 "td {overflow: hidden;}\n" \
263 "th {background-color: #cccccc}" \
264 "table {width: 90%%; border: 1px black" \
265 " solid; table-layout: fixed}\n</style>\n\n"
266 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
267 #define XT_MAX_UNDO_CLOSE_TAB (32)
270 #define SZ_KB ((uint64_t) 1024)
271 #define SZ_MB (SZ_KB * SZ_KB)
272 #define SZ_GB (SZ_KB * SZ_KB * SZ_KB)
273 #define SZ_TB (SZ_KB * SZ_KB * SZ_KB * SZ_KB)
276 * xxxterm "protocol" (xtp)
277 * We use this for managing stuff like downloads and favorites. They
278 * make magical HTML pages in memory which have xxxt:// links in order
279 * to communicate with xxxterm's internals. These links take the format:
280 * xxxt://class/session_key/action/arg
282 * Don't begin xtp class/actions as 0. atoi returns that on error.
284 * Typically we have not put addition of items in this framework, as
285 * adding items is either done via an ex-command or via a keybinding instead.
288 #define XT_XTP_STR "xxxt://"
290 /* XTP classes (xxxt://<class>) */
291 #define XT_XTP_DL 1 /* downloads */
292 #define XT_XTP_HL 2 /* history */
293 #define XT_XTP_CL 3 /* cookies */
294 #define XT_XTP_FL 4 /* favorites */
296 /* XTP download actions */
297 #define XT_XTP_DL_LIST 1
298 #define XT_XTP_DL_CANCEL 2
299 #define XT_XTP_DL_REMOVE 3
301 /* XTP history actions */
302 #define XT_XTP_HL_LIST 1
303 #define XT_XTP_HL_REMOVE 2
305 /* XTP cookie actions */
306 #define XT_XTP_CL_LIST 1
307 #define XT_XTP_CL_REMOVE 2
309 /* XTP cookie actions */
310 #define XT_XTP_FL_LIST 1
311 #define XT_XTP_FL_REMOVE 2
313 /* xtp tab meanings - identifies which tabs have xtp pages in */
314 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
315 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
316 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
317 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
318 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
321 #define XT_MOVE_INVALID (0)
322 #define XT_MOVE_DOWN (1)
323 #define XT_MOVE_UP (2)
324 #define XT_MOVE_BOTTOM (3)
325 #define XT_MOVE_TOP (4)
326 #define XT_MOVE_PAGEDOWN (5)
327 #define XT_MOVE_PAGEUP (6)
328 #define XT_MOVE_HALFDOWN (7)
329 #define XT_MOVE_HALFUP (8)
330 #define XT_MOVE_LEFT (9)
331 #define XT_MOVE_FARLEFT (10)
332 #define XT_MOVE_RIGHT (11)
333 #define XT_MOVE_FARRIGHT (12)
335 #define XT_TAB_LAST (-4)
336 #define XT_TAB_FIRST (-3)
337 #define XT_TAB_PREV (-2)
338 #define XT_TAB_NEXT (-1)
339 #define XT_TAB_INVALID (0)
340 #define XT_TAB_NEW (1)
341 #define XT_TAB_DELETE (2)
342 #define XT_TAB_DELQUIT (3)
343 #define XT_TAB_OPEN (4)
344 #define XT_TAB_UNDO_CLOSE (5)
346 #define XT_NAV_INVALID (0)
347 #define XT_NAV_BACK (1)
348 #define XT_NAV_FORWARD (2)
349 #define XT_NAV_RELOAD (3)
350 #define XT_NAV_RELOAD_CACHE (4)
352 #define XT_FOCUS_INVALID (0)
353 #define XT_FOCUS_URI (1)
354 #define XT_FOCUS_SEARCH (2)
356 #define XT_SEARCH_INVALID (0)
357 #define XT_SEARCH_NEXT (1)
358 #define XT_SEARCH_PREV (2)
360 #define XT_PASTE_CURRENT_TAB (0)
361 #define XT_PASTE_NEW_TAB (1)
363 #define XT_FONT_SET (0)
365 #define XT_WL_TOGGLE (1<<0)
366 #define XT_WL_ENABLE (1<<1)
367 #define XT_WL_DISABLE (1<<2)
368 #define XT_WL_FQDN (1<<3) /* default */
369 #define XT_WL_TOPLEVEL (1<<4)
371 #define XT_CMD_OPEN (0)
372 #define XT_CMD_OPEN_CURRENT (1)
373 #define XT_CMD_TABNEW (2)
374 #define XT_CMD_TABNEW_CURRENT (3)
381 TAILQ_ENTRY(mime_type
) entry
;
383 TAILQ_HEAD(mime_type_list
, mime_type
);
389 TAILQ_ENTRY(alias
) entry
;
391 TAILQ_HEAD(alias_list
, alias
);
393 /* settings that require restart */
394 int showtabs
= 1; /* show tabs on notebook */
395 int showurl
= 1; /* show url toolbar on notebook */
396 int tabless
= 0; /* allow only 1 tab */
397 int enable_socket
= 0;
398 int single_instance
= 0; /* only allow one xxxterm to run */
399 int fancy_bar
= 1; /* fancy toolbar */
401 /* runtime settings */
402 int ctrl_click_focus
= 0; /* ctrl click gets focus */
403 int cookies_enabled
= 1; /* enable cookies */
404 int read_only_cookies
= 0; /* enable to not write cookies */
405 int enable_scripts
= 0;
406 int enable_plugins
= 0;
407 int default_font_size
= 12;
408 int window_height
= 768;
409 int window_width
= 1024;
410 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
411 unsigned refresh_interval
= 10; /* download refresh interval */
412 int enable_cookie_whitelist
= 1;
413 int enable_js_whitelist
= 1;
414 time_t session_timeout
= 3600; /* cookie session timeout */
415 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
416 char *ssl_ca_file
= NULL
;
417 char *resource_dir
= NULL
;
418 gboolean ssl_strict_certs
= FALSE
;
419 int append_next
= 1; /* append tab after current tab */
421 char *search_string
= NULL
;
422 char *http_proxy
= NULL
;
423 char download_dir
[PATH_MAX
];
424 char runtime_settings
[PATH_MAX
]; /* override of settings */
425 int allow_volatile_cookies
= 0;
426 int save_global_history
= 0; /* save global history to disk */
427 char *user_agent
= NULL
;
428 int save_rejected_cookies
= 0;
431 int set_download_dir(struct settings
*, char *);
432 int set_runtime_dir(struct settings
*, char *);
433 int set_cookie_policy(struct settings
*, char *);
434 int add_alias(struct settings
*, char *);
435 int add_mime_type(struct settings
*, char *);
436 int add_cookie_wl(struct settings
*, char *);
437 int add_js_wl(struct settings
*, char *);
438 void button_set_stockid(GtkWidget
*, char *);
439 GtkWidget
* create_button(char *, char *, int);
441 char *get_cookie_policy(struct settings
*);
443 char *get_download_dir(struct settings
*);
444 char *get_runtime_dir(struct settings
*);
446 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
447 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
448 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
449 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
452 int (*set
)(struct settings
*, char *);
453 char *(*get
)(struct settings
*);
454 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
457 struct special s_cookie
= {
463 struct special s_alias
= {
469 struct special s_mime
= {
475 struct special s_js
= {
481 struct special s_cookie_wl
= {
487 struct special s_download_dir
= {
496 #define XT_S_INVALID (0)
500 #define XT_SF_RESTART (1<<0)
501 #define XT_SF_RUNTIME (1<<1)
506 { "append_next", XT_S_INT
, 0 , &append_next
, NULL
, NULL
},
507 { "allow_volatile_cookies", XT_S_INT
, 0 , &allow_volatile_cookies
, NULL
, NULL
},
508 { "cookies_enabled", XT_S_INT
, 0 , &cookies_enabled
, NULL
, NULL
},
509 { "cookie_policy", XT_S_INT
, 0 , NULL
, NULL
, &s_cookie
},
510 { "ctrl_click_focus", XT_S_INT
, 0 , &ctrl_click_focus
, NULL
, NULL
},
511 { "default_font_size", XT_S_INT
, 0 , &default_font_size
, NULL
, NULL
},
512 { "download_dir", XT_S_STR
, 0 , NULL
, NULL
, &s_download_dir
},
513 { "enable_cookie_whitelist", XT_S_INT
, 0 , &enable_cookie_whitelist
, NULL
, NULL
},
514 { "enable_js_whitelist", XT_S_INT
, 0 , &enable_js_whitelist
, NULL
, NULL
},
515 { "enable_plugins", XT_S_INT
, 0 , &enable_plugins
, NULL
, NULL
},
516 { "enable_scripts", XT_S_INT
, 0 , &enable_scripts
, NULL
, NULL
},
517 { "enable_socket", XT_S_INT
, XT_SF_RESTART
, &enable_socket
, NULL
, NULL
},
518 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
, &fancy_bar
, NULL
, NULL
},
519 { "home", XT_S_STR
, 0 , NULL
, &home
, NULL
},
520 { "http_proxy", XT_S_STR
, 0 , NULL
, &http_proxy
, NULL
},
521 { "icon_size", XT_S_INT
, 0 , &icon_size
, NULL
, NULL
},
522 { "read_only_cookies", XT_S_INT
, 0 , &read_only_cookies
, NULL
, NULL
},
523 { "refresh_interval", XT_S_INT
, 0 , &refresh_interval
, NULL
, NULL
},
524 { "resource_dir", XT_S_STR
, 0 , NULL
, &resource_dir
, NULL
},
525 { "search_string", XT_S_STR
, 0 , NULL
, &search_string
, NULL
},
526 { "session_timeout", XT_S_INT
, 0 , &session_timeout
, NULL
, NULL
},
527 { "save_global_history", XT_S_INT
, XT_SF_RESTART
, &save_global_history
, NULL
, NULL
},
528 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
, &save_rejected_cookies
, NULL
, NULL
},
529 { "single_instance", XT_S_INT
, XT_SF_RESTART
, &single_instance
, NULL
, NULL
},
530 { "ssl_ca_file", XT_S_STR
, 0 , NULL
, &ssl_ca_file
, NULL
},
531 { "ssl_strict_certs", XT_S_INT
, 0 , &ssl_strict_certs
, NULL
, NULL
},
532 { "user_agent", XT_S_STR
, 0 , NULL
, &user_agent
, NULL
},
533 { "window_height", XT_S_INT
, 0 , &window_height
, NULL
, NULL
},
534 { "window_width", XT_S_INT
, 0 , &window_width
, NULL
, NULL
},
536 /* runtime settings */
537 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
538 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
539 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
540 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
544 extern char *__progname
;
547 GtkWidget
*main_window
;
548 GtkNotebook
*notebook
;
549 GtkWidget
*arrow
, *abtn
;
550 struct tab_list tabs
;
551 struct history_list hl
;
552 struct download_list downloads
;
553 struct domain_list c_wl
;
554 struct domain_list js_wl
;
555 struct undo_tailq undos
;
557 int updating_dl_tabs
= 0;
558 int updating_hl_tabs
= 0;
559 int updating_cl_tabs
= 0;
560 int updating_fl_tabs
= 0;
562 uint64_t blocked_cookies
= 0;
563 char named_session
[PATH_MAX
];
564 int notify_icon_loaded_cb(WebKitWebView
*, char *,
566 void update_favicon(struct tab
*);
567 int icon_size_map(int);
570 check_favicon(struct tab
*t
)
572 const gchar
*iconuri
= webkit_web_view_get_icon_uri(t
->wv
);
574 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri '%s'\n",
575 __func__
, t
->tab_id
, iconuri
);
577 if (iconuri
&& strlen(iconuri
) > 0)
578 notify_icon_loaded_cb(t
->wv
, (char *)iconuri
, t
);
587 update_favicon(struct tab
*t
)
589 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri '%s'\n",
590 __func__
, t
->tab_id
, t
->icon_uri
);
592 if (t
->icon_uri
&& strlen(t
->icon_uri
) > 0 && t
->icon_pixbuf
) {
593 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
594 GTK_ENTRY_ICON_PRIMARY
, t
->icon_pixbuf
);
595 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri '%s'"
596 " (set)\n", __func__
, t
->tab_id
,
597 t
->icon_uri
? t
->icon_uri
: "(NULL)");
599 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
600 GTK_ENTRY_ICON_PRIMARY
, "text-html");
601 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri '%s'"
602 " (!set)\n", __func__
, t
->tab_id
,
603 (t
->icon_uri
&& strlen(t
->icon_uri
) > 0) ?
604 t
->icon_uri
: "<empty>");
609 load_webkit_string(struct tab
*t
, const char *str
)
611 /* we set this to indicate we want to manually do navaction */
612 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
613 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
617 hide_oops(struct tab
*t
)
619 gtk_widget_hide(t
->oops
);
623 hide_cmd(struct tab
*t
)
625 gtk_widget_hide(t
->cmd
);
629 show_cmd(struct tab
*t
)
631 gtk_widget_hide(t
->oops
);
632 gtk_widget_show(t
->cmd
);
636 show_oops(struct tab
*t
, const char *fmt
, ...)
645 if (vasprintf(&msg
, fmt
, ap
) == -1)
649 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
650 gtk_widget_hide(t
->cmd
);
651 gtk_widget_show(t
->oops
);
654 get_as_string(struct settings
*s
)
665 warnx("get_as_string skip %s\n", s
->name
);
666 } else if (s
->type
== XT_S_INT
)
667 r
= g_strdup_printf("%d", *s
->ival
);
668 else if (s
->type
== XT_S_STR
)
669 r
= g_strdup(*s
->sval
);
671 r
= g_strdup_printf("INVALID TYPE");
677 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
682 for (i
= 0; i
< LENGTH(rs
); i
++) {
683 if (rs
[i
].s
&& rs
[i
].s
->walk
)
684 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
686 s
= get_as_string(&rs
[i
]);
687 cb(&rs
[i
], s
, cb_args
);
694 set_cookie_policy(struct settings
*s
, char *val
)
696 if (!strcmp(val
, "no3rdparty"))
697 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
698 else if (!strcmp(val
, "accept"))
699 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
700 else if (!strcmp(val
, "reject"))
701 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
709 get_cookie_policy(struct settings
*s
)
713 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
714 r
= g_strdup("no3rdparty");
715 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
716 r
= g_strdup("accept");
717 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
718 r
= g_strdup("reject");
726 get_download_dir(struct settings
*s
)
728 if (download_dir
[0] == '\0')
730 return (g_strdup(download_dir
));
734 set_download_dir(struct settings
*s
, char *val
)
737 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
738 pwd
->pw_dir
, &val
[1]);
740 strlcpy(download_dir
, val
, sizeof download_dir
);
747 * We use these to prevent people putting xxxt:// URLs on
748 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
750 #define XT_XTP_SES_KEY_SZ 8
751 #define XT_XTP_SES_KEY_HEX_FMT \
752 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
753 char *dl_session_key
; /* downloads */
754 char *hl_session_key
; /* history list */
755 char *cl_session_key
; /* cookie list */
756 char *fl_session_key
; /* favorites list */
758 char work_dir
[PATH_MAX
];
759 char certs_dir
[PATH_MAX
];
760 char cache_dir
[PATH_MAX
];
761 char sessions_dir
[PATH_MAX
];
762 char cookie_file
[PATH_MAX
];
763 SoupURI
*proxy_uri
= NULL
;
764 SoupSession
*session
;
765 SoupCookieJar
*s_cookiejar
;
766 SoupCookieJar
*p_cookiejar
;
767 char rc_fname
[PATH_MAX
];
769 struct mime_type_list mtl
;
770 struct alias_list aliases
;
773 void create_new_tab(char *, struct undo
*, int);
774 void delete_tab(struct tab
*);
775 void adjustfont_webkit(struct tab
*, int);
776 int run_script(struct tab
*, char *);
777 int download_rb_cmp(struct download
*, struct download
*);
778 int xtp_page_hl(struct tab
*t
, struct karg
*args
);
779 int xtp_page_dl(struct tab
*t
, struct karg
*args
);
780 int xtp_page_cl(struct tab
*t
, struct karg
*args
);
781 int xtp_page_fl(struct tab
*t
, struct karg
*args
);
784 history_rb_cmp(struct history
*h1
, struct history
*h2
)
786 return (strcmp(h1
->uri
, h2
->uri
));
788 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
791 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
793 return (strcmp(d1
->d
, d2
->d
));
795 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
798 * generate a session key to secure xtp commands.
799 * pass in a ptr to the key in question and it will
800 * be modified in place.
803 generate_xtp_session_key(char **key
)
805 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
812 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
813 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
814 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
815 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
817 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
821 * validate a xtp session key.
825 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
827 if (strcmp(trusted
, untrusted
) != 0) {
828 show_oops(t
, "%s: xtp session key mismatch possible spoof",
837 download_rb_cmp(struct download
*e1
, struct download
*e2
)
839 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
841 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
843 struct valid_url_types
{
854 valid_url_type(char *url
)
858 for (i
= 0; i
< LENGTH(vut
); i
++)
859 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
866 print_cookie(char *msg
, SoupCookie
*c
)
872 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
873 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
874 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
875 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
876 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
877 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
878 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
879 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
880 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
881 DNPRINTF(XT_D_COOKIE
, "====================================\n");
885 walk_alias(struct settings
*s
,
886 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
891 if (s
== NULL
|| cb
== NULL
)
892 errx(1, "walk_alias");
894 TAILQ_FOREACH(a
, &aliases
, entry
) {
895 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
902 match_alias(char *url_in
)
906 char *url_out
= NULL
;
909 if (strsep(&arg
, " \t") == NULL
)
910 errx(1, "match_alias: NULL URL");
912 TAILQ_FOREACH(a
, &aliases
, entry
) {
913 if (!strcmp(url_in
, a
->a_name
))
918 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
921 url_out
= g_strdup_printf(a
->a_uri
, arg
);
923 url_out
= g_strdup(a
->a_uri
);
930 guess_url_type(char *url_in
)
933 char *url_out
= NULL
;
935 url_out
= match_alias(url_in
);
939 /* XXX not sure about this heuristic */
940 if (stat(url_in
, &sb
) == 0)
941 url_out
= g_strdup_printf("file://%s", url_in
);
943 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
945 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
951 add_alias(struct settings
*s
, char *line
)
957 errx(1, "add_alias");
960 a
= g_malloc(sizeof(*a
));
962 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
)
963 errx(1, "add_alias: incomplete alias definition");
965 if (strlen(alias
) == 0 || strlen(l
) == 0)
966 errx(1, "add_alias: invalid alias definition");
968 a
->a_name
= g_strdup(alias
);
969 a
->a_uri
= g_strdup(l
);
971 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
973 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
979 add_mime_type(struct settings
*s
, char *line
)
985 /* XXX this could be smarter */
988 errx(1, "add_mime_type");
991 m
= g_malloc(sizeof(*m
));
993 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
)
994 errx(1, "add_mime_type: invalid mime_type");
996 if (mime_type
[strlen(mime_type
) - 1] == '*') {
997 mime_type
[strlen(mime_type
) - 1] = '\0';
1002 if (strlen(mime_type
) == 0 || strlen(l
) == 0)
1003 errx(1, "add_mime_type: invalid mime_type");
1005 m
->mt_type
= g_strdup(mime_type
);
1006 m
->mt_action
= g_strdup(l
);
1008 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1009 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1011 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1017 find_mime_type(char *mime_type
)
1019 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1021 TAILQ_FOREACH(m
, &mtl
, entry
) {
1022 if (m
->mt_default
&&
1023 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1026 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1039 walk_mime_type(struct settings
*s
,
1040 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1042 struct mime_type
*m
;
1045 if (s
== NULL
|| cb
== NULL
)
1046 errx(1, "walk_mime_type");
1048 TAILQ_FOREACH(m
, &mtl
, entry
) {
1049 str
= g_strdup_printf("%s%s --> %s",
1051 m
->mt_default
? "*" : "",
1053 cb(s
, str
, cb_args
);
1059 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1064 if (str
== NULL
|| wl
== NULL
)
1066 if (strlen(str
) < 2)
1069 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1071 /* treat *.moo.com the same as .moo.com */
1072 if (str
[0] == '*' && str
[1] == '.')
1074 else if (str
[0] == '.')
1079 d
= g_malloc(sizeof *d
);
1081 d
->d
= g_strdup_printf(".%s", str
);
1083 d
->d
= g_strdup(str
);
1086 if (RB_INSERT(domain_list
, wl
, d
))
1089 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1100 add_cookie_wl(struct settings
*s
, char *entry
)
1102 wl_add(entry
, &c_wl
, 1);
1107 walk_cookie_wl(struct settings
*s
,
1108 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1112 if (s
== NULL
|| cb
== NULL
)
1113 errx(1, "walk_cookie_wl");
1115 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1116 cb(s
, d
->d
, cb_args
);
1120 walk_js_wl(struct settings
*s
,
1121 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1125 if (s
== NULL
|| cb
== NULL
)
1126 errx(1, "walk_js_wl");
1128 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1129 cb(s
, d
->d
, cb_args
);
1133 add_js_wl(struct settings
*s
, char *entry
)
1135 wl_add(entry
, &js_wl
, 1 /* persistent */);
1140 wl_find(const gchar
*search
, struct domain_list
*wl
)
1143 struct domain
*d
= NULL
, dfind
;
1146 if (search
== NULL
|| wl
== NULL
)
1148 if (strlen(search
) < 2)
1151 if (search
[0] != '.')
1152 s
= g_strdup_printf(".%s", search
);
1154 s
= g_strdup(search
);
1156 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1159 d
= RB_FIND(domain_list
, wl
, &dfind
);
1173 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1179 if (s
== NULL
|| wl
== NULL
)
1182 if (!strncmp(s
, "http://", strlen("http://")))
1183 s
= &s
[strlen("http://")];
1184 else if (!strncmp(s
, "https://", strlen("https://")))
1185 s
= &s
[strlen("https://")];
1190 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1191 /* chop string at first slash */
1192 if (s
[i
] == '/' || s
[i
] == '\0') {
1195 r
= wl_find(ss
, wl
);
1204 get_toplevel_domain(char *domain
)
1211 if (strlen(domain
) < 2)
1214 s
= &domain
[strlen(domain
) - 1];
1215 while (s
!= domain
) {
1232 config_parse(char *filename
, int runtime
)
1235 char *line
, *cp
, *var
, *val
;
1236 size_t len
, lineno
= 0;
1238 char **s
, file
[PATH_MAX
];
1241 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1243 if (filename
== NULL
)
1246 if (runtime
&& runtime_settings
[0] != '\0') {
1247 snprintf(file
, sizeof file
, "%s/%s",
1248 work_dir
, runtime_settings
);
1249 if (stat(file
, &sb
)) {
1250 warnx("runtime file doesn't exist, creating it");
1251 if ((f
= fopen(file
, "w")) == NULL
)
1253 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1257 strlcpy(file
, filename
, sizeof file
);
1259 if ((config
= fopen(file
, "r")) == NULL
) {
1260 warn("config_parse: cannot open %s", filename
);
1265 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1266 if (feof(config
) || ferror(config
))
1270 cp
+= (long)strspn(cp
, WS
);
1271 if (cp
[0] == '\0') {
1277 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1280 cp
+= (long)strspn(cp
, WS
);
1282 if ((val
= strsep(&cp
, "\0")) == NULL
)
1285 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1288 for (i
= 0, handled
= 0; i
< LENGTH(rs
); i
++) {
1289 if (strcmp(var
, rs
[i
].name
))
1293 if (rs
[i
].s
->set(&rs
[i
], val
))
1294 errx(1, "invalid value for %s", var
);
1298 switch (rs
[i
].type
) {
1307 errx(1, "invalid sval for %s",
1316 errx(1, "invalid type for %s", var
);
1322 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1331 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1337 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1341 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1344 JSStringGetUTF8CString(jsref
, s
, l
);
1345 JSStringRelease(jsref
);
1351 disable_hints(struct tab
*t
)
1353 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1354 bzero(t
->hint_num
, sizeof t
->hint_num
);
1355 run_script(t
, "vimprobable_clear()");
1357 t
->hint_mode
= XT_HINT_NONE
;
1361 enable_hints(struct tab
*t
)
1363 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1364 run_script(t
, "vimprobable_show_hints()");
1366 t
->hint_mode
= XT_HINT_NONE
;
1369 #define XT_JS_OPEN ("open;")
1370 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1371 #define XT_JS_FIRE ("fire;")
1372 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1373 #define XT_JS_FOUND ("found;")
1374 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1377 run_script(struct tab
*t
, char *s
)
1379 JSGlobalContextRef ctx
;
1380 WebKitWebFrame
*frame
;
1382 JSValueRef val
, exception
;
1385 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1386 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1388 frame
= webkit_web_view_get_main_frame(t
->wv
);
1389 ctx
= webkit_web_frame_get_global_context(frame
);
1391 str
= JSStringCreateWithUTF8CString(s
);
1392 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1393 NULL
, 0, &exception
);
1394 JSStringRelease(str
);
1396 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1398 es
= js_ref_to_string(ctx
, exception
);
1399 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1403 es
= js_ref_to_string(ctx
, val
);
1404 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1406 /* handle return value right here */
1407 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1409 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1412 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1413 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1414 &es
[XT_JS_FIRE_LEN
]);
1419 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1420 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1431 hint(struct tab
*t
, struct karg
*args
)
1434 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1436 if (t
->hints_on
== 0)
1444 /* Doesn't work fully, due to the following bug:
1445 * https://bugs.webkit.org/show_bug.cgi?id=51747
1448 restore_global_history(void)
1450 char file
[PATH_MAX
];
1456 snprintf(file
, sizeof file
, "%s/%s/%s",
1457 pwd
->pw_dir
, XT_DIR
, XT_HISTORY_FILE
);
1459 if ((f
= fopen(file
, "r")) == NULL
) {
1460 warnx("%s: fopen", __func__
);
1465 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1466 if (feof(f
) || ferror(f
))
1469 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1470 if (feof(f
) || ferror(f
)) {
1472 warnx("%s: broken history file\n", __func__
);
1476 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1477 webkit_web_history_item_new_with_data(uri
, title
);
1478 h
= g_malloc(sizeof(struct history
));
1479 h
->uri
= g_strdup(uri
);
1480 h
->title
= g_strdup(title
);
1481 RB_INSERT(history_list
, &hl
, h
);
1483 warnx("%s: failed to restore history\n", __func__
);
1499 save_global_history_to_disk(struct tab
*t
)
1501 char file
[PATH_MAX
];
1505 snprintf(file
, sizeof file
, "%s/%s/%s",
1506 pwd
->pw_dir
, XT_DIR
, XT_HISTORY_FILE
);
1508 if ((f
= fopen(file
, "w")) == NULL
) {
1509 show_oops(t
, "%s: global history file: %s",
1510 __func__
, strerror(errno
));
1514 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1515 if (h
->uri
&& h
->title
)
1516 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
1525 quit(struct tab
*t
, struct karg
*args
)
1527 if (save_global_history
)
1528 save_global_history_to_disk(t
);
1536 open_tabs(struct tab
*t
, struct karg
*a
)
1538 char file
[PATH_MAX
];
1546 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1548 if ((f
= fopen(file
, "r")) == NULL
)
1552 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1553 if (feof(f
) || ferror(f
))
1556 if (uri
&& strlen(uri
))
1557 create_new_tab(uri
, NULL
, 1);
1572 restore_saved_tabs(void)
1574 char file
[PATH_MAX
];
1575 int unlink_file
= 0;
1579 snprintf(file
, sizeof file
, "%s/%s",
1580 sessions_dir
, XT_RESTART_TABS_FILE
);
1581 if (stat(file
, &sb
) == -1)
1582 a
.s
= XT_SAVED_TABS_FILE
;
1585 a
.s
= XT_RESTART_TABS_FILE
;
1588 open_tabs(NULL
, &a
);
1597 save_tabs(struct tab
*t
, struct karg
*a
)
1599 char file
[PATH_MAX
];
1602 WebKitWebFrame
*frame
;
1610 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1612 if ((f
= fopen(file
, "w")) == NULL
) {
1613 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
1617 TAILQ_FOREACH(ti
, &tabs
, entry
) {
1618 frame
= webkit_web_view_get_main_frame(ti
->wv
);
1619 uri
= webkit_web_frame_get_uri(frame
);
1620 if (uri
&& strlen(uri
) > 0)
1621 fprintf(f
, "%s\n", uri
);
1630 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
1634 a
.s
= XT_SAVED_TABS_FILE
;
1642 yank_uri(struct tab
*t
, struct karg
*args
)
1644 WebKitWebFrame
*frame
;
1646 GtkClipboard
*clipboard
;
1648 frame
= webkit_web_view_get_main_frame(t
->wv
);
1649 uri
= webkit_web_frame_get_uri(frame
);
1653 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1654 gtk_clipboard_set_text(clipboard
, uri
, -1);
1665 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
1667 struct paste_args
*pap
;
1672 pap
= (struct paste_args
*)data
;
1675 case XT_PASTE_CURRENT_TAB
:
1676 webkit_web_view_load_uri(pap
->t
->wv
, text
);
1678 case XT_PASTE_NEW_TAB
:
1679 create_new_tab((char *)text
, NULL
, 1);
1687 paste_uri(struct tab
*t
, struct karg
*args
)
1689 GtkClipboard
*clipboard
;
1690 struct paste_args
*pap
;
1692 pap
= g_malloc(sizeof(struct paste_args
));
1697 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1698 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
1704 find_domain(const char *s
, int add_dot
)
1707 char *r
= NULL
, *ss
= NULL
;
1712 if (!strncmp(s
, "http://", strlen("http://")))
1713 s
= &s
[strlen("http://")];
1714 else if (!strncmp(s
, "https://", strlen("https://")))
1715 s
= &s
[strlen("https://")];
1721 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
1722 /* chop string at first slash */
1723 if (ss
[i
] == '/' || ss
[i
] == '\0') {
1726 r
= g_strdup_printf(".%s", ss
);
1737 toggle_cwl(struct tab
*t
, struct karg
*args
)
1739 WebKitWebFrame
*frame
;
1742 char *dom
= NULL
, *dom_toggle
= NULL
;
1748 frame
= webkit_web_view_get_main_frame(t
->wv
);
1749 uri
= (char *)webkit_web_frame_get_uri(frame
);
1750 dom
= find_domain(uri
, 1);
1751 d
= wl_find(dom
, &c_wl
);
1757 if (args
->i
& XT_WL_TOGGLE
)
1759 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
1761 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
1764 if (args
->i
& XT_WL_TOPLEVEL
)
1765 dom_toggle
= get_toplevel_domain(dom
);
1770 /* enable cookies for domain */
1771 wl_add(dom_toggle
, &c_wl
, 0);
1773 /* disable cookies for domain */
1774 RB_REMOVE(domain_list
, &c_wl
, d
);
1777 webkit_web_view_reload(t
->wv
);
1784 toggle_js(struct tab
*t
, struct karg
*args
)
1787 WebKitWebFrame
*frame
;
1790 char *dom
= NULL
, *dom_toggle
= NULL
;
1795 g_object_get((GObject
*)t
->settings
,
1796 "enable-scripts", &es
, (char *)NULL
);
1797 if (args
->i
& XT_WL_TOGGLE
)
1799 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
1801 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
1806 frame
= webkit_web_view_get_main_frame(t
->wv
);
1807 uri
= (char *)webkit_web_frame_get_uri(frame
);
1808 dom
= find_domain(uri
, 1);
1809 if (uri
== NULL
|| dom
== NULL
) {
1810 show_oops(t
, "Can't toggle domain in JavaScript white list");
1814 if (args
->i
& XT_WL_TOPLEVEL
)
1815 dom_toggle
= get_toplevel_domain(dom
);
1820 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
1821 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
1823 d
= wl_find(dom_toggle
, &js_wl
);
1825 RB_REMOVE(domain_list
, &js_wl
, d
);
1826 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
1828 g_object_set((GObject
*)t
->settings
,
1829 "enable-scripts", es
, (char *)NULL
);
1830 webkit_web_view_set_settings(t
->wv
, t
->settings
);
1831 webkit_web_view_reload(t
->wv
);
1839 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
1843 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
1848 toggle_src(struct tab
*t
, struct karg
*args
)
1855 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
1856 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
1857 webkit_web_view_reload(t
->wv
);
1863 focus(struct tab
*t
, struct karg
*args
)
1865 if (t
== NULL
|| args
== NULL
)
1868 if (args
->i
== XT_FOCUS_URI
)
1869 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
1870 else if (args
->i
== XT_FOCUS_SEARCH
)
1871 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
1877 stats(struct tab
*t
, struct karg
*args
)
1879 char *stats
, *s
, line
[64 * 1024];
1880 uint64_t line_count
= 0;
1887 if (save_rejected_cookies
) {
1888 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
1890 s
= fgets(line
, sizeof line
, r_cookie_f
);
1891 if (s
== NULL
|| feof(r_cookie_f
) ||
1897 snprintf(line
, sizeof line
,
1898 "<br>Cookies blocked(*) total: %llu", line_count
);
1900 show_oops(t
, "Can't open blocked cookies file: %s",
1904 stats
= g_strdup_printf(XT_DOCTYPE
1907 "<title>Statistics</title>"
1909 "<h1>Statistics</h1>"
1911 "Cookies blocked(*) this session: %llu"
1913 "<p><small><b>*</b> results vary based on settings"
1919 load_webkit_string(t
, stats
);
1926 about(struct tab
*t
, struct karg
*args
)
1933 about
= g_strdup_printf(XT_DOCTYPE
1936 "<title>About</title>"
1940 "<b>Version: %s</b><p>"
1943 "<li>Marco Peereboom <marco@peereboom.us></li>"
1944 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
1945 "<li>Edd Barrett <vext01@gmail.com> </li>"
1947 "Copyrights and licenses can be found on the XXXterm "
1948 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
1954 load_webkit_string(t
, about
);
1961 help(struct tab
*t
, struct karg
*args
)
1971 "<title>XXXterm</title>"
1972 "<meta http-equiv=\"REFRESH\" content=\"0;"
1973 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
1976 "XXXterm man page <a href=\"http://opensource.conformal.com/"
1977 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
1978 "cgi-bin/man-cgi?xxxterm</a>"
1983 load_webkit_string(t
, help
);
1989 * update all favorite tabs apart from one. Pass NULL if
1990 * you want to update all.
1993 update_favorite_tabs(struct tab
*apart_from
)
1996 if (!updating_fl_tabs
) {
1997 updating_fl_tabs
= 1; /* stop infinite recursion */
1998 TAILQ_FOREACH(t
, &tabs
, entry
)
1999 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
2000 && (t
!= apart_from
))
2001 xtp_page_fl(t
, NULL
);
2002 updating_fl_tabs
= 0;
2006 /* show a list of favorites (bookmarks) */
2008 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2010 char file
[PATH_MAX
];
2012 char *uri
= NULL
, *title
= NULL
;
2013 size_t len
, lineno
= 0;
2015 char *header
, *body
, *tmp
, *html
= NULL
;
2017 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2020 warn("%s: bad param", __func__
);
2022 /* mark tab as favorite list */
2023 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2025 /* new session key */
2026 if (!updating_fl_tabs
)
2027 generate_xtp_session_key(&fl_session_key
);
2029 /* open favorites */
2030 snprintf(file
, sizeof file
, "%s/%s/%s",
2031 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
2032 if ((f
= fopen(file
, "r")) == NULL
) {
2033 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2038 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2039 "<title>Favorites</title>\n"
2042 "<h1>Favorites</h1>\n",
2046 body
= g_strdup_printf("<div align='center'><table><tr>"
2047 "<th style='width: 4%%'>#</th><th>Link</th>"
2048 "<th style='width: 15%%'>Remove</th></tr>\n");
2051 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2052 if (feof(f
) || ferror(f
))
2060 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2061 if (feof(f
) || ferror(f
)) {
2062 errx(0, "%s: can't parse favorites\n",
2069 body
= g_strdup_printf("%s<tr>"
2071 "<td><a href='%s'>%s</a></td>"
2072 "<td style='text-align: center'>"
2073 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2075 body
, i
, uri
, title
,
2076 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2088 /* if none, say so */
2091 body
= g_strdup_printf("%s<tr>"
2092 "<td colspan='3' style='text-align: center'>"
2093 "No favorites - To add one use the 'favadd' command."
2094 "</td></tr>", body
);
2105 html
= g_strdup_printf("%s%s</table></div></html>",
2107 load_webkit_string(t
, html
);
2110 update_favorite_tabs(t
);
2123 getparams(char *cmd
, char *cmp
)
2128 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2129 rv
= cmd
+ strlen(cmp
);
2132 if (strlen(rv
) == 0)
2141 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2142 size_t cert_count
, char *title
)
2144 gnutls_datum_t cinfo
;
2145 char *tmp
, *header
, *body
, *footer
;
2148 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2149 footer
= g_strdup("</body></html>");
2150 body
= g_strdup("");
2152 for (i
= 0; i
< cert_count
; i
++) {
2153 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2158 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2159 body
, i
, cinfo
.data
);
2160 gnutls_free(cinfo
.data
);
2164 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2168 load_webkit_string(t
, tmp
);
2173 ca_cmd(struct tab
*t
, struct karg
*args
)
2176 int rv
= 1, certs
= 0, certs_read
;
2179 gnutls_x509_crt_t
*c
= NULL
;
2180 char *certs_buf
= NULL
, *s
;
2182 /* yeah yeah stat race */
2183 if (stat(ssl_ca_file
, &sb
)) {
2184 show_oops(t
, "no CA file: %s", ssl_ca_file
);
2188 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2189 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2193 certs_buf
= g_malloc(sb
.st_size
+ 1);
2194 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2195 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2198 certs_buf
[sb
.st_size
] = '\0';
2201 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2203 s
+= strlen("BEGIN CERTIFICATE");
2206 bzero(&dt
, sizeof dt
);
2207 dt
.data
= certs_buf
;
2208 dt
.size
= sb
.st_size
;
2209 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2210 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
, GNUTLS_X509_FMT_PEM
, 0);
2211 if (certs_read
<= 0) {
2212 show_oops(t
, "No cert(s) available");
2215 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2228 connect_socket_from_uri(char *uri
, char *domain
, size_t domain_sz
)
2231 struct addrinfo hints
, *res
= NULL
, *ai
;
2235 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2238 su
= soup_uri_new(uri
);
2241 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2244 snprintf(port
, sizeof port
, "%d", su
->port
);
2245 bzero(&hints
, sizeof(struct addrinfo
));
2246 hints
.ai_flags
= AI_CANONNAME
;
2247 hints
.ai_family
= AF_UNSPEC
;
2248 hints
.ai_socktype
= SOCK_STREAM
;
2250 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2253 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2254 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2257 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2260 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2264 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2269 strlcpy(domain
, su
->host
, domain_sz
);
2280 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2283 gnutls_deinit(gsession
);
2285 gnutls_certificate_free_credentials(xcred
);
2291 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2292 gnutls_certificate_credentials_t
*xc
)
2294 gnutls_certificate_credentials_t xcred
;
2295 gnutls_session_t gsession
;
2298 if (gs
== NULL
|| xc
== NULL
)
2304 gnutls_certificate_allocate_credentials(&xcred
);
2305 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2306 GNUTLS_X509_FMT_PEM
);
2307 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2308 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2309 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2310 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2311 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2312 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2314 gnutls_error_is_fatal(rv
),
2315 gnutls_strerror_name(rv
));
2316 stop_tls(gsession
, xcred
);
2320 gnutls_credentials_type_t cred
;
2321 cred
= gnutls_auth_get_type(gsession
);
2322 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2323 stop_tls(gsession
, xcred
);
2335 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2339 const gnutls_datum_t
*cl
;
2340 gnutls_x509_crt_t
*all_certs
;
2343 if (certs
== NULL
|| cert_count
== NULL
)
2345 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2347 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2351 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2352 for (i
= 0; i
< len
; i
++) {
2353 gnutls_x509_crt_init(&all_certs
[i
]);
2354 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2355 GNUTLS_X509_FMT_PEM
< 0)) {
2369 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2373 for (i
= 0; i
< cert_count
; i
++)
2374 gnutls_x509_crt_deinit(certs
[i
]);
2379 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2380 size_t cert_count
, char *domain
)
2383 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2388 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2391 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2392 if ((f
= fopen(file
, "w")) == NULL
) {
2393 show_oops(t
, "Can't create cert file %s %s",
2394 file
, strerror(errno
));
2398 for (i
= 0; i
< cert_count
; i
++) {
2399 cert_buf_sz
= sizeof cert_buf
;
2400 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2401 cert_buf
, &cert_buf_sz
)) {
2402 show_oops(t
, "gnutls_x509_crt_export failed");
2405 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2406 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2411 /* not the best spot but oh well */
2412 gdk_color_parse("lightblue", &color
);
2413 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2419 load_compare_cert(struct tab
*t
, struct karg
*args
)
2421 WebKitWebFrame
*frame
;
2422 char *uri
, domain
[8182], file
[PATH_MAX
];
2423 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2424 int s
= -1, rv
= 1, i
;
2428 gnutls_session_t gsession
;
2429 gnutls_x509_crt_t
*certs
;
2430 gnutls_certificate_credentials_t xcred
;
2435 frame
= webkit_web_view_get_main_frame(t
->wv
);
2436 uri
= (char *)webkit_web_frame_get_uri(frame
);
2437 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2441 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2442 show_oops(t
, "Start TLS failed");
2447 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2448 show_oops(t
, "Can't get connection certificates");
2452 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2453 if ((f
= fopen(file
, "r")) == NULL
)
2456 for (i
= 0; i
< cert_count
; i
++) {
2457 cert_buf_sz
= sizeof cert_buf
;
2458 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2459 cert_buf
, &cert_buf_sz
)) {
2462 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2463 rv
= -1; /* critical */
2466 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
2467 rv
= -1; /* critical */
2476 free_connection_certs(certs
, cert_count
);
2478 /* we close the socket first for speed */
2481 stop_tls(gsession
, xcred
);
2487 cert_cmd(struct tab
*t
, struct karg
*args
)
2489 WebKitWebFrame
*frame
;
2490 char *uri
, *action
, domain
[8182];
2493 gnutls_session_t gsession
;
2494 gnutls_x509_crt_t
*certs
;
2495 gnutls_certificate_credentials_t xcred
;
2500 if ((action
= getparams(args
->s
, "cert")))
2505 frame
= webkit_web_view_get_main_frame(t
->wv
);
2506 uri
= (char *)webkit_web_frame_get_uri(frame
);
2507 if (uri
&& strlen(uri
) == 0) {
2508 show_oops(t
, "Invalid URI");
2510 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
2511 show_oops(t
, "Invalid certidicate URI: %s", uri
);
2516 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2517 show_oops(t
, "Start TLS failed");
2522 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2523 show_oops(t
, "get_connection_certs failed");
2527 if (!strcmp(action
, "show"))
2528 show_certs(t
, certs
, cert_count
, "Certificate Chain");
2529 else if (!strcmp(action
, "save"))
2530 save_certs(t
, certs
, cert_count
, domain
);
2532 show_oops(t
, "Invalid command: %s", action
);
2534 free_connection_certs(certs
, cert_count
);
2536 /* we close the socket first for speed */
2539 stop_tls(gsession
, xcred
);
2545 remove_cookie(int index
)
2551 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
2553 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2555 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
2559 print_cookie("remove cookie", c
);
2560 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
2565 soup_cookies_free(cf
);
2571 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
2574 char *tmp
, *header
, *body
, *footer
;
2575 int p_js
= 0, s_js
= 0;
2577 /* we set this to indicate we want to manually do navaction */
2578 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2580 if (g_str_has_prefix(args
, "show a") ||
2581 !strcmp(args
, "show")) {
2585 } else if (g_str_has_prefix(args
, "show p")) {
2586 /* show persistent */
2588 } else if (g_str_has_prefix(args
, "show s")) {
2594 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
2596 footer
= g_strdup("</body></html>");
2597 body
= g_strdup("");
2602 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
2604 RB_FOREACH(d
, domain_list
, wl
) {
2608 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
2616 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
2618 RB_FOREACH(d
, domain_list
, wl
) {
2622 body
= g_strdup_printf("%s%s", body
, d
->d
);
2627 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2631 load_webkit_string(t
, tmp
);
2637 wl_save(struct tab
*t
, struct karg
*args
, int js
)
2639 char file
[PATH_MAX
];
2641 char *line
= NULL
, *lt
= NULL
;
2643 WebKitWebFrame
*frame
;
2644 char *dom
= NULL
, *uri
, *dom_save
= NULL
;
2651 if (t
== NULL
|| args
== NULL
)
2654 if (runtime_settings
[0] == '\0')
2657 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
2658 if ((f
= fopen(file
, "r+")) == NULL
)
2661 frame
= webkit_web_view_get_main_frame(t
->wv
);
2662 uri
= (char *)webkit_web_frame_get_uri(frame
);
2663 dom
= find_domain(uri
, 1);
2664 if (uri
== NULL
|| dom
== NULL
) {
2665 show_oops(t
, "Can't add domain to %s white list",
2666 js
? "JavaScript" : "cookie");
2670 if (g_str_has_prefix(args
->s
, "save d")) {
2672 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
2673 show_oops(t
, "invalid domain: %s", dom
);
2676 flags
= XT_WL_TOPLEVEL
;
2677 } else if (g_str_has_prefix(args
->s
, "save f") ||
2678 !strcmp(args
->s
, "save")) {
2683 show_oops(t
, "invalid command: %s", args
->s
);
2687 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
2690 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
2693 if (!strcmp(line
, lt
))
2699 fprintf(f
, "%s\n", lt
);
2704 d
= wl_find(dom_save
, &js_wl
);
2707 d
= wl_find(dom_save
, &c_wl
);
2710 /* find and add to persistent jar */
2711 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2712 for (;cf
; cf
= cf
->next
) {
2714 if (!strcmp(dom_save
, ci
->domain
) ||
2715 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
2716 c
= soup_cookie_copy(ci
);
2717 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
2720 soup_cookies_free(cf
);
2738 cookie_cmd(struct tab
*t
, struct karg
*args
)
2743 if ((cmd
= getparams(args
->s
, "cookie")))
2749 if (g_str_has_prefix(cmd
, "show")) {
2750 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
2751 } else if (g_str_has_prefix(cmd
, "save")) {
2754 } else if (g_str_has_prefix(cmd
, "toggle")) {
2756 if (g_str_has_prefix(cmd
, "toggle d"))
2757 a
.i
|= XT_WL_TOPLEVEL
;
2761 } else if (g_str_has_prefix(cmd
, "delete")) {
2762 show_oops(t
, "'cookie delete' currently unimplemented");
2764 show_oops(t
, "unknown cookie command: %s", cmd
);
2770 js_cmd(struct tab
*t
, struct karg
*args
)
2775 if ((cmd
= getparams(args
->s
, "js")))
2780 if (g_str_has_prefix(cmd
, "show")) {
2781 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
2782 } else if (g_str_has_prefix(cmd
, "save")) {
2785 } else if (g_str_has_prefix(cmd
, "toggle")) {
2787 if (g_str_has_prefix(cmd
, "toggle d"))
2788 a
.i
|= XT_WL_TOPLEVEL
;
2792 } else if (g_str_has_prefix(cmd
, "delete")) {
2793 show_oops(t
, "'js delete' currently unimplemented");
2795 show_oops(t
, "unknown js command: %s", cmd
);
2801 add_favorite(struct tab
*t
, struct karg
*args
)
2803 char file
[PATH_MAX
];
2806 size_t urilen
, linelen
;
2807 WebKitWebFrame
*frame
;
2808 const gchar
*uri
, *title
;
2813 /* don't allow adding of xtp pages to favorites */
2814 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
2815 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
2819 snprintf(file
, sizeof file
, "%s/%s/%s",
2820 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
2821 if ((f
= fopen(file
, "r+")) == NULL
) {
2822 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2826 title
= webkit_web_view_get_title(t
->wv
);
2827 frame
= webkit_web_view_get_main_frame(t
->wv
);
2828 uri
= webkit_web_frame_get_uri(frame
);
2832 if (title
== NULL
|| uri
== NULL
) {
2833 show_oops(t
, "can't add page to favorites");
2837 urilen
= strlen(uri
);
2840 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
2841 if (linelen
== urilen
&& !strcmp(line
, uri
))
2847 fprintf(f
, "\n%s\n%s", title
, uri
);
2853 update_favorite_tabs(NULL
);
2859 navaction(struct tab
*t
, struct karg
*args
)
2861 WebKitWebHistoryItem
*item
;
2863 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
2864 t
->tab_id
, args
->i
);
2867 if (args
->i
== XT_NAV_BACK
)
2868 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2870 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
2872 return (XT_CB_PASSTHROUGH
);;
2873 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
2875 return (XT_CB_PASSTHROUGH
);
2880 webkit_web_view_go_back(t
->wv
);
2882 case XT_NAV_FORWARD
:
2883 webkit_web_view_go_forward(t
->wv
);
2886 webkit_web_view_reload(t
->wv
);
2888 case XT_NAV_RELOAD_CACHE
:
2889 webkit_web_view_reload_bypass_cache(t
->wv
);
2892 return (XT_CB_PASSTHROUGH
);
2896 move(struct tab
*t
, struct karg
*args
)
2898 GtkAdjustment
*adjust
;
2899 double pi
, si
, pos
, ps
, upper
, lower
, max
;
2904 case XT_MOVE_BOTTOM
:
2906 case XT_MOVE_PAGEDOWN
:
2907 case XT_MOVE_PAGEUP
:
2908 case XT_MOVE_HALFDOWN
:
2909 case XT_MOVE_HALFUP
:
2910 adjust
= t
->adjust_v
;
2913 adjust
= t
->adjust_h
;
2917 pos
= gtk_adjustment_get_value(adjust
);
2918 ps
= gtk_adjustment_get_page_size(adjust
);
2919 upper
= gtk_adjustment_get_upper(adjust
);
2920 lower
= gtk_adjustment_get_lower(adjust
);
2921 si
= gtk_adjustment_get_step_increment(adjust
);
2922 pi
= gtk_adjustment_get_page_increment(adjust
);
2925 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
2926 "max %f si %f pi %f\n",
2927 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
2928 pos
, ps
, upper
, lower
, max
, si
, pi
);
2934 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2939 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2941 case XT_MOVE_BOTTOM
:
2942 case XT_MOVE_FARRIGHT
:
2943 gtk_adjustment_set_value(adjust
, max
);
2946 case XT_MOVE_FARLEFT
:
2947 gtk_adjustment_set_value(adjust
, lower
);
2949 case XT_MOVE_PAGEDOWN
:
2951 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2953 case XT_MOVE_PAGEUP
:
2955 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2957 case XT_MOVE_HALFDOWN
:
2959 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2961 case XT_MOVE_HALFUP
:
2963 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2966 return (XT_CB_PASSTHROUGH
);
2969 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
2971 return (XT_CB_HANDLED
);
2975 tabaction(struct tab
*t
, struct karg
*args
)
2977 int rv
= XT_CB_HANDLED
;
2978 char *url
= NULL
, *newuri
= NULL
;
2981 DNPRINTF(XT_D_TAB
, "tabaction: %p %d %d\n", t
, args
->i
, t
->focus_wv
);
2984 return (XT_CB_PASSTHROUGH
);
2988 if ((url
= getparams(args
->s
, "tabnew")))
2989 create_new_tab(url
, NULL
, 1);
2991 create_new_tab(NULL
, NULL
, 1);
2996 case XT_TAB_DELQUIT
:
2997 if (gtk_notebook_get_n_pages(notebook
) > 1)
3003 if ((url
= getparams(args
->s
, "open")) ||
3004 ((url
= getparams(args
->s
, "op"))) ||
3005 ((url
= getparams(args
->s
, "o"))))
3008 rv
= XT_CB_PASSTHROUGH
;
3012 if (valid_url_type(url
)) {
3013 newuri
= guess_url_type(url
);
3016 webkit_web_view_load_uri(t
->wv
, url
);
3020 case XT_TAB_UNDO_CLOSE
:
3021 if (undo_count
== 0) {
3022 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3026 u
= TAILQ_FIRST(&undos
);
3027 create_new_tab(u
->uri
, u
, 1);
3029 TAILQ_REMOVE(&undos
, u
, entry
);
3031 /* u->history is freed in create_new_tab() */
3036 rv
= XT_CB_PASSTHROUGH
;
3050 resizetab(struct tab
*t
, struct karg
*args
)
3052 if (t
== NULL
|| args
== NULL
)
3053 errx(1, "resizetab");
3055 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3056 t
->tab_id
, args
->i
);
3058 adjustfont_webkit(t
, args
->i
);
3060 return (XT_CB_HANDLED
);
3064 movetab(struct tab
*t
, struct karg
*args
)
3069 if (t
== NULL
|| args
== NULL
)
3072 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3073 t
->tab_id
, args
->i
);
3075 if (args
->i
== XT_TAB_INVALID
)
3076 return (XT_CB_PASSTHROUGH
);
3078 if (args
->i
< XT_TAB_INVALID
) {
3079 /* next or previous tab */
3080 if (TAILQ_EMPTY(&tabs
))
3081 return (XT_CB_PASSTHROUGH
);
3085 /* if at the last page, loop around to the first */
3086 if (gtk_notebook_get_current_page(notebook
) ==
3087 gtk_notebook_get_n_pages(notebook
) - 1) {
3088 gtk_notebook_set_current_page(notebook
, 0);
3090 gtk_notebook_next_page(notebook
);
3094 /* if at the first page, loop around to the last */
3095 if (gtk_notebook_current_page(notebook
) == 0) {
3096 gtk_notebook_set_current_page(notebook
,
3097 gtk_notebook_get_n_pages(notebook
) - 1);
3099 gtk_notebook_prev_page(notebook
);
3103 gtk_notebook_set_current_page(notebook
, 0);
3106 gtk_notebook_set_current_page(notebook
, -1);
3109 return (XT_CB_PASSTHROUGH
);
3112 return (XT_CB_HANDLED
);
3117 if (t
->tab_id
== x
) {
3118 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3119 return (XT_CB_HANDLED
);
3122 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3123 if (tt
->tab_id
== x
) {
3124 gtk_notebook_set_current_page(notebook
, x
);
3125 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3127 gtk_widget_grab_focus(GTK_WIDGET(tt
->wv
));
3131 return (XT_CB_HANDLED
);
3135 command(struct tab
*t
, struct karg
*args
)
3137 WebKitWebFrame
*frame
;
3138 char *s
= NULL
, *ss
= NULL
;
3142 if (t
== NULL
|| args
== NULL
)
3161 case XT_CMD_OPEN_CURRENT
:
3164 case XT_CMD_TABNEW_CURRENT
:
3165 if (!s
) /* FALL THROUGH? */
3167 frame
= webkit_web_view_get_main_frame(t
->wv
);
3168 uri
= webkit_web_frame_get_uri(frame
);
3169 if (uri
&& strlen(uri
)) {
3170 ss
= g_strdup_printf("%s%s", s
, uri
);
3175 show_oops(t
, "command: invalid opcode %d", args
->i
);
3176 return (XT_CB_PASSTHROUGH
);
3179 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3181 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3182 gdk_color_parse("white", &color
);
3183 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3185 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3186 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3191 return (XT_CB_HANDLED
);
3195 * Return a new string with a download row (in html)
3196 * appended. Old string is freed.
3199 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3202 WebKitDownloadStatus stat
;
3203 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3205 char cur_sz
[FMT_SCALED_STRSIZE
];
3206 char tot_sz
[FMT_SCALED_STRSIZE
];
3209 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3211 /* All actions wil take this form:
3212 * xxxt://class/seskey
3214 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3215 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3217 stat
= webkit_download_get_status(dl
->download
);
3220 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3221 status_html
= g_strdup_printf("Finished");
3222 cmd_html
= g_strdup_printf(
3223 "<a href='%s%d/%d'>Remove</a>",
3224 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3226 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3227 /* gather size info */
3228 progress
= 100 * webkit_download_get_progress(dl
->download
);
3231 webkit_download_get_current_size(dl
->download
), cur_sz
);
3233 webkit_download_get_total_size(dl
->download
), tot_sz
);
3235 status_html
= g_strdup_printf("%s of %s (%.2f%%)", cur_sz
,
3237 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3238 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3242 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3243 status_html
= g_strdup_printf("Cancelled");
3244 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3245 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3247 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3248 status_html
= g_strdup_printf("Error!");
3249 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3250 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3252 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3253 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3254 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3255 status_html
= g_strdup_printf("Starting");
3258 show_oops(t
, "%s: unknown download status", __func__
);
3261 new_html
= g_strdup_printf(
3262 "%s\n<tr><td>%s</td><td>%s</td>"
3263 "<td style='text-align:center'>%s</td></tr>\n",
3264 html
, webkit_download_get_uri(dl
->download
),
3265 status_html
, cmd_html
);
3269 g_free(status_html
);
3280 * update all download tabs apart from one. Pass NULL if
3281 * you want to update all.
3284 update_download_tabs(struct tab
*apart_from
)
3287 if (!updating_dl_tabs
) {
3288 updating_dl_tabs
= 1; /* stop infinite recursion */
3289 TAILQ_FOREACH(t
, &tabs
, entry
)
3290 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3291 && (t
!= apart_from
))
3292 xtp_page_dl(t
, NULL
);
3293 updating_dl_tabs
= 0;
3298 * update all cookie tabs apart from one. Pass NULL if
3299 * you want to update all.
3302 update_cookie_tabs(struct tab
*apart_from
)
3305 if (!updating_cl_tabs
) {
3306 updating_cl_tabs
= 1; /* stop infinite recursion */
3307 TAILQ_FOREACH(t
, &tabs
, entry
)
3308 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3309 && (t
!= apart_from
))
3310 xtp_page_cl(t
, NULL
);
3311 updating_cl_tabs
= 0;
3316 * update all history tabs apart from one. Pass NULL if
3317 * you want to update all.
3320 update_history_tabs(struct tab
*apart_from
)
3324 if (!updating_hl_tabs
) {
3325 updating_hl_tabs
= 1; /* stop infinite recursion */
3326 TAILQ_FOREACH(t
, &tabs
, entry
)
3327 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
3328 && (t
!= apart_from
))
3329 xtp_page_hl(t
, NULL
);
3330 updating_hl_tabs
= 0;
3334 /* cookie management XTP page */
3336 xtp_page_cl(struct tab
*t
, struct karg
*args
)
3338 char *header
, *body
, *footer
, *page
, *tmp
;
3339 int i
= 1; /* all ids start 1 */
3340 GSList
*sc
, *pc
, *pc_start
;
3344 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3347 errx(1, "%s: null tab", __func__
);
3349 /* mark this tab as cookie jar */
3350 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
3352 /* Generate a new session key */
3353 if (!updating_cl_tabs
)
3354 generate_xtp_session_key(&cl_session_key
);
3357 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3358 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
3359 "</head><body><h1>Cookie Jar</h1>\n");
3362 body
= g_strdup_printf("<div align='center'><table><tr>"
3370 "<th>HTTP_only</th>"
3371 "<th>Remove</th></tr>\n");
3373 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
3374 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
3377 for (; sc
; sc
= sc
->next
) {
3381 for (pc
= pc_start
; pc
; pc
= pc
->next
)
3382 if (soup_cookie_equal(pc
->data
, c
)) {
3383 type
= "Session + Persistent";
3388 body
= g_strdup_printf(
3390 "<td style='width: 3%%; text-align: center'>%s</td>"
3391 "<td style='width: 10%%; word-break: break-all'>%s</td>"
3392 "<td style='width: 20%%; word-break: break-all'>%s</td>"
3393 "<td style='width: 10%%; word-break: break-all'>%s</td>"
3394 "<td style='width: 8%%; word-break: break-all'>%s</td>"
3395 "<td style='width: 12%%; word-break: break-all'>%s</td>"
3396 "<td style='width: 3%%; text-align: center'>%d</td>"
3397 "<td style='width: 3%%; text-align: center'>%d</td>"
3398 "<td style='width: 3%%; text-align: center'>"
3399 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3407 soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "",
3422 soup_cookies_free(sc
);
3423 soup_cookies_free(pc
);
3425 /* small message if there are none */
3428 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3429 "colspan='8'>No Cookies</td></tr>\n", body
);
3434 footer
= g_strdup_printf("</table></div></body></html>");
3436 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3442 load_webkit_string(t
, page
);
3443 update_cookie_tabs(t
);
3451 xtp_page_hl(struct tab
*t
, struct karg
*args
)
3453 char *header
, *body
, *footer
, *page
, *tmp
;
3455 int i
= 1; /* all ids start 1 */
3457 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3460 errx(1, "%s: null tab", __func__
);
3462 /* mark this tab as history manager */
3463 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
3465 /* Generate a new session key */
3466 if (!updating_hl_tabs
)
3467 generate_xtp_session_key(&hl_session_key
);
3470 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
3471 "<title>History</title>\n"
3474 "<h1>History</h1>\n",
3478 body
= g_strdup_printf("<div align='center'><table><tr>"
3479 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
3481 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
3483 body
= g_strdup_printf(
3485 "<td><a href='%s'>%s</a></td>"
3487 "<td style='text-align: center'>"
3488 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3489 body
, h
->uri
, h
->uri
, h
->title
,
3490 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
3491 XT_XTP_HL_REMOVE
, i
);
3497 /* small message if there are none */
3500 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3501 "colspan='3'>No History</td></tr>\n", body
);
3506 footer
= g_strdup_printf("</table></div></body></html>");
3508 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3511 * update all history manager tabs as the xtp session
3512 * key has now changed. No need to update the current tab.
3513 * Already did that above.
3515 update_history_tabs(t
);
3521 load_webkit_string(t
, page
);
3528 * Generate a web page detailing the status of any downloads
3531 xtp_page_dl(struct tab
*t
, struct karg
*args
)
3533 struct download
*dl
;
3534 char *header
, *body
, *footer
, *page
, *tmp
;
3538 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
3541 errx(1, "%s: null tab", __func__
);
3543 /* mark as a download manager tab */
3544 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
3547 * Generate a new session key for next page instance.
3548 * This only happens for the top level call to xtp_page_dl()
3549 * in which case updating_dl_tabs is 0.
3551 if (!updating_dl_tabs
)
3552 generate_xtp_session_key(&dl_session_key
);
3554 /* header - with refresh so as to update */
3555 if (refresh_interval
>= 1)
3556 ref
= g_strdup_printf(
3557 "<meta http-equiv='refresh' content='%u"
3558 ";url=%s%d/%s/%d' />\n",
3568 header
= g_strdup_printf(
3570 "<title>Downloads</title>\n%s%s</head>\n",
3571 XT_DOCTYPE XT_HTML_TAG
,
3575 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
3576 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
3577 "</p><table><tr><th style='width: 60%%'>"
3578 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
3579 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
3581 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
3582 body
= xtp_page_dl_row(t
, body
, dl
);
3586 /* message if no downloads in list */
3589 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
3590 " style='text-align: center'>"
3591 "No downloads</td></tr>\n", body
);
3596 footer
= g_strdup_printf("</table></div></body></html>");
3598 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3602 * update all download manager tabs as the xtp session
3603 * key has now changed. No need to update the current tab.
3604 * Already did that above.
3606 update_download_tabs(t
);
3613 load_webkit_string(t
, page
);
3620 search(struct tab
*t
, struct karg
*args
)
3624 if (t
== NULL
|| args
== NULL
)
3626 if (t
->search_text
== NULL
) {
3627 if (global_search
== NULL
)
3628 return (XT_CB_PASSTHROUGH
);
3630 t
->search_text
= g_strdup(global_search
);
3631 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
3632 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
3636 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
3637 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
3640 case XT_SEARCH_NEXT
:
3641 d
= t
->search_forward
;
3643 case XT_SEARCH_PREV
:
3644 d
= !t
->search_forward
;
3647 return (XT_CB_PASSTHROUGH
);
3650 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
3652 return (XT_CB_HANDLED
);
3655 struct settings_args
{
3661 print_setting(struct settings
*s
, char *val
, void *cb_args
)
3664 struct settings_args
*sa
= cb_args
;
3669 if (s
->flags
& XT_SF_RUNTIME
)
3675 *sa
->body
= g_strdup_printf(
3677 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
3678 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
3690 set(struct tab
*t
, struct karg
*args
)
3692 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
3694 struct settings_args sa
;
3696 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
3697 bzero(&sa
, sizeof sa
);
3701 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3702 "\n<head><title>Settings</title>\n"
3703 "</head><body><h1>Settings</h1>\n");
3706 body
= g_strdup_printf("<div align='center'><table><tr>"
3707 "<th align='left'>Setting</th>"
3708 "<th align='left'>Value</th></tr>\n");
3710 settings_walk(print_setting
, &sa
);
3713 /* small message if there are none */
3716 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3717 "colspan='2'>No settings</td></tr>\n", body
);
3722 footer
= g_strdup_printf("</table></div></body></html>");
3724 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3730 load_webkit_string(t
, page
);
3732 show_oops(t
, "Invalid command: %s", pars
);
3735 return (XT_CB_PASSTHROUGH
);
3739 session_save(struct tab
*t
, char *filename
, char **ret
)
3745 f
+= strlen("save");
3746 while (*f
== ' ' && *f
!= '\0')
3753 strlcpy(named_session
, f
, sizeof named_session
);
3762 session_open(struct tab
*t
, char *filename
, char **ret
)
3768 f
+= strlen("open");
3769 while (*f
== ' ' && *f
!= '\0')
3776 strlcpy(named_session
, f
, sizeof named_session
);
3785 session_cmd(struct tab
*t
, struct karg
*args
)
3787 char *action
= NULL
;
3788 char *filename
= NULL
;
3793 if ((action
= getparams(args
->s
, "session")))
3798 if (!strcmp(action
, "show"))
3799 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
3800 XT_SAVED_TABS_FILE
: named_session
);
3801 else if (g_str_has_prefix(action
, "save ")) {
3802 if (session_save(t
, action
, &filename
)) {
3803 show_oops(t
, "Can't save session: %s",
3804 filename
? filename
: "INVALID");
3807 } else if (g_str_has_prefix(action
, "open ")) {
3808 if (session_open(t
, action
, &filename
)) {
3809 show_oops(t
, "Can't open session: %s",
3810 filename
? filename
: "INVALID");
3814 show_oops(t
, "Invalid command: %s", action
);
3816 return (XT_CB_PASSTHROUGH
);
3820 * Make a hardcopy of the page
3823 print_page(struct tab
*t
, struct karg
*args
)
3825 WebKitWebFrame
*frame
;
3827 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
3830 * for now we just call the GTK print box,
3831 * but later we might decide to hook in a command.
3833 frame
= webkit_web_view_get_main_frame(t
->wv
);
3834 webkit_web_frame_print(frame
);
3840 go_home(struct tab
*t
, struct karg
*args
)
3844 newuri
= guess_url_type((char *)home
);
3845 webkit_web_view_load_uri(t
->wv
, newuri
);
3852 restart(struct tab
*t
, struct karg
*args
)
3856 a
.s
= XT_RESTART_TABS_FILE
;
3858 execvp(start_argv
[0], start_argv
);
3864 /* inherent to GTK not all keys will be caught at all times */
3865 /* XXX sort key bindings */
3866 struct key_bindings
{
3870 int (*func
)(struct tab
*, struct karg
*);
3873 { GDK_MOD1_MASK
, 0, GDK_d
, xtp_page_dl
, {0} },
3874 { GDK_MOD1_MASK
, 0, GDK_h
, xtp_page_hl
, {0} },
3875 { GDK_CONTROL_MASK
, 0, GDK_p
, print_page
, {0}},
3876 { 0, 0, GDK_slash
, command
, {.i
= '/'} },
3877 { GDK_SHIFT_MASK
, 0, GDK_question
, command
, {.i
= '?'} },
3878 { GDK_SHIFT_MASK
, 0, GDK_colon
, command
, {.i
= ':'} },
3879 { GDK_CONTROL_MASK
, 0, GDK_q
, quit
, {0} },
3880 { GDK_MOD1_MASK
, 0, GDK_q
, restart
, {0} },
3881 { GDK_CONTROL_MASK
, 0, GDK_j
, toggle_js
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
3882 { GDK_MOD1_MASK
, 0, GDK_c
, toggle_cwl
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
3883 { GDK_CONTROL_MASK
, 0, GDK_s
, toggle_src
, {0} },
3884 { 0, 0, GDK_y
, yank_uri
, {0} },
3885 { 0, 0, GDK_p
, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
} },
3886 { GDK_SHIFT_MASK
, 0, GDK_P
, paste_uri
, {.i
= XT_PASTE_NEW_TAB
} },
3889 { 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
3890 { GDK_SHIFT_MASK
, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
3893 { 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
3894 { 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
3896 /* command aliases (handy when -S flag is used) */
3897 { 0, 0, GDK_F9
, command
, {.i
= XT_CMD_OPEN
} },
3898 { 0, 0, GDK_F10
, command
, {.i
= XT_CMD_OPEN_CURRENT
} },
3899 { 0, 0, GDK_F11
, command
, {.i
= XT_CMD_TABNEW
} },
3900 { 0, 0, GDK_F12
, command
, {.i
= XT_CMD_TABNEW_CURRENT
} },
3903 { 0, 0, GDK_f
, hint
, {.i
= 0} },
3906 { 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
3907 { GDK_MOD1_MASK
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
3908 { GDK_SHIFT_MASK
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
3909 { GDK_MOD1_MASK
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
3910 { 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
3911 { GDK_CONTROL_MASK
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
3912 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_R
, navaction
, {.i
= XT_NAV_RELOAD_CACHE
} },
3913 { GDK_CONTROL_MASK
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
3914 { GDK_MOD1_MASK
, 1, GDK_f
, xtp_page_fl
, {0} },
3916 /* vertical movement */
3917 { 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
3918 { 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
3919 { 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
3920 { 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
3921 { GDK_SHIFT_MASK
, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
3922 { 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
3923 { 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
3924 { 0, 0, GDK_g
, move
, {.i
= XT_MOVE_TOP
} }, /* XXX make this work */
3925 { 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3926 { GDK_CONTROL_MASK
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3927 { GDK_CONTROL_MASK
, 0, GDK_d
, move
, {.i
= XT_MOVE_HALFDOWN
} },
3928 { 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3929 { 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
3930 { GDK_CONTROL_MASK
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
3931 { GDK_CONTROL_MASK
, 0, GDK_u
, move
, {.i
= XT_MOVE_HALFUP
} },
3932 /* horizontal movement */
3933 { 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
3934 { 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
3935 { 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
3936 { 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
3937 { GDK_SHIFT_MASK
, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
3938 { 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
3941 { GDK_CONTROL_MASK
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
3942 { GDK_CONTROL_MASK
, 1, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
3943 { GDK_SHIFT_MASK
, 0, GDK_U
, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
3944 { GDK_CONTROL_MASK
, 0, GDK_1
, movetab
, {.i
= 1} },
3945 { GDK_CONTROL_MASK
, 0, GDK_2
, movetab
, {.i
= 2} },
3946 { GDK_CONTROL_MASK
, 0, GDK_3
, movetab
, {.i
= 3} },
3947 { GDK_CONTROL_MASK
, 0, GDK_4
, movetab
, {.i
= 4} },
3948 { GDK_CONTROL_MASK
, 0, GDK_5
, movetab
, {.i
= 5} },
3949 { GDK_CONTROL_MASK
, 0, GDK_6
, movetab
, {.i
= 6} },
3950 { GDK_CONTROL_MASK
, 0, GDK_7
, movetab
, {.i
= 7} },
3951 { GDK_CONTROL_MASK
, 0, GDK_8
, movetab
, {.i
= 8} },
3952 { GDK_CONTROL_MASK
, 0, GDK_9
, movetab
, {.i
= 9} },
3953 { GDK_CONTROL_MASK
, 0, GDK_0
, movetab
, {.i
= 10} },
3954 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
3955 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
3956 { GDK_CONTROL_MASK
, 0, GDK_Left
, movetab
, {.i
= XT_TAB_PREV
} },
3957 { GDK_CONTROL_MASK
, 0, GDK_Right
, movetab
, {.i
= XT_TAB_NEXT
} },
3958 { GDK_CONTROL_MASK
, 0, GDK_minus
, resizetab
, {.i
= -1} },
3959 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_plus
, resizetab
, {.i
= 1} },
3960 { GDK_CONTROL_MASK
, 0, GDK_equal
, resizetab
, {.i
= 1} },
3966 int (*func
)(struct tab
*, struct karg
*);
3969 { "q!", 0, quit
, {0} },
3970 { "qa", 0, quit
, {0} },
3971 { "qa!", 0, quit
, {0} },
3972 { "w", 0, save_tabs
, {.s
= XT_SAVED_TABS_FILE
} },
3973 { "wq", 0, save_tabs_and_quit
, {.s
= XT_SAVED_TABS_FILE
} },
3974 { "wq!", 0, save_tabs_and_quit
, {.s
= XT_SAVED_TABS_FILE
} },
3975 { "help", 0, help
, {0} },
3976 { "about", 0, about
, {0} },
3977 { "stats", 0, stats
, {0} },
3978 { "version", 0, about
, {0} },
3979 { "cookies", 0, xtp_page_cl
, {0} },
3980 { "fav", 0, xtp_page_fl
, {0} },
3981 { "favadd", 0, add_favorite
, {0} },
3982 { "js", 2, js_cmd
, {0} },
3983 { "cookie", 2, cookie_cmd
, {0} },
3984 { "cert", 1, cert_cmd
, {0} },
3985 { "ca", 0, ca_cmd
, {0} },
3986 { "dl" , 0, xtp_page_dl
, {0} },
3987 { "h" , 0, xtp_page_hl
, {0} },
3988 { "hist" , 0, xtp_page_hl
, {0} },
3989 { "history" , 0, xtp_page_hl
, {0} },
3990 { "home" , 0, go_home
, {0} },
3991 { "restart" , 0, restart
, {0} },
3993 { "1", 0, move
, {.i
= XT_MOVE_TOP
} },
3994 { "print", 0, print_page
, {0} },
3997 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3998 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3999 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
4000 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4001 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4002 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
4003 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4004 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4005 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4006 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4007 /* XXX add count to these commands */
4008 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4009 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4010 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4011 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4012 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
4013 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
4014 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
4015 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
4016 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4017 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4020 { "set", 1, set
, {0} },
4023 { "session", 1, session_cmd
, {0} },
4027 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4035 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4037 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
4039 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
4046 * cancel, remove, etc. downloads
4049 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
4051 struct download find
, *d
;
4053 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
4055 /* some commands require a valid download id */
4056 if (cmd
!= XT_XTP_DL_LIST
) {
4057 /* lookup download in question */
4059 d
= RB_FIND(download_list
, &downloads
, &find
);
4062 show_oops(t
, "%s: no such download", __func__
);
4067 /* decide what to do */
4069 case XT_XTP_DL_CANCEL
:
4070 webkit_download_cancel(d
->download
);
4072 case XT_XTP_DL_REMOVE
:
4073 webkit_download_cancel(d
->download
); /* just incase */
4074 g_object_unref(d
->download
);
4075 RB_REMOVE(download_list
, &downloads
, d
);
4077 case XT_XTP_DL_LIST
:
4081 show_oops(t
, "%s: unknown command", __func__
);
4084 xtp_page_dl(t
, NULL
);
4088 * Actions on history, only does one thing for now, but
4089 * we provide the function for future actions
4092 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
4094 struct history
*h
, *next
;
4098 case XT_XTP_HL_REMOVE
:
4099 /* walk backwards, as listed in reverse */
4100 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
4101 next
= RB_PREV(history_list
, &hl
, h
);
4103 RB_REMOVE(history_list
, &hl
, h
);
4104 g_free((gpointer
) h
->title
);
4105 g_free((gpointer
) h
->uri
);
4112 case XT_XTP_HL_LIST
:
4113 /* Nothing - just xtp_page_hl() below */
4116 show_oops(t
, "%s: unknown command", __func__
);
4120 xtp_page_hl(t
, NULL
);
4123 /* remove a favorite */
4125 remove_favorite(struct tab
*t
, int index
)
4127 char file
[PATH_MAX
], *title
, *uri
;
4128 char *new_favs
, *tmp
;
4133 /* open favorites */
4134 snprintf(file
, sizeof file
, "%s/%s/%s",
4135 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
4137 if ((f
= fopen(file
, "r")) == NULL
) {
4138 show_oops(t
, "%s: can't open favorites: %s",
4139 __func__
, strerror(errno
));
4143 /* build a string which will become the new favroites file */
4144 new_favs
= g_strdup_printf("%s", "");
4147 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
4148 if (feof(f
) || ferror(f
))
4150 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
4157 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
4158 if (feof(f
) || ferror(f
)) {
4159 show_oops(t
, "%s: can't parse favorites %s",
4160 __func__
, strerror(errno
));
4165 /* as long as this isn't the one we are deleting add to file */
4168 new_favs
= g_strdup_printf("%s%s\n%s\n",
4169 new_favs
, title
, uri
);
4181 /* write back new favorites file */
4182 if ((f
= fopen(file
, "w")) == NULL
) {
4183 show_oops(t
, "%s: can't open favorites: %s",
4184 __func__
, strerror(errno
));
4188 fwrite(new_favs
, strlen(new_favs
), 1, f
);
4201 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
4204 case XT_XTP_FL_LIST
:
4205 /* nothing, just the below call to xtp_page_fl() */
4207 case XT_XTP_FL_REMOVE
:
4208 remove_favorite(t
, arg
);
4211 show_oops(t
, "%s: invalid favorites command", __func__
);
4215 xtp_page_fl(t
, NULL
);
4219 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
4222 case XT_XTP_CL_LIST
:
4223 /* nothing, just xtp_page_cl() */
4225 case XT_XTP_CL_REMOVE
:
4229 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
4233 xtp_page_cl(t
, NULL
);
4236 /* link an XTP class to it's session key and handler function */
4237 struct xtp_despatch
{
4240 void (*handle_func
)(struct tab
*, uint8_t, int);
4243 struct xtp_despatch xtp_despatches
[] = {
4244 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
4245 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
4246 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
4247 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
4248 { NULL
, NULL
, NULL
}
4252 * is the url xtp protocol? (xxxt://)
4253 * if so, parse and despatch correct bahvior
4256 parse_xtp_url(struct tab
*t
, const char *url
)
4258 char *dup
= NULL
, *p
, *last
;
4259 uint8_t n_tokens
= 0;
4260 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
4261 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
4265 * tokens array meaning:
4267 * tokens[1] = session key
4268 * tokens[2] = action
4269 * tokens[3] = optional argument
4272 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
4274 /*xtp tab meaning is normal unless proven special */
4275 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4277 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
4280 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
4282 /* split out the url */
4283 for ((p
= strtok_r(dup
, "/", &last
)); p
;
4284 (p
= strtok_r(NULL
, "/", &last
))) {
4286 tokens
[n_tokens
++] = p
;
4289 /* should be atleast three fields 'class/seskey/command/arg' */
4293 dsp
= xtp_despatches
;
4294 req_class
= atoi(tokens
[0]);
4295 while (dsp
->xtp_class
!= NULL
) {
4296 if (dsp
->xtp_class
== req_class
) {
4303 /* did we find one atall? */
4304 if (dsp_match
== NULL
) {
4305 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
4309 /* check session key and call despatch function */
4310 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
4311 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
4324 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
4326 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
4327 char *newuri
= NULL
;
4329 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
4332 errx(1, "activate_uri_entry_cb");
4337 uri
+= strspn(uri
, "\t ");
4339 /* if xxxt:// treat specially */
4340 if (!parse_xtp_url(t
, uri
)) {
4341 if (valid_url_type((char *)uri
)) {
4342 newuri
= guess_url_type((char *)uri
);
4346 webkit_web_view_load_uri(t
->wv
, uri
);
4347 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4355 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
4357 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
4358 char *newuri
= NULL
;
4360 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
4363 errx(1, "activate_search_entry_cb");
4365 if (search_string
== NULL
) {
4366 show_oops(t
, "no search_string");
4370 newuri
= g_strdup_printf(search_string
, search
);
4372 webkit_web_view_load_uri(t
->wv
, newuri
);
4373 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4380 check_and_set_js(gchar
*uri
, struct tab
*t
)
4382 struct domain
*d
= NULL
;
4385 if (uri
== NULL
|| t
== NULL
)
4388 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
4393 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
4394 es
? "enable" : "disable", uri
);
4396 g_object_set((GObject
*)t
->settings
,
4397 "enable-scripts", es
, (char *)NULL
);
4398 webkit_web_view_set_settings(t
->wv
, t
->settings
);
4400 button_set_stockid(t
->js_toggle
,
4401 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
4405 show_ca_status(struct tab
*t
, const char *uri
)
4407 WebKitWebFrame
*frame
;
4408 WebKitWebDataSource
*source
;
4409 WebKitNetworkRequest
*request
;
4410 SoupMessage
*message
;
4412 gchar
*col_str
= "white";
4415 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
4416 ssl_strict_certs
, ssl_ca_file
, uri
);
4420 if (ssl_ca_file
== NULL
) {
4421 if (g_str_has_prefix(uri
, "http://"))
4423 if (g_str_has_prefix(uri
, "https://")) {
4429 if (g_str_has_prefix(uri
, "http://") ||
4430 !g_str_has_prefix(uri
, "https://"))
4433 frame
= webkit_web_view_get_main_frame(t
->wv
);
4434 source
= webkit_web_frame_get_data_source(frame
);
4435 request
= webkit_web_data_source_get_request(source
);
4436 message
= webkit_network_request_get_message(request
);
4438 if (message
&& (soup_message_get_flags(message
) &
4439 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
4443 r
= load_compare_cert(t
, NULL
);
4445 col_str
= "lightblue";
4454 gdk_color_parse(col_str
, &color
);
4455 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
4460 load_favicon(struct tab
*t
)
4464 GdkPixbuf
*pixbuf
, *scaled
;
4465 char file
[PATH_MAX
];
4467 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
,
4469 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
4471 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
4477 g_object_get(pixbuf
, "width", &width
, "height", &height
, (char *)NULL
);
4478 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
4479 __func__
, t
->tab_id
, width
, height
);
4481 if (width
> 16 || height
> 16) {
4482 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
4483 GDK_INTERP_BILINEAR
);
4484 g_object_unref(pixbuf
);
4492 g_object_unref(t
->icon_pixbuf
);
4494 t
->icon_pixbuf
= scaled
;
4500 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
4503 WebKitDownloadStatus status
= webkit_download_get_status (download
);
4506 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4508 g_object_unref(t
->icon_download
);
4510 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4511 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4512 g_object_unref(t
->icon_download
);
4520 notify_icon_loaded_cb(WebKitWebView
*wv
, char *uri
, struct tab
*t
)
4522 WebKitNetworkRequest
*request
;
4523 gchar
*name_hash
, *dest_uri
, file
[PATH_MAX
];
4526 if (uri
== NULL
|| t
== NULL
)
4530 if (!strcmp(t
->icon_uri
, uri
))
4532 g_free(t
->icon_uri
);
4535 t
->icon_uri
= g_strdup_printf("%s", uri
);
4536 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri %s "
4537 "(dl!)\n", __func__
, t
->tab_id
, uri
);
4539 if (t
->icon_download
&& G_IS_OBJECT(t
->icon_download
)
4540 && WEBKIT_IS_DOWNLOAD(t
->icon_download
)) {
4541 webkit_download_cancel(t
->icon_download
);
4542 g_object_unref(t
->icon_download
);
4545 request
= webkit_network_request_new(t
->icon_uri
);
4547 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri %s "
4548 "(request failed)\n", __func__
, t
->tab_id
, uri
);
4551 t
->icon_download
= webkit_download_new(request
);
4552 g_object_unref(request
);
4554 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
4555 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
4557 if (!stat(file
, &sb
)) {
4558 if (sb
.st_size
> 0) {
4563 /* corrupt icon so trash it */
4567 dest_uri
= g_strdup_printf("file://%s", file
);
4568 webkit_download_set_destination_uri(t
->icon_download
, dest_uri
);
4571 g_signal_connect(G_OBJECT (t
->icon_download
), "notify::status",
4572 G_CALLBACK (favicon_download_status_changed_cb
), t
);
4574 webkit_download_start(t
->icon_download
);
4580 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
4582 WebKitWebFrame
*frame
;
4583 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
4584 struct history
*h
, find
;
4586 const gchar
*s_loading
;
4588 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d\n",
4589 webkit_web_view_get_load_status(wview
));
4592 errx(1, "notify_load_status_cb");
4594 switch (webkit_web_view_get_load_status(wview
)) {
4595 case WEBKIT_LOAD_PROVISIONAL
:
4596 #if GTK_CHECK_VERSION(2, 20, 0)
4597 gtk_widget_show(t
->spinner
);
4598 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
4600 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
4602 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
4605 /* take focus if we are visible */
4606 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
4607 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4611 case WEBKIT_LOAD_COMMITTED
:
4612 frame
= webkit_web_view_get_main_frame(wview
);
4613 uri
= webkit_web_frame_get_uri(frame
);
4615 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
4617 /* check if js white listing is enabled */
4618 if (enable_js_whitelist
) {
4619 frame
= webkit_web_view_get_main_frame(wview
);
4620 uri
= webkit_web_frame_get_uri(frame
);
4621 check_and_set_js((gchar
*)uri
, t
);
4624 show_ca_status(t
, uri
);
4627 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
4628 title
= webkit_web_view_get_title(wview
);
4629 frame
= webkit_web_view_get_main_frame(wview
);
4630 uri
= webkit_web_frame_get_uri(frame
);
4638 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
4639 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
4642 if (!strncmp(uri
, "http://", strlen("http://")) ||
4643 !strncmp(uri
, "https://", strlen("https://")) ||
4644 !strncmp(uri
, "file://", strlen("file://")))
4649 h
= RB_FIND(history_list
, &hl
, &find
);
4653 h
= g_malloc(sizeof *h
);
4654 h
->uri
= g_strdup(uri
);
4656 h
->title
= g_strdup(title
);
4658 h
->title
= g_strdup(uri
);
4659 RB_INSERT(history_list
, &hl
, h
);
4660 update_history_tabs(NULL
);
4665 case WEBKIT_LOAD_FINISHED
:
4666 #if WEBKIT_CHECK_VERSION(1, 1, 18)
4667 case WEBKIT_LOAD_FAILED
:
4669 #if GTK_CHECK_VERSION(2, 20, 0)
4670 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
4671 gtk_widget_hide(t
->spinner
);
4673 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
4674 if (s_loading
&& !strcmp(s_loading
, "Loading"))
4675 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
4677 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
4683 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
4685 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
4686 webkit_web_view_can_go_back(wview
));
4688 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
4689 webkit_web_view_can_go_forward(wview
));
4693 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
4695 run_script(t
, JS_HINTING
);
4699 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
4701 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
4702 progress
== 100 ? 0 : (double)progress
/ 100);
4706 webview_nw_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
4707 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
4708 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
4713 errx(1, "webview_nw_cb");
4715 DNPRINTF(XT_D_NAV
, "webview_nw_cb: %s\n",
4716 webkit_network_request_get_uri(request
));
4718 /* open in current tab */
4719 uri
= (char *)webkit_network_request_get_uri(request
);
4720 webkit_web_view_load_uri(t
->wv
, uri
);
4721 webkit_web_policy_decision_ignore(pd
);
4723 return (TRUE
); /* we made the decission */
4727 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
4728 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
4729 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
4734 errx(1, "webview_npd_cb");
4736 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
4738 webkit_network_request_get_uri(request
));
4740 uri
= (char *)webkit_network_request_get_uri(request
);
4742 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
4744 create_new_tab(uri
, NULL
, ctrl_click_focus
);
4745 webkit_web_policy_decision_ignore(pd
);
4746 return (TRUE
); /* we made the decission */
4749 webkit_web_policy_decision_use(pd
);
4750 return (TRUE
); /* we made the decission */
4754 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
4757 errx(1, "webview_cwv_cb");
4759 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
4760 webkit_web_view_get_uri(wv
));
4766 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
4768 /* we can not eat the event without throwing gtk off so defer it */
4770 /* catch middle click */
4771 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
4776 /* catch ctrl click */
4777 if (e
->type
== GDK_BUTTON_RELEASE
&&
4778 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
4783 return (XT_CB_PASSTHROUGH
);
4787 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
4789 struct mime_type
*m
;
4791 m
= find_mime_type(mime_type
);
4806 execlp(m
->mt_action
, m
->mt_action
,
4807 webkit_network_request_get_uri(request
), (void *)NULL
);
4816 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
4817 WebKitNetworkRequest
*request
, char *mime_type
,
4818 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
4821 errx(1, "webview_mimetype_cb");
4823 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
4824 t
->tab_id
, mime_type
);
4826 if (run_mimehandler(t
, mime_type
, request
) == 0) {
4827 webkit_web_policy_decision_ignore(decision
);
4828 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4832 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
4833 webkit_web_policy_decision_download(decision
);
4841 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
, struct tab
*t
)
4843 const gchar
*filename
;
4845 struct download
*download_entry
;
4848 if (wk_download
== NULL
|| t
== NULL
)
4849 errx(1, "%s: invalid pointers", __func__
);
4851 filename
= webkit_download_get_suggested_filename(wk_download
);
4852 if (filename
== NULL
)
4853 return (FALSE
); /* abort download */
4855 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
4857 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
4858 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
4860 webkit_download_set_destination_uri(wk_download
, uri
);
4862 if (webkit_download_get_status(wk_download
) ==
4863 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
4864 show_oops(t
, "%s: download failed to start", __func__
);
4866 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
4868 download_entry
= g_malloc(sizeof(struct download
));
4869 download_entry
->download
= wk_download
;
4870 download_entry
->tab
= t
;
4871 download_entry
->id
= next_download_id
++;
4872 RB_INSERT(download_list
, &downloads
, download_entry
);
4873 /* get from history */
4874 g_object_ref(wk_download
);
4875 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
4881 /* sync other download manager tabs */
4882 update_download_tabs(NULL
);
4885 * NOTE: never redirect/render the current tab before this
4886 * function returns. This will cause the download to never start.
4888 return (ret
); /* start download */
4891 /* XXX currently unused */
4893 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
4895 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
4898 errx(1, "webview_hover_cb");
4905 t
->hover
= g_strdup(uri
);
4906 } else if (t
->hover
) {
4913 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
4916 char s
[2], buf
[128];
4917 const char *errstr
= NULL
;
4920 /* don't use w directly; use t->whatever instead */
4923 errx(1, "wv_keypress_after_cb");
4925 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
4926 e
->keyval
, e
->state
, t
);
4930 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
4932 return (XT_CB_HANDLED
);
4936 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
4937 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
4939 /* we have a string */
4941 /* we have a number */
4942 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
4950 /* XXX unfuck this */
4951 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
4952 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
4953 /* last input was numerical */
4955 l
= strlen(t
->hint_num
);
4962 t
->hint_num
[l
] = '\0';
4966 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
4967 /* last input was alphanumerical */
4969 l
= strlen(t
->hint_buf
);
4976 t
->hint_buf
[l
] = '\0';
4986 /* numerical input */
4987 if (CLEAN(e
->state
) == 0 &&
4988 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
4989 snprintf(s
, sizeof s
, "%c", e
->keyval
);
4990 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
4991 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
4994 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
4996 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
4999 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
5001 t
->hint_mode
= XT_HINT_NUMERICAL
;
5005 /* empty the counter buffer */
5006 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
5007 return (XT_CB_HANDLED
);
5010 /* alphanumerical input */
5012 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
5013 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
5014 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
5015 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
5016 snprintf(s
, sizeof s
, "%c", e
->keyval
);
5017 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
5018 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
5021 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
5024 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
5026 t
->hint_mode
= XT_HINT_ALPHANUM
;
5029 /* empty the counter buffer */
5030 bzero(t
->hint_num
, sizeof t
->hint_num
);
5031 return (XT_CB_HANDLED
);
5034 return (XT_CB_HANDLED
);
5037 for (i
= 0; i
< LENGTH(keys
); i
++)
5038 if (e
->keyval
== keys
[i
].key
&& CLEAN(e
->state
) ==
5040 keys
[i
].func(t
, &keys
[i
].arg
);
5041 return (XT_CB_HANDLED
);
5045 return (XT_CB_PASSTHROUGH
);
5049 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5053 return (XT_CB_PASSTHROUGH
);
5057 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5059 const gchar
*c
= gtk_entry_get_text(w
);
5063 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
5064 e
->keyval
, e
->state
, t
);
5067 errx(1, "cmd_keyrelease_cb");
5069 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
5070 e
->keyval
, e
->state
, t
);
5074 if (strlen(c
) == 1) {
5075 webkit_web_view_unmark_text_matches(t
->wv
);
5081 else if (c
[0] == '?')
5087 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
5089 /* not found, mark red */
5090 gdk_color_parse("red", &color
);
5091 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
5092 /* unmark and remove selection */
5093 webkit_web_view_unmark_text_matches(t
->wv
);
5094 /* my kingdom for a way to unselect text in webview */
5096 /* found, highlight all */
5097 webkit_web_view_unmark_text_matches(t
->wv
);
5098 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
5099 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5100 gdk_color_parse("white", &color
);
5101 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
5104 return (XT_CB_PASSTHROUGH
);
5109 cmd_complete(struct tab
*t
, char *s
)
5112 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
5114 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
5116 for (i
= 0; i
< LENGTH(cmds
); i
++) {
5117 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
5118 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
5120 gtk_entry_set_text(w
, ":");
5121 gtk_entry_append_text(w
, cmds
[i
].cmd
);
5122 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
5132 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5137 errx(1, "entry_key_cb");
5139 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
5140 e
->keyval
, e
->state
, t
);
5144 if (e
->keyval
== GDK_Escape
)
5145 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5147 for (i
= 0; i
< LENGTH(keys
); i
++)
5148 if (e
->keyval
== keys
[i
].key
&&
5149 CLEAN(e
->state
) == keys
[i
].mask
&&
5150 keys
[i
].use_in_entry
) {
5151 keys
[i
].func(t
, &keys
[i
].arg
);
5152 return (XT_CB_HANDLED
);
5155 return (XT_CB_PASSTHROUGH
);
5159 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5161 int rv
= XT_CB_HANDLED
;
5162 const gchar
*c
= gtk_entry_get_text(w
);
5165 errx(1, "cmd_keypress_cb");
5167 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
5168 e
->keyval
, e
->state
, t
);
5172 e
->keyval
= GDK_Escape
;
5173 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
5174 e
->keyval
= GDK_Escape
;
5176 switch (e
->keyval
) {
5182 if (strchr (c
, ' ')) {
5183 /* par completion */
5184 fprintf(stderr
, "completeme par\n");
5188 cmd_complete(t
, (char *)&c
[1]);
5193 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
5198 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5201 if (c
[0] == '/' || c
[0] == '?')
5202 webkit_web_view_unmark_text_matches(t
->wv
);
5206 rv
= XT_CB_PASSTHROUGH
;
5212 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
5215 errx(1, "cmd_focusout_cb");
5217 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d focus_wv %d\n",
5218 t
->tab_id
, t
->focus_wv
);
5224 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5226 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
5228 return (XT_CB_PASSTHROUGH
);
5232 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
5236 const gchar
*c
= gtk_entry_get_text(entry
);
5239 errx(1, "cmd_activate_cb");
5241 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
5246 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
5252 if (c
[0] == '/' || c
[0] == '?') {
5253 if (t
->search_text
) {
5254 g_free(t
->search_text
);
5255 t
->search_text
= NULL
;
5258 t
->search_text
= g_strdup(s
);
5260 g_free(global_search
);
5261 global_search
= g_strdup(s
);
5262 t
->search_forward
= c
[0] == '/';
5267 for (i
= 0; i
< LENGTH(cmds
); i
++)
5268 if (cmds
[i
].params
) {
5269 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
5270 cmds
[i
].arg
.s
= g_strdup(s
);
5271 goto execute_command
;
5274 if (!strcmp(s
, cmds
[i
].cmd
))
5275 goto execute_command
;
5277 show_oops(t
, "Invalid command: %s", s
);
5284 cmds
[i
].func(t
, &cmds
[i
].arg
);
5287 backward_cb(GtkWidget
*w
, struct tab
*t
)
5292 errx(1, "backward_cb");
5294 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
5301 forward_cb(GtkWidget
*w
, struct tab
*t
)
5306 errx(1, "forward_cb");
5308 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
5310 a
.i
= XT_NAV_FORWARD
;
5315 stop_cb(GtkWidget
*w
, struct tab
*t
)
5317 WebKitWebFrame
*frame
;
5322 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
5324 frame
= webkit_web_view_get_main_frame(t
->wv
);
5325 if (frame
== NULL
) {
5326 show_oops(t
, "stop_cb: no frame");
5330 webkit_web_frame_stop_loading(frame
);
5334 setup_webkit(struct tab
*t
)
5336 g_object_set((GObject
*)t
->settings
,
5337 "user-agent", t
->user_agent
, (char *)NULL
);
5338 g_object_set((GObject
*)t
->settings
,
5339 "enable-scripts", enable_scripts
, (char *)NULL
);
5340 g_object_set((GObject
*)t
->settings
,
5341 "enable-plugins", enable_plugins
, (char *)NULL
);
5342 adjustfont_webkit(t
, XT_FONT_SET
);
5344 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5348 create_browser(struct tab
*t
)
5354 errx(1, "create_browser");
5356 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
5357 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
5358 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
5359 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
5361 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
5362 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
5363 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
5365 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
5366 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
5369 t
->settings
= webkit_web_settings_new();
5371 if (user_agent
== NULL
) {
5372 g_object_get((GObject
*)t
->settings
, "user-agent", &strval
,
5374 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
5377 t
->user_agent
= g_strdup(user_agent
);
5390 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
5391 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
5392 gtk_widget_set_name(w
, "xxxterm");
5393 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
5394 g_signal_connect(G_OBJECT(w
), "delete_event",
5395 G_CALLBACK (gtk_main_quit
), NULL
);
5401 create_toolbar(struct tab
*t
)
5403 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
5405 b
= gtk_hbox_new(FALSE
, 0);
5407 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
5410 /* backward button */
5411 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
5412 gtk_widget_set_sensitive(t
->backward
, FALSE
);
5413 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
5414 G_CALLBACK(backward_cb
), t
);
5415 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
5417 /* forward button */
5418 t
->forward
= create_button("GoForward",GTK_STOCK_GO_FORWARD
, 0);
5419 gtk_widget_set_sensitive(t
->forward
, FALSE
);
5420 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
5421 G_CALLBACK(forward_cb
), t
);
5422 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
5426 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
5427 gtk_widget_set_sensitive(t
->stop
, FALSE
);
5428 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
5429 G_CALLBACK(stop_cb
), t
);
5430 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
5434 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
5435 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
5436 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
5437 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
5438 G_CALLBACK(js_toggle_cb
), t
);
5439 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
5442 t
->uri_entry
= gtk_entry_new();
5443 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
5444 G_CALLBACK(activate_uri_entry_cb
), t
);
5445 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
5446 (GCallback
)entry_key_cb
, t
);
5447 eb1
= gtk_hbox_new(FALSE
, 0);
5448 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
5449 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
5450 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
5453 if (fancy_bar
&& search_string
) {
5455 t
->search_entry
= gtk_entry_new();
5456 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
5457 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
5458 G_CALLBACK(activate_search_entry_cb
), t
);
5459 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
5460 (GCallback
)entry_key_cb
, t
);
5461 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
5462 eb2
= gtk_hbox_new(FALSE
, 0);
5463 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
5464 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
5466 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
5476 TAILQ_FOREACH(t
, &tabs
, entry
)
5477 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
5481 undo_close_tab_save(struct tab
*t
)
5485 struct undo
*u1
, *u2
;
5486 WebKitWebFrame
*frame
;
5488 WebKitWebHistoryItem
*item
;
5490 frame
= webkit_web_view_get_main_frame(t
->wv
);
5491 uri
= webkit_web_frame_get_uri(frame
);
5493 if (uri
&& !strlen(uri
))
5496 u1
= g_malloc0(sizeof(struct undo
));
5497 u1
->uri
= g_strdup(uri
);
5499 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
5501 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
5502 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
5505 /* forward history */
5506 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
5510 u1
->history
= g_list_prepend(u1
->history
,
5511 webkit_web_history_item_copy(item
));
5512 items
= g_list_next(items
);
5517 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
5518 u1
->history
= g_list_prepend(u1
->history
,
5519 webkit_web_history_item_copy(item
));
5523 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
5527 u1
->history
= g_list_prepend(u1
->history
,
5528 webkit_web_history_item_copy(item
));
5529 items
= g_list_next(items
);
5532 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
5534 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
5535 u2
= TAILQ_LAST(&undos
, undo_tailq
);
5536 TAILQ_REMOVE(&undos
, u2
, entry
);
5538 g_list_free(u2
->history
);
5547 delete_tab(struct tab
*t
)
5549 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
5554 TAILQ_REMOVE(&tabs
, t
, entry
);
5556 /* halt all webkit activity */
5557 webkit_web_view_stop_loading(t
->wv
);
5558 if (t
->icon_download
&& G_IS_OBJECT(t
->icon_download
) &&
5559 WEBKIT_IS_DOWNLOAD(t
->icon_download
)) {
5560 webkit_download_cancel(t
->icon_download
);
5561 g_object_unref(t
->icon_download
);
5563 undo_close_tab_save(t
);
5565 gtk_widget_destroy(t
->vbox
);
5566 g_free(t
->user_agent
);
5570 if (TAILQ_EMPTY(&tabs
))
5571 create_new_tab(NULL
, NULL
, 1);
5575 adjustfont_webkit(struct tab
*t
, int adjust
)
5578 errx(1, "adjustfont_webkit");
5580 if (adjust
== XT_FONT_SET
)
5581 t
->font_size
= default_font_size
;
5583 t
->font_size
+= adjust
;
5584 g_object_set((GObject
*)t
->settings
, "default-font-size",
5585 t
->font_size
, (char *)NULL
);
5586 g_object_get((GObject
*)t
->settings
, "default-font-size",
5587 &t
->font_size
, (char *)NULL
);
5591 append_tab(struct tab
*t
)
5596 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
5597 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
5601 create_new_tab(char *title
, struct undo
*u
, int focus
)
5604 int load
= 1, id
, notfound
;
5605 char *newuri
= NULL
;
5607 WebKitWebHistoryItem
*item
;
5611 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
5613 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
5614 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
5618 t
= g_malloc0(sizeof *t
);
5620 if (title
== NULL
) {
5621 title
= "(untitled)";
5624 if (valid_url_type(title
)) {
5625 newuri
= guess_url_type(title
);
5630 t
->vbox
= gtk_vbox_new(FALSE
, 0);
5632 /* label + button for tab */
5633 b
= gtk_hbox_new(FALSE
, 0);
5636 #if GTK_CHECK_VERSION(2, 20, 0)
5637 t
->spinner
= gtk_spinner_new ();
5639 t
->label
= gtk_label_new(title
);
5640 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
5641 gtk_widget_set_size_request(t
->label
, 100, 0);
5642 gtk_widget_set_size_request(b
, 130, 0);
5643 gtk_notebook_set_homogeneous_tabs(notebook
, TRUE
);
5645 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
5646 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
5647 #if GTK_CHECK_VERSION(2, 20, 0)
5648 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
5652 t
->toolbar
= create_toolbar(t
);
5653 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
5656 t
->browser_win
= create_browser(t
);
5657 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
5659 /* oops message for user feedback */
5660 t
->oops
= gtk_entry_new();
5661 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
5662 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
5663 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
5664 gdk_color_parse("red", &color
);
5665 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
5666 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
5669 t
->cmd
= gtk_entry_new();
5670 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
5671 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
5672 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
5674 /* xtp meaning is normal by default */
5675 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5677 /* and show it all */
5678 gtk_widget_show_all(b
);
5679 gtk_widget_show_all(t
->vbox
);
5681 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
5685 id
= gtk_notebook_get_current_page(notebook
);
5686 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5687 if (tt
->tab_id
== id
) {
5689 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
5690 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
5700 #if GTK_CHECK_VERSION(2, 20, 0)
5701 /* turn spinner off if we are a new tab without uri */
5703 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5704 gtk_widget_hide(t
->spinner
);
5707 /* make notebook tabs reorderable */
5708 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
5710 g_object_connect((GObject
*)t
->cmd
,
5711 "signal::key-press-event", (GCallback
)cmd_keypress_cb
, t
,
5712 "signal::key-release-event", (GCallback
)cmd_keyrelease_cb
, t
,
5713 "signal::focus-out-event", (GCallback
)cmd_focusout_cb
, t
,
5714 "signal::activate", (GCallback
)cmd_activate_cb
, t
,
5717 /* reuse wv_button_cb to hide oops */
5718 g_object_connect((GObject
*)t
->oops
,
5719 "signal::button_press_event", (GCallback
)wv_button_cb
, t
,
5722 g_object_connect((GObject
*)t
->wv
,
5723 "signal::key-press-event", (GCallback
)wv_keypress_cb
, t
,
5724 "signal-after::key-press-event", (GCallback
)wv_keypress_after_cb
, t
,
5725 /* "signal::hovering-over-link", (GCallback)webview_hover_cb, t, */
5726 "signal::download-requested", (GCallback
)webview_download_cb
, t
,
5727 "signal::mime-type-policy-decision-requested", (GCallback
)webview_mimetype_cb
, t
,
5728 "signal::navigation-policy-decision-requested", (GCallback
)webview_npd_cb
, t
,
5729 "signal::new-window-policy-decision-requested", (GCallback
)webview_nw_cb
, t
,
5730 "signal::create-web-view", (GCallback
)webview_cwv_cb
, t
,
5731 "signal::event", (GCallback
)webview_event_cb
, t
,
5732 "signal::load-finished", (GCallback
)webview_load_finished_cb
, t
,
5733 "signal::load-progress-changed", (GCallback
)webview_progress_changed_cb
, t
,
5734 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5735 "signal::icon-loaded", (GCallback
)notify_icon_loaded_cb
, t
,
5737 "signal::button_press_event", (GCallback
)wv_button_cb
, t
,
5739 g_signal_connect(t
->wv
,
5740 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
5742 /* hijack the unused keys as if we were the browser */
5743 g_object_connect((GObject
*)t
->toolbar
,
5744 "signal-after::key-press-event", (GCallback
)wv_keypress_after_cb
, t
,
5747 g_signal_connect(G_OBJECT(bb
), "button_press_event",
5748 G_CALLBACK(tab_close_cb
), t
);
5754 gtk_widget_hide(t
->toolbar
);
5757 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
5758 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
5763 webkit_web_view_load_uri(t
->wv
, title
);
5765 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
5769 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
5770 /* restore the tab's history */
5771 if (u
&& u
->history
) {
5775 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
5776 items
= g_list_next(items
);
5779 item
= g_list_nth_data(u
->history
, u
->back
);
5781 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
5784 g_list_free(u
->history
);
5792 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
5798 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
5800 TAILQ_FOREACH(t
, &tabs
, entry
) {
5801 if (t
->tab_id
== pn
) {
5802 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
5805 uri
= webkit_web_view_get_title(t
->wv
);
5808 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
5814 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5820 menuitem_response(struct tab
*t
)
5822 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
5826 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
5828 GtkWidget
*menu
, *menu_items
;
5829 GdkEventButton
*bevent
;
5830 WebKitWebFrame
*frame
;
5834 if (event
->type
== GDK_BUTTON_PRESS
) {
5835 bevent
= (GdkEventButton
*) event
;
5836 menu
= gtk_menu_new();
5838 TAILQ_FOREACH(ti
, &tabs
, entry
) {
5839 frame
= webkit_web_view_get_main_frame(ti
->wv
);
5840 uri
= webkit_web_frame_get_uri(frame
);
5841 /* XXX make sure there is something to print */
5842 /* XXX add gui pages in here to look purdy */
5845 if (strlen(uri
) == 0)
5847 menu_items
= gtk_menu_item_new_with_label(uri
);
5848 gtk_menu_append(GTK_MENU (menu
), menu_items
);
5849 gtk_widget_show(menu_items
);
5851 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
5852 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
5856 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
5857 bevent
->button
, bevent
->time
);
5859 /* unref object so it'll free itself when popped down */
5860 g_object_ref_sink(menu
);
5861 g_object_unref(menu
);
5863 return (TRUE
/* eat event */);
5866 return (FALSE
/* propagate */);
5870 icon_size_map(int icon_size
)
5872 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
5873 icon_size
> GTK_ICON_SIZE_DIALOG
)
5874 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
5880 create_button(char *name
, char *stockid
, int size
)
5882 GtkWidget
*button
, *image
;
5885 rcstring
= g_strdup_printf(
5886 "style \"%s-style\"\n"
5888 " GtkWidget::focus-padding = 0\n"
5889 " GtkWidget::focus-line-width = 0\n"
5893 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
5894 gtk_rc_parse_string(rcstring
);
5896 button
= gtk_button_new();
5897 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
5898 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
5900 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
5901 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
5902 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
5903 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
5904 gtk_widget_set_name(button
, name
);
5905 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
5906 gtk_widget_set_tooltip_text(button
, name
);
5912 button_set_stockid(GtkWidget
*button
, char *stockid
)
5915 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
5916 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
5917 gtk_button_set_image(GTK_BUTTON(button
), image
);
5926 char file
[PATH_MAX
];
5929 vbox
= gtk_vbox_new(FALSE
, 0);
5930 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
5931 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
5933 gtk_notebook_set_show_tabs(notebook
, FALSE
);
5935 gtk_notebook_set_tab_hborder(notebook
, 0);
5936 gtk_notebook_set_tab_vborder(notebook
, 0);
5938 gtk_notebook_set_show_border(notebook
, FALSE
);
5939 gtk_notebook_set_scrollable(notebook
, TRUE
);
5940 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
5942 abtn
= gtk_button_new();
5943 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
5944 gtk_widget_set_size_request(arrow
, -1, -1);
5945 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
5946 gtk_widget_set_size_request(abtn
, -1, 20);
5947 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
5949 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
5950 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
5951 gtk_widget_set_size_request(vbox
, -1, -1);
5953 g_object_connect((GObject
*)notebook
,
5954 "signal::switch-page", (GCallback
)notebook_switchpage_cb
, NULL
,
5956 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
5957 G_CALLBACK(arrow_cb
), NULL
);
5959 main_window
= create_window();
5960 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
5961 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
5964 for (i
= 0; i
< LENGTH(icons
); i
++) {
5965 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
5966 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5967 l
= g_list_append(l
, pb
);
5969 gtk_window_set_default_icon_list(l
);
5971 gtk_widget_show_all(abtn
);
5972 gtk_widget_show_all(main_window
);
5976 set_hook(void **hook
, char *name
)
5979 errx(1, "set_hook");
5981 if (*hook
== NULL
) {
5982 *hook
= dlsym(RTLD_NEXT
, name
);
5984 errx(1, "can't hook %s", name
);
5988 /* override libsoup soup_cookie_equal because it doesn't look at domain */
5990 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
5992 g_return_val_if_fail(cookie1
, FALSE
);
5993 g_return_val_if_fail(cookie2
, FALSE
);
5995 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
5996 !strcmp (cookie1
->value
, cookie2
->value
) &&
5997 !strcmp (cookie1
->path
, cookie2
->path
) &&
5998 !strcmp (cookie1
->domain
, cookie2
->domain
));
6002 transfer_cookies(void)
6005 SoupCookie
*sc
, *pc
;
6007 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
6009 for (;cf
; cf
= cf
->next
) {
6011 sc
= soup_cookie_copy(pc
);
6012 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
6015 soup_cookies_free(cf
);
6019 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
6024 print_cookie("soup_cookie_jar_delete_cookie", c
);
6026 if (cookies_enabled
== 0)
6029 if (jar
== NULL
|| c
== NULL
)
6032 /* find and remove from persistent jar */
6033 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
6035 for (;cf
; cf
= cf
->next
) {
6037 if (soup_cookie_equal(ci
, c
)) {
6038 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
6043 soup_cookies_free(cf
);
6045 /* delete from session jar */
6046 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
6050 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
6056 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
6057 jar
, p_cookiejar
, s_cookiejar
);
6059 if (cookies_enabled
== 0)
6062 /* see if we are up and running */
6063 if (p_cookiejar
== NULL
) {
6064 _soup_cookie_jar_add_cookie(jar
, cookie
);
6067 /* disallow p_cookiejar adds, shouldn't happen */
6068 if (jar
== p_cookiejar
)
6071 if ((d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
6073 DNPRINTF(XT_D_COOKIE
,
6074 "soup_cookie_jar_add_cookie: reject %s\n",
6076 if (save_rejected_cookies
) {
6077 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
)
6078 err(1, "reject cookie file");
6079 fseek(r_cookie_f
, 0, SEEK_END
);
6080 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
6081 cookie
->http_only
? "#HttpOnly_" : "",
6083 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
6085 cookie
->secure
? "TRUE" : "FALSE",
6087 (gulong
)soup_date_to_time_t(cookie
->expires
) :
6094 if (!allow_volatile_cookies
)
6098 if (cookie
->expires
== NULL
&& session_timeout
) {
6099 soup_cookie_set_expires(cookie
,
6100 soup_date_new_from_now(session_timeout
));
6101 print_cookie("modified add cookie", cookie
);
6104 /* see if we are white listed for persistence */
6105 if (d
&& d
->handy
) {
6106 /* add to persistent jar */
6107 c
= soup_cookie_copy(cookie
);
6108 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
6109 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
6112 /* add to session jar */
6113 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
6114 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
6120 char file
[PATH_MAX
];
6122 set_hook((void *)&_soup_cookie_jar_add_cookie
,
6123 "soup_cookie_jar_add_cookie");
6124 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
6125 "soup_cookie_jar_delete_cookie");
6127 if (cookies_enabled
== 0)
6131 * the following code is intricate due to overriding several libsoup
6133 * do not alter order of these operations.
6136 /* rejected cookies */
6137 if (save_rejected_cookies
)
6138 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
6140 /* persistent cookies */
6141 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
6142 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
6144 /* session cookies */
6145 s_cookiejar
= soup_cookie_jar_new();
6146 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
6147 cookie_policy
, (void *)NULL
);
6150 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
6154 setup_proxy(char *uri
)
6157 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
6158 soup_uri_free(proxy_uri
);
6162 if (http_proxy
!= uri
) {
6169 http_proxy
= g_strdup(uri
);
6170 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
6171 proxy_uri
= soup_uri_new(http_proxy
);
6172 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
6177 send_url_to_socket(char *url
)
6179 int s
, len
, rv
= -1;
6180 struct sockaddr_un sa
;
6182 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6183 warnx("send_url_to_socket: socket");
6187 sa
.sun_family
= AF_UNIX
;
6188 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6189 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6192 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6193 warnx("send_url_to_socket: connect");
6197 if (send(s
, url
, strlen(url
) + 1, 0) == -1) {
6198 warnx("send_url_to_socket: send");
6207 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
6210 char str
[XT_MAX_URL_LENGTH
];
6211 socklen_t t
= sizeof(struct sockaddr_un
);
6212 struct sockaddr_un sa
;
6217 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
6218 warn("socket_watcher: accept");
6222 if (getpeereid(s
, &uid
, &gid
) == -1) {
6223 warn("socket_watcher: getpeereid");
6226 if (uid
!= getuid() || gid
!= getgid()) {
6227 warnx("socket_watcher: unauthorized user");
6233 warnx("socket_watcher: not a valid user");
6237 n
= recv(s
, str
, sizeof(str
), 0);
6241 create_new_tab(str
, NULL
, 1);
6248 struct sockaddr_un sa
;
6250 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6251 warn("is_running: socket");
6255 sa
.sun_family
= AF_UNIX
;
6256 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6257 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6260 /* connect to see if there is a listener */
6261 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
6262 rv
= 0; /* not running */
6264 rv
= 1; /* already running */
6275 struct sockaddr_un sa
;
6277 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6278 warn("build_socket: socket");
6282 sa
.sun_family
= AF_UNIX
;
6283 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6284 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6287 /* connect to see if there is a listener */
6288 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6289 /* no listener so we will */
6290 unlink(sa
.sun_path
);
6292 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6293 warn("build_socket: bind");
6297 if (listen(s
, 1) == -1) {
6298 warn("build_socket: listen");
6314 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
6319 main(int argc
, char *argv
[])
6322 int c
, focus
= 1, s
, optn
= 0;
6323 char conf
[PATH_MAX
] = { '\0' };
6324 char file
[PATH_MAX
];
6325 char *env_proxy
= NULL
;
6331 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
6333 while ((c
= getopt(argc
, argv
, "STVf:s:tn")) != -1) {
6342 errx(0 , "Version: %s", version
);
6345 strlcpy(conf
, optarg
, sizeof(conf
));
6348 strlcpy(named_session
, optarg
, sizeof(named_session
));
6367 RB_INIT(&downloads
);
6369 TAILQ_INIT(&aliases
);
6372 gnutls_global_init();
6374 /* generate session keys for xtp pages */
6375 generate_xtp_session_key(&dl_session_key
);
6376 generate_xtp_session_key(&hl_session_key
);
6377 generate_xtp_session_key(&cl_session_key
);
6378 generate_xtp_session_key(&fl_session_key
);
6381 gtk_init(&argc
, &argv
);
6382 if (!g_thread_supported())
6383 g_thread_init(NULL
);
6385 pwd
= getpwuid(getuid());
6387 errx(1, "invalid user %d", getuid());
6389 /* set download dir */
6390 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
6392 /* set default string settings */
6393 home
= g_strdup("http://www.peereboom.us");
6394 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
6395 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
6397 /* read config file */
6398 if (strlen(conf
) == 0)
6399 snprintf(conf
, sizeof conf
, "%s/.%s",
6400 pwd
->pw_dir
, XT_CONF_FILE
);
6401 config_parse(conf
, 0);
6403 /* working directory */
6404 snprintf(work_dir
, sizeof work_dir
, "%s/%s", pwd
->pw_dir
, XT_DIR
);
6405 if (stat(work_dir
, &sb
)) {
6406 if (mkdir(work_dir
, S_IRWXU
) == -1)
6407 err(1, "mkdir work_dir");
6408 if (stat(work_dir
, &sb
))
6409 err(1, "stat work_dir");
6411 if (S_ISDIR(sb
.st_mode
) == 0)
6412 errx(1, "%s not a dir", work_dir
);
6413 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6414 warnx("fixing invalid permissions on %s", work_dir
);
6415 if (chmod(work_dir
, S_IRWXU
) == -1)
6419 /* icon cache dir */
6420 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s/%s",
6421 pwd
->pw_dir
, XT_DIR
, XT_CACHE_DIR
);
6422 if (stat(cache_dir
, &sb
)) {
6423 if (mkdir(cache_dir
, S_IRWXU
) == -1)
6424 err(1, "mkdir cache_dir");
6425 if (stat(cache_dir
, &sb
))
6426 err(1, "stat cache_dir");
6428 if (S_ISDIR(sb
.st_mode
) == 0)
6429 errx(1, "%s not a dir", cache_dir
);
6430 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6431 warnx("fixing invalid permissions on %s", cache_dir
);
6432 if (chmod(cache_dir
, S_IRWXU
) == -1)
6437 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s/%s",
6438 pwd
->pw_dir
, XT_DIR
, XT_CERT_DIR
);
6439 if (stat(certs_dir
, &sb
)) {
6440 if (mkdir(certs_dir
, S_IRWXU
) == -1)
6441 err(1, "mkdir certs_dir");
6442 if (stat(certs_dir
, &sb
))
6443 err(1, "stat certs_dir");
6445 if (S_ISDIR(sb
.st_mode
) == 0)
6446 errx(1, "%s not a dir", certs_dir
);
6447 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6448 warnx("fixing invalid permissions on %s", certs_dir
);
6449 if (chmod(certs_dir
, S_IRWXU
) == -1)
6454 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s/%s",
6455 pwd
->pw_dir
, XT_DIR
, XT_SESSIONS_DIR
);
6456 if (stat(sessions_dir
, &sb
)) {
6457 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
6458 err(1, "mkdir sessions_dir");
6459 if (stat(sessions_dir
, &sb
))
6460 err(1, "stat sessions_dir");
6462 if (S_ISDIR(sb
.st_mode
) == 0)
6463 errx(1, "%s not a dir", sessions_dir
);
6464 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6465 warnx("fixing invalid permissions on %s", sessions_dir
);
6466 if (chmod(sessions_dir
, S_IRWXU
) == -1)
6469 /* runtime settings that can override config file */
6470 if (runtime_settings
[0] != '\0')
6471 config_parse(runtime_settings
, 1);
6474 if (!strcmp(download_dir
, pwd
->pw_dir
))
6475 strlcat(download_dir
, "/downloads", sizeof download_dir
);
6476 if (stat(download_dir
, &sb
)) {
6477 if (mkdir(download_dir
, S_IRWXU
) == -1)
6478 err(1, "mkdir download_dir");
6479 if (stat(download_dir
, &sb
))
6480 err(1, "stat download_dir");
6482 if (S_ISDIR(sb
.st_mode
) == 0)
6483 errx(1, "%s not a dir", download_dir
);
6484 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6485 warnx("fixing invalid permissions on %s", download_dir
);
6486 if (chmod(download_dir
, S_IRWXU
) == -1)
6490 /* favorites file */
6491 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6492 if (stat(file
, &sb
)) {
6493 warnx("favorites file doesn't exist, creating it");
6494 if ((f
= fopen(file
, "w")) == NULL
)
6495 err(1, "favorites");
6500 session
= webkit_get_default_session();
6505 if (stat(ssl_ca_file
, &sb
)) {
6506 warn("no CA file: %s", ssl_ca_file
);
6507 g_free(ssl_ca_file
);
6510 g_object_set(session
,
6511 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
6512 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
6517 env_proxy
= getenv("http_proxy");
6519 setup_proxy(env_proxy
);
6521 setup_proxy(http_proxy
);
6523 /* see if there is already an xxxterm running */
6524 if (single_instance
&& is_running()) {
6526 warnx("already running");
6531 send_url_to_socket(argv
[0]);
6542 if (save_global_history
)
6543 restore_global_history();
6545 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
6546 focus
= restore_saved_tabs();
6548 a
.s
= named_session
;
6549 focus
= open_tabs(NULL
, &a
);
6553 create_new_tab(argv
[0], NULL
, focus
);
6560 create_new_tab(home
, NULL
, 1);
6563 if ((s
= build_socket()) != -1)
6564 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
6568 gnutls_global_deinit();