1/* 2 * Copyright (C) 2012 Igalia S.L. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2,1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20#include "config.h" 21#include "WebKitSoupCookieJarSqlite.h" 22 23#include <WebCore/SQLiteDatabase.h> 24#include <WebCore/SQLiteStatement.h> 25#include <WebCore/SQLiteTransaction.h> 26#include <libsoup/soup.h> 27#include <wtf/CurrentTime.h> 28#include <wtf/MathExtras.h> 29 30using namespace WebCore; 31 32struct _WebKitSoupCookieJarSqlitePrivate { 33 String databasePath; 34 SQLiteDatabase database; 35 bool isLoading; 36}; 37 38G_DEFINE_TYPE(WebKitSoupCookieJarSqlite, webkit_soup_cookie_jar_sqlite, SOUP_TYPE_COOKIE_JAR) 39 40enum { 41 ColumnID, 42 ColumnName, 43 ColumnValue, 44 ColumnHost, 45 ColumnPath, 46 ColumnExpiry, 47 ColumnLastAccess, 48 ColumnSecure, 49 ColumnHTTPOnly 50}; 51 52static bool webkitSoupCookieJarSqliteOpenDatabase(WebKitSoupCookieJarSqlite* sqliteJar) 53{ 54 WebKitSoupCookieJarSqlitePrivate* priv = sqliteJar->priv; 55 if (priv->database.isOpen()) 56 return true; 57 58 ASSERT(!priv->databasePath.isEmpty()); 59 if (!priv->database.open(priv->databasePath)) { 60 g_warning("Can't open database %s", priv->databasePath.utf8().data()); 61 return false; 62 } 63 64 priv->database.setSynchronous(SQLiteDatabase::SyncOff); 65 priv->database.executeCommand("PRAGMA secure_delete = 1;"); 66 67 return true; 68} 69 70static bool webkitSoupCookieJarSqliteCreateTable(WebKitSoupCookieJarSqlite* sqliteJar) 71{ 72 WebKitSoupCookieJarSqlitePrivate* priv = sqliteJar->priv; 73 if (priv->database.tableExists("moz_cookies")) 74 return true; 75 76 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)")) { 77 g_warning("Failed to create table moz_cookies: (%i) - %s", priv->database.lastError(), priv->database.lastErrorMsg()); 78 priv->database.close(); 79 80 return false; 81 } 82 83 return true; 84} 85 86static void webkitSoupCookieJarSqliteLoad(WebKitSoupCookieJarSqlite* sqliteJar) 87{ 88 if (!webkitSoupCookieJarSqliteOpenDatabase(sqliteJar)) 89 return; 90 if (!webkitSoupCookieJarSqliteCreateTable(sqliteJar)) 91 return; 92 93 WebKitSoupCookieJarSqlitePrivate* priv = sqliteJar->priv; 94 priv->isLoading = true; 95 SQLiteStatement query(priv->database, "SELECT id, name, value, host, path, expiry, lastAccessed, isSecure, isHttpOnly FROM moz_cookies;"); 96 if (query.prepare() != SQLResultOk) { 97 g_warning("Failed to prepare all cookies query"); 98 priv->isLoading = false; 99 return; 100 } 101 102 SoupCookieJar* jar = SOUP_COOKIE_JAR(sqliteJar); 103 time_t now = floorf(currentTime()); 104 int result; 105 while ((result = query.step()) == SQLResultRow) { 106 int expireTime = query.getColumnInt(ColumnExpiry); 107 if (now >= expireTime) 108 continue; 109 110 SoupCookie* cookie = soup_cookie_new(query.getColumnText(ColumnName).utf8().data(), query.getColumnText(ColumnValue).utf8().data(), 111 query.getColumnText(ColumnHost).utf8().data(), query.getColumnText(ColumnPath).utf8().data(), 112 expireTime - now <= G_MAXINT ? expireTime - now : G_MAXINT); 113 if (query.getColumnInt(ColumnSecure)) 114 soup_cookie_set_secure(cookie, TRUE); 115 if (query.getColumnInt(ColumnHTTPOnly)) 116 soup_cookie_set_http_only(cookie, TRUE); 117 118 soup_cookie_jar_add_cookie(jar, cookie); 119 } 120 121 if (result != SQLResultDone) 122 g_warning("Error reading cookies from database"); 123 priv->isLoading = false; 124} 125 126static bool webkitSoupCookieJarSqliteInsertCookie(WebKitSoupCookieJarSqlite* sqliteJar, SoupCookie* cookie) 127{ 128 WebKitSoupCookieJarSqlitePrivate* priv = sqliteJar->priv; 129 SQLiteStatement query(priv->database, "INSERT INTO moz_cookies VALUES(NULL, ?, ?, ?, ?, ?, NULL, ?, ?);"); 130 if (query.prepare() != SQLResultOk) { 131 g_warning("Failed to prepare insert cookies query"); 132 return false; 133 } 134 135 query.bindText(1, String::fromUTF8(cookie->name)); 136 query.bindText(2, String::fromUTF8(cookie->value)); 137 query.bindText(3, String::fromUTF8(cookie->domain)); 138 query.bindText(4, String::fromUTF8(cookie->path)); 139 query.bindInt(5, static_cast<int64_t>(soup_date_to_time_t(cookie->expires))); 140 query.bindInt(6, cookie->secure); 141 query.bindInt(7, cookie->http_only); 142 if (query.step() != SQLResultDone) { 143 g_warning("Error adding cookie (name=%s, domain=%s) to database", cookie->name, cookie->name); 144 return false; 145 } 146 147 return true; 148} 149 150static bool webkitSoupCookieJarSqliteDeleteCookie(WebKitSoupCookieJarSqlite* sqliteJar, SoupCookie* cookie) 151{ 152 WebKitSoupCookieJarSqlitePrivate* priv = sqliteJar->priv; 153 SQLiteStatement query(priv->database, "DELETE FROM moz_cookies WHERE name = (?) AND host = (?);"); 154 if (query.prepare() != SQLResultOk) { 155 g_warning("Failed to prepare delete cookies query"); 156 return false; 157 } 158 159 query.bindText(1, String::fromUTF8(cookie->name)); 160 query.bindText(2, String::fromUTF8(cookie->domain)); 161 if (query.step() != SQLResultDone) { 162 g_warning("Error deleting cookie (name=%s, domain=%s) from database", cookie->name, cookie->name); 163 return false; 164 } 165 166 return true; 167} 168 169static void webkitSoupCookieJarSqliteChanged(SoupCookieJar* jar, SoupCookie* oldCookie, SoupCookie* newCookie) 170{ 171 WebKitSoupCookieJarSqlite* sqliteJar = WEBKIT_SOUP_COOKIE_JAR_SQLITE(jar); 172 if (sqliteJar->priv->isLoading) 173 return; 174 if (!webkitSoupCookieJarSqliteOpenDatabase(sqliteJar)) 175 return; 176 if (!oldCookie && (!newCookie || !newCookie->expires)) 177 return; 178 if (!webkitSoupCookieJarSqliteCreateTable(sqliteJar)) 179 return; 180 181 SQLiteTransaction updateTransaction(sqliteJar->priv->database); 182 updateTransaction.begin(); 183 184 if (oldCookie && !webkitSoupCookieJarSqliteDeleteCookie(sqliteJar, oldCookie)) 185 return; 186 187 if (newCookie && newCookie->expires && !webkitSoupCookieJarSqliteInsertCookie(sqliteJar, newCookie)) 188 return; 189 190 updateTransaction.commit(); 191} 192 193static void webkitSoupCookieJarSqliteFinalize(GObject* object) 194{ 195 WEBKIT_SOUP_COOKIE_JAR_SQLITE(object)->priv->~WebKitSoupCookieJarSqlitePrivate(); 196 G_OBJECT_CLASS(webkit_soup_cookie_jar_sqlite_parent_class)->finalize(object); 197} 198 199static void webkit_soup_cookie_jar_sqlite_init(WebKitSoupCookieJarSqlite* sqliteJar) 200{ 201 WebKitSoupCookieJarSqlitePrivate* priv = G_TYPE_INSTANCE_GET_PRIVATE(sqliteJar, WEBKIT_TYPE_SOUP_COOKIE_JAR_SQLITE, WebKitSoupCookieJarSqlitePrivate); 202 sqliteJar->priv = priv; 203 new (priv) WebKitSoupCookieJarSqlitePrivate(); 204} 205 206static void webkit_soup_cookie_jar_sqlite_class_init(WebKitSoupCookieJarSqliteClass* sqliteJarClass) 207{ 208 SoupCookieJarClass* cookieJarClass = SOUP_COOKIE_JAR_CLASS(sqliteJarClass); 209 cookieJarClass->changed = webkitSoupCookieJarSqliteChanged; 210 211 GObjectClass* gObjectClass = G_OBJECT_CLASS(sqliteJarClass); 212 gObjectClass->finalize = webkitSoupCookieJarSqliteFinalize; 213 214 g_type_class_add_private(sqliteJarClass, sizeof(WebKitSoupCookieJarSqlitePrivate)); 215} 216 217SoupCookieJar* webkitSoupCookieJarSqliteNew(const String& databasePath) 218{ 219 ASSERT(!databasePath.isEmpty()); 220 WebKitSoupCookieJarSqlite* sqliteJar = WEBKIT_SOUP_COOKIE_JAR_SQLITE(g_object_new(WEBKIT_TYPE_SOUP_COOKIE_JAR_SQLITE, NULL)); 221 sqliteJar->priv->databasePath = databasePath; 222 webkitSoupCookieJarSqliteLoad(sqliteJar); 223 return SOUP_COOKIE_JAR(sqliteJar); 224} 225