WebKitSoupCookieJarSqlite.cpp [plain text]
#include "config.h"
#include "WebKitSoupCookieJarSqlite.h"
#include <WebCore/SQLiteDatabase.h>
#include <WebCore/SQLiteStatement.h>
#include <WebCore/SQLiteTransaction.h>
#include <libsoup/soup.h>
#include <wtf/CurrentTime.h>
#include <wtf/MathExtras.h>
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<int64_t>(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);
}