Update of /cvsroot/mahogany/M/src/mail
In directory usw-pr-cvs1:/tmp/cvs-serv7121/src/mail

Modified Files:
        ASMailFolder.cpp MailFolder.cpp MailFolderCC.cpp 
        MailFolderCmn.cpp 
Added Files:
        MFDriver.cpp MFPool.cpp 
Removed Files:
        MMailFolder.cpp 
Log Message:
redesigned the MailFolder and related classes to virtualize access to the folder
functions via folder drivers -- this should allow to plug in a virtual folder
implementation easily now

WARNING: many untested changes, don't use for now for anything but testing!


***** Error reading new file: [Errno 2] No such file or directory: 'MFDriver.cpp'
***** Error reading new file: [Errno 2] No such file or directory: 'MFPool.cpp'
Index: ASMailFolder.cpp
===================================================================
RCS file: /cvsroot/mahogany/M/src/mail/ASMailFolder.cpp,v
retrieving revision 1.62
retrieving revision 1.63
diff -b -u -2 -r1.62 -r1.63
--- ASMailFolder.cpp    4 Jul 2002 17:22:37 -0000       1.62
+++ ASMailFolder.cpp    14 Jul 2002 23:41:22 -0000      1.63
@@ -41,4 +41,8 @@
 #endif
 
+// subscription code is not used for now and probably broken, so it is not
+// compiled in -- but keep it for the future
+//#define USE_SUBSCRIBE
+
 ASMailFolder::ResultImpl::~ResultImpl()
 {
@@ -605,4 +609,6 @@
 };
 
+#ifdef USE_SUBSCRIBE
+
 class MT_Subscribe : public MailThread
 {
@@ -635,4 +641,6 @@
 };
 
+#endif // USE_SUBSCRIBE
+
 class MT_ListFolders : public MailThread
 {
@@ -958,4 +966,5 @@
    /**@name Subscription management */
    //@{
+#ifdef USE_SUBSCRIBE
    /** Subscribe to a given mailbox (related to the
        mailfolder/mailstream underlying this folder.
@@ -974,4 +983,6 @@
                                   mailboxname, subscribe))->Start();
       }
+#endif // USE_SUBSCRIBE
+
    /** Get a listing of all mailboxes.
        @param pattern a wildcard matching the folders to list
@@ -1134,4 +1145,6 @@
  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
+#ifdef USE_SUBSCRIBE
+
 /** Subscribe to a given mailbox (related to the
     mailfolder/mailstream underlying this folder.
@@ -1149,4 +1162,6 @@
    return ASMailFolderImpl::Subscribe(host, protocol, mailboxname, subscribe, ud);
 }
+
+#endif // USE_SUBSCRIBE
 
 Ticket

Index: MailFolder.cpp
===================================================================
RCS file: /cvsroot/mahogany/M/src/mail/MailFolder.cpp,v
retrieving revision 1.283
retrieving revision 1.284
diff -b -u -2 -r1.283 -r1.284
--- MailFolder.cpp      5 Jul 2002 22:34:16 -0000       1.283
+++ MailFolder.cpp      14 Jul 2002 23:41:22 -0000      1.284
@@ -4,5 +4,5 @@
 //              use cclient (i.e. don't really work with mail folders)
 // Purpose:     handling of mail folders with c-client lib
-// Author:      Karsten Ball�der
+// Author:      Karsten Ball�der, Vadim Zeitlin
 // Modified by:
 // Created:     1997
@@ -16,6 +16,10 @@
 // ============================================================================
 
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
 #ifdef __GNUG__
-#   pragma implementation "MailFolder.h"
+   #pragma implementation "MailFolder.h"
 #endif
 
@@ -50,8 +54,8 @@
 #include "LogCircle.h"
 
-#include "MFFactory.h"
 #include "MFPrivate.h"
-
-#include "MailFolderCC.h"
+#include "mail/Driver.h"
+#include "mail/FolderPool.h"
+#include "mail/ServerInfo.h"
 
 #include "Composer.h"
@@ -64,6 +68,4 @@
 extern const MOption MP_DEFAULT_REPLY_KIND;
 extern const MOption MP_FOLDER_COMMENT;
-extern const MOption MP_FOLDER_LOGIN;
-extern const MOption MP_FOLDER_PASSWORD;
 extern const MOption MP_FOLDER_PATH;
 extern const MOption MP_FOLDER_TYPE;
@@ -79,9 +81,62 @@
 extern const MOption MP_USERNAME;
 
+// ----------------------------------------------------------------------------
+// persistent msgboxes we use here
+// ----------------------------------------------------------------------------
+
+extern const MPersMsgBox *M_MSGBOX_DIALUP_ON_OPEN_FOLDER;
+extern const MPersMsgBox *M_MSGBOX_KEEP_PWD;
+extern const MPersMsgBox *M_MSGBOX_NET_DOWN_OPEN_ANYWAY;
+extern const MPersMsgBox *M_MSGBOX_REMEMBER_PWD;
+
+// ----------------------------------------------------------------------------
+// global static variables
+// ----------------------------------------------------------------------------
+
+ServerInfoEntry::ServerInfoList ServerInfoEntry::ms_servers;
+
+MFSubSystem *MFSubSystem::ms_initilizers = NULL;
+
 // ============================================================================
-// implementation
+// MailFolder implementation
 // ============================================================================
 
-MFFactory *MFFactory::ms_factories = NULL;
+// ----------------------------------------------------------------------------
+// MailFolder initialization and shutdown
+// ----------------------------------------------------------------------------
+
+/* static */
+bool
+MailFolder::Init()
+{
+   for ( MFSubSystem *subsys = MFSubSystem::GetFirst();
+         subsys;
+         subsys = subsys->GetNext() )
+   {
+      if ( !subsys->Init() )
+      {
+         // any error here is fatal because normally we don't do anything
+         // error-prone in the Init() functions
+         return false;
+      }
+   }
+
+   return true;
+}
+
+/* static */
+void
+MailFolder::CleanUp()
+{
+   ServerInfoEntry::DeleteAll();
+   MFPool::DeleteAll();
+
+   for ( MFSubSystem *subsys = MFSubSystem::GetFirst();
+         subsys;
+         subsys = subsys->GetNext() )
+   {
+      subsys->CleanUp();
+   }
+}
 
 // ----------------------------------------------------------------------------
@@ -89,51 +144,169 @@
 // ----------------------------------------------------------------------------
 
+/**
+  initialize all mail subsystems and return the folder driver or NULL if none
+  (logging the error message in this case)
+ */
+static MFDriver *GetFolderDriver(const MFolder *folder)
+{
+   if ( !MailFolder::Init() )
+      return NULL;
+
+   CHECK( folder, NULL, "MailFolder: NULL folder object" );
+
+   const String kind = folder->GetClass();
+
+   // find the creation function for this kind of folders
+   MFDriver *driver = MFDriver::Get(kind);
+   if ( !driver )
+   {
+      ERRORMESSAGE((_("Unknown folder kind '%s'"), kind.c_str()));
+   }
+
+   return driver;
+}
+
 /* static */
 MailFolder *
 MailFolder::OpenFolder(const MFolder *folder, OpenMode mode, wxFrame *frame)
 {
-   if ( !Init() )
+   MFDriver *driver = GetFolderDriver(folder);
+   if ( !driver )
       return NULL;
 
-   CHECK( folder, NULL, "NULL MFolder in OpenFolder()" );
+   // ensure that we have the authentification information for this folder
+   // before trying to open it
+   bool userEnteredPwd = false;
+   String login, password;
+   if ( !GetAuthInfoForFolder(folder, login, password, &userEnteredPwd) )
+   {
+      // can't continue without login/password
+      return NULL;
+   }
 
-   String kind = folder->GetClass();
+   // look if we don't already have it opened
+   //
+   // NB: if the folder is already opened, the password [possibly] asked for
+   //     above is unused and not checked at all and although it's not a
+   //     serious problem it still doesn't look right (fixme)
+   MailFolder *mf = MFPool::Find(driver, folder, login);
+   if ( mf )
+   {
+      // make sure it's updated
+      mf->Ping();
 
-   // this is a hack needed for backwards compatibility and which also allows
-   // to have the empty default value for the folder class which saves quite
-   // some bytes in the profile
-   if ( kind.empty() )
+      // the connection could have been lost from inside Ping() so check for it
+      // yet again and reopen the folder if this is what happened
+      if ( !mf->IsOpened() )
    {
-      kind = "cclient";
+         mf->DecRef();
+         mf = NULL;
+      }
    }
 
-   // find the creation function for this kind of folders
-   for ( const MFFactory *factory = MFFactory::GetHead();
-         factory;
-         factory = factory->GetNext() )
+   if ( !mf )
    {
-      if ( factory->GetName() == kind )
+      // check whether this folder is accessible
+      if ( !CheckNetwork(folder, frame) )
       {
-         return factory->OpenFolder(folder, mode, frame);
+         return NULL;
+      }
+
+      // and now do [try to] open the folder
+      mf = driver->OpenFolder(folder, login, password, mode, frame);
+
+      // if we have succeeded to open the folder ...
+      if ( mf )
+      {
+         // ... add it to the pool
+         MFPool::Add(driver, mf, folder, login);
+
+         // ... and propose to the user to save the password so that he doesn't
+         // have to enter it again during the subsequent attempts to open it
+         if ( userEnteredPwd )
+         {
+            // const_cast needed, unfortunately
+            ProposeSavePassword(mf, (MFolder *)folder, login, password);
+         }
       }
    }
 
-   ERRORMESSAGE((_("Unknown folder kind '%s'"), kind.c_str()));
+   return mf;
+}
 
-   return NULL;
+/* static */
+bool MailFolder::CheckNetwork(const MFolder *folder, wxFrame *frame)
+{
+#ifdef USE_DIALUP
+   // check if we need to dial up to open this folder
+   if ( folder->NeedsNetwork() && !mApplication->IsOnline() )
+   {
+      String msg;
+      msg.Printf(_("To open the folder '%s', network access is required "
+                   "but it is currently not available.\n"
+                   "Would you like to connect to the network now?"),
+                 folder->GetFullName().c_str());
+
+      if ( MDialog_YesNoDialog(msg,
+                               frame,
+                               MDIALOG_YESNOTITLE,
+                               M_DLG_YES_DEFAULT,
+                               M_MSGBOX_DIALUP_ON_OPEN_FOLDER,
+                               folder->GetFullName()) )
+      {
+         mApplication->GoOnline();
+      }
+
+      if ( !mApplication->IsOnline() ) // still not?
+      {
+         if ( !MDialog_YesNoDialog(_("Opening this folder will probably fail!\n"
+                                     "Continue anyway?"),
+                                   frame,
+                                   MDIALOG_YESNOTITLE,
+                                   M_DLG_NO_DEFAULT,
+                                   M_MSGBOX_NET_DOWN_OPEN_ANYWAY,
+                                   folder->GetFullName()) )
+         {
+             // remember that the user cancelled opening the folder
+             mApplication->SetLastError(M_ERROR_CANCEL);
+
+             return false;
+         }
+      }
+   }
+#endif // USE_DIALUP
+
+   return true;
 }
 
 /* static */
 bool
-MailFolder::CloseFolder(const MFolder *mfolder)
+MailFolder::CloseFolder(const MFolder *folder)
 {
-   // don't try to close the folder which can't be opened
-   if ( !mfolder->CanOpen() )
+   CHECK( folder, false, "MailFolder::CloseFolder(): NULL folder" );
+
+   // don't try to close the folder which can't be opened, it's a NOOP
+   if ( !folder->CanOpen() )
    {
       return true;
    }
 
-   // for now there is only one implementation to call:
-   return MailFolderCC::CloseFolder(mfolder);
+   MFDriver *driver = GetFolderDriver(folder);
+   if ( !driver )
+      return false;
+
+   // find the associated open folder
+   //
+   // FIXME: using GetLogin() here is wrong, see comments before MFPool::Find()
+   MailFolder_obj mf = MFPool::Find(driver, folder, folder->GetLogin());
+   if ( !mf )
+   {
+      // nothing to close
+      return false;
+   }
+
+   mf->Close();
+
+   return true;
 }
 
@@ -142,41 +315,37 @@
 MailFolder::CloseAll()
 {
-   MailFolder **mfOpened = GetAllOpened();
-   if ( !mfOpened )
-      return 0;
-
+   // count the number of folders we close
    size_t n = 0;
-   while ( mfOpened[n] )
-   {
-      MailFolder *mf = mfOpened[n++];
 
+   MFPool::Cookie cookie;
+   for ( MailFolder *mf = MFPool::GetFirst(cookie);
+         mf;
+         mf = MFPool::GetNext(cookie) )
+   {
       mf->Close();
 
       // notify any opened folder views
       MEventManager::Send(new MEventFolderClosedData(mf));
-   }
 
-   delete [] mfOpened;
+      mf->DecRef();
+   }
 
    return n;
 }
 
+
 // ----------------------------------------------------------------------------
 // MailFolder: other operations
 // ----------------------------------------------------------------------------
 
-/* static */
-MailFolder **
-MailFolder::GetAllOpened()
-{
-   // if we ever have other kinds of folders, we should append them to the
-   // same array too
-   return MailFolderCC::GetAllOpened();
-}
-
 MailFolder *
 MailFolder::GetOpenedFolderFor(const MFolder *folder)
 {
-   return MailFolderCC::FindFolder(folder);
+   MFDriver *driver = GetFolderDriver(folder);
+   if ( !driver )
+      return NULL;
+
+   // FIXME: login problem again
+   return MFPool::Find(driver, folder, folder->GetLogin());
 }
 
@@ -184,13 +353,11 @@
 bool MailFolder::PingAllOpened(wxFrame *frame)
 {
-   MailFolder **mfOpened = GetAllOpened();
-   if ( !mfOpened )
-      return 0;
-
    bool rc = true;
-   for ( size_t n = 0; mfOpened[n]; n++ )
-   {
-      MailFolder *mf = mfOpened[n];
 
+   MFPool::Cookie cookie;
+   for ( MailFolder *mf = MFPool::GetFirst(cookie);
+         mf;
+         mf = MFPool::GetNext(cookie) )
+   {
       NonInteractiveLock noInter(mf, frame != NULL);
 
@@ -200,7 +367,7 @@
          rc = false;
       }
-   }
 
-   delete [] mfOpened;
+      mf->DecRef();
+   }
 
    return rc;
@@ -211,7 +378,4 @@
 MailFolder::CheckFolder(const MFolder *folder, wxFrame *frame)
 {
-   if ( !Init() )
-      return false;
-
    bool rc;
 
@@ -227,6 +391,8 @@
    else // not opened
    {
+      MFDriver *driver = GetFolderDriver(folder);
+
       // check its status without opening it
-      return MailFolderCC::CheckStatus(folder);
+      rc = driver ? driver->CheckFolder(folder) : false;
    }
 
@@ -236,21 +402,18 @@
 /* static */
 bool
-MailFolder::DeleteFolder(const MFolder *mfolder)
+MailFolder::DeleteFolder(const MFolder *folder)
 {
-   if ( !Init() )
-      return false;
+   MFDriver *driver = GetFolderDriver(folder);
 
-   // for now there is only one implementation to call:
-   return MailFolderCC::DeleteFolder(mfolder);
+   return driver ? driver->DeleteFolder(folder) : false;
 }
 
 /* static */
 bool
-MailFolder::Rename(const MFolder *mfolder, const String& name)
+MailFolder::Rename(const MFolder *folder, const String& name)
 {
-   if ( !Init() )
-      return false;
+   MFDriver *driver = GetFolderDriver(folder);
 
-   return MailFolderCC::Rename(mfolder, name);
+   return driver ? driver->RenameFolder(folder, name) : false;
 }
 
@@ -259,51 +422,25 @@
 MailFolder::ClearFolder(const MFolder *folder)
 {
-   if ( !Init() )
-      return -1;
+   MFDriver *driver = GetFolderDriver(folder);
 
-   // for now there is only one implementation to call:
-   return MailFolderCC::ClearFolder(folder);
+   return driver ? driver->ClearFolder(folder) : -1;
 }
 
 /* static */
-bool
-MailFolder::CreateFolder(const String& name,
-                         MFolderType type,
-                         int flags,
-                         const String &path,
-                         const String &comment)
+bool MailFolder::CanExit(String *which)
 {
-   if ( !Init() )
-      return false;
-
-   MFolder *mfolder = MFolder::Create(name, type);
-   if ( !mfolder )
-      return false;
-
-   mfolder->SetFlags(flags);
-   mfolder->SetPath(path);
-   mfolder->SetComment(comment);
-
-   // So far the drivers do an auto-create when we open a folder, so now we
-   // attempt to open the folder to see what happens:
-   MailFolder_obj mf = MailFolder::OpenFolder(mfolder);
-
-   mfolder->DecRef();
+   bool rc = true;
 
-   return true;
-}
+   MFPool::Cookie cookie;
+   for ( MailFolder *mf = MFPool::GetFirst(cookie);
+         mf && rc;
+         mf = MFPool::GetNext(cookie) )
+   {
+      rc = !mf->IsInCriticalSection();
 
-/* static */
-bool MailFolder::CanExit(String *which)
-{
-   return MailFolderCC::CanExit(which);
-}
+      mf->DecRef();
+   }
 
-/* static */
-bool
-MailFolder::Subscribe(const String &host, MFolderType protocol,
-                      const String &mailboxname, bool subscribe)
-{
-   return MailFolderCC::Subscribe(host, protocol, mailboxname, subscribe);
+   return rc;
 }
 
@@ -937,43 +1074,5 @@
 
 // ----------------------------------------------------------------------------
-// static functions used by MailFolder
-// ----------------------------------------------------------------------------
-
-/* static */
-bool
-MailFolder::Init()
-{
-   static bool s_initialized = false;
-
-   if ( !s_initialized )
-   {
-      if ( !MailFolderCmnInit() || !MailFolderCCInit() )
-      {
-         wxLogError(_("Failed to initialize mail subsystem."));
-      }
-      else // remember that we're initialized now
-      {
-#ifdef USE_DIALUP
-         // must be done before using the network
-         mApplication->SetupOnlineManager();
-#endif // USE_DIALUP
-
-         s_initialized = true;
-      }
-   }
-
-   return s_initialized;
-}
-
-/* static */
-void
-MailFolder::CleanUp(void)
-{
-   MailFolderCmnCleanup();
-   MailFolderCCCleanup();
-}
-
-// ----------------------------------------------------------------------------
-// misc
+// misc static MailFolder methods
 // ----------------------------------------------------------------------------
 
@@ -1044,4 +1143,109 @@
 
    return s_LogCircle;
+}
+
+// ----------------------------------------------------------------------------
+// MailFolder login/password management
+// ----------------------------------------------------------------------------
+
+/* static */
+void
+MailFolder::ProposeSavePassword(MailFolder *mf,
+                                MFolder *folder,
+                                const String& login,
+                                const String& password)
+{
+   // do not process the events while we're showing the dialog boxes below:
+   // this could lead to the new calls to OpenFolder() which would be bad as
+   // we're called from inside OpenFolder()
+   MEventManagerSuspender suspendEvents;
+
+   // ask the user if he'd like to remember the password for the future:
+   // this is especially useful for the folders created initially by the
+   // setup wizard as it doesn't ask for the passwords
+   if ( MDialog_YesNoDialog
+        (
+         String::Format
+         (
+          _("Would you like to permanently remember the password "
+            "for the folder '%s'?\n"
+            "(WARNING: don't do it if you are concerned about security)"),
+            mf->GetName().c_str()
+         ),
+         NULL,
+         MDIALOG_YESNOTITLE,
+         M_DLG_YES_DEFAULT,
+         M_MSGBOX_REMEMBER_PWD,
+         mf->GetName()
+        ) )
+   {
+      folder->SetAuthInfo(login, password);
+   }
+   else // don't save password permanently
+   {
+      // but should we keep it at least during this session?
+      if ( MDialog_YesNoDialog
+           (
+               _("Should the password be kept in memory during this\n"
+                 "session only (it won't be saved to a disk file)?\n"
+                 "If you answer \"No\", you will be asked for the password\n"
+                 "each time when the folder is accessed."),
+               NULL,
+               MDIALOG_YESNOTITLE,
+               M_DLG_YES_DEFAULT,
+               M_MSGBOX_KEEP_PWD,
+               mf->GetName()
+           ) )
+      {
+         ServerInfoEntry *server = ServerInfoEntry::GetOrCreate(folder, mf);
+         CHECK_RET( server, "folder which has login must have server as well!" );
+
+         server->SetAuthInfo(login, password);
+      }
+      //else: don't keep the login info even in memory
+   }
+}
+
+/* static */
+bool MailFolder::GetAuthInfoForFolder(const MFolder *mfolder,
+                                      String& login,
+                                      String& password,
+                                      bool *userEnteredPwd)
+{
+   if ( !mfolder->NeedsLogin() )
+   {
+      // nothing to do then, everything is already fine
+      return true;
+   }
+
+   login = mfolder->GetLogin();
+   password = mfolder->GetPassword();
+
+   if ( login.empty() || password.empty() )
+   {
+      // do we already have login/password for this folder?
+      ServerInfoEntry *server = ServerInfoEntry::Get(mfolder);
+      if ( server && server->GetAuthInfo(login, password) )
+      {
+         return true;
+      }
+
+      // we don't have password for this folder, ask the user about it
+      if ( !MDialog_GetPassword(mfolder->GetFullName(), &login, &password) )
+      {
+         ERRORMESSAGE((_("Cannot access this folder without a password.")));
+
+         mApplication->SetLastError(M_ERROR_CANCEL);
+
+         return false;
+      }
+
+      // remember that the password was entered interactively and propose to
+      // remember it if it really works later
+      if ( userEnteredPwd )
+         *userEnteredPwd = true;
+   }
+
+   return true;
 }
 

Index: MailFolderCC.cpp
===================================================================
RCS file: /cvsroot/mahogany/M/src/mail/MailFolderCC.cpp,v
retrieving revision 1.629
retrieving revision 1.630
diff -b -u -2 -r1.629 -r1.630
--- MailFolderCC.cpp    5 Jul 2002 17:58:35 -0000       1.629
+++ MailFolderCC.cpp    14 Jul 2002 23:41:22 -0000      1.630
@@ -60,5 +60,8 @@
 #include "Sequence.h"
 
-#include "MFFactory.h"
+#include "MFPrivate.h"
+#include "mail/Driver.h"
+#include "mail/FolderPool.h"
+#include "mail/ServerInfo.h"
 
 // just to use wxFindFirstFile()/wxFindNextFile() for lockfile checking and
@@ -133,11 +136,4 @@
 //@}
[...1205 lines suppressed...]
+   // any connections left?
+   return !m_connections.empty();
 }
 
 /* static */
-void ServerInfoEntry::CheckTimeoutAll()
+void ServerInfoEntryCC::CheckTimeoutAll()
 {
    bool hasAnyConns = false;
@@ -6779,9 +6234,6 @@
          ++i )
    {
-      // micro optimization
-      if ( !i->m_connections.empty() )
+      if ( i->CheckTimeout() )
       {
-         i->CheckTimeout();
-
          hasAnyConns = true;
       }

Index: MailFolderCmn.cpp
===================================================================
RCS file: /cvsroot/mahogany/M/src/mail/MailFolderCmn.cpp,v
retrieving revision 1.97
retrieving revision 1.98
diff -b -u -2 -r1.97 -r1.98
--- MailFolderCmn.cpp   5 Jul 2002 16:56:18 -0000       1.97
+++ MailFolderCmn.cpp   14 Jul 2002 23:41:23 -0000      1.98
@@ -64,4 +64,5 @@
 #include "MailFolderCmn.h"
 #include "MFPrivate.h"
+#include "mail/FolderPool.h"
 
 #include <wx/timer.h>
@@ -481,4 +482,9 @@
 void MailFolderCmn::Close(void)
 {
+   // this folder shouldn't be reused by MailFolder::OpenFolder() any more as
+   // it is being closed anyhow, so prevent this from happening by removing it
+   // from the pool of available folders
+   MFPool::Remove(this);
+
    if ( m_headers )
    {
@@ -2125,5 +2131,5 @@
 // ----------------------------------------------------------------------------
 
-extern bool MailFolderCmnInit()
+bool MailFolderCmnInit()
 {
    if ( !gs_MailFolderCloser )
@@ -2135,5 +2141,5 @@
 }
 
-extern void MailFolderCmnCleanup()
+void MailFolderCmnCleanup()
 {
    if ( gs_MailFolderCloser )
@@ -2147,3 +2153,5 @@
    }
 }
+
+static MFSubSystem gs_subsysCmn(MailFolderCmnInit, MailFolderCmnCleanup);
 

--- MMailFolder.cpp DELETED ---



-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
Mahogany-cvsupdates mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/mahogany-cvsupdates

Reply via email to