File indexing completed on 2025-01-05 04:54:57
0001 /* 0002 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com> 0003 0004 This library is free software; you can redistribute it and/or modify it 0005 under the terms of the GNU Library General Public License as published by 0006 the Free Software Foundation; either version 2 of the License, or (at your 0007 option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, but WITHOUT 0010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 0012 License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to the 0016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0017 02110-1301, USA. 0018 */ 0019 #include "accountsettings.h" 0020 0021 #include <sink/store.h> 0022 #include <sink/log.h> 0023 #include <QDebug> 0024 #include <QDir> 0025 #include <QUrl> 0026 0027 #include "keyring.h" 0028 #include <sink/crypto.h> 0029 0030 using namespace Sink; 0031 using namespace Sink::ApplicationDomain; 0032 0033 AccountSettings::AccountSettings(QObject *parent) 0034 : QObject(parent) 0035 { 0036 } 0037 0038 void AccountSettings::setAccountType(const QByteArray &type) 0039 { 0040 mAccountType = type; 0041 } 0042 0043 QByteArray AccountSettings::accountType() const 0044 { 0045 return mAccountType; 0046 } 0047 0048 void AccountSettings::setAccountIdentifier(const QByteArray &id) 0049 { 0050 if (id.isEmpty()) { 0051 return; 0052 } 0053 mAccountIdentifier = id; 0054 0055 //Clear 0056 mIcon = QString(); 0057 mName = QString(); 0058 mImapServer = QString(); 0059 mImapUsername = QString(); 0060 mImapAuthenticationMode = QString(); 0061 mImapStarttls = false; 0062 mSmtpServer = QString(); 0063 mSmtpUsername = QString(); 0064 mCardDavServer = QString(); 0065 mCardDavUsername = QString(); 0066 mCalDavServer = QString(); 0067 mCalDavUsername = QString(); 0068 mPath = QString(); 0069 emit changed(); 0070 emit imapResourceChanged(); 0071 emit smtpResourceChanged(); 0072 emit cardDavResourceChanged(); 0073 emit calDavResourceChanged(); 0074 emit pathChanged(); 0075 0076 load(); 0077 0078 } 0079 0080 QByteArray AccountSettings::accountIdentifier() const 0081 { 0082 return mAccountIdentifier; 0083 } 0084 0085 void AccountSettings::setPath(const QUrl &path) 0086 { 0087 auto normalizedPath = path.path(); 0088 if (mPath != normalizedPath) { 0089 mPath = normalizedPath; 0090 emit pathChanged(); 0091 } 0092 } 0093 0094 QUrl AccountSettings::path() const 0095 { 0096 return QUrl(mPath); 0097 } 0098 0099 QValidator *AccountSettings::pathValidator() const 0100 { 0101 class PathValidator : public QValidator { 0102 State validate(QString &input, int &pos) const { 0103 Q_UNUSED(pos); 0104 if (!input.isEmpty() && QDir(input).exists()) { 0105 return Acceptable; 0106 } else { 0107 return Intermediate; 0108 } 0109 } 0110 }; 0111 static PathValidator *pathValidator = new PathValidator; 0112 return pathValidator; 0113 } 0114 0115 QValidator *AccountSettings::imapServerValidator() const 0116 { 0117 class ImapServerValidator : public QValidator { 0118 State validate(QString &input, int &pos) const { 0119 Q_UNUSED(pos); 0120 // imaps://mainserver.example.net:475 0121 const QUrl url(input); 0122 static QSet<QString> validProtocols{{"imap"}, {"imaps"}}; 0123 if (url.isValid() && validProtocols.contains(url.scheme().toLower())) { 0124 return Acceptable; 0125 } else { 0126 return Intermediate; 0127 } 0128 } 0129 }; 0130 static ImapServerValidator *validator = new ImapServerValidator; 0131 return validator; 0132 } 0133 0134 QValidator *AccountSettings::smtpServerValidator() const 0135 { 0136 class SmtpServerValidator : public QValidator { 0137 State validate(QString &input, int &pos) const { 0138 Q_UNUSED(pos); 0139 // smtps://mainserver.example.net:475 0140 const QUrl url(input); 0141 static QSet<QString> validProtocols = QSet<QString>() << "smtp" << "smtps"; 0142 if (url.isValid() && validProtocols.contains(url.scheme().toLower())) { 0143 return Acceptable; 0144 } else { 0145 return Intermediate; 0146 } 0147 } 0148 }; 0149 static SmtpServerValidator *validator = new SmtpServerValidator; 0150 return validator; 0151 } 0152 0153 void AccountSettings::saveAccount() 0154 { 0155 if (mAccountIdentifier.isEmpty()) { 0156 auto account = ApplicationDomainType::createEntity<SinkAccount>(); 0157 mAccountIdentifier = account.identifier(); 0158 Q_ASSERT(!mAccountType.isEmpty()); 0159 account.setAccountType(mAccountType); 0160 account.setName(mName); 0161 account.setIcon(mIcon); 0162 Store::create(account) 0163 .onError([](const KAsync::Error &error) { 0164 qWarning() << "Error while creating account: " << error.errorMessage;; 0165 }) 0166 .exec().waitForFinished(); 0167 } else { 0168 qDebug() << "Saving account " << mAccountIdentifier; 0169 Q_ASSERT(!mAccountIdentifier.isEmpty()); 0170 SinkAccount account(mAccountIdentifier); 0171 account.setAccountType(mAccountType); 0172 account.setName(mName); 0173 account.setIcon(mIcon); 0174 Q_ASSERT(!account.identifier().isEmpty()); 0175 Store::modify(account) 0176 .onError([](const KAsync::Error &error) { 0177 qWarning() << "Error while creating account: " << error.errorMessage;; 0178 }) 0179 .exec().waitForFinished(); 0180 } 0181 } 0182 0183 void AccountSettings::loadAccount() 0184 { 0185 Q_ASSERT(!mAccountIdentifier.isEmpty()); 0186 Store::fetchOne<SinkAccount>(Query().filter(mAccountIdentifier).request<SinkAccount::Icon>().request<SinkAccount::Name>().request<SinkAccount::AccountType>()) 0187 .then([this](const SinkAccount &account) { 0188 mAccountType = account.getAccountType().toLatin1(); 0189 mIcon = account.getIcon(); 0190 mName = account.getName(); 0191 emit changed(); 0192 }).onError([](const KAsync::Error &error) { 0193 qWarning() << "Failed to load the account: " << error.errorMessage; 0194 }).exec().waitForFinished(); 0195 } 0196 0197 void AccountSettings::loadImapResource() 0198 { 0199 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).filter<SinkResource::ResourceType>("sink.imap")) 0200 .then([this](const SinkResource &resource) { 0201 mImapIdentifier = resource.identifier(); 0202 mImapServer = resource.getProperty("server").toString(); 0203 mImapUsername = resource.getProperty("username").toString(); 0204 mImapStarttls = resource.getProperty("starttls").toBool(); 0205 mImapAuthenticationMode = resource.getProperty("authenticationMode").toString(); 0206 emit imapResourceChanged(); 0207 }).onError([](const KAsync::Error &error) { 0208 qWarning() << "Failed to load the imap resource: " << error.errorMessage; 0209 }).exec().waitForFinished(); 0210 } 0211 0212 void AccountSettings::loadMaildirResource() 0213 { 0214 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).filter<SinkResource::ResourceType>("sink.maildir")) 0215 .then([this](const SinkResource &resource) { 0216 mMaildirIdentifier = resource.identifier(); 0217 mPath = resource.getProperty("path").toString(); 0218 emit pathChanged(); 0219 }).onError([](const KAsync::Error &error) { 0220 SinkWarning() << "Failed to load the maildir resource: " << error.errorMessage; 0221 }).exec().waitForFinished(); 0222 } 0223 0224 void AccountSettings::loadMailtransportResource() 0225 { 0226 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).filter<SinkResource::ResourceType>("sink.mailtransport")) 0227 .then([this](const SinkResource &resource) { 0228 mMailtransportIdentifier = resource.identifier(); 0229 mSmtpServer = resource.getProperty("server").toString(); 0230 mSmtpUsername = resource.getProperty("username").toString(); 0231 emit smtpResourceChanged(); 0232 }).onError([](const KAsync::Error &error) { 0233 SinkWarning() << "Failed to load the smtp resource: " << error.errorMessage; 0234 }).exec().waitForFinished(); 0235 } 0236 0237 void AccountSettings::loadIdentity() 0238 { 0239 //FIXME this assumes that we only ever have one identity per account 0240 Store::fetchOne<Identity>(Query().filter<Identity::Account>(mAccountIdentifier)) 0241 .then([this](const Identity &identity) { 0242 mIdentityIdentifier = identity.identifier(); 0243 mUsername = identity.getName(); 0244 mEmailAddress = identity.getAddress(); 0245 emit identityChanged(); 0246 }).onError([](const KAsync::Error &error) { 0247 SinkWarning() << "Failed to load the identity: " << error.errorMessage; 0248 }).exec().waitForFinished(); 0249 } 0250 0251 void AccountSettings::loadCardDavResource() 0252 { 0253 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).filter<SinkResource::ResourceType>("sink.carddav")) 0254 .then([this](const SinkResource &resource) { 0255 mCardDavIdentifier = resource.identifier(); 0256 mCardDavServer = resource.getProperty("server").toString(); 0257 mCardDavUsername = resource.getProperty("username").toString(); 0258 emit cardDavResourceChanged(); 0259 }).onError([](const KAsync::Error &error) { 0260 qWarning() << "Failed to load the CardDAV resource: " << error.errorMessage; 0261 }).exec().waitForFinished(); 0262 } 0263 0264 void AccountSettings::loadCalDavResource() 0265 { 0266 Store::fetchOne<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier).filter<SinkResource::ResourceType>("sink.caldav")) 0267 .then([this](const SinkResource &resource) { 0268 mCalDavIdentifier = resource.identifier(); 0269 mCalDavServer = resource.getProperty("server").toString(); 0270 mCalDavUsername = resource.getProperty("username").toString(); 0271 emit calDavResourceChanged(); 0272 }).onError([](const KAsync::Error &error) { 0273 qWarning() << "Failed to load the CalDAV resource: " << error.errorMessage; 0274 }).exec().waitForFinished(); 0275 } 0276 0277 0278 template<typename ResourceType> 0279 static QByteArray saveResource(const QByteArray &accountIdentifier, const QByteArray &identifier, const std::map<QByteArray, QVariant> &properties) 0280 { 0281 if (!identifier.isEmpty()) { 0282 SinkResource resource(identifier); 0283 for (const auto &pair : properties) { 0284 resource.setProperty(pair.first, pair.second); 0285 } 0286 Store::modify(resource) 0287 .onError([](const KAsync::Error &error) { 0288 SinkWarning() << "Error while modifying resource: " << error.errorMessage; 0289 }) 0290 .exec().waitForFinished(); 0291 } else { 0292 auto resource = ResourceType::create(accountIdentifier); 0293 auto newIdentifier = resource.identifier(); 0294 for (const auto &pair : properties) { 0295 resource.setProperty(pair.first, pair.second); 0296 } 0297 Store::create(resource) 0298 .onError([](const KAsync::Error &error) { 0299 SinkWarning() << "Error while creating resource: " << error.errorMessage; 0300 }) 0301 .exec().waitForFinished(); 0302 return newIdentifier; 0303 } 0304 return identifier; 0305 } 0306 0307 void AccountSettings::saveImapResource() 0308 { 0309 mImapIdentifier = saveResource<ImapResource>(mAccountIdentifier, mImapIdentifier, { 0310 {"server", mImapServer}, 0311 {"username", mImapUsername}, 0312 {"starttls", mImapStarttls}, 0313 {"authenticationMode", mImapAuthenticationMode} 0314 }); 0315 } 0316 0317 void AccountSettings::saveCardDavResource() 0318 { 0319 mCardDavIdentifier = saveResource<CardDavResource>(mAccountIdentifier, mCardDavIdentifier, { 0320 {"server", mCardDavServer}, 0321 {"username", mCardDavUsername} 0322 }); 0323 } 0324 0325 void AccountSettings::saveCalDavResource() 0326 { 0327 mCalDavIdentifier = saveResource<CalDavResource>(mAccountIdentifier, mCalDavIdentifier, { 0328 {"server", mCalDavServer}, 0329 {"username", mCalDavUsername} 0330 }); 0331 } 0332 0333 void AccountSettings::saveMaildirResource() 0334 { 0335 mMaildirIdentifier = saveResource<MaildirResource>(mAccountIdentifier, mMaildirIdentifier, { 0336 {"path", mPath}, 0337 }); 0338 } 0339 0340 void AccountSettings::saveMailtransportResource() 0341 { 0342 mMailtransportIdentifier = saveResource<MailtransportResource>(mAccountIdentifier, mMailtransportIdentifier, { 0343 {"server", mSmtpServer}, 0344 {"username", mSmtpUsername} 0345 }); 0346 } 0347 0348 void AccountSettings::login(const QVariantMap &secrets) 0349 { 0350 // We'll attempt to store your account secrets using a key matching the email address. 0351 const auto accountSecret = secrets.value("accountSecret").toString(); 0352 Store::fetchAll<SinkResource>(Query().filter<SinkResource::Account>(mAccountIdentifier)) 0353 .then([=](const QList<SinkResource::Ptr> &resources) { 0354 Kube::AccountKeyring keyring{mAccountIdentifier}; 0355 for (const auto &resource : resources) { 0356 keyring.addPassword(resource->identifier(), accountSecret); 0357 } 0358 const auto keys = Crypto::findKeys({{mEmailAddress}}, true); 0359 if (!keys.empty()) { 0360 qInfo() << "Storing account secrets."; 0361 keyring.save(keys); 0362 } else { 0363 qInfo() << "Failed to find a GPG key for " << mEmailAddress << ". Not storing account secrets."; 0364 } 0365 }).onError([](const KAsync::Error &error) { 0366 qWarning() << "Failed to load any account resources resource: " << error; 0367 }).exec(); 0368 } 0369 0370 void AccountSettings::saveIdentity() 0371 { 0372 if (!mIdentityIdentifier.isEmpty()) { 0373 Identity identity(mIdentityIdentifier); 0374 identity.setName(mUsername); 0375 identity.setAddress(mEmailAddress); 0376 Store::modify(identity) 0377 .onError([](const KAsync::Error &error) { 0378 SinkWarning() << "Error while modifying identity: " << error.errorMessage; 0379 }) 0380 .exec().waitForFinished(); 0381 } else { 0382 auto identity = ApplicationDomainType::createEntity<Identity>(); 0383 mIdentityIdentifier = identity.identifier(); 0384 identity.setAccount(mAccountIdentifier); 0385 identity.setName(mUsername); 0386 identity.setAddress(mEmailAddress); 0387 Store::create(identity) 0388 .onError([](const KAsync::Error &error) { 0389 SinkWarning() << "Error while creating identity: " << error.errorMessage; 0390 }) 0391 .exec().waitForFinished(); 0392 } 0393 } 0394 0395 void AccountSettings::removeResource(const QByteArray &identifier) 0396 { 0397 if (identifier.isEmpty()) { 0398 SinkWarning() << "We're missing an identifier"; 0399 } else { 0400 SinkResource resource(identifier); 0401 Store::remove(resource) 0402 .onError([](const KAsync::Error &error) { 0403 SinkWarning() << "Error while removing resource: " << error.errorMessage; 0404 }) 0405 .exec().waitForFinished(); 0406 } 0407 } 0408 0409 void AccountSettings::removeAccount() 0410 { 0411 if (mAccountIdentifier.isEmpty()) { 0412 SinkWarning() << "We're missing an identifier"; 0413 } else { 0414 SinkAccount account(mAccountIdentifier); 0415 Store::remove(account) 0416 .onError([](const KAsync::Error &error) { 0417 SinkWarning() << "Error while removing account: " << error.errorMessage; 0418 }) 0419 .exec().waitForFinished(); 0420 } 0421 } 0422 0423 void AccountSettings::removeIdentity() 0424 { 0425 if (mIdentityIdentifier.isEmpty()) { 0426 SinkWarning() << "We're missing an identifier"; 0427 } else { 0428 Identity identity(mIdentityIdentifier); 0429 Store::remove(identity) 0430 .onError([](const KAsync::Error &error) { 0431 SinkWarning() << "Error while removing identity: " << error.errorMessage; 0432 }) 0433 .exec().waitForFinished(); 0434 } 0435 } 0436 0437 void AccountSettings::load() 0438 { 0439 loadAccount(); 0440 loadImapResource(); 0441 loadMailtransportResource(); 0442 loadCardDavResource(); 0443 loadCalDavResource(); 0444 loadIdentity(); 0445 } 0446 0447 void AccountSettings::save() 0448 { 0449 saveAccount(); 0450 saveImapResource(); 0451 saveMailtransportResource(); 0452 saveCardDavResource(); 0453 saveCalDavResource(); 0454 saveIdentity(); 0455 } 0456 0457 void AccountSettings::remove() 0458 { 0459 removeResource(mMailtransportIdentifier); 0460 removeResource(mImapIdentifier); 0461 removeResource(mCardDavIdentifier); 0462 removeResource(mCalDavIdentifier); 0463 removeIdentity(); 0464 removeAccount(); 0465 } 0466