[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kmail-devel
Subject:    [Patch] Wallet support for KMail
From:       Volker Krause <volker.krause () rwth-aachen ! de>
Date:       2004-08-26 21:34:24
Message-ID: 200408262334.24760.volker.krause () rwth-aachen ! de
[Download RAW message or body]

Hi,

took a bit longer than I had expected but here it is finally: Wallet support 
for mail accounts and SMTP servers. 

Mail accounts work as expected (as few as possible wallet password dialogs), 
the code is mostly taken from KNode. 

SMTP servers are a bit different: First they don't have a unique id which is 
needed for the wallet, I added unique id's by coping the corresponding code 
from the account manager. Second they are read from the config file on demand 
instead of kept in memory. This makes password caching impossible, ie. the 
wallet needs to be accessed for every send message (which will result in a 
new wallet password dialog if the wallet has been closed in the meantime).

One thing that might be sub-optimal is the location of the static wallet 
pointer and the wallet opening method in KAcctMgr, since a) it is not account 
specific and also accessed by the KMTransport code and b) I didn't found a 
way to access the winID of the toplevel widget from there. I guess you might 
know a better place for this code ;)

Beside the issues mentioned above the code works so far, including migration 
from the config file to KWallet.

regards
Volker

BTW: Where is the difference between KMTransportInfo::availableTransports() 
and TransportManager::transportNames()?


["kmail-wallet.diff" (text/x-diff)]

? .kateconfig
? kmail-wallet.diff
? kmail.kdevelop
? kmail.kdevelop.pcs
? kmail.kdevses
Index: kmacctmgr.cpp
===================================================================
RCS file: /home/kde/kdepim/kmail/kmacctmgr.cpp,v
retrieving revision 1.121
diff -u -p -r1.121 kmacctmgr.cpp
--- kmacctmgr.cpp	26 Aug 2004 03:02:44 -0000	1.121
+++ kmacctmgr.cpp	26 Aug 2004 21:05:57 -0000
@@ -22,6 +22,9 @@ using KMail::NetworkAccount;
 #include <kdebug.h>
 #include <kconfig.h>
 #include <kapplication.h>
+#include <kstaticdeleter.h>
+#include <kwallet.h>
+using KWallet::Wallet;
 
 #include <qregexp.h>
 #include <qvaluelist.h>
@@ -458,4 +461,42 @@ QString KMAcctMgr::hostForAccount( const
   return net_acct ? net_acct->host() : QString::null;
 }
 
+//-----------------------------------------------------------------------------
+void KMAcctMgr::readPasswords()
+{
+  for (QPtrListIterator<KMAccount> it(mAcctList); it.current(); ++it) {
+    NetworkAccount *acct = dynamic_cast<NetworkAccount*>( it.current() );
+    if( acct )
+      acct->readPassword();
+  }
+}
+
+//-----------------------------------------------------------------------------
+Wallet *KMAcctMgr::mWallet = 0;
+
+Wallet *KMAcctMgr::wallet() {
+  if ( mWallet && mWallet->isOpen() )
+    return mWallet;
+
+  if ( !Wallet::isEnabled() )
+    return 0;
+
+  delete mWallet;
+  static KStaticDeleter<Wallet> sd;
+  // FIXME: we should pass topLevelWidget()->winId() to openWallet()
+  // but where do I get a widget from?
+  sd.setObject( mWallet, Wallet::openWallet(Wallet::NetworkWallet()) );
+
+  if ( !mWallet ) {
+    KMessageBox::error(0, i18n("The wallet could not be opened. "
+        "This error is most probably caused by providing a wrong password."));
+    return 0;
+  }
+
+  if (!mWallet->hasFolder("kmail"))
+    mWallet->createFolder("kmail");
+  mWallet->setFolder("kmail");
+  return mWallet;
+}
+
 #include "kmacctmgr.moc"
Index: kmacctmgr.h
===================================================================
RCS file: /home/kde/kdepim/kmail/kmacctmgr.h,v
retrieving revision 1.48
diff -u -p -r1.48 kmacctmgr.h
--- kmacctmgr.h	26 Aug 2004 03:02:44 -0000	1.48
+++ kmacctmgr.h	26 Aug 2004 21:05:57 -0000
@@ -11,6 +11,9 @@
 
 class QString;
 class QStringList;
+namespace KWallet {
+  class Wallet;
+}
 
 
 class KMAcctMgr: public QObject
@@ -70,6 +73,12 @@ public:
   /// Called on exit (KMMainWin::queryExit)
   void cancelMailCheck();
 
+  /** Read passwords of all accounts from the wallet */
+  void readPasswords();
+
+  /** Open KDE wallet and set it to kmail folder */
+  static KWallet::Wallet *wallet();
+
 public slots:
   virtual void singleCheckMail(KMAccount *, bool _interactive = true);
   virtual void singleInvalidateIMAPFolders(KMAccount *);
@@ -113,6 +122,7 @@ private:
   // if a summary should be displayed
   bool mDisplaySummary;
 
+  static KWallet::Wallet *mWallet;
 };
 
 #endif /*kmacctmgr_h*/
Index: kmsender.cpp
===================================================================
RCS file: /home/kde/kdepim/kmail/kmsender.cpp,v
retrieving revision 1.220
diff -u -p -r1.220 kmsender.cpp
--- kmsender.cpp	28 Jul 2004 09:33:56 -0000	1.220
+++ kmsender.cpp	26 Aug 2004 21:05:57 -0000
@@ -1045,13 +1045,13 @@ bool KMSendSMTP::send(KMMessage *aMsg)
 
   if (ti->auth)
   {
-    if(ti->user.isEmpty() || ti->pass.isEmpty())
+    if(ti->user.isEmpty() || ti->passwd().isEmpty())
     {
       bool b = FALSE;
       int result;
 
       KCursorSaver idle(KBusyPtr::idle());
-      result = KIO::PasswordDialog::getNameAndPassword(ti->user, ti->pass,
+      result = KIO::PasswordDialog::getNameAndPassword(ti->user, ti->passwd(),
 	&b, i18n("You need to supply a username and a password to use this "
 	     "SMTP server."), FALSE, QString::null, ti->name, QString::null);
 
@@ -1064,7 +1064,7 @@ bool KMSendSMTP::send(KMMessage *aMsg)
         ti->writeConfig(id);
     }
     destination.setUser(ti->user);
-    destination.setPass(ti->pass);
+    destination.setPass(ti->passwd());
   }
 
   if (!mSlave || !mInProcess)
Index: kmtransport.cpp
===================================================================
RCS file: /home/kde/kdepim/kmail/kmtransport.cpp,v
retrieving revision 1.39
diff -u -p -r1.39 kmtransport.cpp
--- kmtransport.cpp	13 Jul 2004 22:10:31 -0000	1.39
+++ kmtransport.cpp	26 Aug 2004 21:05:57 -0000
@@ -38,13 +38,18 @@
 #include <kmessagebox.h>
 #include <kseparator.h>
 #include <kdebug.h>
+#include <kwallet.h>
+using KWallet::Wallet;
 
 #include "kmservertest.h"
 #include "kmaccount.h"
 #include "kmkernel.h"
 #include "protocols.h"
+#include "kmacctmgr.h"
+#include "transportmanager.h"
+using namespace KMail;
 
-KMTransportInfo::KMTransportInfo()
+KMTransportInfo::KMTransportInfo() : mPasswdDirty( false ), mId( 0 )
 {
   name = i18n("Unnamed");
   port = "25";
@@ -63,12 +68,13 @@ void KMTransportInfo::readConfig(int id)
 {
   KConfig *config = KMKernel::config();
   KConfigGroupSaver saver(config, "Transport " + QString::number(id));
+  mId = config->readUnsignedNumEntry( "id", 0 );
   type = config->readEntry("type", "smtp");
   name = config->readEntry("name", i18n("Unnamed"));
   host = config->readEntry("host", "localhost");
   port = config->readEntry("port", "25");
   user = config->readEntry("user");
-  pass = KMAccount::decryptStr(config->readEntry("pass"));
+  mPasswd = KMAccount::decryptStr(config->readEntry("pass"));
   precommand = config->readPathEntry("precommand");
   encryption = config->readEntry("encryption");
   authType = config->readEntry("authtype");
@@ -76,6 +82,19 @@ void KMTransportInfo::readConfig(int id)
   storePass = config->readBoolEntry("storepass");
   specifyHostname = config->readBoolEntry("specifyHostname", false);
   localHostname = config->readEntry("localHostname");
+
+  if (!storePass)
+    return;
+
+  if (!mPasswd.isEmpty()) {
+    // migration to kwallet
+    config->deleteEntry( "pass" );
+    mPasswdDirty = true;
+  } else {
+    // read password if wallet is open, defer otherwise
+    if (Wallet::isOpen(Wallet::NetworkWallet()))
+      readPassword();
+  }
 }
 
 
@@ -83,13 +102,14 @@ void KMTransportInfo::writeConfig(int id
 {
   KConfig *config = KMKernel::config();
   KConfigGroupSaver saver(config, "Transport " + QString::number(id));
+  if (!mId)
+    mId = TransportManager::createId();
+  config->writeEntry("id", mId);
   config->writeEntry("type", type);
   config->writeEntry("name", name);
   config->writeEntry("host", host);
   config->writeEntry("port", port);
   config->writeEntry("user", user);
-  config->writeEntry("pass", (storePass) ? KMAccount::encryptStr(pass) :
-                                           QString("") );
   config->writePathEntry("precommand", precommand);
   config->writeEntry("encryption", encryption);
   config->writeEntry("authtype", authType);
@@ -97,6 +117,16 @@ void KMTransportInfo::writeConfig(int id
   config->writeEntry("storepass", storePass);
   config->writeEntry("specifyHostname", specifyHostname);
   config->writeEntry("localHostname", localHostname);
+
+  if (storePass && auth && mPasswdDirty) {
+    Wallet *wallet = KMAcctMgr::wallet();
+    if (!wallet || wallet->writePassword("transport-" + QString::number(mId), passwd()) ) {
+      KMessageBox::information(0, i18n("KWallet is not running. It is strongly recommend to use "
+          "KWallet for managing your password"),
+          i18n("KWallet is Not Running."), "KWalletWarning" );
+      config->writeEntry( "pass", KMAccount::encryptStr(passwd()) );
+    }
+  }
 }
 
 
@@ -129,6 +159,37 @@ QStringList KMTransportInfo::availableTr
 }
 
 
+QString& KMTransportInfo::passwd()
+{
+  if (auth && storePass && mPasswd.isEmpty())
+    readPassword();
+  return mPasswd;
+}
+
+
+void KMTransportInfo::setPasswd( const QString &passwd )
+{
+  if ( passwd != mPasswd ) {
+    mPasswd = passwd;
+    mPasswdDirty = true;
+  }
+}
+
+
+void KMTransportInfo::readPassword()
+{
+  if (!storePass || !auth)
+    return;
+
+  if ( Wallet::folderDoesNotExist(Wallet::NetworkWallet(), "kmail") ||
+       Wallet::keyDoesNotExist(Wallet::NetworkWallet(), "kmail", "transport-" + QString::number(mId)) )
+    return;
+
+  if ( KMAcctMgr::wallet() )
+    KMAcctMgr::wallet()->readPassword( "transport-" + QString::number(mId), mPasswd );
+}
+
+
 KMTransportSelDlg::KMTransportSelDlg( QWidget *parent, const char *name,
   bool modal )
   : KDialogBase( parent, name, modal, i18n("Add Transport"), Ok|Cancel, Ok )
@@ -429,7 +490,7 @@ void KMTransportDialog::setupSettings()
     mSmtp.portEdit->setText(mTransportInfo->port);
     mSmtp.authCheck->setChecked(mTransportInfo->auth);
     mSmtp.loginEdit->setText(mTransportInfo->user);
-    mSmtp.passwordEdit->setText(mTransportInfo->pass);
+    mSmtp.passwordEdit->setText(mTransportInfo->passwd());
     mSmtp.storePasswordCheck->setChecked(mTransportInfo->storePass);
     mSmtp.precommand->setText(mTransportInfo->precommand);
     mSmtp.specifyHostnameCheck->setChecked(mTransportInfo->specifyHostname);
@@ -468,7 +529,7 @@ void KMTransportDialog::saveSettings()
     mTransportInfo->port = mSmtp.portEdit->text().stripWhiteSpace();
     mTransportInfo->auth = mSmtp.authCheck->isChecked();
     mTransportInfo->user = mSmtp.loginEdit->text().stripWhiteSpace();
-    mTransportInfo->pass = mSmtp.passwordEdit->text();
+    mTransportInfo->setPasswd( mSmtp.passwordEdit->text() );
     mTransportInfo->storePass = mSmtp.storePasswordCheck->isChecked();
     mTransportInfo->precommand = mSmtp.precommand->text().stripWhiteSpace();
     mTransportInfo->specifyHostname = mSmtp.specifyHostnameCheck->isChecked();
Index: kmtransport.h
===================================================================
RCS file: /home/kde/kdepim/kmail/kmtransport.h,v
retrieving revision 1.10
diff -u -p -r1.10 kmtransport.h
--- kmtransport.h	10 Apr 2004 09:21:44 -0000	1.10
+++ kmtransport.h	26 Aug 2004 21:05:57 -0000
@@ -19,7 +19,7 @@
 
 #ifndef _KMTRANSPORT_H_
 #define _KMTRANSPORT_H_
- 
+
 #include <kdialogbase.h>
 
 class QCheckBox;
@@ -38,22 +38,36 @@ public:
   void writeConfig(int id);
   static int findTransport(const QString &name);
   static QStringList availableTransports();
-  QString type, name, host, port, user, pass, precommand, encryption, authType;
+  uint id() const { return mId; }
+
+  /** Get/set password for this account */
+  QString& passwd();
+  void setPasswd( const QString& passwd );
+
+  /** Read password from wallet */
+  void readPassword();
+
+  QString type, name, host, port, user, precommand, encryption, authType;
   QString localHostname;
   bool auth, storePass, specifyHostname;
+
+  private:
+    QString mPasswd;
+    bool mPasswdDirty;
+    uint mId;
 };
 
 class KMTransportSelDlg : public KDialogBase
 {
   Q_OBJECT
- 
+
 public:
   KMTransportSelDlg( QWidget *parent=0, const char *name=0, bool modal=TRUE );
   int selected() const;
- 
+
 private slots:
   void buttonClicked( int id );
- 
+
 private:
   int mSelectedButton;
 };
@@ -73,8 +87,8 @@ private slots:
   void slotRequiresAuthClicked();
   void slotSmtpEncryptionChanged(int);
   void slotCheckSmtpCapabilities();
-  void slotSmtpCapabilities( const QStringList &, const QStringList &, 
-                             const QString &, const QString &, 
+  void slotSmtpCapabilities( const QStringList &, const QStringList &,
+                             const QString &, const QString &,
                              const QString & );
   void slotSendmailEditPath(const QString &);
 private:
Index: networkaccount.cpp
===================================================================
RCS file: /home/kde/kdepim/kmail/networkaccount.cpp,v
retrieving revision 1.9
diff -u -p -r1.9 networkaccount.cpp
--- networkaccount.cpp	10 Mar 2004 18:41:27 -0000	1.9
+++ networkaccount.cpp	26 Aug 2004 21:05:57 -0000
@@ -28,10 +28,15 @@
 #endif
 
 #include "networkaccount.h"
+#include "kmacctmgr.h"
 
 #include <kconfig.h>
 #include <kio/global.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kwallet.h>
 using KIO::MetaData;
+using KWallet::Wallet;
 
 #include <climits>
 
@@ -45,7 +50,8 @@ namespace KMail {
       mStorePasswd( false ),
       mUseSSL( false ),
       mUseTLS( false ),
-      mAskAgain( false )
+      mAskAgain( false ),
+      mPasswdDirty( false )
   {
 
   }
@@ -80,11 +86,16 @@ namespace KMail {
   }
 
   QString NetworkAccount::passwd() const {
+    if (storePasswd() && mPasswd.isEmpty())
+      mOwner->readPasswords();
     return decryptStr( mPasswd );
   }
 
   void NetworkAccount::setPasswd( const QString & passwd, bool storeInConfig ) {
-    mPasswd = encryptStr( passwd );
+    if (mPasswd != encryptStr( passwd )) {
+      mPasswd = encryptStr( passwd );
+      mPasswdDirty = true;
+    }
     setStorePasswd( storeInConfig );
   }
 
@@ -132,25 +143,39 @@ namespace KMail {
     setLogin( config.readEntry( "login" ) );
 
     if ( config.readNumEntry( "store-passwd", false ) ) { // ### s/Num/Bool/
+      setStorePasswd( true );
       QString encpasswd = config.readEntry( "pass" );
       if ( encpasswd.isEmpty() ) {
-	encpasswd = config.readEntry( "passwd" );
-	if ( !encpasswd.isEmpty() ) encpasswd = importPassword( encpasswd );
+        encpasswd = config.readEntry( "passwd" );
+        if ( !encpasswd.isEmpty() ) encpasswd = importPassword( encpasswd );
       }
-      setPasswd( decryptStr( encpasswd ), true );
-    } else
+
+      if ( !encpasswd.isEmpty() ) {
+        // migration to KWallet
+        setPasswd( decryptStr( encpasswd ), true );
+        config.deleteEntry( "pass" );
+        config.deleteEntry( "passwd" );
+        mPasswdDirty = true;
+      } else {
+        // read password if wallet is already open, otherwise defer to on-demand loading
+        if (Wallet::isOpen(Wallet::NetworkWallet()))
+          readPassword();
+      }
+
+    } else {
       setPasswd( "", false );
-    
+    }
+
     setHost( config.readEntry( "host" ) );
 
     unsigned int port = config.readUnsignedNumEntry( "port", defaultPort() );
     if ( port > USHRT_MAX ) port = defaultPort();
     setPort( port );
-    
+
     setAuth( config.readEntry( "auth", "*" ) );
     setUseSSL( config.readBoolEntry( "use-ssl", false ) );
     setUseTLS( config.readBoolEntry( "use-tls", false ) );
-    
+
     mSieveConfig.readConfig( config );
   }
 
@@ -159,7 +184,16 @@ namespace KMail {
 
     config.writeEntry( "login", login() );
     config.writeEntry( "store-passwd", storePasswd() );
-    if ( storePasswd() ) config.writeEntry( "pass", mPasswd ); // NOT passwd()
+    if ( storePasswd() && mPasswdDirty ) { //config.writeEntry( "pass", mPasswd ); // NOT passwd()
+      Wallet *wallet = KMAcctMgr::wallet();
+      if (!wallet || wallet->writePassword("account-" + QString::number(mId), passwd()) ) {
+        KMessageBox::information(0, i18n("KWallet is not running. It is strongly recommend to use "
+            "KWallet for managing your password"),
+            i18n("KWallet is Not Running."), "KWalletWarning" );
+        config.writeEntry( "pass", encryptStr(passwd()) );
+      }
+    }
+
     else config.writeEntry( "passwd", "" ); // ### ???? why two different keys?
 
     config.writeEntry( "host", host() );
@@ -209,6 +243,22 @@ namespace KMail {
     setSieveConfig( n->sieveConfig() );
   }
 
+  void NetworkAccount::readPassword() {
+    if (!storePasswd())
+      return;
+
+    if ( Wallet::folderDoesNotExist(Wallet::NetworkWallet(), "kmail") ||
+          Wallet::keyDoesNotExist(Wallet::NetworkWallet(), "kmail", "account-" + QString::number(mId)) )
+      return;
+
+    if ( KMAcctMgr::wallet() ) {
+      QString passwd;
+      KMAcctMgr::wallet()->readPassword( "account-" + QString::number(mId), passwd );
+      setPasswd( passwd, true );
+      mPasswdDirty = false;
+    }
+  }
+
 } // namespace KMail
 
 #include "networkaccount.moc"
Index: networkaccount.h
===================================================================
RCS file: /home/kde/kdepim/kmail/networkaccount.h,v
retrieving revision 1.6
diff -u -p -r1.6 networkaccount.h
--- networkaccount.h	10 Mar 2004 18:41:27 -0000	1.6
+++ networkaccount.h	26 Aug 2004 21:05:57 -0000
@@ -110,6 +110,9 @@ namespace KMail {
     /** Kill all jobs that are currently in progress */
     virtual void killAllJobs( bool disconnectSlave = false ) = 0;
 
+    /** Read password from wallet, used for on-demand wallet opening */
+    void readPassword();
+
   protected:
     virtual QString protocol() const = 0;
     virtual unsigned short int defaultPort() const = 0;
@@ -123,6 +126,7 @@ namespace KMail {
     bool mUseSSL : 1;
     bool mUseTLS : 1;
     bool mAskAgain : 1;
+    bool mPasswdDirty;
 
   };
 
Index: transportmanager.cpp
===================================================================
RCS file: /home/kde/kdepim/kmail/transportmanager.cpp,v
retrieving revision 1.12
diff -u -p -r1.12 transportmanager.cpp
--- transportmanager.cpp	11 Jan 2004 21:57:08 -0000	1.12
+++ transportmanager.cpp	26 Aug 2004 21:05:57 -0000
@@ -20,6 +20,7 @@
 
 #include "kmtransport.h"
 #include "kmkernel.h"
+#include <kapplication.h>
 #include <kconfig.h>
 
 namespace KMail {
@@ -40,4 +41,28 @@ namespace KMail {
     return transportNames;
   }
 
+  // more or less copied from KMAcctMgr
+  uint TransportManager::createId()
+  {
+    QValueList<unsigned int> usedIds;
+
+    KConfigGroup general( KMKernel::config(), "General");
+    int numTransports = general.readNumEntry("transports", 0);
+
+    for ( int i = 1 ; i <= numTransports ; i++ ) {
+      KMTransportInfo ti;
+      ti.readConfig(i);
+      usedIds << ti.id();
+    }
+
+    usedIds << 0; // 0 is default for unknown
+    int newId;
+    do
+    {
+      newId = kapp->random();
+    } while ( usedIds.find(newId) != usedIds.end() );
+
+    return newId;
+  }
+
 } // namespace KMail
Index: transportmanager.h
===================================================================
RCS file: /home/kde/kdepim/kmail/transportmanager.h,v
retrieving revision 1.3
diff -u -p -r1.3 transportmanager.h
--- transportmanager.h	26 Jul 2003 14:51:50 -0000	1.3
+++ transportmanager.h	26 Aug 2004 21:05:57 -0000
@@ -25,13 +25,16 @@ namespace KMail {
    * @author Ingo Kloecker <kloecker@kde.org>
    **/
   class TransportManager {
-    
+
   public:
     TransportManager() {};
     virtual ~TransportManager() {};
-    
+
     /** Returns the list for transport names */
     static QStringList transportNames();
+
+    /** Create a unique id for a transport info item */
+    static unsigned int createId();
   };
 
 } // namespace KMail


_______________________________________________
KMail developers mailing list
KMail-devel@kde.org
https://mail.kde.org/mailman/listinfo/kmail-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic