/* * Copyright (c) 2010, 2011 Marco Peereboom * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #define XT_REJECT_FILE ("rejected.txt") #define XT_COOKIE_FILE ("cookies.txt") /* hooked functions */ void (*_soup_cookie_jar_add_cookie)(SoupCookieJar *, SoupCookie *); void (*_soup_cookie_jar_delete_cookie)(SoupCookieJar *, SoupCookie *); void print_cookie(char *msg, SoupCookie *c) { if (c == NULL) return; if (msg) { DNPRINTF(XT_D_COOKIE, "%s\n", msg); } DNPRINTF(XT_D_COOKIE, "name : %s\n", c->name); DNPRINTF(XT_D_COOKIE, "value : %s\n", c->value); DNPRINTF(XT_D_COOKIE, "domain : %s\n", c->domain); DNPRINTF(XT_D_COOKIE, "path : %s\n", c->path); DNPRINTF(XT_D_COOKIE, "expires : %s\n", c->expires ? soup_date_to_string(c->expires, SOUP_DATE_HTTP) : ""); DNPRINTF(XT_D_COOKIE, "secure : %d\n", c->secure); DNPRINTF(XT_D_COOKIE, "http_only: %d\n", c->http_only); DNPRINTF(XT_D_COOKIE, "====================================\n"); } void set_hook(void **hook, char *name) { if (hook == NULL) errx(1, "set_hook"); if (*hook == NULL) { *hook = dlsym(RTLD_NEXT, name); if (*hook == NULL) errx(1, "can't hook %s", name); } } /* override libsoup soup_cookie_equal because it doesn't look at domain */ gboolean soup_cookie_equal(SoupCookie *cookie1, SoupCookie *cookie2) { g_return_val_if_fail(cookie1, FALSE); g_return_val_if_fail(cookie2, FALSE); return (!strcmp (cookie1->name, cookie2->name) && !strcmp (cookie1->value, cookie2->value) && !strcmp (cookie1->path, cookie2->path) && !strcmp (cookie1->domain, cookie2->domain)); } void transfer_cookies(void) { GSList *cf; SoupCookie *sc, *pc; cf = soup_cookie_jar_all_cookies(p_cookiejar); for (;cf; cf = cf->next) { pc = cf->data; sc = soup_cookie_copy(pc); _soup_cookie_jar_add_cookie(s_cookiejar, sc); } soup_cookies_free(cf); } void soup_cookie_jar_delete_cookie(SoupCookieJar *jar, SoupCookie *c) { GSList *cf; SoupCookie *ci; print_cookie("soup_cookie_jar_delete_cookie", c); if (cookies_enabled == 0) return; if (jar == NULL || c == NULL) return; /* find and remove from persistent jar */ cf = soup_cookie_jar_all_cookies(p_cookiejar); for (;cf; cf = cf->next) { ci = cf->data; if (soup_cookie_equal(ci, c)) { _soup_cookie_jar_delete_cookie(p_cookiejar, ci); break; } } soup_cookies_free(cf); /* delete from session jar */ _soup_cookie_jar_delete_cookie(s_cookiejar, c); } void soup_cookie_jar_add_cookie(SoupCookieJar *jar, SoupCookie *cookie) { struct wl_entry *w = NULL; SoupCookie *c; FILE *r_cookie_f; char *public_suffix; DNPRINTF(XT_D_COOKIE, "soup_cookie_jar_add_cookie: %p %p %p\n", jar, p_cookiejar, s_cookiejar); if (cookies_enabled == 0) return; /* see if we are up and running */ if (p_cookiejar == NULL) { _soup_cookie_jar_add_cookie(jar, cookie); return; } /* disallow p_cookiejar adds, shouldn't happen */ if (jar == p_cookiejar) return; /* sanity */ if (jar == NULL || cookie == NULL) return; /* check if domain is valid */ public_suffix = tld_get_suffix(cookie->domain[0] == '.' ? cookie->domain + 1 : cookie->domain); if (public_suffix == NULL || (enable_cookie_whitelist && (w = wl_find(cookie->domain, &c_wl)) == NULL)) { blocked_cookies++; DNPRINTF(XT_D_COOKIE, "soup_cookie_jar_add_cookie: reject %s\n", cookie->domain); if (save_rejected_cookies) { if ((r_cookie_f = fopen(rc_fname, "a+")) == NULL) { show_oops(NULL, "can't open reject cookie file"); return; } fseek(r_cookie_f, 0, SEEK_END); fprintf(r_cookie_f, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n", cookie->http_only ? "#HttpOnly_" : "", cookie->domain, *cookie->domain == '.' ? "TRUE" : "FALSE", cookie->path, cookie->secure ? "TRUE" : "FALSE", cookie->expires ? (gulong)soup_date_to_time_t(cookie->expires) : 0, cookie->name, cookie->value); fflush(r_cookie_f); fclose(r_cookie_f); } if (!allow_volatile_cookies) return; } if (cookie->expires == NULL && session_timeout) { soup_cookie_set_expires(cookie, soup_date_new_from_now(session_timeout)); print_cookie("modified add cookie", cookie); } /* see if we are white listed for persistence */ if ((w && w->handy) || (enable_cookie_whitelist == 0)) { /* add to persistent jar */ c = soup_cookie_copy(cookie); print_cookie("soup_cookie_jar_add_cookie p_cookiejar", c); _soup_cookie_jar_add_cookie(p_cookiejar, c); } /* add to session jar */ print_cookie("soup_cookie_jar_add_cookie s_cookiejar", cookie); _soup_cookie_jar_add_cookie(s_cookiejar, cookie); } void setup_cookies(void) { char file[PATH_MAX]; set_hook((void *)&_soup_cookie_jar_add_cookie, "soup_cookie_jar_add_cookie"); set_hook((void *)&_soup_cookie_jar_delete_cookie, "soup_cookie_jar_delete_cookie"); #if defined __MINGW32__ /* windows hooking is a horror show, nothing to see here move along */ fixup_windows_hooks(); #endif if (cookies_enabled == 0) return; /* * the following code is intricate due to overriding several libsoup * functions. * do not alter order of these operations. */ /* rejected cookies */ if (save_rejected_cookies) snprintf(rc_fname, sizeof file, "%s" PS "%s", work_dir, XT_REJECT_FILE); /* persistent cookies */ snprintf(file, sizeof file, "%s" PS "%s", work_dir, XT_COOKIE_FILE); p_cookiejar = soup_cookie_jar_text_new(file, read_only_cookies); /* session cookies */ s_cookiejar = soup_cookie_jar_new(); g_object_set(G_OBJECT(s_cookiejar), SOUP_COOKIE_JAR_ACCEPT_POLICY, cookie_policy, (void *)NULL); transfer_cookies(); soup_session_add_feature(session, (SoupSessionFeature*)s_cookiejar); }