File indexing completed on 2024-11-24 04:43:58

0001 /*
0002     SPDX-FileCopyrightText: 2011-2013 Dan Vratil <dan@progdan.cz>
0003     SPDX-FileCopyrightText: 2020 Igor Poboiko <igor.poboiko@gmail.com>
0004     SPDX-FileCopyrightText: 2021 Carl Schwan <carl@carlschwan.eu>
0005 
0006     SPDX-License-Identifier: GPL-3.0-or-later
0007 */
0008 
0009 #include "googlesettings.h"
0010 #include "googleresource_debug.h"
0011 #include "googlescopes.h"
0012 #include "settingsadaptor.h"
0013 
0014 #include <KGAPI/Account>
0015 #include <KLocalizedString>
0016 #include <KMessageBox>
0017 
0018 #include <QDataStream>
0019 #include <QIODevice>
0020 
0021 #include <qt6keychain/keychain.h>
0022 using namespace QKeychain;
0023 
0024 using namespace KGAPI2;
0025 
0026 static const QString googleWalletFolder = QStringLiteral("Akonadi Google");
0027 
0028 GoogleSettings::GoogleSettings(const KSharedConfigPtr &config, Options options)
0029     : SettingsBase(config)
0030 {
0031     if (options & Option::ExportToDBus) {
0032         new SettingsAdaptor(this);
0033         QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"),
0034                                                      this,
0035                                                      QDBusConnection::ExportAdaptors | QDBusConnection::ExportScriptableContents);
0036     }
0037 }
0038 
0039 void GoogleSettings::init()
0040 {
0041     if (account().isEmpty()) {
0042         qCWarning(GOOGLE_LOG) << Q_FUNC_INFO << "No username set";
0043         Q_EMIT accountReady(false);
0044         return;
0045     }
0046 
0047     qCWarning(GOOGLE_LOG) << "Trying to read password for" << account();
0048 
0049     // First read from QtKeyChain
0050     auto job = new QKeychain::ReadPasswordJob(googleWalletFolder, this);
0051     job->setKey(account());
0052     connect(job, &QKeychain::Job::finished, this, [this, job]() {
0053         if (job->error() != QKeychain::Error::NoError) {
0054             qCWarning(GOOGLE_LOG) << "Unable to read password" << job->error();
0055             Q_EMIT accountReady(false);
0056             return;
0057         }
0058 
0059         // Found something with QtKeyChain
0060         m_account = fetchAccountFromKeychain(account(), job);
0061         m_isReady = true;
0062         Q_EMIT accountReady(true);
0063     });
0064     job->start();
0065 }
0066 
0067 KGAPI2::AccountPtr GoogleSettings::fetchAccountFromKeychain(const QString &accountName, QKeychain::ReadPasswordJob *job)
0068 {
0069     QMap<QString, QString> map;
0070     auto value = job->binaryData();
0071     if (value.isEmpty()) {
0072         qCWarning(GOOGLE_LOG) << "Account" << accountName << "not found in KWallet";
0073         return {};
0074     }
0075 
0076     QDataStream ds(value);
0077     ds >> map;
0078 
0079     const QStringList scopes = map[QStringLiteral("scopes")].split(QLatin1Char(','), Qt::SkipEmptyParts);
0080     QList<QUrl> scopeUrls;
0081     scopeUrls.reserve(scopes.count());
0082     for (const QString &scope : scopes) {
0083         scopeUrls << QUrl(scope);
0084     }
0085     AccountPtr account(new Account(accountName, map[QStringLiteral("accessToken")], map[QStringLiteral("refreshToken")], scopeUrls));
0086     return account;
0087 }
0088 
0089 WritePasswordJob *GoogleSettings::storeAccount(AccountPtr account)
0090 {
0091     // Removing the old one (if present)
0092     if (m_account && (account->accountName() != m_account->accountName())) {
0093         cleanup();
0094     }
0095     // Populating the new one
0096     m_account = account;
0097 
0098     QStringList scopes;
0099     const QList<QUrl> urlScopes = googleScopes();
0100     scopes.reserve(urlScopes.count());
0101     for (const QUrl &url : urlScopes) {
0102         scopes << url.toString();
0103     }
0104 
0105     const QMap<QString, QString> map = {
0106         {QStringLiteral("accessToken"), m_account->accessToken()},
0107         {QStringLiteral("refreshToken"), m_account->refreshToken()},
0108         {QStringLiteral("scopes"), scopes.join(QLatin1Char(','))},
0109     };
0110 
0111     // Legacy: store the map exactly like Kwallet is doing it
0112     QByteArray mapData;
0113     QDataStream ds(&mapData, QIODevice::WriteOnly);
0114     ds << map;
0115 
0116     auto writeJob = new WritePasswordJob(googleWalletFolder, this);
0117     writeJob->setKey(m_account->accountName());
0118     writeJob->setBinaryData(mapData);
0119 
0120     connect(writeJob, &WritePasswordJob::finished, this, [this, writeJob]() {
0121         if (writeJob->error()) {
0122             qCWarning(GOOGLE_LOG) << "Unable to write password" << writeJob->error();
0123             return;
0124         }
0125         SettingsBase::setAccount(m_account->accountName());
0126         m_isReady = true;
0127     });
0128 
0129     return writeJob;
0130 }
0131 
0132 void GoogleSettings::cleanup()
0133 {
0134     if (m_account) {
0135         auto deleteJob = new DeletePasswordJob(googleWalletFolder, this);
0136         deleteJob->setKey(m_account->accountName());
0137         deleteJob->start();
0138     }
0139 }
0140 
0141 void GoogleSettings::addCalendar(const QString &calendar)
0142 {
0143     if (calendars().isEmpty() || calendars().contains(calendar)) {
0144         return;
0145     }
0146     setCalendars(calendars() << calendar);
0147     save();
0148 }
0149 
0150 void GoogleSettings::addTaskList(const QString &taskList)
0151 {
0152     if (calendars().isEmpty() || taskLists().contains(taskList)) {
0153         return;
0154     }
0155     setTaskLists(taskLists() << taskList);
0156     save();
0157 }
0158 
0159 QString GoogleSettings::clientId() const
0160 {
0161     return QStringLiteral("554041944266.apps.googleusercontent.com");
0162 }
0163 
0164 QString GoogleSettings::clientSecret() const
0165 {
0166     return QStringLiteral("mdT1DjzohxN3npUUzkENT0gO");
0167 }
0168 
0169 bool GoogleSettings::isReady() const
0170 {
0171     return m_isReady;
0172 }
0173 
0174 AccountPtr GoogleSettings::accountPtr()
0175 {
0176     return m_account;
0177 }
0178 
0179 void GoogleSettings::setWindowId(WId id)
0180 {
0181     m_winId = id;
0182 }
0183 
0184 void GoogleSettings::setResourceId(const QString &resourceIdentificator)
0185 {
0186     m_resourceId = resourceIdentificator;
0187 }
0188 
0189 #include "moc_googlesettings.cpp"