midori/midori/midori-session.c

388 lines
14 KiB
C

/*
Copyright (C) 2008-2013 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.
*/
#include "midori/midori-session.h"
#include <midori/midori-core.h>
#include "midori-array.h"
#include "midori-extension.h"
#include "sokoke.h"
#include <glib/gi18n-lib.h>
#include <libsoup/soup-cookie-jar-sqlite.h>
#include <libsoup/soup-gnome-features.h>
#define LIBSOUP_USE_UNSTABLE_REQUEST_API
#include <libsoup/soup-cache.h>
#ifndef HAVE_WEBKIT2
static void
midori_soup_session_set_proxy_uri (SoupSession* session,
const gchar* uri)
{
SoupURI* proxy_uri;
/* soup_uri_new expects a non-NULL string with a protocol */
gchar* scheme = uri ? g_uri_parse_scheme (uri): NULL;
if (scheme)
{
proxy_uri = soup_uri_new (uri);
g_free (scheme);
}
else if (uri && *uri)
{
gchar* fixed_uri = g_strconcat ("http://", uri, NULL);
proxy_uri = soup_uri_new (fixed_uri);
g_free (fixed_uri);
}
else
proxy_uri = NULL;
g_object_set (session, "proxy-uri", proxy_uri, NULL);
if (proxy_uri)
soup_uri_free (proxy_uri);
}
static void
soup_session_settings_notify_http_proxy_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
SoupSession* session)
{
gboolean uses_proxy = TRUE;
MidoriProxy proxy_type = katze_object_get_enum (settings, "proxy-type");
if (proxy_type == MIDORI_PROXY_AUTOMATIC)
{
soup_session_add_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_GNOME);
GProxyResolver* resolver = g_proxy_resolver_get_default ();
gchar** proxies = g_proxy_resolver_lookup (resolver, "none", NULL, NULL);
if (!proxies || !g_strcmp0 (proxies[0], "direct://"))
uses_proxy = FALSE;
g_strfreev (proxies);
}
else if (proxy_type == MIDORI_PROXY_HTTP)
{
soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_GNOME);
gchar* proxy = katze_object_get_string (settings, "http-proxy");
GString* http_proxy = g_string_new (proxy);
g_string_append_printf (http_proxy, ":%d", katze_object_get_int (settings, "http-proxy-port"));
midori_soup_session_set_proxy_uri (session, http_proxy->str);
g_string_free (http_proxy, TRUE);
g_free (proxy);
}
else
{
uses_proxy = FALSE;
soup_session_remove_feature_by_type (session, SOUP_TYPE_PROXY_RESOLVER_GNOME);
midori_soup_session_set_proxy_uri (session, NULL);
}
/* If a proxy server looks to be active, we disable prefetching, otherwise
libSoup may be prefetching outside the proxy server beyond our control.
*/
if (uses_proxy)
g_object_set (settings, "enable-dns-prefetching", FALSE, NULL);
}
#endif
#if WEBKIT_CHECK_VERSION (1, 1, 21)
static void
soup_session_settings_notify_first_party_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
gpointer user_data)
{
gboolean yes = katze_object_get_boolean (settings, "first-party-cookies-only");
#ifdef HAVE_WEBKIT2
WebKitWebContext* context = webkit_web_context_get_default ();
WebKitCookieManager* cookie_manager = webkit_web_context_get_cookie_manager (context);
webkit_cookie_manager_set_accept_policy (cookie_manager,
yes ? WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY
: WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS);
#else
SoupSession* session = webkit_get_default_session ();
gpointer jar = soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR);
g_object_set (jar, "accept-policy",
yes ? SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY
: SOUP_COOKIE_JAR_ACCEPT_ALWAYS, NULL);
#endif
}
#endif
#if !defined (HAVE_WEBKIT2)
/* Implemented in MidoriLocationAction */
void
midori_map_add_message (SoupMessage* message);
static void
midori_soup_session_request_started_cb (SoupSession* session,
SoupMessage* message,
SoupSocket* socket,
gpointer user_data)
{
midori_map_add_message (message);
}
#endif
#ifndef HAVE_WEBKIT2
const gchar*
midori_web_settings_get_accept_language (MidoriWebSettings* settings);
static void
midori_soup_session_settings_accept_language_cb (SoupSession* session,
SoupMessage* msg,
MidoriWebSettings* settings)
{
const gchar* accept = midori_web_settings_get_accept_language (settings);
soup_message_headers_append (msg->request_headers, "Accept-Language", accept);
if (katze_object_get_boolean (settings, "strip-referer"))
{
const gchar* referer
= soup_message_headers_get_one (msg->request_headers, "Referer");
SoupURI* destination = soup_message_get_uri (msg);
SoupURI* stripped_uri;
if (referer && destination && !strstr (referer, destination->host)
&& (stripped_uri = soup_uri_new (referer)))
{
gchar* stripped_referer;
soup_uri_set_path (stripped_uri, "");
soup_uri_set_query (stripped_uri, NULL);
stripped_referer = soup_uri_to_string (stripped_uri, FALSE);
soup_uri_free (stripped_uri);
if (strcmp (stripped_referer, referer))
{
if (midori_debug ("referer"))
g_message ("Referer '%s' stripped to '%s'", referer, stripped_referer);
soup_message_headers_replace (msg->request_headers, "Referer",
stripped_referer);
}
g_free (stripped_referer);
}
/* With HTTP, Host is optional. Strip to outsmart some filter proxies */
if (destination && destination->scheme == SOUP_URI_SCHEME_HTTP)
soup_message_headers_remove (msg->request_headers, "Host");
}
}
#endif
gboolean
midori_load_soup_session (gpointer settings)
{
#if WEBKIT_CHECK_VERSION (1, 1, 21)
g_signal_connect (settings, "notify::first-party-cookies-only",
G_CALLBACK (soup_session_settings_notify_first_party_cb), NULL);
#endif
#ifndef HAVE_WEBKIT2
SoupSession* session = webkit_get_default_session ();
#ifndef G_OS_WIN32
g_object_set (session,
"ssl-use-system-ca-file", TRUE,
"ssl-strict", FALSE,
NULL);
#else /* G_OS_WIN32 */
/* We cannot use "ssl-use-system-ca-file" on Windows
* some GTLS backend pieces are missing currently.
* Instead we specify the bundle we ship ourselves */
gchar* certificate_file = midori_paths_get_res_filename ("ca-bundle.crt");
g_object_set (session,
"ssl-ca-file", certificate_file,
"ssl-strict", FALSE,
NULL);
g_free (certificate_file);
#endif
g_object_set_data (G_OBJECT (session), "midori-settings", settings);
soup_session_settings_notify_http_proxy_cb (settings, NULL, session);
g_signal_connect (settings, "notify::http-proxy",
G_CALLBACK (soup_session_settings_notify_http_proxy_cb), session);
g_signal_connect (settings, "notify::proxy-type",
G_CALLBACK (soup_session_settings_notify_http_proxy_cb), session);
g_signal_connect (session, "request-started",
G_CALLBACK (midori_soup_session_request_started_cb), session);
g_signal_connect (session, "request-queued",
G_CALLBACK (midori_soup_session_settings_accept_language_cb), settings);
soup_session_add_feature (session, SOUP_SESSION_FEATURE (midori_hsts_new ()));
if (midori_debug ("headers"))
{
SoupLogger* logger = soup_logger_new (SOUP_LOGGER_LOG_HEADERS, -1);
soup_logger_attach (logger, session);
g_object_unref (logger);
}
else if (midori_debug ("body"))
{
SoupLogger* logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
soup_logger_attach (logger, session);
g_object_unref (logger);
}
g_object_set_data (G_OBJECT (session), "midori-session-initialized", (void*)1);
#endif
return FALSE;
}
#ifndef HAVE_WEBKIT2
static void
midori_session_cookie_jar_changed_cb (SoupCookieJar* jar,
SoupCookie* old_cookie,
SoupCookie* new_cookie,
MidoriWebSettings* settings)
{
if (midori_debug ("cookies"))
{
gchar* old = old_cookie ? soup_cookie_to_cookie_header (old_cookie) : NULL;
gchar* new = new_cookie ? soup_cookie_to_cookie_header (new_cookie) : NULL;
g_print ("cookie changed from %s to %s\n", old, new);
g_free (old);
g_free (new);
}
/* Don't allow revival of expiring cookies */
if (new_cookie && old_cookie && old_cookie->expires)
soup_cookie_set_expires (new_cookie, old_cookie->expires);
if (new_cookie && new_cookie->expires)
{
time_t expires = soup_date_to_time_t (new_cookie->expires);
gint age = katze_object_get_int (settings, "maximum-cookie-age");
/* An age of 0 to SoupCookie means already-expired
A user choosing 0 days probably expects 1 hour.
*/
int seconds = age > 0 ? age * SOUP_COOKIE_MAX_AGE_ONE_DAY : SOUP_COOKIE_MAX_AGE_ONE_HOUR;
SoupDate* max_date = soup_date_new_from_now (seconds);
if (expires > soup_date_to_time_t (max_date))
{
if (midori_debug ("cookies"))
{
gchar* new_date = soup_date_to_string (max_date, SOUP_DATE_COOKIE);
g_print ("^^ enforcing expiry: %s\n", new_date);
g_free (new_date);
}
soup_cookie_set_expires (new_cookie, max_date);
}
soup_date_free (max_date);
}
}
#endif
gboolean
midori_load_soup_session_full (gpointer settings)
{
#ifndef HAVE_WEBKIT2
SoupSession* session = webkit_get_default_session ();
SoupCookieJar* jar;
gchar* config_file;
SoupSessionFeature* feature;
midori_load_soup_session (settings);
config_file = midori_paths_get_config_filename_for_writing ("logins");
feature = g_object_new (KATZE_TYPE_HTTP_AUTH, "filename", config_file, NULL);
soup_session_add_feature (session, feature);
g_object_unref (feature);
katze_assign (config_file, midori_paths_get_config_filename_for_writing ("cookies.db"));
jar = soup_cookie_jar_sqlite_new (config_file, FALSE);
soup_session_add_feature (session, SOUP_SESSION_FEATURE (jar));
g_signal_connect (jar, "changed",
G_CALLBACK (midori_session_cookie_jar_changed_cb), settings);
g_object_unref (jar);
katze_assign (config_file, g_build_filename (midori_paths_get_cache_dir (), "web", NULL));
feature = SOUP_SESSION_FEATURE (soup_cache_new (config_file, 0));
soup_session_add_feature (session, feature);
soup_cache_set_max_size (SOUP_CACHE (feature),
katze_object_get_int (settings, "maximum-cache-size") * 1024 * 1024);
soup_cache_load (SOUP_CACHE (feature));
g_free (config_file);
#endif
return FALSE;
}
static void
extensions_update_cb (KatzeArray* extensions,
MidoriApp* app)
{
MidoriWebSettings* settings = katze_object_get_object (app, "settings");
g_object_notify (G_OBJECT (settings), "load-on-startup");
g_object_unref (settings);
}
gboolean
midori_load_extensions (gpointer data)
{
MidoriApp* app = MIDORI_APP (data);
gchar** keys = g_object_get_data (G_OBJECT (app), "extensions");
KatzeArray* extensions;
gboolean startup_timer = midori_debug ("startup");
GTimer* timer = startup_timer ? g_timer_new () : NULL;
/* Load extensions */
extensions = katze_array_new (MIDORI_TYPE_EXTENSION);
g_signal_connect (extensions, "update", G_CALLBACK (extensions_update_cb), app);
g_object_set (app, "extensions", extensions, NULL);
midori_extension_load_from_folder (app, keys, TRUE);
if (startup_timer)
g_debug ("Extensions:\t%f", g_timer_elapsed (timer, NULL));
return FALSE;
}
static void
settings_notify_cb (MidoriWebSettings* settings,
GParamSpec* pspec,
MidoriApp* app)
{
GError* error = NULL;
gchar* config_file;
/* Skip state related properties to avoid disk I/ O */
if (pspec && midori_settings_delay_saving (MIDORI_SETTINGS (settings), pspec->name))
return;
config_file = midori_paths_get_config_filename_for_writing ("config");
if (!midori_settings_save_to_file (settings, G_OBJECT (app), config_file, &error))
{
g_warning (_("The configuration couldn't be saved. %s"), error->message);
g_error_free (error);
}
g_free (config_file);
}
static void
midori_session_accel_map_changed_cb (GtkAccelMap* accel_map,
gchar* accel_path,
guint accel_key,
GdkModifierType accel_mods)
{
gchar* config_file = midori_paths_get_config_filename_for_writing ("accels");
gtk_accel_map_save (config_file);
g_free (config_file);
}
void
midori_session_persistent_settings (MidoriWebSettings* settings,
MidoriApp* app)
{
g_signal_connect_after (settings, "notify", G_CALLBACK (settings_notify_cb), app);
g_signal_connect_after (gtk_accel_map_get (), "changed",
G_CALLBACK (midori_session_accel_map_changed_cb), NULL);
}