/*
* Copyright (c) 2010, 2011 Marco Peereboom "
"Authors:"
"%s
\n"
"%s\n\n"
"",
title,
addstyles ? XT_PAGE_STYLE : "",
head,
title,
body);
return (r);
}
/*
* Display a web page from a HTML string in memory, rather than from a URL
*/
void
load_webkit_string(struct tab *t, const char *str, gchar *title)
{
char file[PATH_MAX];
int i;
/* we set this to indicate we want to manually do navaction */
if (t->bfl)
t->item = webkit_web_back_forward_list_get_current_item(t->bfl);
t->xtp_meaning = XT_XTP_TAB_MEANING_NORMAL;
if (title) {
/* set t->xtp_meaning */
for (i = 0; i < LENGTH(about_list); i++)
if (!strcmp(title, about_list[i].name)) {
t->xtp_meaning = i;
break;
}
webkit_web_view_load_string(t->wv, str, NULL, encoding,
"file://");
#if GTK_CHECK_VERSION(2, 20, 0)
gtk_spinner_stop(GTK_SPINNER(t->spinner));
gtk_widget_hide(t->spinner);
#endif
snprintf(file, sizeof file, "%s/%s", resource_dir, icons[0]);
xt_icon_from_file(t, file);
}
}
int
blank(struct tab *t, struct karg *args)
{
if (t == NULL)
show_oops(NULL, "blank invalid parameters");
load_webkit_string(t, "", XT_URI_ABOUT_BLANK);
return (0);
}
int
about(struct tab *t, struct karg *args)
{
char *page, *body;
if (t == NULL)
show_oops(NULL, "about invalid parameters");
body = g_strdup_printf("Version: %s"
#ifdef XXXTERM_BUILDSTR
"
Build: %s"
#endif
""
"
"
"Copyrights and licenses can be found on the XXXTerm "
"website"
"
* results vary based on settings
", blocked_cookies, line); page = get_html_page("Statistics", body, "", 0); g_free(body); load_webkit_string(t, page, XT_URI_ABOUT_STATS); g_free(page); return (0); } void show_certs(struct tab *t, gnutls_x509_crt_t *certs, size_t cert_count, char *title) { gnutls_datum_t cinfo; char *tmp, *body; int i; body = g_strdup(""); for (i = 0; i < cert_count; i++) { if (gnutls_x509_crt_print(certs[i], GNUTLS_CRT_PRINT_FULL, &cinfo)) return; tmp = body; body = g_strdup_printf("%s%s", body, i, cinfo.data); gnutls_free(cinfo.data); g_free(tmp); } tmp = get_html_page(title, body, "", 0); g_free(body); load_webkit_string(t, tmp, XT_URI_ABOUT_CERTS); g_free(tmp); } int ca_cmd(struct tab *t, struct karg *args) { FILE *f = NULL; int rv = 1, certs = 0, certs_read; struct stat sb; gnutls_datum_t dt; gnutls_x509_crt_t *c = NULL; char *certs_buf = NULL, *s; if ((f = fopen(ssl_ca_file, "r")) == NULL) { show_oops(t, "Can't open CA file: %s", ssl_ca_file); return (1); } if (fstat(fileno(f), &sb) == -1) { show_oops(t, "Can't stat CA file: %s", ssl_ca_file); goto done; } certs_buf = g_malloc(sb.st_size + 1); if (fread(certs_buf, 1, sb.st_size, f) != sb.st_size) { show_oops(t, "Can't read CA file: %s", strerror(errno)); goto done; } certs_buf[sb.st_size] = '\0'; s = certs_buf; while ((s = strstr(s, "BEGIN CERTIFICATE"))) { certs++; s += strlen("BEGIN CERTIFICATE"); } bzero(&dt, sizeof dt); dt.data = (unsigned char *)certs_buf; dt.size = sb.st_size; c = g_malloc(sizeof(gnutls_x509_crt_t) * certs); certs_read = gnutls_x509_crt_list_import(c, (unsigned int *)&certs, &dt, GNUTLS_X509_FMT_PEM, 0); if (certs_read <= 0) { show_oops(t, "No cert(s) available"); goto done; } show_certs(t, c, certs_read, "Certificate Authority Certificates"); done: if (c) g_free(c); if (certs_buf) g_free(certs_buf); if (f) fclose(f); return (rv); } int cookie_show_wl(struct tab *t, struct karg *args) { args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION; wl_show(t, args, "Cookie White List", &c_wl); return (0); } int js_show_wl(struct tab *t, struct karg *args) { args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION; wl_show(t, args, "JavaScript White List", &js_wl); return (0); } int cookie_cmd(struct tab *t, struct karg *args) { if (args->i & XT_SHOW) wl_show(t, args, "Cookie White List", &c_wl); else if (args->i & XT_WL_TOGGLE) { args->i |= XT_WL_RELOAD; toggle_cwl(t, args); } else if (args->i & XT_SAVE) { args->i |= XT_WL_RELOAD; wl_save(t, args, XT_WL_COOKIE); } else if (args->i & XT_DELETE) show_oops(t, "'cookie delete' currently unimplemented"); return (0); } int js_cmd(struct tab *t, struct karg *args) { if (args->i & XT_SHOW) wl_show(t, args, "JavaScript White List", &js_wl); else if (args->i & XT_SAVE) { args->i |= XT_WL_RELOAD; wl_save(t, args, XT_WL_JAVASCRIPT); } else if (args->i & XT_WL_TOGGLE) { args->i |= XT_WL_RELOAD; toggle_js(t, args); } else if (args->i & XT_DELETE) show_oops(t, "'js delete' currently unimplemented"); return (0); } int pl_show_wl(struct tab *t, struct karg *args) { args->i = XT_SHOW | XT_WL_PERSISTENT | XT_WL_SESSION; wl_show(t, args, "Plugin White List", &pl_wl); return (0); } int pl_cmd(struct tab *t, struct karg *args) { if (args->i & XT_SHOW) wl_show(t, args, "Plugin White List", &pl_wl); else if (args->i & XT_SAVE) { args->i |= XT_WL_RELOAD; wl_save(t, args, XT_WL_PLUGIN); } else if (args->i & XT_WL_TOGGLE) { args->i |= XT_WL_RELOAD; toggle_pl(t, args); } else if (args->i & XT_DELETE) show_oops(t, "'plugin delete' currently unimplemented"); return (0); } /* * cancel, remove, etc. downloads */ void xtp_handle_dl(struct tab *t, uint8_t cmd, int id) { struct download find, *d = NULL; DNPRINTF(XT_D_DOWNLOAD, "download control: cmd %d, id %d\n", cmd, id); /* some commands require a valid download id */ if (cmd != XT_XTP_DL_LIST) { /* lookup download in question */ find.id = id; d = RB_FIND(download_list, &downloads, &find); if (d == NULL) { show_oops(t, "%s: no such download", __func__); return; } } /* decide what to do */ switch (cmd) { case XT_XTP_DL_CANCEL: webkit_download_cancel(d->download); break; case XT_XTP_DL_UNLINK: unlink(webkit_download_get_destination_uri(d->download) + strlen("file://")); /* FALLTHROUGH */ case XT_XTP_DL_REMOVE: webkit_download_cancel(d->download); /* just incase */ g_object_unref(d->download); RB_REMOVE(download_list, &downloads, d); break; case XT_XTP_DL_LIST: /* Nothing */ break; default: show_oops(t, "%s: unknown command", __func__); break; }; xtp_page_dl(t, NULL); } /* * Actions on history, only does one thing for now, but * we provide the function for future actions */ void xtp_handle_hl(struct tab *t, uint8_t cmd, int id) { struct history *h, *next; int i = 1; switch (cmd) { case XT_XTP_HL_REMOVE: /* walk backwards, as listed in reverse */ for (h = RB_MAX(history_list, &hl); h != NULL; h = next) { next = RB_PREV(history_list, &hl, h); if (id == i) { RB_REMOVE(history_list, &hl, h); g_free((gpointer) h->title); g_free((gpointer) h->uri); g_free(h); break; } i++; } break; case XT_XTP_HL_LIST: /* Nothing - just xtp_page_hl() below */ break; default: show_oops(t, "%s: unknown command", __func__); break; }; xtp_page_hl(t, NULL); } /* remove a favorite */ void remove_favorite(struct tab *t, int index) { char file[PATH_MAX], *title, *uri = NULL; char *new_favs, *tmp; FILE *f; int i; size_t len, lineno; /* open favorites */ snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE); if ((f = fopen(file, "r")) == NULL) { show_oops(t, "%s: can't open favorites: %s", __func__, strerror(errno)); return; } /* build a string which will become the new favroites file */ new_favs = g_strdup(""); for (i = 1;;) { if ((title = fparseln(f, &len, &lineno, NULL, 0)) == NULL) if (feof(f) || ferror(f)) break; /* XXX THIS IS NOT THE RIGHT HEURISTIC */ if (len == 0) { free(title); title = NULL; continue; } if ((uri = fparseln(f, &len, &lineno, NULL, 0)) == NULL) { if (feof(f) || ferror(f)) { show_oops(t, "%s: can't parse favorites %s", __func__, strerror(errno)); goto clean; } } /* as long as this isn't the one we are deleting add to file */ if (i != index) { tmp = new_favs; new_favs = g_strdup_printf("%s%s\n%s\n", new_favs, title, uri); g_free(tmp); } free(uri); uri = NULL; free(title); title = NULL; i++; } fclose(f); /* write back new favorites file */ if ((f = fopen(file, "w")) == NULL) { show_oops(t, "%s: can't open favorites: %s", __func__, strerror(errno)); goto clean; } if (fwrite(new_favs, strlen(new_favs), 1, f) != 1) show_oops(t, "%s: can't fwrite"); /* shut gcc up */ fclose(f); clean: if (uri) free(uri); if (title) free(title); g_free(new_favs); } int add_favorite(struct tab *t, struct karg *args) { char file[PATH_MAX]; FILE *f; char *line = NULL; size_t urilen, linelen; const gchar *uri, *title; if (t == NULL) return (1); /* don't allow adding of xtp pages to favorites */ if (t->xtp_meaning != XT_XTP_TAB_MEANING_NORMAL) { show_oops(t, "%s: can't add xtp pages to favorites", __func__); return (1); } snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE); if ((f = fopen(file, "r+")) == NULL) { show_oops(t, "Can't open favorites file: %s", strerror(errno)); return (1); } title = get_title(t, FALSE); uri = get_uri(t); if (title == NULL || uri == NULL) { show_oops(t, "can't add page to favorites"); goto done; } urilen = strlen(uri); for (;;) { if ((line = fparseln(f, &linelen, NULL, NULL, 0)) == NULL) if (feof(f) || ferror(f)) break; if (linelen == urilen && !strcmp(line, uri)) goto done; free(line); line = NULL; } fprintf(f, "\n%s\n%s", title, uri); done: if (line) free(line); fclose(f); update_favorite_tabs(NULL); return (0); } void xtp_handle_fl(struct tab *t, uint8_t cmd, int arg) { switch (cmd) { case XT_XTP_FL_LIST: /* nothing, just the below call to xtp_page_fl() */ break; case XT_XTP_FL_REMOVE: remove_favorite(t, arg); break; default: show_oops(t, "%s: invalid favorites command", __func__); break; }; xtp_page_fl(t, NULL); } void xtp_handle_cl(struct tab *t, uint8_t cmd, int arg) { switch (cmd) { case XT_XTP_CL_LIST: /* nothing, just xtp_page_cl() */ break; case XT_XTP_CL_REMOVE: remove_cookie(arg); break; case XT_XTP_CL_REMOVE_DOMAIN: remove_cookie_domain(arg); break; default: show_oops(t, "%s: unknown cookie xtp command", __func__); break; }; xtp_page_cl(t, NULL); } /* link an XTP class to it's session key and handler function */ struct xtp_despatch { uint8_t xtp_class; char **session_key; void (*handle_func)(struct tab *, uint8_t, int); }; struct xtp_despatch xtp_despatches[] = { { XT_XTP_DL, &dl_session_key, xtp_handle_dl }, { XT_XTP_HL, &hl_session_key, xtp_handle_hl }, { XT_XTP_FL, &fl_session_key, xtp_handle_fl }, { XT_XTP_CL, &cl_session_key, xtp_handle_cl }, { XT_XTP_INVALID, NULL, NULL } }; /* * generate a session key to secure xtp commands. * pass in a ptr to the key in question and it will * be modified in place. */ void generate_xtp_session_key(char **key) { uint8_t rand_bytes[XT_XTP_SES_KEY_SZ]; /* free old key */ if (*key) g_free(*key); /* make a new one */ arc4random_buf(rand_bytes, XT_XTP_SES_KEY_SZ); *key = g_strdup_printf(XT_XTP_SES_KEY_HEX_FMT, rand_bytes[0], rand_bytes[1], rand_bytes[2], rand_bytes[3], rand_bytes[4], rand_bytes[5], rand_bytes[6], rand_bytes[7]); DNPRINTF(XT_D_DOWNLOAD, "%s: new session key '%s'\n", __func__, *key); } void xtp_generate_keys(void) { /* generate session keys for xtp pages */ generate_xtp_session_key(&dl_session_key); generate_xtp_session_key(&hl_session_key); generate_xtp_session_key(&cl_session_key); generate_xtp_session_key(&fl_session_key); } /* * validate a xtp session key. * return (1) if OK */ int validate_xtp_session_key(struct tab *t, char *trusted, char *untrusted) { if (strcmp(trusted, untrusted) != 0) { show_oops(t, "%s: xtp session key mismatch possible spoof", __func__); return (0); } return (1); } /* * is the url xtp protocol? (xxxt://) * if so, parse and despatch correct bahvior */ int parse_xtp_url(struct tab *t, const char *url) { char *dup = NULL, *p, *last = NULL; uint8_t n_tokens = 0; char *tokens[4] = {NULL, NULL, NULL, ""}; struct xtp_despatch *dsp, *dsp_match = NULL; uint8_t req_class; int ret = FALSE; /* * tokens array meaning: * tokens[0] = class * tokens[1] = session key * tokens[2] = action * tokens[3] = optional argument */ DNPRINTF(XT_D_URL, "%s: url %s\n", __func__, url); if (strncmp(url, XT_XTP_STR, strlen(XT_XTP_STR))) goto clean; dup = g_strdup(url + strlen(XT_XTP_STR)); /* split out the url */ for ((p = strtok_r(dup, "/", &last)); p; (p = strtok_r(NULL, "/", &last))) { if (n_tokens < 4) tokens[n_tokens++] = p; } /* should be atleast three fields 'class/seskey/command/arg' */ if (n_tokens < 3) goto clean; dsp = xtp_despatches; req_class = atoi(tokens[0]); while (dsp->xtp_class) { if (dsp->xtp_class == req_class) { dsp_match = dsp; break; } dsp++; } /* did we find one atall? */ if (dsp_match == NULL) { show_oops(t, "%s: no matching xtp despatch found", __func__); goto clean; } /* check session key and call despatch function */ if (validate_xtp_session_key(t, *(dsp_match->session_key), tokens[1])) { ret = TRUE; /* all is well, this was a valid xtp request */ dsp_match->handle_func(t, atoi(tokens[2]), atoi(tokens[3])); } clean: if (dup) g_free(dup); return (ret); } /* * update all favorite tabs apart from one. Pass NULL if * you want to update all. */ void update_favorite_tabs(struct tab *apart_from) { struct tab *t; if (!updating_fl_tabs) { updating_fl_tabs = 1; /* stop infinite recursion */ TAILQ_FOREACH(t, &tabs, entry) if ((t->xtp_meaning == XT_XTP_TAB_MEANING_FL) && (t != apart_from)) xtp_page_fl(t, NULL); updating_fl_tabs = 0; } } /* * update all download tabs apart from one. Pass NULL if * you want to update all. */ void update_download_tabs(struct tab *apart_from) { struct tab *t; if (!updating_dl_tabs) { updating_dl_tabs = 1; /* stop infinite recursion */ TAILQ_FOREACH(t, &tabs, entry) if ((t->xtp_meaning == XT_XTP_TAB_MEANING_DL) && (t != apart_from)) xtp_page_dl(t, NULL); updating_dl_tabs = 0; } } /* * update all cookie tabs apart from one. Pass NULL if * you want to update all. */ void update_cookie_tabs(struct tab *apart_from) { struct tab *t; if (!updating_cl_tabs) { updating_cl_tabs = 1; /* stop infinite recursion */ TAILQ_FOREACH(t, &tabs, entry) if ((t->xtp_meaning == XT_XTP_TAB_MEANING_CL) && (t != apart_from)) xtp_page_cl(t, NULL); updating_cl_tabs = 0; } } /* * update all history tabs apart from one. Pass NULL if * you want to update all. */ void update_history_tabs(struct tab *apart_from) { struct tab *t; if (!updating_hl_tabs) { updating_hl_tabs = 1; /* stop infinite recursion */ TAILQ_FOREACH(t, &tabs, entry) if ((t->xtp_meaning == XT_XTP_TAB_MEANING_HL) && (t != apart_from)) xtp_page_hl(t, NULL); updating_hl_tabs = 0; } } /* show a list of favorites (bookmarks) */ int xtp_page_fl(struct tab *t, struct karg *args) { char file[PATH_MAX]; FILE *f; char *uri = NULL, *title = NULL; size_t len, lineno = 0; int i, failed = 0; char *body, *tmp, *page = NULL; const char delim[3] = {'\\', '\\', '\0'}; DNPRINTF(XT_D_FAVORITE, "%s:", __func__); if (t == NULL) warn("%s: bad param", __func__); /* new session key */ if (!updating_fl_tabs) generate_xtp_session_key(&fl_session_key); /* open favorites */ snprintf(file, sizeof file, "%s/%s", work_dir, XT_FAVS_FILE); if ((f = fopen(file, "r")) == NULL) { show_oops(t, "Can't open favorites file: %s", strerror(errno)); return (1); } /* body */ body = g_strdup_printf("
# | Link | " "Rm |
---|---|---|
%d | " "%s | " "" "X | " "
" "No favorites - To add one use the 'favadd' command." " |
Type | " "Name | " "Value | " "Path | " "Expires | " "Secure | " "HTTP only | "
"Rm |
---|
URI | Title | Last visited | Rm |
---|---|---|---|
%s | " "%s | " "%s | " "" "X |
No History |
\n\n[ Refresh Downloads ]\n" "
" "File | \nProgress | Command |
---|---|---|
" "No downloads |
");
TAILQ_FOREACH(s, &spl, entry) {
b = body;
body = g_strdup_printf("%s%s
", body, s->line);
g_free(b);
}
page = get_html_page("Startup Exception", body, "", 0);
g_free(body);
load_webkit_string(t, page, XT_URI_ABOUT_STARTPAGE);
g_free(page);
return (0);
}
void
startpage_add(const char *fmt, ...)
{
va_list ap;
char *msg;
struct sp *s;
if (fmt == NULL)
return;
va_start(ap, fmt);
if (vasprintf(&msg, fmt, ap) == -1)
errx(1, "startpage_add failed");
va_end(ap);
s = g_malloc0(sizeof *s);
s->line = msg;
TAILQ_INSERT_TAIL(&spl, s, entry);
}