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
;
167 WebKitWebHistoryItem
*item
;
168 WebKitWebBackForwardList
*bfl
;
171 WebKitNetworkRequest
*icon_request
;
172 WebKitDownload
*icon_download
;
173 GdkPixbuf
*icon_pixbuf
;
174 gchar
*icon_dest_uri
;
176 /* adjustments for browser */
179 GtkAdjustment
*adjust_h
;
180 GtkAdjustment
*adjust_v
;
186 int xtp_meaning
; /* identifies dls/favorites */
191 #define XT_HINT_NONE (0)
192 #define XT_HINT_NUMERICAL (1)
193 #define XT_HINT_ALPHANUM (2)
202 WebKitWebSettings
*settings
;
206 TAILQ_HEAD(tab_list
, tab
);
209 RB_ENTRY(history
) entry
;
213 RB_HEAD(history_list
, history
);
216 RB_ENTRY(download
) entry
;
218 WebKitDownload
*download
;
221 RB_HEAD(download_list
, download
);
224 RB_ENTRY(domain
) entry
;
226 int handy
; /* app use */
228 RB_HEAD(domain_list
, domain
);
231 TAILQ_ENTRY(undo
) entry
;
234 int back
; /* Keeps track of how many back
235 * history items there are. */
237 TAILQ_HEAD(undo_tailq
, undo
);
239 /* starts from 1 to catch atoi() failures when calling xtp_handle_dl() */
240 int next_download_id
= 1;
248 #define XT_NAME ("XXXTerm")
249 #define XT_DIR (".xxxterm")
250 #define XT_CACHE_DIR ("cache")
251 #define XT_CERT_DIR ("certs/")
252 #define XT_SESSIONS_DIR ("sessions/")
253 #define XT_CONF_FILE ("xxxterm.conf")
254 #define XT_FAVS_FILE ("favorites")
255 #define XT_SAVED_TABS_FILE ("main_session")
256 #define XT_RESTART_TABS_FILE ("restart_tabs")
257 #define XT_SOCKET_FILE ("socket")
258 #define XT_HISTORY_FILE ("history")
259 #define XT_CB_HANDLED (TRUE)
260 #define XT_CB_PASSTHROUGH (FALSE)
261 #define XT_DOCTYPE "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>"
262 #define XT_HTML_TAG "<html xmlns='http://www.w3.org/1999/xhtml'>"
263 #define XT_DLMAN_REFRESH "10"
264 #define XT_PAGE_STYLE "<style type='text/css'>\n" \
265 "td {overflow: hidden;}\n" \
266 "th {background-color: #cccccc}" \
267 "table {width: 90%%; border: 1px black" \
268 " solid; table-layout: fixed}\n</style>\n\n"
269 #define XT_MAX_URL_LENGTH (4096) /* 1 page is atomic, don't make bigger */
270 #define XT_MAX_UNDO_CLOSE_TAB (32)
273 #define SZ_KB ((uint64_t) 1024)
274 #define SZ_MB (SZ_KB * SZ_KB)
275 #define SZ_GB (SZ_KB * SZ_KB * SZ_KB)
276 #define SZ_TB (SZ_KB * SZ_KB * SZ_KB * SZ_KB)
279 * xxxterm "protocol" (xtp)
280 * We use this for managing stuff like downloads and favorites. They
281 * make magical HTML pages in memory which have xxxt:// links in order
282 * to communicate with xxxterm's internals. These links take the format:
283 * xxxt://class/session_key/action/arg
285 * Don't begin xtp class/actions as 0. atoi returns that on error.
287 * Typically we have not put addition of items in this framework, as
288 * adding items is either done via an ex-command or via a keybinding instead.
291 #define XT_XTP_STR "xxxt://"
293 /* XTP classes (xxxt://<class>) */
294 #define XT_XTP_DL 1 /* downloads */
295 #define XT_XTP_HL 2 /* history */
296 #define XT_XTP_CL 3 /* cookies */
297 #define XT_XTP_FL 4 /* favorites */
299 /* XTP download actions */
300 #define XT_XTP_DL_LIST 1
301 #define XT_XTP_DL_CANCEL 2
302 #define XT_XTP_DL_REMOVE 3
304 /* XTP history actions */
305 #define XT_XTP_HL_LIST 1
306 #define XT_XTP_HL_REMOVE 2
308 /* XTP cookie actions */
309 #define XT_XTP_CL_LIST 1
310 #define XT_XTP_CL_REMOVE 2
312 /* XTP cookie actions */
313 #define XT_XTP_FL_LIST 1
314 #define XT_XTP_FL_REMOVE 2
316 /* xtp tab meanings - identifies which tabs have xtp pages in */
317 #define XT_XTP_TAB_MEANING_NORMAL 0 /* normal url */
318 #define XT_XTP_TAB_MEANING_DL 1 /* download manager in this tab */
319 #define XT_XTP_TAB_MEANING_FL 2 /* favorite manager in this tab */
320 #define XT_XTP_TAB_MEANING_HL 3 /* history manager in this tab */
321 #define XT_XTP_TAB_MEANING_CL 4 /* cookie manager in this tab */
324 #define XT_MOVE_INVALID (0)
325 #define XT_MOVE_DOWN (1)
326 #define XT_MOVE_UP (2)
327 #define XT_MOVE_BOTTOM (3)
328 #define XT_MOVE_TOP (4)
329 #define XT_MOVE_PAGEDOWN (5)
330 #define XT_MOVE_PAGEUP (6)
331 #define XT_MOVE_HALFDOWN (7)
332 #define XT_MOVE_HALFUP (8)
333 #define XT_MOVE_LEFT (9)
334 #define XT_MOVE_FARLEFT (10)
335 #define XT_MOVE_RIGHT (11)
336 #define XT_MOVE_FARRIGHT (12)
338 #define XT_TAB_LAST (-4)
339 #define XT_TAB_FIRST (-3)
340 #define XT_TAB_PREV (-2)
341 #define XT_TAB_NEXT (-1)
342 #define XT_TAB_INVALID (0)
343 #define XT_TAB_NEW (1)
344 #define XT_TAB_DELETE (2)
345 #define XT_TAB_DELQUIT (3)
346 #define XT_TAB_OPEN (4)
347 #define XT_TAB_UNDO_CLOSE (5)
349 #define XT_NAV_INVALID (0)
350 #define XT_NAV_BACK (1)
351 #define XT_NAV_FORWARD (2)
352 #define XT_NAV_RELOAD (3)
353 #define XT_NAV_RELOAD_CACHE (4)
355 #define XT_FOCUS_INVALID (0)
356 #define XT_FOCUS_URI (1)
357 #define XT_FOCUS_SEARCH (2)
359 #define XT_SEARCH_INVALID (0)
360 #define XT_SEARCH_NEXT (1)
361 #define XT_SEARCH_PREV (2)
363 #define XT_PASTE_CURRENT_TAB (0)
364 #define XT_PASTE_NEW_TAB (1)
366 #define XT_FONT_SET (0)
368 #define XT_WL_TOGGLE (1<<0)
369 #define XT_WL_ENABLE (1<<1)
370 #define XT_WL_DISABLE (1<<2)
371 #define XT_WL_FQDN (1<<3) /* default */
372 #define XT_WL_TOPLEVEL (1<<4)
374 #define XT_CMD_OPEN (0)
375 #define XT_CMD_OPEN_CURRENT (1)
376 #define XT_CMD_TABNEW (2)
377 #define XT_CMD_TABNEW_CURRENT (3)
384 TAILQ_ENTRY(mime_type
) entry
;
386 TAILQ_HEAD(mime_type_list
, mime_type
);
392 TAILQ_ENTRY(alias
) entry
;
394 TAILQ_HEAD(alias_list
, alias
);
396 /* settings that require restart */
397 int showtabs
= 1; /* show tabs on notebook */
398 int showurl
= 1; /* show url toolbar on notebook */
399 int tabless
= 0; /* allow only 1 tab */
400 int enable_socket
= 0;
401 int single_instance
= 0; /* only allow one xxxterm to run */
402 int fancy_bar
= 1; /* fancy toolbar */
404 /* runtime settings */
405 int ctrl_click_focus
= 0; /* ctrl click gets focus */
406 int cookies_enabled
= 1; /* enable cookies */
407 int read_only_cookies
= 0; /* enable to not write cookies */
408 int enable_scripts
= 0;
409 int enable_plugins
= 0;
410 int default_font_size
= 12;
411 int window_height
= 768;
412 int window_width
= 1024;
413 int icon_size
= 2; /* 1 = smallest, 2+ = bigger */
414 unsigned refresh_interval
= 10; /* download refresh interval */
415 int enable_cookie_whitelist
= 1;
416 int enable_js_whitelist
= 1;
417 time_t session_timeout
= 3600; /* cookie session timeout */
418 int cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
419 char *ssl_ca_file
= NULL
;
420 char *resource_dir
= NULL
;
421 gboolean ssl_strict_certs
= FALSE
;
422 int append_next
= 1; /* append tab after current tab */
424 char *search_string
= NULL
;
425 char *http_proxy
= NULL
;
426 char download_dir
[PATH_MAX
];
427 char runtime_settings
[PATH_MAX
]; /* override of settings */
428 int allow_volatile_cookies
= 0;
429 int save_global_history
= 0; /* save global history to disk */
430 char *user_agent
= NULL
;
431 int save_rejected_cookies
= 0;
434 int set_download_dir(struct settings
*, char *);
435 int set_runtime_dir(struct settings
*, char *);
436 int set_cookie_policy(struct settings
*, char *);
437 int add_alias(struct settings
*, char *);
438 int add_mime_type(struct settings
*, char *);
439 int add_cookie_wl(struct settings
*, char *);
440 int add_js_wl(struct settings
*, char *);
441 void button_set_stockid(GtkWidget
*, char *);
442 GtkWidget
* create_button(char *, char *, int);
444 char *get_cookie_policy(struct settings
*);
446 char *get_download_dir(struct settings
*);
447 char *get_runtime_dir(struct settings
*);
449 void walk_alias(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
450 void walk_cookie_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
451 void walk_js_wl(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
452 void walk_mime_type(struct settings
*, void (*)(struct settings
*, char *, void *), void *);
455 int (*set
)(struct settings
*, char *);
456 char *(*get
)(struct settings
*);
457 void (*walk
)(struct settings
*, void (*cb
)(struct settings
*, char *, void *), void *);
460 struct special s_cookie
= {
466 struct special s_alias
= {
472 struct special s_mime
= {
478 struct special s_js
= {
484 struct special s_cookie_wl
= {
490 struct special s_download_dir
= {
499 #define XT_S_INVALID (0)
503 #define XT_SF_RESTART (1<<0)
504 #define XT_SF_RUNTIME (1<<1)
509 { "append_next", XT_S_INT
, 0 , &append_next
, NULL
, NULL
},
510 { "allow_volatile_cookies", XT_S_INT
, 0 , &allow_volatile_cookies
, NULL
, NULL
},
511 { "cookies_enabled", XT_S_INT
, 0 , &cookies_enabled
, NULL
, NULL
},
512 { "cookie_policy", XT_S_INT
, 0 , NULL
, NULL
, &s_cookie
},
513 { "ctrl_click_focus", XT_S_INT
, 0 , &ctrl_click_focus
, NULL
, NULL
},
514 { "default_font_size", XT_S_INT
, 0 , &default_font_size
, NULL
, NULL
},
515 { "download_dir", XT_S_STR
, 0 , NULL
, NULL
, &s_download_dir
},
516 { "enable_cookie_whitelist", XT_S_INT
, 0 , &enable_cookie_whitelist
, NULL
, NULL
},
517 { "enable_js_whitelist", XT_S_INT
, 0 , &enable_js_whitelist
, NULL
, NULL
},
518 { "enable_plugins", XT_S_INT
, 0 , &enable_plugins
, NULL
, NULL
},
519 { "enable_scripts", XT_S_INT
, 0 , &enable_scripts
, NULL
, NULL
},
520 { "enable_socket", XT_S_INT
, XT_SF_RESTART
, &enable_socket
, NULL
, NULL
},
521 { "fancy_bar", XT_S_INT
, XT_SF_RESTART
, &fancy_bar
, NULL
, NULL
},
522 { "home", XT_S_STR
, 0 , NULL
, &home
, NULL
},
523 { "http_proxy", XT_S_STR
, 0 , NULL
, &http_proxy
, NULL
},
524 { "icon_size", XT_S_INT
, 0 , &icon_size
, NULL
, NULL
},
525 { "read_only_cookies", XT_S_INT
, 0 , &read_only_cookies
, NULL
, NULL
},
526 { "refresh_interval", XT_S_INT
, 0 , &refresh_interval
, NULL
, NULL
},
527 { "resource_dir", XT_S_STR
, 0 , NULL
, &resource_dir
, NULL
},
528 { "search_string", XT_S_STR
, 0 , NULL
, &search_string
, NULL
},
529 { "session_timeout", XT_S_INT
, 0 , &session_timeout
, NULL
, NULL
},
530 { "save_global_history", XT_S_INT
, XT_SF_RESTART
, &save_global_history
, NULL
, NULL
},
531 { "save_rejected_cookies", XT_S_INT
, XT_SF_RESTART
, &save_rejected_cookies
, NULL
, NULL
},
532 { "single_instance", XT_S_INT
, XT_SF_RESTART
, &single_instance
, NULL
, NULL
},
533 { "ssl_ca_file", XT_S_STR
, 0 , NULL
, &ssl_ca_file
, NULL
},
534 { "ssl_strict_certs", XT_S_INT
, 0 , &ssl_strict_certs
, NULL
, NULL
},
535 { "user_agent", XT_S_STR
, 0 , NULL
, &user_agent
, NULL
},
536 { "window_height", XT_S_INT
, 0 , &window_height
, NULL
, NULL
},
537 { "window_width", XT_S_INT
, 0 , &window_width
, NULL
, NULL
},
539 /* runtime settings */
540 { "alias", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_alias
},
541 { "cookie_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_cookie_wl
},
542 { "js_wl", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_js
},
543 { "mime_type", XT_S_STR
, XT_SF_RUNTIME
, NULL
, NULL
, &s_mime
},
547 extern char *__progname
;
550 GtkWidget
*main_window
;
551 GtkNotebook
*notebook
;
552 GtkWidget
*arrow
, *abtn
;
553 struct tab_list tabs
;
554 struct history_list hl
;
555 struct download_list downloads
;
556 struct domain_list c_wl
;
557 struct domain_list js_wl
;
558 struct undo_tailq undos
;
560 int updating_dl_tabs
= 0;
561 int updating_hl_tabs
= 0;
562 int updating_cl_tabs
= 0;
563 int updating_fl_tabs
= 0;
565 uint64_t blocked_cookies
= 0;
566 char named_session
[PATH_MAX
];
567 void update_favicon(struct tab
*);
568 int icon_size_map(int);
571 load_webkit_string(struct tab
*t
, const char *str
)
573 /* we set this to indicate we want to manually do navaction */
574 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
575 webkit_web_view_load_string(t
->wv
, str
, NULL
, NULL
, NULL
);
579 hide_oops(struct tab
*t
)
581 gtk_widget_hide(t
->oops
);
585 hide_cmd(struct tab
*t
)
587 gtk_widget_hide(t
->cmd
);
591 show_cmd(struct tab
*t
)
593 gtk_widget_hide(t
->oops
);
594 gtk_widget_show(t
->cmd
);
598 show_oops(struct tab
*t
, const char *fmt
, ...)
607 if (vasprintf(&msg
, fmt
, ap
) == -1)
611 gtk_entry_set_text(GTK_ENTRY(t
->oops
), msg
);
612 gtk_widget_hide(t
->cmd
);
613 gtk_widget_show(t
->oops
);
616 get_as_string(struct settings
*s
)
627 warnx("get_as_string skip %s\n", s
->name
);
628 } else if (s
->type
== XT_S_INT
)
629 r
= g_strdup_printf("%d", *s
->ival
);
630 else if (s
->type
== XT_S_STR
)
631 r
= g_strdup(*s
->sval
);
633 r
= g_strdup_printf("INVALID TYPE");
639 settings_walk(void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
644 for (i
= 0; i
< LENGTH(rs
); i
++) {
645 if (rs
[i
].s
&& rs
[i
].s
->walk
)
646 rs
[i
].s
->walk(&rs
[i
], cb
, cb_args
);
648 s
= get_as_string(&rs
[i
]);
649 cb(&rs
[i
], s
, cb_args
);
656 set_cookie_policy(struct settings
*s
, char *val
)
658 if (!strcmp(val
, "no3rdparty"))
659 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
;
660 else if (!strcmp(val
, "accept"))
661 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_ALWAYS
;
662 else if (!strcmp(val
, "reject"))
663 cookie_policy
= SOUP_COOKIE_JAR_ACCEPT_NEVER
;
671 get_cookie_policy(struct settings
*s
)
675 if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
)
676 r
= g_strdup("no3rdparty");
677 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_ALWAYS
)
678 r
= g_strdup("accept");
679 else if (cookie_policy
== SOUP_COOKIE_JAR_ACCEPT_NEVER
)
680 r
= g_strdup("reject");
688 get_download_dir(struct settings
*s
)
690 if (download_dir
[0] == '\0')
692 return (g_strdup(download_dir
));
696 set_download_dir(struct settings
*s
, char *val
)
699 snprintf(download_dir
, sizeof download_dir
, "%s/%s",
700 pwd
->pw_dir
, &val
[1]);
702 strlcpy(download_dir
, val
, sizeof download_dir
);
709 * We use these to prevent people putting xxxt:// URLs on
710 * websites in the wild. We generate 8 bytes and represent in hex (16 chars)
712 #define XT_XTP_SES_KEY_SZ 8
713 #define XT_XTP_SES_KEY_HEX_FMT \
714 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
715 char *dl_session_key
; /* downloads */
716 char *hl_session_key
; /* history list */
717 char *cl_session_key
; /* cookie list */
718 char *fl_session_key
; /* favorites list */
720 char work_dir
[PATH_MAX
];
721 char certs_dir
[PATH_MAX
];
722 char cache_dir
[PATH_MAX
];
723 char sessions_dir
[PATH_MAX
];
724 char cookie_file
[PATH_MAX
];
725 SoupURI
*proxy_uri
= NULL
;
726 SoupSession
*session
;
727 SoupCookieJar
*s_cookiejar
;
728 SoupCookieJar
*p_cookiejar
;
729 char rc_fname
[PATH_MAX
];
731 struct mime_type_list mtl
;
732 struct alias_list aliases
;
735 void create_new_tab(char *, struct undo
*, int);
736 void delete_tab(struct tab
*);
737 void adjustfont_webkit(struct tab
*, int);
738 int run_script(struct tab
*, char *);
739 int download_rb_cmp(struct download
*, struct download
*);
740 int xtp_page_hl(struct tab
*t
, struct karg
*args
);
741 int xtp_page_dl(struct tab
*t
, struct karg
*args
);
742 int xtp_page_cl(struct tab
*t
, struct karg
*args
);
743 int xtp_page_fl(struct tab
*t
, struct karg
*args
);
746 history_rb_cmp(struct history
*h1
, struct history
*h2
)
748 return (strcmp(h1
->uri
, h2
->uri
));
750 RB_GENERATE(history_list
, history
, entry
, history_rb_cmp
);
753 domain_rb_cmp(struct domain
*d1
, struct domain
*d2
)
755 return (strcmp(d1
->d
, d2
->d
));
757 RB_GENERATE(domain_list
, domain
, entry
, domain_rb_cmp
);
760 * generate a session key to secure xtp commands.
761 * pass in a ptr to the key in question and it will
762 * be modified in place.
765 generate_xtp_session_key(char **key
)
767 uint8_t rand_bytes
[XT_XTP_SES_KEY_SZ
];
774 arc4random_buf(rand_bytes
, XT_XTP_SES_KEY_SZ
);
775 *key
= g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT
,
776 rand_bytes
[0], rand_bytes
[1], rand_bytes
[2], rand_bytes
[3],
777 rand_bytes
[4], rand_bytes
[5], rand_bytes
[6], rand_bytes
[7]);
779 DNPRINTF(XT_D_DOWNLOAD
, "%s: new session key '%s'\n", __func__
, *key
);
783 * validate a xtp session key.
787 validate_xtp_session_key(struct tab
*t
, char *trusted
, char *untrusted
)
789 if (strcmp(trusted
, untrusted
) != 0) {
790 show_oops(t
, "%s: xtp session key mismatch possible spoof",
799 download_rb_cmp(struct download
*e1
, struct download
*e2
)
801 return (e1
->id
< e2
->id
? -1 : e1
->id
> e2
->id
);
803 RB_GENERATE(download_list
, download
, entry
, download_rb_cmp
);
805 struct valid_url_types
{
816 valid_url_type(char *url
)
820 for (i
= 0; i
< LENGTH(vut
); i
++)
821 if (!strncasecmp(vut
[i
].type
, url
, strlen(vut
[i
].type
)))
828 print_cookie(char *msg
, SoupCookie
*c
)
834 DNPRINTF(XT_D_COOKIE
, "%s\n", msg
);
835 DNPRINTF(XT_D_COOKIE
, "name : %s\n", c
->name
);
836 DNPRINTF(XT_D_COOKIE
, "value : %s\n", c
->value
);
837 DNPRINTF(XT_D_COOKIE
, "domain : %s\n", c
->domain
);
838 DNPRINTF(XT_D_COOKIE
, "path : %s\n", c
->path
);
839 DNPRINTF(XT_D_COOKIE
, "expires : %s\n",
840 c
->expires
? soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "");
841 DNPRINTF(XT_D_COOKIE
, "secure : %d\n", c
->secure
);
842 DNPRINTF(XT_D_COOKIE
, "http_only: %d\n", c
->http_only
);
843 DNPRINTF(XT_D_COOKIE
, "====================================\n");
847 walk_alias(struct settings
*s
,
848 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
853 if (s
== NULL
|| cb
== NULL
)
854 errx(1, "walk_alias");
856 TAILQ_FOREACH(a
, &aliases
, entry
) {
857 str
= g_strdup_printf("%s --> %s", a
->a_name
, a
->a_uri
);
864 match_alias(char *url_in
)
868 char *url_out
= NULL
, *search
;
870 search
= g_strdup(url_in
);
872 if (strsep(&arg
, " \t") == NULL
)
873 errx(1, "match_alias: NULL URL");
875 TAILQ_FOREACH(a
, &aliases
, entry
) {
876 if (!strcmp(search
, a
->a_name
))
881 DNPRINTF(XT_D_URL
, "match_alias: matched alias %s\n",
884 url_out
= g_strdup_printf(a
->a_uri
, arg
);
886 url_out
= g_strdup(a
->a_uri
);
895 guess_url_type(char *url_in
)
898 char *url_out
= NULL
;
900 url_out
= match_alias(url_in
);
904 /* XXX not sure about this heuristic */
905 if (stat(url_in
, &sb
) == 0)
906 url_out
= g_strdup_printf("file://%s", url_in
);
908 url_out
= g_strdup_printf("http://%s", url_in
); /* guess http */
910 DNPRINTF(XT_D_URL
, "guess_url_type: guessed %s\n", url_out
);
916 add_alias(struct settings
*s
, char *line
)
922 errx(1, "add_alias");
925 a
= g_malloc(sizeof(*a
));
927 if ((alias
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
)
928 errx(1, "add_alias: incomplete alias definition");
930 if (strlen(alias
) == 0 || strlen(l
) == 0)
931 errx(1, "add_alias: invalid alias definition");
933 a
->a_name
= g_strdup(alias
);
934 a
->a_uri
= g_strdup(l
);
936 DNPRINTF(XT_D_CONFIG
, "add_alias: %s for %s\n", a
->a_name
, a
->a_uri
);
938 TAILQ_INSERT_TAIL(&aliases
, a
, entry
);
944 add_mime_type(struct settings
*s
, char *line
)
950 /* XXX this could be smarter */
953 errx(1, "add_mime_type");
956 m
= g_malloc(sizeof(*m
));
958 if ((mime_type
= strsep(&l
, " \t,")) == NULL
|| l
== NULL
)
959 errx(1, "add_mime_type: invalid mime_type");
961 if (mime_type
[strlen(mime_type
) - 1] == '*') {
962 mime_type
[strlen(mime_type
) - 1] = '\0';
967 if (strlen(mime_type
) == 0 || strlen(l
) == 0)
968 errx(1, "add_mime_type: invalid mime_type");
970 m
->mt_type
= g_strdup(mime_type
);
971 m
->mt_action
= g_strdup(l
);
973 DNPRINTF(XT_D_CONFIG
, "add_mime_type: type %s action %s default %d\n",
974 m
->mt_type
, m
->mt_action
, m
->mt_default
);
976 TAILQ_INSERT_TAIL(&mtl
, m
, entry
);
982 find_mime_type(char *mime_type
)
984 struct mime_type
*m
, *def
= NULL
, *rv
= NULL
;
986 TAILQ_FOREACH(m
, &mtl
, entry
) {
988 !strncmp(mime_type
, m
->mt_type
, strlen(m
->mt_type
)))
991 if (m
->mt_default
== 0 && !strcmp(mime_type
, m
->mt_type
)) {
1004 walk_mime_type(struct settings
*s
,
1005 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1007 struct mime_type
*m
;
1010 if (s
== NULL
|| cb
== NULL
)
1011 errx(1, "walk_mime_type");
1013 TAILQ_FOREACH(m
, &mtl
, entry
) {
1014 str
= g_strdup_printf("%s%s --> %s",
1016 m
->mt_default
? "*" : "",
1018 cb(s
, str
, cb_args
);
1024 wl_add(char *str
, struct domain_list
*wl
, int handy
)
1029 if (str
== NULL
|| wl
== NULL
)
1031 if (strlen(str
) < 2)
1034 DNPRINTF(XT_D_COOKIE
, "wl_add in: %s\n", str
);
1036 /* treat *.moo.com the same as .moo.com */
1037 if (str
[0] == '*' && str
[1] == '.')
1039 else if (str
[0] == '.')
1044 d
= g_malloc(sizeof *d
);
1046 d
->d
= g_strdup_printf(".%s", str
);
1048 d
->d
= g_strdup(str
);
1051 if (RB_INSERT(domain_list
, wl
, d
))
1054 DNPRINTF(XT_D_COOKIE
, "wl_add: %s\n", d
->d
);
1065 add_cookie_wl(struct settings
*s
, char *entry
)
1067 wl_add(entry
, &c_wl
, 1);
1072 walk_cookie_wl(struct settings
*s
,
1073 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1077 if (s
== NULL
|| cb
== NULL
)
1078 errx(1, "walk_cookie_wl");
1080 RB_FOREACH_REVERSE(d
, domain_list
, &c_wl
)
1081 cb(s
, d
->d
, cb_args
);
1085 walk_js_wl(struct settings
*s
,
1086 void (*cb
)(struct settings
*, char *, void *), void *cb_args
)
1090 if (s
== NULL
|| cb
== NULL
)
1091 errx(1, "walk_js_wl");
1093 RB_FOREACH_REVERSE(d
, domain_list
, &js_wl
)
1094 cb(s
, d
->d
, cb_args
);
1098 add_js_wl(struct settings
*s
, char *entry
)
1100 wl_add(entry
, &js_wl
, 1 /* persistent */);
1105 wl_find(const gchar
*search
, struct domain_list
*wl
)
1108 struct domain
*d
= NULL
, dfind
;
1111 if (search
== NULL
|| wl
== NULL
)
1113 if (strlen(search
) < 2)
1116 if (search
[0] != '.')
1117 s
= g_strdup_printf(".%s", search
);
1119 s
= g_strdup(search
);
1121 for (i
= strlen(s
) - 1; i
>= 0; i
--) {
1124 d
= RB_FIND(domain_list
, wl
, &dfind
);
1138 wl_find_uri(const gchar
*s
, struct domain_list
*wl
)
1144 if (s
== NULL
|| wl
== NULL
)
1147 if (!strncmp(s
, "http://", strlen("http://")))
1148 s
= &s
[strlen("http://")];
1149 else if (!strncmp(s
, "https://", strlen("https://")))
1150 s
= &s
[strlen("https://")];
1155 for (i
= 0; i
< strlen(s
) + 1 /* yes er need this */; i
++)
1156 /* chop string at first slash */
1157 if (s
[i
] == '/' || s
[i
] == '\0') {
1160 r
= wl_find(ss
, wl
);
1169 get_toplevel_domain(char *domain
)
1176 if (strlen(domain
) < 2)
1179 s
= &domain
[strlen(domain
) - 1];
1180 while (s
!= domain
) {
1197 config_parse(char *filename
, int runtime
)
1200 char *line
, *cp
, *var
, *val
;
1201 size_t len
, lineno
= 0;
1203 char **s
, file
[PATH_MAX
];
1206 DNPRINTF(XT_D_CONFIG
, "config_parse: filename %s\n", filename
);
1208 if (filename
== NULL
)
1211 if (runtime
&& runtime_settings
[0] != '\0') {
1212 snprintf(file
, sizeof file
, "%s/%s",
1213 work_dir
, runtime_settings
);
1214 if (stat(file
, &sb
)) {
1215 warnx("runtime file doesn't exist, creating it");
1216 if ((f
= fopen(file
, "w")) == NULL
)
1218 fprintf(f
, "# AUTO GENERATED, DO NOT EDIT\n");
1222 strlcpy(file
, filename
, sizeof file
);
1224 if ((config
= fopen(file
, "r")) == NULL
) {
1225 warn("config_parse: cannot open %s", filename
);
1230 if ((line
= fparseln(config
, &len
, &lineno
, NULL
, 0)) == NULL
)
1231 if (feof(config
) || ferror(config
))
1235 cp
+= (long)strspn(cp
, WS
);
1236 if (cp
[0] == '\0') {
1242 if ((var
= strsep(&cp
, WS
)) == NULL
|| cp
== NULL
)
1245 cp
+= (long)strspn(cp
, WS
);
1247 if ((val
= strsep(&cp
, "\0")) == NULL
)
1250 DNPRINTF(XT_D_CONFIG
, "config_parse: %s=%s\n",var
,val
);
1253 for (i
= 0, handled
= 0; i
< LENGTH(rs
); i
++) {
1254 if (strcmp(var
, rs
[i
].name
))
1258 if (rs
[i
].s
->set(&rs
[i
], val
))
1259 errx(1, "invalid value for %s", var
);
1263 switch (rs
[i
].type
) {
1272 errx(1, "invalid sval for %s",
1281 errx(1, "invalid type for %s", var
);
1287 errx(1, "invalid conf file entry: %s=%s", var
, val
);
1296 js_ref_to_string(JSContextRef context
, JSValueRef ref
)
1302 jsref
= JSValueToStringCopy(context
, ref
, NULL
);
1306 l
= JSStringGetMaximumUTF8CStringSize(jsref
);
1309 JSStringGetUTF8CString(jsref
, s
, l
);
1310 JSStringRelease(jsref
);
1316 disable_hints(struct tab
*t
)
1318 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1319 bzero(t
->hint_num
, sizeof t
->hint_num
);
1320 run_script(t
, "vimprobable_clear()");
1322 t
->hint_mode
= XT_HINT_NONE
;
1326 enable_hints(struct tab
*t
)
1328 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
1329 run_script(t
, "vimprobable_show_hints()");
1331 t
->hint_mode
= XT_HINT_NONE
;
1334 #define XT_JS_OPEN ("open;")
1335 #define XT_JS_OPEN_LEN (strlen(XT_JS_OPEN))
1336 #define XT_JS_FIRE ("fire;")
1337 #define XT_JS_FIRE_LEN (strlen(XT_JS_FIRE))
1338 #define XT_JS_FOUND ("found;")
1339 #define XT_JS_FOUND_LEN (strlen(XT_JS_FOUND))
1342 run_script(struct tab
*t
, char *s
)
1344 JSGlobalContextRef ctx
;
1345 WebKitWebFrame
*frame
;
1347 JSValueRef val
, exception
;
1350 DNPRINTF(XT_D_JS
, "run_script: tab %d %s\n",
1351 t
->tab_id
, s
== (char *)JS_HINTING
? "JS_HINTING" : s
);
1353 frame
= webkit_web_view_get_main_frame(t
->wv
);
1354 ctx
= webkit_web_frame_get_global_context(frame
);
1356 str
= JSStringCreateWithUTF8CString(s
);
1357 val
= JSEvaluateScript(ctx
, str
, JSContextGetGlobalObject(ctx
),
1358 NULL
, 0, &exception
);
1359 JSStringRelease(str
);
1361 DNPRINTF(XT_D_JS
, "run_script: val %p\n", val
);
1363 es
= js_ref_to_string(ctx
, exception
);
1364 DNPRINTF(XT_D_JS
, "run_script: exception %s\n", es
);
1368 es
= js_ref_to_string(ctx
, val
);
1369 DNPRINTF(XT_D_JS
, "run_script: val %s\n", es
);
1371 /* handle return value right here */
1372 if (!strncmp(es
, XT_JS_OPEN
, XT_JS_OPEN_LEN
)) {
1374 webkit_web_view_load_uri(t
->wv
, &es
[XT_JS_OPEN_LEN
]);
1377 if (!strncmp(es
, XT_JS_FIRE
, XT_JS_FIRE_LEN
)) {
1378 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
1379 &es
[XT_JS_FIRE_LEN
]);
1384 if (!strncmp(es
, XT_JS_FOUND
, XT_JS_FOUND_LEN
)) {
1385 if (atoi(&es
[XT_JS_FOUND_LEN
]) == 0)
1396 hint(struct tab
*t
, struct karg
*args
)
1399 DNPRINTF(XT_D_JS
, "hint: tab %d\n", t
->tab_id
);
1401 if (t
->hints_on
== 0)
1409 /* Doesn't work fully, due to the following bug:
1410 * https://bugs.webkit.org/show_bug.cgi?id=51747
1413 restore_global_history(void)
1415 char file
[PATH_MAX
];
1421 snprintf(file
, sizeof file
, "%s/%s/%s",
1422 pwd
->pw_dir
, XT_DIR
, XT_HISTORY_FILE
);
1424 if ((f
= fopen(file
, "r")) == NULL
) {
1425 warnx("%s: fopen", __func__
);
1430 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1431 if (feof(f
) || ferror(f
))
1434 if ((title
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1435 if (feof(f
) || ferror(f
)) {
1437 warnx("%s: broken history file\n", __func__
);
1441 if (uri
&& strlen(uri
) && title
&& strlen(title
)) {
1442 webkit_web_history_item_new_with_data(uri
, title
);
1443 h
= g_malloc(sizeof(struct history
));
1444 h
->uri
= g_strdup(uri
);
1445 h
->title
= g_strdup(title
);
1446 RB_INSERT(history_list
, &hl
, h
);
1448 warnx("%s: failed to restore history\n", __func__
);
1464 save_global_history_to_disk(struct tab
*t
)
1466 char file
[PATH_MAX
];
1470 snprintf(file
, sizeof file
, "%s/%s/%s",
1471 pwd
->pw_dir
, XT_DIR
, XT_HISTORY_FILE
);
1473 if ((f
= fopen(file
, "w")) == NULL
) {
1474 show_oops(t
, "%s: global history file: %s",
1475 __func__
, strerror(errno
));
1479 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
1480 if (h
->uri
&& h
->title
)
1481 fprintf(f
, "%s\n%s\n", h
->uri
, h
->title
);
1490 quit(struct tab
*t
, struct karg
*args
)
1492 if (save_global_history
)
1493 save_global_history_to_disk(t
);
1501 open_tabs(struct tab
*t
, struct karg
*a
)
1503 char file
[PATH_MAX
];
1511 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1513 if ((f
= fopen(file
, "r")) == NULL
)
1517 if ((uri
= fparseln(f
, NULL
, NULL
, NULL
, 0)) == NULL
)
1518 if (feof(f
) || ferror(f
))
1521 if (uri
&& strlen(uri
)) {
1522 create_new_tab(uri
, NULL
, 1);
1538 restore_saved_tabs(void)
1540 char file
[PATH_MAX
];
1541 int unlink_file
= 0;
1546 snprintf(file
, sizeof file
, "%s/%s",
1547 sessions_dir
, XT_RESTART_TABS_FILE
);
1548 if (stat(file
, &sb
) == -1)
1549 a
.s
= XT_SAVED_TABS_FILE
;
1552 a
.s
= XT_RESTART_TABS_FILE
;
1555 rv
= open_tabs(NULL
, &a
);
1564 save_tabs(struct tab
*t
, struct karg
*a
)
1566 char file
[PATH_MAX
];
1569 WebKitWebFrame
*frame
;
1577 snprintf(file
, sizeof file
, "%s/%s", sessions_dir
, a
->s
);
1579 if ((f
= fopen(file
, "w")) == NULL
) {
1580 show_oops(t
, "Can't open save_tabs file: %s", strerror(errno
));
1584 TAILQ_FOREACH(ti
, &tabs
, entry
) {
1585 frame
= webkit_web_view_get_main_frame(ti
->wv
);
1586 uri
= webkit_web_frame_get_uri(frame
);
1587 if (uri
&& strlen(uri
) > 0)
1588 fprintf(f
, "%s\n", uri
);
1597 save_tabs_and_quit(struct tab
*t
, struct karg
*args
)
1601 a
.s
= XT_SAVED_TABS_FILE
;
1609 yank_uri(struct tab
*t
, struct karg
*args
)
1611 WebKitWebFrame
*frame
;
1613 GtkClipboard
*clipboard
;
1615 frame
= webkit_web_view_get_main_frame(t
->wv
);
1616 uri
= webkit_web_frame_get_uri(frame
);
1620 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1621 gtk_clipboard_set_text(clipboard
, uri
, -1);
1632 paste_uri_cb(GtkClipboard
*clipboard
, const gchar
*text
, gpointer data
)
1634 struct paste_args
*pap
;
1639 pap
= (struct paste_args
*)data
;
1642 case XT_PASTE_CURRENT_TAB
:
1643 webkit_web_view_load_uri(pap
->t
->wv
, text
);
1645 case XT_PASTE_NEW_TAB
:
1646 create_new_tab((char *)text
, NULL
, 1);
1654 paste_uri(struct tab
*t
, struct karg
*args
)
1656 GtkClipboard
*clipboard
;
1657 struct paste_args
*pap
;
1659 pap
= g_malloc(sizeof(struct paste_args
));
1664 clipboard
= gtk_clipboard_get(GDK_SELECTION_PRIMARY
);
1665 gtk_clipboard_request_text(clipboard
, paste_uri_cb
, pap
);
1671 find_domain(const char *s
, int add_dot
)
1674 char *r
= NULL
, *ss
= NULL
;
1679 if (!strncmp(s
, "http://", strlen("http://")))
1680 s
= &s
[strlen("http://")];
1681 else if (!strncmp(s
, "https://", strlen("https://")))
1682 s
= &s
[strlen("https://")];
1688 for (i
= 0; i
< strlen(ss
) + 1 /* yes er need this */; i
++)
1689 /* chop string at first slash */
1690 if (ss
[i
] == '/' || ss
[i
] == '\0') {
1693 r
= g_strdup_printf(".%s", ss
);
1704 toggle_cwl(struct tab
*t
, struct karg
*args
)
1706 WebKitWebFrame
*frame
;
1709 char *dom
= NULL
, *dom_toggle
= NULL
;
1715 frame
= webkit_web_view_get_main_frame(t
->wv
);
1716 uri
= (char *)webkit_web_frame_get_uri(frame
);
1717 dom
= find_domain(uri
, 1);
1718 d
= wl_find(dom
, &c_wl
);
1724 if (args
->i
& XT_WL_TOGGLE
)
1726 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
1728 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
1731 if (args
->i
& XT_WL_TOPLEVEL
)
1732 dom_toggle
= get_toplevel_domain(dom
);
1737 /* enable cookies for domain */
1738 wl_add(dom_toggle
, &c_wl
, 0);
1740 /* disable cookies for domain */
1741 RB_REMOVE(domain_list
, &c_wl
, d
);
1744 webkit_web_view_reload(t
->wv
);
1751 toggle_js(struct tab
*t
, struct karg
*args
)
1754 WebKitWebFrame
*frame
;
1757 char *dom
= NULL
, *dom_toggle
= NULL
;
1762 g_object_get((GObject
*)t
->settings
,
1763 "enable-scripts", &es
, (char *)NULL
);
1764 if (args
->i
& XT_WL_TOGGLE
)
1766 else if ((args
->i
& XT_WL_ENABLE
) && es
!= 1)
1768 else if ((args
->i
& XT_WL_DISABLE
) && es
!= 0)
1773 frame
= webkit_web_view_get_main_frame(t
->wv
);
1774 uri
= (char *)webkit_web_frame_get_uri(frame
);
1775 dom
= find_domain(uri
, 1);
1776 if (uri
== NULL
|| dom
== NULL
) {
1777 show_oops(t
, "Can't toggle domain in JavaScript white list");
1781 if (args
->i
& XT_WL_TOPLEVEL
)
1782 dom_toggle
= get_toplevel_domain(dom
);
1787 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PLAY
);
1788 wl_add(dom_toggle
, &js_wl
, 0 /* session */);
1790 d
= wl_find(dom_toggle
, &js_wl
);
1792 RB_REMOVE(domain_list
, &js_wl
, d
);
1793 button_set_stockid(t
->js_toggle
, GTK_STOCK_MEDIA_PAUSE
);
1795 g_object_set((GObject
*)t
->settings
,
1796 "enable-scripts", es
, (char *)NULL
);
1797 webkit_web_view_set_settings(t
->wv
, t
->settings
);
1798 webkit_web_view_reload(t
->wv
);
1806 js_toggle_cb(GtkWidget
*w
, struct tab
*t
)
1810 a
.i
= XT_WL_TOGGLE
| XT_WL_FQDN
;
1815 toggle_src(struct tab
*t
, struct karg
*args
)
1822 mode
= webkit_web_view_get_view_source_mode(t
->wv
);
1823 webkit_web_view_set_view_source_mode(t
->wv
, !mode
);
1824 webkit_web_view_reload(t
->wv
);
1830 focus(struct tab
*t
, struct karg
*args
)
1832 if (t
== NULL
|| args
== NULL
)
1835 if (args
->i
== XT_FOCUS_URI
)
1836 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
1837 else if (args
->i
== XT_FOCUS_SEARCH
)
1838 gtk_widget_grab_focus(GTK_WIDGET(t
->search_entry
));
1844 stats(struct tab
*t
, struct karg
*args
)
1846 char *stats
, *s
, line
[64 * 1024];
1847 uint64_t line_count
= 0;
1854 if (save_rejected_cookies
) {
1855 if ((r_cookie_f
= fopen(rc_fname
, "r"))) {
1857 s
= fgets(line
, sizeof line
, r_cookie_f
);
1858 if (s
== NULL
|| feof(r_cookie_f
) ||
1864 snprintf(line
, sizeof line
,
1865 "<br>Cookies blocked(*) total: %llu", line_count
);
1867 show_oops(t
, "Can't open blocked cookies file: %s",
1871 stats
= g_strdup_printf(XT_DOCTYPE
1874 "<title>Statistics</title>"
1876 "<h1>Statistics</h1>"
1878 "Cookies blocked(*) this session: %llu"
1880 "<p><small><b>*</b> results vary based on settings"
1886 load_webkit_string(t
, stats
);
1893 about(struct tab
*t
, struct karg
*args
)
1900 about
= g_strdup_printf(XT_DOCTYPE
1903 "<title>About</title>"
1907 "<b>Version: %s</b><p>"
1910 "<li>Marco Peereboom <marco@peereboom.us></li>"
1911 "<li>Stevan Andjelkovic <stevan@student.chalmers.se></li>"
1912 "<li>Edd Barrett <vext01@gmail.com> </li>"
1914 "Copyrights and licenses can be found on the XXXterm "
1915 "<a href=\"http://opensource.conformal.com/wiki/XXXTerm\">website</a>"
1921 load_webkit_string(t
, about
);
1928 help(struct tab
*t
, struct karg
*args
)
1938 "<title>XXXterm</title>"
1939 "<meta http-equiv=\"REFRESH\" content=\"0;"
1940 "url=http://opensource.conformal.com/cgi-bin/man-cgi?xxxterm\">"
1943 "XXXterm man page <a href=\"http://opensource.conformal.com/"
1944 "cgi-bin/man-cgi?xxxterm\">http://opensource.conformal.com/"
1945 "cgi-bin/man-cgi?xxxterm</a>"
1950 load_webkit_string(t
, help
);
1956 * update all favorite tabs apart from one. Pass NULL if
1957 * you want to update all.
1960 update_favorite_tabs(struct tab
*apart_from
)
1963 if (!updating_fl_tabs
) {
1964 updating_fl_tabs
= 1; /* stop infinite recursion */
1965 TAILQ_FOREACH(t
, &tabs
, entry
)
1966 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_FL
)
1967 && (t
!= apart_from
))
1968 xtp_page_fl(t
, NULL
);
1969 updating_fl_tabs
= 0;
1973 /* show a list of favorites (bookmarks) */
1975 xtp_page_fl(struct tab
*t
, struct karg
*args
)
1977 char file
[PATH_MAX
];
1979 char *uri
= NULL
, *title
= NULL
;
1980 size_t len
, lineno
= 0;
1982 char *header
, *body
, *tmp
, *html
= NULL
;
1984 DNPRINTF(XT_D_FAVORITE
, "%s:", __func__
);
1987 warn("%s: bad param", __func__
);
1989 /* mark tab as favorite list */
1990 t
->xtp_meaning
= XT_XTP_TAB_MEANING_FL
;
1992 /* new session key */
1993 if (!updating_fl_tabs
)
1994 generate_xtp_session_key(&fl_session_key
);
1996 /* open favorites */
1997 snprintf(file
, sizeof file
, "%s/%s/%s",
1998 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
1999 if ((f
= fopen(file
, "r")) == NULL
) {
2000 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2005 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
2006 "<title>Favorites</title>\n"
2009 "<h1>Favorites</h1>\n",
2013 body
= g_strdup_printf("<div align='center'><table><tr>"
2014 "<th style='width: 4%%'>#</th><th>Link</th>"
2015 "<th style='width: 15%%'>Remove</th></tr>\n");
2018 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2019 if (feof(f
) || ferror(f
))
2027 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
2028 if (feof(f
) || ferror(f
)) {
2029 errx(0, "%s: can't parse favorites\n",
2036 body
= g_strdup_printf("%s<tr>"
2038 "<td><a href='%s'>%s</a></td>"
2039 "<td style='text-align: center'>"
2040 "<a href='%s%d/%s/%d/%d'>X</a></td>"
2042 body
, i
, uri
, title
,
2043 XT_XTP_STR
, XT_XTP_FL
, fl_session_key
, XT_XTP_FL_REMOVE
, i
);
2055 /* if none, say so */
2058 body
= g_strdup_printf("%s<tr>"
2059 "<td colspan='3' style='text-align: center'>"
2060 "No favorites - To add one use the 'favadd' command."
2061 "</td></tr>", body
);
2072 html
= g_strdup_printf("%s%s</table></div></html>",
2074 load_webkit_string(t
, html
);
2077 update_favorite_tabs(t
);
2090 getparams(char *cmd
, char *cmp
)
2095 if (!strncmp(cmd
, cmp
, strlen(cmp
))) {
2096 rv
= cmd
+ strlen(cmp
);
2099 if (strlen(rv
) == 0)
2108 show_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2109 size_t cert_count
, char *title
)
2111 gnutls_datum_t cinfo
;
2112 char *tmp
, *header
, *body
, *footer
;
2115 header
= g_strdup_printf("<title>%s</title><html><body>", title
);
2116 footer
= g_strdup("</body></html>");
2117 body
= g_strdup("");
2119 for (i
= 0; i
< cert_count
; i
++) {
2120 if (gnutls_x509_crt_print(certs
[i
], GNUTLS_CRT_PRINT_FULL
,
2125 body
= g_strdup_printf("%s<h2>Cert #%d</h2><pre>%s</pre>",
2126 body
, i
, cinfo
.data
);
2127 gnutls_free(cinfo
.data
);
2131 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2135 load_webkit_string(t
, tmp
);
2140 ca_cmd(struct tab
*t
, struct karg
*args
)
2143 int rv
= 1, certs
= 0, certs_read
;
2146 gnutls_x509_crt_t
*c
= NULL
;
2147 char *certs_buf
= NULL
, *s
;
2149 /* yeah yeah stat race */
2150 if (stat(ssl_ca_file
, &sb
)) {
2151 show_oops(t
, "no CA file: %s", ssl_ca_file
);
2155 if ((f
= fopen(ssl_ca_file
, "r")) == NULL
) {
2156 show_oops(t
, "Can't open CA file: %s", strerror(errno
));
2160 certs_buf
= g_malloc(sb
.st_size
+ 1);
2161 if (fread(certs_buf
, 1, sb
.st_size
, f
) != sb
.st_size
) {
2162 show_oops(t
, "Can't read CA file: %s", strerror(errno
));
2165 certs_buf
[sb
.st_size
] = '\0';
2168 while ((s
= strstr(s
, "BEGIN CERTIFICATE"))) {
2170 s
+= strlen("BEGIN CERTIFICATE");
2173 bzero(&dt
, sizeof dt
);
2174 dt
.data
= certs_buf
;
2175 dt
.size
= sb
.st_size
;
2176 c
= g_malloc(sizeof(gnutls_x509_crt_t
) * certs
);
2177 certs_read
= gnutls_x509_crt_list_import(c
, &certs
, &dt
, GNUTLS_X509_FMT_PEM
, 0);
2178 if (certs_read
<= 0) {
2179 show_oops(t
, "No cert(s) available");
2182 show_certs(t
, c
, certs_read
, "Certificate Authority Certificates");
2195 connect_socket_from_uri(char *uri
, char *domain
, size_t domain_sz
)
2198 struct addrinfo hints
, *res
= NULL
, *ai
;
2202 if (uri
&& !g_str_has_prefix(uri
, "https://"))
2205 su
= soup_uri_new(uri
);
2208 if (!SOUP_URI_VALID_FOR_HTTP(su
))
2211 snprintf(port
, sizeof port
, "%d", su
->port
);
2212 bzero(&hints
, sizeof(struct addrinfo
));
2213 hints
.ai_flags
= AI_CANONNAME
;
2214 hints
.ai_family
= AF_UNSPEC
;
2215 hints
.ai_socktype
= SOCK_STREAM
;
2217 if (getaddrinfo(su
->host
, port
, &hints
, &res
))
2220 for (ai
= res
; ai
; ai
= ai
->ai_next
) {
2221 if (ai
->ai_family
!= AF_INET
&& ai
->ai_family
!= AF_INET6
)
2224 s
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
2227 if (setsockopt(s
, SOL_SOCKET
, SO_REUSEADDR
, &on
,
2231 if (connect(s
, ai
->ai_addr
, ai
->ai_addrlen
) < 0)
2236 strlcpy(domain
, su
->host
, domain_sz
);
2247 stop_tls(gnutls_session_t gsession
, gnutls_certificate_credentials_t xcred
)
2250 gnutls_deinit(gsession
);
2252 gnutls_certificate_free_credentials(xcred
);
2258 start_tls(struct tab
*t
, int s
, gnutls_session_t
*gs
,
2259 gnutls_certificate_credentials_t
*xc
)
2261 gnutls_certificate_credentials_t xcred
;
2262 gnutls_session_t gsession
;
2265 if (gs
== NULL
|| xc
== NULL
)
2271 gnutls_certificate_allocate_credentials(&xcred
);
2272 gnutls_certificate_set_x509_trust_file(xcred
, ssl_ca_file
,
2273 GNUTLS_X509_FMT_PEM
);
2274 gnutls_init(&gsession
, GNUTLS_CLIENT
);
2275 gnutls_priority_set_direct(gsession
, "PERFORMANCE", NULL
);
2276 gnutls_credentials_set(gsession
, GNUTLS_CRD_CERTIFICATE
, xcred
);
2277 gnutls_transport_set_ptr(gsession
, (gnutls_transport_ptr_t
)(long)s
);
2278 if ((rv
= gnutls_handshake(gsession
)) < 0) {
2279 show_oops(t
, "gnutls_handshake failed %d fatal %d %s",
2281 gnutls_error_is_fatal(rv
),
2282 gnutls_strerror_name(rv
));
2283 stop_tls(gsession
, xcred
);
2287 gnutls_credentials_type_t cred
;
2288 cred
= gnutls_auth_get_type(gsession
);
2289 if (cred
!= GNUTLS_CRD_CERTIFICATE
) {
2290 stop_tls(gsession
, xcred
);
2302 get_connection_certs(gnutls_session_t gsession
, gnutls_x509_crt_t
**certs
,
2306 const gnutls_datum_t
*cl
;
2307 gnutls_x509_crt_t
*all_certs
;
2310 if (certs
== NULL
|| cert_count
== NULL
)
2312 if (gnutls_certificate_type_get(gsession
) != GNUTLS_CRT_X509
)
2314 cl
= gnutls_certificate_get_peers(gsession
, &len
);
2318 all_certs
= g_malloc(sizeof(gnutls_x509_crt_t
) * len
);
2319 for (i
= 0; i
< len
; i
++) {
2320 gnutls_x509_crt_init(&all_certs
[i
]);
2321 if (gnutls_x509_crt_import(all_certs
[i
], &cl
[i
],
2322 GNUTLS_X509_FMT_PEM
< 0)) {
2336 free_connection_certs(gnutls_x509_crt_t
*certs
, size_t cert_count
)
2340 for (i
= 0; i
< cert_count
; i
++)
2341 gnutls_x509_crt_deinit(certs
[i
]);
2346 save_certs(struct tab
*t
, gnutls_x509_crt_t
*certs
,
2347 size_t cert_count
, char *domain
)
2350 char cert_buf
[64 * 1024], file
[PATH_MAX
];
2355 if (t
== NULL
|| certs
== NULL
|| cert_count
<= 0 || domain
== NULL
)
2358 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2359 if ((f
= fopen(file
, "w")) == NULL
) {
2360 show_oops(t
, "Can't create cert file %s %s",
2361 file
, strerror(errno
));
2365 for (i
= 0; i
< cert_count
; i
++) {
2366 cert_buf_sz
= sizeof cert_buf
;
2367 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2368 cert_buf
, &cert_buf_sz
)) {
2369 show_oops(t
, "gnutls_x509_crt_export failed");
2372 if (fwrite(cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2373 show_oops(t
, "Can't write certs: %s", strerror(errno
));
2378 /* not the best spot but oh well */
2379 gdk_color_parse("lightblue", &color
);
2380 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
2386 load_compare_cert(struct tab
*t
, struct karg
*args
)
2388 WebKitWebFrame
*frame
;
2389 char *uri
, domain
[8182], file
[PATH_MAX
];
2390 char cert_buf
[64 * 1024], r_cert_buf
[64 * 1024];
2391 int s
= -1, rv
= 1, i
;
2395 gnutls_session_t gsession
;
2396 gnutls_x509_crt_t
*certs
;
2397 gnutls_certificate_credentials_t xcred
;
2402 frame
= webkit_web_view_get_main_frame(t
->wv
);
2403 uri
= (char *)webkit_web_frame_get_uri(frame
);
2404 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1)
2408 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2409 show_oops(t
, "Start TLS failed");
2414 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2415 show_oops(t
, "Can't get connection certificates");
2419 snprintf(file
, sizeof file
, "%s/%s", certs_dir
, domain
);
2420 if ((f
= fopen(file
, "r")) == NULL
)
2423 for (i
= 0; i
< cert_count
; i
++) {
2424 cert_buf_sz
= sizeof cert_buf
;
2425 if (gnutls_x509_crt_export(certs
[i
], GNUTLS_X509_FMT_PEM
,
2426 cert_buf
, &cert_buf_sz
)) {
2429 if (fread(r_cert_buf
, cert_buf_sz
, 1, f
) != 1) {
2430 rv
= -1; /* critical */
2433 if (bcmp(r_cert_buf
, cert_buf
, sizeof cert_buf_sz
)) {
2434 rv
= -1; /* critical */
2443 free_connection_certs(certs
, cert_count
);
2445 /* we close the socket first for speed */
2448 stop_tls(gsession
, xcred
);
2454 cert_cmd(struct tab
*t
, struct karg
*args
)
2456 WebKitWebFrame
*frame
;
2457 char *uri
, *action
, domain
[8182];
2460 gnutls_session_t gsession
;
2461 gnutls_x509_crt_t
*certs
;
2462 gnutls_certificate_credentials_t xcred
;
2467 if ((action
= getparams(args
->s
, "cert")))
2472 frame
= webkit_web_view_get_main_frame(t
->wv
);
2473 uri
= (char *)webkit_web_frame_get_uri(frame
);
2474 if (uri
&& strlen(uri
) == 0) {
2475 show_oops(t
, "Invalid URI");
2477 if ((s
= connect_socket_from_uri(uri
, domain
, sizeof domain
)) == -1) {
2478 show_oops(t
, "Invalid certidicate URI: %s", uri
);
2483 if (start_tls(t
, s
, &gsession
, &xcred
)) {
2484 show_oops(t
, "Start TLS failed");
2489 if (get_connection_certs(gsession
, &certs
, &cert_count
)) {
2490 show_oops(t
, "get_connection_certs failed");
2494 if (!strcmp(action
, "show"))
2495 show_certs(t
, certs
, cert_count
, "Certificate Chain");
2496 else if (!strcmp(action
, "save"))
2497 save_certs(t
, certs
, cert_count
, domain
);
2499 show_oops(t
, "Invalid command: %s", action
);
2501 free_connection_certs(certs
, cert_count
);
2503 /* we close the socket first for speed */
2506 stop_tls(gsession
, xcred
);
2512 remove_cookie(int index
)
2518 DNPRINTF(XT_D_COOKIE
, "remove_cookie: %d\n", index
);
2520 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2522 for (i
= 1; cf
; cf
= cf
->next
, i
++) {
2526 print_cookie("remove cookie", c
);
2527 soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
2532 soup_cookies_free(cf
);
2538 wl_show(struct tab
*t
, char *args
, char *title
, struct domain_list
*wl
)
2541 char *tmp
, *header
, *body
, *footer
;
2542 int p_js
= 0, s_js
= 0;
2544 /* we set this to indicate we want to manually do navaction */
2545 t
->item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2547 if (g_str_has_prefix(args
, "show a") ||
2548 !strcmp(args
, "show")) {
2552 } else if (g_str_has_prefix(args
, "show p")) {
2553 /* show persistent */
2555 } else if (g_str_has_prefix(args
, "show s")) {
2561 header
= g_strdup_printf("<title>%s</title><html><body><h1>%s</h1>",
2563 footer
= g_strdup("</body></html>");
2564 body
= g_strdup("");
2569 body
= g_strdup_printf("%s<h2>Persistent</h2>", body
);
2571 RB_FOREACH(d
, domain_list
, wl
) {
2575 body
= g_strdup_printf("%s%s<br>", body
, d
->d
);
2583 body
= g_strdup_printf("%s<h2>Session</h2>", body
);
2585 RB_FOREACH(d
, domain_list
, wl
) {
2589 body
= g_strdup_printf("%s%s", body
, d
->d
);
2594 tmp
= g_strdup_printf("%s%s%s", header
, body
, footer
);
2598 load_webkit_string(t
, tmp
);
2604 wl_save(struct tab
*t
, struct karg
*args
, int js
)
2606 char file
[PATH_MAX
];
2608 char *line
= NULL
, *lt
= NULL
;
2610 WebKitWebFrame
*frame
;
2611 char *dom
= NULL
, *uri
, *dom_save
= NULL
;
2618 if (t
== NULL
|| args
== NULL
)
2621 if (runtime_settings
[0] == '\0')
2624 snprintf(file
, sizeof file
, "%s/%s", work_dir
, runtime_settings
);
2625 if ((f
= fopen(file
, "r+")) == NULL
)
2628 frame
= webkit_web_view_get_main_frame(t
->wv
);
2629 uri
= (char *)webkit_web_frame_get_uri(frame
);
2630 dom
= find_domain(uri
, 1);
2631 if (uri
== NULL
|| dom
== NULL
) {
2632 show_oops(t
, "Can't add domain to %s white list",
2633 js
? "JavaScript" : "cookie");
2637 if (g_str_has_prefix(args
->s
, "save d")) {
2639 if ((dom_save
= get_toplevel_domain(dom
)) == NULL
) {
2640 show_oops(t
, "invalid domain: %s", dom
);
2643 flags
= XT_WL_TOPLEVEL
;
2644 } else if (g_str_has_prefix(args
->s
, "save f") ||
2645 !strcmp(args
->s
, "save")) {
2650 show_oops(t
, "invalid command: %s", args
->s
);
2654 lt
= g_strdup_printf("%s=%s", js
? "js_wl" : "cookie_wl", dom_save
);
2657 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
2660 if (!strcmp(line
, lt
))
2666 fprintf(f
, "%s\n", lt
);
2671 d
= wl_find(dom_save
, &js_wl
);
2674 d
= wl_find(dom_save
, &c_wl
);
2677 /* find and add to persistent jar */
2678 cf
= soup_cookie_jar_all_cookies(s_cookiejar
);
2679 for (;cf
; cf
= cf
->next
) {
2681 if (!strcmp(dom_save
, ci
->domain
) ||
2682 !strcmp(&dom_save
[1], ci
->domain
)) /* deal with leading . */ {
2683 c
= soup_cookie_copy(ci
);
2684 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
2687 soup_cookies_free(cf
);
2705 cookie_cmd(struct tab
*t
, struct karg
*args
)
2710 if ((cmd
= getparams(args
->s
, "cookie")))
2716 if (g_str_has_prefix(cmd
, "show")) {
2717 wl_show(t
, cmd
, "Cookie White List", &c_wl
);
2718 } else if (g_str_has_prefix(cmd
, "save")) {
2721 } else if (g_str_has_prefix(cmd
, "toggle")) {
2723 if (g_str_has_prefix(cmd
, "toggle d"))
2724 a
.i
|= XT_WL_TOPLEVEL
;
2728 } else if (g_str_has_prefix(cmd
, "delete")) {
2729 show_oops(t
, "'cookie delete' currently unimplemented");
2731 show_oops(t
, "unknown cookie command: %s", cmd
);
2737 js_cmd(struct tab
*t
, struct karg
*args
)
2742 if ((cmd
= getparams(args
->s
, "js")))
2747 if (g_str_has_prefix(cmd
, "show")) {
2748 wl_show(t
, cmd
, "JavaScript White List", &js_wl
);
2749 } else if (g_str_has_prefix(cmd
, "save")) {
2752 } else if (g_str_has_prefix(cmd
, "toggle")) {
2754 if (g_str_has_prefix(cmd
, "toggle d"))
2755 a
.i
|= XT_WL_TOPLEVEL
;
2759 } else if (g_str_has_prefix(cmd
, "delete")) {
2760 show_oops(t
, "'js delete' currently unimplemented");
2762 show_oops(t
, "unknown js command: %s", cmd
);
2768 add_favorite(struct tab
*t
, struct karg
*args
)
2770 char file
[PATH_MAX
];
2773 size_t urilen
, linelen
;
2774 WebKitWebFrame
*frame
;
2775 const gchar
*uri
, *title
;
2780 /* don't allow adding of xtp pages to favorites */
2781 if (t
->xtp_meaning
!= XT_XTP_TAB_MEANING_NORMAL
) {
2782 show_oops(t
, "%s: can't add xtp pages to favorites", __func__
);
2786 snprintf(file
, sizeof file
, "%s/%s/%s",
2787 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
2788 if ((f
= fopen(file
, "r+")) == NULL
) {
2789 show_oops(t
, "Can't open favorites file: %s", strerror(errno
));
2793 title
= webkit_web_view_get_title(t
->wv
);
2794 frame
= webkit_web_view_get_main_frame(t
->wv
);
2795 uri
= webkit_web_frame_get_uri(frame
);
2799 if (title
== NULL
|| uri
== NULL
) {
2800 show_oops(t
, "can't add page to favorites");
2804 urilen
= strlen(uri
);
2807 line
= fparseln(f
, &linelen
, NULL
, NULL
, 0);
2808 if (linelen
== urilen
&& !strcmp(line
, uri
))
2814 fprintf(f
, "\n%s\n%s", title
, uri
);
2820 update_favorite_tabs(NULL
);
2826 navaction(struct tab
*t
, struct karg
*args
)
2828 WebKitWebHistoryItem
*item
;
2830 DNPRINTF(XT_D_NAV
, "navaction: tab %d opcode %d\n",
2831 t
->tab_id
, args
->i
);
2834 if (args
->i
== XT_NAV_BACK
)
2835 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
2837 item
= webkit_web_back_forward_list_get_forward_item(t
->bfl
);
2839 return (XT_CB_PASSTHROUGH
);;
2840 webkit_web_view_load_uri(t
->wv
, webkit_web_history_item_get_uri(item
));
2842 return (XT_CB_PASSTHROUGH
);
2847 webkit_web_view_go_back(t
->wv
);
2849 case XT_NAV_FORWARD
:
2850 webkit_web_view_go_forward(t
->wv
);
2853 webkit_web_view_reload(t
->wv
);
2855 case XT_NAV_RELOAD_CACHE
:
2856 webkit_web_view_reload_bypass_cache(t
->wv
);
2859 return (XT_CB_PASSTHROUGH
);
2863 move(struct tab
*t
, struct karg
*args
)
2865 GtkAdjustment
*adjust
;
2866 double pi
, si
, pos
, ps
, upper
, lower
, max
;
2871 case XT_MOVE_BOTTOM
:
2873 case XT_MOVE_PAGEDOWN
:
2874 case XT_MOVE_PAGEUP
:
2875 case XT_MOVE_HALFDOWN
:
2876 case XT_MOVE_HALFUP
:
2877 adjust
= t
->adjust_v
;
2880 adjust
= t
->adjust_h
;
2884 pos
= gtk_adjustment_get_value(adjust
);
2885 ps
= gtk_adjustment_get_page_size(adjust
);
2886 upper
= gtk_adjustment_get_upper(adjust
);
2887 lower
= gtk_adjustment_get_lower(adjust
);
2888 si
= gtk_adjustment_get_step_increment(adjust
);
2889 pi
= gtk_adjustment_get_page_increment(adjust
);
2892 DNPRINTF(XT_D_MOVE
, "move: opcode %d %s pos %f ps %f upper %f lower %f "
2893 "max %f si %f pi %f\n",
2894 args
->i
, adjust
== t
->adjust_h
? "horizontal" : "vertical",
2895 pos
, ps
, upper
, lower
, max
, si
, pi
);
2901 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2906 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2908 case XT_MOVE_BOTTOM
:
2909 case XT_MOVE_FARRIGHT
:
2910 gtk_adjustment_set_value(adjust
, max
);
2913 case XT_MOVE_FARLEFT
:
2914 gtk_adjustment_set_value(adjust
, lower
);
2916 case XT_MOVE_PAGEDOWN
:
2918 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2920 case XT_MOVE_PAGEUP
:
2922 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2924 case XT_MOVE_HALFDOWN
:
2926 gtk_adjustment_set_value(adjust
, MIN(pos
, max
));
2928 case XT_MOVE_HALFUP
:
2930 gtk_adjustment_set_value(adjust
, MAX(pos
, lower
));
2933 return (XT_CB_PASSTHROUGH
);
2936 DNPRINTF(XT_D_MOVE
, "move: new pos %f %f\n", pos
, MIN(pos
, max
));
2938 return (XT_CB_HANDLED
);
2942 tabaction(struct tab
*t
, struct karg
*args
)
2944 int rv
= XT_CB_HANDLED
;
2945 char *url
= NULL
, *newuri
= NULL
;
2948 DNPRINTF(XT_D_TAB
, "tabaction: %p %d %d\n", t
, args
->i
, t
->focus_wv
);
2951 return (XT_CB_PASSTHROUGH
);
2955 if ((url
= getparams(args
->s
, "tabnew")))
2956 create_new_tab(url
, NULL
, 1);
2958 create_new_tab(NULL
, NULL
, 1);
2963 case XT_TAB_DELQUIT
:
2964 if (gtk_notebook_get_n_pages(notebook
) > 1)
2970 if ((url
= getparams(args
->s
, "open")) ||
2971 ((url
= getparams(args
->s
, "op"))) ||
2972 ((url
= getparams(args
->s
, "o"))))
2975 rv
= XT_CB_PASSTHROUGH
;
2979 if (valid_url_type(url
)) {
2980 newuri
= guess_url_type(url
);
2983 webkit_web_view_load_uri(t
->wv
, url
);
2987 case XT_TAB_UNDO_CLOSE
:
2988 if (undo_count
== 0) {
2989 DNPRINTF(XT_D_TAB
, "%s: no tabs to undo close", __func__
);
2993 u
= TAILQ_FIRST(&undos
);
2994 create_new_tab(u
->uri
, u
, 1);
2996 TAILQ_REMOVE(&undos
, u
, entry
);
2998 /* u->history is freed in create_new_tab() */
3003 rv
= XT_CB_PASSTHROUGH
;
3017 resizetab(struct tab
*t
, struct karg
*args
)
3019 if (t
== NULL
|| args
== NULL
)
3020 errx(1, "resizetab");
3022 DNPRINTF(XT_D_TAB
, "resizetab: tab %d %d\n",
3023 t
->tab_id
, args
->i
);
3025 adjustfont_webkit(t
, args
->i
);
3027 return (XT_CB_HANDLED
);
3031 movetab(struct tab
*t
, struct karg
*args
)
3036 if (t
== NULL
|| args
== NULL
)
3039 DNPRINTF(XT_D_TAB
, "movetab: tab %d opcode %d\n",
3040 t
->tab_id
, args
->i
);
3042 if (args
->i
== XT_TAB_INVALID
)
3043 return (XT_CB_PASSTHROUGH
);
3045 if (args
->i
< XT_TAB_INVALID
) {
3046 /* next or previous tab */
3047 if (TAILQ_EMPTY(&tabs
))
3048 return (XT_CB_PASSTHROUGH
);
3052 /* if at the last page, loop around to the first */
3053 if (gtk_notebook_get_current_page(notebook
) ==
3054 gtk_notebook_get_n_pages(notebook
) - 1) {
3055 gtk_notebook_set_current_page(notebook
, 0);
3057 gtk_notebook_next_page(notebook
);
3061 /* if at the first page, loop around to the last */
3062 if (gtk_notebook_current_page(notebook
) == 0) {
3063 gtk_notebook_set_current_page(notebook
,
3064 gtk_notebook_get_n_pages(notebook
) - 1);
3066 gtk_notebook_prev_page(notebook
);
3070 gtk_notebook_set_current_page(notebook
, 0);
3073 gtk_notebook_set_current_page(notebook
, -1);
3076 return (XT_CB_PASSTHROUGH
);
3079 return (XT_CB_HANDLED
);
3084 if (t
->tab_id
== x
) {
3085 DNPRINTF(XT_D_TAB
, "movetab: do nothing\n");
3086 return (XT_CB_HANDLED
);
3089 TAILQ_FOREACH(tt
, &tabs
, entry
) {
3090 if (tt
->tab_id
== x
) {
3091 gtk_notebook_set_current_page(notebook
, x
);
3092 DNPRINTF(XT_D_TAB
, "movetab: going to %d\n", x
);
3094 gtk_widget_grab_focus(GTK_WIDGET(tt
->wv
));
3098 return (XT_CB_HANDLED
);
3102 command(struct tab
*t
, struct karg
*args
)
3104 WebKitWebFrame
*frame
;
3105 char *s
= NULL
, *ss
= NULL
;
3109 if (t
== NULL
|| args
== NULL
)
3128 case XT_CMD_OPEN_CURRENT
:
3131 case XT_CMD_TABNEW_CURRENT
:
3132 if (!s
) /* FALL THROUGH? */
3134 frame
= webkit_web_view_get_main_frame(t
->wv
);
3135 uri
= webkit_web_frame_get_uri(frame
);
3136 if (uri
&& strlen(uri
)) {
3137 ss
= g_strdup_printf("%s%s", s
, uri
);
3142 show_oops(t
, "command: invalid opcode %d", args
->i
);
3143 return (XT_CB_PASSTHROUGH
);
3146 DNPRINTF(XT_D_CMD
, "command: type %s\n", s
);
3148 gtk_entry_set_text(GTK_ENTRY(t
->cmd
), s
);
3149 gdk_color_parse("white", &color
);
3150 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
3152 gtk_widget_grab_focus(GTK_WIDGET(t
->cmd
));
3153 gtk_editable_set_position(GTK_EDITABLE(t
->cmd
), -1);
3158 return (XT_CB_HANDLED
);
3162 * Return a new string with a download row (in html)
3163 * appended. Old string is freed.
3166 xtp_page_dl_row(struct tab
*t
, char *html
, struct download
*dl
)
3169 WebKitDownloadStatus stat
;
3170 char *status_html
= NULL
, *cmd_html
= NULL
, *new_html
;
3172 char cur_sz
[FMT_SCALED_STRSIZE
];
3173 char tot_sz
[FMT_SCALED_STRSIZE
];
3176 DNPRINTF(XT_D_DOWNLOAD
, "%s: dl->id %d\n", __func__
, dl
->id
);
3178 /* All actions wil take this form:
3179 * xxxt://class/seskey
3181 xtp_prefix
= g_strdup_printf("%s%d/%s/",
3182 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
);
3184 stat
= webkit_download_get_status(dl
->download
);
3187 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
3188 status_html
= g_strdup_printf("Finished");
3189 cmd_html
= g_strdup_printf(
3190 "<a href='%s%d/%d'>Remove</a>",
3191 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3193 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
3194 /* gather size info */
3195 progress
= 100 * webkit_download_get_progress(dl
->download
);
3198 webkit_download_get_current_size(dl
->download
), cur_sz
);
3200 webkit_download_get_total_size(dl
->download
), tot_sz
);
3202 status_html
= g_strdup_printf("%s of %s (%.2f%%)", cur_sz
,
3204 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3205 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3209 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
3210 status_html
= g_strdup_printf("Cancelled");
3211 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3212 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3214 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
3215 status_html
= g_strdup_printf("Error!");
3216 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Remove</a>",
3217 xtp_prefix
, XT_XTP_DL_REMOVE
, dl
->id
);
3219 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
3220 cmd_html
= g_strdup_printf("<a href='%s%d/%d'>Cancel</a>",
3221 xtp_prefix
, XT_XTP_DL_CANCEL
, dl
->id
);
3222 status_html
= g_strdup_printf("Starting");
3225 show_oops(t
, "%s: unknown download status", __func__
);
3228 new_html
= g_strdup_printf(
3229 "%s\n<tr><td>%s</td><td>%s</td>"
3230 "<td style='text-align:center'>%s</td></tr>\n",
3231 html
, webkit_download_get_uri(dl
->download
),
3232 status_html
, cmd_html
);
3236 g_free(status_html
);
3247 * update all download tabs apart from one. Pass NULL if
3248 * you want to update all.
3251 update_download_tabs(struct tab
*apart_from
)
3254 if (!updating_dl_tabs
) {
3255 updating_dl_tabs
= 1; /* stop infinite recursion */
3256 TAILQ_FOREACH(t
, &tabs
, entry
)
3257 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_DL
)
3258 && (t
!= apart_from
))
3259 xtp_page_dl(t
, NULL
);
3260 updating_dl_tabs
= 0;
3265 * update all cookie tabs apart from one. Pass NULL if
3266 * you want to update all.
3269 update_cookie_tabs(struct tab
*apart_from
)
3272 if (!updating_cl_tabs
) {
3273 updating_cl_tabs
= 1; /* stop infinite recursion */
3274 TAILQ_FOREACH(t
, &tabs
, entry
)
3275 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_CL
)
3276 && (t
!= apart_from
))
3277 xtp_page_cl(t
, NULL
);
3278 updating_cl_tabs
= 0;
3283 * update all history tabs apart from one. Pass NULL if
3284 * you want to update all.
3287 update_history_tabs(struct tab
*apart_from
)
3291 if (!updating_hl_tabs
) {
3292 updating_hl_tabs
= 1; /* stop infinite recursion */
3293 TAILQ_FOREACH(t
, &tabs
, entry
)
3294 if ((t
->xtp_meaning
== XT_XTP_TAB_MEANING_HL
)
3295 && (t
!= apart_from
))
3296 xtp_page_hl(t
, NULL
);
3297 updating_hl_tabs
= 0;
3301 /* cookie management XTP page */
3303 xtp_page_cl(struct tab
*t
, struct karg
*args
)
3305 char *header
, *body
, *footer
, *page
, *tmp
;
3306 int i
= 1; /* all ids start 1 */
3307 GSList
*sc
, *pc
, *pc_start
;
3311 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3314 errx(1, "%s: null tab", __func__
);
3316 /* mark this tab as cookie jar */
3317 t
->xtp_meaning
= XT_XTP_TAB_MEANING_CL
;
3319 /* Generate a new session key */
3320 if (!updating_cl_tabs
)
3321 generate_xtp_session_key(&cl_session_key
);
3324 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3325 "\n<head><title>Cookie Jar</title>\n" XT_PAGE_STYLE
3326 "</head><body><h1>Cookie Jar</h1>\n");
3329 body
= g_strdup_printf("<div align='center'><table><tr>"
3337 "<th>HTTP_only</th>"
3338 "<th>Remove</th></tr>\n");
3340 sc
= soup_cookie_jar_all_cookies(s_cookiejar
);
3341 pc
= soup_cookie_jar_all_cookies(p_cookiejar
);
3344 for (; sc
; sc
= sc
->next
) {
3348 for (pc
= pc_start
; pc
; pc
= pc
->next
)
3349 if (soup_cookie_equal(pc
->data
, c
)) {
3350 type
= "Session + Persistent";
3355 body
= g_strdup_printf(
3357 "<td style='width: 3%%; text-align: center'>%s</td>"
3358 "<td style='width: 10%%; word-break: break-all'>%s</td>"
3359 "<td style='width: 20%%; word-break: break-all'>%s</td>"
3360 "<td style='width: 10%%; word-break: break-all'>%s</td>"
3361 "<td style='width: 8%%; word-break: break-all'>%s</td>"
3362 "<td style='width: 12%%; word-break: break-all'>%s</td>"
3363 "<td style='width: 3%%; text-align: center'>%d</td>"
3364 "<td style='width: 3%%; text-align: center'>%d</td>"
3365 "<td style='width: 3%%; text-align: center'>"
3366 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3374 soup_date_to_string(c
->expires
, SOUP_DATE_HTTP
) : "",
3389 soup_cookies_free(sc
);
3390 soup_cookies_free(pc
);
3392 /* small message if there are none */
3395 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3396 "colspan='8'>No Cookies</td></tr>\n", body
);
3401 footer
= g_strdup_printf("</table></div></body></html>");
3403 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3409 load_webkit_string(t
, page
);
3410 update_cookie_tabs(t
);
3418 xtp_page_hl(struct tab
*t
, struct karg
*args
)
3420 char *header
, *body
, *footer
, *page
, *tmp
;
3422 int i
= 1; /* all ids start 1 */
3424 DNPRINTF(XT_D_CMD
, "%s", __func__
);
3427 errx(1, "%s: null tab", __func__
);
3429 /* mark this tab as history manager */
3430 t
->xtp_meaning
= XT_XTP_TAB_MEANING_HL
;
3432 /* Generate a new session key */
3433 if (!updating_hl_tabs
)
3434 generate_xtp_session_key(&hl_session_key
);
3437 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
"\n<head>"
3438 "<title>History</title>\n"
3441 "<h1>History</h1>\n",
3445 body
= g_strdup_printf("<div align='center'><table><tr>"
3446 "<th>URI</th><th>Title</th><th style='width: 15%%'>Remove</th></tr>\n");
3448 RB_FOREACH_REVERSE(h
, history_list
, &hl
) {
3450 body
= g_strdup_printf(
3452 "<td><a href='%s'>%s</a></td>"
3454 "<td style='text-align: center'>"
3455 "<a href='%s%d/%s/%d/%d'>X</a></td></tr>\n",
3456 body
, h
->uri
, h
->uri
, h
->title
,
3457 XT_XTP_STR
, XT_XTP_HL
, hl_session_key
,
3458 XT_XTP_HL_REMOVE
, i
);
3464 /* small message if there are none */
3467 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3468 "colspan='3'>No History</td></tr>\n", body
);
3473 footer
= g_strdup_printf("</table></div></body></html>");
3475 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3478 * update all history manager tabs as the xtp session
3479 * key has now changed. No need to update the current tab.
3480 * Already did that above.
3482 update_history_tabs(t
);
3488 load_webkit_string(t
, page
);
3495 * Generate a web page detailing the status of any downloads
3498 xtp_page_dl(struct tab
*t
, struct karg
*args
)
3500 struct download
*dl
;
3501 char *header
, *body
, *footer
, *page
, *tmp
;
3505 DNPRINTF(XT_D_DOWNLOAD
, "%s", __func__
);
3508 errx(1, "%s: null tab", __func__
);
3510 /* mark as a download manager tab */
3511 t
->xtp_meaning
= XT_XTP_TAB_MEANING_DL
;
3514 * Generate a new session key for next page instance.
3515 * This only happens for the top level call to xtp_page_dl()
3516 * in which case updating_dl_tabs is 0.
3518 if (!updating_dl_tabs
)
3519 generate_xtp_session_key(&dl_session_key
);
3521 /* header - with refresh so as to update */
3522 if (refresh_interval
>= 1)
3523 ref
= g_strdup_printf(
3524 "<meta http-equiv='refresh' content='%u"
3525 ";url=%s%d/%s/%d' />\n",
3535 header
= g_strdup_printf(
3537 "<title>Downloads</title>\n%s%s</head>\n",
3538 XT_DOCTYPE XT_HTML_TAG
,
3542 body
= g_strdup_printf("<body><h1>Downloads</h1><div align='center'>"
3543 "<p>\n<a href='%s%d/%s/%d'>\n[ Refresh Downloads ]</a>\n"
3544 "</p><table><tr><th style='width: 60%%'>"
3545 "File</th>\n<th>Progress</th><th>Command</th></tr>\n",
3546 XT_XTP_STR
, XT_XTP_DL
, dl_session_key
, XT_XTP_DL_LIST
);
3548 RB_FOREACH_REVERSE(dl
, download_list
, &downloads
) {
3549 body
= xtp_page_dl_row(t
, body
, dl
);
3553 /* message if no downloads in list */
3556 body
= g_strdup_printf("%s\n<tr><td colspan='3'"
3557 " style='text-align: center'>"
3558 "No downloads</td></tr>\n", body
);
3563 footer
= g_strdup_printf("</table></div></body></html>");
3565 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3569 * update all download manager tabs as the xtp session
3570 * key has now changed. No need to update the current tab.
3571 * Already did that above.
3573 update_download_tabs(t
);
3580 load_webkit_string(t
, page
);
3587 search(struct tab
*t
, struct karg
*args
)
3591 if (t
== NULL
|| args
== NULL
)
3593 if (t
->search_text
== NULL
) {
3594 if (global_search
== NULL
)
3595 return (XT_CB_PASSTHROUGH
);
3597 t
->search_text
= g_strdup(global_search
);
3598 webkit_web_view_mark_text_matches(t
->wv
, global_search
, FALSE
, 0);
3599 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
3603 DNPRINTF(XT_D_CMD
, "search: tab %d opc %d forw %d text %s\n",
3604 t
->tab_id
, args
->i
, t
->search_forward
, t
->search_text
);
3607 case XT_SEARCH_NEXT
:
3608 d
= t
->search_forward
;
3610 case XT_SEARCH_PREV
:
3611 d
= !t
->search_forward
;
3614 return (XT_CB_PASSTHROUGH
);
3617 webkit_web_view_search_text(t
->wv
, t
->search_text
, FALSE
, d
, TRUE
);
3619 return (XT_CB_HANDLED
);
3622 struct settings_args
{
3628 print_setting(struct settings
*s
, char *val
, void *cb_args
)
3631 struct settings_args
*sa
= cb_args
;
3636 if (s
->flags
& XT_SF_RUNTIME
)
3642 *sa
->body
= g_strdup_printf(
3644 "<td style='background-color: %s; width: 10%%; word-break: break-all'>%s</td>"
3645 "<td style='background-color: %s; width: 20%%; word-break: break-all'>%s</td>",
3657 set(struct tab
*t
, struct karg
*args
)
3659 char *header
, *body
, *footer
, *page
, *tmp
, *pars
;
3661 struct settings_args sa
;
3663 if ((pars
= getparams(args
->s
, "set")) == NULL
) {
3664 bzero(&sa
, sizeof sa
);
3668 header
= g_strdup_printf(XT_DOCTYPE XT_HTML_TAG
3669 "\n<head><title>Settings</title>\n"
3670 "</head><body><h1>Settings</h1>\n");
3673 body
= g_strdup_printf("<div align='center'><table><tr>"
3674 "<th align='left'>Setting</th>"
3675 "<th align='left'>Value</th></tr>\n");
3677 settings_walk(print_setting
, &sa
);
3680 /* small message if there are none */
3683 body
= g_strdup_printf("%s\n<tr><td style='text-align:center'"
3684 "colspan='2'>No settings</td></tr>\n", body
);
3689 footer
= g_strdup_printf("</table></div></body></html>");
3691 page
= g_strdup_printf("%s%s%s", header
, body
, footer
);
3697 load_webkit_string(t
, page
);
3699 show_oops(t
, "Invalid command: %s", pars
);
3702 return (XT_CB_PASSTHROUGH
);
3706 session_save(struct tab
*t
, char *filename
, char **ret
)
3712 f
+= strlen("save");
3713 while (*f
== ' ' && *f
!= '\0')
3720 strlcpy(named_session
, f
, sizeof named_session
);
3729 session_open(struct tab
*t
, char *filename
, char **ret
)
3735 f
+= strlen("open");
3736 while (*f
== ' ' && *f
!= '\0')
3743 strlcpy(named_session
, f
, sizeof named_session
);
3752 session_cmd(struct tab
*t
, struct karg
*args
)
3754 char *action
= NULL
;
3755 char *filename
= NULL
;
3760 if ((action
= getparams(args
->s
, "session")))
3765 if (!strcmp(action
, "show"))
3766 show_oops(t
, "Current session: %s", named_session
[0] == '\0' ?
3767 XT_SAVED_TABS_FILE
: named_session
);
3768 else if (g_str_has_prefix(action
, "save ")) {
3769 if (session_save(t
, action
, &filename
)) {
3770 show_oops(t
, "Can't save session: %s",
3771 filename
? filename
: "INVALID");
3774 } else if (g_str_has_prefix(action
, "open ")) {
3775 if (session_open(t
, action
, &filename
)) {
3776 show_oops(t
, "Can't open session: %s",
3777 filename
? filename
: "INVALID");
3781 show_oops(t
, "Invalid command: %s", action
);
3783 return (XT_CB_PASSTHROUGH
);
3787 * Make a hardcopy of the page
3790 print_page(struct tab
*t
, struct karg
*args
)
3792 WebKitWebFrame
*frame
;
3794 DNPRINTF(XT_D_PRINTING
, "%s:", __func__
);
3797 * for now we just call the GTK print box,
3798 * but later we might decide to hook in a command.
3800 frame
= webkit_web_view_get_main_frame(t
->wv
);
3801 webkit_web_frame_print(frame
);
3807 go_home(struct tab
*t
, struct karg
*args
)
3811 newuri
= guess_url_type((char *)home
);
3812 webkit_web_view_load_uri(t
->wv
, newuri
);
3819 restart(struct tab
*t
, struct karg
*args
)
3823 a
.s
= XT_RESTART_TABS_FILE
;
3825 execvp(start_argv
[0], start_argv
);
3831 /* inherent to GTK not all keys will be caught at all times */
3832 /* XXX sort key bindings */
3833 struct key_bindings
{
3837 int (*func
)(struct tab
*, struct karg
*);
3840 { GDK_MOD1_MASK
, 0, GDK_d
, xtp_page_dl
, {0} },
3841 { GDK_MOD1_MASK
, 0, GDK_h
, xtp_page_hl
, {0} },
3842 { GDK_CONTROL_MASK
, 0, GDK_p
, print_page
, {0}},
3843 { 0, 0, GDK_slash
, command
, {.i
= '/'} },
3844 { GDK_SHIFT_MASK
, 0, GDK_question
, command
, {.i
= '?'} },
3845 { GDK_SHIFT_MASK
, 0, GDK_colon
, command
, {.i
= ':'} },
3846 { GDK_CONTROL_MASK
, 0, GDK_q
, quit
, {0} },
3847 { GDK_MOD1_MASK
, 0, GDK_q
, restart
, {0} },
3848 { GDK_CONTROL_MASK
, 0, GDK_j
, toggle_js
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
3849 { GDK_MOD1_MASK
, 0, GDK_c
, toggle_cwl
, {.i
= XT_WL_TOGGLE
| XT_WL_FQDN
} },
3850 { GDK_CONTROL_MASK
, 0, GDK_s
, toggle_src
, {0} },
3851 { 0, 0, GDK_y
, yank_uri
, {0} },
3852 { 0, 0, GDK_p
, paste_uri
, {.i
= XT_PASTE_CURRENT_TAB
} },
3853 { GDK_SHIFT_MASK
, 0, GDK_P
, paste_uri
, {.i
= XT_PASTE_NEW_TAB
} },
3856 { 0, 0, GDK_n
, search
, {.i
= XT_SEARCH_NEXT
} },
3857 { GDK_SHIFT_MASK
, 0, GDK_N
, search
, {.i
= XT_SEARCH_PREV
} },
3860 { 0, 0, GDK_F6
, focus
, {.i
= XT_FOCUS_URI
} },
3861 { 0, 0, GDK_F7
, focus
, {.i
= XT_FOCUS_SEARCH
} },
3863 /* command aliases (handy when -S flag is used) */
3864 { 0, 0, GDK_F9
, command
, {.i
= XT_CMD_OPEN
} },
3865 { 0, 0, GDK_F10
, command
, {.i
= XT_CMD_OPEN_CURRENT
} },
3866 { 0, 0, GDK_F11
, command
, {.i
= XT_CMD_TABNEW
} },
3867 { 0, 0, GDK_F12
, command
, {.i
= XT_CMD_TABNEW_CURRENT
} },
3870 { 0, 0, GDK_f
, hint
, {.i
= 0} },
3873 { 0, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_BACK
} },
3874 { GDK_MOD1_MASK
, 0, GDK_Left
, navaction
, {.i
= XT_NAV_BACK
} },
3875 { GDK_SHIFT_MASK
, 0, GDK_BackSpace
, navaction
, {.i
= XT_NAV_FORWARD
} },
3876 { GDK_MOD1_MASK
, 0, GDK_Right
, navaction
, {.i
= XT_NAV_FORWARD
} },
3877 { 0, 0, GDK_F5
, navaction
, {.i
= XT_NAV_RELOAD
} },
3878 { GDK_CONTROL_MASK
, 0, GDK_r
, navaction
, {.i
= XT_NAV_RELOAD
} },
3879 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_R
, navaction
, {.i
= XT_NAV_RELOAD_CACHE
} },
3880 { GDK_CONTROL_MASK
, 0, GDK_l
, navaction
, {.i
= XT_NAV_RELOAD
} },
3881 { GDK_MOD1_MASK
, 1, GDK_f
, xtp_page_fl
, {0} },
3883 /* vertical movement */
3884 { 0, 0, GDK_j
, move
, {.i
= XT_MOVE_DOWN
} },
3885 { 0, 0, GDK_Down
, move
, {.i
= XT_MOVE_DOWN
} },
3886 { 0, 0, GDK_Up
, move
, {.i
= XT_MOVE_UP
} },
3887 { 0, 0, GDK_k
, move
, {.i
= XT_MOVE_UP
} },
3888 { GDK_SHIFT_MASK
, 0, GDK_G
, move
, {.i
= XT_MOVE_BOTTOM
} },
3889 { 0, 0, GDK_End
, move
, {.i
= XT_MOVE_BOTTOM
} },
3890 { 0, 0, GDK_Home
, move
, {.i
= XT_MOVE_TOP
} },
3891 { 0, 0, GDK_g
, move
, {.i
= XT_MOVE_TOP
} }, /* XXX make this work */
3892 { 0, 0, GDK_space
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3893 { GDK_CONTROL_MASK
, 0, GDK_f
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3894 { GDK_CONTROL_MASK
, 0, GDK_d
, move
, {.i
= XT_MOVE_HALFDOWN
} },
3895 { 0, 0, GDK_Page_Down
, move
, {.i
= XT_MOVE_PAGEDOWN
} },
3896 { 0, 0, GDK_Page_Up
, move
, {.i
= XT_MOVE_PAGEUP
} },
3897 { GDK_CONTROL_MASK
, 0, GDK_b
, move
, {.i
= XT_MOVE_PAGEUP
} },
3898 { GDK_CONTROL_MASK
, 0, GDK_u
, move
, {.i
= XT_MOVE_HALFUP
} },
3899 /* horizontal movement */
3900 { 0, 0, GDK_l
, move
, {.i
= XT_MOVE_RIGHT
} },
3901 { 0, 0, GDK_Right
, move
, {.i
= XT_MOVE_RIGHT
} },
3902 { 0, 0, GDK_Left
, move
, {.i
= XT_MOVE_LEFT
} },
3903 { 0, 0, GDK_h
, move
, {.i
= XT_MOVE_LEFT
} },
3904 { GDK_SHIFT_MASK
, 0, GDK_dollar
, move
, {.i
= XT_MOVE_FARRIGHT
} },
3905 { 0, 0, GDK_0
, move
, {.i
= XT_MOVE_FARLEFT
} },
3908 { GDK_CONTROL_MASK
, 0, GDK_t
, tabaction
, {.i
= XT_TAB_NEW
} },
3909 { GDK_CONTROL_MASK
, 1, GDK_w
, tabaction
, {.i
= XT_TAB_DELETE
} },
3910 { GDK_SHIFT_MASK
, 0, GDK_U
, tabaction
, {.i
= XT_TAB_UNDO_CLOSE
} },
3911 { GDK_CONTROL_MASK
, 0, GDK_1
, movetab
, {.i
= 1} },
3912 { GDK_CONTROL_MASK
, 0, GDK_2
, movetab
, {.i
= 2} },
3913 { GDK_CONTROL_MASK
, 0, GDK_3
, movetab
, {.i
= 3} },
3914 { GDK_CONTROL_MASK
, 0, GDK_4
, movetab
, {.i
= 4} },
3915 { GDK_CONTROL_MASK
, 0, GDK_5
, movetab
, {.i
= 5} },
3916 { GDK_CONTROL_MASK
, 0, GDK_6
, movetab
, {.i
= 6} },
3917 { GDK_CONTROL_MASK
, 0, GDK_7
, movetab
, {.i
= 7} },
3918 { GDK_CONTROL_MASK
, 0, GDK_8
, movetab
, {.i
= 8} },
3919 { GDK_CONTROL_MASK
, 0, GDK_9
, movetab
, {.i
= 9} },
3920 { GDK_CONTROL_MASK
, 0, GDK_0
, movetab
, {.i
= 10} },
3921 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_less
, movetab
, {.i
= XT_TAB_FIRST
} },
3922 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_greater
, movetab
, {.i
= XT_TAB_LAST
} },
3923 { GDK_CONTROL_MASK
, 0, GDK_Left
, movetab
, {.i
= XT_TAB_PREV
} },
3924 { GDK_CONTROL_MASK
, 0, GDK_Right
, movetab
, {.i
= XT_TAB_NEXT
} },
3925 { GDK_CONTROL_MASK
, 0, GDK_minus
, resizetab
, {.i
= -1} },
3926 { GDK_CONTROL_MASK
|GDK_SHIFT_MASK
, 0, GDK_plus
, resizetab
, {.i
= 1} },
3927 { GDK_CONTROL_MASK
, 0, GDK_equal
, resizetab
, {.i
= 1} },
3933 int (*func
)(struct tab
*, struct karg
*);
3936 { "q!", 0, quit
, {0} },
3937 { "qa", 0, quit
, {0} },
3938 { "qa!", 0, quit
, {0} },
3939 { "w", 0, save_tabs
, {.s
= XT_SAVED_TABS_FILE
} },
3940 { "wq", 0, save_tabs_and_quit
, {.s
= XT_SAVED_TABS_FILE
} },
3941 { "wq!", 0, save_tabs_and_quit
, {.s
= XT_SAVED_TABS_FILE
} },
3942 { "help", 0, help
, {0} },
3943 { "about", 0, about
, {0} },
3944 { "stats", 0, stats
, {0} },
3945 { "version", 0, about
, {0} },
3946 { "cookies", 0, xtp_page_cl
, {0} },
3947 { "fav", 0, xtp_page_fl
, {0} },
3948 { "favadd", 0, add_favorite
, {0} },
3949 { "js", 2, js_cmd
, {0} },
3950 { "cookie", 2, cookie_cmd
, {0} },
3951 { "cert", 1, cert_cmd
, {0} },
3952 { "ca", 0, ca_cmd
, {0} },
3953 { "dl" , 0, xtp_page_dl
, {0} },
3954 { "h" , 0, xtp_page_hl
, {0} },
3955 { "hist" , 0, xtp_page_hl
, {0} },
3956 { "history" , 0, xtp_page_hl
, {0} },
3957 { "home" , 0, go_home
, {0} },
3958 { "restart" , 0, restart
, {0} },
3960 { "1", 0, move
, {.i
= XT_MOVE_TOP
} },
3961 { "print", 0, print_page
, {0} },
3964 { "o", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3965 { "op", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3966 { "open", 1, tabaction
, {.i
= XT_TAB_OPEN
} },
3967 { "tabnew", 1, tabaction
, {.i
= XT_TAB_NEW
} },
3968 { "tabedit", 1, tabaction
, {.i
= XT_TAB_NEW
} },
3969 { "tabe", 1, tabaction
, {.i
= XT_TAB_NEW
} },
3970 { "tabclose", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
3971 { "tabc", 0, tabaction
, {.i
= XT_TAB_DELETE
} },
3972 { "quit", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
3973 { "q", 0, tabaction
, {.i
= XT_TAB_DELQUIT
} },
3974 /* XXX add count to these commands */
3975 { "tabfirst", 0, movetab
, {.i
= XT_TAB_FIRST
} },
3976 { "tabfir", 0, movetab
, {.i
= XT_TAB_FIRST
} },
3977 { "tabrewind", 0, movetab
, {.i
= XT_TAB_FIRST
} },
3978 { "tabr", 0, movetab
, {.i
= XT_TAB_FIRST
} },
3979 { "tablast", 0, movetab
, {.i
= XT_TAB_LAST
} },
3980 { "tabl", 0, movetab
, {.i
= XT_TAB_LAST
} },
3981 { "tabprevious", 0, movetab
, {.i
= XT_TAB_PREV
} },
3982 { "tabp", 0, movetab
, {.i
= XT_TAB_PREV
} },
3983 { "tabnext", 0, movetab
, {.i
= XT_TAB_NEXT
} },
3984 { "tabn", 0, movetab
, {.i
= XT_TAB_NEXT
} },
3987 { "set", 1, set
, {0} },
3990 { "session", 1, session_cmd
, {0} },
3994 wv_button_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4002 tab_close_cb(GtkWidget
*btn
, GdkEventButton
*e
, struct tab
*t
)
4004 DNPRINTF(XT_D_TAB
, "tab_close_cb: tab %d\n", t
->tab_id
);
4006 if (e
->type
== GDK_BUTTON_PRESS
&& e
->button
== 1)
4013 * cancel, remove, etc. downloads
4016 xtp_handle_dl(struct tab
*t
, uint8_t cmd
, int id
)
4018 struct download find
, *d
;
4020 DNPRINTF(XT_D_DOWNLOAD
, "download control: cmd %d, id %d\n", cmd
, id
);
4022 /* some commands require a valid download id */
4023 if (cmd
!= XT_XTP_DL_LIST
) {
4024 /* lookup download in question */
4026 d
= RB_FIND(download_list
, &downloads
, &find
);
4029 show_oops(t
, "%s: no such download", __func__
);
4034 /* decide what to do */
4036 case XT_XTP_DL_CANCEL
:
4037 webkit_download_cancel(d
->download
);
4039 case XT_XTP_DL_REMOVE
:
4040 webkit_download_cancel(d
->download
); /* just incase */
4041 g_object_unref(d
->download
);
4042 RB_REMOVE(download_list
, &downloads
, d
);
4044 case XT_XTP_DL_LIST
:
4048 show_oops(t
, "%s: unknown command", __func__
);
4051 xtp_page_dl(t
, NULL
);
4055 * Actions on history, only does one thing for now, but
4056 * we provide the function for future actions
4059 xtp_handle_hl(struct tab
*t
, uint8_t cmd
, int id
)
4061 struct history
*h
, *next
;
4065 case XT_XTP_HL_REMOVE
:
4066 /* walk backwards, as listed in reverse */
4067 for (h
= RB_MAX(history_list
, &hl
); h
!= NULL
; h
= next
) {
4068 next
= RB_PREV(history_list
, &hl
, h
);
4070 RB_REMOVE(history_list
, &hl
, h
);
4071 g_free((gpointer
) h
->title
);
4072 g_free((gpointer
) h
->uri
);
4079 case XT_XTP_HL_LIST
:
4080 /* Nothing - just xtp_page_hl() below */
4083 show_oops(t
, "%s: unknown command", __func__
);
4087 xtp_page_hl(t
, NULL
);
4090 /* remove a favorite */
4092 remove_favorite(struct tab
*t
, int index
)
4094 char file
[PATH_MAX
], *title
, *uri
;
4095 char *new_favs
, *tmp
;
4100 /* open favorites */
4101 snprintf(file
, sizeof file
, "%s/%s/%s",
4102 pwd
->pw_dir
, XT_DIR
, XT_FAVS_FILE
);
4104 if ((f
= fopen(file
, "r")) == NULL
) {
4105 show_oops(t
, "%s: can't open favorites: %s",
4106 __func__
, strerror(errno
));
4110 /* build a string which will become the new favroites file */
4111 new_favs
= g_strdup_printf("%s", "");
4114 if ((title
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
)
4115 if (feof(f
) || ferror(f
))
4117 /* XXX THIS IS NOT THE RIGHT HEURISTIC */
4124 if ((uri
= fparseln(f
, &len
, &lineno
, NULL
, 0)) == NULL
) {
4125 if (feof(f
) || ferror(f
)) {
4126 show_oops(t
, "%s: can't parse favorites %s",
4127 __func__
, strerror(errno
));
4132 /* as long as this isn't the one we are deleting add to file */
4135 new_favs
= g_strdup_printf("%s%s\n%s\n",
4136 new_favs
, title
, uri
);
4148 /* write back new favorites file */
4149 if ((f
= fopen(file
, "w")) == NULL
) {
4150 show_oops(t
, "%s: can't open favorites: %s",
4151 __func__
, strerror(errno
));
4155 fwrite(new_favs
, strlen(new_favs
), 1, f
);
4168 xtp_handle_fl(struct tab
*t
, uint8_t cmd
, int arg
)
4171 case XT_XTP_FL_LIST
:
4172 /* nothing, just the below call to xtp_page_fl() */
4174 case XT_XTP_FL_REMOVE
:
4175 remove_favorite(t
, arg
);
4178 show_oops(t
, "%s: invalid favorites command", __func__
);
4182 xtp_page_fl(t
, NULL
);
4186 xtp_handle_cl(struct tab
*t
, uint8_t cmd
, int arg
)
4189 case XT_XTP_CL_LIST
:
4190 /* nothing, just xtp_page_cl() */
4192 case XT_XTP_CL_REMOVE
:
4196 show_oops(t
, "%s: unknown cookie xtp command", __func__
);
4200 xtp_page_cl(t
, NULL
);
4203 /* link an XTP class to it's session key and handler function */
4204 struct xtp_despatch
{
4207 void (*handle_func
)(struct tab
*, uint8_t, int);
4210 struct xtp_despatch xtp_despatches
[] = {
4211 { XT_XTP_DL
, &dl_session_key
, xtp_handle_dl
},
4212 { XT_XTP_HL
, &hl_session_key
, xtp_handle_hl
},
4213 { XT_XTP_FL
, &fl_session_key
, xtp_handle_fl
},
4214 { XT_XTP_CL
, &cl_session_key
, xtp_handle_cl
},
4215 { NULL
, NULL
, NULL
}
4219 * is the url xtp protocol? (xxxt://)
4220 * if so, parse and despatch correct bahvior
4223 parse_xtp_url(struct tab
*t
, const char *url
)
4225 char *dup
= NULL
, *p
, *last
;
4226 uint8_t n_tokens
= 0;
4227 char *tokens
[4] = {NULL
, NULL
, NULL
, ""};
4228 struct xtp_despatch
*dsp
, *dsp_match
= NULL
;
4232 * tokens array meaning:
4234 * tokens[1] = session key
4235 * tokens[2] = action
4236 * tokens[3] = optional argument
4239 DNPRINTF(XT_D_URL
, "%s: url %s\n", __func__
, url
);
4241 /*xtp tab meaning is normal unless proven special */
4242 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
4244 if (strncmp(url
, XT_XTP_STR
, strlen(XT_XTP_STR
)))
4247 dup
= g_strdup(url
+ strlen(XT_XTP_STR
));
4249 /* split out the url */
4250 for ((p
= strtok_r(dup
, "/", &last
)); p
;
4251 (p
= strtok_r(NULL
, "/", &last
))) {
4253 tokens
[n_tokens
++] = p
;
4256 /* should be atleast three fields 'class/seskey/command/arg' */
4260 dsp
= xtp_despatches
;
4261 req_class
= atoi(tokens
[0]);
4262 while (dsp
->xtp_class
!= NULL
) {
4263 if (dsp
->xtp_class
== req_class
) {
4270 /* did we find one atall? */
4271 if (dsp_match
== NULL
) {
4272 show_oops(t
, "%s: no matching xtp despatch found", __func__
);
4276 /* check session key and call despatch function */
4277 if (validate_xtp_session_key(t
, *(dsp_match
->session_key
), tokens
[1])) {
4278 dsp_match
->handle_func(t
, atoi(tokens
[2]), atoi(tokens
[3]));
4291 activate_uri_entry_cb(GtkWidget
* entry
, struct tab
*t
)
4293 const gchar
*uri
= gtk_entry_get_text(GTK_ENTRY(entry
));
4294 char *newuri
= NULL
;
4296 DNPRINTF(XT_D_URL
, "activate_uri_entry_cb: %s\n", uri
);
4299 errx(1, "activate_uri_entry_cb");
4304 uri
+= strspn(uri
, "\t ");
4306 /* if xxxt:// treat specially */
4307 if (!parse_xtp_url(t
, uri
)) {
4308 if (valid_url_type((char *)uri
)) {
4309 newuri
= guess_url_type((char *)uri
);
4313 webkit_web_view_load_uri(t
->wv
, uri
);
4314 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4322 activate_search_entry_cb(GtkWidget
* entry
, struct tab
*t
)
4324 const gchar
*search
= gtk_entry_get_text(GTK_ENTRY(entry
));
4325 char *newuri
= NULL
;
4327 DNPRINTF(XT_D_URL
, "activate_search_entry_cb: %s\n", search
);
4330 errx(1, "activate_search_entry_cb");
4332 if (search_string
== NULL
) {
4333 show_oops(t
, "no search_string");
4337 newuri
= g_strdup_printf(search_string
, search
);
4339 webkit_web_view_load_uri(t
->wv
, newuri
);
4340 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4347 check_and_set_js(gchar
*uri
, struct tab
*t
)
4349 struct domain
*d
= NULL
;
4352 if (uri
== NULL
|| t
== NULL
)
4355 if ((d
= wl_find_uri(uri
, &js_wl
)) == NULL
)
4360 DNPRINTF(XT_D_JS
, "check_and_set_js: %s %s\n",
4361 es
? "enable" : "disable", uri
);
4363 g_object_set((GObject
*)t
->settings
,
4364 "enable-scripts", es
, (char *)NULL
);
4365 webkit_web_view_set_settings(t
->wv
, t
->settings
);
4367 button_set_stockid(t
->js_toggle
,
4368 es
? GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
);
4372 show_ca_status(struct tab
*t
, const char *uri
)
4374 WebKitWebFrame
*frame
;
4375 WebKitWebDataSource
*source
;
4376 WebKitNetworkRequest
*request
;
4377 SoupMessage
*message
;
4379 gchar
*col_str
= "white";
4382 DNPRINTF(XT_D_URL
, "show_ca_status: %d %s %s\n",
4383 ssl_strict_certs
, ssl_ca_file
, uri
);
4387 if (ssl_ca_file
== NULL
) {
4388 if (g_str_has_prefix(uri
, "http://"))
4390 if (g_str_has_prefix(uri
, "https://")) {
4396 if (g_str_has_prefix(uri
, "http://") ||
4397 !g_str_has_prefix(uri
, "https://"))
4400 frame
= webkit_web_view_get_main_frame(t
->wv
);
4401 source
= webkit_web_frame_get_data_source(frame
);
4402 request
= webkit_web_data_source_get_request(source
);
4403 message
= webkit_network_request_get_message(request
);
4405 if (message
&& (soup_message_get_flags(message
) &
4406 SOUP_MESSAGE_CERTIFICATE_TRUSTED
)) {
4410 r
= load_compare_cert(t
, NULL
);
4412 col_str
= "lightblue";
4421 gdk_color_parse(col_str
, &color
);
4422 gtk_widget_modify_base(t
->uri_entry
, GTK_STATE_NORMAL
, &color
);
4427 abort_and_free_favicon(struct tab
*t
)
4429 DNPRINTF(XT_D_DOWNLOAD
, "%s: down %p req %p pix %p\n",
4430 __func__
, t
->icon_download
, t
->icon_request
, t
->icon_pixbuf
);
4431 if (t
->icon_download
) {
4432 webkit_download_cancel(t
->icon_download
);
4433 g_object_unref(t
->icon_download
);
4435 if (t
->icon_request
)
4436 g_object_unref(t
->icon_request
);
4438 g_object_unref(t
->icon_pixbuf
);
4439 if (t
->icon_dest_uri
)
4440 g_free(t
->icon_dest_uri
);
4442 t
->icon_pixbuf
= NULL
;
4443 t
->icon_request
= NULL
;
4444 t
->icon_download
= NULL
;
4445 t
->icon_dest_uri
= NULL
;
4447 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
4448 GTK_ENTRY_ICON_PRIMARY
, "text-html");
4452 set_favicon_from_file(struct tab
*t
, char *file
)
4455 GdkPixbuf
*pixbuf
, *scaled
;
4458 if (t
== NULL
|| file
== NULL
)
4460 if (t
->icon_pixbuf
) {
4461 DNPRINTF(XT_D_DOWNLOAD
, "%s: icon already set\n", __func__
);
4465 if (g_str_has_prefix(file
, "file://"))
4466 file
+= strlen("file://");
4467 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading %s\n", __func__
, file
);
4469 if (!stat(file
, &sb
)) {
4470 if (sb
.st_size
== 0) {
4471 /* corrupt icon so trash it */
4472 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
4475 /* no need to set icon to default here */
4480 pixbuf
= gdk_pixbuf_new_from_file(file
, NULL
);
4481 if (pixbuf
== NULL
) {
4482 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
4483 GTK_ENTRY_ICON_PRIMARY
, "text-html");
4487 g_object_get(pixbuf
, "width", &width
, "height", &height
,
4489 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d icon size %dx%d\n",
4490 __func__
, t
->tab_id
, width
, height
);
4492 if (width
> 16 || height
> 16) {
4493 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
4494 GDK_INTERP_BILINEAR
);
4495 g_object_unref(pixbuf
);
4499 if (scaled
== NULL
) {
4500 scaled
= gdk_pixbuf_scale_simple(pixbuf
, 16, 16,
4501 GDK_INTERP_BILINEAR
);
4505 t
->icon_pixbuf
= scaled
;
4506 gtk_entry_set_icon_from_pixbuf(GTK_ENTRY(t
->uri_entry
),
4507 GTK_ENTRY_ICON_PRIMARY
, t
->icon_pixbuf
);
4511 favicon_download_status_changed_cb(WebKitDownload
*download
, GParamSpec
*spec
,
4514 WebKitDownloadStatus status
= webkit_download_get_status (download
);
4519 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d status %d\n",
4520 __func__
, t
->tab_id
, status
);
4523 case WEBKIT_DOWNLOAD_STATUS_ERROR
:
4526 case WEBKIT_DOWNLOAD_STATUS_CREATED
:
4529 case WEBKIT_DOWNLOAD_STATUS_STARTED
:
4532 case WEBKIT_DOWNLOAD_STATUS_CANCELLED
:
4535 case WEBKIT_DOWNLOAD_STATUS_FINISHED
:
4537 DNPRINTF(XT_D_DOWNLOAD
, "%s: setting icon to %s\n",
4538 __func__
, t
->icon_dest_uri
);
4539 set_favicon_from_file(t
, t
->icon_dest_uri
);
4540 g_free(t
->icon_dest_uri
);
4541 /* these will be freed post callback */
4542 t
->icon_request
= NULL
;
4543 t
->icon_download
= NULL
;
4544 t
->icon_dest_uri
= NULL
;
4552 notify_icon_loaded_cb(WebKitWebView
*wv
, gchar
*uri
, struct tab
*t
)
4554 gchar
*name_hash
, file
[PATH_MAX
];
4557 DNPRINTF(XT_D_DOWNLOAD
, "notify_icon_loaded_cb %s\n", uri
);
4559 if (uri
== NULL
|| t
== NULL
)
4562 if (t
->icon_request
) {
4563 DNPRINTF(XT_D_DOWNLOAD
, "%s: download in progress\n", __func__
);
4567 /* check to see if we got the icon in cache */
4568 name_hash
= g_compute_checksum_for_string(G_CHECKSUM_SHA256
, uri
, -1);
4569 snprintf(file
, sizeof file
, "%s/%s.ico", cache_dir
, name_hash
);
4572 if (!stat(file
, &sb
)) {
4573 if (sb
.st_size
> 0) {
4574 DNPRINTF(XT_D_DOWNLOAD
, "%s: loading from cache %s\n",
4576 set_favicon_from_file(t
, file
);
4580 /* corrupt icon so trash it */
4581 DNPRINTF(XT_D_DOWNLOAD
, "%s: corrupt icon %s\n",
4586 /* create download for icon */
4587 t
->icon_request
= webkit_network_request_new(uri
);
4588 if (t
->icon_request
== NULL
) {
4589 DNPRINTF(XT_D_DOWNLOAD
, "%s: invalid uri %s\n",
4594 t
->icon_download
= webkit_download_new(t
->icon_request
);
4596 /* we have to free icon_dest_uri later */
4597 t
->icon_dest_uri
= g_strdup_printf("file://%s", file
);
4598 webkit_download_set_destination_uri(t
->icon_download
,
4601 g_signal_connect(G_OBJECT(t
->icon_download
), "notify::status",
4602 G_CALLBACK(favicon_download_status_changed_cb
), t
);
4604 webkit_download_start(t
->icon_download
);
4608 notify_load_status_cb(WebKitWebView
* wview
, GParamSpec
* pspec
, struct tab
*t
)
4610 WebKitWebFrame
*frame
;
4611 const gchar
*set
= NULL
, *uri
= NULL
, *title
= NULL
;
4612 struct history
*h
, find
;
4614 const gchar
*s_loading
;
4616 DNPRINTF(XT_D_URL
, "notify_load_status_cb: %d\n",
4617 webkit_web_view_get_load_status(wview
));
4620 errx(1, "notify_load_status_cb");
4622 switch (webkit_web_view_get_load_status(wview
)) {
4623 case WEBKIT_LOAD_PROVISIONAL
:
4624 abort_and_free_favicon(t
);
4625 #if GTK_CHECK_VERSION(2, 20, 0)
4626 gtk_widget_show(t
->spinner
);
4627 gtk_spinner_start(GTK_SPINNER(t
->spinner
));
4629 gtk_label_set_text(GTK_LABEL(t
->label
), "Loading");
4631 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), TRUE
);
4634 /* take focus if we are visible */
4635 if (gtk_notebook_get_current_page(notebook
) == t
->tab_id
)
4636 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4640 case WEBKIT_LOAD_COMMITTED
:
4641 frame
= webkit_web_view_get_main_frame(wview
);
4642 uri
= webkit_web_frame_get_uri(frame
);
4644 gtk_entry_set_text(GTK_ENTRY(t
->uri_entry
), uri
);
4646 /* check if js white listing is enabled */
4647 if (enable_js_whitelist
) {
4648 frame
= webkit_web_view_get_main_frame(wview
);
4649 uri
= webkit_web_frame_get_uri(frame
);
4650 check_and_set_js((gchar
*)uri
, t
);
4653 show_ca_status(t
, uri
);
4656 case WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT
:
4657 title
= webkit_web_view_get_title(wview
);
4658 frame
= webkit_web_view_get_main_frame(wview
);
4659 uri
= webkit_web_frame_get_uri(frame
);
4667 gtk_label_set_text(GTK_LABEL(t
->label
), set
);
4668 gtk_window_set_title(GTK_WINDOW(main_window
), set
);
4671 if (!strncmp(uri
, "http://", strlen("http://")) ||
4672 !strncmp(uri
, "https://", strlen("https://")) ||
4673 !strncmp(uri
, "file://", strlen("file://")))
4678 h
= RB_FIND(history_list
, &hl
, &find
);
4682 h
= g_malloc(sizeof *h
);
4683 h
->uri
= g_strdup(uri
);
4685 h
->title
= g_strdup(title
);
4687 h
->title
= g_strdup(uri
);
4688 RB_INSERT(history_list
, &hl
, h
);
4689 update_history_tabs(NULL
);
4694 case WEBKIT_LOAD_FINISHED
:
4695 #if WEBKIT_CHECK_VERSION(1, 1, 18)
4696 case WEBKIT_LOAD_FAILED
:
4698 #if GTK_CHECK_VERSION(2, 20, 0)
4699 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
4700 gtk_widget_hide(t
->spinner
);
4702 s_loading
= gtk_label_get_text(GTK_LABEL(t
->label
));
4703 if (s_loading
&& !strcmp(s_loading
, "Loading"))
4704 gtk_label_set_text(GTK_LABEL(t
->label
), "(untitled)");
4706 gtk_widget_set_sensitive(GTK_WIDGET(t
->stop
), FALSE
);
4711 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
), TRUE
);
4713 gtk_widget_set_sensitive(GTK_WIDGET(t
->backward
),
4714 webkit_web_view_can_go_back(wview
));
4716 gtk_widget_set_sensitive(GTK_WIDGET(t
->forward
),
4717 webkit_web_view_can_go_forward(wview
));
4721 webview_load_finished_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
4723 run_script(t
, JS_HINTING
);
4727 webview_progress_changed_cb(WebKitWebView
*wv
, int progress
, struct tab
*t
)
4729 gtk_entry_set_progress_fraction(GTK_ENTRY(t
->uri_entry
),
4730 progress
== 100 ? 0 : (double)progress
/ 100);
4734 webview_nw_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
4735 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
4736 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
4741 errx(1, "webview_nw_cb");
4743 DNPRINTF(XT_D_NAV
, "webview_nw_cb: %s\n",
4744 webkit_network_request_get_uri(request
));
4746 /* open in current tab */
4747 uri
= (char *)webkit_network_request_get_uri(request
);
4748 webkit_web_view_load_uri(t
->wv
, uri
);
4749 webkit_web_policy_decision_ignore(pd
);
4751 return (TRUE
); /* we made the decission */
4755 webview_npd_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
,
4756 WebKitNetworkRequest
*request
, WebKitWebNavigationAction
*na
,
4757 WebKitWebPolicyDecision
*pd
, struct tab
*t
)
4762 errx(1, "webview_npd_cb");
4764 DNPRINTF(XT_D_NAV
, "webview_npd_cb: ctrl_click %d %s\n",
4766 webkit_network_request_get_uri(request
));
4768 uri
= (char *)webkit_network_request_get_uri(request
);
4770 if ((!parse_xtp_url(t
, uri
) && (t
->ctrl_click
))) {
4772 create_new_tab(uri
, NULL
, ctrl_click_focus
);
4773 webkit_web_policy_decision_ignore(pd
);
4774 return (TRUE
); /* we made the decission */
4777 webkit_web_policy_decision_use(pd
);
4778 return (TRUE
); /* we made the decission */
4782 webview_cwv_cb(WebKitWebView
*wv
, WebKitWebFrame
*wf
, struct tab
*t
)
4785 errx(1, "webview_cwv_cb");
4787 DNPRINTF(XT_D_NAV
, "webview_cwv_cb: %s\n",
4788 webkit_web_view_get_uri(wv
));
4794 webview_event_cb(GtkWidget
*w
, GdkEventButton
*e
, struct tab
*t
)
4796 /* we can not eat the event without throwing gtk off so defer it */
4798 /* catch middle click */
4799 if (e
->type
== GDK_BUTTON_RELEASE
&& e
->button
== 2) {
4804 /* catch ctrl click */
4805 if (e
->type
== GDK_BUTTON_RELEASE
&&
4806 CLEAN(e
->state
) == GDK_CONTROL_MASK
)
4811 return (XT_CB_PASSTHROUGH
);
4815 run_mimehandler(struct tab
*t
, char *mime_type
, WebKitNetworkRequest
*request
)
4817 struct mime_type
*m
;
4819 m
= find_mime_type(mime_type
);
4834 execlp(m
->mt_action
, m
->mt_action
,
4835 webkit_network_request_get_uri(request
), (void *)NULL
);
4844 webview_mimetype_cb(WebKitWebView
*wv
, WebKitWebFrame
*frame
,
4845 WebKitNetworkRequest
*request
, char *mime_type
,
4846 WebKitWebPolicyDecision
*decision
, struct tab
*t
)
4849 errx(1, "webview_mimetype_cb");
4851 DNPRINTF(XT_D_DOWNLOAD
, "webview_mimetype_cb: tab %d mime %s\n",
4852 t
->tab_id
, mime_type
);
4854 if (run_mimehandler(t
, mime_type
, request
) == 0) {
4855 webkit_web_policy_decision_ignore(decision
);
4856 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
4860 if (webkit_web_view_can_show_mime_type(wv
, mime_type
) == FALSE
) {
4861 webkit_web_policy_decision_download(decision
);
4869 webview_download_cb(WebKitWebView
*wv
, WebKitDownload
*wk_download
, struct tab
*t
)
4871 const gchar
*filename
;
4873 struct download
*download_entry
;
4876 if (wk_download
== NULL
|| t
== NULL
)
4877 errx(1, "%s: invalid pointers", __func__
);
4879 filename
= webkit_download_get_suggested_filename(wk_download
);
4880 if (filename
== NULL
)
4881 return (FALSE
); /* abort download */
4883 uri
= g_strdup_printf("file://%s/%s", download_dir
, filename
);
4885 DNPRINTF(XT_D_DOWNLOAD
, "%s: tab %d filename %s "
4886 "local %s\n", __func__
, t
->tab_id
, filename
, uri
);
4888 webkit_download_set_destination_uri(wk_download
, uri
);
4890 if (webkit_download_get_status(wk_download
) ==
4891 WEBKIT_DOWNLOAD_STATUS_ERROR
) {
4892 show_oops(t
, "%s: download failed to start", __func__
);
4894 gtk_label_set_text(GTK_LABEL(t
->label
), "Download Failed");
4896 download_entry
= g_malloc(sizeof(struct download
));
4897 download_entry
->download
= wk_download
;
4898 download_entry
->tab
= t
;
4899 download_entry
->id
= next_download_id
++;
4900 RB_INSERT(download_list
, &downloads
, download_entry
);
4901 /* get from history */
4902 g_object_ref(wk_download
);
4903 gtk_label_set_text(GTK_LABEL(t
->label
), "Downloading");
4909 /* sync other download manager tabs */
4910 update_download_tabs(NULL
);
4913 * NOTE: never redirect/render the current tab before this
4914 * function returns. This will cause the download to never start.
4916 return (ret
); /* start download */
4919 /* XXX currently unused */
4921 webview_hover_cb(WebKitWebView
*wv
, gchar
*title
, gchar
*uri
, struct tab
*t
)
4923 DNPRINTF(XT_D_KEY
, "webview_hover_cb: %s %s\n", title
, uri
);
4926 errx(1, "webview_hover_cb");
4933 t
->hover
= g_strdup(uri
);
4934 } else if (t
->hover
) {
4941 wv_keypress_after_cb(GtkWidget
*w
, GdkEventKey
*e
, struct tab
*t
)
4944 char s
[2], buf
[128];
4945 const char *errstr
= NULL
;
4948 /* don't use w directly; use t->whatever instead */
4951 errx(1, "wv_keypress_after_cb");
4953 DNPRINTF(XT_D_KEY
, "wv_keypress_after_cb: keyval 0x%x mask 0x%x t %p\n",
4954 e
->keyval
, e
->state
, t
);
4958 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Escape
) {
4960 return (XT_CB_HANDLED
);
4964 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_Return
) {
4965 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
4967 /* we have a string */
4969 /* we have a number */
4970 snprintf(buf
, sizeof buf
, "vimprobable_fire(%s)",
4978 /* XXX unfuck this */
4979 if (CLEAN(e
->state
) == 0 && e
->keyval
== GDK_BackSpace
) {
4980 if (t
->hint_mode
== XT_HINT_NUMERICAL
) {
4981 /* last input was numerical */
4983 l
= strlen(t
->hint_num
);
4990 t
->hint_num
[l
] = '\0';
4994 } else if (t
->hint_mode
== XT_HINT_ALPHANUM
) {
4995 /* last input was alphanumerical */
4997 l
= strlen(t
->hint_buf
);
5004 t
->hint_buf
[l
] = '\0';
5014 /* numerical input */
5015 if (CLEAN(e
->state
) == 0 &&
5016 ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) || (e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
))) {
5017 snprintf(s
, sizeof s
, "%c", e
->keyval
);
5018 strlcat(t
->hint_num
, s
, sizeof t
->hint_num
);
5019 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: numerical %s\n",
5022 link
= strtonum(t
->hint_num
, 1, 1000, &errstr
);
5024 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: invalid link number\n");
5027 snprintf(buf
, sizeof buf
, "vimprobable_update_hints(%s)",
5029 t
->hint_mode
= XT_HINT_NUMERICAL
;
5033 /* empty the counter buffer */
5034 bzero(t
->hint_buf
, sizeof t
->hint_buf
);
5035 return (XT_CB_HANDLED
);
5038 /* alphanumerical input */
5040 (CLEAN(e
->state
) == 0 && e
->keyval
>= GDK_a
&& e
->keyval
<= GDK_z
) ||
5041 (CLEAN(e
->state
) == GDK_SHIFT_MASK
&& e
->keyval
>= GDK_A
&& e
->keyval
<= GDK_Z
) ||
5042 (CLEAN(e
->state
) == 0 && ((e
->keyval
>= GDK_0
&& e
->keyval
<= GDK_9
) ||
5043 ((e
->keyval
>= GDK_KP_0
&& e
->keyval
<= GDK_KP_9
) && (t
->hint_mode
!= XT_HINT_NUMERICAL
))))) {
5044 snprintf(s
, sizeof s
, "%c", e
->keyval
);
5045 strlcat(t
->hint_buf
, s
, sizeof t
->hint_buf
);
5046 DNPRINTF(XT_D_JS
, "wv_keypress_after_cb: alphanumerical %s\n",
5049 snprintf(buf
, sizeof buf
, "vimprobable_cleanup()");
5052 snprintf(buf
, sizeof buf
, "vimprobable_show_hints('%s')",
5054 t
->hint_mode
= XT_HINT_ALPHANUM
;
5057 /* empty the counter buffer */
5058 bzero(t
->hint_num
, sizeof t
->hint_num
);
5059 return (XT_CB_HANDLED
);
5062 return (XT_CB_HANDLED
);
5065 for (i
= 0; i
< LENGTH(keys
); i
++)
5066 if (e
->keyval
== keys
[i
].key
&& CLEAN(e
->state
) ==
5068 keys
[i
].func(t
, &keys
[i
].arg
);
5069 return (XT_CB_HANDLED
);
5073 return (XT_CB_PASSTHROUGH
);
5077 wv_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5081 return (XT_CB_PASSTHROUGH
);
5085 cmd_keyrelease_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5087 const gchar
*c
= gtk_entry_get_text(w
);
5091 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
5092 e
->keyval
, e
->state
, t
);
5095 errx(1, "cmd_keyrelease_cb");
5097 DNPRINTF(XT_D_CMD
, "cmd_keyrelease_cb: keyval 0x%x mask 0x%x t %p\n",
5098 e
->keyval
, e
->state
, t
);
5102 if (strlen(c
) == 1) {
5103 webkit_web_view_unmark_text_matches(t
->wv
);
5109 else if (c
[0] == '?')
5115 if (webkit_web_view_search_text(t
->wv
, &c
[1], FALSE
, forward
, TRUE
) ==
5117 /* not found, mark red */
5118 gdk_color_parse("red", &color
);
5119 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
5120 /* unmark and remove selection */
5121 webkit_web_view_unmark_text_matches(t
->wv
);
5122 /* my kingdom for a way to unselect text in webview */
5124 /* found, highlight all */
5125 webkit_web_view_unmark_text_matches(t
->wv
);
5126 webkit_web_view_mark_text_matches(t
->wv
, &c
[1], FALSE
, 0);
5127 webkit_web_view_set_highlight_text_matches(t
->wv
, TRUE
);
5128 gdk_color_parse("white", &color
);
5129 gtk_widget_modify_base(t
->cmd
, GTK_STATE_NORMAL
, &color
);
5132 return (XT_CB_PASSTHROUGH
);
5137 cmd_complete(struct tab
*t
, char *s
)
5140 GtkEntry
*w
= GTK_ENTRY(t
->cmd
);
5142 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: complete %s\n", s
);
5144 for (i
= 0; i
< LENGTH(cmds
); i
++) {
5145 if (!strncasecmp(cmds
[i
].cmd
, s
, strlen(s
))) {
5146 fprintf(stderr
, "match %s %d\n", cmds
[i
].cmd
, strcasecmp(cmds
[i
].cmd
, s
));
5148 gtk_entry_set_text(w
, ":");
5149 gtk_entry_append_text(w
, cmds
[i
].cmd
);
5150 gtk_editable_set_position(GTK_EDITABLE(w
), -1);
5160 entry_key_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5165 errx(1, "entry_key_cb");
5167 DNPRINTF(XT_D_CMD
, "entry_key_cb: keyval 0x%x mask 0x%x t %p\n",
5168 e
->keyval
, e
->state
, t
);
5172 if (e
->keyval
== GDK_Escape
)
5173 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5175 for (i
= 0; i
< LENGTH(keys
); i
++)
5176 if (e
->keyval
== keys
[i
].key
&&
5177 CLEAN(e
->state
) == keys
[i
].mask
&&
5178 keys
[i
].use_in_entry
) {
5179 keys
[i
].func(t
, &keys
[i
].arg
);
5180 return (XT_CB_HANDLED
);
5183 return (XT_CB_PASSTHROUGH
);
5187 cmd_keypress_cb(GtkEntry
*w
, GdkEventKey
*e
, struct tab
*t
)
5189 int rv
= XT_CB_HANDLED
;
5190 const gchar
*c
= gtk_entry_get_text(w
);
5193 errx(1, "cmd_keypress_cb");
5195 DNPRINTF(XT_D_CMD
, "cmd_keypress_cb: keyval 0x%x mask 0x%x t %p\n",
5196 e
->keyval
, e
->state
, t
);
5200 e
->keyval
= GDK_Escape
;
5201 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
5202 e
->keyval
= GDK_Escape
;
5204 switch (e
->keyval
) {
5210 if (strchr (c
, ' ')) {
5211 /* par completion */
5212 fprintf(stderr
, "completeme par\n");
5216 cmd_complete(t
, (char *)&c
[1]);
5221 if (!(!strcmp(c
, ":") || !strcmp(c
, "/") || !strcmp(c
, "?")))
5226 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5229 if (c
[0] == '/' || c
[0] == '?')
5230 webkit_web_view_unmark_text_matches(t
->wv
);
5234 rv
= XT_CB_PASSTHROUGH
;
5240 cmd_focusout_cb(GtkWidget
*w
, GdkEventFocus
*e
, struct tab
*t
)
5243 errx(1, "cmd_focusout_cb");
5245 DNPRINTF(XT_D_CMD
, "cmd_focusout_cb: tab %d focus_wv %d\n",
5246 t
->tab_id
, t
->focus_wv
);
5252 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5254 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
5256 return (XT_CB_PASSTHROUGH
);
5260 cmd_activate_cb(GtkEntry
*entry
, struct tab
*t
)
5264 const gchar
*c
= gtk_entry_get_text(entry
);
5267 errx(1, "cmd_activate_cb");
5269 DNPRINTF(XT_D_CMD
, "cmd_activate_cb: tab %d %s\n", t
->tab_id
, c
);
5274 else if (!(c
[0] == ':' || c
[0] == '/' || c
[0] == '?'))
5280 if (c
[0] == '/' || c
[0] == '?') {
5281 if (t
->search_text
) {
5282 g_free(t
->search_text
);
5283 t
->search_text
= NULL
;
5286 t
->search_text
= g_strdup(s
);
5288 g_free(global_search
);
5289 global_search
= g_strdup(s
);
5290 t
->search_forward
= c
[0] == '/';
5295 for (i
= 0; i
< LENGTH(cmds
); i
++)
5296 if (cmds
[i
].params
) {
5297 if (!strncmp(s
, cmds
[i
].cmd
, strlen(cmds
[i
].cmd
))) {
5298 cmds
[i
].arg
.s
= g_strdup(s
);
5299 goto execute_command
;
5302 if (!strcmp(s
, cmds
[i
].cmd
))
5303 goto execute_command
;
5305 show_oops(t
, "Invalid command: %s", s
);
5312 cmds
[i
].func(t
, &cmds
[i
].arg
);
5315 backward_cb(GtkWidget
*w
, struct tab
*t
)
5320 errx(1, "backward_cb");
5322 DNPRINTF(XT_D_NAV
, "backward_cb: tab %d\n", t
->tab_id
);
5329 forward_cb(GtkWidget
*w
, struct tab
*t
)
5334 errx(1, "forward_cb");
5336 DNPRINTF(XT_D_NAV
, "forward_cb: tab %d\n", t
->tab_id
);
5338 a
.i
= XT_NAV_FORWARD
;
5343 stop_cb(GtkWidget
*w
, struct tab
*t
)
5345 WebKitWebFrame
*frame
;
5350 DNPRINTF(XT_D_NAV
, "stop_cb: tab %d\n", t
->tab_id
);
5352 frame
= webkit_web_view_get_main_frame(t
->wv
);
5353 if (frame
== NULL
) {
5354 show_oops(t
, "stop_cb: no frame");
5358 webkit_web_frame_stop_loading(frame
);
5359 abort_and_free_favicon(t
);
5363 setup_webkit(struct tab
*t
)
5365 g_object_set((GObject
*)t
->settings
,
5366 "user-agent", t
->user_agent
, (char *)NULL
);
5367 g_object_set((GObject
*)t
->settings
,
5368 "enable-scripts", enable_scripts
, (char *)NULL
);
5369 g_object_set((GObject
*)t
->settings
,
5370 "enable-plugins", enable_plugins
, (char *)NULL
);
5371 adjustfont_webkit(t
, XT_FONT_SET
);
5373 webkit_web_view_set_settings(t
->wv
, t
->settings
);
5377 create_browser(struct tab
*t
)
5383 errx(1, "create_browser");
5385 t
->sb_h
= GTK_SCROLLBAR(gtk_hscrollbar_new(NULL
));
5386 t
->sb_v
= GTK_SCROLLBAR(gtk_vscrollbar_new(NULL
));
5387 t
->adjust_h
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_h
));
5388 t
->adjust_v
= gtk_range_get_adjustment(GTK_RANGE(t
->sb_v
));
5390 w
= gtk_scrolled_window_new(t
->adjust_h
, t
->adjust_v
);
5391 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w
),
5392 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
5394 t
->wv
= WEBKIT_WEB_VIEW(webkit_web_view_new());
5395 gtk_container_add(GTK_CONTAINER(w
), GTK_WIDGET(t
->wv
));
5398 t
->settings
= webkit_web_settings_new();
5400 if (user_agent
== NULL
) {
5401 g_object_get((GObject
*)t
->settings
, "user-agent", &strval
,
5403 t
->user_agent
= g_strdup_printf("%s %s+", strval
, version
);
5406 t
->user_agent
= g_strdup(user_agent
);
5419 w
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
5420 gtk_window_set_default_size(GTK_WINDOW(w
), window_width
, window_height
);
5421 gtk_widget_set_name(w
, "xxxterm");
5422 gtk_window_set_wmclass(GTK_WINDOW(w
), "xxxterm", "XXXTerm");
5423 g_signal_connect(G_OBJECT(w
), "delete_event",
5424 G_CALLBACK (gtk_main_quit
), NULL
);
5430 create_toolbar(struct tab
*t
)
5432 GtkWidget
*toolbar
= NULL
, *b
, *eb1
;
5434 b
= gtk_hbox_new(FALSE
, 0);
5436 gtk_container_set_border_width(GTK_CONTAINER(toolbar
), 0);
5439 /* backward button */
5440 t
->backward
= create_button("GoBack", GTK_STOCK_GO_BACK
, 0);
5441 gtk_widget_set_sensitive(t
->backward
, FALSE
);
5442 g_signal_connect(G_OBJECT(t
->backward
), "clicked",
5443 G_CALLBACK(backward_cb
), t
);
5444 gtk_box_pack_start(GTK_BOX(b
), t
->backward
, FALSE
, FALSE
, 0);
5446 /* forward button */
5447 t
->forward
= create_button("GoForward",GTK_STOCK_GO_FORWARD
, 0);
5448 gtk_widget_set_sensitive(t
->forward
, FALSE
);
5449 g_signal_connect(G_OBJECT(t
->forward
), "clicked",
5450 G_CALLBACK(forward_cb
), t
);
5451 gtk_box_pack_start(GTK_BOX(b
), t
->forward
, FALSE
,
5455 t
->stop
= create_button("Stop", GTK_STOCK_STOP
, 0);
5456 gtk_widget_set_sensitive(t
->stop
, FALSE
);
5457 g_signal_connect(G_OBJECT(t
->stop
), "clicked",
5458 G_CALLBACK(stop_cb
), t
);
5459 gtk_box_pack_start(GTK_BOX(b
), t
->stop
, FALSE
,
5463 t
->js_toggle
= create_button("JS-Toggle", enable_scripts
?
5464 GTK_STOCK_MEDIA_PLAY
: GTK_STOCK_MEDIA_PAUSE
, 0);
5465 gtk_widget_set_sensitive(t
->js_toggle
, TRUE
);
5466 g_signal_connect(G_OBJECT(t
->js_toggle
), "clicked",
5467 G_CALLBACK(js_toggle_cb
), t
);
5468 gtk_box_pack_start(GTK_BOX(b
), t
->js_toggle
, FALSE
, FALSE
, 0);
5471 t
->uri_entry
= gtk_entry_new();
5472 g_signal_connect(G_OBJECT(t
->uri_entry
), "activate",
5473 G_CALLBACK(activate_uri_entry_cb
), t
);
5474 g_signal_connect(G_OBJECT(t
->uri_entry
), "key-press-event",
5475 (GCallback
)entry_key_cb
, t
);
5476 eb1
= gtk_hbox_new(FALSE
, 0);
5477 gtk_container_set_border_width(GTK_CONTAINER(eb1
), 1);
5478 gtk_box_pack_start(GTK_BOX(eb1
), t
->uri_entry
, TRUE
, TRUE
, 0);
5479 gtk_box_pack_start(GTK_BOX(b
), eb1
, TRUE
, TRUE
, 0);
5481 /* set empty favicon */
5482 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(t
->uri_entry
),
5483 GTK_ENTRY_ICON_PRIMARY
, "text-html");
5486 if (fancy_bar
&& search_string
) {
5488 t
->search_entry
= gtk_entry_new();
5489 gtk_entry_set_width_chars(GTK_ENTRY(t
->search_entry
), 30);
5490 g_signal_connect(G_OBJECT(t
->search_entry
), "activate",
5491 G_CALLBACK(activate_search_entry_cb
), t
);
5492 g_signal_connect(G_OBJECT(t
->search_entry
), "key-press-event",
5493 (GCallback
)entry_key_cb
, t
);
5494 gtk_widget_set_size_request(t
->search_entry
, -1, -1);
5495 eb2
= gtk_hbox_new(FALSE
, 0);
5496 gtk_container_set_border_width(GTK_CONTAINER(eb2
), 1);
5497 gtk_box_pack_start(GTK_BOX(eb2
), t
->search_entry
, TRUE
, TRUE
,
5499 gtk_box_pack_start(GTK_BOX(b
), eb2
, FALSE
, FALSE
, 0);
5509 TAILQ_FOREACH(t
, &tabs
, entry
)
5510 t
->tab_id
= gtk_notebook_page_num(notebook
, t
->vbox
);
5514 undo_close_tab_save(struct tab
*t
)
5518 struct undo
*u1
, *u2
;
5519 WebKitWebFrame
*frame
;
5521 WebKitWebHistoryItem
*item
;
5523 frame
= webkit_web_view_get_main_frame(t
->wv
);
5524 uri
= webkit_web_frame_get_uri(frame
);
5526 if (uri
&& !strlen(uri
))
5529 u1
= g_malloc0(sizeof(struct undo
));
5530 u1
->uri
= g_strdup(uri
);
5532 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
5534 m
= webkit_web_back_forward_list_get_forward_length(t
->bfl
);
5535 n
= webkit_web_back_forward_list_get_back_length(t
->bfl
);
5538 /* forward history */
5539 items
= webkit_web_back_forward_list_get_forward_list_with_limit(t
->bfl
, m
);
5543 u1
->history
= g_list_prepend(u1
->history
,
5544 webkit_web_history_item_copy(item
));
5545 items
= g_list_next(items
);
5550 item
= webkit_web_back_forward_list_get_current_item(t
->bfl
);
5551 u1
->history
= g_list_prepend(u1
->history
,
5552 webkit_web_history_item_copy(item
));
5556 items
= webkit_web_back_forward_list_get_back_list_with_limit(t
->bfl
, n
);
5560 u1
->history
= g_list_prepend(u1
->history
,
5561 webkit_web_history_item_copy(item
));
5562 items
= g_list_next(items
);
5565 TAILQ_INSERT_HEAD(&undos
, u1
, entry
);
5567 if (undo_count
> XT_MAX_UNDO_CLOSE_TAB
) {
5568 u2
= TAILQ_LAST(&undos
, undo_tailq
);
5569 TAILQ_REMOVE(&undos
, u2
, entry
);
5571 g_list_free(u2
->history
);
5580 delete_tab(struct tab
*t
)
5582 DNPRINTF(XT_D_TAB
, "delete_tab: %p\n", t
);
5587 TAILQ_REMOVE(&tabs
, t
, entry
);
5589 /* halt all webkit activity */
5590 abort_and_free_favicon(t
);
5591 webkit_web_view_stop_loading(t
->wv
);
5592 undo_close_tab_save(t
);
5594 gtk_widget_destroy(t
->vbox
);
5595 g_free(t
->user_agent
);
5599 if (TAILQ_EMPTY(&tabs
))
5600 create_new_tab(NULL
, NULL
, 1);
5604 adjustfont_webkit(struct tab
*t
, int adjust
)
5607 errx(1, "adjustfont_webkit");
5609 if (adjust
== XT_FONT_SET
)
5610 t
->font_size
= default_font_size
;
5612 t
->font_size
+= adjust
;
5613 g_object_set((GObject
*)t
->settings
, "default-font-size",
5614 t
->font_size
, (char *)NULL
);
5615 g_object_get((GObject
*)t
->settings
, "default-font-size",
5616 &t
->font_size
, (char *)NULL
);
5620 append_tab(struct tab
*t
)
5625 TAILQ_INSERT_TAIL(&tabs
, t
, entry
);
5626 t
->tab_id
= gtk_notebook_append_page(notebook
, t
->vbox
, t
->tab_content
);
5630 create_new_tab(char *title
, struct undo
*u
, int focus
)
5633 int load
= 1, id
, notfound
;
5634 char *newuri
= NULL
;
5636 WebKitWebHistoryItem
*item
;
5640 DNPRINTF(XT_D_TAB
, "create_new_tab: title %s focus %d\n", title
, focus
);
5642 if (tabless
&& !TAILQ_EMPTY(&tabs
)) {
5643 DNPRINTF(XT_D_TAB
, "create_new_tab: new tab rejected\n");
5647 t
= g_malloc0(sizeof *t
);
5649 if (title
== NULL
) {
5650 title
= "(untitled)";
5653 if (valid_url_type(title
)) {
5654 newuri
= guess_url_type(title
);
5659 t
->vbox
= gtk_vbox_new(FALSE
, 0);
5661 /* label + button for tab */
5662 b
= gtk_hbox_new(FALSE
, 0);
5665 #if GTK_CHECK_VERSION(2, 20, 0)
5666 t
->spinner
= gtk_spinner_new ();
5668 t
->label
= gtk_label_new(title
);
5669 bb
= create_button("Close", GTK_STOCK_CLOSE
, 1);
5670 gtk_widget_set_size_request(t
->label
, 100, 0);
5671 gtk_widget_set_size_request(b
, 130, 0);
5673 gtk_box_pack_start(GTK_BOX(b
), bb
, FALSE
, FALSE
, 0);
5674 gtk_box_pack_start(GTK_BOX(b
), t
->label
, FALSE
, FALSE
, 0);
5675 #if GTK_CHECK_VERSION(2, 20, 0)
5676 gtk_box_pack_start(GTK_BOX(b
), t
->spinner
, FALSE
, FALSE
, 0);
5680 t
->toolbar
= create_toolbar(t
);
5681 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->toolbar
, FALSE
, FALSE
, 0);
5684 t
->browser_win
= create_browser(t
);
5685 gtk_box_pack_start(GTK_BOX(t
->vbox
), t
->browser_win
, TRUE
, TRUE
, 0);
5687 /* oops message for user feedback */
5688 t
->oops
= gtk_entry_new();
5689 gtk_entry_set_inner_border(GTK_ENTRY(t
->oops
), NULL
);
5690 gtk_entry_set_has_frame(GTK_ENTRY(t
->oops
), FALSE
);
5691 gtk_widget_set_can_focus(GTK_WIDGET(t
->oops
), FALSE
);
5692 gdk_color_parse("red", &color
);
5693 gtk_widget_modify_base(t
->oops
, GTK_STATE_NORMAL
, &color
);
5694 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->oops
, FALSE
, FALSE
, 0);
5697 t
->cmd
= gtk_entry_new();
5698 gtk_entry_set_inner_border(GTK_ENTRY(t
->cmd
), NULL
);
5699 gtk_entry_set_has_frame(GTK_ENTRY(t
->cmd
), FALSE
);
5700 gtk_box_pack_end(GTK_BOX(t
->vbox
), t
->cmd
, FALSE
, FALSE
, 0);
5702 /* xtp meaning is normal by default */
5703 t
->xtp_meaning
= XT_XTP_TAB_MEANING_NORMAL
;
5705 /* and show it all */
5706 gtk_widget_show_all(b
);
5707 gtk_widget_show_all(t
->vbox
);
5709 if (append_next
== 0 || gtk_notebook_get_n_pages(notebook
) == 0)
5713 id
= gtk_notebook_get_current_page(notebook
);
5714 TAILQ_FOREACH(tt
, &tabs
, entry
) {
5715 if (tt
->tab_id
== id
) {
5717 TAILQ_INSERT_AFTER(&tabs
, tt
, t
, entry
);
5718 gtk_notebook_insert_page(notebook
, t
->vbox
, b
,
5728 #if GTK_CHECK_VERSION(2, 20, 0)
5729 /* turn spinner off if we are a new tab without uri */
5731 gtk_spinner_stop(GTK_SPINNER(t
->spinner
));
5732 gtk_widget_hide(t
->spinner
);
5735 /* make notebook tabs reorderable */
5736 gtk_notebook_set_tab_reorderable(notebook
, t
->vbox
, TRUE
);
5738 g_object_connect((GObject
*)t
->cmd
,
5739 "signal::key-press-event", (GCallback
)cmd_keypress_cb
, t
,
5740 "signal::key-release-event", (GCallback
)cmd_keyrelease_cb
, t
,
5741 "signal::focus-out-event", (GCallback
)cmd_focusout_cb
, t
,
5742 "signal::activate", (GCallback
)cmd_activate_cb
, t
,
5745 /* reuse wv_button_cb to hide oops */
5746 g_object_connect((GObject
*)t
->oops
,
5747 "signal::button_press_event", (GCallback
)wv_button_cb
, t
,
5750 g_object_connect((GObject
*)t
->wv
,
5751 "signal::key-press-event", (GCallback
)wv_keypress_cb
, t
,
5752 "signal-after::key-press-event", (GCallback
)wv_keypress_after_cb
, t
,
5753 /* "signal::hovering-over-link", (GCallback)webview_hover_cb, t, */
5754 "signal::download-requested", (GCallback
)webview_download_cb
, t
,
5755 "signal::mime-type-policy-decision-requested", (GCallback
)webview_mimetype_cb
, t
,
5756 "signal::navigation-policy-decision-requested", (GCallback
)webview_npd_cb
, t
,
5757 "signal::new-window-policy-decision-requested", (GCallback
)webview_nw_cb
, t
,
5758 "signal::create-web-view", (GCallback
)webview_cwv_cb
, t
,
5759 "signal::event", (GCallback
)webview_event_cb
, t
,
5760 "signal::load-finished", (GCallback
)webview_load_finished_cb
, t
,
5761 "signal::load-progress-changed", (GCallback
)webview_progress_changed_cb
, t
,
5762 #if WEBKIT_CHECK_VERSION(1, 1, 18)
5763 "signal::icon-loaded", (GCallback
)notify_icon_loaded_cb
, t
,
5765 "signal::button_press_event", (GCallback
)wv_button_cb
, t
,
5767 g_signal_connect(t
->wv
,
5768 "notify::load-status", G_CALLBACK(notify_load_status_cb
), t
);
5770 /* hijack the unused keys as if we were the browser */
5771 g_object_connect((GObject
*)t
->toolbar
,
5772 "signal-after::key-press-event", (GCallback
)wv_keypress_after_cb
, t
,
5775 g_signal_connect(G_OBJECT(bb
), "button_press_event",
5776 G_CALLBACK(tab_close_cb
), t
);
5782 gtk_widget_hide(t
->toolbar
);
5785 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
5786 DNPRINTF(XT_D_TAB
, "create_new_tab: going to tab: %d\n",
5791 webkit_web_view_load_uri(t
->wv
, title
);
5793 gtk_widget_grab_focus(GTK_WIDGET(t
->uri_entry
));
5795 t
->bfl
= webkit_web_view_get_back_forward_list(t
->wv
);
5796 /* restore the tab's history */
5797 if (u
&& u
->history
) {
5801 webkit_web_back_forward_list_add_item(t
->bfl
, item
);
5802 items
= g_list_next(items
);
5805 item
= g_list_nth_data(u
->history
, u
->back
);
5807 webkit_web_view_go_to_back_forward_item(t
->wv
, item
);
5810 g_list_free(u
->history
);
5818 notebook_switchpage_cb(GtkNotebook
*nb
, GtkNotebookPage
*nbp
, guint pn
,
5824 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: tab: %d\n", pn
);
5826 TAILQ_FOREACH(t
, &tabs
, entry
) {
5827 if (t
->tab_id
== pn
) {
5828 DNPRINTF(XT_D_TAB
, "notebook_switchpage_cb: going to "
5831 uri
= webkit_web_view_get_title(t
->wv
);
5834 gtk_window_set_title(GTK_WINDOW(main_window
), uri
);
5840 gtk_widget_grab_focus(GTK_WIDGET(t
->wv
));
5846 menuitem_response(struct tab
*t
)
5848 gtk_notebook_set_current_page(notebook
, t
->tab_id
);
5852 arrow_cb(GtkWidget
*w
, GdkEventButton
*event
, gpointer user_data
)
5854 GtkWidget
*menu
, *menu_items
;
5855 GdkEventButton
*bevent
;
5856 WebKitWebFrame
*frame
;
5860 if (event
->type
== GDK_BUTTON_PRESS
) {
5861 bevent
= (GdkEventButton
*) event
;
5862 menu
= gtk_menu_new();
5864 TAILQ_FOREACH(ti
, &tabs
, entry
) {
5865 frame
= webkit_web_view_get_main_frame(ti
->wv
);
5866 uri
= webkit_web_frame_get_uri(frame
);
5867 /* XXX make sure there is something to print */
5868 /* XXX add gui pages in here to look purdy */
5871 if (strlen(uri
) == 0)
5873 menu_items
= gtk_menu_item_new_with_label(uri
);
5874 gtk_menu_append(GTK_MENU (menu
), menu_items
);
5875 gtk_widget_show(menu_items
);
5877 gtk_signal_connect_object(GTK_OBJECT(menu_items
),
5878 "activate", GTK_SIGNAL_FUNC(menuitem_response
),
5882 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
,
5883 bevent
->button
, bevent
->time
);
5885 /* unref object so it'll free itself when popped down */
5886 g_object_ref_sink(menu
);
5887 g_object_unref(menu
);
5889 return (TRUE
/* eat event */);
5892 return (FALSE
/* propagate */);
5896 icon_size_map(int icon_size
)
5898 if (icon_size
<= GTK_ICON_SIZE_INVALID
||
5899 icon_size
> GTK_ICON_SIZE_DIALOG
)
5900 return (GTK_ICON_SIZE_SMALL_TOOLBAR
);
5906 create_button(char *name
, char *stockid
, int size
)
5908 GtkWidget
*button
, *image
;
5911 rcstring
= g_strdup_printf(
5912 "style \"%s-style\"\n"
5914 " GtkWidget::focus-padding = 0\n"
5915 " GtkWidget::focus-line-width = 0\n"
5919 "widget \"*.%s\" style \"%s-style\"",name
,name
,name
);
5920 gtk_rc_parse_string(rcstring
);
5922 button
= gtk_button_new();
5923 gtk_button_set_focus_on_click(GTK_BUTTON(button
), FALSE
);
5924 gtk_icon_size
= icon_size_map(size
?size
:icon_size
);
5926 image
= gtk_image_new_from_stock(stockid
, gtk_icon_size
);
5927 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
5928 gtk_container_set_border_width(GTK_CONTAINER(button
), 1);
5929 gtk_container_add(GTK_CONTAINER(button
), GTK_WIDGET(image
));
5930 gtk_widget_set_name(button
, name
);
5931 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
5932 gtk_widget_set_tooltip_text(button
, name
);
5938 button_set_stockid(GtkWidget
*button
, char *stockid
)
5941 image
= gtk_image_new_from_stock(stockid
, icon_size_map(icon_size
));
5942 gtk_widget_set_size_request(GTK_WIDGET(image
), -1, -1);
5943 gtk_button_set_image(GTK_BUTTON(button
), image
);
5952 char file
[PATH_MAX
];
5955 vbox
= gtk_vbox_new(FALSE
, 0);
5956 gtk_box_set_spacing(GTK_BOX(vbox
), 0);
5957 notebook
= GTK_NOTEBOOK(gtk_notebook_new());
5959 gtk_notebook_set_show_tabs(notebook
, FALSE
);
5961 gtk_notebook_set_tab_hborder(notebook
, 0);
5962 gtk_notebook_set_tab_vborder(notebook
, 0);
5964 gtk_notebook_set_show_border(notebook
, FALSE
);
5965 gtk_notebook_set_scrollable(notebook
, TRUE
);
5966 gtk_notebook_set_homogeneous_tabs(notebook
, TRUE
);
5967 gtk_widget_set_can_focus(GTK_WIDGET(notebook
), FALSE
);
5969 abtn
= gtk_button_new();
5970 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_NONE
);
5971 gtk_widget_set_size_request(arrow
, -1, -1);
5972 gtk_container_add(GTK_CONTAINER(abtn
), arrow
);
5973 gtk_widget_set_size_request(abtn
, -1, 20);
5974 gtk_notebook_set_action_widget(notebook
, abtn
, GTK_PACK_END
);
5976 gtk_widget_set_size_request(GTK_WIDGET(notebook
), -1, -1);
5977 gtk_box_pack_start(GTK_BOX(vbox
), GTK_WIDGET(notebook
), TRUE
, TRUE
, 0);
5978 gtk_widget_set_size_request(vbox
, -1, -1);
5980 g_object_connect((GObject
*)notebook
,
5981 "signal::switch-page", (GCallback
)notebook_switchpage_cb
, NULL
,
5983 g_signal_connect(G_OBJECT(abtn
), "button_press_event",
5984 G_CALLBACK(arrow_cb
), NULL
);
5986 main_window
= create_window();
5987 gtk_container_add(GTK_CONTAINER(main_window
), vbox
);
5988 gtk_window_set_title(GTK_WINDOW(main_window
), XT_NAME
);
5991 for (i
= 0; i
< LENGTH(icons
); i
++) {
5992 snprintf(file
, sizeof file
, "%s/%s", resource_dir
, icons
[i
]);
5993 pb
= gdk_pixbuf_new_from_file(file
, NULL
);
5994 l
= g_list_append(l
, pb
);
5996 gtk_window_set_default_icon_list(l
);
5998 gtk_widget_show_all(abtn
);
5999 gtk_widget_show_all(main_window
);
6003 set_hook(void **hook
, char *name
)
6006 errx(1, "set_hook");
6008 if (*hook
== NULL
) {
6009 *hook
= dlsym(RTLD_NEXT
, name
);
6011 errx(1, "can't hook %s", name
);
6015 /* override libsoup soup_cookie_equal because it doesn't look at domain */
6017 soup_cookie_equal(SoupCookie
*cookie1
, SoupCookie
*cookie2
)
6019 g_return_val_if_fail(cookie1
, FALSE
);
6020 g_return_val_if_fail(cookie2
, FALSE
);
6022 return (!strcmp (cookie1
->name
, cookie2
->name
) &&
6023 !strcmp (cookie1
->value
, cookie2
->value
) &&
6024 !strcmp (cookie1
->path
, cookie2
->path
) &&
6025 !strcmp (cookie1
->domain
, cookie2
->domain
));
6029 transfer_cookies(void)
6032 SoupCookie
*sc
, *pc
;
6034 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
6036 for (;cf
; cf
= cf
->next
) {
6038 sc
= soup_cookie_copy(pc
);
6039 _soup_cookie_jar_add_cookie(s_cookiejar
, sc
);
6042 soup_cookies_free(cf
);
6046 soup_cookie_jar_delete_cookie(SoupCookieJar
*jar
, SoupCookie
*c
)
6051 print_cookie("soup_cookie_jar_delete_cookie", c
);
6053 if (cookies_enabled
== 0)
6056 if (jar
== NULL
|| c
== NULL
)
6059 /* find and remove from persistent jar */
6060 cf
= soup_cookie_jar_all_cookies(p_cookiejar
);
6062 for (;cf
; cf
= cf
->next
) {
6064 if (soup_cookie_equal(ci
, c
)) {
6065 _soup_cookie_jar_delete_cookie(p_cookiejar
, ci
);
6070 soup_cookies_free(cf
);
6072 /* delete from session jar */
6073 _soup_cookie_jar_delete_cookie(s_cookiejar
, c
);
6077 soup_cookie_jar_add_cookie(SoupCookieJar
*jar
, SoupCookie
*cookie
)
6083 DNPRINTF(XT_D_COOKIE
, "soup_cookie_jar_add_cookie: %p %p %p\n",
6084 jar
, p_cookiejar
, s_cookiejar
);
6086 if (cookies_enabled
== 0)
6089 /* see if we are up and running */
6090 if (p_cookiejar
== NULL
) {
6091 _soup_cookie_jar_add_cookie(jar
, cookie
);
6094 /* disallow p_cookiejar adds, shouldn't happen */
6095 if (jar
== p_cookiejar
)
6098 if ((d
= wl_find(cookie
->domain
, &c_wl
)) == NULL
) {
6100 DNPRINTF(XT_D_COOKIE
,
6101 "soup_cookie_jar_add_cookie: reject %s\n",
6103 if (save_rejected_cookies
) {
6104 if ((r_cookie_f
= fopen(rc_fname
, "a+")) == NULL
)
6105 err(1, "reject cookie file");
6106 fseek(r_cookie_f
, 0, SEEK_END
);
6107 fprintf(r_cookie_f
, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
6108 cookie
->http_only
? "#HttpOnly_" : "",
6110 *cookie
->domain
== '.' ? "TRUE" : "FALSE",
6112 cookie
->secure
? "TRUE" : "FALSE",
6114 (gulong
)soup_date_to_time_t(cookie
->expires
) :
6121 if (!allow_volatile_cookies
)
6125 if (cookie
->expires
== NULL
&& session_timeout
) {
6126 soup_cookie_set_expires(cookie
,
6127 soup_date_new_from_now(session_timeout
));
6128 print_cookie("modified add cookie", cookie
);
6131 /* see if we are white listed for persistence */
6132 if (d
&& d
->handy
) {
6133 /* add to persistent jar */
6134 c
= soup_cookie_copy(cookie
);
6135 print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c
);
6136 _soup_cookie_jar_add_cookie(p_cookiejar
, c
);
6139 /* add to session jar */
6140 print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie
);
6141 _soup_cookie_jar_add_cookie(s_cookiejar
, cookie
);
6147 char file
[PATH_MAX
];
6149 set_hook((void *)&_soup_cookie_jar_add_cookie
,
6150 "soup_cookie_jar_add_cookie");
6151 set_hook((void *)&_soup_cookie_jar_delete_cookie
,
6152 "soup_cookie_jar_delete_cookie");
6154 if (cookies_enabled
== 0)
6158 * the following code is intricate due to overriding several libsoup
6160 * do not alter order of these operations.
6163 /* rejected cookies */
6164 if (save_rejected_cookies
)
6165 snprintf(rc_fname
, sizeof file
, "%s/rejected.txt", work_dir
);
6167 /* persistent cookies */
6168 snprintf(file
, sizeof file
, "%s/cookies.txt", work_dir
);
6169 p_cookiejar
= soup_cookie_jar_text_new(file
, read_only_cookies
);
6171 /* session cookies */
6172 s_cookiejar
= soup_cookie_jar_new();
6173 g_object_set(G_OBJECT(s_cookiejar
), SOUP_COOKIE_JAR_ACCEPT_POLICY
,
6174 cookie_policy
, (void *)NULL
);
6177 soup_session_add_feature(session
, (SoupSessionFeature
*)s_cookiejar
);
6181 setup_proxy(char *uri
)
6184 g_object_set(session
, "proxy_uri", NULL
, (char *)NULL
);
6185 soup_uri_free(proxy_uri
);
6189 if (http_proxy
!= uri
) {
6196 http_proxy
= g_strdup(uri
);
6197 DNPRINTF(XT_D_CONFIG
, "setup_proxy: %s\n", uri
);
6198 proxy_uri
= soup_uri_new(http_proxy
);
6199 g_object_set(session
, "proxy-uri", proxy_uri
, (char *)NULL
);
6204 send_url_to_socket(char *url
)
6206 int s
, len
, rv
= -1;
6207 struct sockaddr_un sa
;
6209 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6210 warnx("send_url_to_socket: socket");
6214 sa
.sun_family
= AF_UNIX
;
6215 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6216 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6219 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6220 warnx("send_url_to_socket: connect");
6224 if (send(s
, url
, strlen(url
) + 1, 0) == -1) {
6225 warnx("send_url_to_socket: send");
6234 socket_watcher(gpointer data
, gint fd
, GdkInputCondition cond
)
6237 char str
[XT_MAX_URL_LENGTH
];
6238 socklen_t t
= sizeof(struct sockaddr_un
);
6239 struct sockaddr_un sa
;
6244 if ((s
= accept(fd
, (struct sockaddr
*)&sa
, &t
)) == -1) {
6245 warn("socket_watcher: accept");
6249 if (getpeereid(s
, &uid
, &gid
) == -1) {
6250 warn("socket_watcher: getpeereid");
6253 if (uid
!= getuid() || gid
!= getgid()) {
6254 warnx("socket_watcher: unauthorized user");
6260 warnx("socket_watcher: not a valid user");
6264 n
= recv(s
, str
, sizeof(str
), 0);
6268 create_new_tab(str
, NULL
, 1);
6275 struct sockaddr_un sa
;
6277 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6278 warn("is_running: socket");
6282 sa
.sun_family
= AF_UNIX
;
6283 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6284 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6287 /* connect to see if there is a listener */
6288 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1)
6289 rv
= 0; /* not running */
6291 rv
= 1; /* already running */
6302 struct sockaddr_un sa
;
6304 if ((s
= socket(AF_UNIX
, SOCK_STREAM
, 0)) == -1) {
6305 warn("build_socket: socket");
6309 sa
.sun_family
= AF_UNIX
;
6310 snprintf(sa
.sun_path
, sizeof(sa
.sun_path
), "%s/%s/%s",
6311 pwd
->pw_dir
, XT_DIR
, XT_SOCKET_FILE
);
6314 /* connect to see if there is a listener */
6315 if (connect(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6316 /* no listener so we will */
6317 unlink(sa
.sun_path
);
6319 if (bind(s
, (struct sockaddr
*)&sa
, len
) == -1) {
6320 warn("build_socket: bind");
6324 if (listen(s
, 1) == -1) {
6325 warn("build_socket: listen");
6341 "%s [-nSTVt][-f file][-s session] url ...\n", __progname
);
6346 main(int argc
, char *argv
[])
6349 int c
, s
, optn
= 0, focus
= 1;
6350 char conf
[PATH_MAX
] = { '\0' };
6351 char file
[PATH_MAX
];
6352 char *env_proxy
= NULL
;
6358 strlcpy(named_session
, XT_SAVED_TABS_FILE
, sizeof named_session
);
6360 while ((c
= getopt(argc
, argv
, "STVf:s:tn")) != -1) {
6369 errx(0 , "Version: %s", version
);
6372 strlcpy(conf
, optarg
, sizeof(conf
));
6375 strlcpy(named_session
, optarg
, sizeof(named_session
));
6394 RB_INIT(&downloads
);
6396 TAILQ_INIT(&aliases
);
6399 gnutls_global_init();
6401 /* generate session keys for xtp pages */
6402 generate_xtp_session_key(&dl_session_key
);
6403 generate_xtp_session_key(&hl_session_key
);
6404 generate_xtp_session_key(&cl_session_key
);
6405 generate_xtp_session_key(&fl_session_key
);
6408 gtk_init(&argc
, &argv
);
6409 if (!g_thread_supported())
6410 g_thread_init(NULL
);
6412 pwd
= getpwuid(getuid());
6414 errx(1, "invalid user %d", getuid());
6416 /* set download dir */
6417 strlcpy(download_dir
, pwd
->pw_dir
, sizeof download_dir
);
6419 /* set default string settings */
6420 home
= g_strdup("http://www.peereboom.us");
6421 resource_dir
= g_strdup("/usr/local/share/xxxterm/");
6422 strlcpy(runtime_settings
,"runtime", sizeof runtime_settings
);
6424 /* read config file */
6425 if (strlen(conf
) == 0)
6426 snprintf(conf
, sizeof conf
, "%s/.%s",
6427 pwd
->pw_dir
, XT_CONF_FILE
);
6428 config_parse(conf
, 0);
6430 /* working directory */
6431 snprintf(work_dir
, sizeof work_dir
, "%s/%s", pwd
->pw_dir
, XT_DIR
);
6432 if (stat(work_dir
, &sb
)) {
6433 if (mkdir(work_dir
, S_IRWXU
) == -1)
6434 err(1, "mkdir work_dir");
6435 if (stat(work_dir
, &sb
))
6436 err(1, "stat work_dir");
6438 if (S_ISDIR(sb
.st_mode
) == 0)
6439 errx(1, "%s not a dir", work_dir
);
6440 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6441 warnx("fixing invalid permissions on %s", work_dir
);
6442 if (chmod(work_dir
, S_IRWXU
) == -1)
6446 /* icon cache dir */
6447 snprintf(cache_dir
, sizeof cache_dir
, "%s/%s/%s",
6448 pwd
->pw_dir
, XT_DIR
, XT_CACHE_DIR
);
6449 if (stat(cache_dir
, &sb
)) {
6450 if (mkdir(cache_dir
, S_IRWXU
) == -1)
6451 err(1, "mkdir cache_dir");
6452 if (stat(cache_dir
, &sb
))
6453 err(1, "stat cache_dir");
6455 if (S_ISDIR(sb
.st_mode
) == 0)
6456 errx(1, "%s not a dir", cache_dir
);
6457 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6458 warnx("fixing invalid permissions on %s", cache_dir
);
6459 if (chmod(cache_dir
, S_IRWXU
) == -1)
6464 snprintf(certs_dir
, sizeof certs_dir
, "%s/%s/%s",
6465 pwd
->pw_dir
, XT_DIR
, XT_CERT_DIR
);
6466 if (stat(certs_dir
, &sb
)) {
6467 if (mkdir(certs_dir
, S_IRWXU
) == -1)
6468 err(1, "mkdir certs_dir");
6469 if (stat(certs_dir
, &sb
))
6470 err(1, "stat certs_dir");
6472 if (S_ISDIR(sb
.st_mode
) == 0)
6473 errx(1, "%s not a dir", certs_dir
);
6474 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6475 warnx("fixing invalid permissions on %s", certs_dir
);
6476 if (chmod(certs_dir
, S_IRWXU
) == -1)
6481 snprintf(sessions_dir
, sizeof sessions_dir
, "%s/%s/%s",
6482 pwd
->pw_dir
, XT_DIR
, XT_SESSIONS_DIR
);
6483 if (stat(sessions_dir
, &sb
)) {
6484 if (mkdir(sessions_dir
, S_IRWXU
) == -1)
6485 err(1, "mkdir sessions_dir");
6486 if (stat(sessions_dir
, &sb
))
6487 err(1, "stat sessions_dir");
6489 if (S_ISDIR(sb
.st_mode
) == 0)
6490 errx(1, "%s not a dir", sessions_dir
);
6491 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6492 warnx("fixing invalid permissions on %s", sessions_dir
);
6493 if (chmod(sessions_dir
, S_IRWXU
) == -1)
6496 /* runtime settings that can override config file */
6497 if (runtime_settings
[0] != '\0')
6498 config_parse(runtime_settings
, 1);
6501 if (!strcmp(download_dir
, pwd
->pw_dir
))
6502 strlcat(download_dir
, "/downloads", sizeof download_dir
);
6503 if (stat(download_dir
, &sb
)) {
6504 if (mkdir(download_dir
, S_IRWXU
) == -1)
6505 err(1, "mkdir download_dir");
6506 if (stat(download_dir
, &sb
))
6507 err(1, "stat download_dir");
6509 if (S_ISDIR(sb
.st_mode
) == 0)
6510 errx(1, "%s not a dir", download_dir
);
6511 if (((sb
.st_mode
& (S_IRWXU
| S_IRWXG
| S_IRWXO
))) != S_IRWXU
) {
6512 warnx("fixing invalid permissions on %s", download_dir
);
6513 if (chmod(download_dir
, S_IRWXU
) == -1)
6517 /* favorites file */
6518 snprintf(file
, sizeof file
, "%s/%s", work_dir
, XT_FAVS_FILE
);
6519 if (stat(file
, &sb
)) {
6520 warnx("favorites file doesn't exist, creating it");
6521 if ((f
= fopen(file
, "w")) == NULL
)
6522 err(1, "favorites");
6527 session
= webkit_get_default_session();
6532 if (stat(ssl_ca_file
, &sb
)) {
6533 warn("no CA file: %s", ssl_ca_file
);
6534 g_free(ssl_ca_file
);
6537 g_object_set(session
,
6538 SOUP_SESSION_SSL_CA_FILE
, ssl_ca_file
,
6539 SOUP_SESSION_SSL_STRICT
, ssl_strict_certs
,
6544 env_proxy
= getenv("http_proxy");
6546 setup_proxy(env_proxy
);
6548 setup_proxy(http_proxy
);
6550 /* see if there is already an xxxterm running */
6551 if (single_instance
&& is_running()) {
6553 warnx("already running");
6558 send_url_to_socket(argv
[0]);
6569 if (save_global_history
)
6570 restore_global_history();
6572 if (!strcmp(named_session
, XT_SAVED_TABS_FILE
))
6573 restore_saved_tabs();
6575 a
.s
= named_session
;
6576 open_tabs(NULL
, &a
);
6580 create_new_tab(argv
[0], NULL
, focus
);
6587 if (TAILQ_EMPTY(&tabs
))
6588 create_new_tab(home
, NULL
, 1);
6591 if ((s
= build_socket()) != -1)
6592 gdk_input_add(s
, GDK_INPUT_READ
, socket_watcher
, NULL
);
6596 gnutls_global_deinit();