1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
/*
Copyright (©) 2003-2025 Teus Benschop.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <database/login.h>
#include <database/sqlite.h>
#include <filter/url.h>
#include <filter/string.h>
#include <filter/md5.h>
#include <filter/roles.h>
#include <filter/date.h>
// This database is resilient.
// The data is stored in a SQLite database.
// This part is read often, and infrequently written to.
// Due to the infrequent write operations, there is a low and acceptable change of corruption.
// The name of the database.
const char * Database_Login::database ()
{
return "login";
}
void Database_Login::create ()
{
SqliteDatabase sql (database ());
sql.add ("CREATE TABLE IF NOT EXISTS logins ("
" username text,"
" address text,"
" agent text,"
" fingerprint text,"
" cookie text,"
" touch boolean,"
" timestamp integer"
");");
sql.execute ();
}
void Database_Login::trim ()
{
// Remove persistent logins after 365 days of inactivity.
SqliteDatabase sql (database ());
sql.add ("DELETE FROM logins WHERE timestamp < ");
sql.add (timestamp () - 365);
sql.add (";");
sql.execute ();
}
void Database_Login::optimize ()
{
if (!healthy ()) {
// (Re)create damaged or non-existing database.
filter_url_unlink (database::sqlite::get_file (database ()));
create ();
}
// Vacuum it.
SqliteDatabase sql (database ());
// On Android, this pragma prevents the following error: VACUUM; Unable to open database file.
sql.add ("PRAGMA temp_store = MEMORY;");
sql.execute ();
sql.clear ();
sql.add ("VACUUM;");
sql.execute ();
}
bool Database_Login::healthy ()
{
return database::sqlite::healthy (database ());
}
// Sets the login security tokens for a user.
// Also store whether the device is touch-enabled.
// It only writes to the table if the combination of username and tokens differs from what the table already contains.
void Database_Login::setTokens (std::string username, std::string address, std::string agent, std::string fingerprint, std::string cookie, bool touch)
{
bool daily;
if (username == getUsername (cookie, daily)) return;
address = md5 (address);
agent = md5 (agent);
fingerprint = md5 (fingerprint);
SqliteDatabase sql (database ());
sql.add ("INSERT INTO logins VALUES (");
sql.add (username);
sql.add (",");
sql.add (address);
sql.add (",");
sql.add (agent);
sql.add (",");
sql.add (fingerprint);
sql.add (",");
sql.add (cookie);
sql.add (",");
sql.add (touch);
sql.add (",");
sql.add (timestamp ());
sql.add (");");
sql.execute ();
}
// Remove the login security tokens for a user.
void Database_Login::removeTokens (std::string username)
{
SqliteDatabase sql (database ());
sql.add ("DELETE FROM logins WHERE username =");
sql.add (username);
sql.add (";");
sql.execute ();
}
// Remove the login security tokens for a user based on the cookie.
void Database_Login::removeTokens (std::string username, std::string cookie)
{
//address = md5 (address);
//agent = md5 (agent);
//fingerprint = md5 (fingerprint);
SqliteDatabase sql (database ());
sql.add ("DELETE FROM logins WHERE username =");
sql.add (username);
sql.add ("AND cookie =");
sql.add (cookie);
sql.add (";");
sql.execute ();
}
void Database_Login::renameTokens (std::string username_existing, std::string username_new, std::string cookie)
{
SqliteDatabase sql (database ());
sql.add ("UPDATE logins SET username =");
sql.add (username_new);
sql.add ("WHERE username =");
sql.add (username_existing);
sql.add ("AND cookie =");
sql.add (cookie);
sql.add (";");
sql.execute ();
}
// Returns the username that matches the cookie sent by the browser.
// Once a day, $daily will be set true.
std::string Database_Login::getUsername (std::string cookie, bool & daily)
{
SqliteDatabase sql (database ());
sql.add ("SELECT rowid, timestamp, username FROM logins WHERE cookie =");
sql.add (cookie);
sql.add (";");
std::map <std::string, std::vector <std::string> > result = sql.query ();
if (result.empty()) return std::string();
std::string username = result ["username"][0];
int stamp = filter::strings::convert_to_int (result ["timestamp"] [0]);
if (stamp != timestamp ()) {
// Touch the timestamp. This occurs once a day.
int rowid = filter::strings::convert_to_int (result ["rowid"] [0]);
sql.clear ();
sql.add ("UPDATE logins SET timestamp =");
sql.add (timestamp ());
sql.add ("WHERE rowid =");
sql.add (rowid);
sql.execute ();
daily = true;
} else {
daily = false;
}
return username;
}
// Returns whether the device, that matches the cookie it sent, is touch-enabled.
bool Database_Login::getTouchEnabled (std::string cookie)
{
SqliteDatabase sql (database ());
sql.add ("SELECT touch FROM logins WHERE cookie =");
sql.add (cookie);
sql.add (";");
std::vector <std::string> result = sql.query () ["touch"];
if (!result.empty()) return filter::strings::convert_to_bool (result [0]);
return false;
}
void Database_Login::testTimestamp ()
{
SqliteDatabase sql (database ());
sql.add ("UPDATE logins SET timestamp = timestamp - 370;");
sql.execute ();
}
// Gets the current number of days since the Unix epoch.
int Database_Login::timestamp ()
{
return filter::date::seconds_since_epoch () / 86400;
}
|