qTox  Version: nightly | Commit: bc751c8e1cac455f9690654fcfe0f560d2d7dfdd
rawdatabase.cpp
Go to the documentation of this file.
1 /*
2  Copyright © 2014-2019 by The qTox Project Contributors
3 
4  This file is part of qTox, a Qt-based graphical interface for Tox.
5 
6  qTox is libre software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  qTox is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "rawdatabase.h"
21 
22 #include <cassert>
23 #include <tox/toxencryptsave.h>
24 
25 #include <QCoreApplication>
26 #include <QDebug>
27 #include <QFile>
28 #include <QMetaObject>
29 #include <QMutexLocker>
30 
31 
87 RawDatabase::RawDatabase(const QString& path, const QString& password, const QByteArray& salt)
88  : workerThread{new QThread}
89  , path{path}
90  , currentSalt{salt} // we need the salt later if a new password should be set
91  , currentHexKey{deriveKey(password, salt)}
92 {
93  workerThread->setObjectName("qTox Database");
94  moveToThread(workerThread.get());
95  workerThread->start();
96 
97  // first try with the new salt
98  if (open(path, currentHexKey)) {
99  return;
100  }
101 
102  // avoid opening the same db twice
103  close();
104 
105  // create a backup before trying to upgrade to new salt
106  bool upgrade = true;
107  if (!QFile::copy(path, path + ".bak")) {
108  qDebug() << "Couldn't create the backup of the database, won't upgrade";
109  upgrade = false;
110  }
111 
112  // fall back to the old salt
113  currentHexKey = deriveKey(password);
114  if (open(path, currentHexKey)) {
115  // upgrade only if backup successful
116  if (upgrade) {
117  // still using old salt, upgrade
118  if (setPassword(password)) {
119  qDebug() << "Successfully upgraded to dynamic salt";
120  } else {
121  qWarning() << "Failed to set password with new salt";
122  }
123  }
124  } else {
125  qDebug() << "Failed to open database with old salt";
126  }
127 }
128 
130 {
131  close();
132  workerThread->exit(0);
133  while (workerThread->isRunning())
134  workerThread->wait(50);
135 }
136 
143 bool RawDatabase::open(const QString& path, const QString& hexKey)
144 {
145  if (QThread::currentThread() != workerThread.get()) {
146  bool ret;
147  QMetaObject::invokeMethod(this, "open", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, ret),
148  Q_ARG(const QString&, path), Q_ARG(const QString&, hexKey));
149  return ret;
150  }
151 
152  if (!QFile::exists(path) && QFile::exists(path + ".tmp")) {
153  qWarning() << "Restoring database from temporary export file! Did we crash while changing "
154  "the password or upgrading?";
155  QFile::rename(path + ".tmp", path);
156  }
157 
158  if (sqlite3_open_v2(path.toUtf8().data(), &sqlite,
159  SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX, nullptr)
160  != SQLITE_OK) {
161  qWarning() << "Failed to open database" << path << "with error:" << sqlite3_errmsg(sqlite);
162  return false;
163  }
164 
165  if (sqlite3_create_function(sqlite, "regexp", 2, SQLITE_UTF8, nullptr, &RawDatabase::regexpInsensitive, nullptr, nullptr)) {
166  qWarning() << "Failed to create function regexp";
167  close();
168  return false;
169  }
170 
171  if (sqlite3_create_function(sqlite, "regexpsensitive", 2, SQLITE_UTF8, nullptr, &RawDatabase::regexpSensitive, nullptr, nullptr)) {
172  qWarning() << "Failed to create function regexpsensitive";
173  close();
174  return false;
175  }
176 
177  if (!hexKey.isEmpty()) {
179  close();
180  return false;
181  }
182  }
183  return true;
184 }
185 
187 {
188  // old qTox database are saved with SQLCipher 3.x defaults. For a period after 1.16.3 but before 1.17.0, databases
189  // could be partially upgraded to SQLCipher 4.0 defaults, since SQLCipher 3.x isn't capable of setitng all the same
190  // params. If SQLCipher 4.x happened to be used, they would have been fully upgraded to 4.0 default params.
191  // We need to support all three of these cases, so also upgrade to the latest possible params while we're here
192  if (!setKey(hexKey)) {
193  return false;
194  }
195 
196  auto highestSupportedVersion = highestSupportedParams();
197  if (setCipherParameters(highestSupportedVersion)) {
198  if (testUsable()) {
199  qInfo() << "Opened database with SQLCipher" << toString(highestSupportedVersion) << "parameters";
200  return true;
201  } else {
202  return updateSavedCipherParameters(hexKey, highestSupportedVersion);
203  }
204  } else {
205  qCritical() << "Failed to set latest supported SQLCipher params!";
206  return false;
207  }
208 }
209 
211 {
212  // this will unfortunately log a warning if it fails, even though we may expect failure
213  return execNow("SELECT count(*) FROM sqlite_master;");
214 }
215 
219 bool RawDatabase::updateSavedCipherParameters(const QString& hexKey, SqlCipherParams newParams)
220 {
221  auto currentParams = readSavedCipherParams(hexKey, newParams);
222  setKey(hexKey); // setKey again because a SELECT has already been run, causing crypto settings to take effect
223  if (!setCipherParameters(currentParams)) {
224  return false;
225  }
226 
227  const auto user_version = getUserVersion();
228  if (user_version < 0) {
229  return false;
230  }
231  if (!execNow("ATTACH DATABASE '" + path + ".tmp' AS newParams KEY \"x'" + hexKey + "'\";")) {
232  return false;
233  }
234  if (!setCipherParameters(newParams, "newParams")) {
235  return false;
236  }
237  if (!execNow("SELECT sqlcipher_export('newParams');")) {
238  return false;
239  }
240  if (!execNow(QString("PRAGMA newParams.user_version = %1;").arg(user_version))) {
241  return false;
242  }
243  if (!execNow("DETACH DATABASE newParams;")) {
244  return false;
245  }
246  if (!commitDbSwap(hexKey)) {
247  return false;
248  }
249  qInfo() << "Upgraded database from SQLCipher" << toString(currentParams) << "params to" <<
250  toString(newParams) << "params complete";
251  return true;
252 }
253 
254 bool RawDatabase::setCipherParameters(SqlCipherParams params, const QString& database)
255 {
256  QString prefix;
257  if (!database.isNull()) {
258  prefix = database + ".";
259  }
260  // from https://www.zetetic.net/blog/2018/11/30/sqlcipher-400-release/
261  const QString default3_xParams{"PRAGMA database.cipher_page_size = 1024;"
262  "PRAGMA database.kdf_iter = 64000;"
263  "PRAGMA database.cipher_hmac_algorithm = HMAC_SHA1;"
264  "PRAGMA database.cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;"};
265  // cipher_hmac_algorithm and cipher_kdf_algorithm weren't supported in sqlcipher 3.x, so our upgrade to 4 only
266  // applied some of the new params if sqlcipher 3.x was used at the time
267  const QString halfUpgradedTo4Params{"PRAGMA database.cipher_page_size = 4096;"
268  "PRAGMA database.kdf_iter = 256000;"
269  "PRAGMA database.cipher_hmac_algorithm = HMAC_SHA1;"
270  "PRAGMA database.cipher_kdf_algorithm = PBKDF2_HMAC_SHA1;"};
271  const QString default4_xParams{"PRAGMA database.cipher_page_size = 4096;"
272  "PRAGMA database.kdf_iter = 256000;"
273  "PRAGMA database.cipher_hmac_algorithm = HMAC_SHA512;"
274  "PRAGMA database.cipher_kdf_algorithm = PBKDF2_HMAC_SHA512;"
275  "PRAGMA database.cipher_memory_security = ON;"}; // got disabled by default in 4.5.0, so manually enable it
276 
277  QString defaultParams;
278  switch(params) {
279  case SqlCipherParams::p3_0: {
280  defaultParams = default3_xParams;
281  break;
282  }
284  defaultParams = halfUpgradedTo4Params;
285  break;
286  }
287  case SqlCipherParams::p4_0: {
288  defaultParams = default4_xParams;
289  break;
290  }
291  }
292 
293  qDebug() << "Setting SQLCipher" << toString(params) << "parameters";
294  return execNow(defaultParams.replace("database.", prefix));
295 }
296 
298 {
299  // Note: This is just calling into the sqlcipher library, not touching the database.
300  QString cipherVersion;
301  if (!execNow(RawDatabase::Query("PRAGMA cipher_version", [&](const QVector<QVariant>& row) {
302  cipherVersion = row[0].toString();
303  }))) {
304  qCritical() << "Failed to read cipher_version";
305  return SqlCipherParams::p3_0;
306  }
307 
308  auto majorVersion = cipherVersion.split('.')[0].toInt();
309 
311  switch (majorVersion) {
312  case 3:
314  break;
315  case 4:
317  break;
318  default:
319  qCritical() << "Unsupported SQLCipher version detected!";
320  return SqlCipherParams::p3_0;
321  }
322  qDebug() << "Highest supported SQLCipher params on this system are" << toString(highestSupportedParams);
323  return highestSupportedParams;
324 }
325 
327 {
328  for (int i = static_cast<int>(SqlCipherParams::p3_0); i < static_cast<int>(newParams); ++i)
329  {
330  if (!setKey(hexKey)) {
331  break;
332  }
333 
334  if (!setCipherParameters(static_cast<SqlCipherParams>(i))) {
335  break;
336  }
337 
338  if (testUsable()) {
339  return static_cast<SqlCipherParams>(i);
340  }
341  }
342  qCritical() << "Failed to check saved SQLCipher params";
343  return SqlCipherParams::p3_0;
344 }
345 
346 bool RawDatabase::setKey(const QString& hexKey)
347 {
348  // setKey again to clear old bad cipher settings
349  if (!execNow("PRAGMA key = \"x'" + hexKey + "'\"")) {
350  qWarning() << "Failed to set encryption key";
351  return false;
352  }
353  return true;
354 }
355 
357 {
358  int64_t user_version;
359  if (!execNow(RawDatabase::Query("PRAGMA user_version", [&](const QVector<QVariant>& row) {
360  user_version = row[0].toLongLong();
361  }))) {
362  qCritical() << "Failed to read user_version during cipher upgrade";
363  return -1;
364  }
365  return user_version;
366 }
367 
372 {
373  if (QThread::currentThread() != workerThread.get())
374  return (void)QMetaObject::invokeMethod(this, "close", Qt::BlockingQueuedConnection);
375 
376  // We assume we're in the ctor or dtor, so we just need to finish processing our transactions
377  process();
378 
379  if (sqlite3_close(sqlite) == SQLITE_OK)
380  sqlite = nullptr;
381  else
382  qWarning() << "Error closing database:" << sqlite3_errmsg(sqlite);
383 }
384 
390 {
391  // We don't need thread safety since only the ctor/dtor can write this pointer
392  return sqlite != nullptr;
393 }
394 
400 bool RawDatabase::execNow(const QString& statement)
401 {
402  return execNow(Query{statement});
403 }
404 
411 {
412  return execNow(QVector<Query>{statement});
413 }
414 
420 bool RawDatabase::execNow(const QVector<RawDatabase::Query>& statements)
421 {
422  if (!sqlite) {
423  qWarning() << "Trying to exec, but the database is not open";
424  return false;
425  }
426 
427  std::atomic_bool done{false};
428  std::atomic_bool success{false};
429 
430  Transaction trans;
431  trans.queries = statements;
432  trans.done = &done;
433  trans.success = &success;
434  {
435  QMutexLocker locker{&transactionsMutex};
436  pendingTransactions.enqueue(trans);
437  }
438 
439  // We can't use blocking queued here, otherwise we might process future transactions
440  // before returning, but we only want to wait until this transaction is done.
441  QMetaObject::invokeMethod(this, "process");
442  while (!done.load(std::memory_order_acquire))
443  QThread::msleep(10);
444 
445  return success.load(std::memory_order_acquire);
446 }
447 
452 void RawDatabase::execLater(const QString& statement)
453 {
454  execLater(Query{statement});
455 }
456 
458 {
459  execLater(QVector<Query>{statement});
460 }
461 
462 void RawDatabase::execLater(const QVector<RawDatabase::Query>& statements)
463 {
464  if (!sqlite) {
465  qWarning() << "Trying to exec, but the database is not open";
466  return;
467  }
468 
469  Transaction trans;
470  trans.queries = statements;
471  {
472  QMutexLocker locker{&transactionsMutex};
473  pendingTransactions.enqueue(trans);
474  }
475 
476  QMetaObject::invokeMethod(this, "process", Qt::QueuedConnection);
477 }
478 
483 {
484  QMetaObject::invokeMethod(this, "process", Qt::BlockingQueuedConnection);
485 }
486 
493 bool RawDatabase::setPassword(const QString& password)
494 {
495  if (!sqlite) {
496  qWarning() << "Trying to change the password, but the database is not open";
497  return false;
498  }
499 
500  if (QThread::currentThread() != workerThread.get()) {
501  bool ret;
502  QMetaObject::invokeMethod(this, "setPassword", Qt::BlockingQueuedConnection,
503  Q_RETURN_ARG(bool, ret), Q_ARG(const QString&, password));
504  return ret;
505  }
506 
507  // If we need to decrypt or encrypt, we'll need to sync and close,
508  // so we always process the pending queue before rekeying for consistency
509  process();
510 
511  if (QFile::exists(path + ".tmp")) {
512  qWarning() << "Found old temporary export file while rekeying, deleting it";
513  QFile::remove(path + ".tmp");
514  }
515 
516  if (!password.isEmpty()) {
517  QString newHexKey = deriveKey(password, currentSalt);
518  if (!currentHexKey.isEmpty()) {
519  if (!execNow("PRAGMA rekey = \"x'" + newHexKey + "'\"")) {
520  qWarning() << "Failed to change encryption key";
521  close();
522  return false;
523  }
524  } else {
525  if (!encryptDatabase(newHexKey)) {
526  close();
527  return false;
528  }
529  currentHexKey = newHexKey;
530  }
531  } else {
532  if (currentHexKey.isEmpty())
533  return true;
534 
535  if (!decryptDatabase()) {
536  close();
537  return false;
538  }
539  }
540  return true;
541 }
542 
543 bool RawDatabase::encryptDatabase(const QString& newHexKey)
544 {
545  const auto user_version = getUserVersion();
546  if (user_version < 0) {
547  return false;
548  }
549  if (!execNow("ATTACH DATABASE '" + path + ".tmp' AS encrypted KEY \"x'" + newHexKey
550  + "'\";")) {
551  qWarning() << "Failed to export encrypted database";
552  return false;
553  }
554  if (!setCipherParameters(SqlCipherParams::p4_0, "encrypted")) {
555  return false;
556  }
557  if (!execNow("SELECT sqlcipher_export('encrypted');")) {
558  return false;
559  }
560  if (!execNow(QString("PRAGMA encrypted.user_version = %1;").arg(user_version))) {
561  return false;
562  }
563  if (!execNow("DETACH DATABASE encrypted;")) {
564  return false;
565  }
566  return commitDbSwap(newHexKey);
567 }
568 
570 {
571  const auto user_version = getUserVersion();
572  if (user_version < 0) {
573  return false;
574  }
575  if (!execNow("ATTACH DATABASE '" + path + ".tmp' AS plaintext KEY '';"
576  "SELECT sqlcipher_export('plaintext');")) {
577  qWarning() << "Failed to export decrypted database";
578  return false;
579  }
580  if (!execNow(QString("PRAGMA plaintext.user_version = %1;").arg(user_version))) {
581  return false;
582  }
583  if (!execNow("DETACH DATABASE plaintext;")) {
584  return false;
585  }
586  return commitDbSwap({});
587 }
588 
589 bool RawDatabase::commitDbSwap(const QString& hexKey)
590 {
591  // This is racy as hell, but nobody will race with us since we hold the profile lock
592  // If we crash or die here, the rename should be atomic, so we can recover no matter
593  // what
594  close();
595  QFile::remove(path);
596  QFile::rename(path + ".tmp", path);
597  currentHexKey = hexKey;
598  if (!open(path, currentHexKey)) {
599  qCritical() << "Failed to swap db";
600  return false;
601  }
602  return true;
603 }
604 
612 bool RawDatabase::rename(const QString& newPath)
613 {
614  if (!sqlite) {
615  qWarning() << "Trying to change the password, but the database is not open";
616  return false;
617  }
618 
619  if (QThread::currentThread() != workerThread.get()) {
620  bool ret;
621  QMetaObject::invokeMethod(this, "rename", Qt::BlockingQueuedConnection,
622  Q_RETURN_ARG(bool, ret), Q_ARG(const QString&, newPath));
623  return ret;
624  }
625 
626  process();
627 
628  if (path == newPath)
629  return true;
630 
631  if (QFile::exists(newPath))
632  return false;
633 
634  close();
635  if (!QFile::rename(path, newPath))
636  return false;
637  path = newPath;
638  return open(path, currentHexKey);
639 }
640 
647 {
648  if (!sqlite) {
649  qWarning() << "Trying to remove the database, but it is not open";
650  return false;
651  }
652 
653  if (QThread::currentThread() != workerThread.get()) {
654  bool ret;
655  QMetaObject::invokeMethod(this, "remove", Qt::BlockingQueuedConnection,
656  Q_RETURN_ARG(bool, ret));
657  return ret;
658  }
659 
660  qDebug() << "Removing database " << path;
661  close();
662  return QFile::remove(path);
663 }
664 
672 {
673  void operator()(Tox_Pass_Key* pass_key)
674  {
675  tox_pass_key_free(pass_key);
676  }
677 };
678 
685 QString RawDatabase::deriveKey(const QString& password)
686 {
687  if (password.isEmpty())
688  return {};
689 
690  const QByteArray passData = password.toUtf8();
691 
692  static_assert(TOX_PASS_KEY_LENGTH >= 32, "toxcore must provide 256bit or longer keys");
693 
694  static const uint8_t expandConstant[TOX_PASS_SALT_LENGTH + 1] =
695  "L'ignorance est le pire des maux";
696  const std::unique_ptr<Tox_Pass_Key, PassKeyDeleter> key(tox_pass_key_derive_with_salt(
697  reinterpret_cast<const uint8_t*>(passData.data()),
698  static_cast<std::size_t>(passData.size()), expandConstant, nullptr));
699  return QByteArray(reinterpret_cast<char*>(key.get()) + 32, 32).toHex();
700 }
701 
708 QString RawDatabase::deriveKey(const QString& password, const QByteArray& salt)
709 {
710  if (password.isEmpty()) {
711  return {};
712  }
713 
714  if (salt.length() != TOX_PASS_SALT_LENGTH) {
715  qWarning() << "Salt length doesn't match toxencryptsave expections";
716  return {};
717  }
718 
719  const QByteArray passData = password.toUtf8();
720 
721  static_assert(TOX_PASS_KEY_LENGTH >= 32, "toxcore must provide 256bit or longer keys");
722 
723  const std::unique_ptr<Tox_Pass_Key, PassKeyDeleter> key(tox_pass_key_derive_with_salt(
724  reinterpret_cast<const uint8_t*>(passData.data()),
725  static_cast<std::size_t>(passData.size()),
726  reinterpret_cast<const uint8_t*>(salt.constData()), nullptr));
727  return QByteArray(reinterpret_cast<char*>(key.get()) + 32, 32).toHex();
728 }
729 
737 {
738  assert(QThread::currentThread() == workerThread.get());
739 
740  if (!sqlite)
741  return;
742 
743  forever
744  {
745  // Fetch the next transaction
746  Transaction trans;
747  {
748  QMutexLocker locker{&transactionsMutex};
749  if (pendingTransactions.isEmpty())
750  return;
751  trans = pendingTransactions.dequeue();
752  }
753 
754  // In case we exit early, prepare to signal errors
755  if (trans.success != nullptr)
756  trans.success->store(false, std::memory_order_release);
757 
758  // Add transaction commands if necessary
759  if (trans.queries.size() > 1) {
760  trans.queries.prepend({"BEGIN;"});
761  trans.queries.append({"COMMIT;"});
762  }
763 
764  // Compile queries
765  for (Query& query : trans.queries) {
766  assert(query.statements.isEmpty());
767  // sqlite3_prepare_v2 only compiles one statement at a time in the query,
768  // we need to loop over them all
769  int curParam = 0;
770  const char* compileTail = query.query.data();
771  do {
772  // Compile the next statement
773  sqlite3_stmt* stmt;
774  int r;
775  if ((r = sqlite3_prepare_v2(sqlite, compileTail,
776  query.query.size()
777  - static_cast<int>(compileTail - query.query.data()),
778  &stmt, &compileTail))
779  != SQLITE_OK) {
780  qWarning() << "Failed to prepare statement" << anonymizeQuery(query.query)
781  << "and returned" << r;
782  qWarning("The full error is %d: %s", sqlite3_errcode(sqlite), sqlite3_errmsg(sqlite));
783  goto cleanupStatements;
784  }
785  query.statements += stmt;
786 
787  // Now we can bind our params to this statement
788  int nParams = sqlite3_bind_parameter_count(stmt);
789  if (query.blobs.size() < curParam + nParams) {
790  qWarning() << "Not enough parameters to bind to query "
791  << anonymizeQuery(query.query);
792  goto cleanupStatements;
793  }
794  for (int i = 0; i < nParams; ++i) {
795  const QByteArray& blob = query.blobs[curParam + i];
796 #pragma GCC diagnostic push
797 #pragma GCC diagnostic ignored "-Wold-style-cast"
798 #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
799  // SQLITE_STATIC uses old-style cast and 0 as null pointer butcomes from system headers, so can't
800  // be fixed by us
801  auto sqliteDataType = SQLITE_STATIC;
802 #pragma GCC diagnostic pop
803  if (sqlite3_bind_blob(stmt, i + 1, blob.data(), blob.size(), sqliteDataType)
804  != SQLITE_OK) {
805  qWarning() << "Failed to bind param" << curParam + i << "to query"
806  << anonymizeQuery(query.query);
807  goto cleanupStatements;
808  }
809  }
810  curParam += nParams;
811  } while (compileTail != query.query.data() + query.query.size());
812 
813 
814  // Execute each statement of each query of our transaction
815  for (sqlite3_stmt* stmt : query.statements) {
816  int column_count = sqlite3_column_count(stmt);
817  int result;
818  do {
819  result = sqlite3_step(stmt);
820 
821  // Execute our row callback
822  if (result == SQLITE_ROW && query.rowCallback) {
823  QVector<QVariant> row;
824  for (int i = 0; i < column_count; ++i)
825  row += extractData(stmt, i);
826 
827  query.rowCallback(row);
828  }
829  } while (result == SQLITE_ROW);
830 
831  if (result == SQLITE_DONE)
832  continue;
833 
834  QString anonQuery = anonymizeQuery(query.query);
835  switch (result) {
836  case SQLITE_ERROR:
837  qWarning() << "Error executing query" << anonQuery;
838  goto cleanupStatements;
839  case SQLITE_MISUSE:
840  qWarning() << "Misuse executing query" << anonQuery;
841  goto cleanupStatements;
842  case SQLITE_CONSTRAINT:
843  qWarning() << "Constraint error executing query" << anonQuery;
844  goto cleanupStatements;
845  default:
846  qWarning() << "Unknown error" << result << "executing query" << anonQuery;
847  goto cleanupStatements;
848  }
849  }
850 
851  if (query.insertCallback)
852  query.insertCallback(RowId{sqlite3_last_insert_rowid(sqlite)});
853  }
854 
855  if (trans.success != nullptr)
856  trans.success->store(true, std::memory_order_release);
857 
858  // Free our statements
859  cleanupStatements:
860  for (Query& query : trans.queries) {
861  for (sqlite3_stmt* stmt : query.statements)
862  sqlite3_finalize(stmt);
863  query.statements.clear();
864  }
865 
866  // Signal transaction results
867  if (trans.done != nullptr)
868  trans.done->store(true, std::memory_order_release);
869  }
870 }
871 
877 QString RawDatabase::anonymizeQuery(const QByteArray& query)
878 {
879  QString queryString(query);
880  queryString.replace(QRegularExpression("chat.public_key='[A-F0-9]{64}'"),
881  "char.public_key='<HERE IS PUBLIC KEY>'");
882  queryString.replace(QRegularExpression("timestamp BETWEEN \\d{5,} AND \\d{5,}"),
883  "timestamp BETWEEN <START HERE> AND <END HERE>");
884 
885  return queryString;
886 }
887 
894 QVariant RawDatabase::extractData(sqlite3_stmt* stmt, int col)
895 {
896  int type = sqlite3_column_type(stmt, col);
897  if (type == SQLITE_INTEGER) {
898  return sqlite3_column_int64(stmt, col);
899  } else if (type == SQLITE_TEXT) {
900  const char* str = reinterpret_cast<const char*>(sqlite3_column_text(stmt, col));
901  int len = sqlite3_column_bytes(stmt, col);
902  return QString::fromUtf8(str, len);
903  } else if (type == SQLITE_NULL) {
904  return QVariant{};
905  } else {
906  const char* data = reinterpret_cast<const char*>(sqlite3_column_blob(stmt, col));
907  int len = sqlite3_column_bytes(stmt, col);
908  return QByteArray::fromRawData(data, len);
909  }
910 }
911 
918 void RawDatabase::regexpInsensitive(sqlite3_context* ctx, int argc, sqlite3_value** argv)
919 {
920  regexp(ctx, argc, argv, QRegularExpression::CaseInsensitiveOption | QRegularExpression::UseUnicodePropertiesOption);
921 }
922 
929 void RawDatabase::regexpSensitive(sqlite3_context* ctx, int argc, sqlite3_value** argv)
930 {
931  regexp(ctx, argc, argv, QRegularExpression::UseUnicodePropertiesOption);
932 }
933 
934 void RawDatabase::regexp(sqlite3_context* ctx, int argc, sqlite3_value** argv, const QRegularExpression::PatternOptions cs)
935 {
936  QRegularExpression regex;
937  const QString str1(reinterpret_cast<const char*>(sqlite3_value_text(argv[0])));
938  const QString str2(reinterpret_cast<const char*>(sqlite3_value_text(argv[1])));
939 
940  regex.setPattern(str1);
941  regex.setPatternOptions(cs);
942 
943  const bool b = str2.contains(regex);
944 
945  if (b) {
946  sqlite3_result_int(ctx, 1);
947  } else {
948  sqlite3_result_int(ctx, 0);
949  }
950 }
RawDatabase::path
QString path
Definition: rawdatabase.h:175
RawDatabase::Query
Definition: rawdatabase.h:57
RawDatabase::rename
bool rename(const QString &newPath)
Moves the database file on disk to match the new path.
Definition: rawdatabase.cpp:612
RawDatabase::Query::insertCallback
std::function< void(RowId)> insertCallback
Called after execution with the last insert rowid.
Definition: rawdatabase.h:82
RawDatabase::decryptDatabase
bool decryptDatabase()
Definition: rawdatabase.cpp:569
RawDatabase::setPassword
bool setPassword(const QString &password)
Changes the database password, encrypting or decrypting if necessary.
Definition: rawdatabase.cpp:493
RawDatabase::readSavedCipherParams
SqlCipherParams readSavedCipherParams(const QString &hexKey, SqlCipherParams newParams)
Definition: rawdatabase.cpp:326
RawDatabase::open
bool open(const QString &path, const QString &hexKey={})
Tries to open the database with the given (possibly empty) key.
Definition: rawdatabase.cpp:143
RowId
NamedType< int64_t, struct RowIdTag, Orderable > RowId
Definition: rawdatabase.h:49
RawDatabase::Transaction
Definition: rawdatabase.h:163
RawDatabase::Query::query
QByteArray query
UTF-8 query string.
Definition: rawdatabase.h:80
RawDatabase::execLater
void execLater(const QString &statement)
Executes a SQL transaction asynchronously.
Definition: rawdatabase.cpp:452
RawDatabase::encryptDatabase
bool encryptDatabase(const QString &newHexKey)
Definition: rawdatabase.cpp:543
RawDatabase::process
void process()
Implements the actual processing of pending transactions. Unqueues, compiles, binds and executes quer...
Definition: rawdatabase.cpp:736
RawDatabase::Query::blobs
QVector< QByteArray > blobs
Bound data blobs.
Definition: rawdatabase.h:81
RawDatabase::remove
bool remove()
Deletes the on disk database file after closing it.
Definition: rawdatabase.cpp:646
RawDatabase::RawDatabase
RawDatabase(const QString &path, const QString &password, const QByteArray &salt)
Tries to open a database.
Definition: rawdatabase.cpp:87
RawDatabase::updateSavedCipherParameters
bool updateSavedCipherParameters(const QString &hexKey, SqlCipherParams newParams)
Changes stored db encryption from SQLCipher 3.x defaults to 4.x defaults.
Definition: rawdatabase.cpp:219
RawDatabase::Transaction::success
std::atomic_bool * success
Definition: rawdatabase.h:166
RawDatabase::setCipherParameters
bool setCipherParameters(SqlCipherParams params, const QString &database={})
Definition: rawdatabase.cpp:254
RawDatabase::toString
static QString toString(SqlCipherParams params)
Definition: rawdatabase.h:114
Transaction
SQL transactions to be processed.
RawDatabase::sync
void sync()
Waits until all the pending transactions are executed.
Definition: rawdatabase.cpp:482
RawDatabase::close
void close()
Close the database and free its associated resources.
Definition: rawdatabase.cpp:371
RawDatabase::SqlCipherParams
SqlCipherParams
Definition: rawdatabase.h:90
RawDatabase::setKey
bool setKey(const QString &hexKey)
Definition: rawdatabase.cpp:346
RawDatabase::regexpSensitive
static void regexpSensitive(sqlite3_context *ctx, int argc, sqlite3_value **argv)
Use for create function in db for search data use regular experessions without case sensitive.
Definition: rawdatabase.cpp:929
RawDatabase::workerThread
std::unique_ptr< QThread > workerThread
Definition: rawdatabase.h:172
RawDatabase::Transaction::done
std::atomic_bool * done
Definition: rawdatabase.h:167
RawDatabase::getUserVersion
int getUserVersion()
Definition: rawdatabase.cpp:356
RawDatabase::regexpInsensitive
static void regexpInsensitive(sqlite3_context *ctx, int argc, sqlite3_value **argv)
Use for create function in db for search data use regular experessions without case sensitive.
Definition: rawdatabase.cpp:918
RawDatabase::Query::statements
QVector< sqlite3_stmt * > statements
Statements to be compiled from the query.
Definition: rawdatabase.h:84
RawDatabase::extractData
static QVariant extractData(sqlite3_stmt *stmt, int col)
Extracts a variant from one column of a result row depending on the column type.
Definition: rawdatabase.cpp:894
RawDatabase::sqlite
sqlite3 * sqlite
Definition: rawdatabase.h:171
RawDatabase::SqlCipherParams::p3_0
@ p3_0
RawDatabase::pendingTransactions
QQueue< Transaction > pendingTransactions
Definition: rawdatabase.h:173
RawDatabase::currentHexKey
QString currentHexKey
Definition: rawdatabase.h:177
RawDatabase::Transaction::queries
QVector< Query > queries
Definition: rawdatabase.h:165
RawDatabase::~RawDatabase
~RawDatabase()
Definition: rawdatabase.cpp:129
PassKeyDeleter
Functor used to free tox_pass_key memory.
Definition: rawdatabase.cpp:671
RawDatabase::testUsable
bool testUsable()
Definition: rawdatabase.cpp:210
RawDatabase::openEncryptedDatabaseAtLatestSupportedVersion
bool openEncryptedDatabaseAtLatestSupportedVersion(const QString &hexKey)
Definition: rawdatabase.cpp:186
RawDatabase::highestSupportedParams
SqlCipherParams highestSupportedParams()
Definition: rawdatabase.cpp:297
RawDatabase::regexp
static void regexp(sqlite3_context *ctx, int argc, sqlite3_value **argv, const QRegularExpression::PatternOptions cs)
Definition: rawdatabase.cpp:934
RawDatabase::deriveKey
static QString deriveKey(const QString &password, const QByteArray &salt)
Derives a 256bit key from the password and returns it hex-encoded.
Definition: rawdatabase.cpp:708
RawDatabase::execNow
bool execNow(const QString &statement)
Executes a SQL transaction synchronously.
Definition: rawdatabase.cpp:400
RawDatabase::anonymizeQuery
QString anonymizeQuery(const QByteArray &query)
Hides public keys and timestamps in query.
Definition: rawdatabase.cpp:877
rawdatabase.h
RawDatabase::currentSalt
QByteArray currentSalt
Definition: rawdatabase.h:176
RawDatabase::transactionsMutex
QMutex transactionsMutex
Protects pendingTransactions.
Definition: rawdatabase.h:174
RawDatabase::commitDbSwap
bool commitDbSwap(const QString &hexKey)
Definition: rawdatabase.cpp:589
PassKeyDeleter::operator()
void operator()(Tox_Pass_Key *pass_key)
Definition: rawdatabase.cpp:673
RawDatabase::SqlCipherParams::halfUpgradedTo4
@ halfUpgradedTo4
RawDatabase::Query::rowCallback
std::function< void(const QVector< QVariant > &)> rowCallback
Called during execution for each row.
Definition: rawdatabase.h:83
RawDatabase::isOpen
bool isOpen()
Checks, that the database is open.
Definition: rawdatabase.cpp:389
RawDatabase::SqlCipherParams::p4_0
@ p4_0