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
;
169 WebKitWebHistoryItem
*item
;
170 WebKitWebBackForwardList
*bfl
;
172 /* adjustments for browser */
175 GtkAdjustment
*adjust_h
;
176 GtkAdjustment
*adjust_v
;
182 int xtp_meaning
; /* identifies dls/favorites */
187 #define XT_HINT_NONE (0)
188 #define XT_HINT_NUMERICAL (1)
189 #define XT_HINT_ALPHANUM (2)
198 WebKitWebSettings
*settings
;
202 TAILQ_HEAD(tab_list
, tab
);
205 RB_ENTRY(history
) entry
;
209 RB_HEAD(history_list
, history
);
212 RB_ENTRY(download
) entry
;
214 WebKitDownload
*download
;
217 RB_HEAD(download_list
, download
);
220 RB_ENTRY(domain
) entry
;
222 int handy
; /* app use */
224 RB_HEAD(domain_list
, domain
);
227 TAILQ_ENTRY(undo
) entry
;
230 int back
; /* Keeps track of how many back
231 * history items there are. */
233 TAILQ_HEAD(undo_tailq
, undo
);
235 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
236 int next_download_id
= 1;
244 #define XT_NAME ("XXXTerm")
245 #define XT_DIR (".xxxterm")
246 #define XT_CACHE_DIR ("cache")
247 #define XT_CERT_DIR ("certs/")
248 #define XT_SESSIONS_DIR ("sessions/")
249 #define XT_CONF_FILE ("xxxterm.conf")
250 #define XT_FAVS_FILE ("favorites")
251 #define XT_SAVED_TABS_FILE ("main_session")
252 #define XT_RESTART_TABS_FILE ("restart_tabs")
253 #define XT_SOCKET_FILE ("socket")
254 #define XT_HISTORY_FILE ("history")
255 #define XT_CB_HANDLED (TRUE)
256 #define XT_CB_PASSTHROUGH (FALSE)
257 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
258 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
259 #define XT_DLMAN_REFRESH "10"
260 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
261 "td {overflow: hidden;}\n" \
262 "th {background-color: #cccccc}" \
263 "table {width: 90%%; border: 1px black" \
264 " solid; table-layout: fixed}\n</style>\n\n"
265 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
266 #define XT_MAX_UNDO_CLOSE_TAB (32)
269 #define SZ_KB ((uint64_t) 1024)
270 #define SZ_MB (SZ_KB * SZ_KB)
271 #define SZ_GB (SZ_KB * SZ_KB * SZ_KB)
272 #define SZ_TB (SZ_KB * SZ_KB * SZ_KB * SZ_KB)
275 * xxxterm "protocol" (xtp)
276 * We use this for managing stuff like downloads and favorites. They
277 * make magical HTML pages in memory which have xxxt:// links in order
278 * to communicate with xxxterm's internals. These links take the format:
279 * xxxt://class/session_key/action/arg
281 * Don't begin xtp class/actions as 0. atoi returns that on error.
283 * Typically we have not put addition of items in this framework, as
284 * adding items is either done via an ex-command or via a keybinding instead.
287 #define XT_XTP_STR "xxxt://"
289 /* XTP classes (xxxt://<class>) */
290 #define XT_XTP_DL 1 /* downloads */
291 #define XT_XTP_HL 2 /* history */
292 #define XT_XTP_CL 3 /* cookies */
293 #define XT_XTP_FL 4 /* favorites */
295 /* XTP download actions */
296 #define XT_XTP_DL_LIST 1
297 #define XT_XTP_DL_CANCEL 2
298 #define XT_XTP_DL_REMOVE 3
300 /* XTP history actions */
301 #define XT_XTP_HL_LIST 1
302 #define XT_XTP_HL_REMOVE 2
304 /* XTP cookie actions */
305 #define XT_XTP_CL_LIST 1
306 #define XT_XTP_CL_REMOVE 2
308 /* XTP cookie actions */
309 #define XT_XTP_FL_LIST 1
310 #define XT_XTP_FL_REMOVE 2
312 /* xtp tab meanings - identifies which tabs have xtp pages in */
313 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
314 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
315 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
316 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
317 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
320 #define XT_MOVE_INVALID (0)
321 #define XT_MOVE_DOWN (1)
322 #define XT_MOVE_UP (2)
323 #define XT_MOVE_BOTTOM (3)
324 #define XT_MOVE_TOP (4)
325 #define XT_MOVE_PAGEDOWN (5)
326 #define XT_MOVE_PAGEUP (6)
327 #define XT_MOVE_HALFDOWN (7)
328 #define XT_MOVE_HALFUP (8)
329 #define XT_MOVE_LEFT (9)
330 #define XT_MOVE_FARLEFT (10)
331 #define XT_MOVE_RIGHT (11)
332 #define XT_MOVE_FARRIGHT (12)
334 #define XT_TAB_LAST (-4)
335 #define XT_TAB_FIRST (-3)
336 #define XT_TAB_PREV (-2)
337 #define XT_TAB_NEXT (-1)
338 #define XT_TAB_INVALID (0)
339 #define XT_TAB_NEW (1)
340 #define XT_TAB_DELETE (2)
341 #define XT_TAB_DELQUIT (3)
342 #define XT_TAB_OPEN (4)
343 #define XT_TAB_UNDO_CLOSE (5)
345 #define XT_NAV_INVALID (0)
346 #define XT_NAV_BACK (1)
347 #define XT_NAV_FORWARD (2)
348 #define XT_NAV_RELOAD (3)
349 #define XT_NAV_RELOAD_CACHE (4)
351 #define XT_FOCUS_INVALID (0)
352 #define XT_FOCUS_URI (1)
353 #define XT_FOCUS_SEARCH (2)
355 #define XT_SEARCH_INVALID (0)
356 #define XT_SEARCH_NEXT (1)
357 #define XT_SEARCH_PREV (2)
359 #define XT_PASTE_CURRENT_TAB (0)
360 #define XT_PASTE_NEW_TAB (1)
362 #define XT_FONT_SET (0)
364 #define XT_WL_TOGGLE (1<<0)
365 #define XT_WL_ENABLE (1<<1)
366 #define XT_WL_DISABLE (1<<2)
367 #define XT_WL_FQDN (1<<3) /* default */
368 #define XT_WL_TOPLEVEL (1<<4)
370 #define XT_CMD_OPEN (0)
371 #define XT_CMD_OPEN_CURRENT (1)
372 #define XT_CMD_TABNEW (2)
373 #define XT_CMD_TABNEW_CURRENT (3)
380 TAILQ_ENTRY(mime_type
) entry
;
382 TAILQ_HEAD(mime_type_list
, mime_type
);
388 TAILQ_ENTRY(alias
) entry
;
390 TAILQ_HEAD(alias_list
, alias
);
392 /* settings that require restart */
393 int showtabs
= 1; /* show tabs on notebook */
394 int showurl
= 1; /* show url toolbar on notebook */
395 int tabless
= 0; /* allow only 1 tab */
396 int enable_socket
= 0;
397 int single_instance
= 0; /* only allow one xxxterm to run */
398 int fancy_bar
= 1; /* fancy toolbar */
400 /* runtime settings */
401 int ctrl_click_focus
= 0; /* ctrl click gets focus */
402 int cookies_enabled
= 1; /* enable cookies */
403 int read_only_cookies
= 0; /* enable to not write cookies */
404 int enable_scripts
= 0;
405 int enable_plugins
= 0;
406 int default_font_size
= 12;
407 int window_height
= 768;
408 int window_width
= 1024;
409 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
410 unsigned refresh_interval
= 10; /* download refresh interval */
411 int enable_cookie_whitelist
= 1;
412 int enable_js_whitelist
= 1;
413 time_t session_timeout
= 3600; /* cookie session timeout */
414 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
415 char *ssl_ca_file
= NULL
;
416 char *resource_dir
= NULL
;
417 gboolean ssl_strict_certs
= FALSE
;
418 int append_next
= 1; /* append tab after current tab */
420 char *search_string
= NULL
;
421 char *http_proxy
= NULL
;
422 char download_dir
[PATH_MAX
];
423 char runtime_settings
[PATH_MAX
]; /* override of settings */
424 int allow_volatile_cookies
= 0;
425 int save_global_history
= 0; /* save global history to disk */
426 char *user_agent
= NULL
;
427 int save_rejected_cookies
= 0;
430 int set_download_dir(struct settings
*, char *);
431 int set_runtime_dir(struct settings
*, char *);
432 int set_cookie_policy(struct settings
*, char *);
433 int add_alias(struct settings
*, char *);
434 int add_mime_type(struct settings
*, char *);
435 int add_cookie_wl(struct settings
*, char *);
436 int add_js_wl(struct settings
*, char *);
437 void button_set_stockid(GtkWidget
*, char *);
438 GtkWidget
* create_button(char *, char *, int);
440 char *get_cookie_policy(struct settings
*);
442 char *get_download_dir(struct settings
*);
443 char *get_runtime_dir(struct settings
*);
445 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
446 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
447 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
448 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
451 int (*set
)(struct settings
*, char *);
452 char *(*get
)(struct settings
*);
453 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
456 struct special s_cookie
= {
462 struct special s_alias
= {
468 struct special s_mime
= {
474 struct special s_js
= {
480 struct special s_cookie_wl
= {
486 struct special s_download_dir
= {
495 #define XT_S_INVALID (0)
499 #define XT_SF_RESTART (1<<0)
500 #define XT_SF_RUNTIME (1<<1)
505 { "append_next", XT_S_INT
, 0 , &append_next
, NULL
, NULL
},
506 { "allow_volatile_cookies", XT_S_INT
, 0 , &allow_volatile_cookies
, NULL
, NULL
},
507 { "cookies_enabled", XT_S_INT
, 0 , &cookies_enabled
, NULL
, NULL
},
508 { "cookie_policy", XT_S_INT
, 0 , NULL
, NULL
, &s_cookie
},
509 { "ctrl_click_focus", XT_S_INT
, 0 , &ctrl_click_focus
, NULL
, NULL
},
510 { "default_font_size", XT_S_INT
, 0 , &default_font_size
, NULL
, NULL
},
511 { "download_dir", XT_S_STR
, 0 , NULL
, NULL
, &s_download_dir
},
512 { "enable_cookie_whitelist", XT_S_INT
, 0 , &enable_cookie_whitelist
, NULL
, NULL
},
513 { "enable_js_whitelist", XT_S_INT
, 0 , &enable_js_whitelist
, NULL
, NULL
},
514 { "enable_plugins", XT_S_INT
, 0 , &enable_plugins
, NULL
, NULL
},
515 { "enable_scripts", XT_S_INT
, 0 , &enable_scripts
, NULL
, NULL
},
516 { "enable_socket", XT_S_INT
, XT_SF_RESTART
, &enable_socket
, NULL
, NULL
},
517 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
, &fancy_bar
, NULL
, NULL
},
518 { "home", XT_S_STR
, 0 , NULL
, &home
, NULL
},
519 { "http_proxy", XT_S_STR
, 0 , NULL
, &http_proxy
, NULL
},
520 { "icon_size", XT_S_INT
, 0 , &icon_size
, NULL
, NULL
},
521 { "read_only_cookies", XT_S_INT
, 0 , &read_only_cookies
, NULL
, NULL
},
522 { "refresh_interval", XT_S_INT
, 0 , &refresh_interval
, NULL
, NULL
},
523 { "resource_dir", XT_S_STR
, 0 , NULL
, &resource_dir
, NULL
},
524 { "search_string", XT_S_STR
, 0 , NULL
, &search_string
, NULL
},
525 { "session_timeout", XT_S_INT
, 0 , &session_timeout
, NULL
, NULL
},
526 { "save_global_history", XT_S_INT
, XT_SF_RESTART
, &save_global_history
, NULL
, NULL
},
527 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
, &save_rejected_cookies
, NULL
, NULL
},
528 { "single_instance", XT_S_INT
, XT_SF_RESTART
, &single_instance
, NULL
, NULL
},
529 { "ssl_ca_file", XT_S_STR
, 0 , NULL
, &ssl_ca_file
, NULL
},
530 { "ssl_strict_certs", XT_S_INT
, 0 , &ssl_strict_certs
, NULL
, NULL
},
531 { "user_agent", XT_S_STR
, 0 , NULL
, &user_agent
, NULL
},
532 { "window_height", XT_S_INT
, 0 , &window_height
, NULL
, NULL
},
533 { "window_width", XT_S_INT
, 0 , &window_width
, NULL
, NULL
},
535 /* runtime settings */
536 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
537 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
538 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
539 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
543 extern char *__progname
;
546 GtkWidget
*main_window
;
547 GtkNotebook
*notebook
;
548 GtkWidget
*arrow
, *abtn
;
549 struct tab_list tabs
;
550 struct history_list hl
;
551 struct download_list downloads
;
552 struct domain_list c_wl
;
553 struct domain_list js_wl
;
554 struct undo_tailq undos
;
556 int updating_dl_tabs
= 0;
557 int updating_hl_tabs
= 0;
558 int updating_cl_tabs
= 0;
559 int updating_fl_tabs
= 0;
561 uint64_t blocked_cookies
= 0;
562 char named_session
[PATH_MAX
];
563 int notify_icon_loaded_cb(WebKitWebView
*, char *,
565 void update_favicon(struct tab
*);
566 int icon_size_map(int);
569 check_favicon(struct tab
*t
)
571 const gchar
*iconuri
= webkit_web_view_get_icon_uri(t
->wv
);
572 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri '%s'\n",
573 __func__
, t
->tab_id
, iconuri
);
574 if (iconuri
&& strlen(iconuri
) > 0) {
575 notify_icon_loaded_cb(t
->wv
, (char *)iconuri
, t
);
584 update_favicon(struct tab
*t
)
586 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri '%s'\n",
587 __func__
, t
->tab_id
, t
->icon_uri
);
588 if (t
->icon_uri
&& strlen(t
->icon_uri
) > 0 && t
->icon_pixbuf
) {
589 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
590 GTK_ENTRY_ICON_PRIMARY
, t
->icon_pixbuf
);
591 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri '%s'"
592 " (set)\n", __func__
, t
->tab_id
,
593 t
->icon_uri
? t
->icon_uri
: "(NULL)");
595 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
596 GTK_ENTRY_ICON_PRIMARY
, "text-html");
597 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri '%s'"
598 " (!set)\n", __func__
, t
->tab_id
,
599 (t
->icon_uri
&& strlen(t
->icon_uri
) > 0) ?
600 t
->icon_uri
: "<empty>");
604 load_webkit_string(struct tab
*t
, const char *str
)
606 /* we set this to indicate we want to manually do navaction */
607 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
608 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
612 hide_oops(struct tab
*t
)
614 gtk_widget_hide(t
->oops
);
618 hide_cmd(struct tab
*t
)
620 gtk_widget_hide(t
->cmd
);
624 show_cmd(struct tab
*t
)
626 gtk_widget_hide(t
->oops
);
627 gtk_widget_show(t
->cmd
);
631 show_oops(struct tab
*t
, const char *fmt
, ...)
640 if (vasprintf(&msg
, fmt
, ap
) == -1)
644 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
645 gtk_widget_hide(t
->cmd
);
646 gtk_widget_show(t
->oops
);
649 get_as_string(struct settings
*s
)
660 warnx("get_as_string skip %s\n", s
->name
);
661 } else if (s
->type
== XT_S_INT
)
662 r
= g_strdup_printf("%d", *s
->ival
);
663 else if (s
->type
== XT_S_STR
)
664 r
= g_strdup(*s
->sval
);
666 r
= g_strdup_printf("INVALID TYPE");
672 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
677 for (i
= 0; i
< LENGTH(rs
); i
++) {
678 if (rs
[i
].s
&& rs
[i
].s
->walk
)
679 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
681 s
= get_as_string(&rs
[i
]);
682 cb(&rs
[i
], s
, cb_args
);
689 set_cookie_policy(struct settings
*s
, char *val
)
691 if (!strcmp(val
, "no3rdparty"))
692 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
693 else if (!strcmp(val
, "accept"))
694 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
695 else if (!strcmp(val
, "reject"))
696 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
704 get_cookie_policy(struct settings
*s
)
708 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
709 r
= g_strdup("no3rdparty");
710 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
711 r
= g_strdup("accept");
712 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
713 r
= g_strdup("reject");
721 get_download_dir(struct settings
*s
)
723 if (download_dir
[0] == '\0')
725 return (g_strdup(download_dir
));
729 set_download_dir(struct settings
*s
, char *val
)
732 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
733 pwd
->pw_dir
, &val
[1]);
735 strlcpy(download_dir
, val
, sizeof download_dir
);
742 * We use these to prevent people putting xxxt:// URLs on
743 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
745 #define XT_XTP_SES_KEY_SZ 8
746 #define XT_XTP_SES_KEY_HEX_FMT \
747 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
748 char *dl_session_key
; /* downloads */
749 char *hl_session_key
; /* history list */
750 char *cl_session_key
; /* cookie list */
751 char *fl_session_key
; /* favorites list */
753 char work_dir
[PATH_MAX
];
754 char certs_dir
[PATH_MAX
];
755 char cache_dir
[PATH_MAX
];
756 char sessions_dir
[PATH_MAX
];
757 char cookie_file
[PATH_MAX
];
758 SoupURI
*proxy_uri
= NULL
;
759 SoupSession
*session
;
760 SoupCookieJar
*s_cookiejar
;
761 SoupCookieJar
*p_cookiejar
;
762 char rc_fname
[PATH_MAX
];
764 struct mime_type_list mtl
;
765 struct alias_list aliases
;
768 void create_new_tab(char *, struct undo
*, int);
769 void delete_tab(struct tab
*);
770 void adjustfont_webkit(struct tab
*, int);
771 int run_script(struct tab
*, char *);
772 int download_rb_cmp(struct download
*, struct download
*);
773 int xtp_page_hl(struct tab
*t
, struct karg
*args
);
774 int xtp_page_dl(struct tab
*t
, struct karg
*args
);
775 int xtp_page_cl(struct tab
*t
, struct karg
*args
);
776 int xtp_page_fl(struct tab
*t
, struct karg
*args
);
779 history_rb_cmp(struct history
*h1
, struct history
*h2
)
781 return (strcmp(h1
->uri
, h2
->uri
));
783 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
786 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
788 return (strcmp(d1
->d
, d2
->d
));
790 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
793 * generate a session key to secure xtp commands.
794 * pass in a ptr to the key in question and it will
795 * be modified in place.
798 generate_xtp_session_key(char **key
)
800 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
807 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
808 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
809 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
810 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
812 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
816 * validate a xtp session key.
820 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
822 if (strcmp(trusted
, untrusted
) != 0) {
823 show_oops(t
, "%s: xtp session key mismatch possible spoof",
832 download_rb_cmp(struct download
*e1
, struct download
*e2
)
834 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
836 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
838 struct valid_url_types
{
849 valid_url_type(char *url
)
853 for (i
= 0; i
< LENGTH(vut
); i
++)
854 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
861 print_cookie(char *msg
, SoupCookie
*c
)
867 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
868 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
869 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
870 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
871 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
872 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
873 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
874 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
875 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
876 DNPRINTF(XT_D_COOKIE
, "====================================\n");
880 walk_alias(struct settings
*s
,
881 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
886 if (s
== NULL
|| cb
== NULL
)
887 errx(1, "walk_alias");
889 TAILQ_FOREACH(a
, &aliases
, entry
) {
890 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
897 match_alias(char *url_in
)
901 char *url_out
= NULL
;
904 if (strsep(&arg
, " \t") == NULL
)
905 errx(1, "match_alias: NULL URL");
907 TAILQ_FOREACH(a
, &aliases
, entry
) {
908 if (!strcmp(url_in
, a
->a_name
))
913 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
916 url_out
= g_strdup_printf(a
->a_uri
, arg
);
918 url_out
= g_strdup(a
->a_uri
);
925 guess_url_type(char *url_in
)
928 char *url_out
= NULL
;
930 url_out
= match_alias(url_in
);
934 /* XXX not sure about this heuristic */
935 if (stat(url_in
, &sb
) == 0)
936 url_out
= g_strdup_printf("file://%s", url_in
);
938 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
940 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
946 add_alias(struct settings
*s
, char *line
)
952 errx(1, "add_alias");
955 a
= g_malloc(sizeof(*a
));
957 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
)
958 errx(1, "add_alias: incomplete alias definition");
960 if (strlen(alias
) == 0 || strlen(l
) == 0)
961 errx(1, "add_alias: invalid alias definition");
963 a
->a_name
= g_strdup(alias
);
964 a
->a_uri
= g_strdup(l
);
966 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
968 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
974 add_mime_type(struct settings
*s
, char *line
)
980 /* XXX this could be smarter */
983 errx(1, "add_mime_type");
986 m
= g_malloc(sizeof(*m
));
988 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
)
989 errx(1, "add_mime_type: invalid mime_type");
991 if (mime_type
[strlen(mime_type
) - 1] == '*') {
992 mime_type
[strlen(mime_type
) - 1] = '\0';
997 if (strlen(mime_type
) == 0 || strlen(l
) == 0)
998 errx(1, "add_mime_type: invalid mime_type");
1000 m
->mt_type
= g_strdup(mime_type
);
1001 m
->mt_action
= g_strdup(l
);
1003 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
1004 m
->mt_type
, m
->mt_action
, m
->mt_default
);
1006 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
1012 find_mime_type(char *mime_type
)
1014 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
1016 TAILQ_FOREACH(m
, &mtl
, entry
) {
1017 if (m
->mt_default
&&
1018 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
1021 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1034 walk_mime_type(struct settings
*s
,
1035 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1037 struct mime_type
*m
;
1040 if (s
== NULL
|| cb
== NULL
)
1041 errx(1, "walk_mime_type");
1043 TAILQ_FOREACH(m
, &mtl
, entry
) {
1044 str
= g_strdup_printf("%s%s --> %s",
1046 m
->mt_default
? "*" : "",
1048 cb(s
, str
, cb_args
);
1054 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1059 if (str
== NULL
|| wl
== NULL
)
1061 if (strlen(str
) < 2)
1064 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1066 /* treat *.moo.com the same as .moo.com */
1067 if (str
[0] == '*' && str
[1] == '.')
1069 else if (str
[0] == '.')
1074 d
= g_malloc(sizeof *d
);
1076 d
->d
= g_strdup_printf(".%s", str
);
1078 d
->d
= g_strdup(str
);
1081 if (RB_INSERT(domain_list
, wl
, d
))
1084 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1095 add_cookie_wl(struct settings
*s
, char *entry
)
1097 wl_add(entry
, &c_wl
, 1);
1102 walk_cookie_wl(struct settings
*s
,
1103 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1107 if (s
== NULL
|| cb
== NULL
)
1108 errx(1, "walk_cookie_wl");
1110 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1111 cb(s
, d
->d
, cb_args
);
1115 walk_js_wl(struct settings
*s
,
1116 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1120 if (s
== NULL
|| cb
== NULL
)
1121 errx(1, "walk_js_wl");
1123 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1124 cb(s
, d
->d
, cb_args
);
1128 add_js_wl(struct settings
*s
, char *entry
)
1130 wl_add(entry
, &js_wl
, 1 /* persistent */);
1135 wl_find(const gchar
*search
, struct domain_list
*wl
)
1138 struct domain
*d
= NULL
, dfind
;
1141 if (search
== NULL
|| wl
== NULL
)
1143 if (strlen(search
) < 2)
1146 if (search
[0] != '.')
1147 s
= g_strdup_printf(".%s", search
);
1149 s
= g_strdup(search
);
1151 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1154 d
= RB_FIND(domain_list
, wl
, &dfind
);
1168 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1174 if (s
== NULL
|| wl
== NULL
)
1177 if (!strncmp(s
, "http://", strlen("http://")))
1178 s
= &s
[strlen("http://")];
1179 else if (!strncmp(s
, "https://", strlen("https://")))
1180 s
= &s
[strlen("https://")];
1185 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1186 /* chop string at first slash */
1187 if (s
[i
] == '/' || s
[i
] == '\0') {
1190 r
= wl_find(ss
, wl
);
1199 get_toplevel_domain(char *domain
)
1206 if (strlen(domain
) < 2)
1209 s
= &domain
[strlen(domain
) - 1];
1210 while (s
!= domain
) {
1227 config_parse(char *filename
, int runtime
)
1230 char *line
, *cp
, *var
, *val
;
1231 size_t len
, lineno
= 0;
1233 char **s
, file
[PATH_MAX
];
1236 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1238 if (filename
== NULL
)
1241 if (runtime
&& runtime_settings
[0] != '\0') {
1242 snprintf(file
, sizeof file
, "%s/%s",
1243 work_dir
, runtime_settings
);
1244 if (stat(file
, &sb
)) {
1245 warnx("runtime file doesn't exist, creating it");
1246 if ((f
= fopen(file
, "w")) == NULL
)
1248 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1252 strlcpy(file
, filename
, sizeof file
);
1254 if ((config
= fopen(file
, "r")) == NULL
) {
1255 warn("config_parse: cannot open %s", filename
);
1260 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1261 if (feof(config
) || ferror(config
))
1265 cp
+= (long)strspn(cp
, WS
);
1266 if (cp
[0] == '\0') {
1272 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1275 cp
+= (long)strspn(cp
, WS
);
1277 if ((val
= strsep(&cp
, "\0")) == NULL
)
1280 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1283 for (i
= 0, handled
= 0; i
< LENGTH(rs
); i
++) {
1284 if (strcmp(var
, rs
[i
].name
))
1288 if (rs
[i
].s
->set(&rs
[i
], val
))
1289 errx(1, "invalid value for %s", var
);
1293 switch (rs
[i
].type
) {
1302 errx(1, "invalid sval for %s",
1311 errx(1, "invalid type for %s", var
);
1317 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1326 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1332 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1336 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1339 JSStringGetUTF8CString(jsref
, s
, l
);
1340 JSStringRelease(jsref
);
1346 disable_hints(struct tab
*t
)
1348 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1349 bzero(t
->hint_num
, sizeof t
->hint_num
);
1350 run_script(t
, "vimprobable_clear()");
1352 t
->hint_mode
= XT_HINT_NONE
;
1356 enable_hints(struct tab
*t
)
1358 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1359 run_script(t
, "vimprobable_show_hints()");
1361 t
->hint_mode
= XT_HINT_NONE
;
1364 #define XT_JS_OPEN ("open;")
1365 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1366 #define XT_JS_FIRE ("fire;")
1367 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1368 #define XT_JS_FOUND ("found;")
1369 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1372 run_script(struct tab
*t
, char *s
)
1374 JSGlobalContextRef ctx
;
1375 WebKitWebFrame
*frame
;
1377 JSValueRef val
, exception
;
1380 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1381 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1383 frame
= webkit_web_view_get_main_frame(t
->wv
);
1384 ctx
= webkit_web_frame_get_global_context(frame
);
1386 str
= JSStringCreateWithUTF8CString(s
);
1387 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1388 NULL
, 0, &exception
);
1389 JSStringRelease(str
);
1391 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1393 es
= js_ref_to_string(ctx
, exception
);
1394 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1398 es
= js_ref_to_string(ctx
, val
);
1399 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1401 /* handle return value right here */
1402 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1404 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1407 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1408 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1409 &es
[XT_JS_FIRE_LEN
]);
1414 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1415 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1426 hint(struct tab
*t
, struct karg
*args
)
1429 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1431 if (t
->hints_on
== 0)
1439 /* Doesn't work fully, due to the following bug:
1440 * https://bugs.webkit.org/show_bug.cgi?id=51747
1443 restore_global_history(void)
1445 char file
[PATH_MAX
];
1451 snprintf(file
, sizeof file
, "%s/%s/%s",
1452 pwd
->pw_dir
, XT_DIR
, XT_HISTORY_FILE
);
1454 if ((f
= fopen(file
, "r")) == NULL
) {
1455 warnx("%s: fopen", __func__
);
1460 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1461 if (feof(f
) || ferror(f
))
1464 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1465 if (feof(f
) || ferror(f
)) {
1467 warnx("%s: broken history file\n", __func__
);
1471 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1472 webkit_web_history_item_new_with_data(uri
, title
);
1473 h
= g_malloc(sizeof(struct history
));
1474 h
->uri
= g_strdup(uri
);
1475 h
->title
= g_strdup(title
);
1476 RB_INSERT(history_list
, &hl
, h
);
1478 warnx("%s: failed to restore history\n", __func__
);
1494 save_global_history_to_disk(struct tab
*t
)
1496 char file
[PATH_MAX
];
1500 snprintf(file
, sizeof file
, "%s/%s/%s",
1501 pwd
->pw_dir
, XT_DIR
, XT_HISTORY_FILE
);
1503 if ((f
= fopen(file
, "w")) == NULL
) {
1504 show_oops(t
, "%s: global history file: %s",
1505 __func__
, strerror(errno
));
1509 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1510 if (h
->uri
&& h
->title
)
1511 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
1520 quit(struct tab
*t
, struct karg
*args
)
1522 if (save_global_history
)
1523 save_global_history_to_disk(t
);
1531 open_tabs(struct tab
*t
, struct karg
*a
)
1533 char file
[PATH_MAX
];
1541 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1543 if ((f
= fopen(file
, "r")) == NULL
)
1547 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1548 if (feof(f
) || ferror(f
))
1551 if (uri
&& strlen(uri
))
1552 create_new_tab(uri
, NULL
, 1);
1567 restore_saved_tabs(void)
1569 char file
[PATH_MAX
];
1570 int unlink_file
= 0;
1574 snprintf(file
, sizeof file
, "%s/%s",
1575 sessions_dir
, XT_RESTART_TABS_FILE
);
1576 if (stat(file
, &sb
) == -1)
1577 a
.s
= XT_SAVED_TABS_FILE
;
1580 a
.s
= XT_RESTART_TABS_FILE
;
1583 open_tabs(NULL
, &a
);
1592 save_tabs(struct tab
*t
, struct karg
*a
)
1594 char file
[PATH_MAX
];
1597 WebKitWebFrame
*frame
;
1605 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1607 if ((f
= fopen(file
, "w")) == NULL
) {
1608 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
1612 TAILQ_FOREACH(ti
, &tabs
, entry
) {
1613 frame
= webkit_web_view_get_main_frame(ti
->wv
);
1614 uri
= webkit_web_frame_get_uri(frame
);
1615 if (uri
&& strlen(uri
) > 0)
1616 fprintf(f
, "%s\n", uri
);
1625 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
1629 a
.s
= XT_SAVED_TABS_FILE
;
1637 yank_uri(struct tab
*t
, struct karg
*args
)
1639 WebKitWebFrame
*frame
;
1641 GtkClipboard
*clipboard
;
1643 frame
= webkit_web_view_get_main_frame(t
->wv
);
1644 uri
= webkit_web_frame_get_uri(frame
);
1648 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1649 gtk_clipboard_set_text(clipboard
, uri
, -1);
1660 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
1662 struct paste_args
*pap
;
1667 pap
= (struct paste_args
*)data
;
1670 case XT_PASTE_CURRENT_TAB
:
1671 webkit_web_view_load_uri(pap
->t
->wv
, text
);
1673 case XT_PASTE_NEW_TAB
:
1674 create_new_tab((char *)text
, NULL
, 1);
1682 paste_uri(struct tab
*t
, struct karg
*args
)
1684 GtkClipboard
*clipboard
;
1685 struct paste_args
*pap
;
1687 pap
= g_malloc(sizeof(struct paste_args
));
1692 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1693 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
1699 find_domain(const char *s
, int add_dot
)
1702 char *r
= NULL
, *ss
= NULL
;
1707 if (!strncmp(s
, "http://", strlen("http://")))
1708 s
= &s
[strlen("http://")];
1709 else if (!strncmp(s
, "https://", strlen("https://")))
1710 s
= &s
[strlen("https://")];
1716 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
1717 /* chop string at first slash */
1718 if (ss
[i
] == '/' || ss
[i
] == '\0') {
1721 r
= g_strdup_printf(".%s", ss
);
1732 toggle_cwl(struct tab
*t
, struct karg
*args
)
1734 WebKitWebFrame
*frame
;
1737 char *dom
= NULL
, *dom_toggle
= NULL
;
1743 frame
= webkit_web_view_get_main_frame(t
->wv
);
1744 uri
= (char *)webkit_web_frame_get_uri(frame
);
1745 dom
= find_domain(uri
, 1);
1746 d
= wl_find(dom
, &c_wl
);
1752 if (args
->i
& XT_WL_TOGGLE
)
1754 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
1756 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
1759 if (args
->i
& XT_WL_TOPLEVEL
)
1760 dom_toggle
= get_toplevel_domain(dom
);
1765 /* enable cookies for domain */
1766 wl_add(dom_toggle
, &c_wl
, 0);
1768 /* disable cookies for domain */
1769 RB_REMOVE(domain_list
, &c_wl
, d
);
1772 webkit_web_view_reload(t
->wv
);
1779 toggle_js(struct tab
*t
, struct karg
*args
)
1782 WebKitWebFrame
*frame
;
1785 char *dom
= NULL
, *dom_toggle
= NULL
;
1790 g_object_get((GObject
*)t
->settings
,
1791 "enable-scripts", &es
, (char *)NULL
);
1792 if (args
->i
& XT_WL_TOGGLE
)
1794 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
1796 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
1801 frame
= webkit_web_view_get_main_frame(t
->wv
);
1802 uri
= (char *)webkit_web_frame_get_uri(frame
);
1803 dom
= find_domain(uri
, 1);
1804 if (uri
== NULL
|| dom
== NULL
) {
1805 show_oops(t
, "Can't toggle domain in JavaScript white list");
1809 if (args
->i
& XT_WL_TOPLEVEL
)
1810 dom_toggle
= get_toplevel_domain(dom
);
1815 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
1816 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
1818 d
= wl_find(dom_toggle
, &js_wl
);
1820 RB_REMOVE(domain_list
, &js_wl
, d
);
1821 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
1823 g_object_set((GObject
*)t
->settings
,
1824 "enable-scripts", es
, (char *)NULL
);
1825 webkit_web_view_set_settings(t
->wv
, t
->settings
);
1826 webkit_web_view_reload(t
->wv
);
1834 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
1838 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
1843 toggle_src(struct tab
*t
, struct karg
*args
)
1850 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
1851 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
1852 webkit_web_view_reload(t
->wv
);
1858 focus(struct tab
*t
, struct karg
*args
)
1860 if (t
== NULL
|| args
== NULL
)
1863 if (args
->i
== XT_FOCUS_URI
)
1864 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
1865 else if (args
->i
== XT_FOCUS_SEARCH
)
1866 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
1872 stats(struct tab
*t
, struct karg
*args
)
1874 char *stats
, *s
, line
[64 * 1024];
1875 uint64_t line_count
= 0;
1882 if (save_rejected_cookies
) {
1883 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
1885 s
= fgets(line
, sizeof line
, r_cookie_f
);
1886 if (s
== NULL
|| feof(r_cookie_f
) ||
1892 snprintf(line
, sizeof line
,
1893 "<br>Cookies blocked(*) total: %llu", line_count
);
1895 show_oops(t
, "Can't open blocked cookies file: %s",
1899 stats
= g_strdup_printf(XT_DOCTYPE
1902 "<title>Statistics</title>"
1904 "<h1>Statistics</h1>"
1906 "Cookies blocked(*) this session: %llu"
1908 "<p><small><b>*</b> results vary based on settings"
1914 load_webkit_string(t
, stats
);
1921 about(struct tab
*t
, struct karg
*args
)
1928 about
= g_strdup_printf(XT_DOCTYPE
1931 "<title>About</title>"
1935 "<b>Version: %s</b><p>"
1938 "<li>Marco Peereboom <marco@peereboom.us></li>"
1939 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
1940 "<li>Edd Barrett <vext01@gmail.com> </li>"
1942 "Copyrights and licenses can be found on the XXXterm "
1943 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
1949 load_webkit_string(t
, about
);
1956 help(struct tab
*t
, struct karg
*args
)
1966 "<title>XXXterm</title>"
1967 "<meta http-equiv=\"REFRESH\" content=\"0;"
1968 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
1971 "XXXterm man page <a href=\"http://opensource.conformal.com/"
1972 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
1973 "cgi-bin/man-cgi?xxxterm</a>"
1978 load_webkit_string(t
, help
);
1984 * update all favorite tabs apart from one. Pass NULL if
1985 * you want to update all.
1988 update_favorite_tabs(struct tab
*apart_from
)
1991 if (!updating_fl_tabs
) {
1992 updating_fl_tabs
= 1; /* stop infinite recursion */
1993 TAILQ_FOREACH(t
, &tabs
, entry
)
1994 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
1995 && (t
!= apart_from
))
1996 xtp_page_fl(t
, NULL
);
1997 updating_fl_tabs
= 0;
2001 /* show a list of favorites (bookmarks) */
2003 xtp_page_fl(struct tab
*t
, struct karg
*args
)
2005 char file
[PATH_MAX
];
2007 char *uri
= NULL
, *title
= NULL
;
2008 size_t len
, lineno
= 0;
2010 char *header
, *body
, *tmp
, *html
= NULL
;
2012 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
2015 warn("%s: bad param", __func__
);
2017 /* mark tab as favorite list */
2018 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
2020 /* new session key */
2021 if (!updating_fl_tabs
)
2022 generate_xtp_session_key(&fl_session_key
);
2024 /* open favorites */
2025 snprintf(file
, sizeof file
, "%s/%s/%s",
2026 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
2027 if ((f
= fopen(file
, "r")) == NULL
) {
2028 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2033 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2034 "<title>Favorites</title>\n"
2037 "<h1>Favorites</h1>\n",
2041 body
= g_strdup_printf("<div align='center'><table><tr>"
2042 "<th style='width: 4%%'>#</th><th>Link</th>"
2043 "<th style='width: 15%%'>Remove</th></tr>\n");
2046 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2047 if (feof(f
) || ferror(f
))
2055 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2056 if (feof(f
) || ferror(f
)) {
2057 errx(0, "%s: can't parse favorites\n",
2064 body
= g_strdup_printf("%s<tr>"
2066 "<td><a href='%s'>%s</a></td>"
2067 "<td style='text-align: center'>"
2068 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2070 body
, i
, uri
, title
,
2071 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2083 /* if none, say so */
2086 body
= g_strdup_printf("%s<tr>"
2087 "<td colspan='3' style='text-align: center'>"
2088 "No favorites - To add one use the 'favadd' command."
2089 "</td></tr>", body
);
2100 html
= g_strdup_printf("%s%s</table></div></html>",
2102 load_webkit_string(t
, html
);
2105 update_favorite_tabs(t
);
2118 getparams(char *cmd
, char *cmp
)
2123 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2124 rv
= cmd
+ strlen(cmp
);
2127 if (strlen(rv
) == 0)
2136 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2137 size_t cert_count
, char *title
)
2139 gnutls_datum_t cinfo
;
2140 char *tmp
, *header
, *body
, *footer
;
2143 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2144 footer
= g_strdup("</body></html>");
2145 body
= g_strdup("");
2147 for (i
= 0; i
< cert_count
; i
++) {
2148 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2153 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2154 body
, i
, cinfo
.data
);
2155 gnutls_free(cinfo
.data
);
2159 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2163 load_webkit_string(t
, tmp
);
2168 ca_cmd(struct tab
*t
, struct karg
*args
)
2171 int rv
= 1, certs
= 0, certs_read
;
2174 gnutls_x509_crt_t
*c
= NULL
;
2175 char *certs_buf
= NULL
, *s
;
2177 /* yeah yeah stat race */
2178 if (stat(ssl_ca_file
, &sb
)) {
2179 show_oops(t
, "no CA file: %s", ssl_ca_file
);
2183 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2184 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2188 certs_buf
= g_malloc(sb
.st_size
+ 1);
2189 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2190 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2193 certs_buf
[sb
.st_size
] = '\0';
2196 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2198 s
+= strlen("BEGIN CERTIFICATE");
2201 bzero(&dt
, sizeof dt
);
2202 dt
.data
= certs_buf
;
2203 dt
.size
= sb
.st_size
;
2204 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2205 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
, GNUTLS_X509_FMT_PEM
, 0);
2206 if (certs_read
<= 0) {
2207 show_oops(t
, "No cert(s) available");
2210 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2223 connect_socket_from_uri(char *uri
, char *domain
, size_t domain_sz
)
2226 struct addrinfo hints
, *res
= NULL
, *ai
;
2230 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2233 su
= soup_uri_new(uri
);
2236 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2239 snprintf(port
, sizeof port
, "%d", su
->port
);
2240 bzero(&hints
, sizeof(struct addrinfo
));
2241 hints
.ai_flags
= AI_CANONNAME
;
2242 hints
.ai_family
= AF_UNSPEC
;
2243 hints
.ai_socktype
= SOCK_STREAM
;
2245 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2248 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2249 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2252 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2255 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2259 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2264 strlcpy(domain
, su
->host
, domain_sz
);
2275 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2278 gnutls_deinit(gsession
);
2280 gnutls_certificate_free_credentials(xcred
);
2286 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2287 gnutls_certificate_credentials_t
*xc
)
2289 gnutls_certificate_credentials_t xcred
;
2290 gnutls_session_t gsession
;
2293 if (gs
== NULL
|| xc
== NULL
)
2299 gnutls_certificate_allocate_credentials(&xcred
);
2300 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2301 GNUTLS_X509_FMT_PEM
);
2302 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2303 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2304 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2305 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2306 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2307 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2309 gnutls_error_is_fatal(rv
),
2310 gnutls_strerror_name(rv
));
2311 stop_tls(gsession
, xcred
);
2315 gnutls_credentials_type_t cred
;
2316 cred
= gnutls_auth_get_type(gsession
);
2317 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2318 stop_tls(gsession
, xcred
);
2330 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2334 const gnutls_datum_t
*cl
;
2335 gnutls_x509_crt_t
*all_certs
;
2338 if (certs
== NULL
|| cert_count
== NULL
)
2340 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2342 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2346 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2347 for (i
= 0; i
< len
; i
++) {
2348 gnutls_x509_crt_init(&all_certs
[i
]);
2349 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2350 GNUTLS_X509_FMT_PEM
< 0)) {
2364 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2368 for (i
= 0; i
< cert_count
; i
++)
2369 gnutls_x509_crt_deinit(certs
[i
]);
2374 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2375 size_t cert_count
, char *domain
)
2378 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2383 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2386 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2387 if ((f
= fopen(file
, "w")) == NULL
) {
2388 show_oops(t
, "Can't create cert file %s %s",
2389 file
, strerror(errno
));
2393 for (i
= 0; i
< cert_count
; i
++) {
2394 cert_buf_sz
= sizeof cert_buf
;
2395 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2396 cert_buf
, &cert_buf_sz
)) {
2397 show_oops(t
, "gnutls_x509_crt_export failed");
2400 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2401 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2406 /* not the best spot but oh well */
2407 gdk_color_parse("lightblue", &color
);
2408 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2414 load_compare_cert(struct tab
*t
, struct karg
*args
)
2416 WebKitWebFrame
*frame
;
2417 char *uri
, domain
[8182], file
[PATH_MAX
];
2418 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2419 int s
= -1, rv
= 1, i
;
2423 gnutls_session_t gsession
;
2424 gnutls_x509_crt_t
*certs
;
2425 gnutls_certificate_credentials_t xcred
;
2430 frame
= webkit_web_view_get_main_frame(t
->wv
);
2431 uri
= (char *)webkit_web_frame_get_uri(frame
);
2432 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2436 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2437 show_oops(t
, "Start TLS failed");
2442 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2443 show_oops(t
, "Can't get connection certificates");
2447 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2448 if ((f
= fopen(file
, "r")) == NULL
)
2451 for (i
= 0; i
< cert_count
; i
++) {
2452 cert_buf_sz
= sizeof cert_buf
;
2453 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2454 cert_buf
, &cert_buf_sz
)) {
2457 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2458 rv
= -1; /* critical */
2461 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
2462 rv
= -1; /* critical */
2471 free_connection_certs(certs
, cert_count
);
2473 /* we close the socket first for speed */
2476 stop_tls(gsession
, xcred
);
2482 cert_cmd(struct tab
*t
, struct karg
*args
)
2484 WebKitWebFrame
*frame
;
2485 char *uri
, *action
, domain
[8182];
2488 gnutls_session_t gsession
;
2489 gnutls_x509_crt_t
*certs
;
2490 gnutls_certificate_credentials_t xcred
;
2495 if ((action
= getparams(args
->s
, "cert")))
2500 frame
= webkit_web_view_get_main_frame(t
->wv
);
2501 uri
= (char *)webkit_web_frame_get_uri(frame
);
2502 if (uri
&& strlen(uri
) == 0) {
2503 show_oops(t
, "Invalid URI");
2505 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
2506 show_oops(t
, "Invalid certidicate URI: %s", uri
);
2511 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2512 show_oops(t
, "Start TLS failed");
2517 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2518 show_oops(t
, "get_connection_certs failed");
2522 if (!strcmp(action
, "show"))
2523 show_certs(t
, certs
, cert_count
, "Certificate Chain");
2524 else if (!strcmp(action
, "save"))
2525 save_certs(t
, certs
, cert_count
, domain
);
2527 show_oops(t
, "Invalid command: %s", action
);
2529 free_connection_certs(certs
, cert_count
);
2531 /* we close the socket first for speed */
2534 stop_tls(gsession
, xcred
);
2540 remove_cookie(int index
)
2546 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
2548 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2550 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
2554 print_cookie("remove cookie", c
);
2555 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
2560 soup_cookies_free(cf
);
2566 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
2569 char *tmp
, *header
, *body
, *footer
;
2570 int p_js
= 0, s_js
= 0;
2572 /* we set this to indicate we want to manually do navaction */
2573 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2575 if (g_str_has_prefix(args
, "show a") ||
2576 !strcmp(args
, "show")) {
2580 } else if (g_str_has_prefix(args
, "show p")) {
2581 /* show persistent */
2583 } else if (g_str_has_prefix(args
, "show s")) {
2589 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
2591 footer
= g_strdup("</body></html>");
2592 body
= g_strdup("");
2597 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
2599 RB_FOREACH(d
, domain_list
, wl
) {
2603 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
2611 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
2613 RB_FOREACH(d
, domain_list
, wl
) {
2617 body
= g_strdup_printf("%s%s", body
, d
->d
);
2622 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2626 load_webkit_string(t
, tmp
);
2632 wl_save(struct tab
*t
, struct karg
*args
, int js
)
2634 char file
[PATH_MAX
];
2636 char *line
= NULL
, *lt
= NULL
;
2638 WebKitWebFrame
*frame
;
2639 char *dom
= NULL
, *uri
, *dom_save
= NULL
;
2646 if (t
== NULL
|| args
== NULL
)
2649 if (runtime_settings
[0] == '\0')
2652 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
2653 if ((f
= fopen(file
, "r+")) == NULL
)
2656 frame
= webkit_web_view_get_main_frame(t
->wv
);
2657 uri
= (char *)webkit_web_frame_get_uri(frame
);
2658 dom
= find_domain(uri
, 1);
2659 if (uri
== NULL
|| dom
== NULL
) {
2660 show_oops(t
, "Can't add domain to %s white list",
2661 js
? "JavaScript" : "cookie");
2665 if (g_str_has_prefix(args
->s
, "save d")) {
2667 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
2668 show_oops(t
, "invalid domain: %s", dom
);
2671 flags
= XT_WL_TOPLEVEL
;
2672 } else if (g_str_has_prefix(args
->s
, "save f") ||
2673 !strcmp(args
->s
, "save")) {
2678 show_oops(t
, "invalid command: %s", args
->s
);
2682 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
2685 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
2688 if (!strcmp(line
, lt
))
2694 fprintf(f
, "%s\n", lt
);
2699 d
= wl_find(dom_save
, &js_wl
);
2702 d
= wl_find(dom_save
, &c_wl
);
2705 /* find and add to persistent jar */
2706 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2707 for (;cf
; cf
= cf
->next
) {
2709 if (!strcmp(dom_save
, ci
->domain
) ||
2710 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
2711 c
= soup_cookie_copy(ci
);
2712 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
2715 soup_cookies_free(cf
);
2733 cookie_cmd(struct tab
*t
, struct karg
*args
)
2738 if ((cmd
= getparams(args
->s
, "cookie")))
2744 if (g_str_has_prefix(cmd
, "show")) {
2745 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
2746 } else if (g_str_has_prefix(cmd
, "save")) {
2749 } else if (g_str_has_prefix(cmd
, "toggle")) {
2751 if (g_str_has_prefix(cmd
, "toggle d"))
2752 a
.i
|= XT_WL_TOPLEVEL
;
2756 } else if (g_str_has_prefix(cmd
, "delete")) {
2757 show_oops(t
, "'cookie delete' currently unimplemented");
2759 show_oops(t
, "unknown cookie command: %s", cmd
);
2765 js_cmd(struct tab
*t
, struct karg
*args
)
2770 if ((cmd
= getparams(args
->s
, "js")))
2775 if (g_str_has_prefix(cmd
, "show")) {
2776 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
2777 } else if (g_str_has_prefix(cmd
, "save")) {
2780 } else if (g_str_has_prefix(cmd
, "toggle")) {
2782 if (g_str_has_prefix(cmd
, "toggle d"))
2783 a
.i
|= XT_WL_TOPLEVEL
;
2787 } else if (g_str_has_prefix(cmd
, "delete")) {
2788 show_oops(t
, "'js delete' currently unimplemented");
2790 show_oops(t
, "unknown js command: %s", cmd
);
2796 add_favorite(struct tab
*t
, struct karg
*args
)
2798 char file
[PATH_MAX
];
2801 size_t urilen
, linelen
;
2802 WebKitWebFrame
*frame
;
2803 const gchar
*uri
, *title
;
2808 /* don't allow adding of xtp pages to favorites */
2809 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
2810 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
2814 snprintf(file
, sizeof file
, "%s/%s/%s",
2815 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
2816 if ((f
= fopen(file
, "r+")) == NULL
) {
2817 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2821 title
= webkit_web_view_get_title(t
->wv
);
2822 frame
= webkit_web_view_get_main_frame(t
->wv
);
2823 uri
= webkit_web_frame_get_uri(frame
);
2827 if (title
== NULL
|| uri
== NULL
) {
2828 show_oops(t
, "can't add page to favorites");
2832 urilen
= strlen(uri
);
2835 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
2836 if (linelen
== urilen
&& !strcmp(line
, uri
))
2842 fprintf(f
, "\n%s\n%s", title
, uri
);
2848 update_favorite_tabs(NULL
);
2854 navaction(struct tab
*t
, struct karg
*args
)
2856 WebKitWebHistoryItem
*item
;
2858 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
2859 t
->tab_id
, args
->i
);
2862 if (args
->i
== XT_NAV_BACK
)
2863 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2865 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
2867 return (XT_CB_PASSTHROUGH
);;
2868 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
2870 return (XT_CB_PASSTHROUGH
);
2875 webkit_web_view_go_back(t
->wv
);
2877 case XT_NAV_FORWARD
:
2878 webkit_web_view_go_forward(t
->wv
);
2881 webkit_web_view_reload(t
->wv
);
2883 case XT_NAV_RELOAD_CACHE
:
2884 webkit_web_view_reload_bypass_cache(t
->wv
);
2887 return (XT_CB_PASSTHROUGH
);
2891 move(struct tab
*t
, struct karg
*args
)
2893 GtkAdjustment
*adjust
;
2894 double pi
, si
, pos
, ps
, upper
, lower
, max
;
2899 case XT_MOVE_BOTTOM
:
2901 case XT_MOVE_PAGEDOWN
:
2902 case XT_MOVE_PAGEUP
:
2903 case XT_MOVE_HALFDOWN
:
2904 case XT_MOVE_HALFUP
:
2905 adjust
= t
->adjust_v
;
2908 adjust
= t
->adjust_h
;
2912 pos
= gtk_adjustment_get_value(adjust
);
2913 ps
= gtk_adjustment_get_page_size(adjust
);
2914 upper
= gtk_adjustment_get_upper(adjust
);
2915 lower
= gtk_adjustment_get_lower(adjust
);
2916 si
= gtk_adjustment_get_step_increment(adjust
);
2917 pi
= gtk_adjustment_get_page_increment(adjust
);
2920 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
2921 "max %f si %f pi %f\n",
2922 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
2923 pos
, ps
, upper
, lower
, max
, si
, pi
);
2929 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2934 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2936 case XT_MOVE_BOTTOM
:
2937 case XT_MOVE_FARRIGHT
:
2938 gtk_adjustment_set_value(adjust
, max
);
2941 case XT_MOVE_FARLEFT
:
2942 gtk_adjustment_set_value(adjust
, lower
);
2944 case XT_MOVE_PAGEDOWN
:
2946 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2948 case XT_MOVE_PAGEUP
:
2950 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2952 case XT_MOVE_HALFDOWN
:
2954 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2956 case XT_MOVE_HALFUP
:
2958 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2961 return (XT_CB_PASSTHROUGH
);
2964 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
2966 return (XT_CB_HANDLED
);
2970 tabaction(struct tab
*t
, struct karg
*args
)
2972 int rv
= XT_CB_HANDLED
;
2973 char *url
= NULL
, *newuri
= NULL
;
2976 DNPRINTF(XT_D_TAB
, "tabaction: %p %d %d\n", t
, args
->i
, t
->focus_wv
);
2979 return (XT_CB_PASSTHROUGH
);
2983 if ((url
= getparams(args
->s
, "tabnew")))
2984 create_new_tab(url
, NULL
, 1);
2986 create_new_tab(NULL
, NULL
, 1);
2991 case XT_TAB_DELQUIT
:
2992 if (gtk_notebook_get_n_pages(notebook
) > 1)
2998 if ((url
= getparams(args
->s
, "open")) ||
2999 ((url
= getparams(args
->s
, "op"))) ||
3000 ((url
= getparams(args
->s
, "o"))))
3003 rv
= XT_CB_PASSTHROUGH
;
3007 if (valid_url_type(url
)) {
3008 newuri
= guess_url_type(url
);
3011 webkit_web_view_load_uri(t
->wv
, url
);
3015 case XT_TAB_UNDO_CLOSE
:
3016 if (undo_count
== 0) {
3017 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
3021 u
= TAILQ_FIRST(&undos
);
3022 create_new_tab(u
->uri
, u
, 1);
3024 TAILQ_REMOVE(&undos
, u
, entry
);
3026 /* u->history is freed in create_new_tab() */
3031 rv
= XT_CB_PASSTHROUGH
;
3045 resizetab(struct tab
*t
, struct karg
*args
)
3047 if (t
== NULL
|| args
== NULL
)
3048 errx(1, "resizetab");
3050 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3051 t
->tab_id
, args
->i
);
3053 adjustfont_webkit(t
, args
->i
);
3055 return (XT_CB_HANDLED
);
3059 movetab(struct tab
*t
, struct karg
*args
)
3064 if (t
== NULL
|| args
== NULL
)
3067 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3068 t
->tab_id
, args
->i
);
3070 if (args
->i
== XT_TAB_INVALID
)
3071 return (XT_CB_PASSTHROUGH
);
3073 if (args
->i
< XT_TAB_INVALID
) {
3074 /* next or previous tab */
3075 if (TAILQ_EMPTY(&tabs
))
3076 return (XT_CB_PASSTHROUGH
);
3080 /* if at the last page, loop around to the first */
3081 if (gtk_notebook_get_current_page(notebook
) ==
3082 gtk_notebook_get_n_pages(notebook
) - 1) {
3083 gtk_notebook_set_current_page(notebook
, 0);
3085 gtk_notebook_next_page(notebook
);
3089 /* if at the first page, loop around to the last */
3090 if (gtk_notebook_current_page(notebook
) == 0) {
3091 gtk_notebook_set_current_page(notebook
,
3092 gtk_notebook_get_n_pages(notebook
) - 1);
3094 gtk_notebook_prev_page(notebook
);
3098 gtk_notebook_set_current_page(notebook
, 0);
3101 gtk_notebook_set_current_page(notebook
, -1);
3104 return (XT_CB_PASSTHROUGH
);
3107 return (XT_CB_HANDLED
);
3112 if (t
->tab_id
== x
) {
3113 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3114 return (XT_CB_HANDLED
);
3117 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3118 if (tt
->tab_id
== x
) {
3119 gtk_notebook_set_current_page(notebook
, x
);
3120 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3122 gtk_widget_grab_focus(GTK_WIDGET(tt
->wv
));
3126 return (XT_CB_HANDLED
);
3130 command(struct tab
*t
, struct karg
*args
)
3132 WebKitWebFrame
*frame
;
3133 char *s
= NULL
, *ss
= NULL
;
3137 if (t
== NULL
|| args
== NULL
)
3156 case XT_CMD_OPEN_CURRENT
:
3159 case XT_CMD_TABNEW_CURRENT
:
3160 if (!s
) /* FALL THROUGH? */
3162 frame
= webkit_web_view_get_main_frame(t
->wv
);
3163 uri
= webkit_web_frame_get_uri(frame
);
3164 if (uri
&& strlen(uri
)) {
3165 ss
= g_strdup_printf("%s%s", s
, uri
);
3170 show_oops(t
, "command: invalid opcode %d", args
->i
);
3171 return (XT_CB_PASSTHROUGH
);
3174 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3176 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3177 gdk_color_parse("white", &color
);
3178 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3180 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3181 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3186 return (XT_CB_HANDLED
);
3190 * Return a new string with a download row (in html)
3191 * appended. Old string is freed.
3194 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3197 WebKitDownloadStatus stat
;
3198 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3200 char cur_sz
[FMT_SCALED_STRSIZE
];
3201 char tot_sz
[FMT_SCALED_STRSIZE
];
3204 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3206 /* All actions wil take this form:
3207 * xxxt://class/seskey
3209 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3210 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3212 stat
= webkit_download_get_status(dl
->download
);
3215 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3216 status_html
= g_strdup_printf("Finished");
3217 cmd_html
= g_strdup_printf(
3218 "<a href='%s%d/%d'>Remove</a>",
3219 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3221 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3222 /* gather size info */
3223 progress
= 100 * webkit_download_get_progress(dl
->download
);
3226 webkit_download_get_current_size(dl
->download
), cur_sz
);
3228 webkit_download_get_total_size(dl
->download
), tot_sz
);
3230 status_html
= g_strdup_printf("%s of %s (%.2f%%)", cur_sz
,
3232 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3233 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3237 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3238 status_html
= g_strdup_printf("Cancelled");
3239 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3240 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3242 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3243 status_html
= g_strdup_printf("Error!");
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_CREATED
:
3248 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3249 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3250 status_html
= g_strdup_printf("Starting");
3253 show_oops(t
, "%s: unknown download status", __func__
);
3256 new_html
= g_strdup_printf(
3257 "%s\n<tr><td>%s</td><td>%s</td>"
3258 "<td style='text-align:center'>%s</td></tr>\n",
3259 html
, webkit_download_get_uri(dl
->download
),
3260 status_html
, cmd_html
);
3264 g_free(status_html
);
3275 * update all download tabs apart from one. Pass NULL if
3276 * you want to update all.
3279 update_download_tabs(struct tab
*apart_from
)
3282 if (!updating_dl_tabs
) {
3283 updating_dl_tabs
= 1; /* stop infinite recursion */
3284 TAILQ_FOREACH(t
, &tabs
, entry
)
3285 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3286 && (t
!= apart_from
))
3287 xtp_page_dl(t
, NULL
);
3288 updating_dl_tabs
= 0;
3293 * update all cookie tabs apart from one. Pass NULL if
3294 * you want to update all.
3297 update_cookie_tabs(struct tab
*apart_from
)
3300 if (!updating_cl_tabs
) {
3301 updating_cl_tabs
= 1; /* stop infinite recursion */
3302 TAILQ_FOREACH(t
, &tabs
, entry
)
3303 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3304 && (t
!= apart_from
))
3305 xtp_page_cl(t
, NULL
);
3306 updating_cl_tabs
= 0;
3311 * update all history tabs apart from one. Pass NULL if
3312 * you want to update all.
3315 update_history_tabs(struct tab
*apart_from
)
3319 if (!updating_hl_tabs
) {
3320 updating_hl_tabs
= 1; /* stop infinite recursion */
3321 TAILQ_FOREACH(t
, &tabs
, entry
)
3322 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
3323 && (t
!= apart_from
))
3324 xtp_page_hl(t
, NULL
);
3325 updating_hl_tabs
= 0;
3329 /* cookie management XTP page */
3331 xtp_page_cl(struct tab
*t
, struct karg
*args
)
3333 char *header
, *body
, *footer
, *page
, *tmp
;
3334 int i
= 1; /* all ids start 1 */
3335 GSList
*sc
, *pc
, *pc_start
;
3339 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3342 errx(1, "%s: null tab", __func__
);
3344 /* mark this tab as cookie jar */
3345 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
3347 /* Generate a new session key */
3348 if (!updating_cl_tabs
)
3349 generate_xtp_session_key(&cl_session_key
);
3352 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3353 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
3354 "</head><body><h1>Cookie Jar</h1>\n");
3357 body
= g_strdup_printf("<div align='center'><table><tr>"
3365 "<th>HTTP_only</th>"
3366 "<th>Remove</th></tr>\n");
3368 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
3369 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
3372 for (; sc
; sc
= sc
->next
) {
3376 for (pc
= pc_start
; pc
; pc
= pc
->next
)
3377 if (soup_cookie_equal(pc
->data
, c
)) {
3378 type
= "Session + Persistent";
3383 body
= g_strdup_printf(
3385 "<td style='width: 3%%; text-align: center'>%s</td>"
3386 "<td style='width: 10%%; word-break: break-all'>%s</td>"
3387 "<td style='width: 20%%; word-break: break-all'>%s</td>"
3388 "<td style='width: 10%%; word-break: break-all'>%s</td>"
3389 "<td style='width: 8%%; word-break: break-all'>%s</td>"
3390 "<td style='width: 12%%; word-break: break-all'>%s</td>"
3391 "<td style='width: 3%%; text-align: center'>%d</td>"
3392 "<td style='width: 3%%; text-align: center'>%d</td>"
3393 "<td style='width: 3%%; text-align: center'>"
3394 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3402 soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "",
3417 soup_cookies_free(sc
);
3418 soup_cookies_free(pc
);
3420 /* small message if there are none */
3423 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3424 "colspan='8'>No Cookies</td></tr>\n", body
);
3429 footer
= g_strdup_printf("</table></div></body></html>");
3431 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3437 load_webkit_string(t
, page
);
3438 update_cookie_tabs(t
);
3446 xtp_page_hl(struct tab
*t
, struct karg
*args
)
3448 char *header
, *body
, *footer
, *page
, *tmp
;
3450 int i
= 1; /* all ids start 1 */
3452 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3455 errx(1, "%s: null tab", __func__
);
3457 /* mark this tab as history manager */
3458 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
3460 /* Generate a new session key */
3461 if (!updating_hl_tabs
)
3462 generate_xtp_session_key(&hl_session_key
);
3465 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
3466 "<title>History</title>\n"
3469 "<h1>History</h1>\n",
3473 body
= g_strdup_printf("<div align='center'><table><tr>"
3474 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
3476 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
3478 body
= g_strdup_printf(
3480 "<td><a href='%s'>%s</a></td>"
3482 "<td style='text-align: center'>"
3483 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3484 body
, h
->uri
, h
->uri
, h
->title
,
3485 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
3486 XT_XTP_HL_REMOVE
, i
);
3492 /* small message if there are none */
3495 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3496 "colspan='3'>No History</td></tr>\n", body
);
3501 footer
= g_strdup_printf("</table></div></body></html>");
3503 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3506 * update all history manager tabs as the xtp session
3507 * key has now changed. No need to update the current tab.
3508 * Already did that above.
3510 update_history_tabs(t
);
3516 load_webkit_string(t
, page
);
3523 * Generate a web page detailing the status of any downloads
3526 xtp_page_dl(struct tab
*t
, struct karg
*args
)
3528 struct download
*dl
;
3529 char *header
, *body
, *footer
, *page
, *tmp
;
3533 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
3536 errx(1, "%s: null tab", __func__
);
3538 /* mark as a download manager tab */
3539 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
3542 * Generate a new session key for next page instance.
3543 * This only happens for the top level call to xtp_page_dl()
3544 * in which case updating_dl_tabs is 0.
3546 if (!updating_dl_tabs
)
3547 generate_xtp_session_key(&dl_session_key
);
3549 /* header - with refresh so as to update */
3550 if (refresh_interval
>= 1)
3551 ref
= g_strdup_printf(
3552 "<meta http-equiv='refresh' content='%u"
3553 ";url=%s%d/%s/%d' />\n",
3563 header
= g_strdup_printf(
3565 "<title>Downloads</title>\n%s%s</head>\n",
3566 XT_DOCTYPE XT_HTML_TAG
,
3570 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
3571 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
3572 "</p><table><tr><th style='width: 60%%'>"
3573 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
3574 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
3576 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
3577 body
= xtp_page_dl_row(t
, body
, dl
);
3581 /* message if no downloads in list */
3584 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
3585 " style='text-align: center'>"
3586 "No downloads</td></tr>\n", body
);
3591 footer
= g_strdup_printf("</table></div></body></html>");
3593 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3597 * update all download manager tabs as the xtp session
3598 * key has now changed. No need to update the current tab.
3599 * Already did that above.
3601 update_download_tabs(t
);
3608 load_webkit_string(t
, page
);
3615 search(struct tab
*t
, struct karg
*args
)
3619 if (t
== NULL
|| args
== NULL
)
3621 if (t
->search_text
== NULL
) {
3622 if (global_search
== NULL
)
3623 return (XT_CB_PASSTHROUGH
);
3625 t
->search_text
= g_strdup(global_search
);
3626 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
3627 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
3631 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
3632 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
3635 case XT_SEARCH_NEXT
:
3636 d
= t
->search_forward
;
3638 case XT_SEARCH_PREV
:
3639 d
= !t
->search_forward
;
3642 return (XT_CB_PASSTHROUGH
);
3645 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
3647 return (XT_CB_HANDLED
);
3650 struct settings_args
{
3656 print_setting(struct settings
*s
, char *val
, void *cb_args
)
3659 struct settings_args
*sa
= cb_args
;
3664 if (s
->flags
& XT_SF_RUNTIME
)
3670 *sa
->body
= g_strdup_printf(
3672 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
3673 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
3685 set(struct tab
*t
, struct karg
*args
)
3687 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
3689 struct settings_args sa
;
3691 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
3692 bzero(&sa
, sizeof sa
);
3696 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3697 "\n<head><title>Settings</title>\n"
3698 "</head><body><h1>Settings</h1>\n");
3701 body
= g_strdup_printf("<div align='center'><table><tr>"
3702 "<th align='left'>Setting</th>"
3703 "<th align='left'>Value</th></tr>\n");
3705 settings_walk(print_setting
, &sa
);
3708 /* small message if there are none */
3711 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3712 "colspan='2'>No settings</td></tr>\n", body
);
3717 footer
= g_strdup_printf("</table></div></body></html>");
3719 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3725 load_webkit_string(t
, page
);
3727 show_oops(t
, "Invalid command: %s", pars
);
3730 return (XT_CB_PASSTHROUGH
);
3734 session_save(struct tab
*t
, char *filename
, char **ret
)
3740 f
+= strlen("save");
3741 while (*f
== ' ' && *f
!= '\0')
3748 strlcpy(named_session
, f
, sizeof named_session
);
3757 session_open(struct tab
*t
, char *filename
, char **ret
)
3763 f
+= strlen("open");
3764 while (*f
== ' ' && *f
!= '\0')
3771 strlcpy(named_session
, f
, sizeof named_session
);
3780 session_cmd(struct tab
*t
, struct karg
*args
)
3782 char *action
= NULL
;
3783 char *filename
= NULL
;
3788 if ((action
= getparams(args
->s
, "session")))
3793 if (!strcmp(action
, "show"))
3794 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
3795 XT_SAVED_TABS_FILE
: named_session
);
3796 else if (g_str_has_prefix(action
, "save ")) {
3797 if (session_save(t
, action
, &filename
)) {
3798 show_oops(t
, "Can't save session: %s",
3799 filename
? filename
: "INVALID");
3802 } else if (g_str_has_prefix(action
, "open ")) {
3803 if (session_open(t
, action
, &filename
)) {
3804 show_oops(t
, "Can't open session: %s",
3805 filename
? filename
: "INVALID");
3809 show_oops(t
, "Invalid command: %s", action
);
3811 return (XT_CB_PASSTHROUGH
);
3815 * Make a hardcopy of the page
3818 print_page(struct tab
*t
, struct karg
*args
)
3820 WebKitWebFrame
*frame
;
3822 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
3825 * for now we just call the GTK print box,
3826 * but later we might decide to hook in a command.
3828 frame
= webkit_web_view_get_main_frame(t
->wv
);
3829 webkit_web_frame_print(frame
);
3835 go_home(struct tab
*t
, struct karg
*args
)
3839 newuri
= guess_url_type((char *)home
);
3840 webkit_web_view_load_uri(t
->wv
, newuri
);
3847 restart(struct tab
*t
, struct karg
*args
)
3851 a
.s
= XT_RESTART_TABS_FILE
;
3853 execvp(start_argv
[0], start_argv
);
3859 /* inherent to GTK not all keys will be caught at all times */
3860 /* XXX sort key bindings */
3861 struct key_bindings
{
3865 int (*func
)(struct tab
*, struct karg
*);
3868 { GDK_MOD1_MASK
, 0, GDK_d
, xtp_page_dl
, {0} },
3869 { GDK_MOD1_MASK
, 0, GDK_h
, xtp_page_hl
, {0} },
3870 { GDK_CONTROL_MASK
, 0, GDK_p
, print_page
, {0}},
3871 { 0, 0, GDK_slash
, command
, {.i
= '/'} },
3872 { GDK_SHIFT_MASK
, 0, GDK_question
, command
, {.i
= '?'} },
3873 { GDK_SHIFT_MASK
, 0, GDK_colon
, command
, {.i
= ':'} },
3874 { GDK_CONTROL_MASK
, 0, GDK_q
, quit
, {0} },
3875 { GDK_MOD1_MASK
, 0, GDK_q
, restart
, {0} },
3876 { GDK_CONTROL_MASK
, 0, GDK_j
, toggle_js
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
3877 { GDK_MOD1_MASK
, 0, GDK_c
, toggle_cwl
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
3878 { GDK_CONTROL_MASK
, 0, GDK_s
, toggle_src
, {0} },
3879 { 0, 0, GDK_y
, yank_uri
, {0} },
3880 { 0, 0, GDK_p
, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
} },
3881 { GDK_SHIFT_MASK
, 0, GDK_P
, paste_uri
, {.i
= XT_PASTE_NEW_TAB
} },
3884 { 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
3885 { GDK_SHIFT_MASK
, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
3888 { 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
3889 { 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
3891 /* command aliases (handy when -S flag is used) */
3892 { 0, 0, GDK_F9
, command
, {.i
= XT_CMD_OPEN
} },
3893 { 0, 0, GDK_F10
, command
, {.i
= XT_CMD_OPEN_CURRENT
} },
3894 { 0, 0, GDK_F11
, command
, {.i
= XT_CMD_TABNEW
} },
3895 { 0, 0, GDK_F12
, command
, {.i
= XT_CMD_TABNEW_CURRENT
} },
3898 { 0, 0, GDK_f
, hint
, {.i
= 0} },
3901 { 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
3902 { GDK_MOD1_MASK
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
3903 { GDK_SHIFT_MASK
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
3904 { GDK_MOD1_MASK
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
3905 { 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
3906 { GDK_CONTROL_MASK
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
3907 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_R
, navaction
, {.i
= XT_NAV_RELOAD_CACHE
} },
3908 { GDK_CONTROL_MASK
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
3909 { GDK_MOD1_MASK
, 1, GDK_f
, xtp_page_fl
, {0} },
3911 /* vertical movement */
3912 { 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
3913 { 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
3914 { 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
3915 { 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
3916 { GDK_SHIFT_MASK
, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
3917 { 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
3918 { 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
3919 { 0, 0, GDK_g
, move
, {.i
= XT_MOVE_TOP
} }, /* XXX make this work */
3920 { 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3921 { GDK_CONTROL_MASK
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3922 { GDK_CONTROL_MASK
, 0, GDK_d
, move
, {.i
= XT_MOVE_HALFDOWN
} },
3923 { 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3924 { 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
3925 { GDK_CONTROL_MASK
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
3926 { GDK_CONTROL_MASK
, 0, GDK_u
, move
, {.i
= XT_MOVE_HALFUP
} },
3927 /* horizontal movement */
3928 { 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
3929 { 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
3930 { 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
3931 { 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
3932 { GDK_SHIFT_MASK
, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
3933 { 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
3936 { GDK_CONTROL_MASK
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
3937 { GDK_CONTROL_MASK
, 1, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
3938 { GDK_SHIFT_MASK
, 0, GDK_U
, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
3939 { GDK_CONTROL_MASK
, 0, GDK_1
, movetab
, {.i
= 1} },
3940 { GDK_CONTROL_MASK
, 0, GDK_2
, movetab
, {.i
= 2} },
3941 { GDK_CONTROL_MASK
, 0, GDK_3
, movetab
, {.i
= 3} },
3942 { GDK_CONTROL_MASK
, 0, GDK_4
, movetab
, {.i
= 4} },
3943 { GDK_CONTROL_MASK
, 0, GDK_5
, movetab
, {.i
= 5} },
3944 { GDK_CONTROL_MASK
, 0, GDK_6
, movetab
, {.i
= 6} },
3945 { GDK_CONTROL_MASK
, 0, GDK_7
, movetab
, {.i
= 7} },
3946 { GDK_CONTROL_MASK
, 0, GDK_8
, movetab
, {.i
= 8} },
3947 { GDK_CONTROL_MASK
, 0, GDK_9
, movetab
, {.i
= 9} },
3948 { GDK_CONTROL_MASK
, 0, GDK_0
, movetab
, {.i
= 10} },
3949 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
3950 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
3951 { GDK_CONTROL_MASK
, 0, GDK_Left
, movetab
, {.i
= XT_TAB_PREV
} },
3952 { GDK_CONTROL_MASK
, 0, GDK_Right
, movetab
, {.i
= XT_TAB_NEXT
} },
3953 { GDK_CONTROL_MASK
, 0, GDK_minus
, resizetab
, {.i
= -1} },
3954 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_plus
, resizetab
, {.i
= 1} },
3955 { GDK_CONTROL_MASK
, 0, GDK_equal
, resizetab
, {.i
= 1} },
3961 int (*func
)(struct tab
*, struct karg
*);
3964 { "q!", 0, quit
, {0} },
3965 { "qa", 0, quit
, {0} },
3966 { "qa!", 0, quit
, {0} },
3967 { "w", 0, save_tabs
, {.s
= XT_SAVED_TABS_FILE
} },
3968 { "wq", 0, save_tabs_and_quit
, {.s
= XT_SAVED_TABS_FILE
} },
3969 { "wq!", 0, save_tabs_and_quit
, {.s
= XT_SAVED_TABS_FILE
} },
3970 { "help", 0, help
, {0} },
3971 { "about", 0, about
, {0} },
3972 { "stats", 0, stats
, {0} },
3973 { "version", 0, about
, {0} },
3974 { "cookies", 0, xtp_page_cl
, {0} },
3975 { "fav", 0, xtp_page_fl
, {0} },
3976 { "favadd", 0, add_favorite
, {0} },
3977 { "js", 2, js_cmd
, {0} },
3978 { "cookie", 2, cookie_cmd
, {0} },
3979 { "cert", 1, cert_cmd
, {0} },
3980 { "ca", 0, ca_cmd
, {0} },
3981 { "dl" , 0, xtp_page_dl
, {0} },
3982 { "h" , 0, xtp_page_hl
, {0} },
3983 { "hist" , 0, xtp_page_hl
, {0} },
3984 { "history" , 0, xtp_page_hl
, {0} },
3985 { "home" , 0, go_home
, {0} },
3986 { "restart" , 0, restart
, {0} },
3988 { "1", 0, move
, {.i
= XT_MOVE_TOP
} },
3989 { "print", 0, print_page
, {0} },
3992 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3993 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3994 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3995 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
3996 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
3997 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
3998 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
3999 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
4000 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4001 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
4002 /* XXX add count to these commands */
4003 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4004 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4005 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4006 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
4007 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
4008 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
4009 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
4010 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
4011 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4012 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
4015 { "set", 1, set
, {0} },
4018 { "session", 1, session_cmd
, {0} },
4022 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4030 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4032 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
4034 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
4041 * cancel, remove, etc. downloads
4044 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
4046 struct download find
, *d
;
4048 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
4050 /* some commands require a valid download id */
4051 if (cmd
!= XT_XTP_DL_LIST
) {
4052 /* lookup download in question */
4054 d
= RB_FIND(download_list
, &downloads
, &find
);
4057 show_oops(t
, "%s: no such download", __func__
);
4062 /* decide what to do */
4064 case XT_XTP_DL_CANCEL
:
4065 webkit_download_cancel(d
->download
);
4067 case XT_XTP_DL_REMOVE
:
4068 webkit_download_cancel(d
->download
); /* just incase */
4069 g_object_unref(d
->download
);
4070 RB_REMOVE(download_list
, &downloads
, d
);
4072 case XT_XTP_DL_LIST
:
4076 show_oops(t
, "%s: unknown command", __func__
);
4079 xtp_page_dl(t
, NULL
);
4083 * Actions on history, only does one thing for now, but
4084 * we provide the function for future actions
4087 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
4089 struct history
*h
, *next
;
4093 case XT_XTP_HL_REMOVE
:
4094 /* walk backwards, as listed in reverse */
4095 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
4096 next
= RB_PREV(history_list
, &hl
, h
);
4098 RB_REMOVE(history_list
, &hl
, h
);
4099 g_free((gpointer
) h
->title
);
4100 g_free((gpointer
) h
->uri
);
4107 case XT_XTP_HL_LIST
:
4108 /* Nothing - just xtp_page_hl() below */
4111 show_oops(t
, "%s: unknown command", __func__
);
4115 xtp_page_hl(t
, NULL
);
4118 /* remove a favorite */
4120 remove_favorite(struct tab
*t
, int index
)
4122 char file
[PATH_MAX
], *title
, *uri
;
4123 char *new_favs
, *tmp
;
4128 /* open favorites */
4129 snprintf(file
, sizeof file
, "%s/%s/%s",
4130 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
4132 if ((f
= fopen(file
, "r")) == NULL
) {
4133 show_oops(t
, "%s: can't open favorites: %s",
4134 __func__
, strerror(errno
));
4138 /* build a string which will become the new favroites file */
4139 new_favs
= g_strdup_printf("%s", "");
4142 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
4143 if (feof(f
) || ferror(f
))
4145 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
4152 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
4153 if (feof(f
) || ferror(f
)) {
4154 show_oops(t
, "%s: can't parse favorites %s",
4155 __func__
, strerror(errno
));
4160 /* as long as this isn't the one we are deleting add to file */
4163 new_favs
= g_strdup_printf("%s%s\n%s\n",
4164 new_favs
, title
, uri
);
4176 /* write back new favorites file */
4177 if ((f
= fopen(file
, "w")) == NULL
) {
4178 show_oops(t
, "%s: can't open favorites: %s",
4179 __func__
, strerror(errno
));
4183 fwrite(new_favs
, strlen(new_favs
), 1, f
);
4196 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
4199 case XT_XTP_FL_LIST
:
4200 /* nothing, just the below call to xtp_page_fl() */
4202 case XT_XTP_FL_REMOVE
:
4203 remove_favorite(t
, arg
);
4206 show_oops(t
, "%s: invalid favorites command", __func__
);
4210 xtp_page_fl(t
, NULL
);
4214 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
4217 case XT_XTP_CL_LIST
:
4218 /* nothing, just xtp_page_cl() */
4220 case XT_XTP_CL_REMOVE
:
4224 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
4228 xtp_page_cl(t
, NULL
);
4231 /* link an XTP class to it's session key and handler function */
4232 struct xtp_despatch
{
4235 void (*handle_func
)(struct tab
*, uint8_t, int);
4238 struct xtp_despatch xtp_despatches
[] = {
4239 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
4240 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
4241 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
4242 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
4243 { NULL
, NULL
, NULL
}
4247 * is the url xtp protocol? (xxxt://)
4248 * if so, parse and despatch correct bahvior
4251 parse_xtp_url(struct tab
*t
, const char *url
)
4253 char *dup
= NULL
, *p
, *last
;
4254 uint8_t n_tokens
= 0;
4255 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
4256 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
4260 * tokens array meaning:
4262 * tokens[1] = session key
4263 * tokens[2] = action
4264 * tokens[3] = optional argument
4267 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
4269 /*xtp tab meaning is normal unless proven special */
4270 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4272 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
4275 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
4277 /* split out the url */
4278 for ((p
= strtok_r(dup
, "/", &last
)); p
;
4279 (p
= strtok_r(NULL
, "/", &last
))) {
4281 tokens
[n_tokens
++] = p
;
4284 /* should be atleast three fields 'class/seskey/command/arg' */
4288 dsp
= xtp_despatches
;
4289 req_class
= atoi(tokens
[0]);
4290 while (dsp
->xtp_class
!= NULL
) {
4291 if (dsp
->xtp_class
== req_class
) {
4298 /* did we find one atall? */
4299 if (dsp_match
== NULL
) {
4300 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
4304 /* check session key and call despatch function */
4305 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
4306 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
4319 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
4321 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
4322 char *newuri
= NULL
;
4324 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
4327 errx(1, "activate_uri_entry_cb");
4332 uri
+= strspn(uri
, "\t ");
4334 /* if xxxt:// treat specially */
4335 if (!parse_xtp_url(t
, uri
)) {
4336 if (valid_url_type((char *)uri
)) {
4337 newuri
= guess_url_type((char *)uri
);
4341 webkit_web_view_load_uri(t
->wv
, uri
);
4342 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4350 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
4352 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
4353 char *newuri
= NULL
;
4355 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
4358 errx(1, "activate_search_entry_cb");
4360 if (search_string
== NULL
) {
4361 show_oops(t
, "no search_string");
4365 newuri
= g_strdup_printf(search_string
, search
);
4367 webkit_web_view_load_uri(t
->wv
, newuri
);
4368 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4375 check_and_set_js(gchar
*uri
, struct tab
*t
)
4377 struct domain
*d
= NULL
;
4380 if (uri
== NULL
|| t
== NULL
)
4383 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
4388 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
4389 es
? "enable" : "disable", uri
);
4391 g_object_set((GObject
*)t
->settings
,
4392 "enable-scripts", es
, (char *)NULL
);
4393 webkit_web_view_set_settings(t
->wv
, t
->settings
);
4395 button_set_stockid(t
->js_toggle
,
4396 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
4400 show_ca_status(struct tab
*t
, const char *uri
)
4402 WebKitWebFrame
*frame
;
4403 WebKitWebDataSource
*source
;
4404 WebKitNetworkRequest
*request
;
4405 SoupMessage
*message
;
4407 gchar
*col_str
= "white";
4410 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
4411 ssl_strict_certs
, ssl_ca_file
, uri
);
4415 if (ssl_ca_file
== NULL
) {
4416 if (g_str_has_prefix(uri
, "http://"))
4418 if (g_str_has_prefix(uri
, "https://")) {
4424 if (g_str_has_prefix(uri
, "http://") ||
4425 !g_str_has_prefix(uri
, "https://"))
4428 frame
= webkit_web_view_get_main_frame(t
->wv
);
4429 source
= webkit_web_frame_get_data_source(frame
);
4430 request
= webkit_web_data_source_get_request(source
);
4431 message
= webkit_network_request_get_message(request
);
4433 if (message
&& (soup_message_get_flags(message
) &
4434 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
4438 r
= load_compare_cert(t
, NULL
);
4440 col_str
= "lightblue";
4449 gdk_color_parse(col_str
, &color
);
4450 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
4455 load_favicon(struct tab
*t
)
4460 GdkPixbuf
*pixbuf
, *scaled
;
4462 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
,
4464 asprintf(&file
, "%s/%s.ico", cache_dir
, name_hash
);
4466 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
4472 g_object_get(pixbuf
,
4476 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
4477 __func__
, t
->tab_id
, width
, height
);
4479 if (width
> 16 || height
> 16) {
4480 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
4481 GDK_INTERP_BILINEAR
);
4482 g_object_unref(pixbuf
);
4489 if (t
->icon_pixbuf
) {
4490 g_object_unref(t
->icon_pixbuf
);
4492 t
->icon_pixbuf
= scaled
;
4498 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
4501 WebKitDownloadStatus status
= webkit_download_get_status (download
);
4504 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4507 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4508 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4509 g_object_unref(download
);
4517 notify_icon_loaded_cb(WebKitWebView
*wv
, char *uri
, struct tab
*t
)
4519 WebKitNetworkRequest
*request
;
4520 WebKitDownload
*download
;
4522 char *dest_uri
, *file
;
4525 if (uri
== NULL
|| t
== NULL
)
4526 errx(1, "%s: invalid pointers", __func__
);
4528 if (!strcmp(t
->icon_uri
,uri
)) {
4533 asprintf(&t
->icon_uri
, "%s", uri
);
4534 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri %s "
4535 "(dl!)\n", __func__
, t
->tab_id
, uri
);
4537 request
= webkit_network_request_new(t
->icon_uri
);
4539 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon uri %s "
4540 "(request failed)\n", __func__
, t
->tab_id
, uri
);
4543 download
= webkit_download_new(request
);
4544 g_object_unref(request
);
4546 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
4547 asprintf(&file
, "%s/%s.ico", cache_dir
, name_hash
);
4550 if (!stat(file
, &sb
)) {
4555 asprintf(&dest_uri
, "file://%s", file
);
4558 webkit_download_set_destination_uri(download
, dest_uri
);
4561 g_signal_connect(G_OBJECT (download
), "notify::status",
4562 G_CALLBACK (favicon_download_status_changed_cb
), t
);
4564 webkit_download_start(download
);
4569 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
4571 WebKitWebFrame
*frame
;
4572 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
4573 struct history
*h
, find
;
4575 const gchar
*s_loading
;
4577 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d\n",
4578 webkit_web_view_get_load_status(wview
));
4581 errx(1, "notify_load_status_cb");
4583 switch (webkit_web_view_get_load_status(wview
)) {
4584 case WEBKIT_LOAD_PROVISIONAL
:
4585 #if GTK_CHECK_VERSION(2, 20, 0)
4586 gtk_widget_show(t
->spinner
);
4587 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
4589 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
4591 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
4594 /* take focus if we are visible */
4595 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
4596 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4600 case WEBKIT_LOAD_COMMITTED
:
4601 frame
= webkit_web_view_get_main_frame(wview
);
4602 uri
= webkit_web_frame_get_uri(frame
);
4604 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
4606 /* check if js white listing is enabled */
4607 if (enable_js_whitelist
) {
4608 frame
= webkit_web_view_get_main_frame(wview
);
4609 uri
= webkit_web_frame_get_uri(frame
);
4610 check_and_set_js((gchar
*)uri
, t
);
4613 show_ca_status(t
, uri
);
4616 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
4617 title
= webkit_web_view_get_title(wview
);
4618 frame
= webkit_web_view_get_main_frame(wview
);
4619 uri
= webkit_web_frame_get_uri(frame
);
4627 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
4628 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
4631 if (!strncmp(uri
, "http://", strlen("http://")) ||
4632 !strncmp(uri
, "https://", strlen("https://")) ||
4633 !strncmp(uri
, "file://", strlen("file://")))
4638 h
= RB_FIND(history_list
, &hl
, &find
);
4642 h
= g_malloc(sizeof *h
);
4643 h
->uri
= g_strdup(uri
);
4645 h
->title
= g_strdup(title
);
4647 h
->title
= g_strdup(uri
);
4648 RB_INSERT(history_list
, &hl
, h
);
4649 update_history_tabs(NULL
);
4654 case WEBKIT_LOAD_FINISHED
:
4655 #if WEBKIT_CHECK_VERSION(1, 1, 18)
4656 case WEBKIT_LOAD_FAILED
:
4658 #if GTK_CHECK_VERSION(2, 20, 0)
4659 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
4660 gtk_widget_hide(t
->spinner
);
4662 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
4663 if (s_loading
&& !strcmp(s_loading
, "Loading"))
4664 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
4666 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
4672 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
4674 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
4675 webkit_web_view_can_go_back(wview
));
4677 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
4678 webkit_web_view_can_go_forward(wview
));
4682 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
4684 run_script(t
, JS_HINTING
);
4688 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
4690 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
4691 progress
== 100 ? 0 : (double)progress
/ 100);
4695 webview_nw_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
4696 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
4697 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
4702 errx(1, "webview_nw_cb");
4704 DNPRINTF(XT_D_NAV
, "webview_nw_cb: %s\n",
4705 webkit_network_request_get_uri(request
));
4707 /* open in current tab */
4708 uri
= (char *)webkit_network_request_get_uri(request
);
4709 webkit_web_view_load_uri(t
->wv
, uri
);
4710 webkit_web_policy_decision_ignore(pd
);
4712 return (TRUE
); /* we made the decission */
4716 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
4717 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
4718 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
4723 errx(1, "webview_npd_cb");
4725 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
4727 webkit_network_request_get_uri(request
));
4729 uri
= (char *)webkit_network_request_get_uri(request
);
4731 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
4733 create_new_tab(uri
, NULL
, ctrl_click_focus
);
4734 webkit_web_policy_decision_ignore(pd
);
4735 return (TRUE
); /* we made the decission */
4738 webkit_web_policy_decision_use(pd
);
4739 return (TRUE
); /* we made the decission */
4743 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
4746 errx(1, "webview_cwv_cb");
4748 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
4749 webkit_web_view_get_uri(wv
));
4755 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
4757 /* we can not eat the event without throwing gtk off so defer it */
4759 /* catch middle click */
4760 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
4765 /* catch ctrl click */
4766 if (e
->type
== GDK_BUTTON_RELEASE
&&
4767 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
4772 return (XT_CB_PASSTHROUGH
);
4776 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
4778 struct mime_type
*m
;
4780 m
= find_mime_type(mime_type
);
4795 execlp(m
->mt_action
, m
->mt_action
,
4796 webkit_network_request_get_uri(request
), (void *)NULL
);
4805 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
4806 WebKitNetworkRequest
*request
, char *mime_type
,
4807 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
4810 errx(1, "webview_mimetype_cb");
4812 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
4813 t
->tab_id
, mime_type
);
4815 if (run_mimehandler(t
, mime_type
, request
) == 0) {
4816 webkit_web_policy_decision_ignore(decision
);
4817 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4821 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
4822 webkit_web_policy_decision_download(decision
);
4830 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
, struct tab
*t
)
4832 const gchar
*filename
;
4834 struct download
*download_entry
;
4837 if (wk_download
== NULL
|| t
== NULL
)
4838 errx(1, "%s: invalid pointers", __func__
);
4840 filename
= webkit_download_get_suggested_filename(wk_download
);
4841 if (filename
== NULL
)
4842 return (FALSE
); /* abort download */
4844 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
4846 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
4847 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
4849 webkit_download_set_destination_uri(wk_download
, uri
);
4851 if (webkit_download_get_status(wk_download
) ==
4852 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
4853 show_oops(t
, "%s: download failed to start", __func__
);
4855 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
4857 download_entry
= g_malloc(sizeof(struct download
));
4858 download_entry
->download
= wk_download
;
4859 download_entry
->tab
= t
;
4860 download_entry
->id
= next_download_id
++;
4861 RB_INSERT(download_list
, &downloads
, download_entry
);
4862 /* get from history */
4863 g_object_ref(wk_download
);
4864 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
4870 /* sync other download manager tabs */
4871 update_download_tabs(NULL
);
4874 * NOTE: never redirect/render the current tab before this
4875 * function returns. This will cause the download to never start.
4877 return (ret
); /* start download */
4880 /* XXX currently unused */
4882 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
4884 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
4887 errx(1, "webview_hover_cb");
4894 t
->hover
= g_strdup(uri
);
4895 } else if (t
->hover
) {
4902 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
4905 char s
[2], buf
[128];
4906 const char *errstr
= NULL
;
4909 /* don't use w directly; use t->whatever instead */
4912 errx(1, "wv_keypress_after_cb");
4914 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
4915 e
->keyval
, e
->state
, t
);
4919 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
4921 return (XT_CB_HANDLED
);
4925 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
4926 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
4928 /* we have a string */
4930 /* we have a number */
4931 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
4939 /* XXX unfuck this */
4940 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
4941 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
4942 /* last input was numerical */
4944 l
= strlen(t
->hint_num
);
4951 t
->hint_num
[l
] = '\0';
4955 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
4956 /* last input was alphanumerical */
4958 l
= strlen(t
->hint_buf
);
4965 t
->hint_buf
[l
] = '\0';
4975 /* numerical input */
4976 if (CLEAN(e
->state
) == 0 &&
4977 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
4978 snprintf(s
, sizeof s
, "%c", e
->keyval
);
4979 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
4980 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
4983 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
4985 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
4988 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
4990 t
->hint_mode
= XT_HINT_NUMERICAL
;
4994 /* empty the counter buffer */
4995 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
4996 return (XT_CB_HANDLED
);
4999 /* alphanumerical input */
5001 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
5002 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
5003 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
5004 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
5005 snprintf(s
, sizeof s
, "%c", e
->keyval
);
5006 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
5007 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
5010 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
5013 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
5015 t
->hint_mode
= XT_HINT_ALPHANUM
;
5018 /* empty the counter buffer */
5019 bzero(t
->hint_num
, sizeof t
->hint_num
);
5020 return (XT_CB_HANDLED
);
5023 return (XT_CB_HANDLED
);
5026 for (i
= 0; i
< LENGTH(keys
); i
++)
5027 if (e
->keyval
== keys
[i
].key
&& CLEAN(e
->state
) ==
5029 keys
[i
].func(t
, &keys
[i
].arg
);
5030 return (XT_CB_HANDLED
);
5034 return (XT_CB_PASSTHROUGH
);
5038 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5042 return (XT_CB_PASSTHROUGH
);
5046 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5048 const gchar
*c
= gtk_entry_get_text(w
);
5052 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
5053 e
->keyval
, e
->state
, t
);
5056 errx(1, "cmd_keyrelease_cb");
5058 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
5059 e
->keyval
, e
->state
, t
);
5063 if (strlen(c
) == 1) {
5064 webkit_web_view_unmark_text_matches(t
->wv
);
5070 else if (c
[0] == '?')
5076 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
5078 /* not found, mark red */
5079 gdk_color_parse("red", &color
);
5080 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
5081 /* unmark and remove selection */
5082 webkit_web_view_unmark_text_matches(t
->wv
);
5083 /* my kingdom for a way to unselect text in webview */
5085 /* found, highlight all */
5086 webkit_web_view_unmark_text_matches(t
->wv
);
5087 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
5088 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5089 gdk_color_parse("white", &color
);
5090 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
5093 return (XT_CB_PASSTHROUGH
);
5098 cmd_complete(struct tab
*t
, char *s
)
5101 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
5103 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
5105 for (i
= 0; i
< LENGTH(cmds
); i
++) {
5106 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
5107 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
5109 gtk_entry_set_text(w
, ":");
5110 gtk_entry_append_text(w
, cmds
[i
].cmd
);
5111 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
5121 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5126 errx(1, "entry_key_cb");
5128 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
5129 e
->keyval
, e
->state
, t
);
5133 if (e
->keyval
== GDK_Escape
)
5134 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5136 for (i
= 0; i
< LENGTH(keys
); i
++)
5137 if (e
->keyval
== keys
[i
].key
&&
5138 CLEAN(e
->state
) == keys
[i
].mask
&&
5139 keys
[i
].use_in_entry
) {
5140 keys
[i
].func(t
, &keys
[i
].arg
);
5141 return (XT_CB_HANDLED
);
5144 return (XT_CB_PASSTHROUGH
);
5148 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5150 int rv
= XT_CB_HANDLED
;
5151 const gchar
*c
= gtk_entry_get_text(w
);
5154 errx(1, "cmd_keypress_cb");
5156 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
5157 e
->keyval
, e
->state
, t
);
5161 e
->keyval
= GDK_Escape
;
5162 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
5163 e
->keyval
= GDK_Escape
;
5165 switch (e
->keyval
) {
5171 if (strchr (c
, ' ')) {
5172 /* par completion */
5173 fprintf(stderr
, "completeme par\n");
5177 cmd_complete(t
, (char *)&c
[1]);
5182 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
5187 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5190 if (c
[0] == '/' || c
[0] == '?')
5191 webkit_web_view_unmark_text_matches(t
->wv
);
5195 rv
= XT_CB_PASSTHROUGH
;
5201 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
5204 errx(1, "cmd_focusout_cb");
5206 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d focus_wv %d\n",
5207 t
->tab_id
, t
->focus_wv
);
5213 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5215 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
5217 return (XT_CB_PASSTHROUGH
);
5221 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
5225 const gchar
*c
= gtk_entry_get_text(entry
);
5228 errx(1, "cmd_activate_cb");
5230 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
5235 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
5241 if (c
[0] == '/' || c
[0] == '?') {
5242 if (t
->search_text
) {
5243 g_free(t
->search_text
);
5244 t
->search_text
= NULL
;
5247 t
->search_text
= g_strdup(s
);
5249 g_free(global_search
);
5250 global_search
= g_strdup(s
);
5251 t
->search_forward
= c
[0] == '/';
5256 for (i
= 0; i
< LENGTH(cmds
); i
++)
5257 if (cmds
[i
].params
) {
5258 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
5259 cmds
[i
].arg
.s
= g_strdup(s
);
5260 goto execute_command
;
5263 if (!strcmp(s
, cmds
[i
].cmd
))
5264 goto execute_command
;
5266 show_oops(t
, "Invalid command: %s", s
);
5273 cmds
[i
].func(t
, &cmds
[i
].arg
);
5276 backward_cb(GtkWidget
*w
, struct tab
*t
)
5281 errx(1, "backward_cb");
5283 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
5290 forward_cb(GtkWidget
*w
, struct tab
*t
)
5295 errx(1, "forward_cb");
5297 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
5299 a
.i
= XT_NAV_FORWARD
;
5304 stop_cb(GtkWidget
*w
, struct tab
*t
)
5306 WebKitWebFrame
*frame
;
5311 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
5313 frame
= webkit_web_view_get_main_frame(t
->wv
);
5314 if (frame
== NULL
) {
5315 show_oops(t
, "stop_cb: no frame");
5319 webkit_web_frame_stop_loading(frame
);
5323 setup_webkit(struct tab
*t
)
5325 g_object_set((GObject
*)t
->settings
,
5326 "user-agent", t
->user_agent
, (char *)NULL
);
5327 g_object_set((GObject
*)t
->settings
,
5328 "enable-scripts", enable_scripts
, (char *)NULL
);
5329 g_object_set((GObject
*)t
->settings
,
5330 "enable-plugins", enable_plugins
, (char *)NULL
);
5331 adjustfont_webkit(t
, XT_FONT_SET
);
5333 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5337 create_browser(struct tab
*t
)
5343 errx(1, "create_browser");
5345 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
5346 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
5347 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
5348 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
5350 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
5351 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
5352 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
5354 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
5355 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
5358 t
->settings
= webkit_web_settings_new();
5360 if (user_agent
== NULL
) {
5361 g_object_get((GObject
*)t
->settings
, "user-agent", &strval
,
5363 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
5366 t
->user_agent
= g_strdup(user_agent
);
5379 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
5380 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
5381 gtk_widget_set_name(w
, "xxxterm");
5382 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
5383 g_signal_connect(G_OBJECT(w
), "delete_event",
5384 G_CALLBACK (gtk_main_quit
), NULL
);
5390 create_toolbar(struct tab
*t
)
5392 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
5394 b
= gtk_hbox_new(FALSE
, 0);
5396 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
5399 /* backward button */
5400 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
5401 gtk_widget_set_sensitive(t
->backward
, FALSE
);
5402 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
5403 G_CALLBACK(backward_cb
), t
);
5404 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
5406 /* forward button */
5407 t
->forward
= create_button("GoForward",GTK_STOCK_GO_FORWARD
, 0);
5408 gtk_widget_set_sensitive(t
->forward
, FALSE
);
5409 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
5410 G_CALLBACK(forward_cb
), t
);
5411 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
5415 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
5416 gtk_widget_set_sensitive(t
->stop
, FALSE
);
5417 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
5418 G_CALLBACK(stop_cb
), t
);
5419 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
5423 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
5424 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
5425 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
5426 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
5427 G_CALLBACK(js_toggle_cb
), t
);
5428 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
5431 t
->uri_entry
= gtk_entry_new();
5432 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
5433 G_CALLBACK(activate_uri_entry_cb
), t
);
5434 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
5435 (GCallback
)entry_key_cb
, t
);
5436 eb1
= gtk_hbox_new(FALSE
, 0);
5437 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
5438 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
5439 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
5442 if (fancy_bar
&& search_string
) {
5444 t
->search_entry
= gtk_entry_new();
5445 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
5446 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
5447 G_CALLBACK(activate_search_entry_cb
), t
);
5448 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
5449 (GCallback
)entry_key_cb
, t
);
5450 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
5451 eb2
= gtk_hbox_new(FALSE
, 0);
5452 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
5453 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
5455 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
5465 TAILQ_FOREACH(t
, &tabs
, entry
)
5466 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
5470 undo_close_tab_save(struct tab
*t
)
5474 struct undo
*u1
, *u2
;
5475 WebKitWebFrame
*frame
;
5477 WebKitWebHistoryItem
*item
;
5479 frame
= webkit_web_view_get_main_frame(t
->wv
);
5480 uri
= webkit_web_frame_get_uri(frame
);
5482 if (uri
&& !strlen(uri
))
5485 u1
= g_malloc0(sizeof(struct undo
));
5486 u1
->uri
= g_strdup(uri
);
5488 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
5490 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
5491 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
5494 /* forward history */
5495 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
5499 u1
->history
= g_list_prepend(u1
->history
,
5500 webkit_web_history_item_copy(item
));
5501 items
= g_list_next(items
);
5506 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
5507 u1
->history
= g_list_prepend(u1
->history
,
5508 webkit_web_history_item_copy(item
));
5512 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
5516 u1
->history
= g_list_prepend(u1
->history
,
5517 webkit_web_history_item_copy(item
));
5518 items
= g_list_next(items
);
5521 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
5523 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
5524 u2
= TAILQ_LAST(&undos
, undo_tailq
);
5525 TAILQ_REMOVE(&undos
, u2
, entry
);
5527 g_list_free(u2
->history
);
5536 delete_tab(struct tab
*t
)
5538 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
5543 TAILQ_REMOVE(&tabs
, t
, entry
);
5545 /* halt all webkit activity */
5546 webkit_web_view_stop_loading(t
->wv
);
5547 undo_close_tab_save(t
);
5549 gtk_widget_destroy(t
->vbox
);
5550 g_free(t
->user_agent
);
5554 if (TAILQ_EMPTY(&tabs
))
5555 create_new_tab(NULL
, NULL
, 1);
5559 adjustfont_webkit(struct tab
*t
, int adjust
)
5562 errx(1, "adjustfont_webkit");
5564 if (adjust
== XT_FONT_SET
)
5565 t
->font_size
= default_font_size
;
5567 t
->font_size
+= adjust
;
5568 g_object_set((GObject
*)t
->settings
, "default-font-size",
5569 t
->font_size
, (char *)NULL
);
5570 g_object_get((GObject
*)t
->settings
, "default-font-size",
5571 &t
->font_size
, (char *)NULL
);
5575 append_tab(struct tab
*t
)
5580 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
5581 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
5585 create_new_tab(char *title
, struct undo
*u
, int focus
)
5588 int load
= 1, id
, notfound
;
5589 char *newuri
= NULL
;
5591 WebKitWebHistoryItem
*item
;
5595 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
5597 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
5598 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
5602 t
= g_malloc0(sizeof *t
);
5604 if (title
== NULL
) {
5605 title
= "(untitled)";
5608 if (valid_url_type(title
)) {
5609 newuri
= guess_url_type(title
);
5614 t
->vbox
= gtk_vbox_new(FALSE
, 0);
5616 /* label + button for tab */
5617 b
= gtk_hbox_new(FALSE
, 0);
5620 #if GTK_CHECK_VERSION(2, 20, 0)
5621 t
->spinner
= gtk_spinner_new ();
5623 t
->label
= gtk_label_new(title
);
5624 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
5625 gtk_widget_set_size_request(t
->label
, 100, 0);
5626 gtk_widget_set_size_request(b
, 130, 0);
5627 gtk_notebook_set_homogeneous_tabs(notebook
, TRUE
);
5629 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
5630 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
5631 #if GTK_CHECK_VERSION(2, 20, 0)
5632 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
5636 t
->toolbar
= create_toolbar(t
);
5637 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
5640 t
->browser_win
= create_browser(t
);
5641 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
5643 /* oops message for user feedback */
5644 t
->oops
= gtk_entry_new();
5645 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
5646 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
5647 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
5648 gdk_color_parse("red", &color
);
5649 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
5650 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
5653 t
->cmd
= gtk_entry_new();
5654 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
5655 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
5656 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
5658 /* xtp meaning is normal by default */
5659 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5661 /* and show it all */
5662 gtk_widget_show_all(b
);
5663 gtk_widget_show_all(t
->vbox
);
5665 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
5669 id
= gtk_notebook_get_current_page(notebook
);
5670 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5671 if (tt
->tab_id
== id
) {
5673 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
5674 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
5684 #if GTK_CHECK_VERSION(2, 20, 0)
5685 /* turn spinner off if we are a new tab without uri */
5687 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5688 gtk_widget_hide(t
->spinner
);
5691 /* make notebook tabs reorderable */
5692 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
5694 g_object_connect((GObject
*)t
->cmd
,
5695 "signal::key-press-event", (GCallback
)cmd_keypress_cb
, t
,
5696 "signal::key-release-event", (GCallback
)cmd_keyrelease_cb
, t
,
5697 "signal::focus-out-event", (GCallback
)cmd_focusout_cb
, t
,
5698 "signal::activate", (GCallback
)cmd_activate_cb
, t
,
5701 /* reuse wv_button_cb to hide oops */
5702 g_object_connect((GObject
*)t
->oops
,
5703 "signal::button_press_event", (GCallback
)wv_button_cb
, t
,
5706 g_object_connect((GObject
*)t
->wv
,
5707 "signal::key-press-event", (GCallback
)wv_keypress_cb
, t
,
5708 "signal-after::key-press-event", (GCallback
)wv_keypress_after_cb
, t
,
5709 /* "signal::hovering-over-link", (GCallback)webview_hover_cb, t, */
5710 "signal::download-requested", (GCallback
)webview_download_cb
, t
,
5711 "signal::mime-type-policy-decision-requested", (GCallback
)webview_mimetype_cb
, t
,
5712 "signal::navigation-policy-decision-requested", (GCallback
)webview_npd_cb
, t
,
5713 "signal::new-window-policy-decision-requested", (GCallback
)webview_nw_cb
, t
,
5714 "signal::create-web-view", (GCallback
)webview_cwv_cb
, t
,
5715 "signal::event", (GCallback
)webview_event_cb
, t
,
5716 "signal::load-finished", (GCallback
)webview_load_finished_cb
, t
,
5717 "signal::load-progress-changed", (GCallback
)webview_progress_changed_cb
, t
,
5718 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5719 "signal::icon-loaded", (GCallback
)notify_icon_loaded_cb
, t
,
5721 "signal::button_press_event", (GCallback
)wv_button_cb
, t
,
5723 g_signal_connect(t
->wv
,
5724 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
5726 /* hijack the unused keys as if we were the browser */
5727 g_object_connect((GObject
*)t
->toolbar
,
5728 "signal-after::key-press-event", (GCallback
)wv_keypress_after_cb
, t
,
5731 g_signal_connect(G_OBJECT(bb
), "button_press_event",
5732 G_CALLBACK(tab_close_cb
), t
);
5738 gtk_widget_hide(t
->toolbar
);
5741 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
5742 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
5747 webkit_web_view_load_uri(t
->wv
, title
);
5749 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
5753 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
5754 /* restore the tab's history */
5755 if (u
&& u
->history
) {
5759 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
5760 items
= g_list_next(items
);
5763 item
= g_list_nth_data(u
->history
, u
->back
);
5765 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
5768 g_list_free(u
->history
);
5776 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
5782 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
5784 TAILQ_FOREACH(t
, &tabs
, entry
) {
5785 if (t
->tab_id
== pn
) {
5786 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
5789 uri
= webkit_web_view_get_title(t
->wv
);
5792 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
5798 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5804 menuitem_response(struct tab
*t
)
5806 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
5810 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
5812 GtkWidget
*menu
, *menu_items
;
5813 GdkEventButton
*bevent
;
5814 WebKitWebFrame
*frame
;
5818 if (event
->type
== GDK_BUTTON_PRESS
) {
5819 bevent
= (GdkEventButton
*) event
;
5820 menu
= gtk_menu_new();
5822 TAILQ_FOREACH(ti
, &tabs
, entry
) {
5823 frame
= webkit_web_view_get_main_frame(ti
->wv
);
5824 uri
= webkit_web_frame_get_uri(frame
);
5825 /* XXX make sure there is something to print */
5826 /* XXX add gui pages in here to look purdy */
5829 if (strlen(uri
) == 0)
5831 menu_items
= gtk_menu_item_new_with_label(uri
);
5832 gtk_menu_append(GTK_MENU (menu
), menu_items
);
5833 gtk_widget_show(menu_items
);
5835 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
5836 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
5840 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
5841 bevent
->button
, bevent
->time
);
5843 /* unref object so it'll free itself when popped down */
5844 g_object_ref_sink(menu
);
5845 g_object_unref(menu
);
5847 return (TRUE
/* eat event */);
5850 return (FALSE
/* propagate */);
5854 icon_size_map(int icon_size
)
5856 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
5857 icon_size
> GTK_ICON_SIZE_DIALOG
)
5858 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
5864 create_button(char *name
, char *stockid
, int size
)
5866 GtkWidget
*button
, *image
;
5870 "style \"%s-style\"\n"
5872 " GtkWidget::focus-padding = 0\n"
5873 " GtkWidget::focus-line-width = 0\n"
5877 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
5878 gtk_rc_parse_string(rcstring
);
5880 button
= gtk_button_new();
5881 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
5882 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
5884 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
5885 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
5886 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
5887 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
5888 gtk_widget_set_name(button
, name
);
5889 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
5890 gtk_widget_set_tooltip_text(button
, name
);
5896 button_set_stockid(GtkWidget
*button
, char *stockid
)
5899 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
5900 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
5901 gtk_button_set_image(GTK_BUTTON(button
), image
);
5910 char file
[PATH_MAX
];
5913 vbox
= gtk_vbox_new(FALSE
, 0);
5914 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
5915 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
5917 gtk_notebook_set_show_tabs(notebook
, FALSE
);
5919 gtk_notebook_set_tab_hborder(notebook
, 0);
5920 gtk_notebook_set_tab_vborder(notebook
, 0);
5922 gtk_notebook_set_show_border(notebook
, FALSE
);
5923 gtk_notebook_set_scrollable(notebook
, TRUE
);
5924 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
5926 abtn
= gtk_button_new();
5927 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
5928 gtk_widget_set_size_request(arrow
, -1, -1);
5929 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
5930 gtk_widget_set_size_request(abtn
, -1, 20);
5931 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
5933 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
5934 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
5935 gtk_widget_set_size_request(vbox
, -1, -1);
5937 g_object_connect((GObject
*)notebook
,
5938 "signal::switch-page", (GCallback
)notebook_switchpage_cb
, NULL
,
5940 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
5941 G_CALLBACK(arrow_cb
), NULL
);
5943 main_window
= create_window();
5944 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
5945 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
5948 for (i
= 0; i
< LENGTH(icons
); i
++) {
5949 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
5950 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5951 l
= g_list_append(l
, pb
);
5953 gtk_window_set_default_icon_list(l
);
5955 gtk_widget_show_all(abtn
);
5956 gtk_widget_show_all(main_window
);
5960 set_hook(void **hook
, char *name
)
5963 errx(1, "set_hook");
5965 if (*hook
== NULL
) {
5966 *hook
= dlsym(RTLD_NEXT
, name
);
5968 errx(1, "can't hook %s", name
);
5972 /* override libsoup soup_cookie_equal because it doesn't look at domain */
5974 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
5976 g_return_val_if_fail(cookie1
, FALSE
);
5977 g_return_val_if_fail(cookie2
, FALSE
);
5979 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
5980 !strcmp (cookie1
->value
, cookie2
->value
) &&
5981 !strcmp (cookie1
->path
, cookie2
->path
) &&
5982 !strcmp (cookie1
->domain
, cookie2
->domain
));
5986 transfer_cookies(void)
5989 SoupCookie
*sc
, *pc
;
5991 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
5993 for (;cf
; cf
= cf
->next
) {
5995 sc
= soup_cookie_copy(pc
);
5996 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
5999 soup_cookies_free(cf
);
6003 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
6008 print_cookie("soup_cookie_jar_delete_cookie", c
);
6010 if (cookies_enabled
== 0)
6013 if (jar
== NULL
|| c
== NULL
)
6016 /* find and remove from persistent jar */
6017 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
6019 for (;cf
; cf
= cf
->next
) {
6021 if (soup_cookie_equal(ci
, c
)) {
6022 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
6027 soup_cookies_free(cf
);
6029 /* delete from session jar */
6030 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
6034 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
6040 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
6041 jar
, p_cookiejar
, s_cookiejar
);
6043 if (cookies_enabled
== 0)
6046 /* see if we are up and running */
6047 if (p_cookiejar
== NULL
) {
6048 _soup_cookie_jar_add_cookie(jar
, cookie
);
6051 /* disallow p_cookiejar adds, shouldn't happen */
6052 if (jar
== p_cookiejar
)
6055 if ((d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
6057 DNPRINTF(XT_D_COOKIE
,
6058 "soup_cookie_jar_add_cookie: reject %s\n",
6060 if (save_rejected_cookies
) {
6061 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
)
6062 err(1, "reject cookie file");
6063 fseek(r_cookie_f
, 0, SEEK_END
);
6064 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
6065 cookie
->http_only
? "#HttpOnly_" : "",
6067 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
6069 cookie
->secure
? "TRUE" : "FALSE",
6071 (gulong
)soup_date_to_time_t(cookie
->expires
) :
6078 if (!allow_volatile_cookies
)
6082 if (cookie
->expires
== NULL
&& session_timeout
) {
6083 soup_cookie_set_expires(cookie
,
6084 soup_date_new_from_now(session_timeout
));
6085 print_cookie("modified add cookie", cookie
);
6088 /* see if we are white listed for persistence */
6089 if (d
&& d
->handy
) {
6090 /* add to persistent jar */
6091 c
= soup_cookie_copy(cookie
);
6092 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
6093 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
6096 /* add to session jar */
6097 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
6098 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
6104 char file
[PATH_MAX
];
6106 set_hook((void *)&_soup_cookie_jar_add_cookie
,
6107 "soup_cookie_jar_add_cookie");
6108 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
6109 "soup_cookie_jar_delete_cookie");
6111 if (cookies_enabled
== 0)
6115 * the following code is intricate due to overriding several libsoup
6117 * do not alter order of these operations.
6120 /* rejected cookies */
6121 if (save_rejected_cookies
)
6122 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
6124 /* persistent cookies */
6125 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
6126 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
6128 /* session cookies */
6129 s_cookiejar
= soup_cookie_jar_new();
6130 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
6131 cookie_policy
, (void *)NULL
);
6134 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
6138 setup_proxy(char *uri
)
6141 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
6142 soup_uri_free(proxy_uri
);
6146 if (http_proxy
!= uri
) {
6153 http_proxy
= g_strdup(uri
);
6154 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
6155 proxy_uri
= soup_uri_new(http_proxy
);
6156 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
6161 send_url_to_socket(char *url
)
6163 int s
, len
, rv
= -1;
6164 struct sockaddr_un sa
;
6166 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6167 warnx("send_url_to_socket: socket");
6171 sa
.sun_family
= AF_UNIX
;
6172 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6173 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6176 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6177 warnx("send_url_to_socket: connect");
6181 if (send(s
, url
, strlen(url
) + 1, 0) == -1) {
6182 warnx("send_url_to_socket: send");
6191 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
6194 char str
[XT_MAX_URL_LENGTH
];
6195 socklen_t t
= sizeof(struct sockaddr_un
);
6196 struct sockaddr_un sa
;
6201 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
6202 warn("socket_watcher: accept");
6206 if (getpeereid(s
, &uid
, &gid
) == -1) {
6207 warn("socket_watcher: getpeereid");
6210 if (uid
!= getuid() || gid
!= getgid()) {
6211 warnx("socket_watcher: unauthorized user");
6217 warnx("socket_watcher: not a valid user");
6221 n
= recv(s
, str
, sizeof(str
), 0);
6225 create_new_tab(str
, NULL
, 1);
6232 struct sockaddr_un sa
;
6234 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6235 warn("is_running: socket");
6239 sa
.sun_family
= AF_UNIX
;
6240 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6241 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6244 /* connect to see if there is a listener */
6245 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
6246 rv
= 0; /* not running */
6248 rv
= 1; /* already running */
6259 struct sockaddr_un sa
;
6261 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6262 warn("build_socket: socket");
6266 sa
.sun_family
= AF_UNIX
;
6267 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6268 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6271 /* connect to see if there is a listener */
6272 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6273 /* no listener so we will */
6274 unlink(sa
.sun_path
);
6276 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6277 warn("build_socket: bind");
6281 if (listen(s
, 1) == -1) {
6282 warn("build_socket: listen");
6298 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
6303 main(int argc
, char *argv
[])
6306 int c
, focus
= 1, s
, optn
= 0;
6307 char conf
[PATH_MAX
] = { '\0' };
6308 char file
[PATH_MAX
];
6309 char *env_proxy
= NULL
;
6315 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
6317 while ((c
= getopt(argc
, argv
, "STVf:s:tn")) != -1) {
6326 errx(0 , "Version: %s", version
);
6329 strlcpy(conf
, optarg
, sizeof(conf
));
6332 strlcpy(named_session
, optarg
, sizeof(named_session
));
6351 RB_INIT(&downloads
);
6353 TAILQ_INIT(&aliases
);
6356 gnutls_global_init();
6358 /* generate session keys for xtp pages */
6359 generate_xtp_session_key(&dl_session_key
);
6360 generate_xtp_session_key(&hl_session_key
);
6361 generate_xtp_session_key(&cl_session_key
);
6362 generate_xtp_session_key(&fl_session_key
);
6365 gtk_init(&argc
, &argv
);
6366 if (!g_thread_supported())
6367 g_thread_init(NULL
);
6369 pwd
= getpwuid(getuid());
6371 errx(1, "invalid user %d", getuid());
6373 /* set download dir */
6374 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
6376 /* set default string settings */
6377 home
= g_strdup("http://www.peereboom.us");
6378 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
6379 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
6381 /* read config file */
6382 if (strlen(conf
) == 0)
6383 snprintf(conf
, sizeof conf
, "%s/.%s",
6384 pwd
->pw_dir
, XT_CONF_FILE
);
6385 config_parse(conf
, 0);
6387 /* working directory */
6388 snprintf(work_dir
, sizeof work_dir
, "%s/%s", pwd
->pw_dir
, XT_DIR
);
6389 if (stat(work_dir
, &sb
)) {
6390 if (mkdir(work_dir
, S_IRWXU
) == -1)
6391 err(1, "mkdir work_dir");
6392 if (stat(work_dir
, &sb
))
6393 err(1, "stat work_dir");
6395 if (S_ISDIR(sb
.st_mode
) == 0)
6396 errx(1, "%s not a dir", work_dir
);
6397 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6398 warnx("fixing invalid permissions on %s", work_dir
);
6399 if (chmod(work_dir
, S_IRWXU
) == -1)
6403 /* icon cache dir */
6404 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s/%s",
6405 pwd
->pw_dir
, XT_DIR
, XT_CACHE_DIR
);
6406 if (stat(cache_dir
, &sb
)) {
6407 if (mkdir(cache_dir
, S_IRWXU
) == -1)
6408 err(1, "mkdir cache_dir");
6409 if (stat(cache_dir
, &sb
))
6410 err(1, "stat cache_dir");
6412 if (S_ISDIR(sb
.st_mode
) == 0)
6413 errx(1, "%s not a dir", cache_dir
);
6414 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6415 warnx("fixing invalid permissions on %s", cache_dir
);
6416 if (chmod(cache_dir
, S_IRWXU
) == -1)
6421 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s/%s",
6422 pwd
->pw_dir
, XT_DIR
, XT_CERT_DIR
);
6423 if (stat(certs_dir
, &sb
)) {
6424 if (mkdir(certs_dir
, S_IRWXU
) == -1)
6425 err(1, "mkdir certs_dir");
6426 if (stat(certs_dir
, &sb
))
6427 err(1, "stat certs_dir");
6429 if (S_ISDIR(sb
.st_mode
) == 0)
6430 errx(1, "%s not a dir", certs_dir
);
6431 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6432 warnx("fixing invalid permissions on %s", certs_dir
);
6433 if (chmod(certs_dir
, S_IRWXU
) == -1)
6438 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s/%s",
6439 pwd
->pw_dir
, XT_DIR
, XT_SESSIONS_DIR
);
6440 if (stat(sessions_dir
, &sb
)) {
6441 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
6442 err(1, "mkdir sessions_dir");
6443 if (stat(sessions_dir
, &sb
))
6444 err(1, "stat sessions_dir");
6446 if (S_ISDIR(sb
.st_mode
) == 0)
6447 errx(1, "%s not a dir", sessions_dir
);
6448 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6449 warnx("fixing invalid permissions on %s", sessions_dir
);
6450 if (chmod(sessions_dir
, S_IRWXU
) == -1)
6453 /* runtime settings that can override config file */
6454 if (runtime_settings
[0] != '\0')
6455 config_parse(runtime_settings
, 1);
6458 if (!strcmp(download_dir
, pwd
->pw_dir
))
6459 strlcat(download_dir
, "/downloads", sizeof download_dir
);
6460 if (stat(download_dir
, &sb
)) {
6461 if (mkdir(download_dir
, S_IRWXU
) == -1)
6462 err(1, "mkdir download_dir");
6463 if (stat(download_dir
, &sb
))
6464 err(1, "stat download_dir");
6466 if (S_ISDIR(sb
.st_mode
) == 0)
6467 errx(1, "%s not a dir", download_dir
);
6468 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6469 warnx("fixing invalid permissions on %s", download_dir
);
6470 if (chmod(download_dir
, S_IRWXU
) == -1)
6474 /* favorites file */
6475 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6476 if (stat(file
, &sb
)) {
6477 warnx("favorites file doesn't exist, creating it");
6478 if ((f
= fopen(file
, "w")) == NULL
)
6479 err(1, "favorites");
6484 session
= webkit_get_default_session();
6489 if (stat(ssl_ca_file
, &sb
)) {
6490 warn("no CA file: %s", ssl_ca_file
);
6491 g_free(ssl_ca_file
);
6494 g_object_set(session
,
6495 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
6496 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
6501 env_proxy
= getenv("http_proxy");
6503 setup_proxy(env_proxy
);
6505 setup_proxy(http_proxy
);
6507 /* see if there is already an xxxterm running */
6508 if (single_instance
&& is_running()) {
6510 warnx("already running");
6515 send_url_to_socket(argv
[0]);
6526 if (save_global_history
)
6527 restore_global_history();
6529 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
6530 focus
= restore_saved_tabs();
6532 a
.s
= named_session
;
6533 focus
= open_tabs(NULL
, &a
);
6537 create_new_tab(argv
[0], NULL
, focus
);
6544 create_new_tab(home
, NULL
, 1);
6547 if ((s
= build_socket()) != -1)
6548 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
6552 gnutls_global_deinit();