Internal
Public Access
602 lines
20 KiB
C++
602 lines
20 KiB
C++
#include "profile_repository.h"
|
|
|
|
#include <QDir>
|
|
#include <QSet>
|
|
#include <QSqlDatabase>
|
|
#include <QSqlError>
|
|
#include <QSqlQuery>
|
|
#include <QStandardPaths>
|
|
#include <QVariant>
|
|
#include <QStringList>
|
|
|
|
namespace {
|
|
QString buildDatabasePath()
|
|
{
|
|
QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
|
if (appDataPath.isEmpty()) {
|
|
appDataPath = QDir::currentPath();
|
|
}
|
|
|
|
QDir dataDir(appDataPath);
|
|
dataDir.mkpath(QStringLiteral("."));
|
|
|
|
return dataDir.filePath(QStringLiteral("orbithub_profiles.sqlite"));
|
|
}
|
|
|
|
QString normalizedRdpSecurityMode(const QString& value)
|
|
{
|
|
const QString mode = value.trimmed();
|
|
if (mode.compare(QStringLiteral("NLA"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("NLA");
|
|
}
|
|
if (mode.compare(QStringLiteral("TLS"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("TLS");
|
|
}
|
|
if (mode.compare(QStringLiteral("RDP"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("RDP");
|
|
}
|
|
return QStringLiteral("Negotiate");
|
|
}
|
|
|
|
QString normalizedRdpPerformanceProfile(const QString& value)
|
|
{
|
|
const QString profile = value.trimmed();
|
|
if (profile.compare(QStringLiteral("Best Quality"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("Best Quality");
|
|
}
|
|
if (profile.compare(QStringLiteral("Best Performance"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("Best Performance");
|
|
}
|
|
if (profile.compare(QStringLiteral("Auto Detect"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("Auto Detect");
|
|
}
|
|
return QStringLiteral("Balanced");
|
|
}
|
|
|
|
QString normalizedProtocol(const QString& value)
|
|
{
|
|
const QString protocol = value.trimmed();
|
|
if (protocol.compare(QStringLiteral("RDP"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("RDP");
|
|
}
|
|
if (protocol.compare(QStringLiteral("VNC"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("VNC");
|
|
}
|
|
return QStringLiteral("SSH");
|
|
}
|
|
|
|
QString normalizedAuthMode(const QString& protocol, const QString& value)
|
|
{
|
|
if (protocol != QStringLiteral("SSH")) {
|
|
return QStringLiteral("Password");
|
|
}
|
|
|
|
const QString authMode = value.trimmed();
|
|
if (authMode.compare(QStringLiteral("Private Key"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("Private Key");
|
|
}
|
|
return QStringLiteral("Password");
|
|
}
|
|
|
|
QString normalizedKnownHostsPolicy(const QString& value)
|
|
{
|
|
const QString policy = value.trimmed();
|
|
if (policy.compare(QStringLiteral("Strict"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("Strict");
|
|
}
|
|
if (policy.compare(QStringLiteral("Accept New"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("Accept New");
|
|
}
|
|
if (policy.compare(QStringLiteral("Ignore"), Qt::CaseInsensitive) == 0) {
|
|
return QStringLiteral("Ignore");
|
|
}
|
|
return QStringLiteral("Ask");
|
|
}
|
|
|
|
QString normalizedFolderPath(const QString& value)
|
|
{
|
|
QString path = value.trimmed();
|
|
path.replace(QChar::fromLatin1('\\'), QChar::fromLatin1('/'));
|
|
|
|
const QStringList rawParts = path.split(QChar::fromLatin1('/'), Qt::SkipEmptyParts);
|
|
QStringList normalized;
|
|
for (const QString& rawPart : rawParts) {
|
|
const QString part = rawPart.trimmed();
|
|
if (!part.isEmpty()) {
|
|
normalized.push_back(part);
|
|
}
|
|
}
|
|
|
|
return normalized.join(QStringLiteral("/"));
|
|
}
|
|
|
|
QString normalizedTags(const QString& value)
|
|
{
|
|
const QStringList rawTokens = value.split(QChar::fromLatin1(','), Qt::SkipEmptyParts);
|
|
QStringList normalized;
|
|
QSet<QString> dedupe;
|
|
for (const QString& token : rawTokens) {
|
|
const QString trimmed = token.trimmed();
|
|
if (trimmed.isEmpty()) {
|
|
continue;
|
|
}
|
|
|
|
const QString key = trimmed.toLower();
|
|
if (dedupe.contains(key)) {
|
|
continue;
|
|
}
|
|
|
|
dedupe.insert(key);
|
|
normalized.push_back(trimmed);
|
|
}
|
|
|
|
return normalized.join(QStringLiteral(", "));
|
|
}
|
|
|
|
QString orderByClause(ProfileSortOrder sortOrder)
|
|
{
|
|
switch (sortOrder) {
|
|
case ProfileSortOrder::ProtocolAsc:
|
|
return QStringLiteral("ORDER BY lower(protocol) ASC, lower(name) ASC, id ASC");
|
|
case ProfileSortOrder::HostAsc:
|
|
return QStringLiteral("ORDER BY lower(host) ASC, lower(name) ASC, id ASC");
|
|
case ProfileSortOrder::NameAsc:
|
|
default:
|
|
return QStringLiteral("ORDER BY lower(name) ASC, id ASC");
|
|
}
|
|
}
|
|
|
|
QString nonNullTrimmed(const QString& value)
|
|
{
|
|
const QString trimmed = value.trimmed();
|
|
return trimmed.isNull() ? QStringLiteral("") : trimmed;
|
|
}
|
|
|
|
void bindProfileFields(QSqlQuery& query, const Profile& profile)
|
|
{
|
|
const QString protocol = normalizedProtocol(profile.protocol);
|
|
const QString authMode = normalizedAuthMode(protocol, profile.authMode);
|
|
const bool isSsh = protocol == QStringLiteral("SSH");
|
|
const bool isRdp = protocol == QStringLiteral("RDP");
|
|
|
|
query.addBindValue(nonNullTrimmed(profile.name));
|
|
query.addBindValue(nonNullTrimmed(profile.host));
|
|
query.addBindValue(profile.port);
|
|
query.addBindValue(nonNullTrimmed(profile.username));
|
|
query.addBindValue(isRdp ? nonNullTrimmed(profile.domain) : QStringLiteral(""));
|
|
query.addBindValue(nonNullTrimmed(normalizedFolderPath(profile.folderPath)));
|
|
query.addBindValue(protocol);
|
|
query.addBindValue(authMode);
|
|
query.addBindValue((isSsh && authMode == QStringLiteral("Private Key"))
|
|
? nonNullTrimmed(profile.privateKeyPath)
|
|
: QStringLiteral(""));
|
|
query.addBindValue(isSsh ? normalizedKnownHostsPolicy(profile.knownHostsPolicy)
|
|
: QStringLiteral("Ask"));
|
|
query.addBindValue(isRdp ? normalizedRdpSecurityMode(profile.rdpSecurityMode)
|
|
: QStringLiteral("Negotiate"));
|
|
query.addBindValue(isRdp ? normalizedRdpPerformanceProfile(profile.rdpPerformanceProfile)
|
|
: QStringLiteral("Balanced"));
|
|
query.addBindValue(normalizedTags(profile.tags));
|
|
}
|
|
|
|
Profile profileFromQuery(const QSqlQuery& query)
|
|
{
|
|
Profile profile;
|
|
profile.id = query.value(0).toLongLong();
|
|
profile.name = query.value(1).toString();
|
|
profile.host = query.value(2).toString();
|
|
profile.port = query.value(3).toInt();
|
|
profile.username = query.value(4).toString();
|
|
profile.domain = query.value(5).toString();
|
|
profile.folderPath = normalizedFolderPath(query.value(6).toString());
|
|
profile.protocol = normalizedProtocol(query.value(7).toString());
|
|
profile.authMode = normalizedAuthMode(profile.protocol, query.value(8).toString());
|
|
profile.privateKeyPath = profile.authMode == QStringLiteral("Private Key")
|
|
? query.value(9).toString().trimmed()
|
|
: QString();
|
|
profile.knownHostsPolicy = profile.protocol == QStringLiteral("SSH")
|
|
? normalizedKnownHostsPolicy(query.value(10).toString())
|
|
: QStringLiteral("Ask");
|
|
profile.rdpSecurityMode = profile.protocol == QStringLiteral("RDP")
|
|
? normalizedRdpSecurityMode(query.value(11).toString())
|
|
: QStringLiteral("Negotiate");
|
|
profile.rdpPerformanceProfile = profile.protocol == QStringLiteral("RDP")
|
|
? normalizedRdpPerformanceProfile(query.value(12).toString())
|
|
: QStringLiteral("Balanced");
|
|
profile.tags = normalizedTags(query.value(13).toString());
|
|
return profile;
|
|
}
|
|
|
|
bool isProfileValid(const Profile& profile, QString* error)
|
|
{
|
|
if (profile.name.trimmed().isEmpty()) {
|
|
if (error != nullptr) {
|
|
*error = QStringLiteral("Profile name is required.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (profile.host.trimmed().isEmpty()) {
|
|
if (error != nullptr) {
|
|
*error = QStringLiteral("Host is required.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (profile.port < 1 || profile.port > 65535) {
|
|
if (error != nullptr) {
|
|
*error = QStringLiteral("Port must be between 1 and 65535.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const QString protocol = normalizedProtocol(profile.protocol);
|
|
if ((protocol == QStringLiteral("SSH") || protocol == QStringLiteral("RDP"))
|
|
&& profile.username.trimmed().isEmpty()) {
|
|
if (error != nullptr) {
|
|
*error = QStringLiteral("Username is required for %1 profiles.").arg(protocol);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const QString authMode = normalizedAuthMode(protocol, profile.authMode);
|
|
if (protocol == QStringLiteral("SSH") && authMode == QStringLiteral("Private Key")
|
|
&& profile.privateKeyPath.trimmed().isEmpty()) {
|
|
if (error != nullptr) {
|
|
*error = QStringLiteral("Private key path is required for SSH private key authentication.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
ProfileRepository::ProfileRepository() : m_connectionName(QStringLiteral("orbithub_main"))
|
|
{
|
|
if (!initializeDatabase()) {
|
|
QSqlDatabase::removeDatabase(m_connectionName);
|
|
}
|
|
}
|
|
|
|
ProfileRepository::~ProfileRepository()
|
|
{
|
|
if (QSqlDatabase::contains(m_connectionName)) {
|
|
QSqlDatabase db = QSqlDatabase::database(m_connectionName);
|
|
if (db.isOpen()) {
|
|
db.close();
|
|
}
|
|
}
|
|
QSqlDatabase::removeDatabase(m_connectionName);
|
|
}
|
|
|
|
QString ProfileRepository::initError() const
|
|
{
|
|
return m_initError;
|
|
}
|
|
|
|
QString ProfileRepository::lastError() const
|
|
{
|
|
return m_lastError;
|
|
}
|
|
|
|
std::vector<QString> ProfileRepository::listFolders() const
|
|
{
|
|
std::vector<QString> result;
|
|
|
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
|
return result;
|
|
}
|
|
|
|
setLastError(QString());
|
|
|
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
|
query.prepare(QStringLiteral("SELECT path FROM profile_folders ORDER BY lower(path) ASC"));
|
|
if (!query.exec()) {
|
|
setLastError(query.lastError().text());
|
|
return result;
|
|
}
|
|
|
|
while (query.next()) {
|
|
const QString normalized = normalizedFolderPath(query.value(0).toString());
|
|
if (!normalized.isEmpty()) {
|
|
result.push_back(normalized);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool ProfileRepository::createFolder(const QString& folderPath) const
|
|
{
|
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
|
return false;
|
|
}
|
|
|
|
const QString normalized = normalizedFolderPath(folderPath);
|
|
if (normalized.isEmpty()) {
|
|
setLastError(QStringLiteral("Folder path is required."));
|
|
return false;
|
|
}
|
|
|
|
setLastError(QString());
|
|
|
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
|
query.prepare(QStringLiteral("INSERT OR IGNORE INTO profile_folders(path) VALUES (?)"));
|
|
query.addBindValue(normalized);
|
|
if (!query.exec()) {
|
|
setLastError(query.lastError().text());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::vector<Profile> ProfileRepository::listProfiles(const QString& searchQuery,
|
|
ProfileSortOrder sortOrder) const
|
|
{
|
|
std::vector<Profile> result;
|
|
|
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
|
return result;
|
|
}
|
|
|
|
setLastError(QString());
|
|
|
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
|
const QString orderBy = orderByClause(sortOrder);
|
|
if (searchQuery.trimmed().isEmpty()) {
|
|
query.prepare(QStringLiteral(
|
|
"SELECT id, name, host, port, username, domain, folder_path, protocol, auth_mode, private_key_path, known_hosts_policy, rdp_security_mode, rdp_performance_profile, tags "
|
|
"FROM profiles ")
|
|
+ orderBy);
|
|
} else {
|
|
query.prepare(QStringLiteral(
|
|
"SELECT id, name, host, port, username, domain, folder_path, protocol, auth_mode, private_key_path, known_hosts_policy, rdp_security_mode, rdp_performance_profile, tags "
|
|
"FROM profiles "
|
|
"WHERE lower(name) LIKE lower(?) OR lower(host) LIKE lower(?) OR lower(tags) LIKE lower(?) OR lower(folder_path) LIKE lower(?) ")
|
|
+ orderBy);
|
|
const QString search = QStringLiteral("%") + searchQuery.trimmed() + QStringLiteral("%");
|
|
query.addBindValue(search);
|
|
query.addBindValue(search);
|
|
query.addBindValue(search);
|
|
query.addBindValue(search);
|
|
}
|
|
|
|
if (!query.exec()) {
|
|
setLastError(query.lastError().text());
|
|
return result;
|
|
}
|
|
|
|
while (query.next()) {
|
|
result.push_back(profileFromQuery(query));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::optional<Profile> ProfileRepository::getProfile(qint64 id) const
|
|
{
|
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
setLastError(QString());
|
|
|
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
|
query.prepare(QStringLiteral(
|
|
"SELECT id, name, host, port, username, domain, folder_path, protocol, auth_mode, private_key_path, known_hosts_policy, rdp_security_mode, rdp_performance_profile, tags "
|
|
"FROM profiles WHERE id = ?"));
|
|
query.addBindValue(id);
|
|
|
|
if (!query.exec()) {
|
|
setLastError(query.lastError().text());
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (!query.next()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
return profileFromQuery(query);
|
|
}
|
|
|
|
std::optional<Profile> ProfileRepository::createProfile(const Profile& profile) const
|
|
{
|
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
setLastError(QString());
|
|
|
|
QString validationError;
|
|
if (!isProfileValid(profile, &validationError)) {
|
|
setLastError(validationError);
|
|
return std::nullopt;
|
|
}
|
|
|
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
|
query.prepare(QStringLiteral(
|
|
"INSERT INTO profiles(name, host, port, username, domain, folder_path, protocol, auth_mode, private_key_path, known_hosts_policy, rdp_security_mode, rdp_performance_profile, tags) "
|
|
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"));
|
|
bindProfileFields(query, profile);
|
|
|
|
if (!query.exec()) {
|
|
setLastError(query.lastError().text());
|
|
return std::nullopt;
|
|
}
|
|
|
|
Profile created = profile;
|
|
created.id = query.lastInsertId().toLongLong();
|
|
return created;
|
|
}
|
|
|
|
bool ProfileRepository::updateProfile(const Profile& profile) const
|
|
{
|
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
|
return false;
|
|
}
|
|
|
|
setLastError(QString());
|
|
|
|
QString validationError;
|
|
if (profile.id < 0 || !isProfileValid(profile, &validationError)) {
|
|
setLastError(validationError.isEmpty() ? QStringLiteral("Invalid profile data.")
|
|
: validationError);
|
|
return false;
|
|
}
|
|
|
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
|
query.prepare(QStringLiteral(
|
|
"UPDATE profiles "
|
|
"SET name = ?, host = ?, port = ?, username = ?, domain = ?, folder_path = ?, protocol = ?, auth_mode = ?, private_key_path = ?, known_hosts_policy = ?, rdp_security_mode = ?, rdp_performance_profile = ?, tags = ? "
|
|
"WHERE id = ?"));
|
|
bindProfileFields(query, profile);
|
|
query.addBindValue(profile.id);
|
|
|
|
if (!query.exec()) {
|
|
setLastError(query.lastError().text());
|
|
return false;
|
|
}
|
|
|
|
return query.numRowsAffected() > 0;
|
|
}
|
|
|
|
bool ProfileRepository::deleteProfile(qint64 id) const
|
|
{
|
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
|
return false;
|
|
}
|
|
|
|
setLastError(QString());
|
|
|
|
QSqlQuery query(QSqlDatabase::database(m_connectionName));
|
|
query.prepare(QStringLiteral("DELETE FROM profiles WHERE id = ?"));
|
|
query.addBindValue(id);
|
|
|
|
if (!query.exec()) {
|
|
setLastError(query.lastError().text());
|
|
return false;
|
|
}
|
|
|
|
return query.numRowsAffected() > 0;
|
|
}
|
|
|
|
bool ProfileRepository::initializeDatabase()
|
|
{
|
|
QSqlDatabase database = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), m_connectionName);
|
|
database.setDatabaseName(buildDatabasePath());
|
|
|
|
if (!database.open()) {
|
|
m_initError = database.lastError().text();
|
|
return false;
|
|
}
|
|
|
|
QSqlQuery query(database);
|
|
const bool created = query.exec(QStringLiteral(
|
|
"CREATE TABLE IF NOT EXISTS profiles ("
|
|
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
|
"name TEXT NOT NULL UNIQUE,"
|
|
"host TEXT NOT NULL DEFAULT '',"
|
|
"port INTEGER NOT NULL DEFAULT 22,"
|
|
"username TEXT NOT NULL DEFAULT '',"
|
|
"domain TEXT NOT NULL DEFAULT '',"
|
|
"folder_path TEXT NOT NULL DEFAULT '',"
|
|
"protocol TEXT NOT NULL DEFAULT 'SSH',"
|
|
"auth_mode TEXT NOT NULL DEFAULT 'Password',"
|
|
"private_key_path TEXT NOT NULL DEFAULT '',"
|
|
"known_hosts_policy TEXT NOT NULL DEFAULT 'Ask',"
|
|
"rdp_security_mode TEXT NOT NULL DEFAULT 'Negotiate',"
|
|
"rdp_performance_profile TEXT NOT NULL DEFAULT 'Balanced',"
|
|
"tags TEXT NOT NULL DEFAULT ''"
|
|
")"));
|
|
|
|
if (!created) {
|
|
m_initError = query.lastError().text();
|
|
return false;
|
|
}
|
|
|
|
const bool foldersCreated = query.exec(QStringLiteral(
|
|
"CREATE TABLE IF NOT EXISTS profile_folders ("
|
|
"path TEXT PRIMARY KEY NOT NULL"
|
|
")"));
|
|
if (!foldersCreated) {
|
|
m_initError = query.lastError().text();
|
|
return false;
|
|
}
|
|
|
|
if (!ensureProfileSchema()) {
|
|
m_initError = m_lastError;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ProfileRepository::ensureProfileSchema() const
|
|
{
|
|
if (!QSqlDatabase::contains(m_connectionName)) {
|
|
setLastError(QStringLiteral("Database connection missing."));
|
|
return false;
|
|
}
|
|
|
|
QSqlQuery tableInfo(QSqlDatabase::database(m_connectionName));
|
|
if (!tableInfo.exec(QStringLiteral("PRAGMA table_info(profiles)"))) {
|
|
setLastError(tableInfo.lastError().text());
|
|
return false;
|
|
}
|
|
|
|
QSet<QString> columns;
|
|
while (tableInfo.next()) {
|
|
columns.insert(tableInfo.value(1).toString());
|
|
}
|
|
|
|
struct ColumnDef {
|
|
QString name;
|
|
QString ddl;
|
|
};
|
|
|
|
const std::vector<ColumnDef> required = {
|
|
{QStringLiteral("host"), QStringLiteral("ALTER TABLE profiles ADD COLUMN host TEXT NOT NULL DEFAULT ''")},
|
|
{QStringLiteral("port"), QStringLiteral("ALTER TABLE profiles ADD COLUMN port INTEGER NOT NULL DEFAULT 22")},
|
|
{QStringLiteral("username"), QStringLiteral("ALTER TABLE profiles ADD COLUMN username TEXT NOT NULL DEFAULT ''")},
|
|
{QStringLiteral("domain"), QStringLiteral("ALTER TABLE profiles ADD COLUMN domain TEXT NOT NULL DEFAULT ''")},
|
|
{QStringLiteral("folder_path"), QStringLiteral("ALTER TABLE profiles ADD COLUMN folder_path TEXT NOT NULL DEFAULT ''")},
|
|
{QStringLiteral("protocol"), QStringLiteral("ALTER TABLE profiles ADD COLUMN protocol TEXT NOT NULL DEFAULT 'SSH'")},
|
|
{QStringLiteral("auth_mode"), QStringLiteral("ALTER TABLE profiles ADD COLUMN auth_mode TEXT NOT NULL DEFAULT 'Password'")},
|
|
{QStringLiteral("private_key_path"), QStringLiteral("ALTER TABLE profiles ADD COLUMN private_key_path TEXT NOT NULL DEFAULT ''")},
|
|
{QStringLiteral("known_hosts_policy"), QStringLiteral("ALTER TABLE profiles ADD COLUMN known_hosts_policy TEXT NOT NULL DEFAULT 'Ask'")},
|
|
{QStringLiteral("rdp_security_mode"), QStringLiteral("ALTER TABLE profiles ADD COLUMN rdp_security_mode TEXT NOT NULL DEFAULT 'Negotiate'")},
|
|
{QStringLiteral("rdp_performance_profile"), QStringLiteral("ALTER TABLE profiles ADD COLUMN rdp_performance_profile TEXT NOT NULL DEFAULT 'Balanced'")},
|
|
{QStringLiteral("tags"), QStringLiteral("ALTER TABLE profiles ADD COLUMN tags TEXT NOT NULL DEFAULT ''")}};
|
|
|
|
for (const ColumnDef& column : required) {
|
|
if (columns.contains(column.name)) {
|
|
continue;
|
|
}
|
|
|
|
QSqlQuery alter(QSqlDatabase::database(m_connectionName));
|
|
if (!alter.exec(column.ddl)) {
|
|
setLastError(alter.lastError().text());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
QSqlQuery ensureFolders(QSqlDatabase::database(m_connectionName));
|
|
if (!ensureFolders.exec(QStringLiteral(
|
|
"CREATE TABLE IF NOT EXISTS profile_folders ("
|
|
"path TEXT PRIMARY KEY NOT NULL"
|
|
")"))) {
|
|
setLastError(ensureFolders.lastError().text());
|
|
return false;
|
|
}
|
|
|
|
setLastError(QString());
|
|
return true;
|
|
}
|
|
|
|
void ProfileRepository::setLastError(const QString& error) const
|
|
{
|
|
m_lastError = error;
|
|
}
|