MailTransport

transport.cpp
1 /*
2  Copyright (c) 2006 - 2007 Volker Krause <[email protected]>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "transport.h"
21 #include "transport_p.h"
22 #include "mailtransport_defs.h"
23 #include "transportmanager.h"
24 #include "transporttype_p.h"
25 
26 #include <QTimer>
27 
28 #include <KConfigGroup>
29 #include "mailtransport_debug.h"
30 #include <KLocalizedString>
31 #include <KMessageBox>
32 #include <KStringHandler>
33 #include <KWallet/KWallet>
34 
35 using namespace MailTransport;
36 using namespace KWallet;
37 
38 Transport::Transport(const QString &cfgGroup)
39  : TransportBase(cfgGroup)
40  , d(new TransportPrivate)
41 {
42  qCDebug(MAILTRANSPORT_LOG) << cfgGroup;
43  d->passwordLoaded = false;
44  d->passwordDirty = false;
45  d->storePasswordInFile = false;
46  d->needsWalletMigration = false;
47  d->passwordNeedsUpdateFromWallet = false;
48  load();
49 }
50 
52 {
53  delete d;
54 }
55 
56 bool Transport::isValid() const
57 {
58  return (id() > 0) && !host().isEmpty() && port() <= 65536;
59 }
60 
62 {
63  if (!d->passwordLoaded && requiresAuthentication() && storePassword()
64  && d->password.isEmpty()) {
65  readPassword();
66  }
67  return d->password;
68 }
69 
70 void Transport::setPassword(const QString &passwd)
71 {
72  d->passwordLoaded = true;
73  if (d->password == passwd) {
74  return;
75  }
76  d->passwordDirty = true;
77  d->password = passwd;
78 }
79 
81 {
82  QStringList existingNames;
83  const auto lstTransports = TransportManager::self()->transports();
84  for (Transport *t : lstTransports) {
85  if (t->id() != id()) {
86  existingNames << t->name();
87  }
88  }
89  int suffix = 1;
90  QString origName = name();
91  while (existingNames.contains(name())) {
92  setName(i18nc("%1: name; %2: number appended to it to make "
93  "it unique among a list of names", "%1 #%2", origName, suffix));
94  ++suffix;
95  }
96 }
97 
99 {
100  Transport *original = TransportManager::self()->transportById(id(), false);
101  if (original == this) {
102  qCWarning(MAILTRANSPORT_LOG) << "Tried to update password state of non-cloned transport.";
103  return;
104  }
105  if (original) {
106  d->password = original->d->password;
107  d->passwordLoaded = original->d->passwordLoaded;
108  d->passwordDirty = original->d->passwordDirty;
109  } else {
110  qCWarning(MAILTRANSPORT_LOG) << "Transport with this ID not managed by transport manager.";
111  }
112 }
113 
115 {
116  return !requiresAuthentication() || !storePassword() || d->passwordLoaded;
117 }
118 
120 {
121  return Transport::authenticationTypeString(authenticationType());
122 }
123 
125 {
126  switch (type) {
127  case EnumAuthenticationType::LOGIN:
128  return QStringLiteral("LOGIN");
129  case EnumAuthenticationType::PLAIN:
130  return QStringLiteral("PLAIN");
131  case EnumAuthenticationType::CRAM_MD5:
132  return QStringLiteral("CRAM-MD5");
133  case EnumAuthenticationType::DIGEST_MD5:
134  return QStringLiteral("DIGEST-MD5");
135  case EnumAuthenticationType::NTLM:
136  return QStringLiteral("NTLM");
137  case EnumAuthenticationType::GSSAPI:
138  return QStringLiteral("GSSAPI");
139  case EnumAuthenticationType::CLEAR:
140  return i18nc("Authentication method", "Clear text");
141  case EnumAuthenticationType::APOP:
142  return QStringLiteral("APOP");
143  case EnumAuthenticationType::ANONYMOUS:
144  return i18nc("Authentication method", "Anonymous");
145  case EnumAuthenticationType::XOAUTH2:
146  return QStringLiteral("XOAUTH2");
147  }
148  Q_ASSERT(false);
149  return QString();
150 }
151 
152 void Transport::usrRead()
153 {
154  TransportBase::usrRead();
155 
156  setHost(host().trimmed());
157 
158  if (d->oldName.isEmpty()) {
159  d->oldName = name();
160  }
161 
162  // Set TransportType.
163  {
164  d->transportType = TransportType();
165  d->transportType.d->mIdentifier = identifier();
166  //qCDebug(MAILTRANSPORT_LOG) << "type" << identifier();
167  // Now we have the type and possibly agentType. Get the name, description
168  // etc. from TransportManager.
170  int index = types.indexOf(d->transportType);
171  if (index != -1) {
172  d->transportType = types[ index ];
173  } else {
174  qCWarning(MAILTRANSPORT_LOG) << "Type unknown to manager.";
175  d->transportType.d->mName = i18nc("An unknown transport type", "Unknown");
176  }
177  }
178 
179  // we have everything we need
180  if (!storePassword()) {
181  return;
182  }
183 
184  if (d->passwordLoaded) {
185  if (d->passwordNeedsUpdateFromWallet) {
186  d->passwordNeedsUpdateFromWallet = false;
187  // read password if wallet is open, defer otherwise
188  if (Wallet::isOpen(Wallet::NetworkWallet())) {
189  // Don't read the password right away because this can lead
190  // to reentrancy problems in KDBusServiceStarter when an application
191  // run in Kontact creates the transports (due to a QEventLoop in the
192  // synchronous KWallet openWallet call).
193  QTimer::singleShot(0, this, &Transport::readPassword);
194  } else {
195  d->passwordLoaded = false;
196  }
197  }
198 
199  return;
200  }
201 
202  // try to find a password in the config file otherwise
203  KConfigGroup group(config(), currentGroup());
204  if (group.hasKey("password")) {
205  d->password = KStringHandler::obscure(group.readEntry("password"));
206  } else if (group.hasKey("password-kmail")) { //Legacy
207  d->password = KStringHandler::obscure(group.readEntry("password-kmail"));
208  }
209 
210  if (!d->password.isEmpty()) {
211  d->passwordLoaded = true;
212  if (Wallet::isEnabled()) {
213  d->needsWalletMigration = true;
214  } else {
215  d->storePasswordInFile = true;
216  }
217  }
218 }
219 
220 bool Transport::usrSave()
221 {
222  if (requiresAuthentication() && storePassword() && d->passwordDirty) {
223  const QString storePassword = d->password;
224  Wallet *wallet = TransportManager::self()->wallet();
225  if (!wallet || wallet->writePassword(QString::number(id()), d->password) != 0) {
226  // wallet saving failed, ask if we should store in the config file instead
227  if (d->storePasswordInFile || KMessageBox::warningYesNo(
228  nullptr,
229  i18n("KWallet is not available. It is strongly recommended to use "
230  "KWallet for managing your passwords.\n"
231  "However, the password can be stored in the configuration "
232  "file instead. The password is stored in an obfuscated format, "
233  "but should not be considered secure from decryption efforts "
234  "if access to the configuration file is obtained.\n"
235  "Do you want to store the password for server '%1' in the "
236  "configuration file?", name()),
237  i18n("KWallet Not Available"),
238  KGuiItem(i18n("Store Password")),
239  KGuiItem(i18n("Do Not Store Password"))) == KMessageBox::Yes) {
240  // write to config file
241  KConfigGroup group(config(), currentGroup());
242  group.writeEntry("password", KStringHandler::obscure(storePassword));
243  d->storePasswordInFile = true;
244  }
245  }
246  d->passwordDirty = false;
247  }
248 
249  if (!TransportBase::usrSave()) {
250  return false;
251  }
252  TransportManager::self()->emitChangesCommitted();
253  if (name() != d->oldName) {
254  Q_EMIT TransportManager::self()->transportRenamed(id(), d->oldName, name());
255  d->oldName = name();
256  }
257 
258  return true;
259 }
260 
261 void Transport::readPassword()
262 {
263  // no need to load a password if the account doesn't require auth
264  if (!requiresAuthentication()) {
265  return;
266  }
267  d->passwordLoaded = true;
268 
269  // check whether there is a chance to find our password at all
270  if (Wallet::folderDoesNotExist(Wallet::NetworkWallet(), WALLET_FOLDER)
271  || Wallet::keyDoesNotExist(Wallet::NetworkWallet(), WALLET_FOLDER,
272  QString::number(id()))) {
273  // try migrating password from kmail
274  if (Wallet::folderDoesNotExist(Wallet::NetworkWallet(), KMAIL_WALLET_FOLDER)
275  || Wallet::keyDoesNotExist(Wallet::NetworkWallet(), KMAIL_WALLET_FOLDER,
276  QStringLiteral("transport-%1").arg(id()))) {
277  return;
278  }
279  qCDebug(MAILTRANSPORT_LOG) << "migrating password from kmail wallet";
281  if (wallet) {
282  QString pwd;
283  wallet->setFolder(KMAIL_WALLET_FOLDER);
284  if (wallet->readPassword(QStringLiteral("transport-%1").arg(id()), pwd) == 0) {
285  setPassword(pwd);
286  save();
287  } else {
288  d->password.clear();
289  d->passwordLoaded = false;
290  }
291  wallet->removeEntry(QStringLiteral("transport-%1").arg(id()));
292  wallet->setFolder(WALLET_FOLDER);
293  }
294  return;
295  }
296 
297  // finally try to open the wallet and read the password
299  if (wallet) {
300  QString pwd;
301  if (wallet->readPassword(QString::number(id()), pwd) == 0) {
302  setPassword(pwd);
303  } else {
304  d->password.clear();
305  d->passwordLoaded = false;
306  }
307  }
308 }
309 
311 {
312  return d->needsWalletMigration;
313 }
314 
316 {
317  qCDebug(MAILTRANSPORT_LOG) << "migrating" << id() << "to wallet";
318  d->needsWalletMigration = false;
319  KConfigGroup group(config(), currentGroup());
320  group.deleteEntry("password");
321  group.deleteEntry("password-kmail");
322  d->passwordDirty = true;
323  d->storePasswordInFile = false;
324  save();
325 }
326 
328 {
329  QString id = currentGroup().mid(10);
330  return new Transport(id);
331 }
332 
334 {
335  if (!d->transportType.isValid()) {
336  qCWarning(MAILTRANSPORT_LOG) << "Invalid transport type.";
337  }
338  return d->transportType;
339 }
A representation of a transport type.
Definition: transporttype.h:42
void migrateToWallet()
Try to migrate the password from the config file to the wallet.
Definition: transport.cpp:315
virtual bool setFolder(const QString &f)
bool isComplete() const
Returns true if all settings have been loaded.
Definition: transport.cpp:114
int indexOf(const T &value, int from) const const
static TransportManager * self()
Returns the TransportManager instance.
void setPassword(const QString &passwd)
Sets the password of this transport.
Definition: transport.cpp:70
KWallet::Wallet * wallet()
Returns a pointer to an open wallet if available, 0 otherwise.
QString password()
Returns the password of this transport.
Definition: transport.cpp:61
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
void writeEntry(const QString &key, const QVariant &value, WriteConfigFlags pFlags=Normal)
virtual int removeEntry(const QString &key)
virtual int writePassword(const QString &key, const QString &value)
void deleteEntry(const QString &pKey, WriteConfigFlags pFlags=Normal)
QString number(int n, int base)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
virtual int readPassword(const QString &key, QString &value)
QString authenticationTypeString() const
Returns a string representation of the authentication type.
Definition: transport.cpp:119
bool needsWalletMigration() const
Returns true if the password was not stored in the wallet.
Definition: transport.cpp:310
bool isValid() const
Returns true if this transport is valid, ie.
Definition: transport.cpp:56
bool hasKey(const QString &key) const
QList< Transport * > transports() const
Returns a list of all available transports.
QString i18n(const char *text, const TYPE &arg...)
QString mid(int position, int n) const const
void transportRenamed(int id, const QString &oldName, const QString &newName)
Emitted when a transport has been renamed.
Transport * clone() const
Returns a deep copy of this Transport object which will no longer be automatically updated...
Definition: transport.cpp:327
void forceUniqueName()
Makes sure the transport has a unique name.
Definition: transport.cpp:80
Internal file containing constant definitions etc.
void updatePasswordState()
This function synchronizes the password of this transport with the password of the transport with the...
Definition: transport.cpp:98
Transport(const QString &cfgGroup)
Creates a Transport object.
Definition: transport.cpp:38
Represents the settings of a specific mail transport.
Definition: transport.h:40
Transport * transportById(int id, bool def=true) const
Returns the Transport object with the given id.
KCOREADDONS_EXPORT QString obscure(const QString &str)
T readEntry(const QString &key, const T &aDefault) const
TransportType::List types() const
Returns a list of all available transport types.
ButtonCode warningYesNo(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
TransportType transportType() const
Returns the type of this transport.
Definition: transport.cpp:333
~Transport() override
Destructor.
Definition: transport.cpp:51
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 1 2020 23:10:04 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.