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

List:       kmail-devel
Subject:    [PATCH] ClientInterface (next try)
From:       Andreas Gungl <a.gungl () gmx ! de>
Date:       2003-07-26 22:16:54
[Download RAW message or body]

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

as discussed long enough on this list, this time I tried to use an approach 
to separate only existing core and UI code. Those who reviewed my last 
patch will have it easy to compare to this one. I used kmsender as an 
example again. And again two issues are open (the interactive messagebox 
and the password dialog). Both require significantly more work, so before I 
continue I better ask about your opinions now.
Special hotspots are:
- - Is my choosen way of handling the strings for the UI okay?
- - I used direct calls to the KMClientInterface's methods. I believe that 
this is appropriate for the moment. If necessary we can switch to 
signals/slots later.
- - I tried to limit the classes used in the KMClientInterface's methods 
parameters to keep dependencies low. 
Any comments, hints and whatever are welcome as everytime.

Andreas
- -- 
    ~
  ' v '
 //   \\
/(     )\  Powered by Penguin.
  ^ ' ^
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.2-rc1-SuSE (GNU/Linux)

iD8DBQE/Iv3WVhjiFd4beU8RAp40AKCu/VY2axUnaMOucrmES56dTGDhHACfYukl
UAzDU9NbIJUFWjVCvfuLeP4=
=g9FG
-----END PGP SIGNATURE-----

["client.cpp" (text/x-c++src)]

/*
    This file is part of KMail.
    Copyright (c) 2003 Andreas Gungl <a.gungl@gmx.de>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/

#include "client.h"
#include "kmbroadcaststatus.h"

#include <kdebug.h>
#include <klocale.h>
#include <kmessagebox.h>


int applyArgsFromList( QString& str, QStringList* strList, int expectedArgs )
{
  int appliedArgs = 0;
  if ( strList != NULL )
  {
    while ( !strList->isEmpty() && expectedArgs-- )
    {
      str = str.arg( strList->first() );
      strList->pop_front();
      ++appliedArgs;
    }
  }
  return appliedArgs;
}


KMailClient* KMailClient::self = NULL;


KMailClient::KMailClient()
{
}


void KMailClient::slotMsgBoxInformation( const InfoId infoId,
                                         QStringList* argList )
{
  QString msg;
  switch( infoId )
  {
    case INFOBOX_ACCOUNT_MISSING:
      msg = i18n( "Please create an account for sending and try again." );
      break;
    case INFOBOX_MAILER_EXEC_FAILED:
      msg = i18n( "Failed to execute mailer program %1" );
      applyArgsFromList( msg, argList, 1 );
      break;
    case INFOBOX_MAILER_UNSPECIFIED:
      msg = i18n("Sending failed:\n%1\n"
        "The message will stay in the 'outbox' folder and will be resent.\n"
        "Please remove it from there if you do not want the message to "
        "be resent.\n"
        "The following transport protocol was used:\n  %2");
      applyArgsFromList( msg, argList, 1 );
      break;
    case INFOBOX_OUT_OF_SPACE:
      msg = i18n( "Critical error: Unable to process sent mail (out of space?) "
                  "Moving failing message to \"sent-mail\" folder." );
      break;
    case INFOBOX_OUTBOX_PROBLEM:
      msg = i18n( "Cannot add message to outbox folder" );
      break;
  }
  KMessageBox::information( 0, msg);
}


void KMailClient::slotMsgBoxError( const ErrorId errorId,
                                   QStringList* argList )
{
  QString msg;
  switch( errorId )
  {
    case ERRORBOX_MOVING_MSG:
      msg = i18n("Moving the sent message \"%1\" from the "
            "\"outbox\" to the \"sent-mail\" folder failed.\n"
            "Possible reasons are lack of disk space or write permission. "
            "Please try to fix the problem and move the message manually.");
      applyArgsFromList( msg, argList, 1 );
      break;
    case ERRORBOX_SENDING_ABORTED:
      msg = i18n("Sending aborted:\n%1\n"
            "The message will stay in the 'outbox' folder until you either "
            "fix the problem (e.g. a broken address) or remove the message "
            "from the 'outbox' folder.\n"
            "The following transport protocol was used:\n  %2");
      applyArgsFromList( msg, argList, 1 );
      break;
    case ERRORBOX_SENDING_FAILED:
      msg = i18n("Sending failed:\n%1\n"
            "The message will stay in the 'outbox' folder until you either "
            "fix the problem (e.g. a broken address) or remove the message "
            "from the 'outbox' folder.\n"
            "The following transport protocol was used:\n %2");
      applyArgsFromList( msg, argList, 1 );
      break;
  }
  KMessageBox::error( 0, msg);
}


int KMailClient::showMsgBoxWarningYesNo( const QString& text,
    const QString& caption, const QString& buttonYes, const QString& buttonNo )
{
  int res = KMessageBox::warningYesNo( 0, text, caption, buttonYes, buttonNo);
  return res;
}


void KMailClient::slotSetStatusMsg( const QString& msg )
{
  KMBroadcastStatus::instance()->setStatusMsg( msg );
}


void KMailClient::slotSetStatusMsg( StatusMsgId msgId,
                                    QStringList* argList )
{
  QString msg;
  switch( msgId )
  {
    case STATUSMSG_CURR_SENT_SUBJECT:
      msg = i18n("%3: subject of message","Sending message %1 of %2: %3");
      applyArgsFromList( msg, argList, 3 );
      break;
    case STATUSMSG_INIT_SENDER:
      msg = i18n("Initiating sender process...");
      break;
    case STATUSMSG_PRECOMMAND:
      msg = i18n("Executing precommand %1");
      applyArgsFromList( msg, argList, 1 );
      break;
    case STATUSMSG_SEND_ABORTED:
      msg = i18n( "Sending aborted." );
      break;
    case STATUSMSG_SEND_FAILED:
      msg = i18n("Failed to send (some) queued messages.");
      break;
    case STATUSMSG_UNKNOWN_TRANSPORT:
      msg = i18n("Unrecognized transport protocol. Unable to send message.");
      break;
  }
  KMBroadcastStatus::instance()->setStatusMsg( msg );
}


void KMailClient::slotSetStatusSendCount( const int sentMessages,
                                          const int totalMessages )
{
  if ( sentMessages == totalMessages ) {
    KMBroadcastStatus::instance()->setStatusMsg(
      i18n( "%n queued message successfully sent.",
            "%n queued messages successfully sent.",
            sentMessages));
  } else {
    KMBroadcastStatus::instance()->setStatusMsg(
      i18n( "%1 of %2 queued messages successfully sent." )
        .arg( sentMessages ).arg( totalMessages ));
  }
}


void KMailClient::slotSetStatusProgressPercent( const QString& msg,
                                                int percentage )
{
  KMBroadcastStatus::instance()->setStatusProgressPercent(msg, percentage);
}

["client.h" (text/x-chdr)]

/*
    This file is part of KMail.
    Copyright (c) 2003 Andreas Gungl <a.gungl@gmx.de>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
*/
#ifndef CLIENT_H
#define CLIENT_H

#include <qstring.h>
#include <qstringlist.h>

/**
 * @short KMail standard GUI client wrapper (singleton).
 * @author Andreas Gungl <a.gungl@gmx.de>
 **/
class KMailClient
{
protected:
  KMailClient();
  static KMailClient* self;

public:
  static KMailClient* instance();

  enum InfoId {
    INFOBOX_ACCOUNT_MISSING,
    INFOBOX_MAILER_EXEC_FAILED,
    INFOBOX_MAILER_UNSPECIFIED,
    INFOBOX_OUT_OF_SPACE,
    INFOBOX_OUTBOX_PROBLEM
  };

  enum ErrorId {
    ERRORBOX_MOVING_MSG,
    ERRORBOX_SENDING_ABORTED,
    ERRORBOX_SENDING_FAILED
  };

  enum StatusMsgId {
    STATUSMSG_CURR_SENT_SUBJECT,
    STATUSMSG_INIT_SENDER,
    STATUSMSG_PRECOMMAND,
    STATUSMSG_SEND_ABORTED,
    STATUSMSG_SEND_FAILED,
    STATUSMSG_UNKNOWN_TRANSPORT
  };


  /** Show an information message. No interaction is possible.
   * @param infoId The ID specifies the message to be shown.
   * @param argList The optional list contains arguments for placeholders.
   */
  virtual void slotMsgBoxInformation( const InfoId infoId,
                                      QStringList* argList = NULL );

  /** Show an error message. No interaction is possible.
   * @param errorId The ID specifies the message to be shown.
   * @param argList The optional list contains arguments for placeholders.
   */
  virtual void slotMsgBoxError( const ErrorId errorId,
                                QStringList* argList = NULL );


  /** Show a yes/no messagebox.
   * @param text      The i18n'ed message to be shown.
   * @param caption   The i18n'ed message to be shown.
   * @param buttonYes The i18n'ed message to be shown.
   * @param buttonNo  The i18n'ed message to be shown.
   */
  virtual int showMsgBoxWarningYesNo( const QString& text,
          const QString& caption, const QString& buttonYes,
          const QString& buttonNo );


  /** Set the status message.
   * @param msg The i18n'ed message to be shown.
   */
  virtual void slotSetStatusMsg( const QString& msg );

  /** Set the status message.
   * @param msgId The ID specifies the message to be shown.
   * @param argList The optional list contains arguments for placeholders.
   */
  virtual void slotSetStatusMsg( StatusMsgId msgId,
                                 QStringList* argList = NULL );

  /** Set the current progress for sent messages in the status bar.
   * @param sentMessages The n-th message being sent in the moment.
   * @param totalMessages Number of all messages to be sent.
   */
  virtual void slotSetStatusSendCount( const int sentMessages,
                                       const int totalMessages );

  /** Sets the percentage value for the status bar.
   * @param msg The i18n'ed message to be shown.
   * @param percentage The value in the range 0-100.
   */
  virtual void slotSetStatusProgressPercent( const QString& msg,
                                             int percentage );
};

inline KMailClient* KMailClient::instance()
{
  if (!self) self = new KMailClient();
  return self;
}

#endif // CLIENT_H

["ui-separation.diff" (text/x-diff)]

Index: Makefile.am
===================================================================
RCS file: /home/kde/kdepim/kmail/Makefile.am,v
retrieving revision 1.224
diff -u -3 -p -u -b -r1.224 Makefile.am
--- Makefile.am	25 Jul 2003 09:40:27 -0000	1.224
+++ Makefile.am	26 Jul 2003 21:44:47 -0000
@@ -81,7 +81,7 @@ libkmailcommon_la_SOURCES = kmmessage.cp
 		maildirjob.cpp mboxjob.cpp imapjob.cpp \
 		subscriptiondialog.cpp kmailicalifaceimpl.cpp aboutdata.cpp \
 		folderIface.cpp folderIface.skel mailserviceimpl.cpp \
-		attachmentlistview.cpp 
+		attachmentlistview.cpp client.cpp
 
 kmail_SOURCES = main.cpp
 
Index: kmsender.cpp
===================================================================
RCS file: /home/kde/kdepim/kmail/kmsender.cpp,v
retrieving revision 1.184
diff -u -3 -p -u -b -r1.184 kmsender.cpp
--- kmsender.cpp	5 Jul 2003 16:23:48 -0000	1.184
+++ kmsender.cpp	26 Jul 2003 21:44:47 -0000
@@ -33,6 +33,7 @@ using namespace KMime::Types;
 #include "kmfoldermgr.h"
 #include "kmmsgdict.h"
 #include "kmmsgpart.h"
+#include "client.h"
 #include <mimelib/mediatyp.h>
 
 #define SENDER_GROUP "sending mail"
@@ -68,7 +69,8 @@ KMSender::~KMSender()
 //-----------------------------------------------------------------------------
 void KMSender::setStatusMsg(const QString &msg)
 {
-  KMBroadcastStatus::instance()->setStatusMsg(msg);
+  // TODO this method should get eliminated while the GUI separation
+  KMailClient::instance()->slotSetStatusMsg( msg );
 }
 
 //-----------------------------------------------------------------------------
@@ -99,7 +101,8 @@ bool KMSender::settingsOk() const
 {
   if (KMTransportInfo::availableTransports().isEmpty())
   {
-    KMessageBox::information(0,i18n("Please create an account for sending and try again."));
+    KMailClient::instance()
+      ->slotMsgBoxInformation( KMailClient::INFOBOX_ACCOUNT_MISSING );
     return false;
   }
   return true;
@@ -154,7 +157,8 @@ bool KMSender::send(KMMessage* aMsg, sho
   rc = kernel->outboxFolder()->addMsg(aMsg);
   if (rc)
   {
-    KMessageBox::information(0,i18n("Cannot add message to outbox folder"));
+    KMailClient::instance()
+      ->slotMsgBoxInformation( KMailClient::INFOBOX_OUTBOX_PROBLEM );
     return FALSE;
   }
 
@@ -216,7 +220,7 @@ void KMSender::emitProgressInfo( int cur
 {
   int percent = (mTotalBytes) ? ( 100 * (mSentBytes+currentFileProgress) / mTotalBytes ) : 0;
   if (percent > 100) percent = 100;
-  KMBroadcastStatus::instance()->setStatusProgressPercent("Sender", percent);
+    KMailClient::instance()->slotSetStatusProgressPercent("Sender", percent);
 }
 
 //-----------------------------------------------------------------------------
@@ -294,9 +298,8 @@ kdDebug(5006) << "KMSender::doSendMsg() 
     switch (processResult) {
     case 2:
       perror("Critical error: Unable to process sent mail (out of space?)");
-      KMessageBox::information(0, i18n("Critical error: "
-				       "Unable to process sent mail (out of space?)"
-				       "Moving failing message to \"sent-mail\" folder."));
+      KMailClient::instance()
+        ->slotMsgBoxInformation( KMailClient::INFOBOX_OUT_OF_SPACE );
       sentFolder->quiet(TRUE);
       sentFolder->moveMsg(mCurrentMsg);
       if ( sentFolder != kernel->sentFolder() )
@@ -308,11 +311,9 @@ kdDebug(5006) << "KMSender::doSendMsg() 
       sentFolder->quiet(TRUE);
       if (sentFolder->moveMsg(mCurrentMsg) != 0)
       {
-        KMessageBox::error(0, i18n("Moving the sent message \"%1\" from the "
-          "\"outbox\" to the \"sent-mail\" folder failed.\n"
-          "Possible reasons are lack of disk space or write permission. "
-          "Please try to fix the problem and move the message manually.")
-          .arg(mCurrentMsg->subject()));
+        QStringList args( mCurrentMsg->subject() );
+        KMailClient::instance()
+          ->slotMsgBoxError( KMailClient::ERRORBOX_MOVING_MSG, &args);
         cleanup();
         sentFolder->quiet(FALSE);
         return;
@@ -346,14 +347,8 @@ kdDebug(5006) << "KMSender::doSendMsg() 
     if ( ( sentFolder != kernel->sentFolder() ) && ( sentFolder != 0 ) )
         sentFolder->close();
     if (someSent) {
-      if ( mSentMessages == mTotalMessages ) {
-        setStatusMsg(i18n("%n queued message successfully sent.",
-		       	  "%n queued messages successfully sent.",
-			  mSentMessages));
-      } else {
-        setStatusMsg(i18n("%1 of %2 queued messages successfully sent.")
-            .arg(mSentMessages).arg( mTotalMessages ));
-      }
+      KMailClient::instance()
+        ->slotSetStatusSendCount( mSentMessages, mTotalMessages );
     }
     cleanup();
     return;
@@ -370,7 +365,8 @@ kdDebug(5006) << "KMSender::doSendMsg() 
     kapp->ref();
 
     mSendInProgress = TRUE;
-    setStatusMsg(i18n("Initiating sender process..."));
+    KMailClient::instance()
+      ->slotSetStatusMsg( KMailClient::STATUSMSG_INIT_SENDER );
   }
 
   QString msgTransport = mCurrentMsg->headerField("X-KMail-Transport");
@@ -397,8 +393,9 @@ kdDebug(5006) << "KMSender::doSendMsg() 
       // Run the precommand if there is one
       if (!mTransportInfo->precommand.isEmpty())
       {
-        setStatusMsg(i18n("Executing precommand %1")
-          .arg(mTransportInfo->precommand));
+        QStringList args(mTransportInfo->precommand);
+        KMailClient::instance()
+          ->slotSetStatusMsg( KMailClient::STATUSMSG_PRECOMMAND, &args);
         mPrecommand = new KMPrecommand(mTransportInfo->precommand);
         connect(mPrecommand, SIGNAL(finished(bool)),
           SLOT(slotPrecommandFinished(bool)));
@@ -427,7 +424,8 @@ void KMSender::sendProcStarted(bool succ
     if (mSendProc)
        mSendProc->finish(true);
     else
-      setStatusMsg(i18n("Unrecognized transport protocol. Unable to send message."));
+    KMailClient::instance()
+      ->slotSetStatusMsg( KMailClient::STATUSMSG_UNKNOWN_TRANSPORT );
     mSendProc = 0;
     mSendProcStarted = false;
     cleanup();
@@ -445,13 +443,17 @@ void KMSender::doSendMsgAux()
   // start sending the current message
 
   mSendProc->preSendInit();
-  setStatusMsg(i18n("%3: subject of message","Sending message %1 of %2: %3")
-	       .arg(mSentMessages+mFailedMessages+1).arg(mTotalMessages)
-	       .arg(mCurrentMsg->subject()));
+  QStringList args;
+  args.append( QString::number( mSentMessages+mFailedMessages+1 ) );
+  args.append( QString::number( mTotalMessages ) );
+  args.append( mCurrentMsg->subject() );
+  KMailClient::instance()
+    ->slotSetStatusMsg( KMailClient::STATUSMSG_CURR_SENT_SUBJECT, &args );
   if (!mSendProc->send(mCurrentMsg))
   {
     cleanup();
-    setStatusMsg(i18n("Failed to send (some) queued messages."));
+    KMailClient::instance()
+      ->slotSetStatusMsg( KMailClient::STATUSMSG_SEND_FAILED );
     return;
   }
   // Do *not* add code here, after send(). It can happen that this method
@@ -513,15 +515,16 @@ void KMSender::slotIdle()
 
   if (mSendAborted) {
     // sending of message aborted
-    msg = i18n("Sending aborted:\n%1\n"
-        "The message will stay in the 'outbox' folder until you either "
-        "fix the problem (e.g. a broken address) or remove the message "
-        "from the 'outbox' folder.\n"
-        "The following transport protocol was used:\n  %2")
-      .arg(errString)
-      .arg(mMethodStr);
-    if (!errString.isEmpty()) KMessageBox::error(0,msg);
-    setStatusMsg( i18n( "Sending aborted." ) );
+    if (!errString.isEmpty())
+    {
+      QStringList args;
+      args.append( errString );
+      args.append( mMethodStr );
+      KMailClient::instance()
+        ->slotMsgBoxError( KMailClient::ERRORBOX_SENDING_ABORTED, &args );
+    }
+    KMailClient::instance()
+      ->slotSetStatusMsg( KMailClient::STATUSMSG_SEND_ABORTED );
   } else {
     if (!mSendProc->sendOk()) {
       mCurrentMsg->setTransferInProgress( false );
@@ -531,6 +534,7 @@ void KMSender::slotIdle()
       if (!errString.isEmpty()) {
         int res = KMessageBox::Yes;
         if (mSentMessages+mFailedMessages != mTotalMessages) {
+	  // TODO  - this needs some more effort to separate kernel and GUI
           msg = i18n("<p>Sending failed:</p>"
             "<p>%1</p>"
             "<p>The message will stay in the 'outbox' folder until you either "
@@ -540,25 +544,23 @@ void KMSender::slotIdle()
             "<p>Do you want me to continue sending the remaining messages?</p>")
             .arg(errString)
             .arg(mMethodStr);
-          res = KMessageBox::warningYesNo( 0 , msg ,
+          res = KMailClient::instance()->showMsgBoxWarningYesNo( msg ,
                   i18n( "Continue sending" ), i18n( "&Continue sending" ),
                   i18n("&Abort sending") );
         } else {
-          msg = i18n("Sending failed:\n%1\n"
-            "The message will stay in the 'outbox' folder until you either "
-            "fix the problem (e.g. a broken address) or remove the message "
-            "from the 'outbox' folder.\n"
-            "The following transport protocol was used:\n %2")
-            .arg(errString)
-            .arg(mMethodStr);
-          KMessageBox::error(0,msg);
+          QStringList args;
+          args.append( errString );
+          args.append( mMethodStr );
+          KMailClient::instance()
+            ->slotMsgBoxError( KMailClient::ERRORBOX_SENDING_FAILED, &args );
         }
         if (res == KMessageBox::Yes) {
           // Try the next one.
           doSendMsg();
           return;
         } else {
-          setStatusMsg( i18n( "Sending aborted." ) );
+          KMailClient::instance()
+            ->slotSetStatusMsg( KMailClient::STATUSMSG_SEND_ABORTED );
         }
       }
     } else {
@@ -818,7 +820,7 @@ QCString KMSendProc::prepareStr(const QC
 //-----------------------------------------------------------------------------
 void KMSendProc::statusMsg(const QString& aMsg)
 {
-  if (mSender) mSender->setStatusMsg(aMsg);
+  KMailClient::instance()->slotSetStatusMsg( aMsg );
 }
 
 //-----------------------------------------------------------------------------
@@ -850,16 +852,12 @@ void KMSendSendmail::start(void)
 {
   if (mSender->transportInfo()->host.isEmpty())
   {
-    QString str = i18n("Please specify a mailer program in the settings.");
-    QString msg;
-    msg = i18n("Sending failed:\n%1\n"
-	"The message will stay in the 'outbox' folder and will be resent.\n"
-        "Please remove it from there if you do not want the message to "
-		"be resent.\n"
-	"The following transport protocol was used:\n  %2")
-    .arg(str + "\n")
-    .arg("sendmail://");
-    KMessageBox::information(0,msg);
+    QStringList args;
+    args.append( i18n( "Please specify a mailer program in the settings." )
+                 + "\n" );
+    args.append("sendmail://");
+    KMailClient::instance()
+      ->slotMsgBoxInformation( KMailClient::INFOBOX_MAILER_UNSPECIFIED, &args );
     emit started(false);
     return;
   }
@@ -923,8 +921,9 @@ bool KMSendSendmail::send(KMMessage* aMs
 
   if (!mMailerProc->start(KProcess::NotifyOnExit,KProcess::All))
   {
-    KMessageBox::information(0,i18n("Failed to execute mailer program %1")
-			     .arg(mSender->transportInfo()->host));
+    QStringList args( mSender->transportInfo()->host);
+    KMailClient::instance()
+      ->slotMsgBoxInformation( KMailClient::INFOBOX_MAILER_EXEC_FAILED, &args );
     return FALSE;
   }
   mMsgPos  = mMsgStr.data();
@@ -1070,6 +1069,7 @@ bool KMSendSMTP::send(KMMessage *aMsg)
       int result;
 
       KCursorSaver idle(KBusyPtr::idle());
+// TODO - this needs effort to separate kernel and GUI
       result = KIO::PasswordDialog::getNameAndPassword(ti->user, ti->pass,
 	&b, i18n("You need to supply a username and a password to use this "
 	     "SMTP server."), FALSE, QString::null, ti->name, QString::null);


_______________________________________________
KMail Developers mailing list
kmail@mail.kde.org
http://mail.kde.org/mailman/listinfo/kmail


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

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