diff --git a/resources/sql/db_init_sqlite.sql b/resources/sql/db_init_sqlite.sql index 96513ef2d..8465e6eb9 100644 --- a/resources/sql/db_init_sqlite.sql +++ b/resources/sql/db_init_sqlite.sql @@ -1,100 +1,44 @@ -DROP TABLE IF EXISTS Information; --- ! -CREATE TABLE IF NOT EXISTS Information ( - id INTEGER PRIMARY KEY, - inf_key TEXT NOT NULL, - inf_value TEXT NOT NULL +CREATE TABLE Information ( + inf_key TEXT NOT NULL UNIQUE CHECK (inf_key != ''), + inf_value TEXT ); -- ! -INSERT INTO Information VALUES (1, 'schema_version', '1'); +INSERT INTO Information VALUES ('schema_version', '1'); -- ! -CREATE TABLE IF NOT EXISTS Accounts ( +CREATE TABLE Accounts ( id INTEGER PRIMARY KEY, - type TEXT NOT NULL CHECK (type != ''), - proxy_type INTEGER NOT NULL CHECK (proxy_type >= 0) DEFAULT 0, + type TEXT NOT NULL CHECK (type != ''), /* ID of the account type. Each account defines its own, for example 'ttrss'. */ + proxy_type INTEGER NOT NULL DEFAULT 0 CHECK (proxy_type >= 0), proxy_host TEXT, proxy_port INTEGER, proxy_username TEXT, - proxy_password TEXT + proxy_password TEXT, + /* Custom attributes dynamically mapped to actual account data fields. */ + custom_data_1 TEXT, + custom_data_2 TEXT, + custom_data_3 TEXT, + custom_data_4 TEXT, + custom_data_5 TEXT, + custom_data_6 TEXT, + custom_data_7 TEXT, + custom_data_8 TEXT, + custom_data_9 TEXT, + custom_data_10 TEXT, + custom_data_11 TEXT, + custom_data_12 TEXT, + custom_data_13 TEXT, + custom_data_14 TEXT, + custom_data_15 TEXT, + custom_data_16 TEXT, + custom_data_17 TEXT, + custom_data_18 TEXT, + custom_data_19 TEXT, + custom_data_20 TEXT ); -- ! -CREATE TABLE IF NOT EXISTS TtRssAccounts ( - id INTEGER, - username TEXT NOT NULL, - password TEXT, - auth_protected INTEGER(1) NOT NULL CHECK (auth_protected >= 0 AND auth_protected <= 1) DEFAULT 0, - auth_username TEXT, - auth_password TEXT, - url TEXT NOT NULL, - force_update INTEGER(1) NOT NULL CHECK (force_update >= 0 AND force_update <= 1) DEFAULT 0, - update_only_unread INTEGER(1) NOT NULL CHECK (update_only_unread >= 0 AND update_only_unread <= 1) DEFAULT 0, - - FOREIGN KEY (id) REFERENCES Accounts (id) -); --- ! -CREATE TABLE IF NOT EXISTS OwnCloudAccounts ( - id INTEGER, - username TEXT NOT NULL, - password TEXT, - url TEXT NOT NULL, - force_update INTEGER(1) NOT NULL CHECK (force_update >= 0 AND force_update <= 1) DEFAULT 0, - msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1), - update_only_unread INTEGER(1) NOT NULL CHECK (update_only_unread >= 0 AND update_only_unread <= 1) DEFAULT 0, - - FOREIGN KEY (id) REFERENCES Accounts (id) -); --- ! -CREATE TABLE IF NOT EXISTS InoreaderAccounts ( - id INTEGER, - username TEXT NOT NULL, - app_id TEXT, - app_key TEXT, - redirect_url TEXT, - refresh_token TEXT, - msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1), - - FOREIGN KEY (id) REFERENCES Accounts (id) -); --- ! -CREATE TABLE IF NOT EXISTS GmailAccounts ( - id INTEGER, - username TEXT NOT NULL, - app_id TEXT, - app_key TEXT, - redirect_url TEXT, - refresh_token TEXT, - msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1), - - FOREIGN KEY (id) REFERENCES Accounts (id) -); --- ! -CREATE TABLE IF NOT EXISTS GoogleReaderApiAccounts ( - id INTEGER, - type INTEGER NOT NULL CHECK (type >= 1), - username TEXT NOT NULL, - password TEXT, - url TEXT NOT NULL, - msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1), - - FOREIGN KEY (id) REFERENCES Accounts (id) -); --- ! -CREATE TABLE IF NOT EXISTS FeedlyAccounts ( - id INTEGER, - username TEXT NOT NULL, - developer_access_token TEXT, - refresh_token TEXT, - msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1), - update_only_unread INTEGER(1) NOT NULL DEFAULT 0 CHECK (update_only_unread >= 0 AND update_only_unread <= 1), - - FOREIGN KEY (id) REFERENCES Accounts (id) -); --- ! -DROP TABLE IF EXISTS Categories; --- ! -CREATE TABLE IF NOT EXISTS Categories ( +CREATE TABLE Categories ( id INTEGER PRIMARY KEY, - parent_id INTEGER NOT NULL, + parent_id INTEGER NOT NULL CHECK (parent_id >= -1), /* Root categories contain -1 here. */ title TEXT NOT NULL CHECK (title != ''), description TEXT, date_created INTEGER, @@ -105,15 +49,13 @@ CREATE TABLE IF NOT EXISTS Categories ( FOREIGN KEY (account_id) REFERENCES Accounts (id) ); -- ! -DROP TABLE IF EXISTS Feeds; --- ! -CREATE TABLE IF NOT EXISTS Feeds ( +CREATE TABLE Feeds ( id INTEGER PRIMARY KEY, title TEXT NOT NULL CHECK (title != ''), description TEXT, date_created INTEGER, icon BLOB, - category INTEGER NOT NULL CHECK (category >= -1), + category INTEGER NOT NULL CHECK (category >= -1), /* Root feeds contain -1 here. */ encoding TEXT, source_type INTEGER, url TEXT, @@ -122,7 +64,7 @@ CREATE TABLE IF NOT EXISTS Feeds ( username TEXT, password TEXT, update_type INTEGER(1) NOT NULL CHECK (update_type >= 0), - update_interval INTEGER NOT NULL CHECK (update_interval >= 1) DEFAULT 15, + update_interval INTEGER NOT NULL DEFAULT 15 CHECK (update_interval >= 1), type INTEGER, account_id INTEGER NOT NULL, custom_id TEXT, @@ -130,20 +72,18 @@ CREATE TABLE IF NOT EXISTS Feeds ( FOREIGN KEY (account_id) REFERENCES Accounts (id) ); -- ! -DROP TABLE IF EXISTS Messages; --- ! -CREATE TABLE IF NOT EXISTS Messages ( +CREATE TABLE Messages ( id INTEGER PRIMARY KEY, - is_read INTEGER(1) NOT NULL CHECK (is_read >= 0 AND is_read <= 1) DEFAULT 0, - is_deleted INTEGER(1) NOT NULL CHECK (is_deleted >= 0 AND is_deleted <= 1) DEFAULT 0, - is_important INTEGER(1) NOT NULL CHECK (is_important >= 0 AND is_important <= 1) DEFAULT 0, + is_read INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_read >= 0 AND is_read <= 1), + is_deleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_deleted >= 0 AND is_deleted <= 1), + is_important INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_important >= 0 AND is_important <= 1), feed TEXT NOT NULL, title TEXT NOT NULL CHECK (title != ''), url TEXT, author TEXT, date_created INTEGER NOT NULL CHECK (date_created != 0), contents TEXT, - is_pdeleted INTEGER(1) NOT NULL CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1) DEFAULT 0, + is_pdeleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1), enclosures TEXT, account_id INTEGER NOT NULL, custom_id TEXT, @@ -152,13 +92,13 @@ CREATE TABLE IF NOT EXISTS Messages ( FOREIGN KEY (account_id) REFERENCES Accounts (id) ); -- ! -CREATE TABLE IF NOT EXISTS MessageFilters ( +CREATE TABLE MessageFilters ( id INTEGER PRIMARY KEY, name TEXT NOT NULL CHECK (name != ''), script TEXT NOT NULL CHECK (script != '') ); -- ! -CREATE TABLE IF NOT EXISTS MessageFiltersInFeeds ( +CREATE TABLE MessageFiltersInFeeds ( filter INTEGER NOT NULL, feed_custom_id TEXT NOT NULL, account_id INTEGER NOT NULL, @@ -167,7 +107,7 @@ CREATE TABLE IF NOT EXISTS MessageFiltersInFeeds ( FOREIGN KEY (account_id) REFERENCES Accounts (id) ON DELETE CASCADE ); -- ! -CREATE TABLE IF NOT EXISTS Labels ( +CREATE TABLE Labels ( id INTEGER PRIMARY KEY, name TEXT NOT NULL CHECK (name != ''), color VARCHAR(7), @@ -177,7 +117,7 @@ CREATE TABLE IF NOT EXISTS Labels ( FOREIGN KEY (account_id) REFERENCES Accounts (id) ); -- ! -CREATE TABLE IF NOT EXISTS LabelsInMessages ( +CREATE TABLE LabelsInMessages ( label TEXT NOT NULL, /* Custom ID of label. */ message TEXT NOT NULL, /* Custom ID of message. */ account_id INTEGER NOT NULL, diff --git a/src/librssguard/miscellaneous/databasequeries.cpp b/src/librssguard/miscellaneous/databasequeries.cpp index 326f2c7f9..1c4d69941 100755 --- a/src/librssguard/miscellaneous/databasequeries.cpp +++ b/src/librssguard/miscellaneous/databasequeries.cpp @@ -6,7 +6,6 @@ #include "exceptions/applicationexception.h" #include "miscellaneous/application.h" #include "miscellaneous/iconfactory.h" -#include "miscellaneous/textfactory.h" #include "network-web/oauth2service.h" #include "services/abstract/category.h" #include "services/feedly/definitions.h" @@ -1682,130 +1681,6 @@ void DatabaseQueries::fillBaseAccountData(const QSqlDatabase& db, ServiceRoot* a } } -QList DatabaseQueries::getGreaderAccounts(const QSqlDatabase& db, bool* ok) { - QSqlQuery query(db); - QList roots; - - if (query.exec("SELECT * FROM GoogleReaderApiAccounts;")) { - while (query.next()) { - auto* root = new GreaderServiceRoot(); - - root->setId(query.value(0).toInt()); - root->setAccountId(query.value(0).toInt()); - root->network()->setService(GreaderServiceRoot::Service(query.value(1).toInt())); - root->network()->setUsername(query.value(2).toString()); - root->network()->setPassword(TextFactory::decrypt(query.value(3).toString())); - root->network()->setBaseUrl(query.value(4).toString()); - root->network()->setBatchSize(query.value(5).toInt()); - root->updateTitleIcon(); - - fillBaseAccountData(db, root); - - roots.append(root); - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - qWarningNN << LOGSEC_GREADER - << "Getting list of activated accounts failed: '" - << query.lastError().text() - << "'."; - - if (ok != nullptr) { - *ok = false; - } - } - - return roots; -} - -QList DatabaseQueries::getOwnCloudAccounts(const QSqlDatabase& db, bool* ok) { - QSqlQuery query(db); - QList roots; - - if (query.exec("SELECT * FROM OwnCloudAccounts;")) { - while (query.next()) { - auto* root = new OwnCloudServiceRoot(); - - root->setId(query.value(0).toInt()); - root->setAccountId(query.value(0).toInt()); - root->network()->setAuthUsername(query.value(1).toString()); - root->network()->setAuthPassword(TextFactory::decrypt(query.value(2).toString())); - root->network()->setUrl(query.value(3).toString()); - root->network()->setForceServerSideUpdate(query.value(4).toBool()); - root->network()->setBatchSize(query.value(5).toInt()); - root->network()->setDownloadOnlyUnreadMessages(query.value(6).toBool()); - root->updateTitle(); - - fillBaseAccountData(db, root); - - roots.append(root); - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - qWarningNN << LOGSEC_NEXTCLOUD - << "Getting list of activated accounts failed: '" - << query.lastError().text() - << "'."; - - if (ok != nullptr) { - *ok = false; - } - } - - return roots; -} - -QList DatabaseQueries::getTtRssAccounts(const QSqlDatabase& db, bool* ok) { - QSqlQuery query(db); - QList roots; - - if (query.exec("SELECT * FROM TtRssAccounts;")) { - while (query.next()) { - auto* root = new TtRssServiceRoot(); - - root->setId(query.value(0).toInt()); - root->setAccountId(query.value(0).toInt()); - root->network()->setUsername(query.value(1).toString()); - root->network()->setPassword(TextFactory::decrypt(query.value(2).toString())); - root->network()->setAuthIsUsed(query.value(3).toBool()); - root->network()->setAuthUsername(query.value(4).toString()); - root->network()->setAuthPassword(TextFactory::decrypt(query.value(5).toString())); - root->network()->setUrl(query.value(6).toString()); - root->network()->setForceServerSideUpdate(query.value(7).toBool()); - root->network()->setDownloadOnlyUnreadMessages(query.value(8).toBool()); - root->updateTitle(); - - fillBaseAccountData(db, root); - - roots.append(root); - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - qWarningNN << LOGSEC_TTRSS - << "Getting list of activated accounts failed: '" - << query.lastError().text() - << "'."; - - if (ok != nullptr) { - *ok = false; - } - } - - return roots; -} - bool DatabaseQueries::deleteOwnCloudAccount(const QSqlDatabase& db, int account_id) { QSqlQuery q(db); @@ -1978,6 +1853,63 @@ bool DatabaseQueries::createOwnCloudAccount(const QSqlDatabase& db, int id_to_as } } +void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot* account) { + QSqlQuery q(db); + + if (account->accountId() <= 0) { + // We need to insert account first. + q.prepare(QSL("INSERT INTO Accounts (type) VALUES (:type);")); + q.bindValue(QSL(":type"), account->code()); + + if (!q.exec()) { + throw ApplicationException(q.lastError().text()); + } + else { + account->setId(q.lastInsertId().toInt()); + account->setAccountId(account->id()); + } + } + + // Now we construct the SQL update query. + auto proxy = account->networkProxy(); + QString sql_statement = QSL("UPDATE Accounts " + "SET proxy_type = :proxy_type, proxy_host = :proxy_host, proxy_port = :proxy_port, " + " proxy_username = :proxy_username, proxy_password = :proxy_password%1 " + "WHERE id = :id"); + auto custom_attributes = account->customDatabaseAttributes(); + QStringList custom_sql_clauses; + + for (int i = 0; i < custom_attributes.size(); i++) { + QString target_data = account->property(custom_attributes.at(i).m_name.toLocal8Bit()).toString(); + + if (custom_attributes.at(i).m_encrypted) { + target_data = TextFactory::encrypt(target_data); + } + + custom_sql_clauses.append(QSL("custom_data_%1 = '%2'").arg(QString::number(i + 1), + target_data)); + } + + if (!custom_sql_clauses.isEmpty()) { + sql_statement = sql_statement.arg(QSL(", ") + custom_sql_clauses.join(QSL(", "))); + } + else { + sql_statement = sql_statement.arg(QString()); + } + + q.prepare(sql_statement); + q.bindValue(QSL(":proxy_type"), proxy.type()); + q.bindValue(QSL(":proxy_host"), proxy.hostName()); + q.bindValue(QSL(":proxy_port"), proxy.port()); + q.bindValue(QSL(":proxy_username"), proxy.user()); + q.bindValue(QSL(":proxy_password"), TextFactory::encrypt(proxy.password())); + q.bindValue(QSL(":id"), account->accountId()); + + if (!q.exec()) { + throw ApplicationException(q.lastError().text()); + } +} + int DatabaseQueries::createBaseAccount(const QSqlDatabase& db, const QString& code, bool* ok) { QSqlQuery q(db); @@ -2478,38 +2410,6 @@ void DatabaseQueries::removeMessageFilterFromFeed(const QSqlDatabase& db, const } } -QList DatabaseQueries::getStandardAccounts(const QSqlDatabase& db, bool* ok) { - QSqlQuery q(db); - QList roots; - - q.setForwardOnly(true); - q.prepare(QSL("SELECT id FROM Accounts WHERE type = :type;")); - q.bindValue(QSL(":type"), SERVICE_CODE_STD_RSS); - - if (q.exec()) { - while (q.next()) { - auto* root = new StandardServiceRoot(); - - root->setAccountId(q.value(0).toInt()); - - fillBaseAccountData(db, root); - - roots.append(root); - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - if (ok != nullptr) { - *ok = false; - } - } - - return roots; -} - bool DatabaseQueries::deleteFeedlyAccount(const QSqlDatabase& db, int account_id) { QSqlQuery q(db); @@ -2624,91 +2524,6 @@ QStringList DatabaseQueries::getAllRecipients(const QSqlDatabase& db, int accoun return rec; } -QList DatabaseQueries::getFeedlyAccounts(const QSqlDatabase& db, bool* ok) { - QSqlQuery query(db); - QList roots; - - if (query.exec("SELECT * FROM FeedlyAccounts;")) { - while (query.next()) { - auto* root = new FeedlyServiceRoot(); - - root->setId(query.value(0).toInt()); - root->setAccountId(query.value(0).toInt()); - root->network()->setUsername(query.value(1).toString()); - root->network()->setDeveloperAccessToken(query.value(2).toString()); - -#if defined(FEEDLY_OFFICIAL_SUPPORT) - root->network()->oauth()->setRefreshToken(query.value(3).toString()); -#endif - - root->network()->setBatchSize(query.value(4).toInt()); - root->network()->setDownloadOnlyUnreadMessages(query.value(5).toBool()); - root->updateTitle(); - - fillBaseAccountData(db, root); - - roots.append(root); - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - qWarningNN << LOGSEC_GMAIL - << "Getting list of activated accounts failed: '" - << query.lastError().text() - << "'."; - - if (ok != nullptr) { - *ok = false; - } - } - - return roots; -} - -QList DatabaseQueries::getGmailAccounts(const QSqlDatabase& db, bool* ok) { - QSqlQuery query(db); - QList roots; - - if (query.exec("SELECT * FROM GmailAccounts;")) { - while (query.next()) { - auto* root = new GmailServiceRoot(); - - root->setId(query.value(0).toInt()); - root->setAccountId(query.value(0).toInt()); - root->network()->setUsername(query.value(1).toString()); - root->network()->oauth()->setClientId(query.value(2).toString()); - root->network()->oauth()->setClientSecret(query.value(3).toString()); - root->network()->oauth()->setRefreshToken(query.value(5).toString()); - root->network()->oauth()->setRedirectUrl(query.value(4).toString()); - root->network()->setBatchSize(query.value(6).toInt()); - root->updateTitle(); - - fillBaseAccountData(db, root); - - roots.append(root); - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - qWarningNN << LOGSEC_GMAIL - << "Getting list of activated accounts failed: '" - << query.lastError().text() - << "'."; - - if (ok != nullptr) { - *ok = false; - } - } - - return roots; -} - bool DatabaseQueries::deleteGmailAccount(const QSqlDatabase& db, int account_id) { QSqlQuery q(db); @@ -2747,47 +2562,6 @@ bool DatabaseQueries::storeNewOauthTokens(const QSqlDatabase& db, const QString& } } -QList DatabaseQueries::getInoreaderAccounts(const QSqlDatabase& db, bool* ok) { - QSqlQuery query(db); - QList roots; - - if (query.exec("SELECT * FROM InoreaderAccounts;")) { - while (query.next()) { - auto* root = new InoreaderServiceRoot(); - - root->setId(query.value(0).toInt()); - root->setAccountId(query.value(0).toInt()); - root->network()->setUsername(query.value(1).toString()); - root->network()->oauth()->setClientId(query.value(2).toString()); - root->network()->oauth()->setClientSecret(query.value(3).toString()); - root->network()->oauth()->setRefreshToken(query.value(5).toString()); - root->network()->oauth()->setRedirectUrl(query.value(4).toString()); - root->network()->setBatchSize(query.value(6).toInt()); - root->updateTitle(); - - fillBaseAccountData(db, root); - - roots.append(root); - } - - if (ok != nullptr) { - *ok = true; - } - } - else { - qWarningNN << LOGSEC_INOREADER - << "Getting list of activated accounts failed: '" - << query.lastError().text() - << "'."; - - if (ok != nullptr) { - *ok = false; - } - } - - return roots; -} - bool DatabaseQueries::overwriteGmailAccount(const QSqlDatabase& db, const QString& username, const QString& app_id, const QString& app_key, const QString& redirect_url, const QString& refresh_token, int batch_size, int account_id) { diff --git a/src/librssguard/miscellaneous/databasequeries.h b/src/librssguard/miscellaneous/databasequeries.h index 5b7afc59d..7f300c719 100644 --- a/src/librssguard/miscellaneous/databasequeries.h +++ b/src/librssguard/miscellaneous/databasequeries.h @@ -6,6 +6,7 @@ #include "services/abstract/rootitem.h" #include "core/messagefilter.h" +#include "miscellaneous/textfactory.h" #include "services/abstract/category.h" #include "services/abstract/label.h" #include "services/abstract/serviceroot.h" @@ -89,9 +90,12 @@ class DatabaseQueries { bool* ok = nullptr); // Common account methods. + template + static QList getAccounts(const QSqlDatabase& db, const QString& code, bool* ok = nullptr); static bool storeNewOauthTokens(const QSqlDatabase& db, const QString& table_name, const QString& refresh_token, int account_id); static void fillBaseAccountData(const QSqlDatabase& db, ServiceRoot* account, bool* ok = nullptr); + static void createOverwriteAccount(const QSqlDatabase& db, ServiceRoot* account); static int createBaseAccount(const QSqlDatabase& db, const QString& code, bool* ok = nullptr); static void editBaseAccount(const QSqlDatabase& db, ServiceRoot* account, bool* ok = nullptr); static int updateMessages(QSqlDatabase db, const QList& messages, const QString& feed_custom_id, @@ -147,14 +151,12 @@ class DatabaseQueries { const QString& username, const QString& password, Feed::AutoUpdateType auto_update_type, int auto_update_interval, StandardFeed::SourceType source_type, const QString& post_process_script, StandardFeed::Type feed_format); - static QList getStandardAccounts(const QSqlDatabase& db, bool* ok = nullptr); template static void fillFeedData(T* feed, const QSqlRecord& sql_record); // Feedly account. static bool deleteFeedlyAccount(const QSqlDatabase& db, int account_id); - static QList getFeedlyAccounts(const QSqlDatabase& db, bool* ok = nullptr); static bool createFeedlyAccount(const QSqlDatabase& db, const QString& username, const QString& developer_access_token, @@ -172,7 +174,6 @@ class DatabaseQueries { // Greader account. static bool deleteGreaderAccount(const QSqlDatabase& db, int account_id); - static QList getGreaderAccounts(const QSqlDatabase& db, bool* ok = nullptr); static bool createGreaderAccount(const QSqlDatabase& db, int id_to_assign, const QString& username, const QString& password, GreaderServiceRoot::Service service, const QString& url, int batch_size); @@ -181,7 +182,6 @@ class DatabaseQueries { int account_id); // Nextcloud account. - static QList getOwnCloudAccounts(const QSqlDatabase& db, bool* ok = nullptr); static bool deleteOwnCloudAccount(const QSqlDatabase& db, int account_id); static bool overwriteOwnCloudAccount(const QSqlDatabase& db, const QString& username, const QString& password, const QString& url, bool force_server_side_feed_update, int batch_size, @@ -191,7 +191,6 @@ class DatabaseQueries { bool download_only_unread_messages, int batch_size); // TT-RSS acccount. - static QList getTtRssAccounts(const QSqlDatabase& db, bool* ok = nullptr); static bool deleteTtRssAccount(const QSqlDatabase& db, int account_id); static bool overwriteTtRssAccount(const QSqlDatabase& db, const QString& username, const QString& password, bool auth_protected, const QString& auth_username, const QString& auth_password, @@ -205,7 +204,6 @@ class DatabaseQueries { // Gmail account. static QStringList getAllRecipients(const QSqlDatabase& db, int account_id); static bool deleteGmailAccount(const QSqlDatabase& db, int account_id); - static QList getGmailAccounts(const QSqlDatabase& db, bool* ok = nullptr); static bool overwriteGmailAccount(const QSqlDatabase& db, const QString& username, const QString& app_id, const QString& app_key, const QString& redirect_url, const QString& refresh_token, int batch_size, int account_id); @@ -215,7 +213,6 @@ class DatabaseQueries { // Inoreader account. static bool deleteInoreaderAccount(const QSqlDatabase& db, int account_id); - static QList getInoreaderAccounts(const QSqlDatabase& db, bool* ok = nullptr); static bool overwriteInoreaderAccount(const QSqlDatabase& db, const QString& username, const QString& app_id, const QString& app_key, const QString& redirect_url, const QString& refresh_token, int batch_size, int account_id); @@ -241,6 +238,63 @@ inline void DatabaseQueries::fillFeedData(StandardFeed* feed, const QSqlRecord& Q_UNUSED(sql_record) } +template +QList DatabaseQueries::getAccounts(const QSqlDatabase& db, const QString& code, bool* ok) { + QSqlQuery query(db); + QList roots; + + if (query.exec(QSL("SELECT * FROM Accounts WHERE type = '%1';").arg(code))) { + while (query.next()) { + ServiceRoot* root = new T(); + + // Load common data. + root->setId(query.value(QSL("id")).toInt()); + root->setAccountId(root->id()); + + QNetworkProxy proxy(QNetworkProxy::ProxyType(query.value(QSL("proxy_type")).toInt()), + query.value(QSL("proxy_host")).toString(), + query.value(QSL("proxy_port")).toInt(), + query.value(QSL("proxy_username")).toString(), + TextFactory::decrypt(query.value(QSL("proxy_password")).toString())); + + root->setNetworkProxy(proxy); + + // Load account-specific custom data. + auto custom_attributes = root->customDatabaseAttributes(); + + for (int i = 0; i < custom_attributes.size(); i++) { + const QString target_db_attribute = QSL("custom_data_%1").arg(QString::number(i + 1)); + QString target_data = query.value(target_db_attribute).toString(); + + if (custom_attributes.at(i).m_encrypted) { + target_data = TextFactory::decrypt(target_data); + } + + root->setProperty(custom_attributes.at(i).m_name.toLocal8Bit(), target_data); + } + + roots.append(root); + } + + if (ok != nullptr) { + *ok = true; + } + } + else { + qWarningNN << LOGSEC_DB + << "Loading of accounts with code" + << QUOTE_W_SPACE(code) + << "failed with error:" + << QUOTE_W_SPACE_DOT(query.lastError().text()); + + if (ok != nullptr) { + *ok = false; + } + } + + return roots; +} + template Assignment DatabaseQueries::getCategories(const QSqlDatabase& db, int account_id, bool* ok) { Assignment categories; diff --git a/src/librssguard/services/abstract/gui/formaccountdetails.cpp b/src/librssguard/services/abstract/gui/formaccountdetails.cpp index b81e8bb27..b38dd5e2a 100644 --- a/src/librssguard/services/abstract/gui/formaccountdetails.cpp +++ b/src/librssguard/services/abstract/gui/formaccountdetails.cpp @@ -21,14 +21,16 @@ FormAccountDetails::FormAccountDetails(const QIcon& icon, QWidget* parent) void FormAccountDetails::apply() { QSqlDatabase database = qApp->database()->connection(QSL("FormAccountDetails")); - if (m_creatingNew) { - m_account->setAccountId(DatabaseQueries::createBaseAccount(database, m_account->code())); - } + /* + if (m_creatingNew) { + m_account->setAccountId(DatabaseQueries::createBaseAccount(database, m_account->code())); + } + */ m_account->setNetworkProxy(m_proxyDetails->proxy()); // NOTE: We edit account common attributes here directly. - DatabaseQueries::editBaseAccount(database, m_account); + //DatabaseQueries::editBaseAccount(database, m_account); } void FormAccountDetails::insertCustomTab(QWidget* custom_tab, const QString& title, int index) { diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp index 6ebd51d9f..2f31b9770 100644 --- a/src/librssguard/services/abstract/serviceroot.cpp +++ b/src/librssguard/services/abstract/serviceroot.cpp @@ -256,6 +256,10 @@ ServiceRoot::LabelOperation ServiceRoot::supportedLabelOperations() const { return LabelOperation::Adding | LabelOperation::Editing | LabelOperation::Deleting; } +QList ServiceRoot::customDatabaseAttributes() const { + return {}; +} + void ServiceRoot::itemChanged(const QList& items) { emit dataChanged(items); } @@ -393,7 +397,9 @@ void ServiceRoot::syncIn() { requestItemExpand({ this }, true); } -void ServiceRoot::performInitialAssembly(const Assignment& categories, const Assignment& feeds, const QList& labels) { +void ServiceRoot::performInitialAssembly(const Assignment& categories, + const Assignment& feeds, + const QList& labels) { // All data are now obtained, lets create the hierarchy. assembleCategories(categories); assembleFeeds(feeds); @@ -401,10 +407,12 @@ void ServiceRoot::performInitialAssembly(const Assignment& categories, const Ass // As the last item, add recycle bin, which is needed. appendChild(recycleBin()); appendChild(importantNode()); - appendChild(labelsNode()); - labelsNode()->loadLabels(labels); - requestItemExpand({ labelsNode() }, true); + if (labelsNode() != nullptr) { + appendChild(labelsNode()); + labelsNode()->loadLabels(labels); + requestItemExpand({ labelsNode() }, true); + } updateCounts(true); } diff --git a/src/librssguard/services/abstract/serviceroot.h b/src/librssguard/services/abstract/serviceroot.h index b7ee97768..1a4119008 100644 --- a/src/librssguard/services/abstract/serviceroot.h +++ b/src/librssguard/services/abstract/serviceroot.h @@ -24,6 +24,14 @@ typedef QList> Assignment; typedef QPair AssignmentItem; typedef QPair ImportanceChange; +struct CustomDatabaseEntry { + public: + CustomDatabaseEntry(const QString& name, bool encrypted = false) : m_name(name), m_encrypted(encrypted) {} + + QString m_name; + bool m_encrypted; +}; + // THIS IS the root node of the service. // NOTE: The root usually contains some core functionality of the // service like service account username/password etc. @@ -53,6 +61,7 @@ class ServiceRoot : public RootItem { virtual bool supportsFeedAdding() const; virtual bool supportsCategoryAdding() const; virtual LabelOperation supportedLabelOperations() const; + virtual QList customDatabaseAttributes() const; // Returns list of specific actions for "Add new item" main window menu. // So typical list of returned actions could look like: diff --git a/src/librssguard/services/feedly/feedlyentrypoint.cpp b/src/librssguard/services/feedly/feedlyentrypoint.cpp index 215caf687..75448295d 100755 --- a/src/librssguard/services/feedly/feedlyentrypoint.cpp +++ b/src/librssguard/services/feedly/feedlyentrypoint.cpp @@ -19,7 +19,7 @@ ServiceRoot* FeedlyEntryPoint::createNewRoot() const { QList FeedlyEntryPoint::initializeSubtree() const { QSqlDatabase database = qApp->database()->connection(QSL("FeedlyEntryPoint")); - return DatabaseQueries::getFeedlyAccounts(database); + return DatabaseQueries::getAccounts(database, code()); } QString FeedlyEntryPoint::name() const { diff --git a/src/librssguard/services/gmail/gmailentrypoint.cpp b/src/librssguard/services/gmail/gmailentrypoint.cpp index 99ad6e21b..8222ea6b6 100644 --- a/src/librssguard/services/gmail/gmailentrypoint.cpp +++ b/src/librssguard/services/gmail/gmailentrypoint.cpp @@ -21,7 +21,7 @@ ServiceRoot* GmailEntryPoint::createNewRoot() const { QList GmailEntryPoint::initializeSubtree() const { QSqlDatabase database = qApp->database()->connection(QSL("GmailEntryPoint")); - return DatabaseQueries::getGmailAccounts(database); + return DatabaseQueries::getAccounts(database, code()); } QString GmailEntryPoint::name() const { diff --git a/src/librssguard/services/greader/greaderentrypoint.cpp b/src/librssguard/services/greader/greaderentrypoint.cpp index 496313af6..c4a6c1233 100755 --- a/src/librssguard/services/greader/greaderentrypoint.cpp +++ b/src/librssguard/services/greader/greaderentrypoint.cpp @@ -19,7 +19,7 @@ ServiceRoot* GreaderEntryPoint::createNewRoot() const { QList GreaderEntryPoint::initializeSubtree() const { QSqlDatabase database = qApp->database()->connection(QSL("GreaderEntryPoint")); - return DatabaseQueries::getGreaderAccounts(database); + return DatabaseQueries::getAccounts(database, code()); } QString GreaderEntryPoint::name() const { diff --git a/src/librssguard/services/inoreader/inoreaderentrypoint.cpp b/src/librssguard/services/inoreader/inoreaderentrypoint.cpp index 9c8c8313a..1167d9bf0 100644 --- a/src/librssguard/services/inoreader/inoreaderentrypoint.cpp +++ b/src/librssguard/services/inoreader/inoreaderentrypoint.cpp @@ -22,7 +22,7 @@ ServiceRoot* InoreaderEntryPoint::createNewRoot() const { QList InoreaderEntryPoint::initializeSubtree() const { QSqlDatabase database = qApp->database()->connection(QSL("InoreaderEntryPoint")); - return DatabaseQueries::getInoreaderAccounts(database); + return DatabaseQueries::getAccounts(database, code()); } QString InoreaderEntryPoint::name() const { diff --git a/src/librssguard/services/owncloud/owncloudserviceentrypoint.cpp b/src/librssguard/services/owncloud/owncloudserviceentrypoint.cpp index 23d64f6dd..6ef0a9527 100644 --- a/src/librssguard/services/owncloud/owncloudserviceentrypoint.cpp +++ b/src/librssguard/services/owncloud/owncloudserviceentrypoint.cpp @@ -19,7 +19,7 @@ ServiceRoot* OwnCloudServiceEntryPoint::createNewRoot() const { QList OwnCloudServiceEntryPoint::initializeSubtree() const { QSqlDatabase database = qApp->database()->connection(QSL("OwnCloudServiceEntryPoint")); - return DatabaseQueries::getOwnCloudAccounts(database); + return DatabaseQueries::getAccounts(database, code()); } QString OwnCloudServiceEntryPoint::name() const { diff --git a/src/librssguard/services/standard/standardserviceentrypoint.cpp b/src/librssguard/services/standard/standardserviceentrypoint.cpp index b5e134a1f..8119d5bca 100644 --- a/src/librssguard/services/standard/standardserviceentrypoint.cpp +++ b/src/librssguard/services/standard/standardserviceentrypoint.cpp @@ -38,5 +38,5 @@ QList StandardServiceEntryPoint::initializeSubtree() const { // Check DB if standard account is enabled. QSqlDatabase database = qApp->database()->connection(QSL("StandardServiceEntryPoint")); - return DatabaseQueries::getStandardAccounts(database); + return DatabaseQueries::getAccounts(database, code()); } diff --git a/src/librssguard/services/tt-rss/gui/formeditttrssaccount.cpp b/src/librssguard/services/tt-rss/gui/formeditttrssaccount.cpp index 225f3aaea..aec7db2cf 100644 --- a/src/librssguard/services/tt-rss/gui/formeditttrssaccount.cpp +++ b/src/librssguard/services/tt-rss/gui/formeditttrssaccount.cpp @@ -30,7 +30,7 @@ void FormEditTtRssAccount::apply() { account()->network()->setForceServerSideUpdate(m_details->m_ui.m_checkServerSideUpdate->isChecked()); account()->network()->setDownloadOnlyUnreadMessages(m_details->m_ui.m_checkDownloadOnlyUnreadMessages->isChecked()); - account()->saveAccountDataToDatabase(m_creatingNew); + account()->saveAccountDataToDatabase(); accept(); if (!m_creatingNew) { diff --git a/src/librssguard/services/tt-rss/ttrssserviceentrypoint.cpp b/src/librssguard/services/tt-rss/ttrssserviceentrypoint.cpp index 26b81bf4d..737591d48 100644 --- a/src/librssguard/services/tt-rss/ttrssserviceentrypoint.cpp +++ b/src/librssguard/services/tt-rss/ttrssserviceentrypoint.cpp @@ -42,5 +42,5 @@ QList TtRssServiceEntryPoint::initializeSubtree() const { // Check DB if standard account is enabled. QSqlDatabase database = qApp->database()->connection(QSL("TtRssServiceEntryPoint")); - return DatabaseQueries::getTtRssAccounts(database); + return DatabaseQueries::getAccounts(database, code()); } diff --git a/src/librssguard/services/tt-rss/ttrssserviceroot.cpp b/src/librssguard/services/tt-rss/ttrssserviceroot.cpp index 591af88cd..81dc5d5e8 100644 --- a/src/librssguard/services/tt-rss/ttrssserviceroot.cpp +++ b/src/librssguard/services/tt-rss/ttrssserviceroot.cpp @@ -42,6 +42,8 @@ void TtRssServiceRoot::start(bool freshly_activated) { loadCacheFromFile(); } + updateTitle(); + if (getSubTreeFeeds().isEmpty()) { syncIn(); } @@ -200,6 +202,13 @@ void TtRssServiceRoot::saveAllCachedData(bool ignore_errors) { } } +QList TtRssServiceRoot::customDatabaseAttributes() const { + return { + { QSL("username") }, { QSL("password"), true }, { QSL("auth_protected") }, { QSL("auth_username") }, + { QSL("auth_password"), true }, { QSL("url") }, { QSL("force_update") }, { QSL("download_only_unread") } + }; +} + QString TtRssServiceRoot::additionalTooltip() const { return tr("Username: %1\nServer: %2\n" "Last error: %3\nLast login on: %4").arg(m_network->username(), @@ -214,29 +223,10 @@ TtRssNetworkFactory* TtRssServiceRoot::network() const { return m_network; } -void TtRssServiceRoot::saveAccountDataToDatabase(bool creating_new) { +void TtRssServiceRoot::saveAccountDataToDatabase() { QSqlDatabase database = qApp->database()->connection(metaObject()->className()); - if (!creating_new) { - // We are overwritting previously saved data. - if (DatabaseQueries::overwriteTtRssAccount(database, m_network->username(), m_network->password(), - m_network->authIsUsed(), m_network->authUsername(), - m_network->authPassword(), m_network->url(), - m_network->forceServerSideUpdate(), m_network->downloadOnlyUnreadMessages(), - accountId())) { - updateTitle(); - itemChanged(QList() << this); - } - } - else { - if (DatabaseQueries::createTtRssAccount(database, accountId(), m_network->username(), - m_network->password(), m_network->authIsUsed(), - m_network->authUsername(), m_network->authPassword(), - m_network->url(), m_network->forceServerSideUpdate(), - m_network->downloadOnlyUnreadMessages())) { - updateTitle(); - } - } + DatabaseQueries::createOverwriteAccount(database, this); } void TtRssServiceRoot::loadFromDatabase() { @@ -255,7 +245,71 @@ void TtRssServiceRoot::updateTitle() { host = m_network->url(); } - setTitle(m_network->username() + QSL(" (Tiny Tiny RSS)")); + setTitle(TextFactory::extractUsernameFromEmail(m_network->username()) + QSL(" (Tiny Tiny RSS)")); +} + +QString TtRssServiceRoot::username() const { + return m_network->username(); +} + +void TtRssServiceRoot::setUsername(const QString& username) { + m_network->setUsername(username); +} + +QString TtRssServiceRoot::password() const { + return m_network->password(); +} + +void TtRssServiceRoot::setPassword(const QString& password) { + m_network->setPassword(password); +} + +bool TtRssServiceRoot::authProtected() const { + return m_network->authIsUsed(); +} + +void TtRssServiceRoot::setAuthProtected(bool auth_protected) { + m_network->setAuthIsUsed(auth_protected); +} + +QString TtRssServiceRoot::authUsername() const { + return m_network->authUsername(); +} + +void TtRssServiceRoot::setAuthUsername(const QString& auth_username) { + m_network->setAuthUsername(auth_username); +} + +QString TtRssServiceRoot::authPassword() const { + return m_network->authPassword(); +} + +void TtRssServiceRoot::setAuthPassword(const QString& auth_password) { + m_network->setAuthPassword(auth_password); +} + +QString TtRssServiceRoot::url() const { + return m_network->url(); +} + +void TtRssServiceRoot::setUrl(const QString& url) { + m_network->setUrl(url); +} + +bool TtRssServiceRoot::forceUpdate() const { + return m_network->forceServerSideUpdate(); +} + +void TtRssServiceRoot::setForceUpdate(bool force_update) { + m_network->setForceServerSideUpdate(force_update); +} + +bool TtRssServiceRoot::downloadOnlyUnread() const { + return m_network->downloadOnlyUnreadMessages(); +} + +void TtRssServiceRoot::setDownloadOnlyUnread(bool download_only_unread) { + m_network->setDownloadOnlyUnreadMessages(download_only_unread); } RootItem* TtRssServiceRoot::obtainNewTreeForSyncIn() const { diff --git a/src/librssguard/services/tt-rss/ttrssserviceroot.h b/src/librssguard/services/tt-rss/ttrssserviceroot.h index 8448efb9f..ac29851f9 100644 --- a/src/librssguard/services/tt-rss/ttrssserviceroot.h +++ b/src/librssguard/services/tt-rss/ttrssserviceroot.h @@ -14,6 +14,14 @@ class TtRssNetworkFactory; class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot { Q_OBJECT + Q_PROPERTY(QString username READ username WRITE setUsername) + Q_PROPERTY(QString password READ password WRITE setPassword) + Q_PROPERTY(bool auth_protected READ authProtected WRITE setAuthProtected) + Q_PROPERTY(QString auth_username READ authUsername WRITE setAuthUsername) + Q_PROPERTY(QString auth_password READ authPassword WRITE setAuthPassword) + Q_PROPERTY(QString url READ url WRITE setUrl) + Q_PROPERTY(bool force_update READ forceUpdate WRITE setForceUpdate) + Q_PROPERTY(bool download_only_unread READ downloadOnlyUnread WRITE setDownloadOnlyUnread) public: explicit TtRssServiceRoot(RootItem* parent = nullptr); @@ -33,13 +41,39 @@ class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot { virtual void addNewFeed(RootItem* selected_item, const QString& url = QString()); virtual QString additionalTooltip() const; virtual void saveAllCachedData(bool ignore_errors); + virtual QList customDatabaseAttributes() const; // Access to network. TtRssNetworkFactory* network() const; - void saveAccountDataToDatabase(bool creating_new); + void saveAccountDataToDatabase(); void updateTitle(); + // Support for dynamic DB attributes. + QString username() const; + void setUsername(const QString& username); + + QString password() const; + void setPassword(const QString& password); + + bool authProtected() const; + void setAuthProtected(bool auth_protected); + + QString authUsername() const; + void setAuthUsername(const QString& auth_username); + + QString authPassword() const; + void setAuthPassword(const QString& auth_password); + + QString url() const; + void setUrl(const QString& url); + + bool forceUpdate() const; + void setForceUpdate(bool force_update); + + bool downloadOnlyUnread() const; + void setDownloadOnlyUnread(bool download_only_unread); + protected: virtual RootItem* obtainNewTreeForSyncIn() const;