Factor persistent cookies into a soup feature

This commit is contained in:
Christian Dywan 2009-02-14 04:14:01 +01:00
parent e87f5f2455
commit 79ed3a06c3
4 changed files with 381 additions and 261 deletions

328
katze/katze-http-cookies.c Normal file
View file

@ -0,0 +1,328 @@
/*
Copyright (C) 2008-2009 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"
#if HAVE_LIBSOUP
#include <libsoup/soup.h>
#endif
#include <stdlib.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
struct _KatzeHttpCookies
{
GObject parent_instance;
};
struct _KatzeHttpCookiesClass
{
GObjectClass parent_class;
};
#if HAVE_LIBSOUP_2_25_2
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 = time (NULL);
if (!g_file_get_contents (filename, &contents, &length, NULL))
return;
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 void
write_cookie (FILE *out, SoupCookie *cookie)
{
fseek (out, 0, SEEK_END);
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);
}
/* 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 void
delete_cookie (const char *filename, SoupCookie *cookie)
{
char *contents = NULL, *line, *p;
gsize length = 0;
FILE *f;
SoupCookie *c;
time_t now = time (NULL);
if (!g_file_get_contents (filename, &contents, &length, NULL))
return;
f = fopen (filename, "w");
if (!f) {
g_free (contents);
return;
}
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';
c = parse_cookie (line, now);
if (!c)
continue;
if (!soup_cookie_equal (cookie, c))
write_cookie (f, c);
line = p + 1;
soup_cookie_free (c);
}
}
c = parse_cookie (line, now);
if (c) {
if (!soup_cookie_equal (cookie, c))
write_cookie (f, c);
soup_cookie_free (c);
}
g_free (contents);
fclose (f);
}
/* 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_changed_cb (SoupCookieJar* jar,
SoupCookie* old_cookie,
SoupCookie* new_cookie,
gchar* filename)
{
GObject* app;
GObject* settings;
guint accept_cookies;
if (old_cookie)
delete_cookie (filename, old_cookie);
if (new_cookie)
{
FILE *out;
/* FIXME: This is really a hack */
app = g_type_get_qdata (SOUP_TYPE_SESSION,
g_quark_from_static_string ("midori-app"));
settings = katze_object_get_object (G_OBJECT (app), "settings");
accept_cookies = katze_object_get_enum (settings, "accept-cookies");
if (accept_cookies == 2 /* MIDORI_ACCEPT_COOKIES_NONE */)
{
soup_cookie_jar_delete_cookie (jar, new_cookie);
}
else if (accept_cookies == 1 /* MIDORI_ACCEPT_COOKIES_SESSION */
&& new_cookie->expires)
{
soup_cookie_jar_delete_cookie (jar, new_cookie);
}
else if (new_cookie->expires)
{
gint age = katze_object_get_int (settings, "maximum-cookie-age");
soup_cookie_set_max_age (new_cookie,
age * SOUP_COOKIE_MAX_AGE_ONE_DAY);
if (!(out = fopen (filename, "a")))
return;
write_cookie (out, new_cookie);
if (fclose (out) != 0)
return;
}
}
}
static void
katze_http_cookies_session_request_queued_cb (SoupSession* session,
SoupMessage* msg,
SoupSessionFeature* feature)
{
const gchar* filename;
SoupSessionFeature* cookie_jar;
filename = g_object_get_data (G_OBJECT (feature), "filename");
if (!filename)
return;
cookie_jar = soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR);
if (!cookie_jar)
return;
g_type_set_qdata (SOUP_TYPE_COOKIE_JAR,
g_quark_from_static_string ("midori-has-jar"), (void*)1);
cookie_jar_load (SOUP_COOKIE_JAR (cookie_jar), filename);
g_signal_connect_data (cookie_jar, "changed",
G_CALLBACK (cookie_jar_changed_cb), g_strdup (filename),
(GClosureNotify)g_free, 0);
g_signal_handlers_disconnect_by_func (session,
katze_http_cookies_session_request_queued_cb, feature);
}
static void
katze_http_cookies_attach (SoupSessionFeature* feature,
SoupSession* session)
{
g_signal_connect (session, "request-queued",
G_CALLBACK (katze_http_cookies_session_request_queued_cb), feature);
}
static void
katze_http_cookies_detach (SoupSessionFeature* feature,
SoupSession* session)
{
g_signal_handlers_disconnect_by_func (session,
katze_http_cookies_session_request_queued_cb, feature);
}
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;
}
#else
G_DEFINE_TYPE (KatzeHttpCookies, katze_http_cookies, G_TYPE_OBJECT)
#endif
static void
katze_http_cookies_class_init (KatzeHttpCookiesClass* class)
{
/* Nothing to do. */
}
static void
katze_http_cookies_init (KatzeHttpCookies* http_cookies)
{
/* Nothing to do. */
}

View file

@ -0,0 +1,42 @@
/*
Copyright (C) 2009 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.
*/
#ifndef __KATZE_HTTP_COOKIES_H__
#define __KATZE_HTTP_COOKIES_H__
#include "katze-utils.h"
#include <glib-object.h>
G_BEGIN_DECLS
#define KATZE_TYPE_HTTP_COOKIES \
(katze_http_cookies_get_type ())
#define KATZE_HTTP_COOKIES(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), KATZE_TYPE_HTTP_COOKIES, KatzeHttpCookies))
#define KATZE_HTTP_COOKIES_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), KATZE_TYPE_HTTP_COOKIES, KatzeHttpCookiesClass))
#define KATZE_IS_HTTP_COOKIES(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), KATZE_TYPE_HTTP_COOKIES))
#define KATZE_IS_HTTP_COOKIES_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), KATZE_TYPE_HTTP_COOKIES))
#define KATZE_HTTP_COOKIES_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), KATZE_TYPE_HTTP_COOKIES, KatzeHttpCookiesClass))
typedef struct _KatzeHttpCookies KatzeHttpCookies;
typedef struct _KatzeHttpCookiesClass KatzeHttpCookiesClass;
GType
katze_http_cookies_get_type (void);
G_END_DECLS
#endif /* __KATZE_HTTP_COOKIES_H__ */

View file

@ -13,6 +13,7 @@
#define __KATZE_H__ #define __KATZE_H__
#include "katze-http-auth.h" #include "katze-http-auth.h"
#include "katze-http-cookies.h"
#include "katze-throbber.h" #include "katze-throbber.h"
#include "katze-utils.h" #include "katze-utils.h"
#include "katze-item.h" #include "katze-item.h"

View file

@ -1329,257 +1329,8 @@ midori_browser_weak_notify_cb (MidoriBrowser* browser,
G_CALLBACK (midori_browser_session_cb), session, NULL); G_CALLBACK (midori_browser_session_cb), session, NULL);
} }
typedef void (*GObjectConstructed) (GObject*);
#if HAVE_LIBSOUP_2_25_2
/* 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 = time (NULL);
if (!g_file_get_contents (filename, &contents, &length, NULL))
return;
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 void
write_cookie (FILE *out, SoupCookie *cookie)
{
fseek (out, 0, SEEK_END);
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);
}
/* 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 void
delete_cookie (const char *filename, SoupCookie *cookie)
{
char *contents = NULL, *line, *p;
gsize length = 0;
FILE *f;
SoupCookie *c;
time_t now = time (NULL);
if (!g_file_get_contents (filename, &contents, &length, NULL))
return;
f = fopen (filename, "w");
if (!f) {
g_free (contents);
return;
}
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';
c = parse_cookie (line, now);
if (!c)
continue;
if (!soup_cookie_equal (cookie, c))
write_cookie (f, c);
line = p + 1;
soup_cookie_free (c);
}
}
c = parse_cookie (line, now);
if (c) {
if (!soup_cookie_equal (cookie, c))
write_cookie (f, c);
soup_cookie_free (c);
}
g_free (contents);
fclose (f);
}
/* 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_changed_cb (SoupCookieJar* jar,
SoupCookie* old_cookie,
SoupCookie* new_cookie,
gchar* filename)
{
MidoriApp* app;
MidoriWebSettings* settings;
MidoriAcceptCookies accept_cookies;
if (old_cookie)
delete_cookie (filename, old_cookie);
if (new_cookie)
{
FILE *out;
app = g_type_get_qdata (SOUP_TYPE_COOKIE_JAR,
g_quark_from_static_string ("midori-app"));
settings = katze_object_get_object (G_OBJECT (app), "settings");
accept_cookies = katze_object_get_enum (settings, "accept-cookies");
if (accept_cookies == MIDORI_ACCEPT_COOKIES_NONE)
{
soup_cookie_jar_delete_cookie (jar, new_cookie);
}
else if (accept_cookies == MIDORI_ACCEPT_COOKIES_SESSION
&& new_cookie->expires)
{
soup_cookie_jar_delete_cookie (jar, new_cookie);
}
else if (new_cookie->expires)
{
gint age = katze_object_get_int (settings, "maximum-cookie-age");
soup_cookie_set_max_age (new_cookie,
age * SOUP_COOKIE_MAX_AGE_ONE_DAY);
if (!(out = fopen (filename, "a")))
return;
write_cookie (out, new_cookie);
if (fclose (out) != 0)
return;
}
}
}
#endif
#if HAVE_LIBSOUP #if HAVE_LIBSOUP
/* The following code hooks up to any created cookie jar in order to typedef void (*GObjectConstructed) (GObject*);
load and save cookies. This is *not* a generally advisable technique
but merely a preliminary workaround until WebKit exposes its
network backend and we can pass our own jar. */
static GObjectConstructed old_jar_constructed_cb;
static void
cookie_jar_constructed_cb (GObject* object)
{
#if HAVE_LIBSOUP_2_25_2
gchar* config_file;
SoupCookieJar* jar;
#endif
if (old_jar_constructed_cb)
old_jar_constructed_cb (object);
g_type_set_qdata (SOUP_TYPE_COOKIE_JAR,
g_quark_from_static_string ("midori-has-jar"), (void*)1);
#if HAVE_LIBSOUP_2_25_2
config_file = build_config_filename ("cookies.txt");
jar = SOUP_COOKIE_JAR (object);
cookie_jar_load (jar, config_file);
g_signal_connect_data (jar, "changed",
G_CALLBACK (cookie_jar_changed_cb), config_file,
(GClosureNotify)g_free, 0);
#endif
}
static void static void
soup_session_settings_notify_http_proxy_cb (MidoriWebSettings* settings, soup_session_settings_notify_http_proxy_cb (MidoriWebSettings* settings,
@ -1623,6 +1374,8 @@ soup_session_constructed_cb (GObject* object)
MidoriApp* app; MidoriApp* app;
MidoriWebSettings* settings; MidoriWebSettings* settings;
SoupSession* session; SoupSession* session;
SoupSessionFeature* feature;
gchar* config_file;
if (old_session_constructed_cb) if (old_session_constructed_cb)
old_session_constructed_cb (object); old_session_constructed_cb (object);
@ -1641,6 +1394,12 @@ soup_session_constructed_cb (GObject* object)
G_CALLBACK (soup_session_settings_notify_ident_string_cb), object); G_CALLBACK (soup_session_settings_notify_ident_string_cb), object);
soup_session_add_feature_by_type (session, KATZE_TYPE_HTTP_AUTH); soup_session_add_feature_by_type (session, KATZE_TYPE_HTTP_AUTH);
feature = g_object_new (KATZE_TYPE_HTTP_COOKIES, NULL);
config_file = build_config_filename ("cookies.txt");
g_object_set_data_full (G_OBJECT (feature), "filename",
config_file, (GDestroyNotify)g_free);
soup_session_add_feature (session, feature);
} }
#endif #endif
@ -2039,17 +1798,6 @@ main (int argc,
webkit_class = g_type_class_ref (WEBKIT_TYPE_WEB_VIEW); webkit_class = g_type_class_ref (WEBKIT_TYPE_WEB_VIEW);
if (!g_object_class_find_property (webkit_class, "session")) if (!g_object_class_find_property (webkit_class, "session"))
{ {
/* This is a nasty trick that allows us to manipulate cookies
even without having a pointer to the jar. */
soup_cookie_jar_get_type ();
SoupCookieJarClass* jar_class = g_type_class_ref (SOUP_TYPE_COOKIE_JAR);
if (jar_class)
{
g_type_set_qdata (SOUP_TYPE_COOKIE_JAR,
g_quark_from_static_string ("midori-app"), app);
old_jar_constructed_cb = G_OBJECT_CLASS (jar_class)->constructed;
G_OBJECT_CLASS (jar_class)->constructed = cookie_jar_constructed_cb;
}
/* This is a nasty trick that allows us to manipulate preferences /* This is a nasty trick that allows us to manipulate preferences
even without having a pointer to the session. */ even without having a pointer to the session. */
soup_session_get_type (); soup_session_get_type ();
@ -2215,6 +1963,7 @@ main (int argc,
#if HAVE_LIBSOUP_2_25_2 #if HAVE_LIBSOUP_2_25_2
katze_assign (config_file, build_config_filename ("cookies.txt")); katze_assign (config_file, build_config_filename ("cookies.txt"));
jar = soup_cookie_jar_text_new (config_file, FALSE); jar = soup_cookie_jar_text_new (config_file, FALSE);
/* FIXME: Handle "accept-cookies" preference */
soup_session_add_feature (s_session, SOUP_SESSION_FEATURE (jar)); soup_session_add_feature (s_session, SOUP_SESSION_FEATURE (jar));
g_object_unref (jar); g_object_unref (jar);
#endif #endif