midori/katze/katze-http-cookies.c
Christian Dywan 392fa7fc85 Revise cookie saving based on a timeout
The variable MIDORI_COOKIES_DEBUG can be used to print
the number of updated cookies when they're written.

The temporary cookie file is written in the same location
now as moving across filesystems may not work.

Saving when Midori is quit is enforced.
2010-10-06 18:32:03 +02:00

336 lines
9.5 KiB
C

/*
Copyright (C) 2008-2010 Christian Dywan <christian@twotoasts.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See the file COPYING for the full license text.
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include "katze-http-cookies.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <glib/gi18n.h>
#include <libsoup/soup.h>
#include <gtk/gtk.h>
#include <glib/gstdio.h>
struct _KatzeHttpCookies
{
GObject parent_instance;
gchar* filename;
SoupCookieJar* jar;
guint timeout;
guint counter;
};
struct _KatzeHttpCookiesClass
{
GObjectClass parent_class;
};
static void
katze_http_cookies_session_feature_iface_init (SoupSessionFeatureInterface *iface,
gpointer data);
G_DEFINE_TYPE_WITH_CODE (KatzeHttpCookies, katze_http_cookies, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
katze_http_cookies_session_feature_iface_init));
/* Cookie jar saving to Mozilla format
Copyright (C) 2008 Xan Lopez <xan@gnome.org>
Copyright (C) 2008 Dan Winship <danw@gnome.org>
Mostly copied from libSoup 2.24, coding style adjusted */
static SoupCookie*
parse_cookie (gchar* line,
time_t now)
{
gchar** result;
SoupCookie *cookie = NULL;
gboolean http_only;
time_t max_age;
gchar* host/*, *is_domain*/, *path, *secure, *expires, *name, *value;
if (g_str_has_prefix (line, "#HttpOnly_"))
{
http_only = TRUE;
line += strlen ("#HttpOnly_");
}
else if (*line == '#' || g_ascii_isspace (*line))
return cookie;
else
http_only = FALSE;
result = g_strsplit (line, "\t", -1);
if (g_strv_length (result) != 7)
goto out;
/* Check this first */
expires = result[4];
max_age = strtoul (expires, NULL, 10) - now;
if (max_age <= 0)
goto out;
host = result[0];
/* is_domain = result[1]; */
path = result[2];
secure = result[3];
name = result[5];
value = result[6];
cookie = soup_cookie_new (name, value, host, path, max_age);
if (strcmp (secure, "FALSE"))
soup_cookie_set_secure (cookie, TRUE);
if (http_only)
soup_cookie_set_http_only (cookie, TRUE);
out:
g_strfreev (result);
return cookie;
}
/* Cookie jar saving to Mozilla format
Copyright (C) 2008 Xan Lopez <xan@gnome.org>
Copyright (C) 2008 Dan Winship <danw@gnome.org>
Mostly copied from libSoup 2.24, coding style adjusted */
static void
parse_line (SoupCookieJar* jar,
gchar* line,
time_t now)
{
SoupCookie* cookie;
if ((cookie = parse_cookie (line, now)))
soup_cookie_jar_add_cookie (jar, cookie);
}
/* Cookie jar saving to Mozilla format
Copyright (C) 2008 Xan Lopez <xan@gnome.org>
Copyright (C) 2008 Dan Winship <danw@gnome.org>
Mostly copied from libSoup 2.24, coding style adjusted */
static void
cookie_jar_load (SoupCookieJar* jar,
const gchar* filename)
{
char* contents = NULL;
gchar* line;
gchar* p;
gsize length = 0;
time_t now;
if (!g_file_get_contents (filename, &contents, &length, NULL))
return;
now = time (NULL);
line = contents;
for (p = contents; *p; p++)
{
/* \r\n comes out as an extra empty line and gets ignored */
if (*p == '\r' || *p == '\n')
{
*p = '\0';
parse_line (jar, line, now);
line = p + 1;
}
}
parse_line (jar, line, now);
g_free (contents);
}
/* Cookie jar saving to Mozilla format
Copyright (C) 2008 Xan Lopez <xan@gnome.org>
Copyright (C) 2008 Dan Winship <danw@gnome.org>
Copied from libSoup 2.24, coding style preserved */
static gboolean
write_cookie (FILE *out, SoupCookie *cookie)
{
if (fprintf (out, "%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",
(gulong)soup_date_to_time_t (cookie->expires),
cookie->name,
cookie->value) < 0)
return FALSE;
return TRUE;
}
static gboolean
katze_http_cookies_update_jar (KatzeHttpCookies* http_cookies)
{
gint fn = 0;
FILE* f = NULL;
gchar* temporary_filename = NULL;
GSList* cookies;
http_cookies->timeout = 0;
temporary_filename = g_strconcat (http_cookies->filename, ".XXXXXX", NULL);
if ((fn = g_mkstemp (temporary_filename)) == -1)
goto failed;
if (!((f = fdopen (fn, "wb"))))
goto failed;
cookies = soup_cookie_jar_all_cookies (http_cookies->jar);
for (; cookies != NULL; cookies = g_slist_next (cookies))
{
SoupCookie* cookie = cookies->data;
if (cookie->expires && !soup_date_is_past (cookie->expires))
write_cookie (f, cookie);
soup_cookie_free (cookie);
}
g_slist_free (cookies);
if (fclose (f) != 0)
{
f = NULL;
goto failed;
}
f = NULL;
if (g_rename (temporary_filename, http_cookies->filename) == -1)
goto failed;
g_free (temporary_filename);
if (g_getenv ("MIDORI_COOKIES_DEBUG") != NULL)
{
g_print ("KatzeHttpCookies: %d cookies changed\n", http_cookies->counter);
http_cookies->counter = 0;
}
return FALSE;
failed:
if (f)
fclose (f);
g_unlink (temporary_filename);
g_free (temporary_filename);
if (g_getenv ("MIDORI_COOKIES_DEBUG") != NULL)
g_print ("KatzeHttpCookies: Failed to write '%s'\n",
http_cookies->filename);
return FALSE;
}
static void
katze_http_cookies_jar_changed_cb (SoupCookieJar* jar,
SoupCookie* old_cookie,
SoupCookie* new_cookie,
KatzeHttpCookies* http_cookies)
{
GObject* settings;
guint accept_cookies;
if (old_cookie)
soup_cookie_set_max_age (old_cookie, 0);
if (new_cookie)
{
settings = g_object_get_data (G_OBJECT (jar), "midori-settings");
accept_cookies = katze_object_get_enum (settings, "accept-cookies");
if (accept_cookies == 2 /* MIDORI_ACCEPT_COOKIES_NONE */)
{
soup_cookie_set_max_age (new_cookie, 0);
}
else if (accept_cookies == 1 /* MIDORI_ACCEPT_COOKIES_SESSION */
&& new_cookie->expires)
{
soup_cookie_set_max_age (new_cookie, -1);
}
else if (new_cookie->expires)
{
gint age = katze_object_get_int (settings, "maximum-cookie-age");
if (age > 0)
{
SoupDate* max_date = soup_date_new_from_now (
age * SOUP_COOKIE_MAX_AGE_ONE_DAY);
if (soup_date_to_time_t (new_cookie->expires)
> soup_date_to_time_t (max_date))
soup_cookie_set_expires (new_cookie, max_date);
}
else
{
/* An age of 0 to SoupCookie means already-expired
A user choosing 0 days probably expects 1 hour. */
soup_cookie_set_max_age (new_cookie, SOUP_COOKIE_MAX_AGE_ONE_HOUR);
}
}
}
if (g_getenv ("MIDORI_COOKIES_DEBUG") != NULL)
http_cookies->counter++;
if (!http_cookies->timeout && (old_cookie || new_cookie->expires))
http_cookies->timeout = g_timeout_add_seconds (5,
(GSourceFunc)katze_http_cookies_update_jar, http_cookies);
}
static void
katze_http_cookies_attach (SoupSessionFeature* feature,
SoupSession* session)
{
KatzeHttpCookies* http_cookies = (KatzeHttpCookies*)feature;
SoupSessionFeature* jar = soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR);
g_return_if_fail (jar != NULL);
http_cookies->filename = g_object_get_data (G_OBJECT (feature), "filename");
g_return_if_fail (http_cookies->filename != NULL);
http_cookies->jar = g_object_ref (jar);
cookie_jar_load (http_cookies->jar, http_cookies->filename);
g_signal_connect (jar, "changed",
G_CALLBACK (katze_http_cookies_jar_changed_cb), feature);
}
static void
katze_http_cookies_detach (SoupSessionFeature* feature,
SoupSession* session)
{
KatzeHttpCookies* http_cookies = (KatzeHttpCookies*)feature;
if (http_cookies->timeout)
katze_http_cookies_update_jar (http_cookies);
katze_assign (http_cookies->filename, NULL);
katze_object_assign (http_cookies->jar, NULL);
}
static void
katze_http_cookies_session_feature_iface_init (SoupSessionFeatureInterface *iface,
gpointer data)
{
iface->attach = katze_http_cookies_attach;
iface->detach = katze_http_cookies_detach;
}
static void
katze_http_cookies_finalize (GObject* object)
{
katze_http_cookies_detach ((SoupSessionFeature*)object, NULL);
}
static void
katze_http_cookies_class_init (KatzeHttpCookiesClass* class)
{
GObjectClass* gobject_class = (GObjectClass*)class;
gobject_class->finalize = katze_http_cookies_finalize;
}
static void
katze_http_cookies_init (KatzeHttpCookies* http_cookies)
{
http_cookies->filename = NULL;
http_cookies->jar = NULL;
http_cookies->timeout = 0;
http_cookies->counter = 0;
}