Back to index

lightning-sunbird  0.9+nobinonly
nsMsgAccountManager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Alec Flett <alecf@netscape.com>
00024  *   Seth Spitzer <sspitzer@netscape.com>
00025  *   Bhuvan Racham <racham@netscape.com>
00026  *   David Bienvenu <bienvenu@mozilla.org>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 /*
00043  * The account manager service - manages all accounts, servers, and identities
00044  */
00045 
00046 #include "nsIComponentManager.h"
00047 #include "nsIServiceManager.h"
00048 #include "nsISupportsArray.h"
00049 #include "nsMsgAccountManager.h"
00050 #include "nsMsgBaseCID.h"
00051 #include "nsMsgCompCID.h"
00052 #include "nsMsgDBCID.h"
00053 #include "prmem.h"
00054 #include "prcmon.h"
00055 #include "prthread.h"
00056 #include "plstr.h"
00057 #include "nsString.h"
00058 #include "nsXPIDLString.h"
00059 #include "nsUnicharUtils.h"
00060 #include "nscore.h"
00061 #include "nsEscape.h"
00062 #include "nsCRT.h"  // for nsCRT::strtok
00063 #include "prprf.h"
00064 #include "nsIMsgFolderCache.h"
00065 #include "nsFileStream.h"
00066 #include "nsIFileStreams.h"
00067 #include "nsMsgUtils.h"
00068 #include "nsIFileSpec.h" 
00069 #include "nsILocalFile.h"
00070 #include "nsIURL.h"
00071 #include "nsNetCID.h"
00072 #include "nsIPrefService.h"
00073 #include "nsIPrefBranch.h"
00074 #include "nsISmtpService.h"
00075 #include "nsIMsgBiffManager.h"
00076 #include "nsIMsgPurgeService.h"
00077 #include "nsIObserverService.h"
00078 #include "nsIMsgMailSession.h"
00079 #include "nsIEventQueueService.h"
00080 #include "nsIDirectoryService.h"
00081 #include "nsAppDirectoryServiceDefs.h"
00082 #include "nsMsgFolderFlags.h"
00083 #include "nsIRDFService.h"
00084 #include "nsRDFCID.h"
00085 #include "nsIImapIncomingServer.h" 
00086 #include "nsIImapUrl.h"
00087 #include "nsIMessengerOSIntegration.h"
00088 #include "nsICategoryManager.h"
00089 #include "nsISupportsPrimitives.h"
00090 #include "nsMsgFilterService.h"
00091 #include "nsIMsgFilter.h"
00092 #include "nsIMsgSearchSession.h"
00093 #include "nsIDBChangeListener.h"
00094 #include "nsIDBFolderInfo.h"
00095 #include "nsIMsgHdr.h"
00096 #include "nsILineInputStream.h"
00097 #include "nsNetUtil.h"
00098 #include "nsEscape.h"
00099 
00100 #define PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS "mail.accountmanager.accounts"
00101 #define PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT "mail.accountmanager.defaultaccount"
00102 #define PREF_MAIL_ACCOUNTMANAGER_LOCALFOLDERSSERVER "mail.accountmanager.localfoldersserver"
00103 #define PREF_MAIL_SERVER_PREFIX "mail.server."
00104 #define ACCOUNT_PREFIX "account"
00105 #define SERVER_PREFIX "server"
00106 #define ID_PREFIX "id"
00107 #define ABOUT_TO_GO_OFFLINE_TOPIC "network:offline-about-to-go-offline"
00108 #define ACCOUNT_DELIMITER ","
00109 #define APPEND_ACCOUNTS_VERSION_PREF_NAME "append_preconfig_accounts.version"
00110 #define MAILNEWS_ROOT_PREF "mailnews."
00111 #define PREF_MAIL_ACCOUNTMANAGER_APPEND_ACCOUNTS "mail.accountmanager.appendaccounts"
00112 
00113 static NS_DEFINE_CID(kMsgAccountCID, NS_MSGACCOUNT_CID);
00114 static NS_DEFINE_CID(kMsgFolderCacheCID, NS_MSGFOLDERCACHE_CID);
00115 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00116 
00117 // use this to search for all servers with the given hostname/iid and
00118 // put them in "servers"
00119 typedef struct _findServerEntry {
00120   const char *hostname;
00121   const char *username;
00122   PRInt32 port;
00123   const char *type;
00124   PRBool useRealSetting;
00125   nsIMsgIncomingServer *server;
00126 } findServerEntry;
00127 
00128 typedef struct _findServerByKeyEntry {
00129   const char *key;
00130   PRInt32 index;
00131 } findServerByKeyEntry;
00132 
00133 // use this to search for all servers that match "server" and
00134 // put all identities in "identities"
00135 typedef struct _findIdentitiesByServerEntry {
00136   nsISupportsArray *identities;
00137   nsIMsgIncomingServer *server;
00138 } findIdentitiesByServerEntry;
00139 
00140 typedef struct _findServersByIdentityEntry {
00141   nsISupportsArray *servers;
00142   nsIMsgIdentity *identity;
00143 } findServersByIdentityEntry;
00144 
00145 typedef struct _findAccountByKeyEntry {
00146     const char* key;
00147     nsIMsgAccount* account;
00148 } findAccountByKeyEntry;
00149 
00150 
00151 NS_IMPL_THREADSAFE_ISUPPORTS5(nsMsgAccountManager,
00152                               nsIMsgAccountManager,
00153                               nsIObserver,
00154                               nsISupportsWeakReference,
00155                               nsIUrlListener,
00156                               nsIFolderListener)
00157 
00158 nsMsgAccountManager::nsMsgAccountManager() :
00159   m_accountsLoaded(PR_FALSE),
00160   m_emptyTrashInProgress(PR_FALSE),
00161   m_cleanupInboxInProgress(PR_FALSE),
00162   m_haveShutdown(PR_FALSE),
00163   m_shutdownInProgress(PR_FALSE),
00164   m_userAuthenticated(PR_FALSE),
00165   m_loadingVirtualFolders(PR_FALSE),
00166   m_virtualFoldersLoaded(PR_FALSE)
00167 {
00168 }
00169 
00170 nsMsgAccountManager::~nsMsgAccountManager()
00171 {
00172   nsresult rv;
00173 
00174   if(!m_haveShutdown)
00175   {
00176     Shutdown();
00177     //Don't remove from Observer service in Shutdown because Shutdown also gets called
00178     //from xpcom shutdown observer.  And we don't want to remove from the service in that case.
00179     nsCOMPtr<nsIObserverService> observerService = 
00180          do_GetService("@mozilla.org/observer-service;1", &rv);
00181     if (NS_SUCCEEDED(rv))
00182     {    
00183       observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
00184       observerService->RemoveObserver(this, ABOUT_TO_GO_OFFLINE_TOPIC);
00185     }
00186   }
00187 }
00188 
00189 nsresult nsMsgAccountManager::Init()
00190 {
00191   nsresult rv;
00192 
00193   rv = NS_NewISupportsArray(getter_AddRefs(m_accounts));
00194   if(NS_FAILED(rv)) return rv;
00195 
00196   rv = NS_NewISupportsArray(getter_AddRefs(mFolderListeners));
00197 
00198   nsCOMPtr<nsIObserverService> observerService = 
00199            do_GetService("@mozilla.org/observer-service;1", &rv);
00200   if (NS_SUCCEEDED(rv))
00201   {    
00202     observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE);
00203     observerService->AddObserver(this, "quit-application" , PR_TRUE);
00204     observerService->AddObserver(this, ABOUT_TO_GO_OFFLINE_TOPIC, PR_TRUE);
00205     observerService->AddObserver(this, "session-logout", PR_TRUE);
00206     observerService->AddObserver(this, "profile-before-change", PR_TRUE);
00207   }
00208 
00209   return NS_OK;
00210 }
00211 
00212 nsresult nsMsgAccountManager::Shutdown()
00213 {
00214   if (m_haveShutdown)     // do not shutdown twice
00215     return NS_OK;
00216 
00217   nsresult rv;
00218 
00219   SaveVirtualFolders();
00220     
00221   nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
00222   if (msgDBService)
00223   {
00224     PRInt32 numVFListeners = m_virtualFolderListeners.Count();
00225     for(PRInt32 i = 0; i < numVFListeners; i++)
00226       msgDBService->UnregisterPendingListener(m_virtualFolderListeners[i]);
00227   }
00228   if(m_msgFolderCache)
00229     WriteToFolderCache(m_msgFolderCache);
00230   (void)ShutdownServers();
00231   (void)UnloadAccounts();
00232   
00233   //shutdown removes nsIIncomingServer listener from biff manager, so do it after accounts have been unloaded
00234   nsCOMPtr<nsIMsgBiffManager> biffService = do_GetService(NS_MSGBIFFMANAGER_CONTRACTID, &rv);
00235   if (NS_SUCCEEDED(rv) && biffService)
00236     biffService->Shutdown();
00237 
00238   //shutdown removes nsIIncomingServer listener from purge service, so do it after accounts have been unloaded
00239   nsCOMPtr<nsIMsgPurgeService> purgeService = do_GetService(NS_MSGPURGESERVICE_CONTRACTID, &rv);
00240   if (NS_SUCCEEDED(rv) && purgeService)
00241     purgeService->Shutdown();
00242   
00243   m_msgFolderCache = nsnull;
00244 
00245   m_haveShutdown = PR_TRUE;
00246   return NS_OK;
00247 }
00248 
00249 NS_IMETHODIMP
00250 nsMsgAccountManager::GetShutdownInProgress(PRBool *_retval)
00251 {
00252     NS_ENSURE_ARG_POINTER(_retval);
00253     *_retval = m_shutdownInProgress;
00254     return NS_OK;
00255 }
00256 
00257 NS_IMETHODIMP
00258 nsMsgAccountManager::GetUserNeedsToAuthenticate(PRBool *aRetval)
00259 {
00260   NS_ENSURE_ARG_POINTER(aRetval);
00261   if (!m_userAuthenticated)
00262     return m_prefs->GetBoolPref("mail.password_protect_local_cache", aRetval);
00263   *aRetval = !m_userAuthenticated;
00264   return NS_OK;
00265 }
00266 
00267 NS_IMETHODIMP
00268 nsMsgAccountManager::SetUserNeedsToAuthenticate(PRBool aUserNeedsToAuthenticate)
00269 {
00270     m_userAuthenticated = !aUserNeedsToAuthenticate;
00271     return NS_OK;
00272 }
00273 
00274 NS_IMETHODIMP nsMsgAccountManager::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
00275 {
00276   if(!nsCRT::strcmp(aTopic,NS_XPCOM_SHUTDOWN_OBSERVER_ID))
00277   {
00278     Shutdown();
00279     return NS_OK;
00280   }
00281   
00282   if (!nsCRT::strcmp(aTopic,"quit-application"))
00283   {
00284     m_shutdownInProgress = PR_TRUE;
00285     return NS_OK;
00286   }
00287   
00288   if (!nsCRT::strcmp(aTopic, ABOUT_TO_GO_OFFLINE_TOPIC))
00289   {
00290     nsAutoString dataString(NS_LITERAL_STRING("offline"));
00291     if (someData)
00292     {
00293       nsAutoString someDataString(someData);
00294       if (dataString == someDataString)
00295         CloseCachedConnections();
00296     }
00297     return NS_OK;
00298   }
00299   
00300   if (!nsCRT::strcmp(aTopic, "session-logout"))
00301   {
00302     m_incomingServers.Enumerate(hashLogoutOfServer, nsnull);
00303     return NS_OK;
00304   }
00305   
00306   if (!nsCRT::strcmp(aTopic, "profile-before-change"))
00307   {
00308     Shutdown();
00309     return NS_OK;
00310   }
00311     
00312  return NS_OK;
00313 }
00314 
00315 nsresult
00316 nsMsgAccountManager::getPrefService()
00317 {
00318 
00319   // get the prefs service
00320   nsresult rv = NS_OK;
00321   
00322   if (!m_prefs)
00323     m_prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
00324 
00325   if (NS_FAILED(rv)) return rv;
00326   /* m_prefs is good now */
00327   return NS_OK;
00328 }
00329 
00330 void
00331 nsMsgAccountManager::getUniqueKey(const char* prefix,
00332                                   nsHashtable *hashTable,
00333                                   nsCString& aResult)
00334 {
00335   PRInt32 i=1;
00336   PRBool unique=PR_FALSE;
00337 
00338   do {
00339     aResult=prefix;
00340     aResult.AppendInt(i++);
00341     nsCStringKey hashKey(aResult);
00342     void* hashElement = hashTable->Get(&hashKey);
00343     
00344     if (!hashElement) unique=PR_TRUE;
00345   } while (!unique);
00346 
00347 }
00348 
00349 void
00350 nsMsgAccountManager::getUniqueAccountKey(const char *prefix,
00351                                          nsISupportsArray *accounts,
00352                                          nsCString& aResult)
00353 {
00354   PRInt32 i=1;
00355   PRBool unique = PR_FALSE;
00356   
00357   findAccountByKeyEntry findEntry;
00358   findEntry.account = nsnull;
00359   
00360   do {
00361       aResult = prefix;
00362       aResult.AppendInt(i++);
00363       findEntry.key = aResult.get();
00364     
00365     accounts->EnumerateForwards(findAccountByKey, (void *)&findEntry);
00366 
00367     if (!findEntry.account) unique=PR_TRUE;
00368     findEntry.account = nsnull;
00369   } while (!unique);
00370 
00371 }
00372 
00373 nsresult
00374 nsMsgAccountManager::CreateIdentity(nsIMsgIdentity **_retval)
00375 {
00376   NS_ENSURE_ARG_POINTER(_retval);
00377   
00378   nsresult rv;
00379 
00380   nsCAutoString key;
00381   getUniqueKey(ID_PREFIX, &m_identities, key);
00382 
00383   rv = createKeyedIdentity(key.get(), _retval);
00384 
00385   return rv;
00386 }
00387 
00388 nsresult
00389 nsMsgAccountManager::GetIdentity(const char* key,
00390                                  nsIMsgIdentity **_retval)
00391 {
00392   if (!_retval) return NS_ERROR_NULL_POINTER;
00393   // null or empty key does not return an identity!
00394   if (!key || !key[0]) {
00395     *_retval = nsnull;
00396     return NS_OK;
00397   }
00398 
00399   nsresult rv;
00400   // check for the identity in the hash table
00401   nsCStringKey hashKey(key);
00402   nsISupports *idsupports = (nsISupports*)m_identities.Get(&hashKey);
00403   nsCOMPtr<nsIMsgIdentity> identity = do_QueryInterface(idsupports, &rv);
00404 
00405   if (NS_SUCCEEDED(rv)) {
00406     NS_ADDREF(*_retval = identity);
00407     return NS_OK;
00408   }
00409 
00410   // identity doesn't exist. create it.
00411   rv = createKeyedIdentity(key, _retval);
00412 
00413   return rv;
00414 }
00415 
00416 /*
00417  * the shared identity-creation code
00418  * create an identity and add it to the accountmanager's list.
00419  */
00420 nsresult
00421 nsMsgAccountManager::createKeyedIdentity(const char* key,
00422                                          nsIMsgIdentity ** aIdentity)
00423 {
00424   nsresult rv;
00425   nsCOMPtr<nsIMsgIdentity> identity =
00426       do_CreateInstance(NS_MSGIDENTITY_CONTRACTID, &rv);
00427   if (NS_FAILED(rv)) return rv;
00428   
00429   identity->SetKey(key);
00430   
00431   nsCStringKey hashKey(key);
00432 
00433   // addref for the hash table`
00434   nsISupports* idsupports = identity;
00435   NS_ADDREF(idsupports);
00436   m_identities.Put(&hashKey, (void *)idsupports);
00437 
00438   NS_ADDREF(*aIdentity = identity);
00439   
00440   return NS_OK;
00441 }
00442 
00443 nsresult
00444 nsMsgAccountManager::CreateIncomingServer(const char* username,
00445                                           const char* hostname,
00446                                           const char* type,
00447                                           nsIMsgIncomingServer **_retval)
00448 {
00449   NS_ENSURE_ARG_POINTER(_retval);
00450 
00451   nsresult rv = LoadAccounts();
00452   if (NS_FAILED(rv)) return rv;
00453 
00454   nsCAutoString key;
00455   getUniqueKey(SERVER_PREFIX, &m_incomingServers, key);
00456   return createKeyedServer(key.get(), username, hostname, type, _retval);
00457 }
00458 
00459 nsresult
00460 nsMsgAccountManager::GetIncomingServer(const char* key,
00461                                        nsIMsgIncomingServer **_retval)
00462 {
00463   NS_ENSURE_ARG_POINTER(_retval);
00464 
00465   nsresult rv=NS_OK;
00466   
00467   nsCStringKey hashKey(key);
00468   nsCOMPtr<nsIMsgIncomingServer> server =
00469     do_QueryInterface((nsISupports*)m_incomingServers.Get(&hashKey), &rv);
00470 
00471   if (NS_SUCCEEDED(rv)) {
00472     NS_ADDREF(*_retval = server);
00473     return NS_OK;
00474   }
00475 
00476   // server doesn't exist, so create it
00477   // this is really horrible because we are doing our own prefname munging
00478   // instead of leaving it up to the incoming server.
00479   // this should be fixed somehow so that we can create the incoming server
00480   // and then read from the incoming server's attributes
00481   
00482   // in order to create the right kind of server, we have to look
00483   // at the pref for this server to get the username, hostname, and type
00484   nsCAutoString serverPrefPrefix(PREF_MAIL_SERVER_PREFIX);
00485   serverPrefPrefix += key;
00486   
00487   nsCAutoString serverPref;
00488 
00489   //
00490   // .type
00491   serverPref = serverPrefPrefix;
00492   serverPref += ".type";
00493   nsXPIDLCString serverType;
00494   rv = m_prefs->GetCharPref(serverPref.get(), getter_Copies(serverType));
00495   NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_INITIALIZED);
00496   
00497   //
00498   // .userName
00499   serverPref = serverPrefPrefix;
00500   serverPref += ".userName";
00501   nsXPIDLCString username;
00502   rv = m_prefs->GetCharPref(serverPref.get(), getter_Copies(username));
00503 
00504   // .hostname
00505   serverPref = serverPrefPrefix;
00506   serverPref += ".hostname";
00507   nsXPIDLCString hostname;
00508   rv = m_prefs->GetCharPref(serverPref.get(), getter_Copies(hostname));
00509   NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_INITIALIZED);
00510   
00511     // the server type doesn't exist. That's bad.
00512 
00513   rv = createKeyedServer(key, username, hostname, serverType, _retval);
00514   NS_ENSURE_SUCCESS(rv, rv);
00515 
00516   return rv;
00517 }
00518 
00519 /*
00520  * create a server when you know the key and the type
00521  */
00522 nsresult
00523 nsMsgAccountManager::createKeyedServer(const char* key,
00524                                        const char* username,
00525                                        const char* hostname,
00526                                        const char* type,
00527                                        nsIMsgIncomingServer ** aServer)
00528 {
00529   nsresult rv;
00530 
00531   //construct the contractid
00532   nsCAutoString serverContractID(NS_MSGINCOMINGSERVER_CONTRACTID_PREFIX);
00533   serverContractID += type;
00534   
00535   // finally, create the server
00536   nsCOMPtr<nsIMsgIncomingServer> server =
00537            do_CreateInstance(serverContractID.get(), &rv);
00538   NS_ENSURE_SUCCESS(rv, rv);
00539   
00540   server->SetKey(key);
00541   server->SetType(type);
00542   server->SetUsername(username);
00543   server->SetHostName(hostname);
00544 
00545   nsCStringKey hashKey(key);
00546 
00547   // addref for the hashtable
00548   nsISupports* serversupports = server;
00549   NS_ADDREF(serversupports);
00550   m_incomingServers.Put(&hashKey, serversupports);
00551 
00552   // now add all listeners that are supposed to be
00553   // waiting on root folders
00554   nsCOMPtr<nsIMsgFolder> rootFolder;
00555   rv = server->GetRootFolder(getter_AddRefs(rootFolder));
00556   NS_ENSURE_SUCCESS(rv, rv);
00557   mFolderListeners->EnumerateForwards(addListenerToFolder,
00558                                       (void *)(nsIMsgFolder*)rootFolder);
00559   NS_ADDREF(*aServer = server);
00560   
00561   return NS_OK;
00562 }
00563 
00564 PRBool
00565 nsMsgAccountManager::addListenerToFolder(nsISupports *element, void *data)
00566 {
00567   nsresult rv;
00568   nsIMsgFolder *rootFolder = (nsIMsgFolder *)data;
00569   nsCOMPtr<nsIFolderListener> listener = do_QueryInterface(element, &rv);
00570   NS_ENSURE_SUCCESS(rv, PR_TRUE);
00571 
00572   rootFolder->AddFolderListener(listener);
00573   return PR_TRUE;
00574 }
00575 
00576 PRBool
00577 nsMsgAccountManager::removeListenerFromFolder(nsISupports *element, void *data)
00578 {
00579   nsresult rv;
00580   nsIMsgFolder *rootFolder = (nsIMsgFolder *)data;
00581   nsCOMPtr<nsIFolderListener> listener = do_QueryInterface(element, &rv);
00582   NS_ENSURE_SUCCESS(rv, PR_TRUE);
00583 
00584   rootFolder->RemoveFolderListener(listener);
00585   return PR_TRUE;
00586 }
00587 
00588 NS_IMETHODIMP
00589 nsMsgAccountManager::DuplicateAccount(nsIMsgAccount *aAccount)
00590 { 
00591   NS_ENSURE_ARG_POINTER(aAccount);
00592   return NS_ERROR_NOT_IMPLEMENTED;
00593 }
00594 
00595 NS_IMETHODIMP
00596 nsMsgAccountManager::RemoveIdentity(nsIMsgIdentity *aIdentity)
00597 {
00598   // finish this
00599   return NS_OK;
00600 }
00601 
00602 NS_IMETHODIMP
00603 nsMsgAccountManager::RemoveAccount(nsIMsgAccount *aAccount)
00604 {
00605   NS_ENSURE_ARG_POINTER(aAccount);
00606   nsresult rv;
00607   rv = LoadAccounts();
00608   if (NS_FAILED(rv)) return rv;
00609 
00610   // order is important!
00611   // remove it from the prefs first
00612   nsXPIDLCString key;
00613   rv = aAccount->GetKey(getter_Copies(key));
00614   if (NS_FAILED(rv)) return rv;
00615   
00616   rv = removeKeyedAccount(key);
00617   if (NS_FAILED(rv)) return rv;
00618 
00619   // we were able to save the new prefs (i.e. not locked) so now remove it
00620   // from the account manager... ignore the error though, because the only
00621   // possible problem is that it wasn't in the hash table anyway... and if
00622   // so, it doesn't matter.
00623   m_accounts->RemoveElement(aAccount);
00624 
00625   // if it's the default, clear the default account
00626   if (m_defaultAccount.get() == aAccount) {
00627     SetDefaultAccount(nsnull);
00628   }
00629 
00630   // XXX - need to figure out if this is the last time this server is
00631   // being used, and only send notification then.
00632   // (and only remove from hashtable then too!)
00633   nsCOMPtr<nsIMsgIncomingServer> server;
00634   rv = aAccount->GetIncomingServer(getter_AddRefs(server));
00635   if (NS_SUCCEEDED(rv) && server) {
00636     nsXPIDLCString serverKey;
00637     rv = server->GetKey(getter_Copies(serverKey));
00638     NS_ENSURE_SUCCESS(rv,rv);
00639 
00640     LogoutOfServer(server); // close cached connections and forget session password
00641 
00642     // invalidate the FindServer() cache if we are removing the cached server
00643     if (m_lastFindServerResult) {
00644         nsXPIDLCString cachedServerKey;
00645         rv = m_lastFindServerResult->GetKey(getter_Copies(cachedServerKey));
00646         NS_ENSURE_SUCCESS(rv,rv);
00647     
00648         if (!nsCRT::strcmp((const char *)serverKey,(const char *)cachedServerKey)) {
00649             rv = SetLastServerFound(nsnull,"","",0,"");
00650             NS_ENSURE_SUCCESS(rv,rv);
00651         }
00652     }
00653 
00654     nsCStringKey hashKey(serverKey);
00655     
00656     nsIMsgIncomingServer* removedServer =
00657       (nsIMsgIncomingServer*) m_incomingServers.Remove(&hashKey);
00658 
00659     //NS_ASSERTION(server.get() == removedServer, "Key maps to different server. something wacky is going on");
00660 
00661     // remove reference from hashtable
00662     NS_IF_RELEASE(removedServer);
00663     
00664     nsCOMPtr<nsIMsgFolder> rootFolder;
00665     server->GetRootFolder(getter_AddRefs(rootFolder));
00666     nsCOMPtr<nsISupportsArray> allDescendents;
00667     NS_NewISupportsArray(getter_AddRefs(allDescendents));
00668     rootFolder->ListDescendents(allDescendents);
00669     PRUint32 cnt =0;
00670     rv = allDescendents->Count(&cnt);
00671     NS_ENSURE_SUCCESS(rv,rv);
00672     for (PRUint32 i=0; i< cnt;i++)
00673     {
00674       nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(allDescendents, i, &rv);
00675       folder->ForceDBClosed();
00676     }
00677    
00678     mFolderListeners->EnumerateForwards(removeListenerFromFolder,
00679                                         (void*)rootFolder);
00680     
00681     NotifyServerUnloaded(server);
00682 
00683 
00684     rv = server->RemoveFiles();
00685     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to remove the files associated with server");
00686 
00687     
00688     // now clear out the server once and for all.
00689     // watch out! could be scary
00690     server->ClearAllValues();
00691 
00692     rootFolder->Shutdown(PR_TRUE);
00693   }
00694   nsCOMPtr<nsISupportsArray> identityArray;
00695   
00696   rv = aAccount->GetIdentities(getter_AddRefs(identityArray));
00697   if (NS_SUCCEEDED(rv)) {
00698 
00699     PRUint32 count=0;
00700     identityArray->Count(&count);
00701 
00702     PRUint32 i;
00703     for (i=0; i<count; i++) {
00704       nsCOMPtr<nsIMsgIdentity> identity;
00705       rv = identityArray->QueryElementAt(i, NS_GET_IID(nsIMsgIdentity),
00706                                          (void **)getter_AddRefs(identity));
00707       if (NS_SUCCEEDED(rv))
00708         // clear out all identity information.
00709         // watch out! could be scary
00710         identity->ClearAllValues();
00711     }
00712 
00713   }
00714 
00715   aAccount->ClearAllValues();
00716   return NS_OK;
00717 }
00718 
00719 // remove the account with the given key.
00720 // note that this does NOT remove any of the related prefs
00721 // (like the server, identity, etc)
00722 nsresult
00723 nsMsgAccountManager::removeKeyedAccount(const char *key)
00724 {
00725   nsresult rv;
00726   rv = getPrefService();
00727   if (NS_FAILED(rv)) return rv;
00728 
00729   nsXPIDLCString accountList;
00730   rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS, getter_Copies(accountList));
00731   if (NS_FAILED(rv)) return rv;
00732 
00733   // reconstruct the new account list, re-adding all accounts except
00734   // the one with 'key'
00735   nsCAutoString newAccountList;
00736   char *newStr;
00737   char *rest = accountList.BeginWriting();
00738   
00739   char *token = nsCRT::strtok(rest, ",", &newStr);
00740   while (token) {
00741     nsCAutoString testKey(token);
00742     testKey.StripWhitespace();
00743 
00744     // re-add the candidate key only if it's not the key we're looking for
00745     if (!testKey.IsEmpty() && !testKey.Equals(key)) {
00746       if (!newAccountList.IsEmpty())
00747         newAccountList += ',';
00748       newAccountList += testKey;
00749     }
00750 
00751     token = nsCRT::strtok(newStr, ",", &newStr);
00752   }
00753 
00754   // Update mAccountKeyList to reflect the deletion
00755   mAccountKeyList = newAccountList;
00756 
00757   // now write the new account list back to the prefs
00758   rv = m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS,
00759                               newAccountList.get());
00760   if (NS_FAILED(rv)) return rv;
00761 
00762 
00763   return NS_OK;
00764 }
00765 
00766 /* get the default account. If no default account, pick the first account */
00767 NS_IMETHODIMP
00768 nsMsgAccountManager::GetDefaultAccount(nsIMsgAccount * *aDefaultAccount)
00769 {
00770   NS_ENSURE_ARG_POINTER(aDefaultAccount);
00771   nsresult rv;
00772   rv = LoadAccounts();
00773   if (NS_FAILED(rv)) return rv;
00774   
00775   PRUint32 count;
00776   if (!m_defaultAccount) {
00777     m_accounts->Count(&count);
00778     if (count == 0) {
00779       *aDefaultAccount=nsnull;
00780       return NS_ERROR_FAILURE;
00781     }
00782 
00783     nsXPIDLCString defaultKey;
00784     rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT, getter_Copies(defaultKey));
00785     
00786     if (NS_SUCCEEDED(rv))
00787       GetAccount(defaultKey, getter_AddRefs(m_defaultAccount));
00788 
00789     if (!m_defaultAccount) {
00790       PRUint32 index;
00791       PRBool foundValidDefaultAccount = PR_FALSE;
00792       for (index = 0; index < count; index++) {
00793         nsCOMPtr<nsIMsgAccount> aAccount;
00794         rv = m_accounts->QueryElementAt(index, NS_GET_IID(nsIMsgAccount),
00795                                         (void **)getter_AddRefs(aAccount));
00796         if (NS_SUCCEEDED(rv)) {
00797           // get incoming server
00798           nsCOMPtr <nsIMsgIncomingServer> server;
00799           rv = aAccount->GetIncomingServer(getter_AddRefs(server));
00800           NS_ENSURE_SUCCESS(rv,rv);
00801           
00802           PRBool canBeDefaultServer = PR_FALSE;
00803           if (server)
00804             server->GetCanBeDefaultServer(&canBeDefaultServer);
00805           
00806           // if this can serve as default server, set it as default and
00807           // break outof the loop.
00808           if (canBeDefaultServer) {
00809             SetDefaultAccount(aAccount);
00810             foundValidDefaultAccount = PR_TRUE;
00811             break;
00812           }
00813         }
00814       }
00815 
00816       if (!foundValidDefaultAccount) {
00817         // get the first account and use it.
00818         // we need to fix this scenario.
00819         NS_WARNING("No valid default account found, just using first (FIXME)");
00820         nsCOMPtr<nsIMsgAccount> firstAccount;
00821         m_accounts->QueryElementAt(0, NS_GET_IID(nsIMsgAccount),
00822                                    (void **)getter_AddRefs(firstAccount));
00823 
00824         SetDefaultAccount(firstAccount);
00825       }
00826     }
00827   }
00828   
00829   *aDefaultAccount = m_defaultAccount;
00830   NS_IF_ADDREF(*aDefaultAccount);
00831   return NS_OK;
00832 }
00833 
00834 
00835 NS_IMETHODIMP
00836 nsMsgAccountManager::SetDefaultAccount(nsIMsgAccount * aDefaultAccount)
00837 {
00838   if (m_defaultAccount != aDefaultAccount)
00839   {
00840     nsCOMPtr<nsIMsgAccount> oldAccount = m_defaultAccount;
00841 
00842     m_defaultAccount = aDefaultAccount;
00843 
00844     // it's ok if this fails
00845     setDefaultAccountPref(aDefaultAccount);
00846 
00847     // ok if notifications fail
00848     notifyDefaultServerChange(oldAccount, aDefaultAccount);
00849   }
00850   return NS_OK;
00851 }
00852 
00853 // fire notifications
00854 nsresult
00855 nsMsgAccountManager::notifyDefaultServerChange(nsIMsgAccount *aOldAccount,
00856                                                nsIMsgAccount *aNewAccount)
00857 {
00858   nsresult rv;
00859 
00860   nsCOMPtr<nsIMsgIncomingServer> server;
00861   nsCOMPtr<nsIMsgFolder> rootFolder;
00862   
00863   // first tell old server it's no longer the default
00864   if (aOldAccount) {
00865     rv = aOldAccount->GetIncomingServer(getter_AddRefs(server));
00866     if (NS_SUCCEEDED(rv) && server) {
00867       rv = server->GetRootFolder(getter_AddRefs(rootFolder));
00868       if (NS_SUCCEEDED(rv) && rootFolder)
00869         rootFolder->NotifyBoolPropertyChanged(kDefaultServerAtom,
00870                                                         PR_TRUE, PR_FALSE);
00871     }
00872   }
00873     
00874     // now tell new server it is.
00875   if (aNewAccount) {
00876     rv = aNewAccount->GetIncomingServer(getter_AddRefs(server));
00877     if (NS_SUCCEEDED(rv) && server) {
00878       rv = server->GetRootFolder(getter_AddRefs(rootFolder));
00879       if (NS_SUCCEEDED(rv) && rootFolder)
00880         rootFolder->NotifyBoolPropertyChanged(kDefaultServerAtom,
00881                                               PR_FALSE, PR_TRUE);
00882     }
00883   }
00884 
00885   if (aOldAccount && aNewAccount)  //only notify if the user goes and changes default account
00886   {
00887     nsCOMPtr<nsIObserverService> observerService = 
00888       do_GetService("@mozilla.org/observer-service;1", &rv);
00889 
00890     if (NS_SUCCEEDED(rv))
00891       observerService->NotifyObservers(nsnull,"mailDefaultAccountChanged",nsnull);
00892   }
00893 
00894   return NS_OK;
00895 }
00896 
00897 nsresult
00898 nsMsgAccountManager::setDefaultAccountPref(nsIMsgAccount* aDefaultAccount)
00899 {
00900   nsresult rv;
00901   
00902   rv = getPrefService();
00903   NS_ENSURE_SUCCESS(rv,rv);
00904 
00905   if (aDefaultAccount) {
00906     nsXPIDLCString key;
00907     rv = aDefaultAccount->GetKey(getter_Copies(key));
00908     NS_ENSURE_SUCCESS(rv, rv);
00909     
00910     rv = m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT, key);
00911     NS_ENSURE_SUCCESS(rv,rv);
00912   }
00913   else
00914     // don't care if this fails
00915     m_prefs->ClearUserPref(PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT);
00916 
00917   return NS_OK;
00918 }
00919     
00920 
00921 // enumaration for sending unload notifications
00922 PRBool
00923 nsMsgAccountManager::hashUnloadServer(nsHashKey *aKey, void *aData,
00924                                           void *closure)
00925 {
00926     nsresult rv;
00927     nsCOMPtr<nsIMsgIncomingServer> server =
00928       do_QueryInterface((nsISupports*)aData, &rv);
00929     if (NS_FAILED(rv)) return PR_TRUE;
00930     
00931     nsMsgAccountManager *accountManager = (nsMsgAccountManager*)closure;
00932     accountManager->NotifyServerUnloaded(server);
00933 
00934     nsCOMPtr<nsIMsgFolder> rootFolder;
00935     rv = server->GetRootFolder(getter_AddRefs(rootFolder));
00936 
00937     accountManager->mFolderListeners->EnumerateForwards(removeListenerFromFolder,
00938                                         (void *)(nsIMsgFolder*)rootFolder);
00939 
00940     if(NS_SUCCEEDED(rv))
00941         rootFolder->Shutdown(PR_TRUE);
00942 
00943     return PR_TRUE;
00944 
00945 }
00946 
00947 /* static */ void nsMsgAccountManager::LogoutOfServer(nsIMsgIncomingServer *aServer)
00948 {
00949     nsresult rv = aServer->Shutdown();
00950     NS_ASSERTION(NS_SUCCEEDED(rv), "Shutdown of server failed");
00951     rv = aServer->ForgetSessionPassword();
00952     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to remove the password associated with server");
00953 }
00954 
00955 /* static */ PRBool
00956 nsMsgAccountManager::hashLogoutOfServer(nsHashKey *aKey, void *aData,
00957                                                void *closure)
00958 {
00959     nsresult rv;
00960     nsCOMPtr<nsIMsgIncomingServer> server =
00961       do_QueryInterface((nsISupports*)aData, &rv);
00962     if (NS_SUCCEEDED(rv))
00963       LogoutOfServer(server);
00964 
00965     return PR_TRUE;
00966 }
00967 
00968 NS_IMETHODIMP nsMsgAccountManager::GetFolderCache(nsIMsgFolderCache* *aFolderCache)
00969 {
00970   if (!aFolderCache) return NS_ERROR_NULL_POINTER;
00971 
00972   nsresult rv = NS_OK;
00973 
00974   if (!m_msgFolderCache)
00975   {
00976     m_msgFolderCache = do_CreateInstance(kMsgFolderCacheCID, &rv);
00977     if (NS_FAILED(rv))
00978         return rv;
00979 
00980     nsCOMPtr<nsIFile> cacheFile;
00981     nsCOMPtr <nsIFileSpec> cacheFileSpec;
00982     
00983     rv = NS_GetSpecialDirectory(NS_APP_MESSENGER_FOLDER_CACHE_50_DIR, getter_AddRefs(cacheFile));
00984     if (NS_FAILED(rv)) return rv;
00985     
00986     // TODO: Make nsIMsgFolderCache::Init take an nsIFile and
00987     // avoid this conversion.
00988     rv = NS_NewFileSpecFromIFile(cacheFile, getter_AddRefs(cacheFileSpec));
00989     if (NS_FAILED(rv)) return rv;
00990                
00991     m_msgFolderCache->Init(cacheFileSpec);
00992   }
00993 
00994   *aFolderCache = m_msgFolderCache;
00995   NS_IF_ADDREF(*aFolderCache);
00996   return rv;
00997 }
00998 
00999 
01000 // enumaration for writing out accounts to folder cache.
01001 PRBool nsMsgAccountManager::writeFolderCache(nsHashKey *aKey, void *aData,
01002                                              void *closure)
01003 {
01004     nsIMsgIncomingServer *server = (nsIMsgIncomingServer*)aData;
01005     nsIMsgFolderCache *folderCache = (nsIMsgFolderCache *)closure;
01006 
01007     server->WriteToFolderCache(folderCache);
01008     return PR_TRUE;
01009 }
01010 
01011 // enumeration for empty trash on exit
01012 PRBool PR_CALLBACK nsMsgAccountManager::cleanupOnExit(nsHashKey *aKey, void *aData,
01013                                              void *closure)
01014 {
01015   nsIMsgIncomingServer *server = (nsIMsgIncomingServer*)aData;
01016   PRBool emptyTrashOnExit = PR_FALSE;
01017   PRBool cleanupInboxOnExit = PR_FALSE;
01018   nsresult rv;
01019     
01020   if (WeAreOffline())
01021     return PR_TRUE;
01022 
01023   server->GetEmptyTrashOnExit(&emptyTrashOnExit);
01024   nsCOMPtr <nsIImapIncomingServer> imapserver = do_QueryInterface(server);
01025   if (imapserver)
01026   {
01027     imapserver->GetCleanupInboxOnExit(&cleanupInboxOnExit);
01028     imapserver->SetShuttingDown(PR_TRUE);
01029   }
01030   if (emptyTrashOnExit || cleanupInboxOnExit)
01031   {
01032     nsCOMPtr<nsIMsgFolder> root;
01033     server->GetRootFolder(getter_AddRefs(root));
01034     nsXPIDLCString type;
01035     server->GetType(getter_Copies(type));
01036     if (root)
01037     {
01038       nsCOMPtr<nsIMsgFolder> folder;
01039       folder = do_QueryInterface(root);
01040       if (folder)
01041       {
01042          nsXPIDLCString passwd;
01043          PRBool serverRequiresPasswordForAuthentication = PR_TRUE;
01044          PRBool isImap = (type ? PL_strcmp(type, "imap") == 0 :
01045                           PR_FALSE);
01046          if (isImap)
01047          {
01048            server->GetServerRequiresPasswordForBiff(&serverRequiresPasswordForAuthentication);
01049            server->GetPassword(getter_Copies(passwd));
01050          }
01051          if (!isImap || (isImap && (!serverRequiresPasswordForAuthentication || (passwd && 
01052                         strlen((const char*) passwd)))))
01053          {
01054            nsCOMPtr<nsIUrlListener> urlListener;
01055            nsCOMPtr<nsIMsgAccountManager> accountManager = 
01056                     do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
01057            if (NS_FAILED(rv)) return rv;
01058            nsCOMPtr<nsIEventQueueService> pEventQService = 
01059                     do_GetService(kEventQueueServiceCID, &rv);
01060            if (NS_FAILED(rv)) return rv;
01061            nsCOMPtr<nsIEventQueue> eventQueue;
01062            pEventQService->GetThreadEventQueue(NS_CURRENT_THREAD,
01063                                     getter_AddRefs(eventQueue)); 
01064            if (isImap)
01065              urlListener = do_QueryInterface(accountManager, &rv);
01066                     
01067            if (isImap && cleanupInboxOnExit)
01068            {
01069              nsCOMPtr<nsIEnumerator> aEnumerator;
01070              folder->GetSubFolders(getter_AddRefs(aEnumerator));
01071              nsCOMPtr<nsISupports> aSupport;
01072              rv = aEnumerator->First();
01073              while (NS_SUCCEEDED(rv))
01074              {  
01075                rv = aEnumerator->CurrentItem(getter_AddRefs(aSupport));
01076                nsCOMPtr<nsIMsgFolder>inboxFolder = do_QueryInterface(aSupport);
01077                PRUint32 flags;
01078                inboxFolder->GetFlags(&flags);
01079                if (flags & MSG_FOLDER_FLAG_INBOX)
01080                {
01081                  rv = inboxFolder->Compact(urlListener, nsnull /* msgwindow */);
01082                  if (NS_SUCCEEDED(rv))
01083                    accountManager->SetFolderDoingCleanupInbox(inboxFolder);
01084                  break;
01085                 }
01086                 else
01087                   rv = aEnumerator->Next();
01088              }
01089            }
01090                           
01091            if (emptyTrashOnExit)
01092            {
01093              rv = folder->EmptyTrash(nsnull, urlListener);
01094              if (isImap && NS_SUCCEEDED(rv))
01095                accountManager->SetFolderDoingEmptyTrash(folder);
01096            }
01097                     
01098            if (isImap && urlListener)
01099            {
01100              PRBool inProgress = PR_FALSE;
01101              if (cleanupInboxOnExit)
01102              {
01103                PRInt32 loopCount = 0; // used to break out after 5 seconds
01104                accountManager->GetCleanupInboxInProgress(&inProgress);
01105                while (inProgress && loopCount++ < 5000)
01106                {
01107                  accountManager->GetCleanupInboxInProgress(&inProgress);
01108                  PR_CEnterMonitor(folder);
01109                  PR_CWait(folder, PR_MicrosecondsToInterval(1000UL));
01110                  PR_CExitMonitor(folder);
01111                  if (eventQueue)
01112                  {
01113                    PLEvent *event;
01114                    do
01115                    {
01116                      eventQueue->GetEvent(&event);
01117                      if (event)
01118                        eventQueue->HandleEvent(event);
01119                    }
01120                    while (event);
01121                  }
01122                }
01123              }
01124              if (emptyTrashOnExit)
01125              {
01126                accountManager->GetEmptyTrashInProgress(&inProgress);
01127                PRInt32 loopCount = 0;
01128                while (inProgress && loopCount++ < 5000)
01129                {
01130                  accountManager->GetEmptyTrashInProgress(&inProgress);
01131                  PR_CEnterMonitor(folder);
01132                  PR_CWait(folder, PR_MicrosecondsToInterval(1000UL));
01133                  PR_CExitMonitor(folder);
01134                  if (eventQueue)
01135                  {
01136                    PLEvent *event;
01137                    do
01138                    {
01139                      eventQueue->GetEvent(&event);
01140                      if (event)
01141                        eventQueue->HandleEvent(event);
01142                    }
01143                    while (event);
01144                  }
01145                }
01146              }
01147            } 
01148          }
01149        }     
01150      }
01151    }
01152    return PR_TRUE;
01153 }
01154 
01155 PRBool nsMsgAccountManager::closeCachedConnections(nsHashKey *aKey, void *aData, void *closure)
01156 {
01157   nsIMsgIncomingServer *server = (nsIMsgIncomingServer*)aData;
01158 
01159   server->CloseCachedConnections();
01160 
01161   return PR_TRUE;
01162 }
01163 
01164 PRBool nsMsgAccountManager::shutdown(nsHashKey *aKey, void *aData, void *closure)
01165 {
01166   nsIMsgIncomingServer *server = (nsIMsgIncomingServer*)aData;
01167 
01168   server->Shutdown();
01169 
01170   return PR_TRUE;
01171 }
01172 
01173 
01174 /* readonly attribute nsISupportsArray accounts; */
01175 NS_IMETHODIMP
01176 nsMsgAccountManager::GetAccounts(nsISupportsArray **_retval)
01177 {
01178   nsresult rv;
01179   
01180   rv = LoadAccounts();
01181   if (NS_FAILED(rv)) return rv;
01182   
01183   nsCOMPtr<nsISupportsArray> accounts;
01184   NS_NewISupportsArray(getter_AddRefs(accounts));
01185 
01186   accounts->AppendElements(m_accounts);
01187 
01188   NS_ADDREF(*_retval = accounts);
01189 
01190   return NS_OK;
01191 }
01192 
01193 PRBool
01194 nsMsgAccountManager::hashElementToArray(nsHashKey *aKey, void *aData,
01195                                         void *closure)
01196 {
01197     nsISupports* element = (nsISupports*)aData;
01198     nsISupportsArray* array = (nsISupportsArray*)closure;
01199 
01200     array->AppendElement(element);
01201     return PR_TRUE;
01202 }
01203 
01204 PRBool
01205 nsMsgAccountManager::hashElementRelease(nsHashKey *aKey, void *aData,
01206                                         void *closure)
01207 {
01208   nsISupports* element = (nsISupports*)aData;
01209 
01210   NS_RELEASE(element);
01211 
01212   return PR_TRUE;               // return true to remove this element
01213 }
01214 
01215 /* nsISupportsArray GetAllIdentities (); */
01216 NS_IMETHODIMP
01217 nsMsgAccountManager::GetAllIdentities(nsISupportsArray **_retval)
01218 {
01219   nsresult rv;
01220   rv = LoadAccounts();
01221   if (NS_FAILED(rv)) return rv;
01222   
01223   nsCOMPtr<nsISupportsArray> identities;
01224   rv = NS_NewISupportsArray(getter_AddRefs(identities));
01225   if (NS_FAILED(rv)) return rv;
01226 
01227   // convert hash table->nsISupportsArray of identities
01228   m_accounts->EnumerateForwards(getIdentitiesToArray,
01229                                 (void *)(nsISupportsArray*)identities);
01230   // convert nsISupportsArray->nsISupportsArray
01231   // when do we free the nsISupportsArray?
01232   NS_ADDREF(*_retval = identities);
01233   return rv;
01234 }
01235 
01236 PRBool
01237 nsMsgAccountManager::addIdentityIfUnique(nsISupports *element, void *aData)
01238 {
01239   nsresult rv;
01240   nsCOMPtr<nsIMsgIdentity> identity = do_QueryInterface(element, &rv);
01241   if (NS_FAILED(rv)) {
01242     printf("addIdentityIfUnique problem\n");
01243     return PR_TRUE;
01244   }
01245   
01246   nsISupportsArray *array = (nsISupportsArray*)aData;
01247 
01248   
01249   nsXPIDLCString key;
01250   rv = identity->GetKey(getter_Copies(key));
01251   if (NS_FAILED(rv)) return PR_TRUE;
01252 
01253   PRUint32 count=0;
01254   rv = array->Count(&count);
01255   if (NS_FAILED(rv)) return PR_TRUE;
01256   
01257   PRBool found=PR_FALSE;
01258   PRUint32 i;
01259   for (i=0; i<count; i++) {
01260     nsCOMPtr<nsISupports> thisElement;
01261     array->GetElementAt(i, getter_AddRefs(thisElement));
01262 
01263     nsCOMPtr<nsIMsgIdentity> thisIdentity =
01264       do_QueryInterface(thisElement, &rv);
01265     if (NS_FAILED(rv)) continue;
01266 
01267     nsXPIDLCString thisKey;
01268     thisIdentity->GetKey(getter_Copies(thisKey));
01269     if (PL_strcmp(key, thisKey)==0) {
01270       found = PR_TRUE;
01271       break;
01272     }
01273   }
01274 
01275   if (!found)
01276     array->AppendElement(identity);
01277 
01278   return PR_TRUE;
01279 }
01280 
01281 PRBool
01282 nsMsgAccountManager::getIdentitiesToArray(nsISupports *element, void *aData)
01283 {
01284   nsresult rv;
01285   nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(element, &rv);
01286   if (NS_FAILED(rv)) return PR_TRUE;
01287   
01288   
01289   nsCOMPtr<nsISupportsArray> identities;
01290   rv = account->GetIdentities(getter_AddRefs(identities));
01291   if (NS_FAILED(rv)) return PR_TRUE;
01292 
01293   identities->EnumerateForwards(addIdentityIfUnique, aData);
01294   
01295   return PR_TRUE;
01296 }
01297 
01298 /* nsISupportsArray GetAllServers (); */
01299 NS_IMETHODIMP
01300 nsMsgAccountManager::GetAllServers(nsISupportsArray **_retval)
01301 {
01302   nsresult rv;
01303   rv = LoadAccounts();
01304   if (NS_FAILED(rv)) return rv;
01305   
01306   nsCOMPtr<nsISupportsArray> servers;
01307   rv = NS_NewISupportsArray(getter_AddRefs(servers));
01308   if (NS_FAILED(rv)) return rv;
01309 
01310   // enumerate by going through the list of accounts, so that we
01311   // get the order correct
01312   m_incomingServers.Enumerate(getServersToArray,
01313                               (void *)(nsISupportsArray*)servers);
01314   NS_ADDREF(*_retval = servers);
01315   return rv;
01316 }
01317 
01318 PRBool PR_CALLBACK
01319 nsMsgAccountManager::getServersToArray(nsHashKey *aKey,
01320                                        void *element,
01321                                        void *aData)
01322 {
01323   nsresult rv;
01324   nsCOMPtr<nsIMsgIncomingServer> server =
01325     do_QueryInterface((nsISupports*)element, &rv);
01326   if (NS_FAILED(rv)) return PR_TRUE;
01327   
01328   nsISupportsArray *array = (nsISupportsArray*)aData;
01329   
01330   nsCOMPtr<nsISupports> serverSupports = do_QueryInterface(server);
01331   if (NS_SUCCEEDED(rv)) 
01332     array->AppendElement(serverSupports);
01333 
01334   return PR_TRUE;
01335 }
01336 
01337 nsresult
01338 nsMsgAccountManager::LoadAccounts()
01339 {
01340   nsresult rv;
01341 
01342   // for now safeguard multiple calls to this function
01343   if (m_accountsLoaded)
01344     return NS_OK;
01345 
01346   kDefaultServerAtom = do_GetAtom("DefaultServer");
01347   
01348   //Ensure biff service has started
01349   nsCOMPtr<nsIMsgBiffManager> biffService = 
01350            do_GetService(NS_MSGBIFFMANAGER_CONTRACTID, &rv);
01351 
01352   if (NS_SUCCEEDED(rv))
01353     biffService->Init();
01354 
01355   //Ensure purge service has started
01356   nsCOMPtr<nsIMsgPurgeService> purgeService = 
01357            do_GetService(NS_MSGPURGESERVICE_CONTRACTID, &rv);
01358 
01359   if (NS_SUCCEEDED(rv))
01360     purgeService->Init();
01361   
01362   // Ensure messenger OS integration service has started
01363   // note, you can't expect the integrationService to be there
01364   // we don't have OS integration on all platforms.
01365   nsCOMPtr<nsIMessengerOSIntegration> integrationService =
01366            do_GetService(NS_MESSENGEROSINTEGRATION_CONTRACTID, &rv);
01367 
01368   // mail.accountmanager.accounts is the main entry point for all accounts
01369   nsXPIDLCString accountList;
01370   rv = getPrefService();
01371   if (NS_SUCCEEDED(rv)) {
01372     rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS, getter_Copies(accountList));
01373     
01389     nsCOMPtr<nsIPrefService> prefservice(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
01390     NS_ENSURE_SUCCESS(rv,rv);
01391     
01392     nsCOMPtr<nsIPrefBranch> defaultsPrefBranch;
01393     rv = prefservice->GetDefaultBranch(MAILNEWS_ROOT_PREF, getter_AddRefs(defaultsPrefBranch));
01394     NS_ENSURE_SUCCESS(rv,rv);
01395 
01396     nsCOMPtr<nsIPrefBranch> prefBranch;
01397     rv = prefservice->GetBranch(MAILNEWS_ROOT_PREF, getter_AddRefs(prefBranch));
01398     NS_ENSURE_SUCCESS(rv,rv);
01399 
01400     PRInt32 appendAccountsCurrentVersion=0;
01401     PRInt32 appendAccountsDefaultVersion=0;
01402     rv = prefBranch->GetIntPref(APPEND_ACCOUNTS_VERSION_PREF_NAME, &appendAccountsCurrentVersion);
01403     NS_ENSURE_SUCCESS(rv,rv);
01404 
01405     rv = defaultsPrefBranch->GetIntPref(APPEND_ACCOUNTS_VERSION_PREF_NAME, &appendAccountsDefaultVersion);
01406     NS_ENSURE_SUCCESS(rv,rv);
01407 
01408     // Update the account list if needed
01409     if ((appendAccountsCurrentVersion <= appendAccountsDefaultVersion)) {
01410 
01411       // Get a list of pre-configured accounts
01412       nsXPIDLCString appendAccountList;
01413       rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_APPEND_ACCOUNTS, getter_Copies(appendAccountList));
01414 
01415       // If there are pre-configured accounts, we need to add them to the existing list.
01416       if (!appendAccountList.IsEmpty()) {
01417         if (!accountList.IsEmpty()) {
01418           nsCStringArray existingAccountsArray;
01419           existingAccountsArray.ParseString(accountList.get(), ACCOUNT_DELIMITER);
01420 
01421           // Tokenize the data and add each account if it is not already there 
01422           // in the user's current mailnews account list
01423           char *newAccountStr;
01424           char *preConfigAccountsStr = ToNewCString(appendAccountList);
01425   
01426           char *token = nsCRT::strtok(preConfigAccountsStr, ACCOUNT_DELIMITER, &newAccountStr);
01427 
01428           nsCAutoString newAccount;
01429           while (token) {
01430             if (token && *token) {
01431               newAccount.Assign(token);
01432               newAccount.StripWhitespace();
01433 
01434               if (existingAccountsArray.IndexOf(newAccount) == -1) {
01435                 accountList += ",";
01436                 accountList += newAccount;
01437               }
01438             }
01439             token = nsCRT::strtok(newAccountStr, ACCOUNT_DELIMITER, &newAccountStr);
01440           }
01441           PR_Free(preConfigAccountsStr);
01442         }
01443         else {
01444           accountList = appendAccountList;
01445         }
01446         // Increase the version number so that updates will happen as and when needed
01447         rv = prefBranch->SetIntPref(APPEND_ACCOUNTS_VERSION_PREF_NAME, appendAccountsCurrentVersion + 1);
01448       }
01449     }
01450   }
01451 
01452   m_accountsLoaded = PR_TRUE;  //It is ok to return null accounts like when we create new profile
01453   m_haveShutdown = PR_FALSE;
01454   
01455   if (!accountList || !accountList[0])
01456     return NS_OK;
01457   
01458   /* parse accountList and run loadAccount on each string, comma-separated */   
01459   nsCOMPtr<nsIMsgAccount> account;
01460   char *newStr;
01461   char *rest = accountList.BeginWriting();
01462   nsCAutoString str;
01463   
01464   for (char *token = nsCRT::strtok(rest, ",", &newStr);
01465        token;
01466        token = nsCRT::strtok(newStr, ",", &newStr))
01467   {
01468     str = token;
01469     str.StripWhitespace();
01470       
01471     if (str.IsEmpty() ||
01472         NS_FAILED(createKeyedAccount(str.get(), getter_AddRefs(account))) ||
01473         !account) {
01474       NS_WARNING("unexpected entry in account list; prefs corrupt?");
01475       continue;
01476     }
01477 
01478     // force load of accounts (need to find a better way to do this)
01479     nsCOMPtr<nsISupportsArray> identities;
01480     account->GetIdentities(getter_AddRefs(identities));
01481     
01482     nsCOMPtr<nsIMsgIncomingServer> server;
01483     account->GetIncomingServer(getter_AddRefs(server));
01484   }
01485 
01486   nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); 
01487 
01488   if (NS_SUCCEEDED(rv))
01489     mailSession->AddFolderListener(this, nsIFolderListener::added | nsIFolderListener::removed);
01490   /* finished loading accounts */
01491   return NS_OK;
01492 }
01493 
01494 PRBool
01495 nsMsgAccountManager::getAccountList(nsISupports *element, void *aData)
01496 {
01497   nsresult rv;
01498   nsCAutoString* accountList = (nsCAutoString*) aData;
01499   nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(element, &rv);
01500   if (NS_FAILED(rv)) return PR_TRUE;
01501   
01502   nsXPIDLCString key;
01503   rv = account->GetKey(getter_Copies(key));
01504   if (NS_FAILED(rv)) return PR_TRUE;
01505 
01506   if ((*accountList).IsEmpty())
01507     (*accountList).Append(key);
01508   else {
01509     (*accountList) += ',';
01510     (*accountList).Append(key);
01511   }
01512 
01513   return PR_TRUE;
01514 }
01515 
01516 // this routine goes through all the identities and makes sure
01517 // that the special folders for each identity have the
01518 // correct special folder flags set, e.g, the Sent folder has
01519 // the sent flag set.
01520 //
01521 // it also goes through all the spam settings for each account
01522 // and makes sure the folder flags are set there, too
01523 NS_IMETHODIMP
01524 nsMsgAccountManager::SetSpecialFolders()
01525 {
01526   nsresult rv;
01527   nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
01528   NS_ENSURE_SUCCESS(rv,rv); 
01529 
01530   nsCOMPtr<nsISupportsArray> identities;
01531   GetAllIdentities(getter_AddRefs(identities));
01532 
01533   PRUint32 idCount=0;
01534   identities->Count(&idCount);
01535 
01536   PRUint32 id;
01537   nsXPIDLCString identityKey;
01538   
01539   for (id=0; id<idCount; id++) 
01540   {
01541     nsCOMPtr<nsISupports> thisSupports;
01542     rv = identities->GetElementAt(id, getter_AddRefs(thisSupports));
01543     if (NS_FAILED(rv)) continue;
01544     
01545     nsCOMPtr<nsIMsgIdentity>
01546       thisIdentity = do_QueryInterface(thisSupports, &rv);
01547 
01548     if (NS_SUCCEEDED(rv) && thisIdentity)
01549     {
01550       nsXPIDLCString folderUri;
01551       nsCOMPtr<nsIRDFResource> res;
01552       nsCOMPtr<nsIMsgFolder> folder;
01553       thisIdentity->GetFccFolder(getter_Copies(folderUri));
01554       if (folderUri && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
01555       {
01556         folder = do_QueryInterface(res, &rv);
01557         nsCOMPtr <nsIMsgFolder> parent;
01558         if (folder && NS_SUCCEEDED(rv))
01559         {
01560           rv = folder->GetParent(getter_AddRefs(parent));
01561           if (NS_SUCCEEDED(rv) && parent)
01562             rv = folder->SetFlag(MSG_FOLDER_FLAG_SENTMAIL);
01563         }
01564       }
01565       thisIdentity->GetDraftFolder(getter_Copies(folderUri));
01566       if (folderUri && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
01567       {
01568         folder = do_QueryInterface(res, &rv);
01569         nsCOMPtr <nsIMsgFolder> parent;
01570         if (folder && NS_SUCCEEDED(rv))
01571         {
01572           rv = folder->GetParent(getter_AddRefs(parent));
01573           if (NS_SUCCEEDED(rv) && parent)
01574             rv = folder->SetFlag(MSG_FOLDER_FLAG_DRAFTS);
01575         }
01576       }
01577       thisIdentity->GetStationeryFolder(getter_Copies(folderUri));
01578       if (folderUri && NS_SUCCEEDED(rdf->GetResource(folderUri, getter_AddRefs(res))))
01579       {
01580         folder = do_QueryInterface(res, &rv);
01581         if (folder && NS_SUCCEEDED(rv))
01582         {
01583           nsCOMPtr <nsIMsgFolder> parent;
01584           rv = folder->GetParent(getter_AddRefs(parent));
01585           if (NS_SUCCEEDED(rv) && parent) // only set flag if folder is real
01586             rv = folder->SetFlag(MSG_FOLDER_FLAG_TEMPLATES);
01587         }
01588       }
01589     }
01590   }
01591 
01592   // XXX todo
01593   // get all servers
01594   // get all spam settings for each server
01595   // set the JUNK folder flag on the spam folders, right?
01596   return NS_OK;
01597 }
01598 
01599 NS_IMETHODIMP
01600 nsMsgAccountManager::UnloadAccounts()
01601 {
01602   // release the default account
01603   kDefaultServerAtom = nsnull;
01604   m_defaultAccount=nsnull;
01605   m_incomingServers.Enumerate(hashUnloadServer, this);
01606 
01607   m_accounts->Clear();          // will release all elements
01608   m_identities.Reset(hashElementRelease, nsnull);
01609   m_incomingServers.Reset(hashElementRelease, nsnull);
01610   m_accountsLoaded = PR_FALSE;
01611   mAccountKeyList.Truncate(0);
01612   SetLastServerFound(nsnull,"","",0,"");
01613   return NS_OK;
01614 }
01615 
01616 NS_IMETHODIMP
01617 nsMsgAccountManager::ShutdownServers()
01618 {
01619   m_incomingServers.Enumerate(shutdown, nsnull);
01620   return NS_OK;
01621 }
01622 
01623 NS_IMETHODIMP
01624 nsMsgAccountManager::CloseCachedConnections()
01625 {
01626   m_incomingServers.Enumerate(closeCachedConnections, nsnull);
01627   return NS_OK;
01628 }
01629 
01630 NS_IMETHODIMP
01631 nsMsgAccountManager::CleanupOnExit()
01632 {
01633   m_incomingServers.Enumerate(cleanupOnExit, nsnull);
01634   return NS_OK;
01635 }
01636 
01637 NS_IMETHODIMP
01638 nsMsgAccountManager::WriteToFolderCache(nsIMsgFolderCache *folderCache)
01639 {
01640     m_incomingServers.Enumerate(writeFolderCache, folderCache);
01641     return folderCache->Close();
01642 }
01643 
01644 nsresult
01645 nsMsgAccountManager::createKeyedAccount(const char* key,
01646                                         nsIMsgAccount ** aAccount)
01647 {
01648     
01649   nsresult rv;
01650   nsCOMPtr<nsIMsgAccount> account = do_CreateInstance(kMsgAccountCID, &rv);
01651   if (NS_FAILED(rv)) return rv;
01652 
01653   account->SetKey(key);
01654 
01655   // add to internal nsISupportsArray
01656   m_accounts->AppendElement(NS_STATIC_CAST(nsISupports*, account));
01657 
01658   // add to string list
01659   if (mAccountKeyList.IsEmpty())
01660     mAccountKeyList = key;
01661   else {
01662     mAccountKeyList += ",";
01663     mAccountKeyList += key;
01664   }
01665 
01666   rv = getPrefService();
01667   if (NS_SUCCEEDED(rv))
01668     m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS,
01669                          mAccountKeyList.get());
01670 
01671   NS_ADDREF(*aAccount = account);
01672   
01673   return NS_OK;
01674 }
01675 
01676 nsresult
01677 nsMsgAccountManager::CreateAccount(nsIMsgAccount **_retval)
01678 {
01679     NS_ENSURE_ARG_POINTER(_retval);
01680 
01681     nsCAutoString key;
01682     getUniqueAccountKey(ACCOUNT_PREFIX, m_accounts, key);
01683 
01684     return createKeyedAccount(key.get(), _retval);
01685 }
01686 
01687 nsresult
01688 nsMsgAccountManager::GetAccount(const char* key,
01689                                 nsIMsgAccount **_retval)
01690 {
01691     if (!_retval) return NS_ERROR_NULL_POINTER;
01692 
01693     findAccountByKeyEntry findEntry;
01694     findEntry.key = key;
01695     findEntry.account = nsnull;
01696     
01697     m_accounts->EnumerateForwards(findAccountByKey, (void *)&findEntry);
01698 
01699     if (findEntry.account)
01700         NS_ADDREF(*_retval = findEntry.account);
01701     else
01702         *_retval = nsnull;
01703 
01704     // not found, create on demand
01705     return NS_OK;
01706 }
01707 
01708 nsresult
01709 nsMsgAccountManager::FindServerIndex(nsIMsgIncomingServer* server,
01710                                      PRInt32* result)
01711 {
01712   NS_ENSURE_ARG_POINTER(server);
01713   nsresult rv;
01714   
01715   nsXPIDLCString key;
01716   rv = server->GetKey(getter_Copies(key));
01717 
01718   findServerByKeyEntry findEntry;
01719   findEntry.key = key;
01720   findEntry.index = -1;
01721   
01722   // do this by account because the account list is in order
01723   m_accounts->EnumerateForwards(findServerIndexByServer, (void *)&findEntry);
01724 
01725   // even if the search failed, we can return index.
01726   // this means that all servers not in the array return an index higher
01727   // than all "registered" servers
01728   *result = findEntry.index;
01729   
01730   return NS_OK;
01731 }
01732 
01733 PRBool
01734 nsMsgAccountManager::findServerIndexByServer(nsISupports *element, void *aData)
01735 {
01736   nsresult rv;
01737   
01738   nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(element);
01739   findServerByKeyEntry *entry = (findServerByKeyEntry*) aData;
01740 
01741   // increment the index;
01742   entry->index++;
01743   
01744   nsCOMPtr<nsIMsgIncomingServer> server;
01745   rv = account->GetIncomingServer(getter_AddRefs(server));
01746   if (!server || NS_FAILED(rv)) return PR_TRUE;
01747   
01748   nsXPIDLCString key;
01749   rv = server->GetKey(getter_Copies(key));
01750   if (NS_FAILED(rv)) return PR_TRUE;
01751 
01752   // stop when found,
01753   // index will be set to the current index 
01754   if (nsCRT::strcmp(key, entry->key)==0)
01755     return PR_FALSE;
01756   
01757   return PR_TRUE;
01758 }
01759 
01760 PRBool
01761 nsMsgAccountManager::findAccountByKey(nsISupports* element, void *aData)
01762 {
01763     nsresult rv;
01764     nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(element, &rv);
01765     if (NS_FAILED(rv)) return PR_TRUE;
01766     
01767     findAccountByKeyEntry *entry = (findAccountByKeyEntry*) aData;
01768 
01769     nsXPIDLCString key;
01770     account->GetKey(getter_Copies(key));
01771     if (PL_strcmp(key, entry->key)==0) {
01772         entry->account = account;
01773         return PR_FALSE;        // stop when found
01774     }
01775 
01776     return PR_TRUE;
01777 }
01778 
01779 NS_IMETHODIMP nsMsgAccountManager::AddIncomingServerListener(nsIIncomingServerListener *serverListener)
01780 {
01781    m_incomingServerListeners.AppendObject(serverListener);
01782    return NS_OK;
01783 }
01784 
01785 NS_IMETHODIMP nsMsgAccountManager::RemoveIncomingServerListener(nsIIncomingServerListener *serverListener)
01786 {
01787     m_incomingServerListeners.RemoveObject(serverListener);
01788     return NS_OK;
01789 }
01790 
01791 
01792 NS_IMETHODIMP nsMsgAccountManager::NotifyServerLoaded(nsIMsgIncomingServer *server)
01793 {
01794     PRInt32 count = m_incomingServerListeners.Count();
01795     
01796     for(PRInt32 i = 0; i < count; i++)
01797     {
01798         nsIIncomingServerListener* listener = m_incomingServerListeners[i];
01799         listener->OnServerLoaded(server);
01800     }
01801 
01802     return NS_OK;
01803 }
01804 
01805 NS_IMETHODIMP nsMsgAccountManager::NotifyServerUnloaded(nsIMsgIncomingServer *server)
01806 {
01807     PRInt32 count = m_incomingServerListeners.Count();
01808     server->SetFilterList(nsnull); // clear this to cut shutdown leaks. we are always passing valid non-null server here. 
01809     
01810     for(PRInt32 i = 0; i < count; i++)
01811     {
01812         nsIIncomingServerListener* listener = m_incomingServerListeners[i];
01813         listener->OnServerUnloaded(server);
01814     }
01815 
01816     return NS_OK;
01817 }
01818 
01819 NS_IMETHODIMP nsMsgAccountManager::NotifyServerChanged(nsIMsgIncomingServer *server)
01820 {
01821     PRInt32 count = m_incomingServerListeners.Count();
01822     
01823     for(PRInt32 i = 0; i < count; i++)
01824     {
01825         nsIIncomingServerListener* listener = m_incomingServerListeners[i];
01826         listener->OnServerChanged(server);
01827     }
01828 
01829     return NS_OK;
01830 }
01831 
01832 NS_IMETHODIMP
01833 nsMsgAccountManager::FindServerByURI(nsIURI *aURI, PRBool aRealFlag,
01834                                 nsIMsgIncomingServer** aResult)
01835 {
01836   nsresult rv;
01837   nsCOMPtr<nsISupportsArray> servers;
01838   
01839   // Get username and hostname and port so we can get the server
01840   nsCAutoString username;
01841   rv = aURI->GetUserPass(username);
01842   if (NS_SUCCEEDED(rv) && !username.IsEmpty())
01843     NS_UnescapeURL(username);
01844 
01845   nsCAutoString hostname;
01846   rv = aURI->GetHost(hostname);
01847   if (NS_SUCCEEDED(rv) && !hostname.IsEmpty())
01848     NS_UnescapeURL(hostname);
01849 
01850   nsCAutoString type;
01851   rv = aURI->GetScheme(type);
01852   if (NS_SUCCEEDED(rv) && !type.IsEmpty())
01853   {
01854     // now modify type if pop or news
01855     if (type.EqualsLiteral("pop"))
01856       type.AssignLiteral("pop3");
01857     // we use "nntp" in the server list so translate it here.
01858     else if (type.EqualsLiteral("news"))
01859       type.AssignLiteral("nntp");
01860     // we use "any" as the wildcard type.
01861     else if (type.EqualsLiteral("any"))
01862       type.Truncate();
01863   }
01864 
01865   PRInt32 port = 0;
01866   // check the port of the scheme is not 'none' or blank
01867   if (!(type.EqualsLiteral("none") || type.IsEmpty()))
01868   {
01869     rv = aURI->GetPort(&port);
01870     // Set the port to zero if we got a -1 (use default)
01871     if (NS_SUCCEEDED(rv) && (port == -1))
01872       port = 0;
01873   }
01874 
01875   // If 'aRealFlag' is set then we want to scan all existing accounts
01876   // to make sure there's no duplicate including those whose host and/or
01877   // user names have been changed.
01878   if (!aRealFlag &&
01879       (m_lastFindServerHostName.Equals(hostname)) &&
01880       (m_lastFindServerUserName.Equals(username)) &&
01881       (port == m_lastFindServerPort) &&
01882       (m_lastFindServerType.Equals(type)) &&
01883       m_lastFindServerResult) 
01884   {
01885     NS_ADDREF(*aResult = m_lastFindServerResult);
01886     return NS_OK;
01887   }
01888 
01889   rv = GetAllServers(getter_AddRefs(servers));
01890   if (NS_FAILED(rv)) return rv;
01891 
01892   findServerEntry serverInfo;
01893 
01894   // "" or 0 (for the port only) act as the wild card.
01895 
01896   // hostname might be blank, pass "" instead
01897   serverInfo.hostname = (!hostname.IsEmpty()) ? hostname.get() : "";
01898   // username might be blank, pass "" instead
01899   serverInfo.username = (!username.IsEmpty()) ? username.get() : "";
01900   // port is initialized to zero if not specified in the url,
01901   // so use it no matter what
01902   serverInfo.port = port;
01903   // type might be blank, pass "" instead
01904   serverInfo.type = (!type.IsEmpty()) ? type.get() : "";
01905   serverInfo.useRealSetting = aRealFlag;
01906 
01907   serverInfo.server = *aResult = nsnull;
01908   
01909   servers->EnumerateForwards(findServerUrl, (void *)&serverInfo);
01910 
01911   if (!serverInfo.server) 
01912     return NS_ERROR_UNEXPECTED;
01913 
01914   // cache for next time
01915   rv = SetLastServerFound(serverInfo.server, hostname.get(), username.get(), port, type.get());
01916   NS_ENSURE_SUCCESS(rv,rv);
01917 
01918   NS_ADDREF(*aResult = serverInfo.server);
01919   
01920   return NS_OK;
01921 }
01922 
01923 nsresult
01924 nsMsgAccountManager::InternalFindServer(const char* username,
01925                                 const char* hostname,
01926                                 const char* type,
01927                                 PRBool useRealSetting,
01928                                 nsIMsgIncomingServer** aResult)
01929 {
01930   nsresult rv;
01931   nsCOMPtr<nsISupportsArray> servers;
01932  
01933   findServerEntry serverInfo;
01934 
01935   // "" acts as the wild card.
01936 
01937   // hostname might be blank, pass "" instead
01938   serverInfo.hostname = hostname ? hostname : "";
01939   // username might be blank, pass "" instead
01940   serverInfo.username = username ? username : "";
01941   // type might be blank, pass "" instead
01942   serverInfo.type = type ? type : "";
01943   serverInfo.useRealSetting = useRealSetting;
01944 
01945   // If 'useRealSetting' is set then we want to scan all existing accounts
01946   // to make sure there's no duplicate including those whose host and/or
01947   // user names have been changed.
01948   if (!useRealSetting &&
01949       (m_lastFindServerHostName.Equals(serverInfo.hostname)) &&
01950       (m_lastFindServerUserName.Equals(serverInfo.username)) &&
01951       (m_lastFindServerType.Equals(serverInfo.type)) &&
01952       m_lastFindServerResult) 
01953   {
01954     NS_ADDREF(*aResult = m_lastFindServerResult);
01955     return NS_OK;
01956   }
01957 
01958   rv = GetAllServers(getter_AddRefs(servers));
01959   if (NS_FAILED(rv)) return rv;
01960 
01961   serverInfo.server = *aResult = nsnull;
01962   
01963   servers->EnumerateForwards(findServer, (void *)&serverInfo);
01964 
01965   if (!serverInfo.server) return NS_ERROR_UNEXPECTED;
01966 
01967   // cache for next time
01968   rv = SetLastServerFound(serverInfo.server, hostname, username, 0, type);
01969   NS_ENSURE_SUCCESS(rv,rv);
01970 
01971   NS_ADDREF(*aResult = serverInfo.server);
01972   
01973   return NS_OK;
01974 
01975 }
01976 
01977 NS_IMETHODIMP
01978 nsMsgAccountManager::FindServer(const char* username,
01979                                 const char* hostname,
01980                                 const char* type,
01981                                 nsIMsgIncomingServer** aResult)
01982 {
01983   return(InternalFindServer(username, hostname, type, PR_FALSE, aResult));
01984 }
01985 
01986 // Interface called by UI js only (always return true).
01987 NS_IMETHODIMP
01988 nsMsgAccountManager::FindRealServer(const char* username,
01989                                 const char* hostname,
01990                                 const char* type,
01991                                 PRInt32 port,
01992                                 nsIMsgIncomingServer** aResult)
01993 {
01994   // Dummy string to initialize the URL
01995   // Needed so that we can use the Set....() items below (except SetSpec())
01996   nsCAutoString spec("http://user@hostname:1111");
01997   nsresult rv;
01998   nsCOMPtr<nsIURL> aUrl = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
01999   if (NS_FAILED(rv)) return NS_OK;
02000   
02001 
02002   aUrl->SetSpec(spec);
02003 
02004   if (!*type) 
02005     // empty type -- use 'any' as the magic scheme value
02006     aUrl->SetScheme(NS_LITERAL_CSTRING("any"));
02007   else
02008     aUrl->SetScheme(nsDependentCString(type));
02009 
02010   aUrl->SetHost(nsDependentCString(hostname));
02011   aUrl->SetUserPass(nsDependentCString(username));
02012   aUrl->SetPort(port);
02013 
02014 #ifdef DEBUG_kteuscher
02015   aUrl->GetSpec(spec);
02016   printf("aUrl == %s\n", spec.get());
02017 #endif
02018   FindServerByURI(aUrl, PR_TRUE, aResult);
02019   return NS_OK;
02020 }
02021 
02022 PRBool
02023 nsMsgAccountManager::findAccountByServerKey(nsISupports *element,
02024                                           void *aData)
02025 {
02026   nsresult rv;
02027   findAccountByKeyEntry *entry = (findAccountByKeyEntry*)aData;
02028   nsCOMPtr<nsIMsgAccount> account =
02029     do_QueryInterface(element, &rv);
02030   if (NS_FAILED(rv)) 
02031     return PR_TRUE;
02032 
02033   nsCOMPtr<nsIMsgIncomingServer> server;
02034   rv = account->GetIncomingServer(getter_AddRefs(server));
02035   if (!server || NS_FAILED(rv)) 
02036     return PR_TRUE;
02037 
02038   nsXPIDLCString key;
02039   rv = server->GetKey(getter_Copies(key));
02040   if (NS_FAILED(rv)) return PR_TRUE;
02041 
02042   // if the keys are equal, the servers are equal
02043   if (PL_strcmp(key, entry->key)==0) 
02044   {
02045     entry->account = account;
02046     return PR_FALSE;            // stop on first found account
02047   }
02048 
02049   return PR_TRUE;
02050 }
02051 
02052 NS_IMETHODIMP
02053 nsMsgAccountManager::FindAccountForServer(nsIMsgIncomingServer *server,
02054                                             nsIMsgAccount **aResult)
02055 {
02056   NS_ENSURE_ARG_POINTER(aResult);
02057   
02058   if (!server) 
02059   {
02060     (*aResult) = nsnull;
02061     return NS_OK;
02062   }
02063   
02064   nsresult rv;
02065 
02066   nsXPIDLCString key;
02067   rv = server->GetKey(getter_Copies(key));
02068   if (NS_FAILED(rv)) return rv;
02069   
02070   findAccountByKeyEntry entry;
02071   entry.key = key;
02072   entry.account = nsnull;
02073 
02074   m_accounts->EnumerateForwards(findAccountByServerKey, (void *)&entry);
02075 
02076   if (entry.account) 
02077     NS_ADDREF(*aResult = entry.account);
02078 
02079   return NS_OK;
02080 }
02081 
02082 // if the aElement matches the given hostname, add it to the given array
02083 PRBool
02084 nsMsgAccountManager::findServerUrl(nsISupports *aElement, void *data)
02085 {
02086   nsresult rv;
02087   
02088   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(aElement);
02089   if (!server) return PR_TRUE;
02090 
02091   findServerEntry *entry = (findServerEntry*) data;
02092   
02093   nsXPIDLCString thisHostname;
02094   if (entry->useRealSetting)
02095     rv = server->GetRealHostName(getter_Copies(thisHostname));
02096   else
02097     rv = server->GetHostName(getter_Copies(thisHostname));
02098   if (NS_FAILED(rv)) return PR_TRUE;
02099 
02100   nsXPIDLCString thisUsername;
02101   if (entry->useRealSetting)
02102     rv = server->GetRealUsername(getter_Copies(thisUsername));
02103   else
02104     rv = server->GetUsername(getter_Copies(thisUsername));
02105   if (NS_FAILED(rv)) return PR_TRUE;
02106  
02107   nsXPIDLCString thisType;
02108   rv = server->GetType(getter_Copies(thisType));
02109   if (NS_FAILED(rv)) return PR_TRUE;
02110  
02111   PRInt32 thisPort = -1; // use the default port identifier
02112   // Don't try and get a port for the 'none' scheme 
02113   if (!thisType.EqualsLiteral("none")) 
02114   {
02115     rv = server->GetPort(&thisPort);
02116     NS_ENSURE_TRUE(NS_SUCCEEDED(rv), PR_TRUE);
02117   }
02118 
02119   // treat "" as a wild card, so if the caller passed in "" for the desired attribute
02120   // treat it as a match
02121   // NOTE: DO NOT REPLACE PL_strcmp OR PL_strcasecmp below because one or more
02122   // of these items may be null
02123   if ((!*entry->type || (PL_strcmp(entry->type, thisType)==0)) && 
02124       (!*entry->hostname || (PL_strcasecmp(entry->hostname, thisHostname)==0)) && 
02125       (!(entry->port != 0) || (entry->port == thisPort)) && 
02126       (!*entry->username || (PL_strcmp(entry->username, thisUsername)==0))) 
02127   {
02128     entry->server = server;
02129     return PR_FALSE;            // stop on first find 
02130   }
02131   
02132   return PR_TRUE;
02133 }
02134 
02135 // if the aElement matches the given hostname, add it to the given array
02136 PRBool
02137 nsMsgAccountManager::findServer(nsISupports *aElement, void *data)
02138 {
02139   nsresult rv;
02140   
02141   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(aElement, &rv);
02142   if (NS_FAILED(rv)) return PR_TRUE;
02143 
02144   findServerEntry *entry = (findServerEntry*) data;
02145   
02146   nsXPIDLCString thisHostname;
02147   if (entry->useRealSetting)
02148     rv = server->GetRealHostName(getter_Copies(thisHostname));
02149   else
02150   rv = server->GetHostName(getter_Copies(thisHostname));
02151   if (NS_FAILED(rv)) return PR_TRUE;
02152 
02153   nsXPIDLCString thisUsername;
02154   if (entry->useRealSetting)
02155     rv = server->GetRealUsername(getter_Copies(thisUsername));
02156   else
02157   rv = server->GetUsername(getter_Copies(thisUsername));
02158   if (NS_FAILED(rv)) return PR_TRUE;
02159  
02160   nsXPIDLCString thisType;
02161   rv = server->GetType(getter_Copies(thisType));
02162   if (NS_FAILED(rv)) return PR_TRUE;
02163  
02164   // treat "" as a wild card, so if the caller passed in "" for the desired attribute
02165   // treat it as a match
02166   PRBool checkType = PL_strcmp(entry->type, "");
02167   PRBool checkHostname = PL_strcmp(entry->hostname,"");
02168   PRBool checkUsername = PL_strcmp(entry->username,"");
02169   if ((!checkType || (PL_strcmp(entry->type, thisType)==0)) && 
02170       (!checkHostname || (PL_strcasecmp(entry->hostname, thisHostname)==0)) && 
02171       (!checkUsername || (PL_strcmp(entry->username, thisUsername)==0))) 
02172   {
02173     entry->server = server;
02174     return PR_FALSE;            // stop on first find 
02175   }
02176   
02177   return PR_TRUE;
02178 }
02179 
02180 NS_IMETHODIMP
02181 nsMsgAccountManager::GetFirstIdentityForServer(nsIMsgIncomingServer *aServer, nsIMsgIdentity **aIdentity)
02182 {
02183   NS_ENSURE_ARG_POINTER(aServer);
02184   NS_ENSURE_ARG_POINTER(aIdentity);
02185 
02186   nsCOMPtr<nsISupportsArray> identities;
02187   nsresult rv = GetIdentitiesForServer(aServer, getter_AddRefs(identities));
02188   NS_ENSURE_SUCCESS(rv, rv);
02189 
02190   // not all servers have identities
02191   // for example, Local Folders
02192   PRUint32 numIdentities;
02193   rv = identities->Count(&numIdentities);
02194   NS_ENSURE_SUCCESS(rv, rv);
02195 
02196   if (numIdentities > 0) 
02197   {
02198     nsCOMPtr<nsIMsgIdentity> identity;
02199     rv = identities->QueryElementAt(0, NS_GET_IID(nsIMsgIdentity),
02200                                   (void **)getter_AddRefs(identity));
02201     NS_ENSURE_SUCCESS(rv, rv);
02202     NS_IF_ADDREF(*aIdentity = identity);
02203   }
02204   else
02205     *aIdentity = nsnull;
02206   return rv;
02207 }
02208 
02209 NS_IMETHODIMP
02210 nsMsgAccountManager::GetIdentitiesForServer(nsIMsgIncomingServer *server,
02211                                             nsISupportsArray **_retval)
02212 {
02213   NS_ENSURE_ARG_POINTER(server);
02214   NS_ENSURE_ARG_POINTER(_retval);
02215   nsresult rv;
02216   rv = LoadAccounts();
02217   if (NS_FAILED(rv)) return rv;
02218   
02219   nsCOMPtr<nsISupportsArray> identities;
02220   rv = NS_NewISupportsArray(getter_AddRefs(identities));
02221   if (NS_FAILED(rv)) return rv;
02222   
02223   findIdentitiesByServerEntry identityInfo;
02224   identityInfo.server = server;
02225   identityInfo.identities = identities;
02226   
02227   m_accounts->EnumerateForwards(findIdentitiesForServer,
02228                                 (void *)&identityInfo);
02229 
02230   // do an addref for the caller.
02231   NS_ADDREF(*_retval = identities);
02232 
02233   return NS_OK;
02234 }
02235 
02236 PRBool
02237 nsMsgAccountManager::findIdentitiesForServer(nsISupports* element, void *aData)
02238 {
02239   nsresult rv;
02240   nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(element, &rv);
02241   if (NS_FAILED(rv)) return PR_TRUE;
02242   
02243   findIdentitiesByServerEntry *entry = (findIdentitiesByServerEntry*)aData;
02244   
02245   nsCOMPtr<nsIMsgIncomingServer> thisServer;
02246   rv = account->GetIncomingServer(getter_AddRefs(thisServer));
02247   if (NS_FAILED(rv)) return PR_TRUE;
02248   nsXPIDLCString serverKey;
02249     
02250 //  NS_ASSERTION(thisServer, "thisServer is null");
02251   NS_ASSERTION(entry, "entry is null");
02252   NS_ASSERTION(entry->server, "entry->server is null");
02253   // if this happens, bail.
02254   if (!thisServer || !entry || !(entry->server)) 
02255     return PR_TRUE;
02256 
02257   entry->server->GetKey(getter_Copies(serverKey));
02258   nsXPIDLCString thisServerKey;
02259   thisServer->GetKey(getter_Copies(thisServerKey));
02260   if (PL_strcmp(serverKey, thisServerKey)==0) 
02261   {
02262     // add all these elements to the nsISupports array
02263     nsCOMPtr<nsISupportsArray> theseIdentities;
02264     rv = account->GetIdentities(getter_AddRefs(theseIdentities));
02265     if (NS_SUCCEEDED(rv))
02266       rv = entry->identities->AppendElements(theseIdentities);
02267   }
02268 
02269   return PR_TRUE;
02270 }
02271 
02272 NS_IMETHODIMP
02273 nsMsgAccountManager::GetServersForIdentity(nsIMsgIdentity *identity,
02274                                            nsISupportsArray **_retval)
02275 {
02276   nsresult rv;
02277   rv = LoadAccounts();
02278   if (NS_FAILED(rv)) return rv;
02279   
02280   nsCOMPtr<nsISupportsArray> servers;
02281   rv = NS_NewISupportsArray(getter_AddRefs(servers));
02282   if (NS_FAILED(rv)) return rv;
02283   
02284   findServersByIdentityEntry serverInfo;
02285   serverInfo.identity = identity;
02286   serverInfo.servers = servers;
02287   
02288   m_accounts->EnumerateForwards(findServersForIdentity,
02289                                 (void *)&serverInfo);
02290 
02291   // do an addref for the caller.
02292   NS_ADDREF(*_retval = servers);
02293 
02294   return NS_OK;
02295 }
02296   
02297 PRBool
02298 nsMsgAccountManager::findServersForIdentity(nsISupports *element, void *aData)
02299 {
02300   nsresult rv;
02301   nsCOMPtr<nsIMsgAccount> account = do_QueryInterface(element, &rv);
02302   if (NS_FAILED(rv)) return PR_TRUE;
02303   
02304   findServersByIdentityEntry *entry = (findServersByIdentityEntry*)aData;
02305 
02306   nsCOMPtr<nsISupportsArray> identities;
02307   account->GetIdentities(getter_AddRefs(identities));
02308 
02309   PRUint32 idCount=0;
02310   identities->Count(&idCount);
02311 
02312   PRUint32 id;
02313   nsXPIDLCString identityKey;
02314   rv = entry->identity->GetKey(getter_Copies(identityKey));
02315 
02316   
02317   for (id=0; id<idCount; id++) 
02318   {
02319 
02320     // convert supports->Identity
02321     nsCOMPtr<nsISupports> thisSupports;
02322     rv = identities->GetElementAt(id, getter_AddRefs(thisSupports));
02323     if (NS_FAILED(rv)) continue;
02324     
02325     nsCOMPtr<nsIMsgIdentity>
02326       thisIdentity = do_QueryInterface(thisSupports, &rv);
02327 
02328     if (NS_SUCCEEDED(rv)) 
02329     {
02330 
02331       nsXPIDLCString thisIdentityKey;
02332       rv = thisIdentity->GetKey(getter_Copies(thisIdentityKey));
02333 
02334       if (NS_SUCCEEDED(rv) && PL_strcmp(identityKey, thisIdentityKey) == 0) 
02335       {
02336         nsCOMPtr<nsIMsgIncomingServer> thisServer;
02337         rv = account->GetIncomingServer(getter_AddRefs(thisServer));
02338         
02339         if (thisServer && NS_SUCCEEDED(rv)) 
02340         {
02341           entry->servers->AppendElement(thisServer);
02342           break;
02343         }
02344       }
02345     }
02346   }
02347 
02348   return PR_TRUE;
02349 }
02350 
02351 nsresult
02352 nsMsgAccountManager::AddRootFolderListener(nsIFolderListener *aListener)
02353 {
02354   nsresult rv;
02355   NS_ENSURE_TRUE(aListener, NS_OK);
02356   // first add listener to the list
02357   rv = mFolderListeners->AppendElement(aListener);
02358   
02359   // now add the listener to all loaded accounts
02360   m_incomingServers.Enumerate(addListener, (void *)aListener);
02361 
02362   return NS_OK;
02363 }
02364 
02365 nsresult
02366 nsMsgAccountManager::RemoveRootFolderListener(nsIFolderListener *aListener)
02367 {
02368   nsresult rv;
02369   NS_ENSURE_TRUE(aListener, NS_OK);
02370   // remove the listener from the notification list
02371   rv = mFolderListeners->RemoveElement(aListener);
02372   
02373   // remove the listener from the individual folders
02374   m_incomingServers.Enumerate(removeListener, (void *)aListener);
02375 
02376   return NS_OK;
02377 }
02378 
02379 // enumeration functions
02380 PRBool
02381 nsMsgAccountManager::addListener(nsHashKey *aKey, void *element, void *aData)
02382 {
02383   nsIMsgIncomingServer *server = (nsIMsgIncomingServer *)element;
02384   nsIFolderListener* listener = (nsIFolderListener *)aData;
02385 
02386   nsresult rv;
02387   
02388   nsCOMPtr<nsIMsgFolder> rootFolder;
02389   rv = server->GetRootFolder(getter_AddRefs(rootFolder));
02390   NS_ENSURE_SUCCESS(rv, PR_TRUE);
02391 
02392   rv = rootFolder->AddFolderListener(listener);
02393   NS_ENSURE_SUCCESS(rv, PR_TRUE);
02394   
02395   return PR_TRUE;
02396 }
02397 
02398 PRBool
02399 nsMsgAccountManager::removeListener(nsHashKey *aKey, void *element, void *aData)
02400 {
02401   nsIMsgIncomingServer *server = (nsIMsgIncomingServer *)element;
02402   nsIFolderListener* listener = (nsIFolderListener *)aData;
02403 
02404   nsresult rv;
02405   
02406   nsCOMPtr<nsIMsgFolder> rootFolder;
02407   rv = server->GetRootFolder(getter_AddRefs(rootFolder));
02408   NS_ENSURE_SUCCESS(rv, PR_TRUE);
02409 
02410   rv = rootFolder->RemoveFolderListener(listener);
02411   NS_ENSURE_SUCCESS(rv, PR_TRUE);
02412   
02413   return PR_TRUE;
02414 }
02415 
02416 NS_IMETHODIMP nsMsgAccountManager::SetLocalFoldersServer(nsIMsgIncomingServer *aServer)
02417 {
02418     nsresult rv;
02419     if (!aServer) return NS_ERROR_NULL_POINTER;
02420 
02421     nsXPIDLCString key;
02422     rv = aServer->GetKey(getter_Copies(key));
02423     if (NS_FAILED(rv)) return rv;
02424     
02425     rv = m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_LOCALFOLDERSSERVER, (const char *)key);
02426     return rv;
02427 }
02428 
02429 NS_IMETHODIMP nsMsgAccountManager::GetLocalFoldersServer(nsIMsgIncomingServer **aServer)
02430 {
02431   nsXPIDLCString serverKey;
02432   nsresult rv;
02433 
02434   if (!aServer) return NS_ERROR_NULL_POINTER;
02435 
02436   if (!m_prefs) 
02437   {
02438     rv = getPrefService();
02439     NS_ENSURE_SUCCESS(rv,rv);
02440   }
02441   rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_LOCALFOLDERSSERVER, getter_Copies(serverKey));
02442 
02443   if (NS_SUCCEEDED(rv) && ((const char *)serverKey)) 
02444   {
02445       rv = GetIncomingServer(serverKey, aServer);
02446       if (!*aServer) return NS_ERROR_FAILURE;
02447       return rv;
02448   }
02449 
02450   // try ("nobody","Local Folders","none"), and work down to any "none" server.
02451   rv = FindServer("nobody","Local Folders","none",aServer);
02452   if (NS_FAILED(rv) || !*aServer) 
02453   {
02454       rv = FindServer("nobody",nsnull,"none",aServer);
02455       if (NS_FAILED(rv) || !*aServer) 
02456       {
02457           rv = FindServer(nsnull,"Local Folders","none",aServer); 
02458           if (NS_FAILED(rv) || !*aServer) 
02459               rv = FindServer(nsnull,nsnull,"none",aServer);
02460       }
02461   }
02462 
02463   if (NS_FAILED(rv)) return rv;
02464   if (!*aServer) return NS_ERROR_FAILURE;
02465   
02466   rv = SetLocalFoldersServer(*aServer);
02467   return rv;
02468 }
02469   // nsIUrlListener methods
02470 
02471 NS_IMETHODIMP
02472 nsMsgAccountManager::OnStartRunningUrl(nsIURI * aUrl)
02473 {
02474     return NS_OK;
02475 }
02476 
02477 NS_IMETHODIMP
02478 nsMsgAccountManager::OnStopRunningUrl(nsIURI * aUrl, nsresult aExitCode)
02479 {
02480   if (aUrl)
02481   {
02482     nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(aUrl); 
02483     if (imapUrl)
02484     {
02485       nsImapAction imapAction = nsIImapUrl::nsImapTest;
02486       imapUrl->GetImapAction(&imapAction);
02487       switch(imapAction)
02488       {
02489         case nsIImapUrl::nsImapExpungeFolder:
02490           if (m_folderDoingCleanupInbox)
02491           {
02492             PR_CEnterMonitor(m_folderDoingCleanupInbox);
02493             PR_CNotifyAll(m_folderDoingCleanupInbox);
02494             m_cleanupInboxInProgress = PR_FALSE;
02495             PR_CExitMonitor(m_folderDoingCleanupInbox);
02496             m_folderDoingCleanupInbox=nsnull;   //reset to nsnull
02497           }
02498           break;
02499         case nsIImapUrl::nsImapDeleteAllMsgs:
02500           if (m_folderDoingEmptyTrash) 
02501           {
02502             PR_CEnterMonitor(m_folderDoingEmptyTrash);
02503             PR_CNotifyAll(m_folderDoingEmptyTrash);
02504             m_emptyTrashInProgress = PR_FALSE;
02505             PR_CExitMonitor(m_folderDoingEmptyTrash);
02506             m_folderDoingEmptyTrash = nsnull;  //reset to nsnull;
02507           }
02508           break;
02509         default:
02510           break;
02511        } 
02512      }
02513    }
02514    return NS_OK;
02515 }
02516 
02517 NS_IMETHODIMP
02518 nsMsgAccountManager::SetFolderDoingEmptyTrash(nsIMsgFolder *folder)
02519 {
02520   m_folderDoingEmptyTrash = folder;
02521   m_emptyTrashInProgress = PR_TRUE;
02522   return NS_OK;
02523 }
02524 
02525 NS_IMETHODIMP
02526 nsMsgAccountManager::GetEmptyTrashInProgress(PRBool *bVal)
02527 {
02528   NS_ENSURE_ARG_POINTER(bVal);
02529   *bVal = m_emptyTrashInProgress;
02530   return NS_OK;
02531 }
02532 
02533 NS_IMETHODIMP
02534 nsMsgAccountManager::SetFolderDoingCleanupInbox(nsIMsgFolder *folder)
02535 {
02536   m_folderDoingCleanupInbox = folder;
02537   m_cleanupInboxInProgress = PR_TRUE;
02538   return NS_OK;
02539 }
02540 
02541 NS_IMETHODIMP
02542 nsMsgAccountManager::GetCleanupInboxInProgress(PRBool *bVal)
02543 {
02544   NS_ENSURE_ARG_POINTER(bVal);
02545   *bVal = m_cleanupInboxInProgress;
02546   return NS_OK;
02547 }
02548 nsresult 
02549 nsMsgAccountManager::SetLastServerFound(nsIMsgIncomingServer *server, const char *hostname, const char *username, PRInt32 port, const char *type)
02550 {
02551     m_lastFindServerResult = server;
02552     m_lastFindServerHostName = hostname;
02553     m_lastFindServerUserName = username;
02554     m_lastFindServerPort = port;
02555     m_lastFindServerType = type;
02556     
02557     return NS_OK;
02558 }
02559 
02560 NS_IMETHODIMP
02561 nsMsgAccountManager::SaveAccountInfo()
02562 {
02563   nsresult rv;
02564   nsCOMPtr<nsIPrefService> pref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
02565   NS_ENSURE_SUCCESS(rv,rv);
02566   return pref->SavePrefFile(nsnull);
02567 }
02568 
02569 NS_IMETHODIMP
02570 nsMsgAccountManager::GetChromePackageName(const char *aExtensionName, char **aChromePackageName)
02571 {
02572   NS_ENSURE_ARG_POINTER(aExtensionName);
02573   NS_ENSURE_ARG_POINTER(aChromePackageName);
02574  
02575   nsresult rv;
02576   nsCOMPtr<nsICategoryManager> catman = do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
02577   NS_ENSURE_SUCCESS(rv,rv);
02578 
02579   nsCOMPtr<nsISimpleEnumerator> e;
02580   rv = catman->EnumerateCategory(MAILNEWS_ACCOUNTMANAGER_EXTENSIONS, getter_AddRefs(e));
02581   if(NS_SUCCEEDED(rv) && e) {
02582     while (PR_TRUE) {
02583       nsCOMPtr<nsISupportsCString> catEntry;
02584       rv = e->GetNext(getter_AddRefs(catEntry));
02585       if (NS_FAILED(rv) || !catEntry) 
02586         break;
02587 
02588       nsCAutoString entryString;
02589       rv = catEntry->GetData(entryString);
02590       if (NS_FAILED(rv))
02591          break;
02592 
02593       nsXPIDLCString contractidString;
02594       rv = catman->GetCategoryEntry(MAILNEWS_ACCOUNTMANAGER_EXTENSIONS, entryString.get(), getter_Copies(contractidString));
02595       if (NS_FAILED(rv)) 
02596         break;
02597 
02598       nsCOMPtr <nsIMsgAccountManagerExtension> extension = do_GetService(contractidString.get(), &rv);
02599       if (NS_FAILED(rv) || !extension)
02600         break;
02601       
02602       nsXPIDLCString name;
02603       rv = extension->GetName(getter_Copies(name));
02604       if (NS_FAILED(rv))
02605         break;
02606       
02607       if (!strcmp(name.get(), aExtensionName))
02608         return extension->GetChromePackageName(aChromePackageName);
02609     }
02610   }
02611   return NS_ERROR_UNEXPECTED;
02612 }
02613 
02614 class VirtualFolderChangeListener : public nsIDBChangeListener
02615 {
02616 public:
02617   VirtualFolderChangeListener();
02618   ~VirtualFolderChangeListener() {};
02619 
02620   NS_DECL_ISUPPORTS
02621   NS_DECL_NSIDBCHANGELISTENER
02622 
02623   nsresult Init();
02624 
02625   nsCOMPtr <nsIMsgFolder> m_virtualFolder; // folder we're listening to db changes on behalf of.
02626   nsCOMPtr <nsIMsgFolder> m_folderWatching; // folder whose db we're listening to.
02627   nsCOMPtr <nsISupportsArray> m_searchTerms;
02628   nsCOMPtr <nsIMsgSearchSession> m_searchSession;
02629   PRBool m_searchOnMsgStatus;
02630 };
02631 
02632 NS_IMPL_ISUPPORTS1(VirtualFolderChangeListener, nsIDBChangeListener)
02633 
02634 VirtualFolderChangeListener::VirtualFolderChangeListener() : m_searchOnMsgStatus(PR_FALSE)
02635 {
02636 }
02637 
02638 nsresult VirtualFolderChangeListener::Init()
02639 {
02640   nsCOMPtr <nsIMsgDatabase> msgDB;
02641   nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
02642 
02643   nsresult rv = m_virtualFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(msgDB));
02644   if (NS_SUCCEEDED(rv) && msgDB)
02645   {
02646     nsXPIDLCString searchTermString;
02647     dbFolderInfo->GetCharPtrProperty("searchStr", getter_Copies(searchTermString));
02648     nsCOMPtr<nsIMsgFilterService> filterService = do_GetService(NS_MSGFILTERSERVICE_CONTRACTID, &rv);
02649     nsCOMPtr<nsIMsgFilterList> filterList;
02650     rv = filterService->GetTempFilterList(m_virtualFolder, getter_AddRefs(filterList));
02651     NS_ENSURE_SUCCESS(rv, rv);
02652     nsCOMPtr <nsIMsgFilter> tempFilter;
02653     filterList->CreateFilter(NS_LITERAL_STRING("temp").get(), getter_AddRefs(tempFilter));
02654     NS_ENSURE_SUCCESS(rv, rv);
02655     filterList->ParseCondition(tempFilter, searchTermString);
02656     NS_ENSURE_SUCCESS(rv, rv);
02657     m_searchSession = do_CreateInstance(NS_MSGSEARCHSESSION_CONTRACTID, &rv);
02658     NS_ENSURE_SUCCESS(rv, rv);
02659 
02660     nsCOMPtr<nsISupportsArray> searchTerms;
02661     rv = tempFilter->GetSearchTerms(getter_AddRefs(searchTerms));
02662     NS_ENSURE_SUCCESS(rv, rv);
02663 
02664     // we add the search scope right before we match the header,
02665     // because we don't want the search scope caching the body input
02666     // stream, because that holds onto the mailbox file, breaking
02667     // compaction.
02668 
02669     // add each item in termsArray to the search session
02670     PRUint32 numTerms;
02671     searchTerms->Count(&numTerms);
02672     for (PRUint32 i = 0; i < numTerms; i++)
02673     {
02674       nsCOMPtr <nsIMsgSearchTerm> searchTerm (do_QueryElementAt(searchTerms, i));
02675       nsMsgSearchAttribValue attrib;
02676       searchTerm->GetAttrib(&attrib);
02677       if (attrib == nsMsgSearchAttrib::MsgStatus)
02678         m_searchOnMsgStatus = true;
02679       m_searchSession->AppendTerm(searchTerm);
02680     }
02681   }
02682   return rv;
02683 }
02684 
02688 NS_IMETHODIMP VirtualFolderChangeListener::OnHdrChange(nsIMsgDBHdr *aHdrChanged, PRUint32 aOldFlags, PRUint32 aNewFlags, nsIDBChangeListener *aInstigator)
02689 {
02690   nsCOMPtr <nsIMsgDatabase> msgDB;
02691 
02692   nsresult rv = m_folderWatching->GetMsgDatabase(nsnull, getter_AddRefs(msgDB));
02693   PRBool oldMatch = PR_FALSE, newMatch = PR_FALSE;
02694   // we don't want any early returns from this function, until we've 
02695   // called ClearScopes 0n the search session.
02696   m_searchSession->AddScopeTerm(nsMsgSearchScope::offlineMail, m_folderWatching);
02697   rv = m_searchSession->MatchHdr(aHdrChanged, msgDB, &newMatch);
02698   if (NS_SUCCEEDED(rv) && m_searchOnMsgStatus)
02699   {
02700     // if status is a search criteria, check if the header matched before
02701     // it changed, in order to determine if we need to bump the counts. 
02702     aHdrChanged->SetFlags(aOldFlags);
02703     rv = m_searchSession->MatchHdr(aHdrChanged, msgDB, &oldMatch);
02704     aHdrChanged->SetFlags(aNewFlags); // restore new flags even on match failure.
02705   }
02706   else
02707     oldMatch = newMatch;
02708   m_searchSession->ClearScopes();
02709   NS_ENSURE_SUCCESS(rv, rv);
02710   // we don't want to change the total counts if this virtual folder is open in a view,
02711   // because we won't remove the header from view while it's open. On the other hand,
02712   // it's hard to fix the count when the user clicks away to another folder, w/o re-running
02713   // the search, or setting some sort of pending count change.
02714   // Maybe this needs to be handled in the view code...the view could do the same calculation
02715   // and also keep track of the counts changed. Then, when the view was closed, if it's a virtual
02716   // folder, it could update the counts for the db.
02717   if (oldMatch != newMatch || (oldMatch && (aOldFlags & MSG_FLAG_READ) != (aNewFlags & MSG_FLAG_READ)))
02718   {
02719     nsCOMPtr <nsIMsgDatabase> virtDatabase;
02720     nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
02721 
02722     rv = m_virtualFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(virtDatabase));
02723     NS_ENSURE_SUCCESS(rv, rv);
02724     PRInt32 totalDelta = 0,  unreadDelta = 0;
02725     if (oldMatch != newMatch)
02726     {
02727  //     PRBool isOpen = PR_FALSE;
02728 //      nsCOMPtr <nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID);
02729 //      if (mailSession && aFolder)
02730 //        mailSession->IsFolderOpenInWindow(m_virtualFolder, &isOpen);
02731       // we can't remove headers that no longer match - but we might add headers that newly match, someday.
02732 //      if (!isOpen)
02733         totalDelta = (oldMatch) ? -1 : 1;
02734     }
02735     PRBool msgHdrIsRead;
02736     aHdrChanged->GetIsRead(&msgHdrIsRead);
02737     if (oldMatch == newMatch) // read flag changed state
02738       unreadDelta = (msgHdrIsRead) ? -1 : 1; 
02739     else if (oldMatch) // else header should removed
02740       unreadDelta = (aOldFlags & MSG_FLAG_READ) ? 0 : -1;
02741     else               // header should be added
02742       unreadDelta = (aNewFlags & MSG_FLAG_READ) ? 0 : 1;
02743     if (unreadDelta)
02744       dbFolderInfo->ChangeNumUnreadMessages(unreadDelta);
02745     if (totalDelta)
02746       dbFolderInfo->ChangeNumMessages(totalDelta);
02747     if (unreadDelta == -1 && aOldFlags & MSG_FLAG_NEW)
02748     {
02749       PRInt32 numNewMessages;
02750       m_virtualFolder->GetNumNewMessages(PR_FALSE, &numNewMessages);
02751       m_virtualFolder->SetNumNewMessages(numNewMessages - 1);
02752       if (numNewMessages == 1)
02753         m_virtualFolder->SetHasNewMessages(PR_FALSE);
02754     }
02755     if (totalDelta)
02756     {
02757       nsXPIDLCString searchUri;
02758       m_virtualFolder->GetURI(getter_Copies(searchUri));
02759       msgDB->UpdateHdrInCache(searchUri, aHdrChanged, totalDelta == 1);
02760     }
02761 
02762     m_virtualFolder->UpdateSummaryTotals(PR_TRUE); // force update from db.
02763     virtDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
02764   }
02765   return rv;
02766 }
02767 
02768 NS_IMETHODIMP VirtualFolderChangeListener::OnHdrDeleted(nsIMsgDBHdr *aHdrDeleted, nsMsgKey aParentKey, PRInt32 aFlags, nsIDBChangeListener *aInstigator)
02769 {
02770   nsCOMPtr <nsIMsgDatabase> msgDB;
02771 
02772   nsresult rv = m_folderWatching->GetMsgDatabase(nsnull, getter_AddRefs(msgDB));
02773   NS_ENSURE_SUCCESS(rv, rv);
02774   PRBool match = PR_FALSE;
02775   m_searchSession->AddScopeTerm(nsMsgSearchScope::offlineMail, m_folderWatching);
02776   rv = m_searchSession->MatchHdr(aHdrDeleted, msgDB, &match);
02777   m_searchSession->ClearScopes();
02778   if (match)
02779   {
02780     nsCOMPtr <nsIMsgDatabase> virtDatabase;
02781     nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
02782 
02783     rv = m_virtualFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(virtDatabase));
02784     PRBool msgHdrIsRead;
02785     aHdrDeleted->GetIsRead(&msgHdrIsRead);
02786     if (!msgHdrIsRead)
02787       dbFolderInfo->ChangeNumUnreadMessages(-1);
02788     dbFolderInfo->ChangeNumMessages(-1);
02789     PRUint32 hdrFlags;
02790     aHdrDeleted->GetFlags(&hdrFlags);
02791     if (hdrFlags & MSG_FLAG_NEW)
02792     {
02793       PRInt32 numNewMessages;
02794       m_virtualFolder->GetNumNewMessages(PR_FALSE, &numNewMessages);
02795       m_virtualFolder->SetNumNewMessages(numNewMessages - 1);
02796       if (numNewMessages == 1)
02797         m_virtualFolder->SetHasNewMessages(PR_FALSE);
02798     }
02799     nsXPIDLCString searchUri;
02800     m_virtualFolder->GetURI(getter_Copies(searchUri));
02801     msgDB->UpdateHdrInCache(searchUri, aHdrDeleted, PR_FALSE);
02802 
02803     m_virtualFolder->UpdateSummaryTotals(PR_TRUE); // force update from db.
02804     virtDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
02805   }
02806   return rv;
02807 }
02808 
02809 NS_IMETHODIMP VirtualFolderChangeListener::OnHdrAdded(nsIMsgDBHdr *aNewHdr, nsMsgKey aParentKey, PRInt32 aFlags, nsIDBChangeListener *aInstigator)
02810 {
02811   nsCOMPtr <nsIMsgDatabase> msgDB;
02812 
02813   nsresult rv = m_folderWatching->GetMsgDatabase(nsnull, getter_AddRefs(msgDB));
02814   NS_ENSURE_SUCCESS(rv, rv);
02815   PRBool match = PR_FALSE;
02816   m_searchSession->AddScopeTerm(nsMsgSearchScope::offlineMail, m_folderWatching);
02817   rv = m_searchSession->MatchHdr(aNewHdr, msgDB, &match);
02818   m_searchSession->ClearScopes();
02819   if (match)
02820   {
02821     nsCOMPtr <nsIMsgDatabase> virtDatabase;
02822     nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
02823 
02824     rv = m_virtualFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(virtDatabase));
02825     PRBool msgHdrIsRead;
02826     PRUint32 msgFlags;
02827     aNewHdr->GetIsRead(&msgHdrIsRead);
02828     aNewHdr->GetFlags(&msgFlags);
02829     if (!msgHdrIsRead)
02830       dbFolderInfo->ChangeNumUnreadMessages(1);
02831     if (msgFlags & MSG_FLAG_NEW)
02832     {
02833       PRInt32 numNewMessages;
02834       m_virtualFolder->GetNumNewMessages(PR_FALSE, &numNewMessages);
02835       m_virtualFolder->SetHasNewMessages(PR_TRUE);
02836       m_virtualFolder->SetNumNewMessages(numNewMessages + 1);
02837     }
02838     nsXPIDLCString searchUri;
02839     m_virtualFolder->GetURI(getter_Copies(searchUri));
02840     msgDB->UpdateHdrInCache(searchUri, aNewHdr, PR_TRUE);
02841     dbFolderInfo->ChangeNumMessages(1);
02842     m_virtualFolder->UpdateSummaryTotals(true); // force update from db.
02843     virtDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
02844   }
02845   return rv;
02846 }
02847 
02848 NS_IMETHODIMP VirtualFolderChangeListener::OnParentChanged(nsMsgKey aKeyChanged, nsMsgKey oldParent, nsMsgKey newParent, nsIDBChangeListener *aInstigator)
02849 {
02850   return NS_OK;
02851 }
02852 
02853 NS_IMETHODIMP VirtualFolderChangeListener::OnAnnouncerGoingAway(nsIDBChangeAnnouncer *instigator)
02854 {
02855   nsCOMPtr <nsIMsgDatabase> msgDB = do_QueryInterface(instigator);
02856   if (msgDB)
02857     msgDB->RemoveListener(this);
02858   return NS_OK;
02859 }
02860 
02861 NS_IMETHODIMP VirtualFolderChangeListener::OnReadChanged(nsIDBChangeListener *aInstigator)
02862 {
02863   return NS_OK;
02864 }
02865 
02866 NS_IMETHODIMP VirtualFolderChangeListener::OnJunkScoreChanged(nsIDBChangeListener *aInstigator)
02867 {
02868   return NS_OK;
02869 }
02870 
02871 nsresult nsMsgAccountManager::GetVirtualFoldersFile(nsCOMPtr<nsILocalFile>& file)
02872 {
02873   nsCOMPtr<nsIFile> profileDir;
02874   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profileDir));
02875   NS_ENSURE_SUCCESS(rv, rv);
02876 
02877   rv = profileDir->AppendNative(nsDependentCString("virtualFolders.dat"));
02878   if (NS_SUCCEEDED(rv))
02879     file = do_QueryInterface(profileDir, &rv);
02880   return rv;
02881 }
02882 
02883 NS_IMETHODIMP nsMsgAccountManager::LoadVirtualFolders()
02884 {
02885   nsCOMPtr <nsILocalFile> file;
02886   GetVirtualFoldersFile(file);
02887   if (!file)
02888     return NS_ERROR_FAILURE;
02889 
02890   m_loadingVirtualFolders = PR_TRUE;
02891 
02892   nsresult rv;
02893   nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
02894   if (msgDBService) 
02895   {
02896      NS_ENSURE_SUCCESS(rv, rv);
02897      nsCOMPtr<nsIFileInputStream> fileStream = do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv);
02898      NS_ENSURE_SUCCESS(rv, rv);
02899 
02900      rv = fileStream->Init(file,  PR_RDONLY, 0664, PR_FALSE);  
02901      nsCOMPtr <nsILineInputStream> lineInputStream(do_QueryInterface(fileStream));
02902 
02903     PRBool isMore = PR_TRUE;
02904     nsCAutoString buffer;
02905     PRInt32 version = -1;
02906     nsCOMPtr <nsIMsgFolder> virtualFolder;
02907     nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
02908     nsCOMPtr<nsIRDFResource> resource;
02909     nsCOMPtr<nsIRDFService> rdf(do_GetService("@mozilla.org/rdf/rdf-service;1", &rv));
02910     NS_ENSURE_SUCCESS(rv, rv);
02911 
02912     while (isMore &&
02913            NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore)))
02914     {
02915       if (buffer.Length() > 0)
02916       {
02917         if (version == -1)
02918         {
02919           buffer.Cut(0, 8);
02920           PRInt32 irv;
02921           version = buffer.ToInteger(&irv);
02922           continue;
02923         }
02924         if (Substring(buffer, 0, 4).Equals("uri="))
02925         {
02926           buffer.Cut(0, 4);
02927 
02928           rv = rdf->GetResource(buffer, getter_AddRefs(resource));
02929           NS_ENSURE_SUCCESS(rv, rv);
02930 
02931           virtualFolder = do_QueryInterface(resource, &rv);
02932           NS_ENSURE_SUCCESS(rv, rv);
02933           if (virtualFolder)
02934           {
02935             nsCOMPtr <nsIMsgFolder> grandParent;
02936             nsCOMPtr <nsIMsgFolder> oldParent;
02937             nsCOMPtr <nsIMsgFolder> parentFolder;
02938             PRBool isServer;
02939             do
02940             {
02941               // need to add the folder as a sub-folder of its parent.
02942               PRInt32 lastSlash = buffer.RFindChar('/');
02943               if (lastSlash == kNotFound)
02944                 break;
02945               nsDependentCSubstring parentUri(buffer, 0, lastSlash);
02946               // hold a reference so it won't get deleted before it's parented.
02947               oldParent = parentFolder; 
02948               
02949               rdf->GetResource(parentUri, getter_AddRefs(resource));
02950               parentFolder = do_QueryInterface(resource);
02951               if (parentFolder)
02952               {
02953                 nsAutoString currentFolderNameStr;
02954                 nsCAutoString currentFolderNameCStr(Substring(buffer, lastSlash + 1, buffer.Length()));
02955                 nsUnescape(currentFolderNameCStr.BeginWriting());
02956                 CopyUTF8toUTF16(currentFolderNameCStr, currentFolderNameStr);
02957                 nsCOMPtr <nsIMsgFolder> childFolder;
02958                 nsCOMPtr <nsIMsgDatabase> db;
02959                 virtualFolder->GetMsgDatabase(nsnull, getter_AddRefs(db)); // force db to get created.
02960                 if (db)
02961                   rv = db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
02962                 else
02963                   break;
02964 
02965                 parentFolder->AddSubfolder(currentFolderNameStr, getter_AddRefs(childFolder));
02966                 virtualFolder->SetFlag(MSG_FOLDER_FLAG_VIRTUAL);
02967                 if (childFolder)
02968                   parentFolder->NotifyItemAdded(childFolder);
02969                 // here we make sure if our parent is rooted - if not, we're
02970                 // going to loop and add our parent as a child of its grandparent
02971                 // and repeat until we get to the server, or a folder that
02972                 // has its parent set.
02973                 parentFolder->GetParent(getter_AddRefs(grandParent));
02974                 parentFolder->GetIsServer(&isServer);
02975                 buffer.Truncate(lastSlash);
02976               }
02977               else
02978                 break;
02979             } while (!grandParent && !isServer);
02980           }
02981         }
02982         else if (dbFolderInfo && Substring(buffer, 0, 6).Equals("scope="))
02983         {
02984           buffer.Cut(0, 6);
02985           // if this is a cross folder virtual folder, we have a list of folders uris,
02986           // and we have to add a pending listener for each of them.
02987           if (buffer.Length())
02988           {
02989             dbFolderInfo->SetCharPtrProperty("searchFolderUri", buffer.get());
02990             AddVFListenersForVF(virtualFolder, buffer.get(), rdf, msgDBService);
02991           }
02992           else // this folder is useless
02993           {
02994           }
02995         }
02996         else if (dbFolderInfo && Substring(buffer, 0, 6).Equals("terms="))
02997         {
02998           buffer.Cut(0, 6);
02999           dbFolderInfo->SetCharPtrProperty("searchStr", buffer.get());
03000         }
03001         else if (dbFolderInfo && Substring(buffer, 0, 13).Equals("searchOnline="))
03002         {
03003           buffer.Cut(0, 13);
03004           dbFolderInfo->SetBooleanProperty("searchOnline", buffer.Equals("true"));
03005         }
03006       }
03007     }
03008   }
03009 
03010   m_loadingVirtualFolders = PR_FALSE;
03011   m_virtualFoldersLoaded = PR_TRUE;
03012   return rv;
03013 }
03014 
03015 
03016 
03017 NS_IMETHODIMP nsMsgAccountManager::SaveVirtualFolders()
03018 {
03019   if (!m_virtualFoldersLoaded)
03020     return NS_OK;
03021   nsCOMPtr<nsISupportsArray> allServers;
03022   nsresult rv = GetAllServers(getter_AddRefs(allServers));
03023   nsCOMPtr <nsILocalFile> file;
03024   if (allServers)
03025   {
03026     PRUint32 count = 0;
03027     allServers->Count(&count);
03028     PRUint32 i;
03029     nsCOMPtr <nsIOutputStream> outputStream;
03030     for (i = 0; i < count; i++) 
03031     {
03032       nsCOMPtr<nsIMsgIncomingServer> server = do_QueryElementAt(allServers, i);
03033       if (server)
03034       {
03035         nsCOMPtr <nsIMsgFolder> rootFolder;
03036         server->GetRootFolder(getter_AddRefs(rootFolder));
03037         if (rootFolder)
03038         {
03039           nsCOMPtr <nsISupportsArray> virtualFolders;
03040           rv = rootFolder->GetAllFoldersWithFlag(MSG_FOLDER_FLAG_VIRTUAL, getter_AddRefs(virtualFolders));
03041           NS_ENSURE_SUCCESS(rv, rv);
03042           PRUint32 vfCount;
03043           virtualFolders->Count(&vfCount);
03044           if (!outputStream)
03045           {
03046             GetVirtualFoldersFile(file);
03047             rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
03048                                              file,
03049                                              PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
03050                                              0664);
03051             NS_ENSURE_SUCCESS(rv, rv);
03052             WriteLineToOutputStream("version=", "1", outputStream);
03053 
03054           }
03055           for (PRUint32 folderIndex = 0; folderIndex < vfCount; folderIndex++)
03056           {
03057             nsCOMPtr <nsIRDFResource> folderRes (do_QueryElementAt(virtualFolders, folderIndex));
03058             nsCOMPtr <nsIMsgFolder> msgFolder = do_QueryInterface(folderRes);
03059             const char *uri;
03060             nsCOMPtr <nsIMsgDatabase> db;
03061             nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
03062             rv = msgFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(db)); // force db to get created.
03063             if (dbFolderInfo)
03064             {
03065               nsXPIDLCString srchFolderUri;
03066               nsXPIDLCString searchTerms; 
03067               PRBool searchOnline = PR_FALSE;
03068               dbFolderInfo->GetBooleanProperty("searchOnline", PR_FALSE, &searchOnline);
03069               dbFolderInfo->GetCharPtrProperty("searchFolderUri", getter_Copies(srchFolderUri));
03070               dbFolderInfo->GetCharPtrProperty("searchStr", getter_Copies(searchTerms));
03071               folderRes->GetValueConst(&uri);
03072               if (!srchFolderUri.IsEmpty() && !searchTerms.IsEmpty())
03073               {
03074                 WriteLineToOutputStream("uri=", uri, outputStream);
03075                 WriteLineToOutputStream("scope=", srchFolderUri.get(), outputStream);
03076                 WriteLineToOutputStream("terms=", searchTerms.get(), outputStream);
03077                 WriteLineToOutputStream("searchOnline=", searchOnline ? "true" : "false", outputStream);
03078               }
03079             }
03080           }
03081         }
03082       }
03083    }
03084    if (outputStream)
03085     outputStream->Close();
03086   }
03087   return rv;
03088 }
03089 
03090 nsresult nsMsgAccountManager::WriteLineToOutputStream(const char *prefix, const char * line, nsIOutputStream *outputStream)
03091 {
03092   PRUint32 writeCount;
03093   outputStream->Write(prefix, strlen(prefix), &writeCount);
03094   outputStream->Write(line, strlen(line), &writeCount);
03095   outputStream->Write("\n", 1, &writeCount);
03096   return NS_OK;
03097 }
03098 
03099 nsresult nsMsgAccountManager::AddVFListenersForVF(nsIMsgFolder *virtualFolder, 
03100                                                   const char *srchFolderUris,
03101                                                   nsIRDFService *rdf,
03102                                                   nsIMsgDBService *msgDBService)
03103 {
03104   nsCStringArray folderUris;
03105   folderUris.ParseString(srchFolderUris, "|");
03106   nsCOMPtr <nsIRDFResource> resource;
03107 
03108   for (PRInt32 i = 0; i < folderUris.Count(); i++)
03109   {
03110     rdf->GetResource(*(folderUris[i]), getter_AddRefs(resource));
03111     nsCOMPtr <nsIMsgFolder> realFolder = do_QueryInterface(resource);
03112     VirtualFolderChangeListener *dbListener = new VirtualFolderChangeListener();
03113     NS_ENSURE_TRUE(dbListener, NS_ERROR_OUT_OF_MEMORY);
03114     m_virtualFolderListeners.AppendObject(dbListener);
03115     dbListener->m_virtualFolder = virtualFolder;
03116     dbListener->m_folderWatching = realFolder;
03117     dbListener->Init();
03118     msgDBService->RegisterPendingListener(realFolder, dbListener);
03119   }
03120   return NS_OK;
03121 }
03122 
03123 NS_IMETHODIMP nsMsgAccountManager::OnItemAdded(nsIRDFResource *parentItem, nsISupports *item)
03124 {
03125   nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(item);
03126   // just kick out with a success code if the item in question is not a folder
03127   if (!folder)
03128     return NS_OK;
03129   PRUint32 folderFlags;
03130   folder->GetFlags(&folderFlags);
03131   nsresult rv = NS_OK;
03132   // need to make sure this isn't happening during loading of virtualfolders.dat
03133   if (folderFlags & MSG_FOLDER_FLAG_VIRTUAL && !m_loadingVirtualFolders)
03134   {
03135     // When a new virtual folder is added, need to create a db Listener for it.
03136     nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
03137     if (msgDBService)
03138     {
03139       nsCOMPtr <nsIMsgDatabase> virtDatabase;
03140       nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
03141       rv = folder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(virtDatabase));
03142       NS_ENSURE_SUCCESS(rv, rv);
03143       nsXPIDLCString srchFolderUri;
03144       dbFolderInfo->GetCharPtrProperty("searchFolderUri", getter_Copies(srchFolderUri));
03145       nsCOMPtr<nsIRDFService> rdf(do_GetService("@mozilla.org/rdf/rdf-service;1", &rv));
03146       AddVFListenersForVF(folder, srchFolderUri, rdf, msgDBService);
03147     }
03148     rv = SaveVirtualFolders();
03149   }
03150   return rv;
03151 }
03152 
03153 NS_IMETHODIMP nsMsgAccountManager::OnItemRemoved(nsIRDFResource *parentItem, nsISupports *item)
03154 {
03155   nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(item);
03156   // just kick out with a success code if the item in question is not a folder
03157   if (!folder)
03158     return NS_OK;
03159   nsresult rv = NS_OK;
03160   PRUint32 folderFlags;
03161   folder->GetFlags(&folderFlags);
03162   if (folderFlags & MSG_FOLDER_FLAG_VIRTUAL) // if we removed a VF, flush VF list to disk.
03163   {
03164     rv = SaveVirtualFolders();
03165     // clear flags on deleted folder if it's a virtual folder, so that creating a new folder
03166     // with the same name doesn't cause confusion.
03167     folder->SetFlags(0);
03168   }
03169   // need to check if the underlying folder for a VF was removed, in which case we need to
03170   // remove the virtual folder.
03171 
03172  return rv;
03173 }
03174 
03175 NS_IMETHODIMP nsMsgAccountManager::OnItemPropertyChanged(nsIRDFResource *item, nsIAtom *property, const char *oldValue, const char *newValue)
03176 {
03177   return NS_ERROR_NOT_IMPLEMENTED;
03178 }
03179 
03180 NS_IMETHODIMP nsMsgAccountManager::OnItemIntPropertyChanged(nsIRDFResource *item, nsIAtom *property, PRInt32 oldValue, PRInt32 newValue)
03181 {
03182   return NS_ERROR_NOT_IMPLEMENTED;
03183 }
03184 
03185 NS_IMETHODIMP nsMsgAccountManager::OnItemBoolPropertyChanged(nsIRDFResource *item, nsIAtom *property, PRBool oldValue, PRBool newValue)
03186 {
03187   return NS_ERROR_NOT_IMPLEMENTED;
03188 }
03189 
03190 NS_IMETHODIMP nsMsgAccountManager::OnItemUnicharPropertyChanged(nsIRDFResource *item, nsIAtom *property, const PRUnichar *oldValue, const PRUnichar *newValue)
03191 {
03192   return NS_ERROR_NOT_IMPLEMENTED;
03193 }
03194 
03195 
03196 NS_IMETHODIMP nsMsgAccountManager::OnItemPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property, PRUint32 oldFlag, PRUint32 newFlag)
03197 {
03198   return NS_ERROR_NOT_IMPLEMENTED;
03199 }
03200 
03201 NS_IMETHODIMP nsMsgAccountManager::OnItemEvent(nsIMsgFolder *aFolder, nsIAtom *aEvent)
03202 {
03203   return NS_ERROR_NOT_IMPLEMENTED;
03204 }