MailTransport

transport.cpp
1/*
2 SPDX-FileCopyrightText: 2006-2007 Volker Krause <vkrause@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "transport.h"
9#include "transport_p.h"
10#include "transportmanager.h"
11#include "transporttype_p.h"
12
13#include "mailtransport_debug.h"
14#include <KConfigGroup>
15#include <KLocalizedString>
16#include <KMessageBox>
17#include <KStringHandler>
18
19#include <qt6keychain/keychain.h>
20using namespace QKeychain;
21using namespace MailTransport;
22
24 : TransportBase(cfgGroup)
25 , d(new TransportPrivate)
26{
27 qCDebug(MAILTRANSPORT_LOG) << cfgGroup;
28 d->passwordLoaded = false;
29 d->passwordDirty = false;
30 d->storePasswordInFile = false;
31 d->needsWalletMigration = false;
32 load();
33 loadPassword();
34}
35
36Transport::~Transport() = default;
37
39{
40 return (id() > 0) && !host().isEmpty() && port() <= 65536;
41}
42
43void Transport::loadPassword()
44{
45 if (!d->passwordLoaded && requiresAuthentication() && storePassword() && d->password.isEmpty()) {
46 readPassword();
47 }
48}
49
50QString Transport::password() const
51{
52 return d->password;
53}
54
56{
57 d->passwordLoaded = true;
58 if (d->password == passwd) {
59 return;
60 }
61 d->passwordDirty = true;
62 d->password = passwd;
63 Q_EMIT passwordChanged();
64}
65
67{
68 QStringList existingNames;
69 const auto lstTransports = TransportManager::self()->transports();
70 for (Transport *t : lstTransports) {
71 if (t->id() != id()) {
72 existingNames << t->name();
73 }
74 }
75 int suffix = 1;
76 QString origName = name();
77 while (existingNames.contains(name())) {
78 setName(
79 i18nc("%1: name; %2: number appended to it to make "
80 "it unique among a list of names",
81 "%1 #%2",
82 origName,
83 suffix));
84 ++suffix;
85 }
86}
87
89{
90 Transport *original = TransportManager::self()->transportById(id(), false);
91 if (original == this) {
92 qCWarning(MAILTRANSPORT_LOG) << "Tried to update password state of non-cloned transport.";
93 return;
94 }
95 if (original) {
96 d->password = original->d->password;
97 d->passwordLoaded = original->d->passwordLoaded;
98 d->passwordDirty = original->d->passwordDirty;
99 Q_EMIT passwordChanged();
100 } else {
101 qCWarning(MAILTRANSPORT_LOG) << "Transport with this ID not managed by transport manager.";
102 }
103}
104
106{
107 return !requiresAuthentication() || !storePassword() || d->passwordLoaded;
108}
109
111{
112 return Transport::authenticationTypeString(authenticationType());
113}
114
116{
117 switch (type) {
118 case EnumAuthenticationType::LOGIN:
119 return QStringLiteral("LOGIN");
120 case EnumAuthenticationType::PLAIN:
121 return QStringLiteral("PLAIN");
122 case EnumAuthenticationType::CRAM_MD5:
123 return QStringLiteral("CRAM-MD5");
124 case EnumAuthenticationType::DIGEST_MD5:
125 return QStringLiteral("DIGEST-MD5");
126 case EnumAuthenticationType::NTLM:
127 return QStringLiteral("NTLM");
128 case EnumAuthenticationType::GSSAPI:
129 return QStringLiteral("GSSAPI");
130 case EnumAuthenticationType::CLEAR:
131 return i18nc("Authentication method", "Clear text");
132 case EnumAuthenticationType::APOP:
133 return QStringLiteral("APOP");
134 case EnumAuthenticationType::ANONYMOUS:
135 return i18nc("Authentication method", "Anonymous");
136 case EnumAuthenticationType::XOAUTH2:
137 return QStringLiteral("XOAUTH2");
138 }
139 Q_ASSERT(false);
140 return {};
141}
142
143void Transport::usrRead()
144{
145 TransportBase::usrRead();
146
147 setHost(host().trimmed());
148
149 if (d->oldName.isEmpty()) {
150 d->oldName = name();
151 }
152
153 // Set TransportType.
154 {
155 d->transportType = TransportType();
156 d->transportType.d->mIdentifier = identifier();
157 // qCDebug(MAILTRANSPORT_LOG) << "type" << identifier();
158 // Now we have the type and possibly agentType. Get the name, description
159 // etc. from TransportManager.
161 int index = types.indexOf(d->transportType);
162 if (index != -1) {
163 d->transportType = types[index];
164 } else {
165 qCWarning(MAILTRANSPORT_LOG) << "Type unknown to manager.";
166 d->transportType.d->mName = i18nc("An unknown transport type", "Unknown");
167 }
168 Q_EMIT transportTypeChanged();
169 }
170
171 // we have everything we need
172 if (!storePassword()) {
173 return;
174 }
175
176 if (d->passwordLoaded) {
177 return;
178 }
179
180 // try to find a password in the config file otherwise
181 KConfigGroup group(config(), currentGroup());
182 if (group.hasKey("password")) {
183 d->password = KStringHandler::obscure(group.readEntry("password"));
184 }
185
186 if (!d->password.isEmpty()) {
187 d->passwordLoaded = true;
188 if (QKeychain::isAvailable()) {
189 // TODO: Needs to replaced with a check, if a backend is available.
190 // 2022-10-12: QtKeyChain has no method to request, if there is any backend.
191 // see https://github.com/frankosterfeld/qtkeychain/issues/224
192 d->needsWalletMigration = true;
193 } else {
194 d->storePasswordInFile = true;
195 }
196 }
197}
198
199bool Transport::usrSave()
200{
201 if (requiresAuthentication() && storePassword() && d->passwordDirty) {
202 const QString storePassword = d->password;
203 auto writeJob = new WritePasswordJob(WALLET_FOLDER, this);
204 connect(writeJob, &Job::finished, this, [this, writeJob, storePassword] {
205 if (writeJob->error()) {
206 qWarning(MAILTRANSPORT_LOG()) << "WritePasswordJob failed with: " << writeJob->errorString();
207 // wallet saving failed, ask if we should store in the config file instead
208 if (d->storePasswordInFile
209 || KMessageBox::warningTwoActions(nullptr,
210 i18n("QKeychain not found a backend for storing your password. "
211 "It is strongly recommended to use strong backend for managing your passwords.\n"
212 "However, the password can be stored in the configuration "
213 "file instead. The password is stored in an obfuscated format, "
214 "but should not be considered secure from decryption efforts "
215 "if access to the configuration file is obtained.\n"
216 "Do you want to store the password for server '%1' in the "
217 "configuration file?",
218 name()),
219 i18n("KWallet Not Available"),
220 KGuiItem(i18nc("@action:button", "Store Password")),
221 KGuiItem(i18nc("@action:button", "Do Not Store Password")))
222 == KMessageBox::ButtonCode::PrimaryAction) {
223 // write to config file
224 KConfigGroup group(config(), currentGroup());
225 group.writeEntry("password", KStringHandler::obscure(storePassword));
226 d->storePasswordInFile = true;
227 }
228 }
229 });
230
231 writeJob->setKey(QString::number(id()));
232 writeJob->setTextData(storePassword);
233 QEventLoop loop;
234 connect(writeJob, &Job::finished, &loop, &QEventLoop::quit);
235 writeJob->start();
236 loop.exec();
237 d->passwordDirty = false;
238 }
239
240 if (!TransportBase::usrSave()) {
241 return false;
242 }
243 TransportManager::self()->emitChangesCommitted();
244 if (name() != d->oldName) {
245 Q_EMIT TransportManager::self()->transportRenamed(id(), d->oldName, name());
246 d->oldName = name();
247 }
248
249 return true;
250}
251
252void Transport::readPassword()
253{
254 // no need to load a password if the account doesn't require auth
255 if (!requiresAuthentication()) {
256 return;
257 }
258 d->passwordLoaded = true;
259
260 auto readJob = new ReadPasswordJob(WALLET_FOLDER, this);
261 connect(readJob, &Job::finished, this, &Transport::readTransportPasswordFinished);
262 readJob->setKey(QString::number(id()));
263 readJob->start();
264}
265
266void Transport::readTransportPasswordFinished(QKeychain::Job *baseJob)
267{
268 auto job = qobject_cast<ReadPasswordJob *>(baseJob);
269 Q_ASSERT(job);
270 if (job->error()) {
271 d->password.clear();
272 d->passwordLoaded = false;
273 qWarning() << "We have an error during reading password " << job->errorString();
274 Q_EMIT passwordChanged();
275 } else {
276 setPassword(job->textData());
277 }
278 Q_EMIT passwordLoaded();
279}
280
282{
283 return d->needsWalletMigration;
284}
285
287{
288 qCDebug(MAILTRANSPORT_LOG) << "migrating" << id() << "to wallet";
289 d->needsWalletMigration = false;
290 KConfigGroup group(config(), currentGroup());
291 group.deleteEntry("password");
292 d->passwordDirty = true;
293 d->storePasswordInFile = false;
294 save();
295}
296
298{
299 const QString id = currentGroup().mid(10);
300 return new Transport(id);
301}
302
303TransportType Transport::transportType() const
304{
305 if (!d->transportType.isValid()) {
306 qCWarning(MAILTRANSPORT_LOG) << "Invalid transport type.";
307 }
308 return d->transportType;
309}
310
311#include "moc_transport.cpp"
void deleteEntry(const char *key, WriteConfigFlags pFlags=Normal)
TransportType::List types() const
Returns a list of all available transport types.
Transport * transportById(Transport::Id id, bool def=true) const
Returns the Transport object with the given id.
static TransportManager * self()
Returns the TransportManager instance.
void transportRenamed(int id, const QString &oldName, const QString &newName)
Emitted when a transport has been renamed.
QList< Transport * > transports() const
Returns a list of all available transports.
A representation of a transport type.
Represents the settings of a specific mail transport.
Definition transport.h:33
void migrateToWallet()
Try to migrate the password from the config file to the wallet.
void passwordLoaded()
Emitted when passwords have been loaded from QKeyChain.
void passwordChanged()
Emitted when the password is changed.
void forceUniqueName()
Makes sure the transport has a unique name.
Definition transport.cpp:66
bool needsWalletMigration() const
Returns true if the password was not stored in the wallet.
void transportTypeChanged()
Emitted when the transport type is changed.
void setPassword(const QString &passwd)
Sets the password of this transport.
Definition transport.cpp:55
QString authenticationTypeString() const
Returns a string representation of the authentication type.
bool isComplete() const
Returns true if all settings have been loaded.
Transport * clone() const
Returns a deep copy of this Transport object which will no longer be automatically updated.
void updatePasswordState()
This function synchronizes the password of this transport with the password of the transport with the...
Definition transport.cpp:88
~Transport() override
Destructor.
Q_INVOKABLE bool isValid() const
Returns true if this transport is valid, ie.
Definition transport.cpp:38
Transport(const QString &cfgGroup)
Creates a Transport object.
Definition transport.cpp:23
QString i18nc(const char *context, const char *text, const TYPE &arg...)
Internal file containing constant definitions etc.
QString name(StandardAction id)
KCOREADDONS_EXPORT QString obscure(const QString &str)
int exec(ProcessEventsFlags flags)
void quit()
qsizetype indexOf(const AT &value, qsizetype from) const const
QString mid(qsizetype position, qsizetype n) const const
QString number(double n, char format, int precision)
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jun 21 2024 12:01:32 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.