/* * Copyright (C) 2012 Igalia S.L. * * 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. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "config.h" #include "WebKitSoupCookieJarSqlite.h" #include #include #include #include #include #include using namespace WebCore; struct _WebKitSoupCookieJarSqlitePrivate { String databasePath; SQLiteDatabase database; bool isLoading; }; G_DEFINE_TYPE(WebKitSoupCookieJarSqlite, webkit_soup_cookie_jar_sqlite, SOUP_TYPE_COOKIE_JAR) enum { ColumnID, ColumnName, ColumnValue, ColumnHost, ColumnPath, ColumnExpiry, ColumnLastAccess, ColumnSecure, ColumnHTTPOnly }; static bool webkitSoupCookieJarSqliteOpenDatabase(WebKitSoupCookieJarSqlite* sqliteJar) { WebKitSoupCookieJarSqlitePrivate* priv = sqliteJar->priv; if (priv->database.isOpen()) return true; ASSERT(!priv->databasePath.isEmpty()); if (!priv->database.open(priv->databasePath)) { g_warning("Can't open database %s", priv->databasePath.utf8().data()); return false; } priv->database.setSynchronous(SQLiteDatabase::SyncOff); priv->database.executeCommand("PRAGMA secure_delete = 1;"); return true; } static bool webkitSoupCookieJarSqliteCreateTable(WebKitSoupCookieJarSqlite* sqliteJar) { WebKitSoupCookieJarSqlitePrivate* priv = sqliteJar->priv; if (priv->database.tableExists("moz_cookies")) return true; if (!priv->database.executeCommand("CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT, expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)")) { g_warning("Failed to create table moz_cookies: (%i) - %s", priv->database.lastError(), priv->database.lastErrorMsg()); priv->database.close(); return false; } return true; } static void webkitSoupCookieJarSqliteLoad(WebKitSoupCookieJarSqlite* sqliteJar) { if (!webkitSoupCookieJarSqliteOpenDatabase(sqliteJar)) return; if (!webkitSoupCookieJarSqliteCreateTable(sqliteJar)) return; WebKitSoupCookieJarSqlitePrivate* priv = sqliteJar->priv; priv->isLoading = true; SQLiteStatement query(priv->database, "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM moz_cookies;"); if (query.prepare() != SQLResultOk) { g_warning("Failed to prepare all cookies query"); priv->isLoading = false; return; } SoupCookieJar* jar = SOUP_COOKIE_JAR(sqliteJar); time_t now = floorf(currentTime()); int result; while ((result = query.step()) == SQLResultRow) { int expireTime = query.getColumnInt(ColumnExpiry); if (now >= expireTime) continue; SoupCookie* cookie = soup_cookie_new(query.getColumnText(ColumnName).utf8().data(), query.getColumnText(ColumnValue).utf8().data(), query.getColumnText(ColumnHost).utf8().data(), query.getColumnText(ColumnPath).utf8().data(), expireTime - now <= G_MAXINT ? expireTime - now : G_MAXINT); if (query.getColumnInt(ColumnSecure)) soup_cookie_set_secure(cookie, TRUE); if (query.getColumnInt(ColumnHTTPOnly)) soup_cookie_set_http_only(cookie, TRUE); soup_cookie_jar_add_cookie(jar, cookie); } if (result != SQLResultDone) g_warning("Error reading cookies from database"); priv->isLoading = false; } static bool webkitSoupCookieJarSqliteInsertCookie(WebKitSoupCookieJarSqlite* sqliteJar, SoupCookie* cookie) { WebKitSoupCookieJarSqlitePrivate* priv = sqliteJar->priv; SQLiteStatement query(priv->database, "INSERT INTO moz_cookies VALUES(NULL, ?, ?, ?, ?, ?, NULL, ?, ?);"); if (query.prepare() != SQLResultOk) { g_warning("Failed to prepare insert cookies query"); return false; } query.bindText(1, String::fromUTF8(cookie->name)); query.bindText(2, String::fromUTF8(cookie->value)); query.bindText(3, String::fromUTF8(cookie->domain)); query.bindText(4, String::fromUTF8(cookie->path)); query.bindInt(5, static_cast(soup_date_to_time_t(cookie->expires))); query.bindInt(6, cookie->secure); query.bindInt(7, cookie->http_only); if (query.step() != SQLResultDone) { g_warning("Error adding cookie (name=%s, domain=%s) to database", cookie->name, cookie->name); return false; } return true; } static bool webkitSoupCookieJarSqliteDeleteCookie(WebKitSoupCookieJarSqlite* sqliteJar, SoupCookie* cookie) { WebKitSoupCookieJarSqlitePrivate* priv = sqliteJar->priv; SQLiteStatement query(priv->database, "DELETE FROM moz_cookies WHERE name = (?) AND host = (?);"); if (query.prepare() != SQLResultOk) { g_warning("Failed to prepare delete cookies query"); return false; } query.bindText(1, String::fromUTF8(cookie->name)); query.bindText(2, String::fromUTF8(cookie->domain)); if (query.step() != SQLResultDone) { g_warning("Error deleting cookie (name=%s, domain=%s) from database", cookie->name, cookie->name); return false; } return true; } static void webkitSoupCookieJarSqliteChanged(SoupCookieJar* jar, SoupCookie* oldCookie, SoupCookie* newCookie) { WebKitSoupCookieJarSqlite* sqliteJar = WEBKIT_SOUP_COOKIE_JAR_SQLITE(jar); if (sqliteJar->priv->isLoading) return; if (!webkitSoupCookieJarSqliteOpenDatabase(sqliteJar)) return; if (!oldCookie && (!newCookie || !newCookie->expires)) return; if (!webkitSoupCookieJarSqliteCreateTable(sqliteJar)) return; SQLiteTransaction updateTransaction(sqliteJar->priv->database); updateTransaction.begin(); if (oldCookie && !webkitSoupCookieJarSqliteDeleteCookie(sqliteJar, oldCookie)) return; if (newCookie && newCookie->expires && !webkitSoupCookieJarSqliteInsertCookie(sqliteJar, newCookie)) return; updateTransaction.commit(); } static void webkitSoupCookieJarSqliteFinalize(GObject* object) { WEBKIT_SOUP_COOKIE_JAR_SQLITE(object)->priv->~WebKitSoupCookieJarSqlitePrivate(); G_OBJECT_CLASS(webkit_soup_cookie_jar_sqlite_parent_class)->finalize(object); } static void webkit_soup_cookie_jar_sqlite_init(WebKitSoupCookieJarSqlite* sqliteJar) { WebKitSoupCookieJarSqlitePrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(sqliteJar, WEBKIT_TYPE_SOUP_COOKIE_JAR_SQLITE, WebKitSoupCookieJarSqlitePrivate); sqliteJar->priv = priv; new (priv) WebKitSoupCookieJarSqlitePrivate(); } static void webkit_soup_cookie_jar_sqlite_class_init(WebKitSoupCookieJarSqliteClass* sqliteJarClass) { SoupCookieJarClass* cookieJarClass = SOUP_COOKIE_JAR_CLASS(sqliteJarClass); cookieJarClass->changed = webkitSoupCookieJarSqliteChanged; GObjectClass* gObjectClass = G_OBJECT_CLASS(sqliteJarClass); gObjectClass->finalize = webkitSoupCookieJarSqliteFinalize; g_type_class_add_private(sqliteJarClass, sizeof(WebKitSoupCookieJarSqlitePrivate)); } SoupCookieJar* webkitSoupCookieJarSqliteNew(const String& databasePath) { ASSERT(!databasePath.isEmpty()); WebKitSoupCookieJarSqlite* sqliteJar = WEBKIT_SOUP_COOKIE_JAR_SQLITE(g_object_new(WEBKIT_TYPE_SOUP_COOKIE_JAR_SQLITE, NULL)); sqliteJar->priv->databasePath = databasePath; webkitSoupCookieJarSqliteLoad(sqliteJar); return SOUP_COOKIE_JAR(sqliteJar); }