Back to index

lightning-sunbird  0.9+nobinonly
nsImapProtocol.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Henry Jia <Henry.Jia@sun.com>
00025  *   Lorenzo Colitti <lorenzo@colitti.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 #define DOING_PSEUDO_MAILBOXES
00041 
00042 #ifdef MOZ_LOGGING
00043 // sorry, this has to be before the pre-compiled header
00044 #define FORCE_PR_LOG /* Allow logging in the release build */
00045 #endif
00046 
00047 // as does this
00048 #include "msgCore.h"  // for pre-compiled headers
00049 #include "nsMsgUtils.h"
00050 
00051 #include "nsIServiceManager.h"
00052 #include "nsICharsetConverterManager.h"
00053 #include "nsIStringBundle.h"
00054 
00055 #include "nsMsgImapCID.h"
00056 #include "nsIEventQueueService.h"
00057 #include "nsISupportsObsolete.h"
00058 
00059 #include "nsImapCore.h"
00060 #include "nsImapProtocol.h"
00061 #include "nsIMsgMailNewsUrl.h"
00062 #include "nscore.h"
00063 #include "nsIMAPHostSessionList.h"
00064 #include "nsIMAPBodyShell.h"
00065 #include "nsImapMailFolder.h"
00066 #include "nsImapServerResponseParser.h"
00067 #include "nspr.h"
00068 #include "plbase64.h"
00069 #include "nsIImapService.h"
00070 #include "nsISocketTransportService.h"
00071 #include "nsIStreamListenerTee.h"
00072 #include "nsNetUtil.h"
00073 #include "nsXPIDLString.h"
00074 #include "nsReadableUtils.h"
00075 #include "nsIPipe.h"
00076 #include "nsIMsgFolder.h"
00077 #include "nsMsgMessageFlags.h"
00078 #include "nsImapStringBundle.h"
00079 #include "nsICopyMsgStreamListener.h"
00080 #include "nsTextFormatter.h"
00081 #include "nsAutoLock.h"
00082 #include "nsIMsgHdr.h"
00083 #include "nsMsgI18N.h"
00084 #include "nsEscape.h"
00085 // for the memory cache...
00086 #include "nsICacheEntryDescriptor.h"
00087 #include "nsICacheSession.h"
00088 #include "nsIPrompt.h"
00089 #include "nsIDocShellLoadInfo.h"
00090 #include "nsIDOMWindowInternal.h"
00091 #include "nsIMessengerWindowService.h"
00092 #include "nsIWindowMediator.h"
00093 #include "nsIWindowWatcher.h"
00094 #include "nsCOMPtr.h"
00095 #include "nsMimeTypes.h"
00096 PRLogModuleInfo *IMAP;
00097 
00098 // netlib required files
00099 #include "nsIStreamListener.h"
00100 #include "nsIMsgIncomingServer.h"
00101 #include "nsIImapIncomingServer.h"
00102 #include "nsIPrefBranch.h"
00103 #include "nsIPrefService.h"
00104 #include "nsIPrefLocalizedString.h"
00105 #include "nsImapUtils.h"
00106 #include "nsIProxyObjectManager.h"
00107 #include "nsIStreamConverterService.h"
00108 #include "nsIProxyInfo.h"
00109 #include "nsEventQueueUtils.h"
00110 #include "nsISSLSocketControl.h"
00111 
00112 #define ONE_SECOND ((PRUint32)1000)    // one second
00113 
00114 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
00115 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00116 static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
00117 static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID);
00118 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
00119 
00120 #define OUTPUT_BUFFER_SIZE (4096*2) // mscott - i should be able to remove this if I can use nsMsgLineBuffer???
00121 
00122 #define IMAP_ENV_HEADERS "From To Cc Subject Date Message-ID "
00123 #define IMAP_DB_HEADERS "Priority X-Priority References Newsgroups In-Reply-To Content-Type"
00124 #define IMAP_ENV_AND_DB_HEADERS IMAP_ENV_HEADERS IMAP_DB_HEADERS
00125 static const PRIntervalTime kImapSleepTime = PR_MillisecondsToInterval(1000);
00126 static PRInt32 gPromoteNoopToCheckCount = 0;
00127 static const PRUint32 kFlagChangesBeforeCheck = 10;
00128 static const PRInt32 kMaxSecondsBeforeCheck = 600;
00129 
00130 NS_IMPL_THREADSAFE_ISUPPORTS1(nsMsgImapHdrXferInfo, nsIImapHeaderXferInfo)
00131 
00132 static const PRInt32 kNumHdrsToXfer=10;
00133 
00134 nsMsgImapHdrXferInfo::nsMsgImapHdrXferInfo()
00135 {
00136   NS_NewISupportsArray(getter_AddRefs(m_hdrInfos));
00137   m_nextFreeHdrInfo = 0;
00138 }
00139 
00140 nsMsgImapHdrXferInfo::~nsMsgImapHdrXferInfo()
00141 {
00142 }
00143 
00144 NS_IMETHODIMP nsMsgImapHdrXferInfo::GetNumHeaders(PRInt32 *aNumHeaders)
00145 {
00146   *aNumHeaders = m_nextFreeHdrInfo;
00147   return NS_OK;
00148 }
00149 
00150 NS_IMETHODIMP nsMsgImapHdrXferInfo::GetHeader(PRInt32 hdrIndex, nsIImapHeaderInfo **aResult)
00151 {
00152   if (m_hdrInfos)
00153     return m_hdrInfos->QueryElementAt(hdrIndex, NS_GET_IID(nsIImapHeaderInfo), (void **) aResult);
00154   else
00155     return NS_ERROR_OUT_OF_MEMORY;
00156 }
00157 
00158 static const PRInt32 kInitLineHdrCacheSize = 512; // should be about right
00159 
00160 nsresult nsMsgImapHdrXferInfo::GetFreeHeaderInfo(nsIImapHeaderInfo **aResult)
00161 {
00162   if (m_nextFreeHdrInfo >= kNumHdrsToXfer)
00163   {
00164     *aResult = nsnull;
00165     return NS_ERROR_NULL_POINTER;
00166   }
00167   nsresult rv = m_hdrInfos->QueryElementAt(m_nextFreeHdrInfo++, NS_GET_IID(nsIImapHeaderInfo), (void **) aResult);
00168   if (!*aResult && m_nextFreeHdrInfo - 1 < kNumHdrsToXfer)
00169   {
00170       nsMsgImapLineDownloadCache *lineCache = new nsMsgImapLineDownloadCache();
00171       if (!lineCache)
00172         return NS_ERROR_OUT_OF_MEMORY;
00173       rv = lineCache->GrowBuffer(kInitLineHdrCacheSize);
00174       NS_ADDREF(*aResult = lineCache);
00175       m_hdrInfos->AppendElement(lineCache);
00176   }
00177   return rv;
00178 }
00179 
00180 void nsMsgImapHdrXferInfo::StartNewHdr(nsIImapHeaderInfo **newHdrInfo)
00181 {
00182   GetFreeHeaderInfo(newHdrInfo);
00183 }
00184 
00185 // maybe not needed...
00186 void nsMsgImapHdrXferInfo::FinishCurrentHdr()
00187 {
00188   // nothing to do?
00189 }
00190 
00191 void nsMsgImapHdrXferInfo::ResetAll()
00192 {
00193   nsCOMPtr <nsIImapHeaderInfo> hdrInfo;
00194   for (PRInt32 i = 0; i < kNumHdrsToXfer; i++)
00195   {
00196     nsresult rv = GetHeader(i, getter_AddRefs(hdrInfo));
00197     if (NS_SUCCEEDED(rv) && hdrInfo)
00198       hdrInfo->ResetCache();
00199   }
00200   m_nextFreeHdrInfo = 0;
00201 }
00202 
00203 void nsMsgImapHdrXferInfo::ReleaseAll()
00204 {
00205   m_hdrInfos->Clear();
00206   m_nextFreeHdrInfo = 0;
00207 }
00208 
00209 NS_IMPL_THREADSAFE_ISUPPORTS1(nsMsgImapLineDownloadCache, nsIImapHeaderInfo)
00210 
00211 // **** helper class for downloading line ****
00212 nsMsgImapLineDownloadCache::nsMsgImapLineDownloadCache()
00213 {
00214     fLineInfo = (msg_line_info *) PR_CALLOC(sizeof( msg_line_info));
00215     fLineInfo->uidOfMessage = nsMsgKey_None;
00216     m_msgSize = 0;
00217 }
00218 
00219 nsMsgImapLineDownloadCache::~nsMsgImapLineDownloadCache()
00220 {
00221     PR_Free( fLineInfo);
00222 }
00223 
00224 PRUint32 nsMsgImapLineDownloadCache::CurrentUID()
00225 {
00226     return fLineInfo->uidOfMessage;
00227 }
00228 
00229 PRUint32 nsMsgImapLineDownloadCache::SpaceAvailable()
00230 {
00231     return kDownLoadCacheSize - m_bufferPos;
00232 }
00233 
00234 msg_line_info *nsMsgImapLineDownloadCache::GetCurrentLineInfo()
00235 {
00236   AppendBuffer("", 1); // null terminate the buffer
00237   fLineInfo->adoptedMessageLine = GetBuffer();
00238   return fLineInfo;
00239 }
00240     
00241 NS_IMETHODIMP nsMsgImapLineDownloadCache::ResetCache()
00242 {
00243     ResetWritePos();
00244     return NS_OK;
00245 }
00246     
00247 PRBool nsMsgImapLineDownloadCache::CacheEmpty()
00248 {
00249     return m_bufferPos == 0;
00250 }
00251 
00252 NS_IMETHODIMP nsMsgImapLineDownloadCache::CacheLine(const char *line, PRUint32 uid)
00253 {
00254     NS_ASSERTION((PL_strlen(line) + 1) <= SpaceAvailable(), 
00255                  "Oops... line length greater than space available");
00256     
00257     fLineInfo->uidOfMessage = uid;
00258     
00259     AppendString(line);
00260     return NS_OK;
00261 }
00262 
00263 /* attribute nsMsgKey msgUid; */
00264 NS_IMETHODIMP nsMsgImapLineDownloadCache::GetMsgUid(nsMsgKey *aMsgUid)
00265 {
00266     *aMsgUid = fLineInfo->uidOfMessage;
00267     return NS_OK;
00268 }
00269 NS_IMETHODIMP nsMsgImapLineDownloadCache::SetMsgUid(nsMsgKey aMsgUid)
00270 {
00271     fLineInfo->uidOfMessage = aMsgUid;
00272     return NS_OK;
00273 }
00274 
00275 /* attribute long msgSize; */
00276 NS_IMETHODIMP nsMsgImapLineDownloadCache::GetMsgSize(PRInt32 *aMsgSize)
00277 {
00278     *aMsgSize = m_msgSize;
00279     return NS_OK;
00280 }
00281 
00282 NS_IMETHODIMP nsMsgImapLineDownloadCache::SetMsgSize(PRInt32 aMsgSize)
00283 {
00284     m_msgSize = aMsgSize;
00285     return NS_OK;
00286 }
00287 
00288 /* attribute string msgHdrs; */
00289 NS_IMETHODIMP nsMsgImapLineDownloadCache::GetMsgHdrs(const char **aMsgHdrs)
00290 {
00291   // this doesn't copy the string
00292     AppendBuffer("", 1); // null terminate the buffer
00293     *aMsgHdrs = GetBuffer();
00294     return NS_OK;
00295 }
00296 
00297 /* the following macros actually implement addref, release and query interface for our component. */
00298 
00299 NS_IMPL_ADDREF_INHERITED(nsImapProtocol, nsMsgProtocol)
00300 NS_IMPL_RELEASE_INHERITED(nsImapProtocol, nsMsgProtocol )
00301 
00302 NS_INTERFACE_MAP_BEGIN(nsImapProtocol)
00303    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIImapProtocol)
00304    NS_INTERFACE_MAP_ENTRY(nsIRunnable)
00305    NS_INTERFACE_MAP_ENTRY(nsIImapProtocol)
00306    NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
00307    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00308 NS_INTERFACE_MAP_END_THREADSAFE
00309 
00310 static PRInt32 gTooFastTime = 2;
00311 static PRInt32 gIdealTime = 4;
00312 static PRInt32 gChunkAddSize = 2048;
00313 static PRInt32 gChunkSize = 10240;
00314 static PRInt32 gChunkThreshold = 10240 + 4096;
00315 static PRBool gFetchByChunks = PR_TRUE;
00316 static PRInt32 gMaxChunkSize = 40960;
00317 static PRBool gInitialized = PR_FALSE;
00318 static PRBool gHideUnusedNamespaces = PR_TRUE;
00319 static PRBool gHideOtherUsersFromList = PR_FALSE;
00320 static PRBool gUseEnvelopeCmd = PR_FALSE;
00321 static PRBool gUseLiteralPlus = PR_TRUE;
00322 static PRBool gExpungeAfterDelete = PR_FALSE;
00323 static PRBool gCheckDeletedBeforeExpunge = PR_FALSE; //bug 235004
00324 static PRInt32 gResponseTimeout = 60;
00325 static nsCStringArray gCustomDBHeaders;
00326 
00327 nsresult nsImapProtocol::GlobalInitialization()
00328 {
00329     gInitialized = PR_TRUE;
00330     nsresult rv;
00331     nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); 
00332     NS_ENSURE_SUCCESS(rv, rv);
00333 
00334     prefBranch->GetIntPref("mail.imap.chunk_fast", &gTooFastTime);   // secs we read too little too fast
00335     prefBranch->GetIntPref("mail.imap.chunk_ideal", &gIdealTime);    // secs we read enough in good time
00336     prefBranch->GetIntPref("mail.imap.chunk_add", &gChunkAddSize);   // buffer size to add when wasting time
00337     prefBranch->GetIntPref("mail.imap.chunk_size", &gChunkSize);
00338     prefBranch->GetIntPref("mail.imap.min_chunk_size_threshold", &gChunkThreshold);
00339     prefBranch->GetIntPref("mail.imap.max_chunk_size", &gMaxChunkSize);
00340     prefBranch->GetBoolPref("mail.imap.hide_other_users",
00341                             &gHideOtherUsersFromList);
00342     prefBranch->GetBoolPref("mail.imap.hide_unused_namespaces",
00343                             &gHideUnusedNamespaces);
00344     prefBranch->GetIntPref("mail.imap.noop_check_count", &gPromoteNoopToCheckCount);
00345     prefBranch->GetBoolPref("mail.imap.use_envelope_cmd",
00346                             &gUseEnvelopeCmd);
00347     prefBranch->GetBoolPref("mail.imap.use_literal_plus", &gUseLiteralPlus);
00348     prefBranch->GetBoolPref("mail.imap.expunge_after_delete", &gExpungeAfterDelete);
00349     prefBranch->GetBoolPref("mail.imap.check_deleted_before_expunge", &gCheckDeletedBeforeExpunge);
00350     prefBranch->GetIntPref("mailnews.tcptimeout", &gResponseTimeout);
00351     nsXPIDLCString customDBHeaders;
00352     prefBranch->GetCharPref("mailnews.customDBHeaders", getter_Copies(customDBHeaders));
00353     gCustomDBHeaders.ParseString(customDBHeaders, " ");
00354     return NS_OK;
00355 }
00356 
00357 nsImapProtocol::nsImapProtocol() : nsMsgProtocol(nsnull),
00358     m_parser(*this)
00359 {
00360   m_urlInProgress = PR_FALSE;
00361   m_idle = PR_FALSE;
00362   m_retryUrlOnError = PR_FALSE;
00363   m_useIdle = PR_TRUE; // by default, use it
00364   m_ignoreExpunges = PR_FALSE;
00365   m_useSecAuth = PR_FALSE;
00366   m_socketType = nsIMsgIncomingServer::tryTLS;
00367   m_connectionStatus = 0;
00368   m_hostSessionList = nsnull;
00369   m_flagState = nsnull;
00370   m_fetchBodyIdList = nsnull;
00371     
00372   if (!gInitialized)
00373     GlobalInitialization();
00374 
00375   // read in the accept languages preference
00376   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID)); 
00377   if (prefBranch)
00378   {
00379     nsCOMPtr<nsIPrefLocalizedString> prefString;
00380     prefBranch->GetComplexValue("intl.accept_languages",
00381                                 NS_GET_IID(nsIPrefLocalizedString),
00382                                 getter_AddRefs(prefString));
00383     if (prefString)
00384       prefString->ToString(getter_Copies(mAcceptLanguages));
00385   }
00386 
00387     // ***** Thread support *****
00388   m_thread = nsnull;
00389   m_dataAvailableMonitor = nsnull;
00390   m_urlReadyToRunMonitor = nsnull;
00391   m_pseudoInterruptMonitor = nsnull;
00392   m_dataMemberMonitor = nsnull;
00393   m_threadDeathMonitor = nsnull;
00394   m_waitForBodyIdsMonitor = nsnull;
00395   m_fetchMsgListMonitor = nsnull;
00396   m_fetchBodyListMonitor = nsnull;
00397   m_imapThreadIsRunning = PR_FALSE;
00398   m_currentServerCommandTagNumber = 0;
00399   m_active = PR_FALSE;
00400   m_folderNeedsSubscribing = PR_FALSE;
00401   m_folderNeedsACLRefreshed = PR_FALSE;
00402   m_threadShouldDie = PR_FALSE;
00403   m_pseudoInterrupted = PR_FALSE;
00404   m_nextUrlReadyToRun = PR_FALSE;
00405   m_trackingTime = PR_FALSE;
00406   LL_I2L(m_startTime, 0);
00407   LL_I2L(m_endTime, 0);
00408   LL_I2L(m_lastActiveTime, 0);
00409   LL_I2L(m_lastProgressTime, 0);
00410   ResetProgressInfo();
00411 
00412   m_tooFastTime = 0;
00413   m_idealTime = 0;
00414   m_chunkAddSize = 0;
00415   m_chunkStartSize = 0;
00416   m_fetchByChunks = PR_TRUE;
00417   m_chunkSize = 0;
00418   m_chunkThreshold = 0;
00419   m_fromHeaderSeen = PR_FALSE;
00420   m_closeNeededBeforeSelect = PR_FALSE;
00421   m_needNoop = PR_FALSE;
00422   m_noopCount = 0;
00423   m_fetchMsgListIsNew = PR_FALSE;
00424   m_fetchBodyListIsNew = PR_FALSE;
00425   m_flagChangeCount = 0;
00426   m_lastCheckTime = PR_Now();
00427 
00428   m_checkForNewMailDownloadsHeaders = PR_TRUE;  // this should be on by default
00429   m_hierarchyNameState = kNoOperationInProgress;
00430   m_discoveryStatus = eContinue;
00431 
00432   m_overRideUrlConnectionInfo = PR_FALSE;
00433   // m_dataOutputBuf is used by Send Data
00434   m_dataOutputBuf = (char *) PR_CALLOC(sizeof(char) * OUTPUT_BUFFER_SIZE);
00435   m_allocatedSize = OUTPUT_BUFFER_SIZE;
00436 
00437   // used to buffer incoming data by ReadNextLine
00438   m_inputStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, PR_TRUE /* allocate new lines */, PR_FALSE /* leave CRLFs on the returned string */);
00439   m_currentBiffState = nsIMsgFolder::nsMsgBiffState_Unknown;
00440 
00441   m_hostName.Truncate();
00442   m_userName = nsnull;
00443   m_serverKey = nsnull;
00444 
00445   m_progressStringId = 0;
00446 
00447   // since these are embedded in the nsImapProtocol object, but passed
00448   // through proxied xpcom methods, just AddRef them here.
00449   m_hdrDownloadCache.AddRef();
00450   m_downloadLineCache.AddRef();
00451 
00452   // subscription
00453   m_autoSubscribe = PR_TRUE;
00454   m_autoUnsubscribe = PR_TRUE;
00455   m_autoSubscribeOnOpen = PR_TRUE;
00456   m_deletableChildren = nsnull;
00457 
00458   Configure(gTooFastTime, gIdealTime, gChunkAddSize, gChunkSize,
00459                     gChunkThreshold, gFetchByChunks, gMaxChunkSize);
00460 
00461   // where should we do this? Perhaps in the factory object?
00462   if (!IMAP)
00463     IMAP = PR_NewLogModule("IMAP");
00464 }
00465 
00466 nsresult nsImapProtocol::Configure(PRInt32 TooFastTime, PRInt32 IdealTime,
00467                   PRInt32 ChunkAddSize, PRInt32 ChunkSize, PRInt32 ChunkThreshold,
00468                   PRBool FetchByChunks, PRInt32 /* MaxChunkSize */)
00469 {
00470   m_tooFastTime = TooFastTime;    // secs we read too little too fast
00471   m_idealTime = IdealTime;    // secs we read enough in good time
00472   m_chunkAddSize = ChunkAddSize;    // buffer size to add when wasting time
00473   m_chunkStartSize = m_chunkSize = ChunkSize;
00474   m_chunkThreshold = ChunkThreshold;
00475   m_fetchByChunks = FetchByChunks;
00476 
00477   return NS_OK;
00478 }
00479 
00480 
00481 nsresult nsImapProtocol::Initialize(nsIImapHostSessionList * aHostSessionList, nsIImapIncomingServer *aServer, 
00482                                     nsIEventQueue * aSinkEventQueue)
00483 {
00484   NS_PRECONDITION(aSinkEventQueue && aHostSessionList, 
00485              "oops...trying to initalize with a null sink event queue!");
00486   if (!aSinkEventQueue || !aHostSessionList || !aServer)
00487         return NS_ERROR_NULL_POINTER;
00488 
00489    nsresult rv = m_downloadLineCache.GrowBuffer(kDownLoadCacheSize);
00490    NS_ENSURE_SUCCESS(rv, rv);
00491 
00492    m_flagState = new nsImapFlagAndUidState(kImapFlagAndUidStateSize, PR_FALSE);
00493    if (!m_flagState)
00494      return NS_ERROR_OUT_OF_MEMORY;
00495 
00496    aServer->GetUseIdle(&m_useIdle);
00497    NS_ADDREF(m_flagState);
00498 
00499     m_sinkEventQueue = aSinkEventQueue;
00500     m_hostSessionList = aHostSessionList; // no ref count...host session list has life time > connection
00501     m_parser.SetHostSessionList(aHostSessionList);
00502     m_parser.SetFlagState(m_flagState);
00503 
00504   // Now initialize the thread for the connection and create appropriate monitors
00505   if (m_thread == nsnull)
00506   {
00507     m_dataAvailableMonitor = PR_NewMonitor();
00508     m_urlReadyToRunMonitor = PR_NewMonitor();
00509     m_pseudoInterruptMonitor = PR_NewMonitor();
00510     m_dataMemberMonitor = PR_NewMonitor();
00511     m_threadDeathMonitor = PR_NewMonitor();
00512     m_waitForBodyIdsMonitor = PR_NewMonitor();
00513     m_fetchMsgListMonitor = PR_NewMonitor();
00514     m_fetchBodyListMonitor = PR_NewMonitor();
00515 
00516     nsresult rv = NS_NewThread(getter_AddRefs(m_iThread), this);
00517     if (NS_FAILED(rv)) 
00518     {
00519       NS_ASSERTION(m_iThread, "Unable to create imap thread.\n");
00520       return rv;
00521     }
00522     m_iThread->GetPRThread(&m_thread);
00523 
00524   }
00525   return NS_OK;
00526 }
00527 
00528 nsImapProtocol::~nsImapProtocol()
00529 {
00530   PR_Free(m_userName);
00531   PR_Free(m_serverKey);
00532   PR_Free(m_fetchBodyIdList);
00533 
00534   NS_IF_RELEASE(m_flagState);
00535 
00536   PR_Free(m_dataOutputBuf);
00537   delete m_inputStreamBuffer;
00538 
00539   // **** We must be out of the thread main loop function
00540   NS_ASSERTION(m_imapThreadIsRunning == PR_FALSE, "Oops, thread is still running.\n");
00541 
00542   if (m_dataAvailableMonitor)
00543   {
00544     PR_DestroyMonitor(m_dataAvailableMonitor);
00545     m_dataAvailableMonitor = nsnull;
00546   }
00547 
00548   if (m_urlReadyToRunMonitor)
00549   {
00550     PR_DestroyMonitor(m_urlReadyToRunMonitor);
00551     m_urlReadyToRunMonitor = nsnull;
00552   }
00553   if (m_pseudoInterruptMonitor)
00554   {
00555     PR_DestroyMonitor(m_pseudoInterruptMonitor);
00556     m_pseudoInterruptMonitor = nsnull;
00557   }
00558   if (m_dataMemberMonitor)
00559   {
00560     PR_DestroyMonitor(m_dataMemberMonitor);
00561     m_dataMemberMonitor = nsnull;
00562   }
00563   if (m_threadDeathMonitor)
00564   {
00565     PR_DestroyMonitor(m_threadDeathMonitor);
00566     m_threadDeathMonitor = nsnull;
00567   }
00568   if (m_waitForBodyIdsMonitor)
00569   {
00570     PR_DestroyMonitor(m_waitForBodyIdsMonitor);
00571     m_waitForBodyIdsMonitor = nsnull;
00572   }
00573   if (m_fetchMsgListMonitor)
00574   {
00575     PR_DestroyMonitor(m_fetchMsgListMonitor);
00576     m_fetchMsgListMonitor = nsnull;
00577   }
00578   if (m_fetchBodyListMonitor)
00579   {
00580     PR_DestroyMonitor(m_fetchBodyListMonitor);
00581     m_fetchBodyListMonitor = nsnull;
00582   }
00583 }
00584 
00585 const char*
00586 nsImapProtocol::GetImapHostName()
00587 {
00588   if (m_runningUrl && m_hostName.IsEmpty())
00589   {
00590     nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningUrl);
00591     url->GetAsciiHost(m_hostName);
00592   }
00593 
00594   return m_hostName.get();
00595 }
00596 
00597 const char*
00598 nsImapProtocol::GetImapUserName()
00599 {
00600   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(m_server);
00601   if (!m_userName && server)
00602     server->GetUsername(&m_userName);
00603   return m_userName;
00604 }
00605 
00606 const char*
00607 nsImapProtocol::GetImapServerKey()
00608 {
00609   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(m_server);
00610   if (!m_serverKey && server)
00611     server->GetKey(&m_serverKey);
00612   return m_serverKey;
00613 }
00614 
00615 void
00616 nsImapProtocol::SetupSinkProxy()
00617 {
00618   nsresult res = NS_ERROR_FAILURE;
00619 
00620   if (m_runningUrl)
00621   {
00622     NS_ASSERTION(m_sinkEventQueue && m_thread, "fatal... null sink event queue or thread");
00623 
00624     nsCOMPtr<nsIProxyObjectManager> proxyManager(do_GetService(kProxyObjectManagerCID, &res));
00625     if (proxyManager) // if we don't get one of these are as good as dead...
00626     {
00627       if (!m_imapMailFolderSink)
00628       {
00629         nsCOMPtr<nsIImapMailFolderSink> aImapMailFolderSink;
00630         res = m_runningUrl->GetImapMailFolderSink(getter_AddRefs(aImapMailFolderSink));
00631         if (NS_SUCCEEDED(res) && aImapMailFolderSink)
00632           res = proxyManager->GetProxyForObject(m_sinkEventQueue,
00633                                              NS_GET_IID(nsIImapMailFolderSink),
00634                                              aImapMailFolderSink,
00635                                              PROXY_SYNC | PROXY_ALWAYS,
00636                                              getter_AddRefs(m_imapMailFolderSink));
00637       }
00638       
00639       if (!m_imapMessageSink)
00640       {
00641         nsCOMPtr<nsIImapMessageSink> aImapMessageSink;
00642         res = m_runningUrl->GetImapMessageSink(getter_AddRefs(aImapMessageSink));
00643         if (NS_SUCCEEDED(res) && aImapMessageSink)
00644           res = proxyManager->GetProxyForObject(m_sinkEventQueue,
00645                                              NS_GET_IID(nsIImapMessageSink),
00646                                              aImapMessageSink,
00647                                              PROXY_SYNC | PROXY_ALWAYS,
00648                                              getter_AddRefs(m_imapMessageSink));
00649       }
00650       if (!m_imapServerSink)
00651       {
00652          nsCOMPtr<nsIImapServerSink> aImapServerSink;
00653          res = m_runningUrl->GetImapServerSink(getter_AddRefs(aImapServerSink));
00654          if (NS_SUCCEEDED(res) && aImapServerSink)
00655             res = proxyManager->GetProxyForObject(  m_sinkEventQueue,
00656                              NS_GET_IID(nsIImapServerSink),
00657                              aImapServerSink,
00658                              PROXY_SYNC | PROXY_ALWAYS,
00659                              getter_AddRefs(m_imapServerSink));
00660         NS_ASSERTION(NS_SUCCEEDED(res), "couldn't get proxies");
00661       }
00662     }
00663     else
00664       NS_ASSERTION(PR_FALSE, "can't get proxy service");
00665   }
00666   NS_ASSERTION(NS_SUCCEEDED(res), "couldn't get proxies");
00667 }
00668 
00669 static void SetSecurityCallbacksFromChannel(nsISocketTransport* aTrans, nsIChannel* aChannel)
00670 {
00671   nsCOMPtr<nsIInterfaceRequestor> callbacks;
00672   aChannel->GetNotificationCallbacks(getter_AddRefs(callbacks));
00673 
00674   nsCOMPtr<nsILoadGroup> loadGroup;
00675   aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
00676 
00677   nsCOMPtr<nsIInterfaceRequestor> securityCallbacks;
00678   NS_NewNotificationCallbacksAggregation(callbacks, loadGroup,
00679                                          getter_AddRefs(securityCallbacks));
00680   if (securityCallbacks)
00681     aTrans->SetSecurityCallbacks(securityCallbacks);
00682 }
00683 
00684 // Setup With Url is intended to set up data which is held on a PER URL basis and not
00685 // a per connection basis. If you have data which is independent of the url we are currently
00686 // running, then you should put it in Initialize(). 
00687 nsresult nsImapProtocol::SetupWithUrl(nsIURI * aURL, nsISupports* aConsumer)
00688 {
00689   nsresult rv = NS_ERROR_FAILURE;
00690   NS_PRECONDITION(aURL, "null URL passed into Imap Protocol");
00691   if (aURL)
00692   {
00693     rv = aURL->QueryInterface(NS_GET_IID(nsIImapUrl), getter_AddRefs(m_runningUrl));
00694     if (NS_FAILED(rv)) return rv;
00695 
00696     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(m_server);
00697     if (!server)
00698     {
00699         nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
00700         rv = mailnewsUrl->GetServer(getter_AddRefs(server));
00701         m_server = do_GetWeakReference(server);
00702     }
00703     nsCOMPtr<nsIImapIncomingServer> imapServer = do_QueryInterface(server);
00704 
00705     nsCOMPtr<nsIStreamListener> aRealStreamListener = do_QueryInterface(aConsumer);
00706     m_runningUrl->GetMockChannel(getter_AddRefs(m_mockChannel));
00707     if (m_mockChannel)
00708     {   
00709       m_mockChannel->SetImapProtocol(this);
00710       // if we have a listener from a mock channel, over-ride the consumer that was passed in
00711       nsCOMPtr<nsIStreamListener> channelListener;
00712       m_mockChannel->GetChannelListener(getter_AddRefs(channelListener));
00713       if (channelListener) // only over-ride if we have a non null channel listener
00714         aRealStreamListener = channelListener;
00715       m_mockChannel->GetChannelContext(getter_AddRefs(m_channelContext));
00716     }
00717 
00718     // since we'll be making calls directly from the imap thread to the channel listener,
00719     // we need to turn it into a proxy object....we'll assume that the listener is on the same thread
00720     // as the event sink queue
00721     if (aRealStreamListener)
00722     {
00723       NS_ASSERTION(!m_channelListener, "shouldn't already have a channel listener");
00724         rv = NS_NewAsyncStreamListener(getter_AddRefs(m_channelListener), aRealStreamListener, m_sinkEventQueue);
00725     }
00726 
00727     PRUint32 capability = kCapabilityUndefined;
00728 
00729     m_hostSessionList->GetCapabilityForHost(GetImapServerKey(), capability);
00730 
00731     PRBool shuttingDown;
00732     (void) server->GetUseSecAuth(&m_useSecAuth);
00733     (void) server->GetSocketType(&m_socketType);
00734     (void) imapServer->GetShuttingDown(&shuttingDown);
00735     if (!shuttingDown)
00736       (void) imapServer->GetUseIdle(&m_useIdle);
00737     else
00738       m_useIdle = PR_FALSE;
00739     if (imapServer)
00740     {
00741       nsXPIDLCString redirectorType;
00742       imapServer->GetRedirectorType(getter_Copies(redirectorType));
00743       m_ignoreExpunges = redirectorType.Equals("aol");
00744       imapServer->GetFetchByChunks(&m_fetchByChunks);
00745     }
00746 
00747     if ( m_runningUrl && !m_transport /* and we don't have a transport yet */)
00748     {
00749       // extract the file name and create a file transport...
00750       PRInt32 port=-1;
00751       server->GetPort(&port);
00752 
00753       if (port <= 0)
00754       {
00755         PRInt32 socketType;
00756         // Be a bit smarter about setting the default port
00757         port = (NS_SUCCEEDED(server->GetSocketType(&socketType)) 
00758                   && socketType == nsIMsgIncomingServer::useSSL)
00759            ? SECURE_IMAP_PORT :IMAP_PORT;
00760       }
00761       
00762       nsXPIDLCString hostName;
00763             
00764       nsCOMPtr<nsISocketTransportService> socketService = 
00765                do_GetService(kSocketTransportServiceCID, &rv);
00766       if (NS_SUCCEEDED(rv) && aURL)
00767       {
00768         aURL->GetPort(&port);
00769         server->GetRealHostName(getter_Copies(hostName));
00770 
00771         Log("SetupWithUrl", nsnull, "clearing IMAP_CONNECTION_IS_OPEN");
00772         ClearFlag(IMAP_CONNECTION_IS_OPEN); 
00773         const char *connectionType = nsnull;
00774         
00775         if (m_socketType == nsIMsgIncomingServer::useSSL) 
00776           connectionType = "ssl";
00777         else if ((m_socketType == nsIMsgIncomingServer::tryTLS && (capability & kHasStartTLSCapability))
00778           || m_socketType == nsIMsgIncomingServer::alwaysUseTLS)
00779           connectionType = "starttls";
00780 
00781         nsCOMPtr<nsIProxyInfo> proxyInfo;
00782         rv = NS_ExamineForProxy("imap", hostName.get(), port, getter_AddRefs(proxyInfo));
00783         if (NS_FAILED(rv)) proxyInfo = nsnull;
00784 
00785         const nsACString *socketHost;
00786         PRUint16 socketPort;
00787 
00788         if (m_overRideUrlConnectionInfo)
00789         {
00790           socketHost = &m_logonHost;
00791           socketPort = m_logonPort;
00792         }
00793         else
00794         {
00795           socketHost = &hostName;
00796           socketPort = port;
00797         }
00798         rv = socketService->CreateTransport(&connectionType, connectionType != nsnull,
00799                                             *socketHost, socketPort, proxyInfo,
00800                                             getter_AddRefs(m_transport));
00801         if (NS_FAILED(rv) && m_socketType == nsIMsgIncomingServer::tryTLS)
00802         {
00803           connectionType = nsnull;
00804           m_socketType = nsIMsgIncomingServer::defaultSocket;
00805           rv = socketService->CreateTransport(&connectionType, connectionType != nsnull,
00806                                               *socketHost, socketPort, proxyInfo,
00807                                               getter_AddRefs(m_transport));
00808         }
00809         // remember so we can know whether we can issue a start tls or not...
00810         m_connectionType = connectionType;
00811         if (m_transport && m_mockChannel)
00812         {
00813           // Ensure that the socket can get the notification callbacks
00814           SetSecurityCallbacksFromChannel(m_transport, m_mockChannel);
00815 
00816           // open buffered, blocking input stream
00817           rv = m_transport->OpenInputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(m_inputStream));
00818           if (NS_FAILED(rv)) return rv;
00819 
00820           // open buffered, blocking output stream
00821           rv = m_transport->OpenOutputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(m_outputStream));
00822           if (NS_FAILED(rv)) return rv;
00823           SetFlag(IMAP_CONNECTION_IS_OPEN);
00824         }
00825       }
00826     } // if m_runningUrl
00827 
00828     if (m_transport && m_mockChannel)
00829     {
00830       m_transport->SetTimeout(nsISocketTransport::TIMEOUT_CONNECT, gResponseTimeout + 60);
00831       m_transport->SetTimeout(nsISocketTransport::TIMEOUT_READ_WRITE, gResponseTimeout);
00832       // set the security info for the mock channel to be the security status for our underlying transport.
00833       nsCOMPtr<nsISupports> securityInfo;
00834       m_transport->GetSecurityInfo(getter_AddRefs(securityInfo));
00835       m_mockChannel->SetSecurityInfo(securityInfo);
00836     
00837       SetSecurityCallbacksFromChannel(m_transport, m_mockChannel);
00838 
00839       nsCOMPtr<nsITransportEventSink> sink = do_QueryInterface(m_mockChannel);
00840       if (sink) {
00841         nsCOMPtr<nsIEventQueue> eventQ;
00842         NS_GetMainEventQ(getter_AddRefs(eventQ));
00843         m_transport->SetEventSink(sink, eventQ);
00844       }
00845 
00846       // and if we have a cache entry that we are saving the message to, set the security info on it too.
00847       // since imap only uses the memory cache, passing this on is the right thing to do.
00848       nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
00849       if (mailnewsUrl)
00850       {
00851         nsCOMPtr<nsICacheEntryDescriptor> cacheEntry;
00852         mailnewsUrl->GetMemCacheEntry(getter_AddRefs(cacheEntry));
00853         if (cacheEntry)
00854           cacheEntry->SetSecurityInfo(securityInfo);
00855       }
00856     }
00857   } // if aUR
00858     
00859   return rv;
00860 }
00861 
00862 
00863 // when the connection is done processing the current state, free any per url state data...
00864 void nsImapProtocol::ReleaseUrlState(PRBool rerunning)
00865 {
00866   // clear out the socket's reference to the notification callbacks for this transaction
00867   if (m_transport)
00868   {
00869     nsAutoCMonitor mon (this);
00870     m_transport->SetSecurityCallbacks(nsnull);
00871     m_transport->SetEventSink(nsnull, nsnull);
00872   }
00873 
00874   if (m_mockChannel && !rerunning)
00875   {
00876     if (m_imapMailFolderSink)
00877       m_imapMailFolderSink->CloseMockChannel(m_mockChannel);
00878     else
00879       m_mockChannel->Close();
00880      m_mockChannel = nsnull;
00881   }
00882   m_channelContext = nsnull; // this might be the url - null it out before the final release of the url
00883   m_imapMessageSink = nsnull;
00884   m_channelListener = nsnull;
00885   
00886   m_channelInputStream = nsnull;
00887   m_channelOutputStream = nsnull;
00888   if (m_runningUrl)
00889   {
00890     nsCOMPtr<nsIMsgMailNewsUrl>  mailnewsurl = do_QueryInterface(m_runningUrl);
00891     if (m_imapServerSink && !rerunning)  
00892       m_imapServerSink->RemoveChannelFromUrl(mailnewsurl, NS_OK);
00893 
00894     {
00895       nsCOMPtr <nsIImapMailFolderSink> saveFolderSink = m_imapMailFolderSink;
00896       {
00897         nsAutoCMonitor mon (this);
00898         m_runningUrl = nsnull; // force us to release our last reference on the url
00899         m_imapMailFolderSink = nsnull;
00900         m_urlInProgress = PR_FALSE;
00901       }
00902 
00903       // we want to make sure the imap protocol's last reference to the url gets released
00904       // back on the UI thread. This ensures that the objects the imap url hangs on to
00905       // properly get released back on the UI thread. In order to do this, we need a
00906       // a fancy texas two step where we first give the ui thread the url we want to
00907       // release, then we forget about our copy. Then we tell it to release the url
00908       // for real.
00909       if (saveFolderSink)
00910       {
00911         nsCOMPtr <nsISupports> supports = do_QueryInterface(mailnewsurl);
00912         saveFolderSink->PrepareToReleaseObject(supports);
00913         supports = nsnull;
00914         mailnewsurl = nsnull;
00915         // at this point in time, we MUST have released all of our references to 
00916         // the url from the imap protocol. otherwise this whole exercise is moot.
00917         saveFolderSink->ReleaseObject();
00918         saveFolderSink = nsnull;
00919       }
00920     }
00921   }
00922   else
00923     m_imapMailFolderSink = nsnull;
00924 
00925 }
00926 
00927 
00928 NS_IMETHODIMP nsImapProtocol::Run()
00929 {
00930   nsImapProtocol *me = this;
00931   NS_ASSERTION(me, "Yuk, me is null.\n");
00932     
00933   PR_CEnterMonitor(this);
00934   NS_ASSERTION(me->m_imapThreadIsRunning == PR_FALSE, 
00935                  "Oh. oh. thread is already running. What's wrong here?");
00936     if (me->m_imapThreadIsRunning)
00937     {
00938         PR_CExitMonitor(me);
00939         return NS_OK;
00940     }
00941 
00942   me->m_imapThreadIsRunning = PR_TRUE;
00943   PR_CExitMonitor(me);
00944 
00945   // call the platform specific main loop ....
00946   me->ImapThreadMainLoop();
00947 
00948       
00949   me->m_runningUrl = nsnull;
00950   CloseStreams();
00951   me->m_sinkEventQueue = nsnull;
00952   me->m_imapMailFolderSink = nsnull;
00953   me->m_imapMessageSink = nsnull;
00954   m_iThread = nsnull;
00955 
00956   return NS_OK;
00957 }
00958 
00959 // called from UI thread.
00960 void nsImapProtocol::CloseStreams()
00961 {
00962   PR_CEnterMonitor(this);
00963   if (m_transport)
00964   {
00965       // make sure the transport closes (even if someone is still indirectly
00966       // referencing it).
00967       m_transport->Close(NS_ERROR_ABORT);
00968       m_transport = nsnull;
00969   }
00970   m_inputStream = nsnull;
00971   m_outputStream = nsnull;
00972   m_channelListener = nsnull;
00973   m_channelContext = nsnull;
00974   if (m_mockChannel)
00975   {
00976       m_mockChannel->Close();
00977       m_mockChannel = nsnull;
00978   }
00979   m_channelInputStream = nsnull;
00980   m_channelOutputStream = nsnull;
00981   nsCOMPtr<nsIMsgIncomingServer> me_server = do_QueryReferent(m_server);
00982 
00983   // we must let go of the monitor before calling RemoveConnection to unblock
00984   // anyone who tries to get a monitor to the protocol object while
00985   // holding onto a monitor to the server.
00986   PR_CExitMonitor(this);
00987 
00988   if (me_server)
00989   {
00990       nsresult result;
00991       nsCOMPtr<nsIImapIncomingServer>
00992           aImapServer(do_QueryInterface(me_server, &result));
00993       if (NS_SUCCEEDED(result))
00994           aImapServer->RemoveConnection(this);
00995       me_server = nsnull;
00996   }
00997   m_server = nsnull;
00998 }
00999 
01000 
01001 NS_IMETHODIMP nsImapProtocol::OnInputStreamReady(nsIAsyncInputStream *inStr)
01002 {
01003   // should we check if it's a close vs. data available?
01004   if (m_idle)
01005   {
01006     PRUint32 bytesAvailable = 0;
01007     (void) inStr->Available(&bytesAvailable);
01008     // check if data available - might be a close
01009     if (bytesAvailable != 0)
01010     {
01011       PR_EnterMonitor(m_urlReadyToRunMonitor);
01012       m_lastActiveTime = PR_Now();
01013       m_nextUrlReadyToRun = PR_TRUE;
01014       PR_Notify(m_urlReadyToRunMonitor);
01015       PR_ExitMonitor(m_urlReadyToRunMonitor);
01016     }
01017   }
01018   return NS_OK;
01019 }
01020 
01021 
01022 NS_IMETHODIMP
01023 nsImapProtocol::TellThreadToDie(PRBool isSafeToClose)
01024 {
01025   nsresult rv = NS_OK;
01026   // ** This routine is called from the ui thread and the imap protocol thread.
01027   // The UI thread always passes in FALSE for isSafeToClose.
01028   {
01029     nsAutoCMonitor mon(this);
01030 
01031     m_urlInProgress = PR_TRUE;  // let's say it's busy so no one tries to use
01032                                 // this about to die connection.
01033     PRBool urlWritingData = PR_FALSE;
01034     PRBool connectionIdle = !m_runningUrl;
01035 
01036     if (!connectionIdle)
01037       urlWritingData = m_imapAction == nsIImapUrl::nsImapAppendMsgFromFile 
01038         || m_imapAction == nsIImapUrl::nsImapAppendDraftFromFile;
01039 
01040     PRBool closeNeeded = GetServerStateParser().GetIMAPstate() ==
01041                   nsImapServerResponseParser::kFolderSelected && isSafeToClose;
01042     nsCString command;
01043 
01044     // if a url is writing data, we can't even logout, so we're just
01045     // going to close the connection as if the user pressed stop.
01046     if (m_currentServerCommandTagNumber > 0 && !urlWritingData)
01047     {
01048       PRBool isAlive = PR_FALSE;
01049       if (m_transport)
01050         rv = m_transport->IsAlive(&isAlive);
01051 
01052       if (TestFlag(IMAP_CONNECTION_IS_OPEN) && m_idle && isAlive)
01053         EndIdle(PR_FALSE);
01054 
01055       if (NS_SUCCEEDED(rv) && isAlive && closeNeeded && GetDeleteIsMoveToTrash() &&
01056           TestFlag(IMAP_CONNECTION_IS_OPEN) && m_outputStream)
01057         Close(PR_TRUE, connectionIdle);
01058 
01059       if (NS_SUCCEEDED(rv) && isAlive && TestFlag(IMAP_CONNECTION_IS_OPEN) && m_outputStream)
01060         Logout(PR_TRUE, connectionIdle);
01061     }
01062   }
01063   CloseStreams(); 
01064   Log("TellThreadToDie", nsnull, "close socket connection");
01065 
01066   PR_EnterMonitor(m_threadDeathMonitor);
01067   m_threadShouldDie = PR_TRUE;
01068   PR_ExitMonitor(m_threadDeathMonitor);
01069 
01070   PR_EnterMonitor(m_dataAvailableMonitor);
01071   PR_Notify(m_dataAvailableMonitor);
01072   PR_ExitMonitor(m_dataAvailableMonitor);
01073 
01074   PR_EnterMonitor(m_urlReadyToRunMonitor);
01075   PR_NotifyAll(m_urlReadyToRunMonitor);
01076   PR_ExitMonitor(m_urlReadyToRunMonitor);
01077   return rv;
01078 }
01079 
01080 NS_IMETHODIMP
01081 nsImapProtocol::GetLastActiveTimeStamp(PRTime* aTimeStamp)
01082 {
01083   if (aTimeStamp)
01084       *aTimeStamp = m_lastActiveTime;
01085   return NS_OK;
01086 }
01087 
01088 NS_IMETHODIMP
01089 nsImapProtocol::PseudoInterruptMsgLoad(nsIMsgFolder *aImapFolder, nsIMsgWindow *aMsgWindow, PRBool *interrupted)
01090 {
01091   NS_ENSURE_ARG (interrupted);
01092 
01093   *interrupted = PR_FALSE;
01094 
01095   nsAutoCMonitor mon(this);
01096 
01097   if (m_runningUrl && !TestFlag(IMAP_CLEAN_UP_URL_STATE))
01098   {
01099     nsImapAction imapAction;
01100     m_runningUrl->GetImapAction(&imapAction);
01101 
01102     if (imapAction == nsIImapUrl::nsImapMsgFetch)
01103     {
01104       nsresult rv = NS_OK;
01105       nsCOMPtr<nsIImapUrl> runningImapURL;
01106 
01107       rv = GetRunningImapURL(getter_AddRefs(runningImapURL));
01108       if (NS_SUCCEEDED(rv) && runningImapURL)
01109       {
01110         nsCOMPtr <nsIMsgFolder> runningImapFolder;
01111         nsCOMPtr <nsIMsgWindow> msgWindow;
01112         nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(runningImapURL);
01113         mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
01114         mailnewsUrl->GetFolder(getter_AddRefs(runningImapFolder));
01115         if (aImapFolder == runningImapFolder && msgWindow == aMsgWindow)
01116         {
01117           PseudoInterrupt(PR_TRUE);
01118           *interrupted = PR_TRUE;
01119         }
01120       }
01121     }
01122   }
01123 #ifdef DEBUG_bienvenu
01124   printf("interrupt msg load : %s\n", (*interrupted) ? "TRUE" : "FALSE");
01125 #endif
01126   return NS_OK;
01127 }
01128 
01129 void
01130 nsImapProtocol::ImapThreadMainLoop()
01131 {
01132   PR_LOG(IMAP, PR_LOG_DEBUG, ("ImapThreadMainLoop entering [this=%x]\n", this));
01133 
01134   PRIntervalTime sleepTime = kImapSleepTime;
01135   while (!DeathSignalReceived())
01136   {
01137     nsresult rv = NS_OK;
01138     PRBool readyToRun;
01139 
01140     // wait for an URL to process...
01141     {
01142       nsAutoMonitor mon(m_urlReadyToRunMonitor);
01143 
01144       while (NS_SUCCEEDED(rv) && !DeathSignalReceived() && !m_nextUrlReadyToRun)
01145         rv = mon.Wait(sleepTime);
01146 
01147       readyToRun = m_nextUrlReadyToRun;
01148       m_nextUrlReadyToRun = PR_FALSE;
01149     }
01150 
01151     if (NS_FAILED(rv) && PR_PENDING_INTERRUPT_ERROR == PR_GetError()) 
01152     {
01153       printf("error waiting for monitor\n");
01154       break;
01155     }
01156 
01157     if (readyToRun && m_runningUrl)
01158     {
01159       //
01160       // NOTE: Though we cleared m_nextUrlReadyToRun above, it may have been
01161       //       set by LoadImapUrl, which runs on the main thread.  Because of this,
01162       //       we must not try to clear m_nextUrlReadyToRun here.
01163       //
01164       if (ProcessCurrentURL())
01165       {
01166         m_nextUrlReadyToRun = PR_TRUE;
01167         m_imapMailFolderSink = nsnull;
01168       }
01169       else
01170       {
01171         // see if we want to go into idle mode. Might want to check a pref here too.
01172         if (m_useIdle && !m_urlInProgress && GetServerStateParser().GetCapabilityFlag() & kHasIdleCapability
01173           && GetServerStateParser().GetIMAPstate() 
01174                 == nsImapServerResponseParser::kFolderSelected)
01175         {
01176           Idle(); // for now, lets just do it. We'll probably want to use a timer
01177         }
01178         else // if not idle, don't need to remember folder sink
01179           m_imapMailFolderSink = nsnull;
01180       }
01181     }
01182     else if (m_idle)
01183     {
01184       HandleIdleResponses();
01185     }
01186     if (!GetServerStateParser().Connected())
01187       break;
01188 #ifdef DEBUG_bienvenu
01189     else
01190       printf("ready to run but no url and not idle\n");
01191 #endif
01192   }
01193   m_imapThreadIsRunning = PR_FALSE;
01194 
01195   PR_LOG(IMAP, PR_LOG_DEBUG, ("ImapThreadMainLoop leaving [this=%x]\n", this));
01196 }
01197 
01198 void nsImapProtocol::HandleIdleResponses()
01199 {
01200   // PRInt32 oldRecent = GetServerStateParser().NumberOfRecentMessages();
01201   nsCAutoString commandBuffer(GetServerCommandTag());
01202   commandBuffer.Append(" IDLE"CRLF);
01203 
01204   do
01205   {
01206     ParseIMAPandCheckForNewMail(commandBuffer.get());
01207   }
01208   while (m_inputStreamBuffer->NextLineAvailable() && GetServerStateParser().Connected());
01209 
01210   //  if (oldRecent != GetServerStateParser().NumberOfRecentMessages())
01211   //  We might check that something actually changed, but for now we can
01212   // just assume it. OnNewIdleMessages must run a url, so that
01213   // we'll go back into asyncwait mode.
01214   if (GetServerStateParser().Connected() && m_imapMailFolderSink)
01215     m_imapMailFolderSink->OnNewIdleMessages();
01216 }
01217 
01218 void nsImapProtocol::EstablishServerConnection()
01219 {
01220   char * serverResponse = CreateNewLineFromSocket(); // read in the greeting
01221 
01222   // record the fact that we've received a greeting for this connection so we don't ever
01223   // try to do it again..
01224   if (serverResponse)
01225     SetFlag(IMAP_RECEIVED_GREETING);
01226 
01227   if (!nsCRT::strncasecmp(serverResponse, "* OK", 4))
01228   {
01229     SetConnectionStatus(0);
01230   }
01231   else if (!nsCRT::strncasecmp(serverResponse, "* PREAUTH", 9))
01232   {
01233     // we've been pre-authenticated.
01234     // we can skip the whole password step, right into the
01235     // kAuthenticated state
01236     GetServerStateParser().PreauthSetAuthenticatedState();
01237 
01238     if (GetServerStateParser().GetCapabilityFlag() == kCapabilityUndefined)
01239       Capability();
01240 
01241     if ( !(GetServerStateParser().GetCapabilityFlag() & 
01242           (kIMAP4Capability | kIMAP4rev1Capability | kIMAP4other) ) )
01243     {
01244       // AlertUserEvent_UsingId(MK_MSG_IMAP_SERVER_NOT_IMAP4);
01245       SetConnectionStatus(-1);        // stop netlib
01246     }
01247     else
01248     {
01249       // let's record the user as authenticated.
01250       m_imapServerSink->SetUserAuthenticated(PR_TRUE);
01251 
01252       ProcessAfterAuthenticated();
01253       // the connection was a success
01254       SetConnectionStatus(0);
01255      }
01256   }
01257   
01258   PR_Free(serverResponse); // we don't care about the greeting yet...
01259 }
01260 
01261 // returns PR_TRUE if another url was run, PR_FALSE otherwise.
01262 PRBool nsImapProtocol::ProcessCurrentURL()
01263 {
01264   nsresult rv = NS_OK;
01265   if (m_idle)
01266     EndIdle();
01267 
01268   if (m_retryUrlOnError)
01269   {
01270     // we clear this flag if we're re-running immediately, because that 
01271     // means we never sent a start running url notification, and later we
01272     // don't send start running notification if we think we're rerunning 
01273     // the url (see first call to SetUrlState below). This means we won't
01274     // send a start running notification, which means our stop running
01275     // notification will be ignored because we don't think we were running.
01276     m_runningUrl->SetRerunningUrl(PR_FALSE);
01277     return RetryUrl();
01278   }
01279   Log("ProcessCurrentURL", nsnull, "entering");
01280   (void) GetImapHostName(); // force m_hostName to get set.
01281 
01282 
01283   PRBool  logonFailed = PR_FALSE;
01284   PRBool anotherUrlRun = PR_FALSE;
01285   PRBool rerunningUrl = PR_FALSE;
01286   PRBool isExternalUrl;
01287   PRBool validUrl = PR_TRUE;
01288 
01289   PseudoInterrupt(PR_FALSE);  // clear this if left over from previous url.
01290 
01291   m_runningUrl->GetRerunningUrl(&rerunningUrl);
01292   m_runningUrl->GetExternalLinkUrl(&isExternalUrl);
01293   m_runningUrl->GetValidUrl(&validUrl);
01294   m_runningUrl->GetImapAction(&m_imapAction);
01295 
01296   if (isExternalUrl)
01297   {
01298     if (m_imapAction == nsIImapUrl::nsImapSelectFolder)
01299     {
01300       // we need to send a start request so that the doc loader
01301       // will call HandleContent on the imap service so we
01302       // can abort this url, and run a new url in a new msg window
01303       // to run the folder load url and get off this crazy merry-go-round.
01304       if (m_channelListener) 
01305       {
01306         nsCOMPtr<nsIRequest> request = do_QueryInterface(m_mockChannel);
01307         m_channelListener->OnStartRequest(request, m_channelContext);
01308       }
01309       return PR_FALSE;
01310     }
01311   }
01312 
01313   if (!m_imapMailFolderSink)
01314     SetupSinkProxy(); // try this again. Evil, but I'm desperate.
01315 
01316   // Reinitialize the parser
01317   GetServerStateParser().InitializeState();
01318   GetServerStateParser().SetConnected(PR_TRUE);
01319 
01320   // acknowledge that we are running the url now..
01321   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_runningUrl, &rv);
01322   nsCAutoString urlSpec;
01323   mailnewsurl->GetSpec(urlSpec);
01324   Log("ProcessCurrentURL", urlSpec.get(), (validUrl) ? " = currentUrl\n" : " is not valid\n");    
01325   if (!validUrl)
01326     return PR_FALSE;
01327 
01328   if (NS_SUCCEEDED(rv) && mailnewsurl && m_imapMailFolderSink && !rerunningUrl)
01329     m_imapMailFolderSink->SetUrlState(this, mailnewsurl, PR_TRUE, NS_OK);
01330 
01331   // if we are set up as a channel, we should notify our channel listener that we are starting...
01332   // so pass in ourself as the channel and not the underlying socket or file channel the protocol
01333   // happens to be using
01334   if (m_channelListener) // ### not sure we want to do this if rerunning url...
01335   {
01336     nsCOMPtr<nsIRequest> request = do_QueryInterface(m_mockChannel);
01337     m_channelListener->OnStartRequest(request, m_channelContext);
01338   }
01339   // If we haven't received the greeting yet, we need to make sure we strip
01340   // it out of the input before we start to do useful things...
01341   if (!TestFlag(IMAP_RECEIVED_GREETING))
01342     EstablishServerConnection();
01343 
01344   // Step 1: If we have not moved into the authenticated state yet then do so
01345   // by attempting to logon.
01346   if (!DeathSignalReceived() && (GetConnectionStatus() >= 0) &&
01347             (GetServerStateParser().GetIMAPstate() == 
01348        nsImapServerResponseParser::kNonAuthenticated))
01349   {
01350       /* if we got here, the server's greeting should not have been PREAUTH */
01351       if (GetServerStateParser().GetCapabilityFlag() == kCapabilityUndefined)
01352           Capability();
01353       
01354       if ( !(GetServerStateParser().GetCapabilityFlag() & (kIMAP4Capability | kIMAP4rev1Capability | 
01355              kIMAP4other) ) )
01356       {
01357         AlertUserEventUsingId(IMAP_SERVER_NOT_IMAP4);
01358 
01359         SetConnectionStatus(-1);        // stop netlib
01360       }
01361       else
01362       {
01363         if (m_connectionType.Equals("starttls") 
01364             && (m_socketType == nsIMsgIncomingServer::tryTLS 
01365             && (GetServerStateParser().GetCapabilityFlag() & kHasStartTLSCapability))
01366           || m_socketType == nsIMsgIncomingServer::alwaysUseTLS)
01367         {
01368           StartTLS();
01369           if (GetServerStateParser().LastCommandSuccessful())
01370           {
01371             nsCOMPtr<nsISupports> secInfo;
01372             nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport, &rv);
01373             if (NS_FAILED(rv)) return rv;
01374 
01375             rv = strans->GetSecurityInfo(getter_AddRefs(secInfo));
01376 
01377             if (NS_SUCCEEDED(rv) && secInfo) 
01378             {
01379               nsCOMPtr<nsISSLSocketControl> sslControl = do_QueryInterface(secInfo, &rv);
01380 
01381               if (NS_SUCCEEDED(rv) && sslControl)
01382               {
01383                 rv = sslControl->StartTLS();
01384                 if (NS_SUCCEEDED(rv))
01385                 {
01386                   Capability();
01387                   PRInt32 capabilityFlag = GetServerStateParser().GetCapabilityFlag();
01388                   // Courier imap doesn't return STARTTLS capability if we've done
01389                   // a STARTTLS! But we need to remember this capability so we'll
01390                   // try to use STARTTLS next time.
01391                   if (!(capabilityFlag & kHasStartTLSCapability))
01392                   {
01393                     capabilityFlag |= kHasStartTLSCapability;
01394                     GetServerStateParser().SetCapabilityFlag(capabilityFlag);
01395                     m_hostSessionList->SetCapabilityForHost(GetImapServerKey(), capabilityFlag);
01396                     CommitCapability();
01397                   }
01398                 }
01399               }
01400             }
01401             if (NS_FAILED(rv))
01402             {
01403               nsCAutoString logLine("STARTTLS negotiation failed. Error 0x");
01404               logLine.AppendInt(rv, 16);
01405               Log("ProcessCurrentURL", nsnull, logLine.get());    
01406               if (m_socketType == nsIMsgIncomingServer::alwaysUseTLS)
01407               {
01408                 SetConnectionStatus(-1);        // stop netlib
01409                 m_transport->Close(rv);
01410               }
01411             }
01412           }
01413           else if (m_socketType == nsIMsgIncomingServer::alwaysUseTLS)
01414             return PR_FALSE;
01415         }
01416         // in this case, we didn't know the server supported TLS when
01417         // we created the socket, so we're going to retry with
01418         // STARTTLS.
01419         else if (m_socketType == nsIMsgIncomingServer::tryTLS 
01420             && (GetServerStateParser().GetCapabilityFlag() & kHasStartTLSCapability))
01421         {
01422           return RetryUrl();
01423         }
01424         logonFailed = !TryToLogon();
01425       }
01426   } // if death signal not received
01427 
01428   if (!DeathSignalReceived() && (GetConnectionStatus() >= 0))
01429   {
01430     // if the server supports a language extension then we should
01431     // attempt to issue the language extension.
01432     if ( GetServerStateParser().GetCapabilityFlag() & kHasLanguageCapability)
01433       Language();
01434 
01435     if (m_runningUrl)
01436       FindMailboxesIfNecessary();
01437     
01438     nsImapState imapState;      
01439     if (m_runningUrl)
01440       m_runningUrl->GetRequiredImapState(&imapState);
01441     
01442     if (imapState == nsIImapUrl::nsImapAuthenticatedState)
01443       ProcessAuthenticatedStateURL();
01444     else   // must be a url that requires us to be in the selected stae 
01445       ProcessSelectedStateURL();
01446 
01447     if (m_retryUrlOnError)
01448       return RetryUrl();
01449 
01450   // The URL has now been processed
01451     if ((!logonFailed && GetConnectionStatus() < 0) || DeathSignalReceived())
01452          HandleCurrentUrlError();
01453       
01454   }
01455   else if (!logonFailed)
01456       HandleCurrentUrlError(); 
01457 
01458   if (mailnewsurl && m_imapMailFolderSink)
01459   {
01460       rv = GetServerStateParser().LastCommandSuccessful() 
01461             ? NS_OK : NS_ERROR_FAILURE;
01462       // we are done with this url.
01463       m_imapMailFolderSink->SetUrlState(this, mailnewsurl, PR_FALSE, rv);
01464        // doom the cache entry
01465       if (NS_FAILED(rv) && DeathSignalReceived() && m_mockChannel)
01466         m_mockChannel->Cancel(rv);
01467   }
01468   else
01469     NS_ASSERTION(PR_FALSE, "missing url or sink");
01470 
01471   // disable timeouts before caching connection.
01472   if (m_transport)
01473     m_transport->SetTimeout(nsISocketTransport::TIMEOUT_READ_WRITE, PR_UINT32_MAX);
01474 
01475 // if we are set up as a channel, we should notify our channel listener that we are stopping...
01476 // so pass in ourself as the channel and not the underlying socket or file channel the protocol
01477 // happens to be using
01478   if (m_channelListener) 
01479   {
01480       nsCOMPtr<nsIRequest> request = do_QueryInterface(m_mockChannel);
01481       NS_ASSERTION(request, "no request");
01482       if (request)
01483         rv = m_channelListener->OnStopRequest(request, m_channelContext, NS_OK);
01484   }
01485   SetFlag(IMAP_CLEAN_UP_URL_STATE);
01486 
01487   nsCOMPtr <nsISupports> copyState;
01488   if (m_runningUrl)
01489     m_runningUrl->GetCopyState(getter_AddRefs(copyState));
01490   // this is so hokey...we MUST clear any local references to the url 
01491   // BEFORE calling ReleaseUrlState
01492   mailnewsurl = nsnull;
01493 
01494   // save the imap folder sink since we need it to do the CopyNextStreamMessage
01495   nsCOMPtr<nsIImapMailFolderSink> imapMailFolderSink = m_imapMailFolderSink;
01496   // release the url as we are done with it...
01497   ReleaseUrlState(PR_FALSE);
01498   ResetProgressInfo();
01499 
01500   ClearFlag(IMAP_CLEAN_UP_URL_STATE);
01501 
01502   if (imapMailFolderSink)
01503   {
01504       imapMailFolderSink->PrepareToReleaseObject(copyState);
01505       imapMailFolderSink->CopyNextStreamMessage(GetServerStateParser().LastCommandSuccessful() 
01506                                                 && GetConnectionStatus() >= 0, copyState);
01507       copyState = nsnull;
01508       imapMailFolderSink->ReleaseObject();
01509       // we might need this to stick around for IDLE support
01510       m_imapMailFolderSink = imapMailFolderSink;
01511       imapMailFolderSink = nsnull;
01512   }
01513 
01514   // now try queued urls, now that we've released this connection.
01515   if (m_imapServerSink)
01516   {
01517     if (GetConnectionStatus() >= 0)
01518       rv = m_imapServerSink->LoadNextQueuedUrl(this, &anotherUrlRun);
01519     else // if we don't do this, they'll just sit and spin until
01520           // we run some other url on this server.
01521     {
01522       Log("ProcessCurrentURL", nsnull, "aborting queued urls");
01523       rv = m_imapServerSink->AbortQueuedUrls();
01524     }
01525   }
01526 
01527   // if we didn't run another url, release the server sink to
01528   // cut circular refs.
01529   if (!anotherUrlRun)
01530       m_imapServerSink = nsnull;
01531   
01532   nsCOMPtr<nsIImapIncomingServer> imapServer  = do_QueryReferent(m_server, &rv);
01533   if (GetConnectionStatus() < 0 || !GetServerStateParser().Connected() 
01534     || GetServerStateParser().SyntaxError())
01535   {
01536     if (imapServer)
01537       imapServer->RemoveConnection(this);
01538 
01539     if (!DeathSignalReceived()) 
01540     {
01541         TellThreadToDie(PR_FALSE);
01542     }
01543   }
01544   else
01545   {
01546     if (imapServer)
01547     {
01548       PRBool shuttingDown;
01549       imapServer->GetShuttingDown(&shuttingDown);
01550       if (shuttingDown)
01551         m_useIdle = PR_FALSE;
01552     }
01553   }
01554   return anotherUrlRun;
01555 }
01556 
01557 PRBool nsImapProtocol::RetryUrl()
01558 {
01559   nsCOMPtr <nsIImapUrl> kungFuGripImapUrl = m_runningUrl;
01560   nsCOMPtr <nsIImapMockChannel> saveMockChannel;
01561   m_runningUrl->GetMockChannel(getter_AddRefs(saveMockChannel));
01562   ReleaseUrlState(PR_TRUE);
01563   nsresult rv;
01564   nsCOMPtr<nsIImapIncomingServer> imapServer  = do_QueryReferent(m_server, &rv);
01565   kungFuGripImapUrl->SetMockChannel(saveMockChannel);
01566   if (NS_SUCCEEDED(rv))
01567     imapServer->RemoveConnection(this);
01568   if (m_imapServerSink)
01569     m_imapServerSink->RetryUrl(kungFuGripImapUrl);
01570   return (m_imapServerSink != nsnull); // we're running a url (the same url)
01571 }
01572 
01573 // ignoreBadAndNOResponses --> don't throw a error dialog if this command results in a NO or Bad response
01574 // from the server..in other words the command is "exploratory" and we don't really care if it succeeds or fails.
01575 void nsImapProtocol::ParseIMAPandCheckForNewMail(const char* commandString, PRBool aIgnoreBadAndNOResponses)
01576 {
01577     if (commandString)
01578         GetServerStateParser().ParseIMAPServerResponse(commandString, aIgnoreBadAndNOResponses);
01579     else
01580         GetServerStateParser().ParseIMAPServerResponse(m_currentCommand.get(), aIgnoreBadAndNOResponses);
01581     // **** fix me for new mail biff state *****
01582 }
01583 
01585 // End of nsIStreamListenerSupport
01587 
01588 NS_IMETHODIMP
01589 nsImapProtocol::GetRunningUrl(nsIURI **result)
01590 {
01591     if (result && m_runningUrl)
01592         return m_runningUrl->QueryInterface(NS_GET_IID(nsIURI), (void**)
01593                                             result);
01594     else
01595         return NS_ERROR_NULL_POINTER;
01596 }
01597 
01598 
01599 NS_IMETHODIMP nsImapProtocol::GetRunningImapURL(nsIImapUrl **aImapUrl)
01600 {
01601   if (aImapUrl && m_runningUrl)
01602      return m_runningUrl->QueryInterface(NS_GET_IID(nsIImapUrl), (void**) aImapUrl);
01603   else
01604     return NS_ERROR_NULL_POINTER;
01605 
01606 }
01607 
01608 /*
01609  * Writes the data contained in dataBuffer into the current output stream. It also informs
01610  * the transport layer that this data is now available for transmission.
01611  * Returns a positive number for success, 0 for failure (not all the bytes were written to the
01612  * stream, etc). We need to make another pass through this file to install an error system (mscott)
01613  */
01614 
01615 nsresult nsImapProtocol::SendData(const char * dataBuffer, PRBool aSuppressLogging)
01616 {
01617   nsresult rv = NS_ERROR_NULL_POINTER;
01618 
01619   if (!m_transport)
01620   {
01621       Log("SendData", nsnull, "clearing IMAP_CONNECTION_IS_OPEN");
01622       // the connection died unexpectedly! so clear the open connection flag
01623       ClearFlag(IMAP_CONNECTION_IS_OPEN); 
01624       TellThreadToDie(PR_FALSE);
01625       SetConnectionStatus(-1);
01626       return NS_ERROR_FAILURE;
01627   }
01628 
01629   if (dataBuffer && m_outputStream)
01630   {
01631     m_currentCommand = dataBuffer;
01632     if (!aSuppressLogging)
01633       Log("SendData", nsnull, dataBuffer);
01634     else
01635       Log("SendData", nsnull, "Logging suppressed for this command (it probably contained authentication information)");
01636     
01637     {
01638       // don't allow someone to close the stream/transport out from under us
01639       // this can happen when the ui thread calls TellThreadToDie.
01640       nsAutoCMonitor mon(this);
01641       PRUint32 n;
01642       if (m_outputStream)
01643         rv = m_outputStream->Write(dataBuffer, PL_strlen(dataBuffer), &n);
01644     }
01645     if (NS_FAILED(rv))
01646     {
01647       Log("SendData", nsnull, "clearing IMAP_CONNECTION_IS_OPEN");
01648       // the connection died unexpectedly! so clear the open connection flag
01649       ClearFlag(IMAP_CONNECTION_IS_OPEN); 
01650       TellThreadToDie(PR_FALSE);
01651       SetConnectionStatus(-1);
01652       if (m_runningUrl && !m_retryUrlOnError)
01653       {
01654         m_runningUrl->SetRerunningUrl(PR_TRUE);
01655         m_retryUrlOnError = PR_TRUE;
01656       }
01657     }
01658   }
01659 
01660   return rv;
01661 }
01662 
01664 // Begin protocol state machine functions...
01666 
01667   // ProcessProtocolState - we override this only so we'll link - it should never get called.
01668   
01669 nsresult nsImapProtocol::ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream, 
01670                                                                PRUint32 sourceOffset, PRUint32 length)
01671 {
01672   return NS_OK;
01673 }
01674 
01675 // LoadImapUrl takes a url, initializes all of our url specific data by calling SetupUrl.
01676 // If we don't have a connection yet, we open the connection. Finally, we signal the 
01677 // url to run monitor to let the imap main thread loop process the current url (it is waiting
01678 // on this monitor). There is a contract that the imap thread has already been started b4 we
01679 // attempt to load a url....
01680 NS_IMETHODIMP nsImapProtocol::LoadImapUrl(nsIURI * aURL, nsISupports * aConsumer)
01681 {
01682   nsresult rv = NS_OK;
01683   if (aURL)
01684   {
01685 #ifdef DEBUG_bienvenu
01686     nsCAutoString urlSpec;
01687     aURL->GetSpec(urlSpec);
01688     printf("loading url %s\n", urlSpec.get());
01689 #endif
01690     m_urlInProgress = PR_TRUE;
01691     m_imapMailFolderSink = nsnull;
01692     rv = SetupWithUrl(aURL, aConsumer); 
01693     NS_ASSERTION(NS_SUCCEEDED(rv), "error setting up imap url");
01694     if (NS_FAILED(rv)) 
01695       return rv;
01696 
01697     SetupSinkProxy(); // generate proxies for all of the event sinks in the url
01698     m_lastActiveTime = PR_Now();
01699     if (m_transport && m_runningUrl)
01700     {
01701       nsImapAction imapAction;
01702       m_runningUrl->GetImapAction(&imapAction);
01703 
01704       // if we're running a select or delete all, do a noop first.
01705       // this should really be in the connection cache code when we know
01706       // we're pulling out a selected state connection, but maybe we
01707       // can get away with this.
01708       m_needNoop = (imapAction == nsIImapUrl::nsImapSelectFolder || imapAction == nsIImapUrl::nsImapDeleteAllMsgs);
01709 
01710       // We now have a url to run so signal the monitor for url ready to be processed...
01711       PR_EnterMonitor(m_urlReadyToRunMonitor);
01712       m_nextUrlReadyToRun = PR_TRUE;
01713       PR_Notify(m_urlReadyToRunMonitor);
01714       PR_ExitMonitor(m_urlReadyToRunMonitor);
01715 
01716     } // if we have an imap url and a transport
01717     else
01718       NS_ASSERTION(PR_FALSE, "missing channel or running url");
01719 
01720   } // if we received a url!
01721 
01722   return rv;
01723 }
01724 
01725 NS_IMETHODIMP nsImapProtocol::IsBusy(PRBool *aIsConnectionBusy,
01726                                      PRBool *isInboxConnection)
01727 {
01728   if (!aIsConnectionBusy || !isInboxConnection)
01729     return NS_ERROR_NULL_POINTER;
01730   NS_LOCK_INSTANCE();
01731   nsresult rv = NS_OK;
01732   *aIsConnectionBusy = PR_FALSE;
01733   *isInboxConnection = PR_FALSE;
01734   if (!m_transport)
01735   {
01736     // this connection might not be fully set up yet.
01737     rv = NS_ERROR_FAILURE;
01738   }
01739   else
01740   {
01741     if (m_urlInProgress) // do we have a url? That means we're working on it... 
01742       *aIsConnectionBusy = PR_TRUE;
01743 
01744     if (GetServerStateParser().GetIMAPstate() ==
01745         nsImapServerResponseParser::kFolderSelected && GetServerStateParser().GetSelectedMailboxName() && 
01746         PL_strcasecmp(GetServerStateParser().GetSelectedMailboxName(),
01747                       "Inbox") == 0)
01748       *isInboxConnection = PR_TRUE;
01749       
01750   }
01751   NS_UNLOCK_INSTANCE();
01752   return rv;
01753 }
01754 
01755 #define IS_SUBSCRIPTION_RELATED_ACTION(action) (action == nsIImapUrl::nsImapSubscribe\
01756 || action == nsIImapUrl::nsImapUnsubscribe || action == nsIImapUrl::nsImapDiscoverAllBoxesUrl || action == nsIImapUrl::nsImapListFolder)
01757 
01758 
01759 // canRunUrl means the connection is not busy, and is in the selcted state
01760 // for the desired folder (or authenticated).
01761 // has to wait means it's in the right selected state, but busy.
01762 NS_IMETHODIMP nsImapProtocol::CanHandleUrl(nsIImapUrl * aImapUrl, 
01763                                            PRBool * aCanRunUrl,
01764                                            PRBool * hasToWait)
01765 {
01766   if (!aCanRunUrl || !hasToWait || !aImapUrl)
01767     return NS_ERROR_NULL_POINTER;
01768   nsresult rv = NS_OK;
01769   nsAutoCMonitor mon(this);
01770   
01771   *aCanRunUrl = PR_FALSE; // assume guilty until proven otherwise...
01772   *hasToWait = PR_FALSE;
01773   
01774   if (DeathSignalReceived())
01775     return NS_ERROR_FAILURE;
01776   PRBool isBusy = PR_FALSE;
01777   PRBool isInboxConnection = PR_FALSE;
01778   
01779   if (!m_transport)
01780   {
01781     // this connection might not be fully set up yet.
01782     return NS_ERROR_FAILURE;
01783   }
01784   else if (m_currentServerCommandTagNumber != 0) 
01785   {
01786     PRBool isAlive;
01787     rv = m_transport->IsAlive(&isAlive);
01788     // if the transport is not alive, and we've ever sent a command with this connection, kill it.
01789     // otherwise, we've probably just not finished setting it so don't kill it!
01790     if (NS_FAILED(rv) || !isAlive)
01791     {
01792       TellThreadToDie(PR_FALSE);
01793       return NS_ERROR_FAILURE;
01794     }
01795   }
01796   IsBusy(&isBusy, &isInboxConnection);
01797   PRBool inSelectedState = GetServerStateParser().GetIMAPstate() ==
01798     nsImapServerResponseParser::kFolderSelected;
01799   
01800   nsCAutoString curSelectedUrlFolderName;
01801   nsCAutoString pendingUrlFolderName;
01802   if (inSelectedState)
01803     curSelectedUrlFolderName = GetServerStateParser().GetSelectedMailboxName();
01804 
01805   if (isBusy)
01806   {
01807     nsImapState curUrlImapState;
01808     NS_ASSERTION(m_runningUrl,"isBusy, but no running url.");
01809     if (m_runningUrl)
01810     {
01811       m_runningUrl->GetRequiredImapState(&curUrlImapState);
01812       if (curUrlImapState == nsIImapUrl::nsImapSelectedState)
01813       {
01814         char *folderName = GetFolderPathString();
01815         if (!curSelectedUrlFolderName.Equals(folderName))
01816           pendingUrlFolderName.Assign(folderName);
01817         inSelectedState = PR_TRUE;
01818         PR_Free(folderName);
01819       }
01820     }
01821   }
01822   
01823   nsImapState imapState;
01824   nsImapAction actionForProposedUrl;
01825   aImapUrl->GetImapAction(&actionForProposedUrl);
01826   aImapUrl->GetRequiredImapState(&imapState);
01827   
01828   // OK, this is a bit of a hack - we're going to pretend that
01829   // these types of urls requires a selected state connection on
01830   // the folder in question. This isn't technically true,
01831   // but we would much rather use that connection for several reasons,
01832   // one is that some UW servers require us to use that connection
01833   // the other is that we don't want to leave a connection dangling in
01834   // the selected state for the deleted folder.
01835   // If we don't find a connection in that selected state,
01836   // we'll fall back to the first free connection.
01837   PRBool isSelectedStateUrl = imapState == nsIImapUrl::nsImapSelectedState 
01838     || actionForProposedUrl == nsIImapUrl::nsImapDeleteFolder || actionForProposedUrl == nsIImapUrl::nsImapRenameFolder
01839     || actionForProposedUrl == nsIImapUrl::nsImapMoveFolderHierarchy 
01840     || actionForProposedUrl == nsIImapUrl::nsImapAppendDraftFromFile 
01841     || actionForProposedUrl == nsIImapUrl::nsImapAppendMsgFromFile 
01842     || actionForProposedUrl == nsIImapUrl::nsImapFolderStatus;
01843   
01844   nsCOMPtr<nsIMsgMailNewsUrl> msgUrl = do_QueryInterface(aImapUrl);
01845   nsCOMPtr<nsIMsgIncomingServer> server;
01846   rv = msgUrl->GetServer(getter_AddRefs(server));
01847   if (NS_SUCCEEDED(rv))
01848   {
01849     // compare host/user between url and connection.
01850     char * urlHostName = nsnull;
01851     char * urlUserName = nsnull;
01852     rv = server->GetHostName(&urlHostName);
01853     if (NS_FAILED(rv)) return rv;
01854     rv = server->GetUsername(&urlUserName);
01855     if (NS_FAILED(rv)) return rv;
01856     if ((!GetImapHostName() || 
01857       PL_strcasecmp(urlHostName, GetImapHostName()) == 0) &&
01858       (!GetImapUserName() || 
01859       PL_strcasecmp(urlUserName, GetImapUserName()) == 0))
01860     {
01861       if (isSelectedStateUrl)
01862       {
01863         if (inSelectedState)
01864         {
01865           // *** jt - in selected state can only run url with
01866           // matching foldername
01867           char *folderNameForProposedUrl = nsnull;
01868           rv = aImapUrl->CreateServerSourceFolderPathString(
01869             &folderNameForProposedUrl);
01870           if (NS_SUCCEEDED(rv) && folderNameForProposedUrl)
01871           {
01872             PRBool isInbox = 
01873               PL_strcasecmp("Inbox", folderNameForProposedUrl) == 0;
01874             if (!curSelectedUrlFolderName.IsEmpty() || !pendingUrlFolderName.IsEmpty())
01875             {
01876               PRBool matched = isInbox ?
01877                 PL_strcasecmp(curSelectedUrlFolderName.get(),
01878                 folderNameForProposedUrl) == 0 : 
01879               PL_strcmp(curSelectedUrlFolderName.get(),
01880                 folderNameForProposedUrl) == 0;
01881               if (!matched && !pendingUrlFolderName.IsEmpty())
01882               {
01883                 matched = isInbox ?
01884                   PL_strcasecmp(pendingUrlFolderName.get(),
01885                   folderNameForProposedUrl) == 0 : 
01886                 PL_strcmp(pendingUrlFolderName.get(),
01887                   folderNameForProposedUrl) == 0;
01888               }
01889               if (matched)
01890               {
01891                 if (isBusy)
01892                   *hasToWait = PR_TRUE;
01893                 else
01894                   *aCanRunUrl = PR_TRUE;
01895               }
01896             }
01897           }
01898 #ifdef DEBUG_bienvenu1
01899           printf("proposed url = %s folder for connection %s has To Wait = %s can run = %s\n",
01900             folderNameForProposedUrl, curUrlFolderName.get(),
01901             (*hasToWait) ? "TRUE" : "FALSE", (*aCanRunUrl) ? "TRUE" : "FALSE");
01902 #endif
01903           PR_FREEIF(folderNameForProposedUrl);
01904         }
01905       }
01906       else // *** jt - an authenticated state url can be run in either
01907         // authenticated or selected state
01908       {
01909         nsImapAction actionForRunningUrl;
01910         
01911         // If proposed url is subscription related, and we are currently running
01912         // a subscription url, then we want to queue the proposed url after the current url.
01913         // Otherwise, we can run this url if we're not busy.
01914         // If we never find a running subscription-related url, the caller will
01915         // just use whatever free connection it can find, which is what we want.
01916         if (IS_SUBSCRIPTION_RELATED_ACTION(actionForProposedUrl))
01917         {
01918           if (isBusy && m_runningUrl)
01919           {
01920             m_runningUrl->GetImapAction(&actionForRunningUrl);
01921             if (IS_SUBSCRIPTION_RELATED_ACTION(actionForRunningUrl))
01922             {
01923               *aCanRunUrl = PR_FALSE;
01924               *hasToWait = PR_TRUE;
01925             }
01926           }
01927         }
01928         else
01929         {
01930           if (!isBusy)
01931             *aCanRunUrl = PR_TRUE;
01932         }
01933       }
01934       
01935       PR_Free(urlHostName);
01936       PR_Free(urlUserName);
01937     }
01938   }
01939   return rv;
01940 }
01941 
01942 
01943 // Command tag handling stuff
01944 void nsImapProtocol::IncrementCommandTagNumber()
01945 {
01946     sprintf(m_currentServerCommandTag,"%ld", (long) ++m_currentServerCommandTagNumber);
01947 }
01948 
01949 char *nsImapProtocol::GetServerCommandTag()
01950 {
01951     return m_currentServerCommandTag;
01952 }
01953 
01954 void nsImapProtocol::ProcessSelectedStateURL()
01955 {
01956   nsXPIDLCString mailboxName;
01957   PRBool          bMessageIdsAreUids = PR_TRUE;
01958   imapMessageFlagsType  msgFlags = 0;
01959   nsCString       urlHost;
01960   
01961   // this can't fail, can it?
01962   nsresult res;
01963   res = m_runningUrl->GetImapAction(&m_imapAction);
01964   m_runningUrl->MessageIdsAreUids(&bMessageIdsAreUids);
01965   m_runningUrl->GetMsgFlags(&msgFlags);
01966   
01967   res = CreateServerSourceFolderPathString(getter_Copies(mailboxName));
01968   if (NS_FAILED(res))
01969     Log("ProcessSelectedStateURL", nsnull, "error getting source folder path string");
01970   
01971   if (NS_SUCCEEDED(res) && !DeathSignalReceived())
01972   {
01973     // OK, code here used to check explicitly for multiple connections to the inbox,
01974     // but the connection pool stuff should handle this now.
01975     PRBool selectIssued = PR_FALSE;
01976     if (GetServerStateParser().GetIMAPstate() == nsImapServerResponseParser::kFolderSelected)
01977     {
01978       if (GetServerStateParser().GetSelectedMailboxName() && 
01979         PL_strcmp(GetServerStateParser().GetSelectedMailboxName(),
01980         mailboxName))
01981       {       // we are selected in another folder
01982         if (m_closeNeededBeforeSelect)
01983           Close();
01984         if (GetServerStateParser().LastCommandSuccessful()) 
01985         {
01986           selectIssued = PR_TRUE;
01987           AutoSubscribeToMailboxIfNecessary(mailboxName);
01988           SelectMailbox(mailboxName);
01989         }
01990       }
01991       else if (!GetServerStateParser().GetSelectedMailboxName())
01992       {       // why are we in the selected state with no box name?
01993         SelectMailbox(mailboxName);
01994         selectIssued = PR_TRUE;
01995       }
01996       else
01997       {
01998         // get new message counts, if any, from server
01999         if (m_needNoop)
02000         {
02001           m_noopCount++;
02002           if ((gPromoteNoopToCheckCount > 0 && (m_noopCount % gPromoteNoopToCheckCount) == 0) ||
02003             CheckNeeded())
02004             Check();
02005           else
02006             Noop(); // I think this is needed when we're using a cached connection
02007           m_needNoop = PR_FALSE;
02008         }
02009       }
02010     }
02011     else
02012     {
02013       // go to selected state
02014       AutoSubscribeToMailboxIfNecessary(mailboxName);
02015       SelectMailbox(mailboxName);
02016       selectIssued = GetServerStateParser().LastCommandSuccessful();
02017     }
02018     
02019     if (selectIssued)
02020       RefreshACLForFolderIfNecessary(mailboxName);
02021     
02022     PRBool uidValidityOk = PR_TRUE;
02023     if (GetServerStateParser().LastCommandSuccessful() && selectIssued && 
02024       (m_imapAction != nsIImapUrl::nsImapSelectFolder) && (m_imapAction != nsIImapUrl::nsImapLiteSelectFolder))
02025     {
02026       if (m_imapMailFolderSink)
02027       {
02028         PRInt32 uidValidity;
02029         m_imapMailFolderSink->GetUidValidity(&uidValidity);
02030         
02031         
02032         // error on the side of caution, if the fe event fails to set uidStruct->returnValidity, then assume that UIDVALIDITY
02033         // did not roll.  This is a common case event for attachments that are fetched within a browser context.
02034         if (!DeathSignalReceived())
02035           uidValidityOk = (uidValidity == kUidUnknown) || (uidValidity == GetServerStateParser().FolderUID());
02036       }
02037       
02038     }
02039     
02040     if (!uidValidityOk)
02041       Log("ProcessSelectedStateURL", nsnull, "uid validity not ok");
02042     if (GetServerStateParser().LastCommandSuccessful() && !DeathSignalReceived() && (uidValidityOk || m_imapAction == nsIImapUrl::nsImapDeleteAllMsgs))
02043     {
02044       
02045       if (GetServerStateParser().CurrentFolderReadOnly())
02046       {
02047         Log("ProcessSelectedStateURL", nsnull, "current folder read only");
02048         if (m_imapAction == nsIImapUrl::nsImapAddMsgFlags ||
02049           m_imapAction == nsIImapUrl::nsImapSubtractMsgFlags) 
02050         {
02051           PRBool canChangeFlag = PR_FALSE;
02052           if (GetServerStateParser().ServerHasACLCapability() && m_imapMailFolderSink)
02053           {
02054             PRUint32 aclFlags = 0;
02055             
02056             if (NS_SUCCEEDED(m_imapMailFolderSink->GetAclFlags(&aclFlags)))
02057             {
02058               if (aclFlags != 0) // make sure we have some acl flags
02059               {
02060                 canChangeFlag = ((msgFlags & kImapMsgSeenFlag) && (aclFlags & IMAP_ACL_STORE_SEEN_FLAG));
02061               }
02062             }
02063           }
02064           else
02065             canChangeFlag = (GetServerStateParser().SettablePermanentFlags() & msgFlags) == msgFlags;
02066           if (!canChangeFlag)
02067             return;
02068         }
02069         if (m_imapAction == nsIImapUrl::nsImapExpungeFolder || m_imapAction == nsIImapUrl::nsImapDeleteMsg ||
02070           m_imapAction == nsIImapUrl::nsImapDeleteAllMsgs)
02071           return;
02072       }
02073       switch (m_imapAction)
02074       {
02075       case nsIImapUrl::nsImapLiteSelectFolder:
02076         if (GetServerStateParser().LastCommandSuccessful() && m_imapMailFolderSink)
02077         {
02078           m_imapMailFolderSink->SetUidValidity(GetServerStateParser().FolderUID());
02079           
02080           // need to update the mailbox count - is this a good place?
02081           ProcessMailboxUpdate(PR_FALSE); // handle uidvalidity change
02082         }
02083         break;
02084       case nsIImapUrl::nsImapSaveMessageToDisk:
02085       case nsIImapUrl::nsImapMsgFetch:
02086       case nsIImapUrl::nsImapMsgFetchPeek:
02087       case nsIImapUrl::nsImapMsgDownloadForOffline:
02088       case nsIImapUrl::nsImapMsgPreview:
02089         {
02090           nsXPIDLCString messageIdString;
02091           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
02092           // we don't want to send the flags back in a group
02093           // GetServerStateParser().ResetFlagInfo(0);
02094           if (HandlingMultipleMessages(messageIdString) || m_imapAction == nsIImapUrl::nsImapMsgDownloadForOffline
02095              || m_imapAction == nsIImapUrl::nsImapMsgPreview)
02096           {
02097             // multiple messages, fetch them all
02098             SetProgressString(IMAP_FOLDER_RECEIVING_MESSAGE_OF);
02099             
02100             m_progressIndex = 0;
02101             m_progressCount = CountMessagesInIdString(messageIdString);
02102             
02103             // we need to set this so we'll get the msg from the memory cache.
02104             if (m_imapAction == nsIImapUrl::nsImapMsgFetchPeek)
02105               SetContentModified(IMAP_CONTENT_NOT_MODIFIED);
02106             FetchMessage(messageIdString, 
02107               (m_imapAction == nsIImapUrl::nsImapMsgPreview)
02108               ? kBodyStart : kEveryThingRFC822Peek,
02109               bMessageIdsAreUids);
02110             if (m_imapAction == nsIImapUrl::nsImapMsgPreview)
02111               HeaderFetchCompleted(); 
02112             SetProgressString(0);
02113           }
02114           else
02115           {
02116             // A single message ID
02117                   nsIMAPeFetchFields whatToFetch = kEveryThingRFC822;
02118                if(m_imapAction == nsIImapUrl::nsImapMsgFetchPeek)
02119               whatToFetch = kEveryThingRFC822Peek;
02120             
02121             // First, let's see if we're requesting a specific MIME part
02122             char *imappart = nsnull;
02123             m_runningUrl->GetImapPartToFetch(&imappart);
02124             if (imappart)
02125             {
02126               if (bMessageIdsAreUids)
02127               {
02128                 // We actually want a specific MIME part of the message.
02129                 // The Body Shell will generate it, even though we haven't downloaded it yet.
02130                 
02131                 IMAP_ContentModifiedType modType = GetShowAttachmentsInline() ? 
02132                   IMAP_CONTENT_MODIFIED_VIEW_INLINE :
02133                   IMAP_CONTENT_MODIFIED_VIEW_AS_LINKS ;
02134                 
02135                 nsIMAPBodyShell *foundShell = nsnull;
02136                 res = m_hostSessionList->FindShellInCacheForHost(GetImapServerKey(),
02137                   GetServerStateParser().GetSelectedMailboxName(), messageIdString, modType, &foundShell);
02138                 if (!foundShell)
02139                 {
02140                   // The shell wasn't in the cache.  Deal with this case later.
02141                   Log("SHELL",NULL,"Loading part, shell not found in cache!");
02142                   //PR_LOG(IMAP, out, ("BODYSHELL: Loading part, shell not found in cache!"));
02143                   // The parser will extract the part number from the current URL.
02144                   SetContentModified(modType);
02145                   Bodystructure(messageIdString, bMessageIdsAreUids);
02146                 }
02147                 else
02148                 {
02149                   Log("SHELL", NULL, "Loading Part, using cached shell.");
02150                   //PR_LOG(IMAP, out, ("BODYSHELL: Loading part, using cached shell."));
02151                   SetContentModified(modType);
02152                   foundShell->SetConnection(this);
02153                   GetServerStateParser().UseCachedShell(foundShell);
02154                   //Set the current uid in server state parser (in case it was used for new mail msgs earlier).
02155                   GetServerStateParser().SetCurrentResponseUID((PRUint32)atoi(messageIdString));
02156                   foundShell->Generate(imappart);
02157                   GetServerStateParser().UseCachedShell(NULL);
02158                 }
02159               }
02160               else
02161               {
02162                 // Message IDs are not UIDs.
02163                 NS_ASSERTION(PR_FALSE, "message ids aren't uids");
02164               }
02165               PR_Free(imappart);
02166             }
02167             else
02168             {
02169               // downloading a single message: try to do it by bodystructure, and/or do it by chunks
02170               PRUint32 messageSize = GetMessageSize(messageIdString, bMessageIdsAreUids);
02171               // We need to check the format_out bits to see if we are allowed to leave out parts,
02172               // or if we are required to get the whole thing.  Some instances where we are allowed
02173               // to do it by parts:  when viewing a message, replying to a message, or viewing its source
02174               // Some times when we're NOT allowed:  when forwarding a message, saving it, moving it, etc.
02175               // need to set a flag in the url, I guess, equiv to allow_content_changed.
02176               PRBool allowedToBreakApart = PR_TRUE; // (ce  && !DeathSignalReceived()) ? ce->URL_s->allow_content_change : PR_FALSE;
02177               PRBool mimePartSelectorDetected;
02178               PRBool urlOKToFetchByParts = PR_FALSE;
02179               m_runningUrl->GetMimePartSelectorDetected(&mimePartSelectorDetected);
02180               m_runningUrl->GetFetchPartsOnDemand(&urlOKToFetchByParts);
02181               
02182               if (urlOKToFetchByParts &&
02183                 allowedToBreakApart && 
02184                 !GetShouldFetchAllParts() &&
02185                 GetServerStateParser().ServerHasIMAP4Rev1Capability() /* &&
02186                 !mimePartSelectorDetected */)  // if a ?part=, don't do BS.
02187               {
02188                 // OK, we're doing bodystructure
02189                 
02190                 // Before fetching the bodystructure, let's check our body shell cache to see if
02191                 // we already have it around.
02192                 nsIMAPBodyShell *foundShell = NULL;
02193                 IMAP_ContentModifiedType modType = GetShowAttachmentsInline() ? 
02194                   IMAP_CONTENT_MODIFIED_VIEW_INLINE :
02195                   IMAP_CONTENT_MODIFIED_VIEW_AS_LINKS ;
02196                 
02197                 nsCOMPtr<nsIMsgMailNewsUrl> mailurl = do_QueryInterface(m_runningUrl);
02198                 if (mailurl)
02199                 {
02200                   mailurl->SetAddToMemoryCache(PR_FALSE);
02201                   // need to proxy this over to the ui thread
02202                   if (m_imapMessageSink)
02203                   {
02204                     m_imapMessageSink->SetNotifyDownloadedLines(PR_FALSE);
02205                     m_imapMessageSink->SetImageCacheSessionForUrl(mailurl);
02206                   }
02207                   
02208                 }
02209                 SetContentModified(modType);  // This will be looked at by the cache
02210                 if (bMessageIdsAreUids)
02211                 {
02212                   res = m_hostSessionList->FindShellInCacheForHost(GetImapServerKey(),
02213                     GetServerStateParser().GetSelectedMailboxName(), messageIdString, modType, &foundShell);
02214                   if (foundShell)
02215                   {
02216                     Log("SHELL",NULL,"Loading message, using cached shell.");
02217                     //PR_LOG(IMAP, out, ("BODYSHELL: Loading message, using cached shell."));
02218                     foundShell->SetConnection(this);
02219                     GetServerStateParser().UseCachedShell(foundShell);
02220                     //Set the current uid in server state parser (in case it was used for new mail msgs earlier).
02221                     GetServerStateParser().SetCurrentResponseUID((PRUint32)atoi(messageIdString));
02222                     foundShell->Generate(NULL);
02223                     GetServerStateParser().UseCachedShell(NULL);
02224                   }
02225                 }
02226                 
02227                 if (!foundShell)
02228                   Bodystructure(messageIdString, bMessageIdsAreUids);
02229               }
02230               else
02231               {
02232                 // Not doing bodystructure.  Fetch the whole thing, and try to do
02233                 // it in chunks.
02234                 SetContentModified(IMAP_CONTENT_NOT_MODIFIED);
02235                 FetchTryChunking(messageIdString, whatToFetch,
02236                   bMessageIdsAreUids, NULL, messageSize, PR_TRUE);
02237               }
02238             }
02239             if (GetServerStateParser().LastCommandSuccessful() 
02240                 && m_imapAction != nsIImapUrl::nsImapMsgPreview 
02241                 && m_imapAction != nsIImapUrl::nsImapMsgFetchPeek)
02242             {
02243               PRUint32 uid = atoi(messageIdString); 
02244               PRInt32 index;
02245               PRBool foundIt;
02246               imapMessageFlagsType flags = m_flagState->GetMessageFlagsFromUID(uid, &foundIt, &index);
02247               if (foundIt)
02248               {
02249                 flags |= kImapMsgSeenFlag;
02250                 m_flagState->SetMessageFlags(index, flags);
02251               }
02252             }
02253 
02254           }
02255         }
02256         break;
02257       case nsIImapUrl::nsImapExpungeFolder:
02258         Expunge();
02259         // note fall through to next cases.
02260       case nsIImapUrl::nsImapSelectFolder:
02261       case nsIImapUrl::nsImapSelectNoopFolder:
02262         ProcessMailboxUpdate(PR_TRUE);
02263         break;
02264       case nsIImapUrl::nsImapMsgHeader:
02265         {
02266           nsXPIDLCString messageIds;
02267           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIds));
02268           
02269           // we don't want to send the flags back in a group
02270           //        GetServerStateParser().ResetFlagInfo(0);
02271           FetchMessage(messageIds, 
02272             kHeadersRFC822andUid,
02273             bMessageIdsAreUids);
02274           // if we explicitly ask for headers, as opposed to getting them as a result
02275           // of selecting the folder, or biff, send the headerFetchCompleted notification
02276           // to flush out the header cache.
02277           HeaderFetchCompleted(); 
02278         }
02279         break;
02280       case nsIImapUrl::nsImapSearch:
02281         {
02282           nsXPIDLCString searchCriteriaString;
02283           m_runningUrl->CreateSearchCriteriaString(getter_Copies(searchCriteriaString));
02284           Search(searchCriteriaString, bMessageIdsAreUids);
02285           // drop the results on the floor for now
02286         }
02287         break;
02288       case nsIImapUrl::nsImapUserDefinedMsgCommand:
02289         {
02290           nsXPIDLCString messageIdString;
02291           nsXPIDLCString command;
02292           
02293           m_runningUrl->GetCommand(getter_Copies(command));
02294           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
02295           IssueUserDefinedMsgCommand(command, messageIdString);
02296         }
02297         break;
02298       case nsIImapUrl::nsImapUserDefinedFetchAttribute:
02299         {
02300           nsXPIDLCString messageIdString;
02301           nsXPIDLCString attribute;
02302           
02303           m_runningUrl->GetCustomAttributeToFetch(getter_Copies(attribute));
02304           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
02305           FetchMsgAttribute(messageIdString, attribute);
02306         }
02307         break;
02308       case nsIImapUrl::nsImapMsgStoreCustomKeywords:
02309         {
02310           // if the server doesn't support user defined flags, don't try to set them.
02311           PRUint16 userFlags;
02312           GetSupportedUserFlags(&userFlags);
02313           if (! (userFlags & kImapMsgSupportUserFlag))
02314             break;
02315           nsXPIDLCString messageIdString;
02316           nsXPIDLCString addFlags;
02317           nsXPIDLCString subtractFlags;
02318           
02319           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
02320           m_runningUrl->GetCustomAddFlags(getter_Copies(addFlags));
02321           m_runningUrl->GetCustomSubtractFlags(getter_Copies(subtractFlags));
02322           if (!addFlags.IsEmpty())
02323           {
02324             nsCAutoString storeString("+FLAGS (");
02325             storeString.Append(addFlags);
02326             storeString.Append(")");
02327             Store(messageIdString, storeString.get(), PR_TRUE);
02328           }
02329           if (!subtractFlags.IsEmpty())
02330           {
02331             nsCAutoString storeString("-FLAGS (");
02332             storeString.Append(subtractFlags);
02333             storeString.Append(")");
02334             Store(messageIdString, storeString.get(), PR_TRUE);
02335           }
02336         }
02337         break;
02338       case nsIImapUrl::nsImapDeleteMsg:
02339         {
02340           nsXPIDLCString messageIdString;
02341           
02342           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
02343           if (HandlingMultipleMessages(messageIdString))
02344             ProgressEventFunctionUsingId (IMAP_DELETING_MESSAGES);
02345           else
02346             ProgressEventFunctionUsingId(IMAP_DELETING_MESSAGE);
02347           
02348           Store(messageIdString, "+FLAGS (\\Deleted)",  bMessageIdsAreUids);
02349           
02350           if (GetServerStateParser().LastCommandSuccessful())
02351           {
02352             //delete_message_struct *deleteMsg = (delete_message_struct *) PR_Malloc (sizeof(delete_message_struct));
02353             // convert name back from utf7
02354             utf_name_struct *nameStruct = (utf_name_struct *) PR_Malloc(sizeof(utf_name_struct));
02355             char *canonicalName = NULL;
02356             if (nameStruct)
02357             {
02358               const char *selectedMailboxName = GetServerStateParser().GetSelectedMailboxName();
02359               if (selectedMailboxName)
02360               {
02361                 m_runningUrl->AllocateCanonicalPath(selectedMailboxName, 
02362                   kOnlineHierarchySeparatorUnknown, &canonicalName);
02363               }
02364             }
02365             
02366             if (m_imapMessageSink)
02367               m_imapMessageSink->NotifyMessageDeleted(canonicalName, PR_FALSE, messageIdString);
02368             // notice we don't wait for this to finish...
02369           }
02370           else
02371             HandleMemoryFailure();
02372         }
02373         break;
02374       case nsIImapUrl::nsImapDeleteFolderAndMsgs:
02375         DeleteFolderAndMsgs(mailboxName);
02376         break;
02377       case nsIImapUrl::nsImapDeleteAllMsgs:
02378         {
02379           uint32 numberOfMessages = GetServerStateParser().NumberOfMessages();
02380           if (numberOfMessages)
02381           {
02382             
02383             Store("1:*", "+FLAGS.SILENT (\\Deleted)",
02384               PR_FALSE);  // use sequence #'s  
02385             
02386             if (GetServerStateParser().LastCommandSuccessful())
02387               Expunge();      // expunge messages with deleted flag
02388             if (GetServerStateParser().LastCommandSuccessful())
02389             {
02390               // convert name back from utf7
02391               utf_name_struct *nameStruct = (utf_name_struct *) PR_Malloc(sizeof(utf_name_struct));
02392               char *canonicalName = NULL;
02393               if (nameStruct)
02394               {
02395                 const char *selectedMailboxName = GetServerStateParser().GetSelectedMailboxName();
02396                 if (selectedMailboxName )
02397                 {
02398                   m_runningUrl->AllocateCanonicalPath(selectedMailboxName,
02399                     kOnlineHierarchySeparatorUnknown, &canonicalName);
02400                 }
02401               }
02402               
02403               if (m_imapMessageSink)
02404                 m_imapMessageSink->NotifyMessageDeleted(canonicalName, PR_TRUE, nsnull);
02405             }
02406             
02407           }
02408           PRBool deleteSelf = PR_FALSE;
02409           DeleteSubFolders(mailboxName, deleteSelf);    // don't delete self
02410         }
02411         break;
02412       case nsIImapUrl::nsImapAppendDraftFromFile:
02413         {
02414           OnAppendMsgFromFile();
02415         }
02416         break;
02417       case nsIImapUrl::nsImapAddMsgFlags:
02418         {
02419           nsXPIDLCString messageIdString;
02420           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
02421           
02422           ProcessStoreFlags(messageIdString, bMessageIdsAreUids,
02423             msgFlags, PR_TRUE);
02424         }
02425         break;
02426       case nsIImapUrl::nsImapSubtractMsgFlags:
02427         {
02428           nsXPIDLCString messageIdString;
02429           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
02430           
02431           ProcessStoreFlags(messageIdString, bMessageIdsAreUids,
02432             msgFlags, PR_FALSE);
02433         }
02434         break;
02435       case nsIImapUrl::nsImapSetMsgFlags:
02436         {
02437           nsXPIDLCString messageIdString;
02438           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
02439           
02440           ProcessStoreFlags(messageIdString, bMessageIdsAreUids,
02441             msgFlags, PR_TRUE);
02442           ProcessStoreFlags(messageIdString, bMessageIdsAreUids,
02443             ~msgFlags, PR_FALSE);
02444         }
02445         break;
02446       case nsIImapUrl::nsImapBiff:
02447         PeriodicBiff(); 
02448         break;
02449       case nsIImapUrl::nsImapOnlineCopy:
02450       case nsIImapUrl::nsImapOnlineMove:
02451         {
02452           nsXPIDLCString messageIdString;
02453           m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
02454           char *destinationMailbox = OnCreateServerDestinationFolderPathString();
02455           
02456           if (destinationMailbox)
02457           {
02458             if (m_imapAction == nsIImapUrl::nsImapOnlineMove) 
02459             {
02460               if (HandlingMultipleMessages(messageIdString))
02461                 ProgressEventFunctionUsingIdWithString (IMAP_MOVING_MESSAGES_TO, destinationMailbox);
02462               else
02463                 ProgressEventFunctionUsingIdWithString (IMAP_MOVING_MESSAGE_TO, destinationMailbox); 
02464             }
02465             else {
02466               if (HandlingMultipleMessages(messageIdString))
02467                 ProgressEventFunctionUsingIdWithString (IMAP_COPYING_MESSAGES_TO, destinationMailbox);
02468               else
02469                 ProgressEventFunctionUsingIdWithString (IMAP_COPYING_MESSAGE_TO, destinationMailbox); 
02470             }
02471             
02472             Copy(messageIdString, destinationMailbox, bMessageIdsAreUids);
02473             PR_FREEIF( destinationMailbox);
02474             ImapOnlineCopyState copyState;
02475             if (DeathSignalReceived())
02476               copyState = ImapOnlineCopyStateType::kInterruptedState;
02477             else
02478               copyState = GetServerStateParser().LastCommandSuccessful() ? 
02479               (ImapOnlineCopyState) ImapOnlineCopyStateType::kSuccessfulCopy : 
02480             (ImapOnlineCopyState) ImapOnlineCopyStateType::kFailedCopy;
02481             if (m_imapMailFolderSink)
02482               m_imapMailFolderSink->OnlineCopyCompleted(this, copyState);
02483             
02484             // Don't mark msg 'Deleted' for aol servers since we already issued 'xaol-move' cmd.
02485             if (GetServerStateParser().LastCommandSuccessful() &&
02486               (m_imapAction == nsIImapUrl::nsImapOnlineMove) &&
02487               !GetServerStateParser().ServerIsAOLServer())
02488             {
02489               Store(messageIdString, "+FLAGS (\\Deleted \\Seen)",
02490                 bMessageIdsAreUids); 
02491               PRBool storeSuccessful = GetServerStateParser().LastCommandSuccessful();
02492 
02493               if (gExpungeAfterDelete && storeSuccessful)
02494                 Expunge();
02495 
02496               if (m_imapMailFolderSink)
02497               {
02498                 copyState = storeSuccessful ? (ImapOnlineCopyState) ImapOnlineCopyStateType::kSuccessfulDelete 
02499                   : (ImapOnlineCopyState) ImapOnlineCopyStateType::kFailedDelete;
02500                 m_imapMailFolderSink->OnlineCopyCompleted(this, copyState);
02501               }
02502             }
02503           }
02504           else
02505             HandleMemoryFailure();
02506         }
02507         break;
02508       case nsIImapUrl::nsImapOnlineToOfflineCopy:
02509       case nsIImapUrl::nsImapOnlineToOfflineMove:
02510         {
02511           nsXPIDLCString messageIdString;
02512           nsresult rv = m_runningUrl->CreateListOfMessageIdsString(getter_Copies(messageIdString));
02513           if (NS_SUCCEEDED(rv))
02514           {
02515             SetProgressString(IMAP_FOLDER_RECEIVING_MESSAGE_OF);
02516             m_progressIndex = 0;
02517             m_progressCount = CountMessagesInIdString(messageIdString);
02518             
02519             FetchMessage(messageIdString, 
02520               kEveryThingRFC822Peek,
02521               bMessageIdsAreUids);
02522             
02523             SetProgressString(0);
02524             if (m_imapMailFolderSink)
02525             {
02526               ImapOnlineCopyState copyStatus;
02527               if (GetServerStateParser().LastCommandSuccessful())
02528                 copyStatus = ImapOnlineCopyStateType::kSuccessfulCopy;
02529               else
02530                 copyStatus = ImapOnlineCopyStateType::kFailedCopy;
02531               m_imapMailFolderSink->OnlineCopyCompleted(this, copyStatus);
02532               if (GetServerStateParser().LastCommandSuccessful() &&
02533                 (m_imapAction == nsIImapUrl::nsImapOnlineToOfflineMove))
02534               {
02535                 Store(messageIdString, "+FLAGS (\\Deleted \\Seen)",bMessageIdsAreUids); 
02536                 if (GetServerStateParser().LastCommandSuccessful())
02537                 {
02538                   copyStatus = ImapOnlineCopyStateType::kSuccessfulDelete;
02539                   if (gExpungeAfterDelete)
02540                     Expunge();
02541                 }
02542                 else
02543                   copyStatus = ImapOnlineCopyStateType::kFailedDelete;
02544                 
02545                 m_imapMailFolderSink->OnlineCopyCompleted(this,  copyStatus);
02546               }
02547             }
02548           }
02549           else
02550             HandleMemoryFailure();
02551         }
02552         break;
02553       default:
02554         if (GetServerStateParser().LastCommandSuccessful() && !uidValidityOk)
02555           ProcessMailboxUpdate(PR_FALSE); // handle uidvalidity change
02556         break;
02557     }
02558    }
02559   }
02560   else if (!DeathSignalReceived())
02561     HandleMemoryFailure();
02562 }
02563 
02564 void nsImapProtocol::AutoSubscribeToMailboxIfNecessary(const char *mailboxName)
02565 {
02566 #ifdef HAVE_PORT
02567   if (m_folderNeedsSubscribing)  // we don't know about this folder - we need to subscribe to it / list it.
02568   {
02569     fHierarchyNameState = kListingForInfoOnly;
02570     List(mailboxName, PR_FALSE);
02571     fHierarchyNameState = kNoOperationInProgress;
02572 
02573     // removing and freeing it as we go.
02574     TIMAPMailboxInfo *mb = NULL;
02575     int total = XP_ListCount(fListedMailboxList);
02576     do
02577     {
02578       mb = (TIMAPMailboxInfo *) XP_ListRemoveTopObject(fListedMailboxList);
02579       delete mb;
02580     } while (mb);
02581 
02582     // if the mailbox exists (it was returned from the LIST response)
02583     if (total > 0)
02584     {
02585       // Subscribe to it, if the pref says so
02586       if (TIMAPHostInfo::GetHostIsUsingSubscription(fCurrentUrl->GetUrlHost()) && m_autoSubscribeOnOpen)
02587       {
02588         XP_Bool lastReportingErrors = GetServerStateParser().GetReportingErrors();
02589         GetServerStateParser().SetReportingErrors(PR_FALSE);
02590         Subscribe(mailboxName);
02591         GetServerStateParser().SetReportingErrors(lastReportingErrors);
02592       }
02593 
02594       // Always LIST it anyway, to get it into the folder lists,
02595       // so that we can continue to perform operations on it, at least
02596       // for this session.
02597       fHierarchyNameState = kNoOperationInProgress;
02598       List(mailboxName, PR_FALSE);
02599     }
02600 
02601     // We should now be subscribed to it, and have it in our folder lists
02602     // and panes.  Even if something failed, we don't want to try this again.
02603     m_folderNeedsSubscribing = PR_FALSE;
02604 
02605   }
02606 #endif
02607 }
02608 
02609 nsresult nsImapProtocol::BeginMessageDownLoad(
02610                                               PRUint32 total_message_size, // for user, headers and body
02611                                               const char *content_type)
02612 {
02613   nsresult rv = NS_OK;
02614   char *sizeString = PR_smprintf("OPEN Size: %ld", total_message_size);
02615   Log("STREAM",sizeString,"Begin Message Download Stream");
02616   PR_Free(sizeString);
02617   //total_message_size)); 
02618   if (content_type)
02619   {
02620     m_fromHeaderSeen = PR_FALSE;
02621     if (GetServerStateParser().GetDownloadingHeaders())
02622     {
02623       // if we get multiple calls to BeginMessageDownload w/o intervening
02624       // calls to NormalEndMessageDownload or Abort, then we're just
02625       // going to fake a NormalMessageEndDownload. This will most likely 
02626       // cause an empty header to get written to the db, and the user
02627       // will have to delete the empty header themselves, which
02628       // should remove the message from the server as well.
02629       if (m_curHdrInfo)
02630         NormalMessageEndDownload();
02631       if (!m_curHdrInfo)
02632         m_hdrDownloadCache.StartNewHdr(getter_AddRefs(m_curHdrInfo));
02633       if (m_curHdrInfo)
02634         m_curHdrInfo->SetMsgSize(total_message_size);
02635       return NS_OK;
02636     }
02637     // if we have a mock channel, that means we have a channel listener who wants the
02638     // message. So set up a pipe. We'll write the messsage into one end of the pipe
02639     // and they will read it out of the other end.
02640     else if (m_channelListener)
02641     {
02642       // create a pipe to pump the message into...the output will go to whoever
02643       // is consuming the message display
02644       // we create an "infinite" pipe in case we get extremely long lines from the imap server,
02645       // and the consumer is waiting for a whole line
02646       rv = NS_NewPipe(getter_AddRefs(m_channelInputStream), getter_AddRefs(m_channelOutputStream), 4096, PR_UINT32_MAX);
02647       NS_ASSERTION(NS_SUCCEEDED(rv), "NS_NewPipe failed!");
02648     }
02649     // else, if we are saving the message to disk!
02650     else if (m_imapMessageSink /* && m_imapAction == nsIImapUrl::nsImapSaveMessageToDisk */) 
02651     {
02652       // we get here when download the inbox for offline use
02653       nsCOMPtr<nsIFileSpec> fileSpec;
02654       PRBool addDummyEnvelope = PR_TRUE;
02655       nsCOMPtr<nsIMsgMessageUrl> msgurl = do_QueryInterface(m_runningUrl);
02656       msgurl->GetMessageFile(getter_AddRefs(fileSpec));
02657       msgurl->GetAddDummyEnvelope(&addDummyEnvelope);
02658       //                m_imapMessageSink->SetupMsgWriteStream(fileSpec, addDummyEnvelope);
02659       nsXPIDLCString nativePath;
02660       //        NS_ASSERTION(fileSpec, "no fileSpec!");
02661       if (fileSpec) 
02662       {
02663         fileSpec->GetNativePath(getter_Copies(nativePath));
02664         rv = m_imapMessageSink->SetupMsgWriteStream(nativePath, addDummyEnvelope);
02665       }
02666     }
02667     if (m_imapMailFolderSink && m_runningUrl)
02668     {
02669       nsCOMPtr <nsISupports> copyState;
02670       if (m_runningUrl)
02671       {
02672         m_runningUrl->GetCopyState(getter_AddRefs(copyState));
02673         if (copyState) // only need this notification during copy
02674         {
02675           nsCOMPtr<nsIMsgMailNewsUrl> mailurl = do_QueryInterface(m_runningUrl);
02676           m_imapMailFolderSink->StartMessage(mailurl);
02677         }
02678       }
02679     }
02680     
02681   }
02682   else
02683     HandleMemoryFailure();
02684   return rv;
02685 }
02686 
02687 void
02688 nsImapProtocol::GetShouldDownloadAllHeaders(PRBool *aResult)
02689 {
02690   if (m_imapMailFolderSink)
02691     m_imapMailFolderSink->GetShouldDownloadAllHeaders(aResult);
02692 }
02693 
02694 void
02695 nsImapProtocol::GetArbitraryHeadersToDownload(char **aResult)
02696 {
02697   if (m_imapServerSink)
02698     m_imapServerSink->GetArbitraryHeaders(aResult);
02699 }
02700 
02701 void
02702 nsImapProtocol::AdjustChunkSize()
02703 {
02704   PRTime deltaTime;
02705   PRInt32 deltaInSeconds;
02706 
02707   m_endTime = PR_Now();
02708   LL_SUB(deltaTime, m_endTime, m_startTime);
02709   PRTime2Seconds(deltaTime, &deltaInSeconds);
02710   m_trackingTime = PR_FALSE;
02711   if (deltaInSeconds < 0)
02712     return;            // bogus for some reason
02713   
02714   if (deltaInSeconds <= m_tooFastTime)
02715   {
02716     m_chunkSize += m_chunkAddSize;
02717     m_chunkThreshold = m_chunkSize + (m_chunkSize / 2);
02718     // we used to have a max for the chunk size - I don't think that's needed. 
02719   }
02720   else if (deltaInSeconds <= m_idealTime)
02721     return;
02722   else 
02723   {
02724     if (m_chunkSize > m_chunkStartSize)
02725       m_chunkSize = m_chunkStartSize;
02726     else if (m_chunkSize > (m_chunkAddSize * 2))
02727       m_chunkSize -= m_chunkAddSize;
02728     m_chunkThreshold = m_chunkSize + (m_chunkSize / 2);
02729   }
02730 }
02731 
02732 // authenticated state commands 
02733 // escape any backslashes or quotes.  Backslashes are used a lot with our NT server
02734 char *nsImapProtocol::CreateEscapedMailboxName(const char *rawName)
02735 {
02736   nsCString escapedName(rawName);
02737 
02738   for (PRInt32 strIndex = 0; *rawName; strIndex++)
02739   {
02740     char currentChar = *rawName++;
02741     if ((currentChar == '\\') || (currentChar == '\"'))
02742     {
02743       escapedName.Insert('\\', strIndex++);
02744     }
02745   }
02746   return ToNewCString(escapedName);
02747 }
02748 
02749 void nsImapProtocol::SelectMailbox(const char *mailboxName)
02750 {
02751   ProgressEventFunctionUsingId (IMAP_STATUS_SELECTING_MAILBOX);
02752   IncrementCommandTagNumber();
02753   
02754   m_closeNeededBeforeSelect = PR_FALSE;   // initial value
02755   GetServerStateParser().ResetFlagInfo(0);    
02756   char *escapedName = CreateEscapedMailboxName(mailboxName);
02757   nsCString commandBuffer(GetServerCommandTag());
02758   commandBuffer.Append(" select \"");
02759   commandBuffer.Append(escapedName);
02760   commandBuffer.Append("\"" CRLF);
02761 
02762   nsMemory::Free(escapedName);
02763   nsresult res;       
02764   res = SendData(commandBuffer.get());
02765   if (NS_FAILED(res)) return;
02766   ParseIMAPandCheckForNewMail();
02767 
02768   PRInt32 numOfMessagesInFlagState = 0;
02769   nsImapAction imapAction; 
02770   m_flagState->GetNumberOfMessages(&numOfMessagesInFlagState);
02771   res = m_runningUrl->GetImapAction(&imapAction);
02772   // if we've selected a mailbox, and we're not going to do an update because of the
02773   // url type, but don't have the flags, go get them!
02774   if (GetServerStateParser().LastCommandSuccessful() && NS_SUCCEEDED(res) &&
02775     imapAction != nsIImapUrl::nsImapSelectFolder && imapAction != nsIImapUrl::nsImapExpungeFolder 
02776     && imapAction != nsIImapUrl::nsImapLiteSelectFolder &&
02777     imapAction != nsIImapUrl::nsImapDeleteAllMsgs && 
02778     ((GetServerStateParser().NumberOfMessages() != numOfMessagesInFlagState) && (numOfMessagesInFlagState == 0))) 
02779   {
02780       ProcessMailboxUpdate(PR_FALSE); 
02781   }
02782 }
02783 
02784 // Please call only with a single message ID
02785 void nsImapProtocol::Bodystructure(const char *messageId, PRBool idIsUid)
02786 {
02787   IncrementCommandTagNumber();
02788   
02789   nsCString commandString(GetServerCommandTag());
02790   if (idIsUid)
02791     commandString.Append(" UID");
02792   commandString.Append(" fetch ");
02793 
02794   commandString.Append(messageId);
02795   commandString.Append(" (BODYSTRUCTURE)" CRLF);
02796             
02797   nsresult rv = SendData(commandString.get());
02798   if (NS_SUCCEEDED(rv))
02799       ParseIMAPandCheckForNewMail(commandString.get());
02800 }
02801 
02802 void nsImapProtocol::PipelinedFetchMessageParts(const char *uid, nsIMAPMessagePartIDArray *parts)
02803 {
02804   // assumes no chunking
02805 
02806   // build up a string to fetch
02807   nsCString stringToFetch, what;
02808   int32 currentPartNum = 0;
02809   while ((parts->GetNumParts() > currentPartNum) && !DeathSignalReceived())
02810   {
02811     nsIMAPMessagePartID *currentPart = parts->GetPart(currentPartNum);
02812     if (currentPart)
02813     {
02814       // Do things here depending on the type of message part
02815       // Append it to the fetch string
02816       if (currentPartNum > 0)
02817         stringToFetch.Append(" ");
02818 
02819       switch (currentPart->GetFields())
02820       {
02821       case kMIMEHeader:
02822         what = "BODY.PEEK[";
02823         what.Append(currentPart->GetPartNumberString());
02824         what.Append(".MIME]");
02825         stringToFetch.Append(what);
02826         break;
02827       case kRFC822HeadersOnly:
02828         if (currentPart->GetPartNumberString())
02829         {
02830           what = "BODY.PEEK[";
02831           what.Append(currentPart->GetPartNumberString());
02832           what.Append(".HEADER]");
02833           stringToFetch.Append(what);
02834         }
02835         else
02836         {
02837           // headers for the top-level message
02838           stringToFetch.Append("BODY.PEEK[HEADER]");
02839         }
02840         break;
02841       default:
02842         NS_ASSERTION(PR_FALSE, "we should only be pipelining MIME headers and Message headers");
02843         break;
02844       }
02845 
02846     }
02847     currentPartNum++;
02848   }
02849 
02850   // Run the single, pipelined fetch command
02851   if ((parts->GetNumParts() > 0) && !DeathSignalReceived() && !GetPseudoInterrupted() && stringToFetch.get())
02852   {
02853       IncrementCommandTagNumber();
02854 
02855     nsCString commandString(GetServerCommandTag()); 
02856     commandString.Append(" UID fetch ");
02857     commandString.Append(uid, 10);
02858     commandString.Append(" (");
02859     commandString.Append(stringToFetch);
02860     commandString.Append(")" CRLF);
02861     nsresult rv = SendData(commandString.get());
02862         if (NS_SUCCEEDED(rv))
02863             ParseIMAPandCheckForNewMail(commandString.get());
02864   }
02865 }
02866 
02867 
02868 void nsImapProtocol::FetchMsgAttribute(const char * messageIds, const char *attribute)
02869 {
02870     IncrementCommandTagNumber();
02871     
02872     nsCAutoString commandString (GetServerCommandTag());
02873     commandString.Append(" UID fetch ");
02874     commandString.Append(messageIds);
02875     commandString.Append(" (");
02876     commandString.Append(attribute);
02877     commandString.Append(")"CRLF);
02878     nsresult rv = SendData(commandString.get());
02879       
02880     if (NS_SUCCEEDED(rv))
02881        ParseIMAPandCheckForNewMail(commandString.get());
02882     GetServerStateParser().SetFetchingFlags(PR_FALSE);
02883     GetServerStateParser().SetFetchingEverythingRFC822(PR_FALSE); // always clear this flag after every fetch....
02884 }
02885 
02886 // this routine is used to fetch a message or messages, or headers for a
02887 // message...
02888 
02889 void nsImapProtocol::FallbackToFetchWholeMsg(const char *messageId, PRUint32 messageSize)
02890 {
02891   if (m_imapMessageSink && m_runningUrl)
02892   {
02893     PRBool shouldStoreMsgOffline;
02894     m_runningUrl->GetShouldStoreMsgOffline(&shouldStoreMsgOffline);
02895     if (shouldStoreMsgOffline)
02896       m_imapMessageSink->SetNotifyDownloadedLines(PR_TRUE);
02897   }
02898   FetchTryChunking(messageId, kEveryThingRFC822, PR_TRUE, NULL, messageSize, PR_TRUE);
02899 }
02900 
02901 void
02902 nsImapProtocol::FetchMessage(const char * messageIds, 
02903                              nsIMAPeFetchFields whatToFetch,
02904                              PRBool idIsUid,
02905                              PRUint32 startByte, PRUint32 endByte,
02906                              char *part)
02907 {
02908   IncrementCommandTagNumber();
02909   
02910   nsCString commandString;
02911   if (idIsUid)
02912     commandString = "%s UID fetch";
02913   else
02914     commandString = "%s fetch";
02915   
02916   switch (whatToFetch) {
02917   case kEveryThingRFC822:
02918     m_flagChangeCount++;
02919     GetServerStateParser().SetFetchingEverythingRFC822(PR_TRUE);
02920     if (m_trackingTime)
02921       AdjustChunkSize();      // we started another segment
02922     m_startTime = PR_Now();     // save start of download time
02923     m_trackingTime = PR_TRUE;
02924     if (GetServerStateParser().ServerHasIMAP4Rev1Capability())
02925     {
02926       if (GetServerStateParser().GetCapabilityFlag() & kHasXSenderCapability)
02927         commandString.Append(" %s (XSENDER UID RFC822.SIZE BODY[]");
02928       else
02929         commandString.Append(" %s (UID RFC822.SIZE BODY[]");
02930     }
02931     else
02932     {
02933       if (GetServerStateParser().GetCapabilityFlag() & kHasXSenderCapability)
02934         commandString.Append(" %s (XSENDER UID RFC822.SIZE RFC822");
02935       else
02936         commandString.Append(" %s (UID RFC822.SIZE RFC822");
02937     }
02938     if (endByte > 0)
02939     {
02940       // if we are retrieving chunks
02941       char *byterangeString = PR_smprintf("<%ld.%ld>",startByte,endByte);
02942       if (byterangeString)
02943       {
02944         commandString.Append(byterangeString);
02945         PR_Free(byterangeString);
02946       }
02947     }
02948     commandString.Append(")");
02949     
02950     break;
02951     
02952   case kEveryThingRFC822Peek:
02953     {
02954       const char *formatString = "";
02955       PRUint32 server_capabilityFlags = GetServerStateParser().GetCapabilityFlag();
02956       
02957       GetServerStateParser().SetFetchingEverythingRFC822(PR_TRUE);
02958       if (server_capabilityFlags & kIMAP4rev1Capability)
02959       {
02960         // use body[].peek since rfc822.peek is not in IMAP4rev1
02961         if (server_capabilityFlags & kHasXSenderCapability)
02962           formatString = " %s (XSENDER UID RFC822.SIZE BODY.PEEK[]";
02963         else
02964           formatString = " %s (UID RFC822.SIZE BODY.PEEK[]";
02965       }
02966       else
02967       {
02968         if (server_capabilityFlags & kHasXSenderCapability)
02969           formatString = " %s (XSENDER UID RFC822.SIZE RFC822.peek";
02970         else
02971           formatString = " %s (UID RFC822.SIZE RFC822.peek";
02972       }
02973       
02974       commandString.Append(formatString);
02975       if (endByte > 0)
02976       {
02977         // if we are retrieving chunks
02978         char *byterangeString = PR_smprintf("<%ld.%ld>",startByte,endByte);
02979         if (byterangeString)
02980         {
02981           commandString.Append(byterangeString);
02982           PR_Free(byterangeString);
02983         }
02984       }
02985       commandString.Append(")");
02986     }
02987     break;
02988   case kHeadersRFC822andUid:
02989     if (GetServerStateParser().ServerHasIMAP4Rev1Capability())
02990     {
02991       PRUint32 server_capabilityFlags = GetServerStateParser().GetCapabilityFlag();
02992       PRBool aolImapServer = ((server_capabilityFlags & kAOLImapCapability) != 0);
02993       PRBool downloadAllHeaders = PR_FALSE; 
02994       // checks if we're filtering on "any header" or running a spam filter requiring all headers
02995       GetShouldDownloadAllHeaders(&downloadAllHeaders); 
02996       
02997       if (!downloadAllHeaders)  // if it's ok -- no filters on any header, etc.
02998       {
02999         char *headersToDL = nsnull;
03000         char *what = nsnull;
03001         const char *dbHeaders = (gUseEnvelopeCmd) ? IMAP_DB_HEADERS : IMAP_ENV_AND_DB_HEADERS;
03002         nsXPIDLCString arbitraryHeaders;
03003         GetArbitraryHeadersToDownload(getter_Copies(arbitraryHeaders));
03004         for (PRInt32 i = 0; i < gCustomDBHeaders.Count(); i++)
03005         {
03006           if (arbitraryHeaders.Find(* (gCustomDBHeaders[i]), PR_TRUE) == kNotFound)
03007           {
03008             if (!arbitraryHeaders.IsEmpty())
03009               arbitraryHeaders.Append(' ');
03010             arbitraryHeaders.Append(gCustomDBHeaders[i]->get());
03011           }
03012         }   
03013         if (arbitraryHeaders.IsEmpty())
03014           headersToDL = nsCRT::strdup(dbHeaders);
03015         else
03016           headersToDL = PR_smprintf("%s %s",dbHeaders, arbitraryHeaders.get());
03017         
03018         if (gUseEnvelopeCmd)
03019           what = PR_smprintf(" ENVELOPE BODY.PEEK[HEADER.FIELDS (%s)])", headersToDL);
03020         else
03021           what = PR_smprintf(" BODY.PEEK[HEADER.FIELDS (%s)])",headersToDL);
03022         nsCRT::free(headersToDL);
03023         if (what)
03024         {
03025           commandString.Append(" %s (UID ");
03026           if (aolImapServer)
03027             commandString.Append(" XAOL.SIZE") ;
03028           else
03029             commandString.Append("RFC822.SIZE");
03030           commandString.Append(" FLAGS");
03031           commandString.Append(what);
03032           PR_Free(what);
03033         }
03034         else 
03035         {
03036           commandString.Append(" %s (UID RFC822.SIZE BODY.PEEK[HEADER] FLAGS)");
03037         }
03038       }
03039       else
03040         commandString.Append(" %s (UID RFC822.SIZE BODY.PEEK[HEADER] FLAGS)");
03041     }
03042     else
03043       commandString.Append(" %s (UID RFC822.SIZE RFC822.HEADER FLAGS)");
03044     break;
03045   case kUid:
03046     commandString.Append(" %s (UID)");
03047     break;
03048   case kFlags:
03049     GetServerStateParser().SetFetchingFlags(PR_TRUE);
03050     commandString.Append(" %s (FLAGS)");
03051     break;
03052   case kRFC822Size:
03053     commandString.Append(" %s (RFC822.SIZE)");
03054     break;
03055   case kBodyStart:
03056     {
03057       PRInt32 numBytesToFetch;
03058       m_runningUrl->GetNumBytesToFetch(&numBytesToFetch);
03059 
03060       commandString.Append(" %s (UID BODY.PEEK[HEADER.FIELDS (Content-Type Content-Transfer-Encoding)] BODY.PEEK[TEXT]<0.");
03061       commandString.AppendInt(numBytesToFetch);
03062       commandString.Append(">)");
03063     }
03064     break;
03065   case kRFC822HeadersOnly:
03066     if (GetServerStateParser().ServerHasIMAP4Rev1Capability())
03067     {
03068       if (part)
03069       {
03070         commandString.Append(" %s (BODY[");
03071         char *what = PR_smprintf("%s.HEADER])", part);
03072         if (what)
03073         {
03074           commandString.Append(what);
03075           PR_Free(what);
03076         }
03077         else
03078           HandleMemoryFailure();
03079       }
03080       else
03081       {
03082         // headers for the top-level message
03083         commandString.Append(" %s (BODY[HEADER])");
03084       }
03085     }
03086     else
03087       commandString.Append(" %s (RFC822.HEADER)");
03088     break;
03089   case kMIMEPart:
03090     commandString.Append(" %s (BODY.PEEK[%s]");
03091     if (endByte > 0)
03092     {
03093       // if we are retrieving chunks
03094       char *byterangeString = PR_smprintf("<%ld.%ld>",startByte,endByte);
03095       if (byterangeString)
03096       {
03097         commandString.Append(byterangeString);
03098         PR_Free(byterangeString);
03099       }
03100     }
03101     commandString.Append(")");
03102     break;
03103   case kMIMEHeader:
03104     commandString.Append(" %s (BODY[%s.MIME])");
03105     break;
03106   };
03107   
03108   commandString.Append(CRLF);
03109   
03110   // since messageIds can be infinitely long, use a dynamic buffer rather than the fixed one
03111   const char *commandTag = GetServerCommandTag();
03112   int protocolStringSize = commandString.Length() + strlen(messageIds) + PL_strlen(commandTag) + 1 +
03113     (part ? PL_strlen(part) : 0);
03114   char *protocolString = (char *) PR_CALLOC( protocolStringSize );
03115   
03116   if (protocolString)
03117   {
03118     char *cCommandStr = ToNewCString(commandString);
03119     if ((whatToFetch == kMIMEPart) ||
03120       (whatToFetch == kMIMEHeader))
03121     {
03122       PR_snprintf(protocolString,                                      // string to create
03123         protocolStringSize,                                      // max size
03124         cCommandStr,                                   // format string
03125         commandTag,                          // command tag
03126         messageIds,
03127         part);
03128     }
03129     else
03130     {
03131       PR_snprintf(protocolString,                                      // string to create
03132         protocolStringSize,                                      // max size
03133         cCommandStr,                                   // format string
03134         commandTag,                          // command tag
03135         messageIds);
03136     }
03137     
03138     nsresult rv = SendData(protocolString);
03139     
03140     nsMemory::Free(cCommandStr);
03141     if (NS_SUCCEEDED(rv))
03142       ParseIMAPandCheckForNewMail(protocolString);
03143     PR_Free(protocolString);
03144     GetServerStateParser().SetFetchingFlags(PR_FALSE);
03145     GetServerStateParser().SetFetchingEverythingRFC822(PR_FALSE); // always clear this flag after every fetch....
03146     if (GetServerStateParser().LastCommandSuccessful() && CheckNeeded())
03147       Check();
03148   }
03149   else
03150     HandleMemoryFailure();
03151 }
03152 
03153 void nsImapProtocol::FetchTryChunking(const char * messageIds,
03154                                       nsIMAPeFetchFields whatToFetch,
03155                                       PRBool idIsUid,
03156                                       char *part,
03157                                       PRUint32 downloadSize,
03158                                       PRBool tryChunking)
03159 {
03160   GetServerStateParser().SetTotalDownloadSize(downloadSize);
03161   if (m_fetchByChunks && tryChunking &&
03162         GetServerStateParser().ServerHasIMAP4Rev1Capability() &&
03163     (downloadSize > (PRUint32) m_chunkThreshold))
03164   {
03165     PRUint32 startByte = 0;
03166     GetServerStateParser().ClearLastFetchChunkReceived();
03167     while (!DeathSignalReceived() && !GetPseudoInterrupted() && 
03168       !GetServerStateParser().GetLastFetchChunkReceived() &&
03169       GetServerStateParser().ContinueParse())
03170     {
03171       PRUint32 sizeToFetch = startByte + m_chunkSize > downloadSize ?
03172         downloadSize - startByte : m_chunkSize;
03173       FetchMessage(messageIds, 
03174              whatToFetch,
03175              idIsUid,
03176              startByte, sizeToFetch,
03177              part);
03178       startByte += sizeToFetch;
03179       // adjust the message size based on rfc822 size, if we're fetching
03180       // the whole message, and not just a mime part.
03181       if (whatToFetch != kMIMEPart)
03182       {
03183         PRUint32 newMsgSize = GetServerStateParser().SizeOfMostRecentMessage();
03184         if (newMsgSize > 0 && newMsgSize != downloadSize)
03185           downloadSize = newMsgSize;
03186       }
03187     }
03188 
03189     // Only abort the stream if this is a normal message download
03190     // Otherwise, let the body shell abort the stream.
03191     if ((whatToFetch == kEveryThingRFC822)
03192       &&
03193       ((startByte > 0 && (startByte < downloadSize) &&
03194       (DeathSignalReceived() || GetPseudoInterrupted())) ||
03195       !GetServerStateParser().ContinueParse()))
03196     {
03197       AbortMessageDownLoad();
03198       PseudoInterrupt(PR_FALSE);
03199     }
03200   }
03201   else
03202   {
03203     // small message, or (we're not chunking and not doing bodystructure),
03204     // or the server is not rev1.
03205     // Just fetch the whole thing.
03206     FetchMessage(messageIds, whatToFetch,idIsUid, 0, 0, part);
03207   }
03208 }
03209 
03210 
03211 void nsImapProtocol::PipelinedFetchMessageParts(nsCString &uid, nsIMAPMessagePartIDArray *parts)
03212 {
03213   // assumes no chunking
03214 
03215   // build up a string to fetch
03216   nsCString stringToFetch;
03217   nsCString what;
03218 
03219   int32 currentPartNum = 0;
03220   while ((parts->GetNumParts() > currentPartNum) && !DeathSignalReceived())
03221   {
03222     nsIMAPMessagePartID *currentPart = parts->GetPart(currentPartNum);
03223     if (currentPart)
03224     {
03225       // Do things here depending on the type of message part
03226       // Append it to the fetch string
03227       if (currentPartNum > 0)
03228         stringToFetch += " ";
03229 
03230       switch (currentPart->GetFields())
03231       {
03232       case kMIMEHeader:
03233         what = "BODY.PEEK[";
03234         what += currentPart->GetPartNumberString();
03235         what += ".MIME]";
03236         stringToFetch += what;
03237         break;
03238       case kRFC822HeadersOnly:
03239         if (currentPart->GetPartNumberString())
03240         {
03241           what = "BODY.PEEK[";
03242           what += currentPart->GetPartNumberString();
03243           what += ".HEADER]";
03244           stringToFetch += what;
03245         }
03246         else
03247         {
03248           // headers for the top-level message
03249           stringToFetch += "BODY.PEEK[HEADER]";
03250         }
03251         break;
03252       default:
03253         NS_ASSERTION(PR_FALSE, "we should only be pipelining MIME headers and Message headers");
03254         break;
03255       }
03256 
03257     }
03258     currentPartNum++;
03259   }
03260 
03261   // Run the single, pipelined fetch command
03262   if ((parts->GetNumParts() > 0) && !DeathSignalReceived() && !GetPseudoInterrupted() && stringToFetch.get())
03263   {
03264       IncrementCommandTagNumber();
03265 
03266     char *commandString = PR_smprintf("%s UID fetch %s (%s)%s",
03267                                           GetServerCommandTag(), uid.get(),
03268                                           stringToFetch.get(), CRLF);
03269 
03270     if (commandString)
03271     {
03272       nsresult rv = SendData(commandString);
03273             if (NS_SUCCEEDED(rv))
03274                 ParseIMAPandCheckForNewMail(commandString);
03275       PR_Free(commandString);
03276     }
03277     else
03278       HandleMemoryFailure();
03279   }
03280 }
03281 
03282 
03283 void
03284 nsImapProtocol::PostLineDownLoadEvent(msg_line_info *downloadLineDontDelete)
03285 {
03286     NS_ASSERTION(downloadLineDontDelete, 
03287                  "Oops... null msg line info not good");
03288 
03289   if (!GetServerStateParser().GetDownloadingHeaders())
03290   {
03291     PRBool echoLineToMessageSink = PR_TRUE;
03292     // if we have a channel listener, then just spool the message
03293     // directly to the listener
03294     if (m_channelListener)
03295     {
03296       PRUint32 count = 0;
03297       const char * line = downloadLineDontDelete->adoptedMessageLine;
03298       if (m_channelOutputStream)
03299       {
03300         nsresult rv = m_channelOutputStream->Write(line, PL_strlen(line), &count);
03301         if (NS_SUCCEEDED(rv))
03302         {
03303           nsCOMPtr<nsIRequest> request = do_QueryInterface(m_mockChannel);
03304           m_channelListener->OnDataAvailable(request, m_channelContext, m_channelInputStream, 0, count); 
03305         }
03306           // here is where we should echo the line to the local folder copy of an online message
03307       }
03308       if (m_imapMessageSink)
03309         m_imapMessageSink->GetNotifyDownloadedLines(&echoLineToMessageSink);
03310     }
03311     if (m_imapMessageSink && downloadLineDontDelete && echoLineToMessageSink && !GetPseudoInterrupted())
03312     {
03313       m_imapMessageSink->ParseAdoptedMsgLine(downloadLineDontDelete->adoptedMessageLine, 
03314                                              downloadLineDontDelete->uidOfMessage);
03315     }
03316   }
03317   // ***** We need to handle the pseudo interrupt here *****
03318 }
03319 
03320 // Handle a line seen by the parser.
03321 // * The argument |lineCopy| must be nsnull or should contain the same string as
03322 //   |line|.  |lineCopy| will be modified.
03323 // * A line may be passed by parts, e.g., "part1 part2\r\n" may be passed as
03324 //     HandleMessageDownLoadLine("part 1 ", 1);
03325 //     HandleMessageDownLoadLine("part 2\r\n", 0); 
03326 //   However, it is assumed that a CRLF or a CRCRLF is never split (i.e., this is
03327 //   ensured *before* invoking this method).
03328 void nsImapProtocol::HandleMessageDownLoadLine(const char *line, PRBool isPartialLine,
03329                                                char *lineCopy)
03330 {
03331   NS_PRECONDITION(lineCopy == nsnull || !PL_strcmp(line, lineCopy),
03332                   "line and lineCopy must contain the same string");    
03333   const char *messageLine = line;
03334   PRUint32 lineLength = strlen(messageLine);
03335   const char *cEndOfLine = messageLine + lineLength;
03336   char *localMessageLine = nsnull;
03337 
03338   // If we obtain a partial line (due to fetching by chunks), we do not
03339   // add/modify the end-of-line terminator.
03340   if (!isPartialLine)
03341   {
03342     // Change this line to native line termination, duplicate if necessary.
03343     // Do not assume that the line really ends in CRLF
03344     // to start with, even though it is supposed to be RFC822
03345 
03346     // note: usually canonicalLineEnding==FALSE 
03347     PRBool canonicalLineEnding = PR_FALSE;
03348     nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningUrl);
03349 
03350     if (m_imapAction == nsIImapUrl::nsImapSaveMessageToDisk && msgUrl)
03351       msgUrl->GetCanonicalLineEnding(&canonicalLineEnding);
03352   
03353     NS_PRECONDITION(MSG_LINEBREAK_LEN == 1 ||
03354                     MSG_LINEBREAK_LEN == 2 && !PL_strcmp(CRLF, MSG_LINEBREAK),
03355                     "violated assumptions on MSG_LINEBREAK");
03356     if (MSG_LINEBREAK_LEN == 1 && !canonicalLineEnding)
03357     {
03358       PRBool lineEndsWithCRorLF = lineLength >= 1 &&
03359         (cEndOfLine[-1] == nsCRT::CR || cEndOfLine[-1] == nsCRT::LF);
03360       char *endOfLine;
03361       if (lineCopy && lineEndsWithCRorLF)  // true for most lines
03362       {
03363         endOfLine = lineCopy + lineLength;
03364         messageLine = lineCopy;
03365       }
03366       else
03367       {
03368         // leave enough room for one more char, MSG_LINEBREAK[0]
03369         localMessageLine = (char *) PR_MALLOC(lineLength + 2);
03370         if (!localMessageLine) // memory failure
03371           return;
03372         PL_strcpy(localMessageLine, line);
03373         endOfLine = localMessageLine + lineLength;
03374         messageLine = localMessageLine;
03375       }
03376       
03377       if (lineLength >= 2 &&
03378         endOfLine[-2] == nsCRT::CR &&
03379         endOfLine[-1] == nsCRT::LF)
03380       {
03381         if(lineLength>=3 && endOfLine[-3] == nsCRT::CR) // CRCRLF
03382         {
03383           endOfLine--;
03384           lineLength--;
03385         }
03386         /* CRLF -> CR or LF */
03387         endOfLine[-2] = MSG_LINEBREAK[0];
03388         endOfLine[-1] = '\0';
03389         lineLength--;
03390       }
03391       else if (lineLength >= 1 &&
03392         ((endOfLine[-1] == nsCRT::CR) || (endOfLine[-1] == nsCRT::LF)))
03393       {
03394         /* CR -> LF or LF -> CR */
03395         endOfLine[-1] = MSG_LINEBREAK[0];
03396       }
03397       else // no eol characters at all
03398       {
03399         endOfLine[0] = MSG_LINEBREAK[0]; // CR or LF
03400         endOfLine[1] = '\0';
03401         lineLength++;
03402       }
03403     }
03404     else  // enforce canonical CRLF linebreaks
03405     {
03406       if (lineLength==0 || lineLength == 1 && cEndOfLine[-1] == nsCRT::LF)
03407       {
03408         messageLine = CRLF;
03409         lineLength = 2;
03410       }
03411       else if (cEndOfLine[-1] != nsCRT::LF || cEndOfLine[-2] != nsCRT::CR ||
03412                lineLength >=3 && cEndOfLine[-3] == nsCRT::CR)
03413       {
03414         // The line does not end in CRLF (or it ends in CRCRLF).
03415         // Copy line and leave enough room for two more chars (CR and LF).
03416         localMessageLine = (char *) PR_MALLOC(lineLength + 3);
03417         if (!localMessageLine) // memory failure
03418             return;
03419         PL_strcpy(localMessageLine, line);
03420         char *endOfLine = localMessageLine + lineLength;
03421         messageLine = localMessageLine;
03422 
03423         if (lineLength>=3 && endOfLine[-1] == nsCRT::LF && 
03424             endOfLine[-2] == nsCRT::CR)
03425         {
03426           // CRCRLF -> CRLF
03427           endOfLine[-2] = nsCRT::LF;
03428           endOfLine[-1] = '\0';
03429           lineLength--;
03430         }
03431         else if ((endOfLine[-1] == nsCRT::CR) || (endOfLine[-1] == nsCRT::LF))
03432         {
03433           // LF -> CRLF or CR -> CRLF 
03434           endOfLine[-1] = nsCRT::CR;
03435           endOfLine[0]  = nsCRT::LF;
03436           endOfLine[1]  = '\0';
03437           lineLength++;
03438         }
03439         else // no eol characters at all
03440         {
03441           endOfLine[0] = nsCRT::CR;
03442           endOfLine[1] = nsCRT::LF;
03443           endOfLine[2] = '\0';
03444           lineLength += 2;
03445         }
03446       }
03447     }
03448   }
03449   NS_ASSERTION(lineLength == PL_strlen(messageLine), "lineLength not accurate");
03450 
03451   // check if sender obtained via XSENDER server extension matches "From:" field
03452   const char *xSenderInfo = GetServerStateParser().GetXSenderInfo();
03453   if (xSenderInfo && *xSenderInfo && !m_fromHeaderSeen)
03454   {
03455     if (!PL_strncmp("From: ", messageLine, 6))
03456     {
03457       m_fromHeaderSeen = PR_TRUE;
03458       if (PL_strstr(messageLine, xSenderInfo) != NULL)
03459           // Adding a X-Mozilla-Status line here is not very elegant but it
03460           // works.  Another X-Mozilla-Status line is added to the message when
03461           // downloading to a local folder; this new line will also contain the
03462           // 'authed' flag we are adding here.  (If the message is again
03463           // uploaded to the server, this flag is lost.)
03464           // 0x0200 == MSG_FLAG_SENDER_AUTHED
03465           HandleMessageDownLoadLine("X-Mozilla-Status: 0200\r\n", PR_FALSE);
03466       GetServerStateParser().FreeXSenderInfo();
03467     }
03468   }
03469   
03470   if (GetServerStateParser().GetDownloadingHeaders())
03471   {
03472     if (!m_curHdrInfo)
03473       BeginMessageDownLoad(GetServerStateParser().SizeOfMostRecentMessage(), MESSAGE_RFC822);
03474     if (m_curHdrInfo)
03475       m_curHdrInfo->CacheLine(messageLine, GetServerStateParser().CurrentResponseUID());
03476     PR_Free(localMessageLine);
03477     return;
03478   }
03479   // if this line is for a different message, or the incoming line is too big
03480   if (((m_downloadLineCache.CurrentUID() != GetServerStateParser().CurrentResponseUID()) && !m_downloadLineCache.CacheEmpty()) ||
03481     (m_downloadLineCache.SpaceAvailable() < lineLength + 1) )
03482   {
03483     if (!m_downloadLineCache.CacheEmpty())
03484     {
03485       msg_line_info *downloadLineDontDelete = m_downloadLineCache.GetCurrentLineInfo();
03486       // post ODA lines....
03487       PostLineDownLoadEvent(downloadLineDontDelete);
03488     }
03489     m_downloadLineCache.ResetCache();
03490   }
03491   
03492   // so now the cache is flushed, but this string might still be to big
03493   if (m_downloadLineCache.SpaceAvailable() < lineLength + 1)
03494   {
03495     // has to be dynamic to pass to other win16 thread
03496     msg_line_info *downLoadInfo = (msg_line_info *) PR_CALLOC(sizeof(msg_line_info));
03497     if (downLoadInfo)
03498     {
03499       downLoadInfo->adoptedMessageLine = messageLine;
03500       downLoadInfo->uidOfMessage = GetServerStateParser().CurrentResponseUID();
03501       PostLineDownLoadEvent(downLoadInfo);
03502       PR_Free(downLoadInfo);
03503     }
03504   }
03505   else
03506     m_downloadLineCache.CacheLine(messageLine, GetServerStateParser().CurrentResponseUID());
03507 
03508   PR_Free(localMessageLine);
03509 }
03510 
03511 void nsImapProtocol::NormalMessageEndDownload()
03512 {
03513   Log("STREAM", "CLOSE", "Normal Message End Download Stream");
03514 
03515   if (m_trackingTime)
03516     AdjustChunkSize();
03517   if (m_imapMailFolderSink && m_curHdrInfo && GetServerStateParser().GetDownloadingHeaders())
03518   {
03519     m_curHdrInfo->SetMsgSize(GetServerStateParser().SizeOfMostRecentMessage());
03520     m_curHdrInfo->SetMsgUid(GetServerStateParser().CurrentResponseUID());
03521     m_hdrDownloadCache.FinishCurrentHdr();
03522     PRInt32 numHdrsCached;
03523     m_hdrDownloadCache.GetNumHeaders(&numHdrsCached);
03524     if (numHdrsCached == kNumHdrsToXfer)
03525     {
03526       m_imapMailFolderSink->ParseMsgHdrs(this, &m_hdrDownloadCache);
03527       m_hdrDownloadCache.ResetAll();
03528     }
03529   }
03530   if (!m_downloadLineCache.CacheEmpty())
03531   {
03532       msg_line_info *downloadLineDontDelete = m_downloadLineCache.GetCurrentLineInfo();
03533       PostLineDownLoadEvent(downloadLineDontDelete);
03534       m_downloadLineCache.ResetCache();
03535     }
03536 
03537   if (!GetServerStateParser().GetDownloadingHeaders())
03538   {
03539     if (m_channelListener)
03540     {
03541 //      PRUint32 inlength = 0;
03542 //      m_channelInputStream->Available(&inlength);
03543       //if (inlength > 0) // broadcast our batched up ODA changes
03544 //        m_channelListener->OnDataAvailable(m_mockChannel, m_channelContext, m_channelInputStream, 0, inlength);   
03545     }
03546 
03547     // need to know if we're downloading for display or not. We'll use action == nsImapMsgFetch for now 
03548     nsImapAction imapAction = nsIImapUrl::nsImapSelectFolder; // just set it to some legal value
03549     if (m_runningUrl)
03550       m_runningUrl->GetImapAction(&imapAction);
03551 
03552     if (m_imapMessageSink)
03553       m_imapMessageSink->NormalEndMsgWriteStream(m_downloadLineCache.CurrentUID(), imapAction == nsIImapUrl::nsImapMsgFetch, m_runningUrl);
03554   
03555     if (m_runningUrl && m_imapMailFolderSink)
03556     {
03557       nsCOMPtr <nsISupports> copyState;
03558       m_runningUrl->GetCopyState(getter_AddRefs(copyState));
03559       if (copyState) // only need this notification during copy
03560       {
03561         nsCOMPtr<nsIMsgMailNewsUrl> mailUrl (do_QueryInterface(m_runningUrl));
03562         m_imapMailFolderSink->EndMessage(mailUrl, m_downloadLineCache.CurrentUID());
03563       }
03564     }
03565   }
03566   m_curHdrInfo = nsnull;
03567 }
03568 
03569 void nsImapProtocol::AbortMessageDownLoad()
03570 {
03571   Log("STREAM", "CLOSE", "Abort Message  Download Stream");
03572 
03573   if (m_trackingTime)
03574     AdjustChunkSize();
03575   if (!m_downloadLineCache.CacheEmpty())
03576   {
03577       msg_line_info *downloadLineDontDelete = m_downloadLineCache.GetCurrentLineInfo();
03578       PostLineDownLoadEvent(downloadLineDontDelete);
03579       m_downloadLineCache.ResetCache();
03580     }
03581 
03582   if (GetServerStateParser().GetDownloadingHeaders())
03583   {
03584     if (m_imapMailFolderSink)
03585       m_imapMailFolderSink->AbortHeaderParseStream(this);
03586   }
03587   else if (m_imapMessageSink)
03588         m_imapMessageSink->AbortMsgWriteStream();
03589 
03590   m_curHdrInfo = nsnull;
03591 }
03592 
03593 
03594 void nsImapProtocol::ProcessMailboxUpdate(PRBool handlePossibleUndo)
03595 {
03596   if (DeathSignalReceived())
03597     return;
03598 
03599   // Update quota information
03600   if (!DeathSignalReceived())
03601   {
03602     char *boxName;
03603     GetSelectedMailboxName(&boxName);
03604     GetQuotaDataIfSupported(boxName);
03605     PR_Free(boxName);
03606   }
03607 
03608   // fetch the flags and uids of all existing messages or new ones
03609   if (!DeathSignalReceived() && GetServerStateParser().NumberOfMessages())
03610   {
03611     if (handlePossibleUndo)
03612     {
03613       // undo any delete flags we may have asked to
03614       nsXPIDLCString undoIdsStr;
03615       nsCAutoString undoIds;
03616     
03617       GetCurrentUrl()->CreateListOfMessageIdsString(getter_Copies(undoIdsStr));
03618       undoIds.Assign(undoIdsStr);
03619       if (!undoIds.IsEmpty())
03620       {
03621         char firstChar = (char) undoIds.CharAt(0);
03622         undoIds.Cut(0, 1);  // remove first character
03623         // if this string started with a '-', then this is an undo of a delete
03624         // if its a '+' its a redo
03625         if (firstChar == '-')
03626           Store(undoIds.get(), "-FLAGS (\\Deleted)", PR_TRUE);  // most servers will fail silently on a failure, deal with it?
03627         else  if (firstChar == '+')
03628           Store(undoIds.get(), "+FLAGS (\\Deleted)", PR_TRUE);  // most servers will fail silently on a failure, deal with it?
03629         else 
03630           NS_ASSERTION(PR_FALSE, "bogus undo Id's");
03631       }
03632     }
03633       
03634     // make the parser record these flags
03635     nsCString fetchStr;
03636     PRInt32 added = 0, deleted = 0;
03637 
03638     m_flagState->GetNumberOfMessages(&added);
03639     deleted = m_flagState->GetNumberOfDeletedMessages();
03640 
03641     if (!added || (added == deleted))
03642     {
03643       nsCString idsToFetch("1:*");
03644       FetchMessage(idsToFetch.get(), kFlags, PR_TRUE);  // id string shows uids
03645       // lets see if we should expunge during a full sync of flags.
03646       if (!DeathSignalReceived()) // only expunge if not reading messages manually and before fetching new
03647       {
03648         // ### TODO read gExpungeThreshhold from prefs. Don't do expunge when we 
03649         // are lite selecting folder because we could be doing undo
03650         if ((m_flagState->GetNumberOfDeletedMessages() >= 20/* gExpungeThreshold */)  
03651                  && !GetShowDeletedMessages() && m_imapAction != nsIImapUrl::nsImapLiteSelectFolder)
03652           Expunge();  
03653       }
03654 
03655     }
03656     else 
03657     {
03658       AppendUid(fetchStr, GetServerStateParser().HighestRecordedUID() + 1);
03659       fetchStr.Append(":*");
03660 
03661       // sprintf(fetchStr, "%ld:*", GetServerStateParser().HighestRecordedUID() + 1);
03662       FetchMessage(fetchStr.get(), kFlags, PR_TRUE);      // only new messages please
03663     }
03664   }
03665   else if (!DeathSignalReceived())
03666     GetServerStateParser().ResetFlagInfo(0);
03667 
03668   if (!DeathSignalReceived())
03669   {
03670     nsImapAction imapAction; 
03671     nsresult res = m_runningUrl->GetImapAction(&imapAction);
03672     if (NS_SUCCEEDED(res) && imapAction == nsIImapUrl::nsImapLiteSelectFolder)
03673       return;
03674   }
03675     
03676   nsImapMailboxSpec *new_spec = GetServerStateParser().CreateCurrentMailboxSpec();
03677   if (new_spec && !DeathSignalReceived())
03678   {
03679     if (!DeathSignalReceived())
03680     {
03681       nsImapAction imapAction; 
03682       nsresult res = m_runningUrl->GetImapAction(&imapAction);
03683       if (NS_SUCCEEDED(res) && imapAction == nsIImapUrl::nsImapExpungeFolder)
03684         new_spec->box_flags |= kJustExpunged;
03685       PR_EnterMonitor(m_waitForBodyIdsMonitor);
03686       UpdatedMailboxSpec(new_spec);
03687     }
03688   }
03689   else if (!new_spec)
03690     HandleMemoryFailure();
03691   
03692   // Block until libmsg decides whether to download headers or not.
03693   PRUint32 *msgIdList = nsnull;
03694   PRUint32 msgCount = 0;
03695   
03696   if (!DeathSignalReceived())
03697   {
03698     WaitForPotentialListOfMsgsToFetch(&msgIdList, msgCount);
03699 
03700     if (new_spec)
03701       PR_ExitMonitor(m_waitForBodyIdsMonitor);
03702 
03703     if (msgIdList && !DeathSignalReceived() && GetServerStateParser().LastCommandSuccessful())
03704     {
03705       FolderHeaderDump(msgIdList, msgCount);
03706       PR_Free( msgIdList);
03707     }
03708     HeaderFetchCompleted();
03709       // this might be bogus, how are we going to do pane notification and stuff when we fetch bodies without
03710       // headers!
03711   }
03712   else if (new_spec) // need to exit this monitor if death signal received
03713     PR_ExitMonitor(m_waitForBodyIdsMonitor);
03714 
03715   // wait for a list of bodies to fetch. 
03716   if (!DeathSignalReceived() && GetServerStateParser().LastCommandSuccessful())
03717   {
03718       WaitForPotentialListOfBodysToFetch(&msgIdList, msgCount);
03719       if ( msgCount && !DeathSignalReceived() && GetServerStateParser().LastCommandSuccessful())
03720     {
03721       FolderMsgDump(msgIdList, msgCount, kEveryThingRFC822Peek);
03722     }
03723   }
03724   if (DeathSignalReceived())
03725     GetServerStateParser().ResetFlagInfo(0);
03726   PR_FREEIF(new_spec->allocatedPathName); 
03727   PR_FREEIF(new_spec->hostName);
03728   NS_IF_RELEASE(new_spec);
03729 }
03730 
03731 void nsImapProtocol::UpdatedMailboxSpec(nsImapMailboxSpec *aSpec)
03732 {
03733   if (m_imapMailFolderSink)
03734     m_imapMailFolderSink->UpdateImapMailboxInfo(this, aSpec);
03735 }
03736 
03737 void nsImapProtocol::FolderHeaderDump(PRUint32 *msgUids, PRUint32 msgCount)
03738 {
03739   FolderMsgDump(msgUids, msgCount, kHeadersRFC822andUid);
03740 }
03741 
03742 void nsImapProtocol::FolderMsgDump(PRUint32 *msgUids, PRUint32 msgCount, nsIMAPeFetchFields fields)
03743 {
03744   // lets worry about this progress stuff later.
03745   switch (fields) {
03746   case kHeadersRFC822andUid:
03747     SetProgressString(IMAP_RECEIVING_MESSAGE_HEADERS_OF);
03748     break;
03749   case kFlags:
03750     SetProgressString(IMAP_RECEIVING_MESSAGE_FLAGS_OF);
03751     break;
03752   default:
03753     SetProgressString(IMAP_FOLDER_RECEIVING_MESSAGE_OF);
03754     break;
03755   }
03756   
03757   m_progressIndex = 0;
03758   m_progressCount = msgCount;
03759   FolderMsgDumpLoop(msgUids, msgCount, fields);
03760   
03761   SetProgressString(0);
03762 }
03763 
03764 void nsImapProtocol::WaitForPotentialListOfMsgsToFetch(PRUint32 **msgIdList, PRUint32 &msgCount)
03765 {
03766   PRIntervalTime sleepTime = kImapSleepTime;
03767 
03768     PR_EnterMonitor(m_fetchMsgListMonitor);
03769     while(!m_fetchMsgListIsNew && !DeathSignalReceived())
03770         PR_Wait(m_fetchMsgListMonitor, sleepTime);
03771     m_fetchMsgListIsNew = PR_FALSE;
03772 
03773     *msgIdList = m_fetchMsgIdList;
03774     msgCount   = m_fetchCount;
03775     
03776     PR_ExitMonitor(m_fetchMsgListMonitor);
03777 }
03778 
03779 void nsImapProtocol::WaitForPotentialListOfBodysToFetch(PRUint32 **msgIdList, PRUint32 &msgCount)
03780 {
03781   PRIntervalTime sleepTime = kImapSleepTime;
03782 
03783     PR_EnterMonitor(m_fetchBodyListMonitor);
03784     while(!m_fetchBodyListIsNew && !DeathSignalReceived())
03785         PR_Wait(m_fetchBodyListMonitor, sleepTime);
03786     m_fetchBodyListIsNew = PR_FALSE;
03787 
03788     *msgIdList = m_fetchBodyIdList;
03789     msgCount   = m_fetchBodyCount;
03790     
03791     PR_ExitMonitor(m_fetchBodyListMonitor);
03792 }
03793 
03794 // libmsg uses this to notify a running imap url about the message headers it should
03795 // download while opening a folder. Generally, the imap thread will be blocked 
03796 // in WaitForPotentialListOfMsgsToFetch waiting for this notification.
03797 NS_IMETHODIMP nsImapProtocol::NotifyHdrsToDownload(PRUint32 *keys, PRUint32 keyCount)
03798 {
03799     PR_EnterMonitor(m_fetchMsgListMonitor);
03800     m_fetchMsgIdList = keys;
03801     m_fetchCount    = keyCount;
03802     m_fetchMsgListIsNew = PR_TRUE;
03803     PR_Notify(m_fetchMsgListMonitor);
03804     PR_ExitMonitor(m_fetchMsgListMonitor);
03805   return NS_OK;
03806 }
03807 
03808 // libmsg uses this to notify a running imap url about message bodies it should download.
03809 // why not just have libmsg explicitly download the message bodies?
03810 NS_IMETHODIMP nsImapProtocol::NotifyBodysToDownload(PRUint32 *keys, PRUint32 keyCount)
03811 {
03812     PR_EnterMonitor(m_fetchBodyListMonitor);
03813     PR_FREEIF(m_fetchBodyIdList);
03814     m_fetchBodyIdList = (PRUint32 *) PR_MALLOC(keyCount * sizeof(PRUint32));
03815     if (m_fetchBodyIdList)
03816       memcpy(m_fetchBodyIdList, keys, keyCount * sizeof(PRUint32));
03817     m_fetchBodyCount    = keyCount;
03818     m_fetchBodyListIsNew = PR_TRUE;
03819     PR_Notify(m_fetchBodyListMonitor);
03820     PR_ExitMonitor(m_fetchBodyListMonitor);
03821   return NS_OK;
03822 }
03823 
03824 NS_IMETHODIMP nsImapProtocol::GetFlagsForUID(PRUint32 uid, PRBool *foundIt, imapMessageFlagsType *resultFlags, char **customFlags)
03825 {
03826   PRInt32 i;
03827 
03828   imapMessageFlagsType flags = m_flagState->GetMessageFlagsFromUID(uid, foundIt, &i);
03829   if (*foundIt)
03830   {
03831     *resultFlags = flags;
03832     if ((flags & kImapMsgCustomKeywordFlag) && customFlags)
03833       m_flagState->GetCustomFlags(uid, customFlags);
03834   }
03835   return NS_OK;
03836 }
03837 
03838 NS_IMETHODIMP nsImapProtocol::GetSupportedUserFlags(PRUint16 *supportedFlags)
03839 {
03840   if (!supportedFlags)
03841     return NS_ERROR_NULL_POINTER;
03842 
03843   *supportedFlags = m_flagState->GetSupportedUserFlags();
03844   return NS_OK;
03845 }
03846 void nsImapProtocol::FolderMsgDumpLoop(PRUint32 *msgUids, PRUint32 msgCount, nsIMAPeFetchFields fields)
03847 {
03848 
03849   PRInt32 msgCountLeft = msgCount;
03850   PRUint32 msgsDownloaded = 0;
03851   do 
03852   {
03853     nsCString idString;
03854 
03855     PRUint32 msgsToDownload = msgCountLeft;
03856     AllocateImapUidString(msgUids + msgsDownloaded, msgsToDownload, m_flagState, idString);  // 20 * 200
03857 
03858     FetchMessage(idString.get(),  fields, PR_TRUE);                  // msg ids are uids                 
03859 
03860     msgsDownloaded += msgsToDownload;
03861     msgCountLeft -= msgsToDownload;
03862 
03863   }
03864   while (msgCountLeft > 0 && !DeathSignalReceived());
03865 }     
03866     
03867 
03868 void nsImapProtocol::HeaderFetchCompleted()
03869 {
03870   if (m_imapMailFolderSink)
03871     m_imapMailFolderSink->ParseMsgHdrs(this, &m_hdrDownloadCache);
03872   m_hdrDownloadCache.ReleaseAll();
03873 
03874   if (m_imapMailFolderSink)
03875     m_imapMailFolderSink->HeaderFetchCompleted(this);
03876 }
03877 
03878 
03879 // Use the noop to tell the server we are still here, and therefore we are willing to receive
03880 // status updates. The recent or exists response from the server could tell us that there is
03881 // more mail waiting for us, but we need to check the flags of the mail and the high water mark
03882 // to make sure that we do not tell the user that there is new mail when perhaps they have
03883 // already read it in another machine.
03884 
03885 void nsImapProtocol::PeriodicBiff()
03886 {
03887   
03888   nsMsgBiffState startingState = m_currentBiffState;
03889   
03890   if (GetServerStateParser().GetIMAPstate() == nsImapServerResponseParser::kFolderSelected)
03891   {
03892     Noop(); // check the latest number of messages
03893     PRInt32 numMessages = 0;
03894     m_flagState->GetNumberOfMessages(&numMessages);
03895     if (GetServerStateParser().NumberOfMessages() != numMessages)
03896     {
03897       PRUint32 id = GetServerStateParser().HighestRecordedUID() + 1;
03898       nsCString fetchStr;           // only update flags
03899       PRUint32 added = 0, deleted = 0;
03900       
03901       deleted = m_flagState->GetNumberOfDeletedMessages();
03902       added = numMessages;
03903       if (!added || (added == deleted)) // empty keys, get them all
03904         id = 1;
03905       
03906       //sprintf(fetchStr, "%ld:%ld", id, id + GetServerStateParser().NumberOfMessages() - fFlagState->GetNumberOfMessages());
03907       AppendUid(fetchStr, id);
03908       fetchStr.Append(":*"); 
03909       FetchMessage(fetchStr.get(), kFlags, PR_TRUE);
03910       
03911       if (((PRUint32) m_flagState->GetHighestNonDeletedUID() >= id) && m_flagState->IsLastMessageUnseen())
03912         m_currentBiffState = nsIMsgFolder::nsMsgBiffState_NewMail;
03913       else
03914         m_currentBiffState = nsIMsgFolder::nsMsgBiffState_NoMail;
03915     }
03916     else
03917       m_currentBiffState = nsIMsgFolder::nsMsgBiffState_NoMail;
03918   }
03919   else
03920     m_currentBiffState = nsIMsgFolder::nsMsgBiffState_Unknown;
03921   
03922   if (startingState != m_currentBiffState)
03923     SendSetBiffIndicatorEvent(m_currentBiffState);
03924 }
03925 
03926 void nsImapProtocol::SendSetBiffIndicatorEvent(nsMsgBiffState newState)
03927 {
03928     if (m_imapMailFolderSink) 
03929       m_imapMailFolderSink->SetBiffStateAndUpdate(newState);
03930 }
03931 
03932 
03933 
03934 // We get called to see if there is mail waiting for us at the server, even if it may have been
03935 // read elsewhere. We just want to know if we should download headers or not.
03936 
03937 PRBool nsImapProtocol::CheckNewMail()
03938 {
03939   return m_checkForNewMailDownloadsHeaders;
03940 }
03941 
03942 
03943 
03944 /* static */ void nsImapProtocol::LogImapUrl(const char *logMsg, nsIImapUrl *imapUrl)
03945 {
03946   // nsImapProtocol is not always constructed before this static method is called
03947   if (!IMAP)
03948     IMAP = PR_NewLogModule("IMAP");
03949 
03950   if (PR_LOG_TEST(IMAP, PR_LOG_ALWAYS))
03951   {
03952     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(imapUrl);
03953     if (mailnewsUrl)
03954     {
03955       nsCAutoString urlSpec;
03956       mailnewsUrl->GetSpec(urlSpec);
03957       nsUnescape(urlSpec.BeginWriting());
03958       PR_LOG(IMAP, PR_LOG_ALWAYS, ("%s:%s", logMsg, urlSpec.get()));
03959     }
03960   }
03961 }
03962 
03963 // log info including current state...
03964 void nsImapProtocol::Log(const char *logSubName, const char *extraInfo, const char *logData)
03965 {
03966   if (PR_LOG_TEST(IMAP, PR_LOG_ALWAYS))
03967   {
03968     static const char nonAuthStateName[] = "NA";
03969     static const char authStateName[] = "A";
03970     static const char selectedStateName[] = "S";
03971       //  static const char waitingStateName[] = "W";
03972     const char *stateName = NULL;
03973     const char *hostName = GetImapHostName();  // initilize to empty string
03974 
03975     PRInt32 logDataLen = PL_strlen(logData); // PL_strlen checks for null
03976     nsCString logDataLines;
03977     const char *logDataToLog;
03978     PRInt32 lastLineEnd;
03979 
03980     const int kLogDataChunkSize = 400; // nspr line length is 512, and we allow some space for the log preamble.
03981 
03982     // break up buffers > 400 bytes on line boundaries.
03983     if (logDataLen > kLogDataChunkSize)
03984     {
03985       logDataLines.Assign(logData);
03986       lastLineEnd = logDataLines.RFindChar('\n', kLogDataChunkSize);
03987       // null terminate the last line
03988       if (lastLineEnd == kNotFound)
03989         lastLineEnd = kLogDataChunkSize - 1;
03990 
03991       logDataLines.Insert( '\0', lastLineEnd + 1);
03992       logDataToLog = logDataLines.get();
03993     }
03994     else
03995     {
03996       logDataToLog = logData;
03997       lastLineEnd = logDataLen;
03998     }
03999     switch (GetServerStateParser().GetIMAPstate())
04000     {
04001     case nsImapServerResponseParser::kFolderSelected:
04002       if (extraInfo)
04003         PR_LOG(IMAP, PR_LOG_ALWAYS, ("%x:%s:%s-%s:%s:%s: %.400s", this,hostName,selectedStateName, GetServerStateParser().GetSelectedMailboxName(), logSubName, extraInfo, logDataToLog));
04004       else
04005         PR_LOG(IMAP, PR_LOG_ALWAYS, ("%x:%s:%s-%s:%s: %.400s", this,hostName,selectedStateName, GetServerStateParser().GetSelectedMailboxName(), logSubName, logDataToLog));
04006       return;
04007     case nsImapServerResponseParser::kNonAuthenticated:
04008       stateName = nonAuthStateName;
04009       break;
04010     case nsImapServerResponseParser::kAuthenticated:
04011       stateName = authStateName;
04012       break;
04013     }
04014 
04015     if (extraInfo)
04016       PR_LOG(IMAP, PR_LOG_ALWAYS, ("%x:%s:%s:%s:%s: %.400s", this,hostName,stateName,logSubName,extraInfo,logDataToLog));
04017     else
04018       PR_LOG(IMAP, PR_LOG_ALWAYS, ("%x:%s:%s:%s: %.400s",this,hostName,stateName,logSubName,logDataToLog));
04019 
04020     // dump the rest of the string in < 400 byte chunks
04021     while (logDataLen > kLogDataChunkSize)
04022     {
04023       logDataLines.Cut(0, lastLineEnd + 2); // + 2 to account for the LF and the '\0' we added
04024       logDataLen = logDataLines.Length();
04025       lastLineEnd = (logDataLen > kLogDataChunkSize) ? logDataLines.RFindChar('\n', kLogDataChunkSize) : kNotFound;
04026       // null terminate the last line
04027       if (lastLineEnd == kNotFound)
04028         lastLineEnd = kLogDataChunkSize - 1;
04029       logDataLines.Insert( '\0', lastLineEnd + 1);
04030       logDataToLog = logDataLines.get();
04031       PR_LOG(IMAP, PR_LOG_ALWAYS, ("%.400s", logDataToLog));
04032     }
04033   }
04034 }
04035 
04036 // In 4.5, this posted an event back to libmsg and blocked until it got a response.
04037 // We may still have to do this.It would be nice if we could preflight this value,
04038 // but we may not always know when we'll need it.
04039 PRUint32 nsImapProtocol::GetMessageSize(const char * messageId, 
04040                                         PRBool idsAreUids)
04041 {
04042   const char *folderFromParser = GetServerStateParser().GetSelectedMailboxName(); 
04043   if (folderFromParser && messageId)
04044   {
04045     char *id = (char *)PR_CALLOC(strlen(messageId) + 1);
04046     char *folderName;
04047     PRUint32 size;
04048 
04049     PL_strcpy(id, messageId);
04050 
04051     nsIMAPNamespace *nsForMailbox = nsnull;
04052         m_hostSessionList->GetNamespaceForMailboxForHost(GetImapServerKey(), folderFromParser,
04053             nsForMailbox);
04054 
04055 
04056     if (nsForMailbox)
04057           m_runningUrl->AllocateCanonicalPath(
04058               folderFromParser, nsForMailbox->GetDelimiter(),
04059               &folderName);
04060     else
04061        m_runningUrl->AllocateCanonicalPath(
04062           folderFromParser,kOnlineHierarchySeparatorUnknown,
04063           &folderName);
04064 
04065     if (id && folderName)
04066     {
04067       if (m_imapMessageSink)
04068           m_imapMessageSink->GetMessageSizeFromDB(id, idsAreUids, &size);
04069     }
04070     PR_FREEIF(id);
04071     PR_FREEIF(folderName);
04072 
04073     PRUint32 rv = 0;
04074     if (!DeathSignalReceived())
04075       rv = size;
04076     return rv;
04077   }
04078   return 0;
04079 }
04080 
04081 // message id string utility functions
04082 /* static */PRBool nsImapProtocol::HandlingMultipleMessages(const char *messageIdString)
04083 {
04084   return (PL_strchr(messageIdString,',') != nsnull ||
04085         PL_strchr(messageIdString,':') != nsnull);
04086 }
04087 
04088 PRUint32 nsImapProtocol::CountMessagesInIdString(const char *idString)
04089 {
04090   PRUint32 numberOfMessages = 0;
04091   char *uidString = PL_strdup(idString);
04092 
04093   if (uidString)
04094   {
04095     // This is in the form <id>,<id>, or <id1>:<id2>
04096     char curChar = *uidString;
04097     PRBool isRange = PR_FALSE;
04098     PRInt32 curToken;
04099     PRInt32 saveStartToken=0;
04100 
04101     for (char *curCharPtr = uidString; curChar && *curCharPtr;)
04102     {
04103       char *currentKeyToken = curCharPtr;
04104       curChar = *curCharPtr;
04105       while (curChar != ':' && curChar != ',' && curChar != '\0')
04106         curChar = *curCharPtr++;
04107       *(curCharPtr - 1) = '\0';
04108       curToken = atol(currentKeyToken);
04109       if (isRange)
04110       {
04111         while (saveStartToken < curToken)
04112         {
04113           numberOfMessages++;
04114           saveStartToken++;
04115         }
04116       }
04117 
04118       numberOfMessages++;
04119       isRange = (curChar == ':');
04120       if (isRange)
04121         saveStartToken = curToken + 1;
04122     }
04123     PR_Free(uidString);
04124   }
04125   return numberOfMessages;
04126 }
04127 
04128 
04129 PRMonitor *nsImapProtocol::GetDataMemberMonitor()
04130 {
04131     return m_dataMemberMonitor;
04132 }
04133 
04134 // It would be really nice not to have to use this method nearly as much as we did
04135 // in 4.5 - we need to think about this some. Some of it may just go away in the new world order
04136 PRBool nsImapProtocol::DeathSignalReceived()
04137 {
04138   nsresult returnValue = NS_OK;
04139   // ignore mock channel status if we've been pseudo interrupted
04140   // ### need to make sure we clear pseudo interrupted status appropriately.
04141   if (!GetPseudoInterrupted() && m_mockChannel) 
04142   { 
04143     nsCOMPtr<nsIRequest> request = do_QueryInterface(m_mockChannel);
04144     if (request) 
04145       request->GetStatus(&returnValue);
04146   }
04147   if (NS_SUCCEEDED(returnValue))   // check the other way of cancelling.
04148   {
04149     PR_EnterMonitor(m_threadDeathMonitor);
04150     returnValue = m_threadShouldDie;
04151     PR_ExitMonitor(m_threadDeathMonitor);
04152   }
04153   return returnValue;
04154 }
04155 
04156 NS_IMETHODIMP nsImapProtocol::ResetToAuthenticatedState()
04157 {
04158     GetServerStateParser().PreauthSetAuthenticatedState();
04159     return NS_OK;
04160 }
04161 
04162 
04163 NS_IMETHODIMP nsImapProtocol::GetSelectedMailboxName(char ** folderName)
04164 {
04165     if (!folderName) return NS_ERROR_NULL_POINTER;
04166     if (GetServerStateParser().GetSelectedMailboxName())
04167         *folderName =
04168             PL_strdup((GetServerStateParser().GetSelectedMailboxName())); 
04169     return NS_OK;
04170 }
04171 
04172 PRBool nsImapProtocol::GetPseudoInterrupted()
04173 {
04174   PRBool rv = PR_FALSE;
04175   PR_EnterMonitor(m_pseudoInterruptMonitor);
04176   rv = m_pseudoInterrupted;
04177   PR_ExitMonitor(m_pseudoInterruptMonitor);
04178   return rv;
04179 }
04180 
04181 void nsImapProtocol::PseudoInterrupt(PRBool the_interrupt)
04182 {
04183   PR_EnterMonitor(m_pseudoInterruptMonitor);
04184   m_pseudoInterrupted = the_interrupt;
04185   if (the_interrupt)
04186   {
04187     Log("CONTROL", NULL, "PSEUDO-Interrupted");
04188   }
04189   PR_ExitMonitor(m_pseudoInterruptMonitor);
04190 }
04191 
04192 void  nsImapProtocol::SetActive(PRBool active)
04193 {
04194   PR_EnterMonitor(GetDataMemberMonitor());
04195   m_active = active;
04196   PR_ExitMonitor(GetDataMemberMonitor());
04197 }
04198 
04199 PRBool  nsImapProtocol::GetActive()
04200 {
04201   PRBool ret;
04202   PR_EnterMonitor(GetDataMemberMonitor());
04203   ret = m_active;
04204   PR_ExitMonitor(GetDataMemberMonitor());
04205   return ret;
04206 }
04207 
04208 PRBool nsImapProtocol::GetShowAttachmentsInline()
04209 {
04210   PRBool showAttachmentsInline = PR_TRUE;
04211   if (m_imapServerSink)
04212     m_imapServerSink->GetShowAttachmentsInline(&showAttachmentsInline);
04213   return showAttachmentsInline;
04214 
04215 }
04216 
04217 void nsImapProtocol::SetContentModified(IMAP_ContentModifiedType modified)
04218 {
04219   if (m_runningUrl && m_imapMessageSink)
04220     m_imapMessageSink->SetContentModified(m_runningUrl, modified);
04221 }
04222 
04223 
04224 PRBool nsImapProtocol::GetShouldFetchAllParts()
04225 {
04226   if (m_runningUrl  && !DeathSignalReceived())
04227   {
04228     nsImapContentModifiedType contentModified;
04229     if (NS_SUCCEEDED(m_runningUrl->GetContentModified(&contentModified)))
04230       return (contentModified == IMAP_CONTENT_FORCE_CONTENT_NOT_MODIFIED);
04231   }
04232   return PR_TRUE;
04233 }
04234 
04235 PRInt32 nsImapProtocol::OpenTunnel (PRInt32 maxNumberOfBytesToRead)
04236 {
04237   return 0;
04238 }
04239 
04240 PRInt32 nsImapProtocol::GetTunnellingThreshold()
04241 {
04242   return 0;
04243 //  return gTunnellingThreshold;
04244 }
04245 
04246 PRBool nsImapProtocol::GetIOTunnellingEnabled()
04247 {
04248   return PR_FALSE;
04249 //  return gIOTunnelling;
04250 }
04251 
04252 // Adds a set of rights for a given user on a given mailbox on the current host.
04253 // if userName is NULL, it means "me," or MYRIGHTS.
04254 void nsImapProtocol::AddFolderRightsForUser(const char *mailboxName, const char *userName, const char *rights)
04255 {
04256   nsIMAPACLRightsInfo *aclRightsInfo = new nsIMAPACLRightsInfo();
04257   if (aclRightsInfo)
04258   {
04259     nsIMAPNamespace *namespaceForFolder = nsnull;
04260     NS_ASSERTION (m_hostSessionList, "fatal ... null host session list");
04261     if (m_hostSessionList)
04262       m_hostSessionList->GetNamespaceForMailboxForHost(
04263       GetImapServerKey(), mailboxName,
04264       namespaceForFolder);
04265     
04266     aclRightsInfo->hostName = PL_strdup(GetImapHostName());
04267     
04268     if (namespaceForFolder)
04269       m_runningUrl->AllocateCanonicalPath(
04270       mailboxName,
04271       namespaceForFolder->GetDelimiter(), 
04272       &aclRightsInfo->mailboxName);
04273     else
04274       m_runningUrl->AllocateCanonicalPath(mailboxName,
04275       kOnlineHierarchySeparatorUnknown, 
04276       &aclRightsInfo->mailboxName);
04277     
04278     if (userName)
04279       aclRightsInfo->userName = PL_strdup(userName);
04280     else
04281       aclRightsInfo->userName = NULL;
04282     aclRightsInfo->rights = PL_strdup(rights);
04283     
04284     
04285     if (aclRightsInfo->hostName && 
04286       aclRightsInfo->mailboxName && aclRightsInfo->rights && 
04287       userName ? (aclRightsInfo->userName != NULL) : PR_TRUE)
04288     {
04289       if (m_imapServerSink)
04290       {
04291         m_imapServerSink->AddFolderRights(mailboxName, userName, rights);
04292       }
04293     }
04294     PR_Free(aclRightsInfo->hostName);
04295     PR_Free(aclRightsInfo->mailboxName);
04296     PR_Free(aclRightsInfo->rights);
04297     PR_Free(aclRightsInfo->userName);
04298     
04299     delete aclRightsInfo;
04300   }
04301   else
04302     HandleMemoryFailure();
04303 }
04304 
04305 void nsImapProtocol::SetCopyResponseUid(const char *msgIdString)
04306 {
04307   if (m_imapMailFolderSink)
04308     m_imapMailFolderSink->SetCopyResponseUid(msgIdString, m_runningUrl);
04309 }
04310 
04311 void nsImapProtocol::CommitNamespacesForHostEvent()
04312 {
04313     if (m_imapServerSink)
04314         m_imapServerSink->CommitNamespaces();
04315 }
04316 
04317 // notifies libmsg that we have new capability data for the current host
04318 void nsImapProtocol::CommitCapability()
04319 {
04320     if (m_imapServerSink)
04321     {
04322         m_imapServerSink->SetCapability(GetServerStateParser().GetCapabilityFlag());
04323     }
04324 }
04325 
04326 // rights is a single string of rights, as specified by RFC2086, the IMAP ACL extension.
04327 // Clears all rights for a given folder, for all users.
04328 void nsImapProtocol::ClearAllFolderRights(const char *mailboxName,
04329                                           nsIMAPNamespace *nsForMailbox)
04330 {
04331   NS_ASSERTION (nsForMailbox, "Oops ... null name space");
04332     nsIMAPACLRightsInfo *aclRightsInfo = new nsIMAPACLRightsInfo();
04333   if (aclRightsInfo)
04334   {
04335         const char *hostName = GetImapHostName();
04336 
04337     aclRightsInfo->hostName = PL_strdup(hostName);
04338     if (nsForMailbox)
04339             m_runningUrl->AllocateCanonicalPath(mailboxName,
04340                                                 nsForMailbox->GetDelimiter(),
04341                                                 &aclRightsInfo->mailboxName); 
04342     else
04343             m_runningUrl->AllocateCanonicalPath(
04344                 mailboxName, kOnlineHierarchySeparatorUnknown,
04345                 &aclRightsInfo->mailboxName);
04346 
04347 
04348     aclRightsInfo->rights = NULL;
04349     aclRightsInfo->userName = NULL;
04350 
04351     if (aclRightsInfo->hostName && aclRightsInfo->mailboxName && m_imapMailFolderSink)
04352         m_imapMailFolderSink->ClearFolderRights();
04353 
04354     PR_Free(aclRightsInfo->hostName);
04355     PR_Free(aclRightsInfo->mailboxName);
04356     
04357     delete aclRightsInfo;
04358   }
04359   else
04360     HandleMemoryFailure();
04361 }
04362 
04363 
04364 char* nsImapProtocol::CreateNewLineFromSocket()
04365 {
04366   PRBool needMoreData = PR_FALSE;
04367   char * newLine = nsnull;
04368   PRUint32 numBytesInLine = 0;
04369   nsresult rv = NS_OK;
04370   // we hold a ref to the input stream in case we get cancelled from the
04371   // ui thread, which releases our ref to the input stream, and can
04372   // cause the pipe to get deleted before the monitor the read is
04373   // blocked on gets notified. When that happens, the imap thread
04374   // will stay blocked.
04375   nsCOMPtr <nsIInputStream> kungFuGrip = m_inputStream;
04376   do
04377   {
04378     newLine = m_inputStreamBuffer->ReadNextLine(m_inputStream, numBytesInLine, needMoreData, &rv); 
04379     PR_LOG(IMAP, PR_LOG_DEBUG, ("ReadNextLine [stream=%x nb=%u needmore=%u]\n",
04380         m_inputStream.get(), numBytesInLine, needMoreData));
04381 
04382   } while (!newLine && NS_SUCCEEDED(rv) && !DeathSignalReceived()); // until we get the next line and haven't been interrupted
04383 
04384   kungFuGrip = nsnull;
04385 
04386   if (NS_FAILED(rv)) 
04387   {
04388     switch (rv) 
04389     {
04390         case NS_ERROR_UNKNOWN_HOST:
04391         case NS_ERROR_UNKNOWN_PROXY_HOST:
04392             AlertUserEventUsingId(IMAP_UNKNOWN_HOST_ERROR);
04393             break;
04394         case NS_ERROR_CONNECTION_REFUSED:
04395         case NS_ERROR_PROXY_CONNECTION_REFUSED:
04396             AlertUserEventUsingId(IMAP_CONNECTION_REFUSED_ERROR);
04397             break;
04398         case NS_ERROR_NET_TIMEOUT:
04399         case NS_ERROR_NET_RESET:
04400         case NS_BASE_STREAM_CLOSED:
04401         case NS_ERROR_NET_INTERRUPT:
04402           // we should retry on RESET, especially for SSL...
04403           if ((TestFlag(IMAP_RECEIVED_GREETING) || rv == NS_ERROR_NET_RESET) &&
04404               m_runningUrl && !m_retryUrlOnError)
04405           {
04406             m_runningUrl->SetRerunningUrl(PR_TRUE);
04407             m_retryUrlOnError = PR_TRUE;
04408           }
04409           else if (rv == NS_ERROR_NET_TIMEOUT)
04410             AlertUserEventUsingId(IMAP_NET_TIMEOUT_ERROR);
04411           else
04412             AlertUserEventUsingId(TestFlag(IMAP_RECEIVED_GREETING) 
04413             ? IMAP_SERVER_DISCONNECTED : IMAP_SERVER_DROPPED_CONNECTION);
04414           break;
04415         default:
04416             break;
04417     }
04418   
04419     nsCAutoString logMsg("clearing IMAP_CONNECTION_IS_OPEN - rv = ");
04420     logMsg.AppendInt(rv, 16);
04421     Log("CreateNewLineFromSocket", nsnull, logMsg.get());
04422     ClearFlag(IMAP_CONNECTION_IS_OPEN);
04423     TellThreadToDie(PR_FALSE);
04424   }
04425   Log("CreateNewLineFromSocket", nsnull, newLine);
04426   SetConnectionStatus(newLine && numBytesInLine ? 1 : -1); // set > 0 if string is not null or empty
04427   return newLine;
04428 }
04429 
04430 PRInt32
04431 nsImapProtocol::GetConnectionStatus()
04432 {
04433     // ***?? I am not sure we really to guard with monitor for 5.0 ***
04434     PRInt32 status;
04435   // mscott -- do we need these monitors? as i was debuggin this I continually
04436   // locked when entering this monitor...control would never return from this
04437   // function...
04438 //    PR_CEnterMonitor(this);
04439     status = m_connectionStatus;
04440 //    PR_CExitMonitor(this);
04441     return status;
04442 }
04443 
04444 void
04445 nsImapProtocol::SetConnectionStatus(PRInt32 status)
04446 {
04447 //    PR_CEnterMonitor(this);
04448     m_connectionStatus = status;
04449 //    PR_CExitMonitor(this);
04450 }
04451 
04452 void
04453 nsImapProtocol::NotifyMessageFlags(imapMessageFlagsType flags, nsMsgKey key)
04454 {
04455     if (m_imapMessageSink)
04456     {
04457       // if we're selecting the folder, don't need to report the flags; we've already fetched them.
04458       if (m_imapAction != nsIImapUrl::nsImapSelectFolder && (m_imapAction != nsIImapUrl::nsImapMsgFetch || (flags & ~kImapMsgRecentFlag) != kImapMsgSeenFlag))
04459         m_imapMessageSink->NotifyMessageFlags(flags, key);
04460     }
04461 }
04462 
04463 void
04464 nsImapProtocol::NotifySearchHit(const char * hitLine)
04465 {
04466     nsresult rv;
04467     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl, &rv);
04468     if (m_imapMailFolderSink)
04469         m_imapMailFolderSink->NotifySearchHit(mailnewsUrl, hitLine);
04470 }
04471 
04472 void nsImapProtocol::SetMailboxDiscoveryStatus(EMailboxDiscoverStatus status)
04473 {
04474     PR_EnterMonitor(GetDataMemberMonitor());
04475   m_discoveryStatus = status;
04476     PR_ExitMonitor(GetDataMemberMonitor());
04477 }
04478 
04479 EMailboxDiscoverStatus nsImapProtocol::GetMailboxDiscoveryStatus( )
04480 {
04481   EMailboxDiscoverStatus returnStatus;
04482     PR_EnterMonitor(GetDataMemberMonitor());
04483   returnStatus = m_discoveryStatus;
04484     PR_ExitMonitor(GetDataMemberMonitor());
04485     
04486     return returnStatus;
04487 }
04488 
04489 PRBool
04490 nsImapProtocol::GetSubscribingNow()
04491 {
04492     // ***** code me *****
04493     return PR_FALSE;// ***** for now
04494 }
04495 
04496 void
04497 nsImapProtocol::DiscoverMailboxSpec(nsImapMailboxSpec * adoptedBoxSpec)  
04498 {
04499   nsIMAPNamespace *ns = nsnull;
04500   
04501   NS_ASSERTION (m_hostSessionList, "fatal null host session list");
04502   if (!m_hostSessionList) return;
04503   
04504   m_hostSessionList->GetDefaultNamespaceOfTypeForHost(
04505     GetImapServerKey(), kPersonalNamespace, ns);
04506   const char *nsPrefix = ns ? ns->GetPrefix() : 0;
04507   
04508   switch (m_hierarchyNameState)
04509   {
04510   case kListingForCreate:
04511   case kNoOperationInProgress:
04512   case kDiscoverTrashFolderInProgress:
04513   case kListingForInfoAndDiscovery:
04514     {
04515       if (ns && nsPrefix) // if no personal namespace, there can be no Trash folder
04516       {
04517         PRBool onlineTrashFolderExists = PR_FALSE;
04518         if (m_hostSessionList)
04519           m_hostSessionList->GetOnlineTrashFolderExistsForHost(
04520           GetImapServerKey(), onlineTrashFolderExists);
04521         
04522         if (GetDeleteIsMoveToTrash() && // don't set the Trash flag
04523           // if not using the Trash model
04524           !onlineTrashFolderExists && 
04525           PL_strstr(adoptedBoxSpec->allocatedPathName, 
04526           GetTrashFolderName()))
04527         {
04528           PRBool trashExists = PR_FALSE;
04529           nsCString trashMatch;
04530           trashMatch.Adopt(CreatePossibleTrashName(nsPrefix));
04531           {
04532             char *serverTrashName = nsnull;
04533             m_runningUrl->AllocateCanonicalPath(
04534               trashMatch.get(),
04535               ns->GetDelimiter(), &serverTrashName); 
04536             if (serverTrashName)
04537             {
04538               if (!PL_strncasecmp(serverTrashName, "INBOX/", 6)) // case-insensitive
04539               {
04540                 trashExists = !PL_strncasecmp(adoptedBoxSpec->allocatedPathName, serverTrashName, 6) && /* "INBOX/" */
04541                               !PL_strcmp(adoptedBoxSpec->allocatedPathName + 6, serverTrashName + 6);
04542               }
04543               else
04544               {
04545                 trashExists = (PL_strcmp(serverTrashName, adoptedBoxSpec->allocatedPathName) == 0);
04546               }
04547               if (m_hostSessionList)
04548                 m_hostSessionList->SetOnlineTrashFolderExistsForHost(GetImapServerKey(), trashExists);
04549               PR_Free(serverTrashName);
04550             }
04551           }
04552           
04553           if (trashExists)
04554             adoptedBoxSpec->box_flags |= kImapTrash;
04555         }
04556       }
04557       
04558       // Discover the folder (shuttle over to libmsg, yay)
04559       // Do this only if the folder name is not empty (i.e. the root)
04560       if (adoptedBoxSpec->allocatedPathName &&
04561         *adoptedBoxSpec->allocatedPathName)
04562       {
04563         nsCString boxNameCopy; 
04564         
04565         boxNameCopy = adoptedBoxSpec->allocatedPathName;
04566         if (m_hierarchyNameState == kListingForCreate)
04567           adoptedBoxSpec->box_flags |= kNewlyCreatedFolder;
04568         
04569         if (m_imapServerSink)
04570         {
04571           PRBool newFolder;
04572           m_imapServerSink->PossibleImapMailbox(boxNameCopy.get(), 
04573             adoptedBoxSpec->hierarchySeparator,
04574             adoptedBoxSpec->box_flags, &newFolder);
04575           // if it's a new folder to the server sink, setting discovery status to
04576           // eContinueNew will cause us to get the ACL for the new folder.
04577           if (newFolder)
04578             SetMailboxDiscoveryStatus(eContinueNew);
04579           
04580           PRBool useSubscription = PR_FALSE;
04581           
04582           if (m_hostSessionList)
04583             m_hostSessionList->GetHostIsUsingSubscription(
04584             GetImapServerKey(),
04585             useSubscription);
04586           
04587           if ((GetMailboxDiscoveryStatus() != eContinue) && 
04588             (GetMailboxDiscoveryStatus() != eContinueNew) &&
04589             (GetMailboxDiscoveryStatus() != eListMyChildren))
04590           {
04591             SetConnectionStatus(-1);
04592           }
04593           else if (!boxNameCopy.IsEmpty() && 
04594             (GetMailboxDiscoveryStatus() == 
04595             eListMyChildren) &&
04596             (!useSubscription || GetSubscribingNow()))
04597           {
04598             NS_ASSERTION (PR_FALSE, 
04599               "we should never get here anymore");
04600             SetMailboxDiscoveryStatus(eContinue);
04601           }
04602           else if (GetMailboxDiscoveryStatus() == eContinueNew)
04603           {
04604             if (m_hierarchyNameState ==
04605               kListingForInfoAndDiscovery &&
04606               !boxNameCopy.IsEmpty() && 
04607               !(adoptedBoxSpec->box_flags & kNameSpace))
04608             {
04609               // remember the info here also
04610               nsIMAPMailboxInfo *mb = new
04611                 nsIMAPMailboxInfo(boxNameCopy.get(),
04612                 adoptedBoxSpec->hierarchySeparator); 
04613               m_listedMailboxList.AppendElement((void*) mb);
04614             }
04615             SetMailboxDiscoveryStatus(eContinue);
04616           }
04617         }
04618       }
04619         }
04620         NS_IF_RELEASE( adoptedBoxSpec);
04621         break;
04622     case kDiscoverBaseFolderInProgress:
04623       break;
04624     case kDeleteSubFoldersInProgress:
04625       {
04626         NS_ASSERTION(m_deletableChildren, 
04627           "Oops .. null m_deletableChildren\n");
04628         m_deletableChildren->AppendElement((void*)
04629           nsCRT::strdup(adoptedBoxSpec->allocatedPathName));
04630         PR_FREEIF(adoptedBoxSpec->hostName);
04631         NS_IF_RELEASE( adoptedBoxSpec);
04632       }
04633       break;
04634     case kListingForInfoOnly:
04635       {
04636         //UpdateProgressWindowForUpgrade(adoptedBoxSpec->allocatedPathName);
04637         ProgressEventFunctionUsingIdWithString(IMAP_DISCOVERING_MAILBOX,
04638           adoptedBoxSpec->allocatedPathName);
04639         nsIMAPMailboxInfo *mb = new
04640           nsIMAPMailboxInfo(adoptedBoxSpec->allocatedPathName,
04641           adoptedBoxSpec->hierarchySeparator);
04642         m_listedMailboxList.AppendElement((void*) mb);
04643         PR_FREEIF(adoptedBoxSpec->allocatedPathName);
04644         NS_IF_RELEASE(adoptedBoxSpec);
04645       }
04646       break;
04647     case kDiscoveringNamespacesOnly:
04648       {
04649         PR_FREEIF(adoptedBoxSpec->allocatedPathName);
04650         NS_IF_RELEASE(adoptedBoxSpec);
04651       }
04652       break;
04653     default:
04654       NS_ASSERTION (PR_FALSE, "we aren't supposed to be here");
04655       break;
04656   }
04657 }
04658 
04659 void
04660 nsImapProtocol::AlertUserEventUsingId(PRUint32 aMessageId)
04661 {
04662   if (m_imapServerSink)
04663   {
04664     PRBool suppressErrorMsg = PR_FALSE;
04665 
04666     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
04667     if (mailnewsUrl)
04668       mailnewsUrl->GetSuppressErrorMsgs(&suppressErrorMsg);
04669 
04670     if (!suppressErrorMsg)
04671     {
04672       PRUnichar *progressString = nsnull;
04673       m_imapServerSink->FormatStringWithHostNameByID(aMessageId, &progressString);
04674       nsCOMPtr<nsIMsgWindow> msgWindow;
04675       GetMsgWindow(getter_AddRefs(msgWindow));
04676       m_imapServerSink->FEAlert(progressString, msgWindow);
04677       PR_Free(progressString);
04678     } 
04679   }
04680 }
04681 
04682 void
04683 nsImapProtocol::AlertUserEvent(const char * message)
04684 {
04685   if (m_imapServerSink)
04686   {
04687     nsCOMPtr<nsIMsgWindow> msgWindow;
04688     GetMsgWindow(getter_AddRefs(msgWindow));
04689     m_imapServerSink->FEAlert(NS_ConvertASCIItoUTF16(message).get(), msgWindow);
04690   }
04691 }
04692 
04693 void
04694 nsImapProtocol::AlertUserEventFromServer(const char * aServerEvent)
04695 {
04696     if (m_imapServerSink)
04697     {
04698       nsCOMPtr<nsIMsgWindow> msgWindow;
04699       GetMsgWindow(getter_AddRefs(msgWindow));
04700       m_imapServerSink->FEAlertFromServer(aServerEvent, msgWindow);
04701     }
04702 }
04703 
04704 void nsImapProtocol::ResetProgressInfo()
04705 {
04706   LL_I2L(m_lastProgressTime, 0);
04707   m_lastPercent = -1;
04708   m_lastProgressStringId = (PRUint32) -1;
04709 }
04710 
04711 void nsImapProtocol::SetProgressString(PRInt32 stringId)
04712 {
04713   m_progressStringId = stringId;
04714   if (m_progressStringId && m_imapServerSink)
04715     m_imapServerSink->GetImapStringByID(stringId, getter_Copies(m_progressString));
04716 }
04717 
04718 void
04719 nsImapProtocol::ShowProgress()
04720 {
04721   if (m_progressString && m_progressStringId)
04722   {
04723     PRUnichar *progressString = NULL;
04724     nsCAutoString cProgressString; cProgressString.AssignWithConversion(m_progressString);
04725     const char *mailboxName = GetServerStateParser().GetSelectedMailboxName();
04726     
04727     nsXPIDLString unicodeMailboxName;
04728     
04729     nsresult rv = CopyMUTF7toUTF16(nsDependentCString(mailboxName),
04730                                    unicodeMailboxName);
04731     if (NS_SUCCEEDED(rv))
04732     {
04733       // ### should convert mailboxName to PRUnichar and change %s to %S in msg text
04734       progressString = nsTextFormatter::smprintf(m_progressString, (const PRUnichar *) unicodeMailboxName, ++m_progressIndex, m_progressCount);
04735       if (progressString)
04736       {
04737         PercentProgressUpdateEvent(progressString, m_progressIndex,m_progressCount);
04738         nsTextFormatter::smprintf_free(progressString);
04739       }
04740     }
04741   }
04742 }
04743 
04744 void
04745 nsImapProtocol::ProgressEventFunctionUsingId(PRUint32 aMsgId)
04746 {
04747   if (m_imapMailFolderSink && aMsgId != m_lastProgressStringId)
04748   {
04749     m_imapMailFolderSink->ProgressStatus(this, aMsgId, nsnull);
04750     m_lastProgressStringId = aMsgId;
04751     // who's going to free this? Does ProgressStatus complete synchronously?
04752   }
04753 }
04754 
04755 void
04756 nsImapProtocol::ProgressEventFunctionUsingIdWithString(PRUint32 aMsgId, const
04757                                                        char * aExtraInfo)
04758 {
04759   if (m_imapMailFolderSink)
04760   {
04761     nsXPIDLString unicodeStr;
04762     nsresult rv = CopyMUTF7toUTF16(nsDependentCString(aExtraInfo), unicodeStr);
04763     if (NS_SUCCEEDED(rv))
04764       m_imapMailFolderSink->ProgressStatus(this, aMsgId, unicodeStr);
04765   }
04766 }
04767 
04768 void
04769 nsImapProtocol::PercentProgressUpdateEvent(PRUnichar *message, PRInt32 currentProgress, PRInt32 maxProgress)
04770 {
04771 
04772   PRInt64 nowMS = LL_ZERO;
04773   PRInt32 percent = (100 * currentProgress) / maxProgress;
04774   if (percent == m_lastPercent)
04775     return; // hasn't changed, right? So just return. Do we need to clear this anywhere?
04776 
04777   if (percent < 100)  // always need to do 100%
04778   {
04779     int64 minIntervalBetweenProgress;
04780 
04781     LL_I2L(minIntervalBetweenProgress, 750);
04782     int64 diffSinceLastProgress;
04783     LL_I2L(nowMS, PR_IntervalToMilliseconds(PR_IntervalNow()));
04784     LL_SUB(diffSinceLastProgress, nowMS, m_lastProgressTime); // r = a - b
04785     LL_SUB(diffSinceLastProgress, diffSinceLastProgress, minIntervalBetweenProgress); // r = a - b
04786     if (!LL_GE_ZERO(diffSinceLastProgress))
04787       return;
04788   }
04789 
04790   m_lastPercent = percent;
04791   m_lastProgressTime = nowMS;
04792 
04793   // set our max progress as the content length on the mock channel
04794   if (m_mockChannel)
04795       m_mockChannel->SetContentLength(maxProgress);
04796       
04797 
04798   if (m_imapMailFolderSink)
04799       m_imapMailFolderSink->PercentProgress(this, message, currentProgress, maxProgress);
04800 }
04801 
04802   // imap commands issued by the parser
04803 void
04804 nsImapProtocol::Store(const char * messageList, const char * messageData,
04805                       PRBool idsAreUid)
04806 {
04807 
04808   // turn messageList back into key array and then back into a message id list,
04809   // but use the flag state to handle ranges correctly.
04810   nsCString messageIdList;
04811   nsMsgKeyArray msgKeys;
04812   if (idsAreUid)
04813     ParseUidString(messageList, msgKeys);
04814 
04815   PRInt32 msgCountLeft = msgKeys.GetSize();
04816   PRUint32 msgsHandled = 0;
04817   do 
04818   {
04819     nsCString idString;
04820 
04821     PRUint32 msgsToHandle = msgCountLeft;
04822     if (idsAreUid)
04823       AllocateImapUidString(msgKeys.GetArray() + msgsHandled, msgsToHandle, m_flagState, idString);  // 20 * 200
04824     else
04825       idString.Assign(messageList);
04826 
04827 
04828     msgsHandled += msgsToHandle;
04829     msgCountLeft -= msgsToHandle;
04830 
04831     IncrementCommandTagNumber();
04832     const char *formatString;
04833     if (idsAreUid)
04834         formatString = "%s uid store %s %s\015\012";
04835     else
04836         formatString = "%s store %s %s\015\012";
04837         
04838     // we might need to close this mailbox after this
04839     m_closeNeededBeforeSelect = GetDeleteIsMoveToTrash() &&
04840         (PL_strcasestr(messageData, "\\Deleted"));
04841 
04842     const char *commandTag = GetServerCommandTag();
04843     int protocolStringSize = PL_strlen(formatString) +
04844           PL_strlen(messageList) + PL_strlen(messageData) +
04845           PL_strlen(commandTag) + 1;
04846     char *protocolString = (char *) PR_CALLOC( protocolStringSize );
04847 
04848     if (protocolString)
04849     {
04850       PR_snprintf(protocolString, // string to create
04851                     protocolStringSize, // max size
04852                     formatString, // format string
04853                     commandTag, // command tag
04854                     idString.get(),
04855                     messageData);
04856 
04857       nsresult rv = SendData(protocolString);
04858       if (NS_SUCCEEDED(rv))
04859       {
04860         m_flagChangeCount++;
04861         ParseIMAPandCheckForNewMail(protocolString);
04862         if (GetServerStateParser().LastCommandSuccessful() && CheckNeeded())
04863           Check();
04864       }
04865       PR_Free(protocolString);
04866     }
04867     else
04868       HandleMemoryFailure();
04869   }
04870   while (msgCountLeft > 0 && !DeathSignalReceived());
04871 
04872 }
04873 
04874 void
04875 nsImapProtocol::IssueUserDefinedMsgCommand(const char *command, const char * messageList)
04876 {
04877   IncrementCommandTagNumber();
04878     
04879   const char *formatString;
04880   formatString = "%s uid %s %s\015\012";
04881         
04882   const char *commandTag = GetServerCommandTag();
04883   int protocolStringSize = PL_strlen(formatString) +
04884         PL_strlen(messageList) + PL_strlen(command) +
04885         PL_strlen(commandTag) + 1;
04886   char *protocolString = (char *) PR_CALLOC( protocolStringSize );
04887 
04888   if (protocolString)
04889   {
04890     PR_snprintf(protocolString, // string to create
04891                   protocolStringSize, // max size
04892                   formatString, // format string
04893                   commandTag, // command tag
04894                   command, 
04895                   messageList);
04896       
04897     nsresult rv = SendData(protocolString);
04898     if (NS_SUCCEEDED(rv))
04899       ParseIMAPandCheckForNewMail(protocolString);
04900     PR_Free(protocolString);
04901   }
04902   else
04903     HandleMemoryFailure();
04904 }
04905 
04906 void
04907 nsImapProtocol::UidExpunge(const char* messageSet)
04908 {
04909     IncrementCommandTagNumber();
04910     nsCString command(GetServerCommandTag());
04911     command.Append(" uid expunge ");
04912     command.Append(messageSet);
04913     command.Append(CRLF);
04914     nsresult rv = SendData(command.get());
04915     if (NS_SUCCEEDED(rv))
04916         ParseIMAPandCheckForNewMail();
04917 }
04918 
04919 void
04920 nsImapProtocol::Expunge()
04921 {
04922   ProgressEventFunctionUsingId (IMAP_STATUS_EXPUNGING_MAILBOX);
04923 
04924   if(gCheckDeletedBeforeExpunge)
04925   {
04926     GetServerStateParser().ResetSearchResultSequence();
04927     Search("SEARCH DELETED", PR_FALSE, PR_FALSE);
04928     if (GetServerStateParser().LastCommandSuccessful()) 
04929     {
04930       nsImapSearchResultIterator *search = GetServerStateParser().CreateSearchResultIterator();
04931       nsMsgKey key = search->GetNextMessageNumber();
04932       delete search;
04933       if (key == 0)
04934         return;  //no deleted messages to expunge (bug 235004)
04935     }
04936   }
04937 
04938   IncrementCommandTagNumber();
04939   nsCAutoString command(GetServerCommandTag());
04940   command.Append(" expunge"CRLF);
04941   
04942   nsresult rv = SendData(command.get());
04943   if (NS_SUCCEEDED(rv))
04944     ParseIMAPandCheckForNewMail();
04945 }
04946 
04947 void
04948 nsImapProtocol::HandleMemoryFailure()
04949 {
04950     PR_CEnterMonitor(this);
04951     // **** jefft fix me!!!!!! ******
04952     // m_imapThreadIsRunning = PR_FALSE;
04953     // SetConnectionStatus(-1);
04954     PR_CExitMonitor(this);
04955 }
04956 
04957 void nsImapProtocol::HandleCurrentUrlError()
04958 {
04959 #ifdef UNREADY_CODE
04960   if (fCurrentUrl->GetIMAPurlType() == TIMAPUrl::kSelectFolder)
04961   {
04962     // let the front end know the select failed so they
04963     // don't leave the view without a database.
04964     nsImapMailboxSpec *notSelectedSpec = new nsImapMailboxSpec;
04965     if (notSelectedSpec)
04966     {
04967       NS_ADDREF(notSelectedSpec);
04968       notSelectedSpec->allocatedPathName = fCurrentUrl->CreateCanonicalSourceFolderPathString();
04969       notSelectedSpec->hostName = fCurrentUrl->GetUrlHost();
04970       notSelectedSpec->folderSelected = PR_FALSE;
04971       notSelectedSpec->flagState = NULL;
04972       notSelectedSpec->onlineVerified = PR_FALSE;
04973       UpdatedMailboxSpec(notSelectedSpec);
04974     }
04975   }
04976   else 
04977 #endif
04978     // this is to handle a move/copy failing, especially because the user
04979     // cancelled the password prompt.
04980   nsresult res;
04981   res = m_runningUrl->GetImapAction(&m_imapAction);
04982     if (m_imapAction == nsIImapUrl::nsImapOfflineToOnlineMove || m_imapAction == nsIImapUrl::nsImapAppendMsgFromFile
04983       || m_imapAction == nsIImapUrl::nsImapAppendDraftFromFile)
04984   {
04985     if (m_imapMailFolderSink)
04986       m_imapMailFolderSink->OnlineCopyCompleted(this, ImapOnlineCopyStateType::kFailedCopy);
04987   }
04988 }
04989 
04990 void nsImapProtocol::StartTLS()
04991 {
04992     IncrementCommandTagNumber();
04993     nsCString command(GetServerCommandTag());
04994 
04995     command.Append(" STARTTLS" CRLF);
04996             
04997     nsresult rv = SendData(command.get());
04998     if (NS_SUCCEEDED(rv))
04999         ParseIMAPandCheckForNewMail();
05000 }
05001 
05002 void nsImapProtocol::Capability()
05003 {
05004 
05005     ProgressEventFunctionUsingId (IMAP_STATUS_CHECK_COMPAT);
05006     IncrementCommandTagNumber();
05007     nsCString command(GetServerCommandTag());
05008 
05009     command.Append(" capability" CRLF);
05010             
05011     nsresult rv = SendData(command.get());
05012     if (NS_SUCCEEDED(rv))
05013         ParseIMAPandCheckForNewMail();
05014     if (!gUseLiteralPlus)
05015     {
05016       PRUint32 capabilityFlag = GetServerStateParser().GetCapabilityFlag();
05017       if (capabilityFlag & kLiteralPlusCapability)
05018       {
05019         GetServerStateParser().SetCapabilityFlag(capabilityFlag & ~kLiteralPlusCapability);
05020         m_hostSessionList->SetCapabilityForHost(GetImapServerKey(), capabilityFlag & ~kLiteralPlusCapability);
05021       }
05022     }
05023 }
05024 
05025 void nsImapProtocol::Language()
05026 {
05027   // only issue the language request if we haven't done so already...
05028   if (!TestFlag(IMAP_ISSUED_LANGUAGE_REQUEST))
05029   {
05030     SetFlag(IMAP_ISSUED_LANGUAGE_REQUEST);
05031     ProgressEventFunctionUsingId (IMAP_STATUS_CHECK_COMPAT);
05032     IncrementCommandTagNumber();
05033     nsCString command(GetServerCommandTag());
05034 
05035     // extract the desired language attribute from prefs
05036     nsresult rv = NS_OK; 
05037 
05038     // we need to parse out the first language out of this comma separated list....
05039     // i.e if we have en,ja we only want to send en to the server.
05040     if (mAcceptLanguages.get())
05041     {
05042       nsCAutoString extractedLanguage;
05043       extractedLanguage.AssignWithConversion(mAcceptLanguages.get());
05044       PRInt32 pos = extractedLanguage.FindChar(',');
05045       if (pos > 0) // we have a comma separated list of languages...
05046         extractedLanguage.Truncate(pos); // truncate everything after the first comma (including the comma)
05047       
05048       if (extractedLanguage.IsEmpty())
05049         return;
05050 
05051       command.Append(" LANGUAGE ");
05052       command.Append(extractedLanguage); 
05053       command.Append(CRLF);
05054             
05055       rv = SendData(command.get());
05056       if (NS_SUCCEEDED(rv))
05057         ParseIMAPandCheckForNewMail(nsnull, PR_TRUE /* ignore bad or no result from the server for this command */);
05058     }
05059   }
05060 }
05061 
05062 void nsImapProtocol::EscapeUserNamePasswordString(const char *strToEscape, nsCString *resultStr)
05063 {
05064   if (strToEscape) 
05065   {
05066     PRUint32 i = 0;
05067     PRUint32 escapeStrlen = strlen(strToEscape);
05068     for (i=0; i<escapeStrlen; i++)
05069     {
05070         if (strToEscape[i] == '\\' || strToEscape[i] == '\"') 
05071         {
05072             resultStr->Append('\\');
05073         }
05074         resultStr->Append(strToEscape[i]);
05075     }
05076   }
05077 }
05078 
05079 void nsImapProtocol::InsecureLogin(const char *userName, const char *password)
05080 {
05081   ProgressEventFunctionUsingId (IMAP_STATUS_SENDING_LOGIN);
05082   IncrementCommandTagNumber();
05083   nsCString command (GetServerCommandTag());
05084   nsCAutoString escapedUserName;
05085   command.Append(" login \"");
05086   EscapeUserNamePasswordString(userName, &escapedUserName);
05087   command.Append(escapedUserName);
05088   command.Append("\" \"");
05089 
05090   // if the password contains a \, login will fail
05091   // turn foo\bar into foo\\bar
05092   nsCAutoString correctedPassword;
05093   EscapeUserNamePasswordString(password, &correctedPassword);
05094   command.Append(correctedPassword);
05095   command.Append("\""CRLF);
05096 
05097   nsresult rv = SendData(command.get(), PR_TRUE /* suppress logging */);
05098   
05099   if (NS_SUCCEEDED(rv))
05100      ParseIMAPandCheckForNewMail();
05101 }
05102 
05103 nsresult nsImapProtocol::AuthLogin(const char *userName, const char *password, eIMAPCapabilityFlag flag)
05104 {
05105   ProgressEventFunctionUsingId (IMAP_STATUS_SENDING_AUTH_LOGIN);
05106   IncrementCommandTagNumber();
05107 
05108   char * currentCommand=nsnull;
05109   nsresult rv;
05110 
05111   if (flag & kHasCRAMCapability)
05112   {
05113       // inform the server that we want to begin a CRAM authentication procedure...
05114       nsCAutoString command (GetServerCommandTag());
05115       command.Append(" authenticate CRAM-MD5" CRLF);
05116       rv = SendData(command.get());
05117       ParseIMAPandCheckForNewMail();
05118       if (GetServerStateParser().LastCommandSuccessful()) 
05119       {
05120         char *digest = nsnull;
05121         char *cramDigest = GetServerStateParser().fAuthChallenge;
05122         char * decodedChallenge = PL_Base64Decode(cramDigest, 
05123                                                   strlen(cramDigest), nsnull);
05124         if (m_imapServerSink)
05125           rv = m_imapServerSink->CramMD5Hash(decodedChallenge, password, &digest);
05126 
05127         PR_Free(decodedChallenge);
05128         if (NS_SUCCEEDED(rv) && digest)
05129         {
05130           nsCAutoString encodedDigest;
05131           char hexVal[8];
05132 
05133           for (PRUint32 j=0; j<16; j++) 
05134           {
05135             PR_snprintf (hexVal,8, "%.2x", 0x0ff & (unsigned short)(digest[j]));
05136             encodedDigest.Append(hexVal); 
05137           }
05138 
05139           PR_snprintf(m_dataOutputBuf, OUTPUT_BUFFER_SIZE, "%s %s", userName, encodedDigest.get());
05140           char *base64Str = PL_Base64Encode(m_dataOutputBuf, nsCRT::strlen(m_dataOutputBuf), nsnull);
05141           PR_snprintf(m_dataOutputBuf, OUTPUT_BUFFER_SIZE, "%s" CRLF, base64Str);
05142           PR_Free(base64Str);
05143           PR_Free(digest);
05144           rv = SendData(m_dataOutputBuf);
05145           if (NS_SUCCEEDED(rv))
05146             ParseIMAPandCheckForNewMail(command.get());
05147           if (GetServerStateParser().LastCommandSuccessful())
05148             return NS_OK;
05149           GetServerStateParser().SetCapabilityFlag(GetServerStateParser().GetCapabilityFlag() & ~kHasCRAMCapability);
05150 
05151         }
05152     }
05153   } // if CRAM response was received
05154   else if (flag & kHasAuthGssApiCapability)
05155   {
05156     // Only try GSSAPI once - if it fails, its going to be because we don't
05157     // have valid credentials
05158     GetServerStateParser().SetCapabilityFlag(GetServerStateParser().GetCapabilityFlag() & ~(kHasAuthGssApiCapability));
05159 
05160     // We do step1 first, so we don't try GSSAPI against a server which
05161     // we can't get credentials for.
05162     nsCAutoString response;
05163     nsresult gssrv;
05164 
05165     nsCAutoString service("imap@");
05166     service.Append(GetImapHostName());
05167     gssrv = DoGSSAPIStep1(service.get(), userName, response);
05168     NS_ENSURE_SUCCESS(gssrv, gssrv);
05169     
05170     nsCAutoString command (GetServerCommandTag());
05171     command.Append(" authenticate GSSAPI" CRLF);
05172     rv = SendData(command.get());
05173     NS_ENSURE_SUCCESS(rv, rv);
05174 
05175     ParseIMAPandCheckForNewMail("AUTH GSSAPI");
05176     if (GetServerStateParser().LastCommandSuccessful())
05177     {
05178       response += CRLF;
05179       rv = SendData(response.get());
05180       NS_ENSURE_SUCCESS(rv, rv);
05181       ParseIMAPandCheckForNewMail(command.get());
05182 
05183       while (GetServerStateParser().LastCommandSuccessful() && 
05184              NS_SUCCEEDED(gssrv) && gssrv != NS_SUCCESS_AUTH_FINISHED)
05185       {
05186         nsCString challengeStr(GetServerStateParser().fAuthChallenge);
05187         gssrv = DoGSSAPIStep2(challengeStr, response);
05188         if (NS_SUCCEEDED(gssrv)) 
05189         {
05190           response += CRLF;
05191           rv = SendData(response.get());
05192         }
05193         else
05194           rv = SendData("*" CRLF);
05195 
05196         NS_ENSURE_SUCCESS(rv, rv);
05197         ParseIMAPandCheckForNewMail(command.get());
05198       }
05199       rv = gssrv;
05200     }
05201     return rv;
05202   }
05203   else if (flag & (kHasAuthNTLMCapability|kHasAuthMSNCapability))
05204   {
05205     nsCAutoString command (GetServerCommandTag());
05206     command.Append((flag & kHasAuthNTLMCapability) ? " authenticate NTLM" CRLF
05207                                                    : " authenticate MSN" CRLF);
05208     rv = SendData(command.get());
05209     ParseIMAPandCheckForNewMail("AUTH NTLM"); // this just waits for ntlm step 1
05210     if (GetServerStateParser().LastCommandSuccessful()) 
05211     {
05212       nsCAutoString cmd;
05213       rv = DoNtlmStep1(userName, password, cmd);
05214       if (NS_SUCCEEDED(rv))
05215       {
05216         cmd += CRLF;
05217         rv = SendData(cmd.get());
05218         if (NS_SUCCEEDED(rv))
05219         {
05220           ParseIMAPandCheckForNewMail(command.get());
05221           if (GetServerStateParser().LastCommandSuccessful()) 
05222           {
05223             nsCString challengeStr(GetServerStateParser().fAuthChallenge);
05224             nsCString response;
05225             rv = DoNtlmStep2(challengeStr, response);
05226             if (NS_SUCCEEDED(rv))
05227             {
05228               response += CRLF;
05229               rv = SendData(response.get());
05230               ParseIMAPandCheckForNewMail(command.get()); 
05231               if (!GetServerStateParser().LastCommandSuccessful())
05232                 GetServerStateParser().SetCapabilityFlag(GetServerStateParser().GetCapabilityFlag() & ~(kHasAuthNTLMCapability|kHasAuthMSNCapability));
05233             }
05234           }
05235         }
05236       }
05237     }
05238   }
05239   else if (flag & kHasAuthPlainCapability)
05240   {
05241     PR_snprintf(m_dataOutputBuf, OUTPUT_BUFFER_SIZE, "%s authenticate plain" CRLF, GetServerCommandTag());
05242     rv = SendData(m_dataOutputBuf);
05243     NS_ENSURE_SUCCESS(rv, rv);
05244     currentCommand = PL_strdup(m_dataOutputBuf); /* StrAllocCopy(currentCommand, GetOutputBuffer()); */
05245     ParseIMAPandCheckForNewMail();
05246     if (GetServerStateParser().LastCommandSuccessful()) 
05247     {
05248       char plainstr[512]; // placeholder for "<NUL>userName<NUL>password"
05249       int len = 1; // count for first <NUL> char
05250       memset(plainstr, 0, 512);
05251       PR_snprintf(&plainstr[1], 510, "%s", userName);
05252       len += PL_strlen(userName);
05253       len++;  // count for second <NUL> char
05254       PR_snprintf(&plainstr[len], 511-len, "%s", password);
05255       len += PL_strlen(password);
05256     
05257       char *base64Str = PL_Base64Encode(plainstr, len, nsnull);
05258       if (base64Str)
05259       {
05260         PR_snprintf(m_dataOutputBuf, OUTPUT_BUFFER_SIZE, "%s" CRLF, base64Str);
05261         PR_Free(base64Str);
05262         rv = SendData(m_dataOutputBuf, PR_TRUE /* suppress logging */);
05263         if (NS_SUCCEEDED(rv))
05264             ParseIMAPandCheckForNewMail(currentCommand);
05265         if (GetServerStateParser().LastCommandSuccessful())
05266         {
05267           PR_Free(currentCommand);
05268           return NS_OK;
05269         } // if the last command succeeded
05270       } // if we got a base 64 encoded string
05271     } // if the last command succeeded
05272   } // if auth plain capability
05273 
05274   else if (flag & kHasAuthLoginCapability)
05275   {
05276     PR_snprintf(m_dataOutputBuf, OUTPUT_BUFFER_SIZE, "%s authenticate login" CRLF, GetServerCommandTag());
05277     rv = SendData(m_dataOutputBuf);
05278     NS_ENSURE_SUCCESS(rv, rv);
05279     currentCommand = PL_strdup(m_dataOutputBuf);
05280     ParseIMAPandCheckForNewMail();
05281 
05282     if (GetServerStateParser().LastCommandSuccessful()) 
05283     {
05284       char *base64Str = PL_Base64Encode(userName, PL_strlen(userName), nsnull);
05285       if(base64Str)
05286       {
05287         PR_snprintf(m_dataOutputBuf, OUTPUT_BUFFER_SIZE, "%s" CRLF, base64Str);
05288         PR_Free(base64Str);
05289         rv = SendData(m_dataOutputBuf, PR_TRUE /* suppress logging */);
05290         if (NS_SUCCEEDED(rv))
05291             ParseIMAPandCheckForNewMail(currentCommand);
05292       }
05293       if (GetServerStateParser().LastCommandSuccessful()) 
05294       {
05295         base64Str = PL_Base64Encode((char*)password, PL_strlen(password), nsnull);
05296         PR_snprintf(m_dataOutputBuf, OUTPUT_BUFFER_SIZE, "%s" CRLF, base64Str);
05297         PR_Free(base64Str);
05298         rv = SendData(m_dataOutputBuf, PR_TRUE /* suppress logging */);
05299         if (NS_SUCCEEDED(rv))
05300            ParseIMAPandCheckForNewMail(currentCommand);
05301         if (GetServerStateParser().LastCommandSuccessful())
05302         {
05303           PR_Free(currentCommand);
05304           return NS_OK;
05305         }
05306       } // if last command successful
05307     } // if last command successful
05308   } // if has auth login capability
05309 
05310   // Fall back to InsecureLogin() if the user did not request secure authentication
05311   if (!m_useSecAuth && ! (GetServerStateParser().GetCapabilityFlag() & kLoginDisabled))
05312     InsecureLogin(userName, password);
05313 
05314   PR_Free(currentCommand);
05315   return NS_OK;
05316 }
05317 
05318 void nsImapProtocol::OnLSubFolders()
05319 {
05320   // **** use to find out whether Drafts, Sent, & Templates folder
05321   // exists or not even the user didn't subscribe to it
05322   char *mailboxName = OnCreateServerSourceFolderPathString();
05323   if (mailboxName)
05324   {
05325     ProgressEventFunctionUsingId(IMAP_STATUS_LOOKING_FOR_MAILBOX);
05326     IncrementCommandTagNumber();
05327     PR_snprintf(m_dataOutputBuf, OUTPUT_BUFFER_SIZE,"%s list \"\" \"%s\"" CRLF, GetServerCommandTag(), mailboxName);
05328     nsresult rv = SendData(m_dataOutputBuf);
05329 #ifdef UNREADY_CODE
05330     TimeStampListNow();
05331 #endif
05332     if (NS_SUCCEEDED(rv))
05333       ParseIMAPandCheckForNewMail();  
05334     PR_Free(mailboxName);
05335   }
05336   else
05337   {
05338     HandleMemoryFailure();
05339   }
05340 
05341 }
05342 
05343 void nsImapProtocol::OnAppendMsgFromFile()
05344 {
05345   nsCOMPtr<nsIFileSpec> fileSpec;
05346   nsresult rv = NS_OK;
05347   rv = m_runningUrl->GetMsgFileSpec(getter_AddRefs(fileSpec));
05348   if (NS_SUCCEEDED(rv) && fileSpec)
05349   {
05350     char *mailboxName =  OnCreateServerSourceFolderPathString();
05351     if (mailboxName)
05352     {
05353       imapMessageFlagsType flagsToSet = 0;
05354       PRUint32 msgFlags = 0;
05355       PRTime date = 0;
05356       nsXPIDLCString keywords;
05357       if (m_imapMessageSink)
05358         m_imapMessageSink->GetCurMoveCopyMessageInfo(m_runningUrl, &date, 
05359                                                     getter_Copies(keywords), &msgFlags);
05360       
05361       if (msgFlags & MSG_FLAG_READ)
05362         flagsToSet |= kImapMsgSeenFlag;
05363       if (msgFlags & MSG_FLAG_MDN_REPORT_SENT)
05364         flagsToSet |= kImapMsgMDNSentFlag;
05365       // convert msg flag label (0xE000000) to imap flag label (0x0E00)
05366       if (msgFlags & MSG_FLAG_LABELS)
05367         flagsToSet |= (msgFlags & MSG_FLAG_LABELS) >> 16;
05368       if (msgFlags & MSG_FLAG_MARKED)
05369         flagsToSet |= kImapMsgFlaggedFlag;
05370       if (msgFlags & MSG_FLAG_REPLIED)
05371         flagsToSet |= kImapMsgAnsweredFlag;
05372       if (msgFlags & MSG_FLAG_FORWARDED)
05373         flagsToSet |= kImapMsgForwardedFlag;
05374 
05375       // If the message copied was a draft, flag it as such
05376       nsImapAction imapAction;
05377       rv = m_runningUrl->GetImapAction(&imapAction);
05378       if (NS_SUCCEEDED(rv) && (imapAction == nsIImapUrl::nsImapAppendDraftFromFile))
05379         flagsToSet |= kImapMsgDraftFlag;
05380       UploadMessageFromFile(fileSpec, mailboxName, date, flagsToSet, keywords);
05381       PR_Free( mailboxName );
05382     }
05383     else
05384     {
05385       HandleMemoryFailure();
05386     }
05387   }
05388 }
05389 
05390 void nsImapProtocol::UploadMessageFromFile (nsIFileSpec* fileSpec,
05391                                             const char* mailboxName,
05392                                             PRTime date,
05393                                             imapMessageFlagsType flags,
05394                                             nsCString &keywords)
05395 {
05396   if (!fileSpec || !mailboxName) return;
05397   IncrementCommandTagNumber();
05398   
05399   PRUint32 fileSize = 0;
05400   PRInt32 totalSize;
05401   PRUint32 readCount;
05402   char *dataBuffer = nsnull;
05403   nsCString command(GetServerCommandTag());
05404   char* escapedName = CreateEscapedMailboxName(mailboxName);
05405   nsresult rv;
05406   PRBool eof = PR_FALSE;
05407   nsCString flagString;
05408   PRBool hasLiteralPlus = (GetServerStateParser().GetCapabilityFlag() &
05409     kLiteralPlusCapability);
05410   
05411   nsCOMPtr <nsIInputStream> fileInputStream;
05412   
05413   if (escapedName)
05414   {
05415     command.Append(" append \"");
05416     command.Append(escapedName);
05417     command.Append("\"");
05418     if (flags || keywords.Length())
05419     {
05420       command.Append(" (");
05421     
05422       if (flags)
05423       {
05424       SetupMessageFlagsString(flagString, flags,
05425         GetServerStateParser().SupportsUserFlags());
05426       command.Append(flagString);
05427       }
05428       if (keywords.Length())
05429       {
05430         if (flags)
05431           command.Append(' ');
05432         command.Append(keywords);
05433       }
05434       command.Append(")");
05435     }
05436     
05437     // date should never be 0, but just in case...
05438     if (date)
05439     {
05440       /* Use PR_FormatTimeUSEnglish() to format the date in US English format,
05441         then figure out what our local GMT offset is, and append it (since
05442         PR_FormatTimeUSEnglish() can't do that.) Generate four digit years as
05443         per RFC 1123 (superceding RFC 822.)
05444         */
05445       char szDateTime[64];
05446       char dateStr[100];
05447       PRExplodedTime exploded;
05448       PR_ExplodeTime(date, PR_LocalTimeParameters, &exploded);
05449       PR_FormatTimeUSEnglish(szDateTime, sizeof(szDateTime), "%d-%b-%Y %H:%M:%S", &exploded);
05450       PRExplodedTime now;
05451       PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
05452       int gmtoffset = (now.tm_params.tp_gmt_offset + now.tm_params.tp_dst_offset) / 60;
05453       PR_snprintf(dateStr, sizeof(dateStr),
05454                             " \"%s %c%02d%02d\"",
05455                             szDateTime,
05456                             (gmtoffset >= 0 ? '+' : '-'),
05457                             ((gmtoffset >= 0 ? gmtoffset : -gmtoffset) / 60),
05458                             ((gmtoffset >= 0 ? gmtoffset : -gmtoffset) % 60));
05459       
05460       command.Append(dateStr);
05461     }
05462     command.Append(" {");
05463     
05464     dataBuffer = (char*) PR_CALLOC(COPY_BUFFER_SIZE+1);
05465     if (!dataBuffer) goto done;
05466     rv = fileSpec->GetFileSize(&fileSize);
05467     if (NS_FAILED(rv)) goto done;
05468     rv = fileSpec->GetInputStream(getter_AddRefs(fileInputStream));
05469     if (NS_FAILED(rv) || !fileInputStream) goto done;
05470     command.AppendInt((PRInt32)fileSize);
05471     if (hasLiteralPlus)
05472       command.Append("+}" CRLF);
05473     else
05474       command.Append("}" CRLF);
05475     
05476     rv = SendData(command.get());
05477     if (NS_FAILED(rv)) goto done;
05478     
05479     if (!hasLiteralPlus)
05480       ParseIMAPandCheckForNewMail();
05481     
05482     totalSize = fileSize;
05483     readCount = 0;
05484     while(NS_SUCCEEDED(rv) && !eof && totalSize > 0)
05485     {
05486       rv = fileInputStream->Read(dataBuffer, COPY_BUFFER_SIZE, &readCount);
05487       if (NS_SUCCEEDED(rv))
05488       {
05489         NS_ASSERTION(readCount <= (PRUint32) totalSize, "got more bytes than there should be");
05490         dataBuffer[readCount] = 0;
05491         rv = SendData(dataBuffer);
05492         totalSize -= readCount;
05493         PercentProgressUpdateEvent(nsnull, fileSize - totalSize, fileSize);
05494         rv = fileSpec->Eof(&eof);
05495       }
05496     }
05497     if (NS_SUCCEEDED(rv))
05498     {
05499       rv = SendData(CRLF); // complete the append
05500       ParseIMAPandCheckForNewMail(command.get());
05501       
05502       nsImapAction imapAction;
05503       m_runningUrl->GetImapAction(&imapAction);
05504 
05505       if (GetServerStateParser().LastCommandSuccessful() &&  (
05506         imapAction == nsIImapUrl::nsImapAppendDraftFromFile  || imapAction==nsIImapUrl::nsImapAppendMsgFromFile))
05507       {
05508         if (GetServerStateParser().GetCapabilityFlag() &
05509           kUidplusCapability)
05510         {
05511           nsMsgKey newKey = GetServerStateParser().CurrentResponseUID();
05512           if (m_imapMailFolderSink)
05513             m_imapMailFolderSink->SetAppendMsgUid(newKey, m_runningUrl);
05514 
05515           // Courier imap server seems to have problems with recently
05516           // appended messages. Noop seems to clear its confusion.
05517           if (FolderIsSelected(mailboxName))
05518               Noop(); 
05519 
05520           nsXPIDLCString oldMsgId;
05521           rv = m_runningUrl->CreateListOfMessageIdsString(getter_Copies(oldMsgId));
05522           if (NS_SUCCEEDED(rv) && !oldMsgId.IsEmpty())
05523           {
05524             PRBool idsAreUids = PR_TRUE;
05525             m_runningUrl->MessageIdsAreUids(&idsAreUids);
05526             Store(oldMsgId, "+FLAGS (\\Deleted)", idsAreUids);
05527             UidExpunge(oldMsgId);
05528           }
05529         }
05530         // for non UIDPLUS servers,
05531         // this code used to check for imapAction==nsIImapUrl::nsImapAppendMsgFromFile, which
05532         // meant we'd get into this code whenever sending a message, as well
05533         // as when copying messages to an imap folder from local folders or an other imap server.
05534         // This made sending a message slow when there was a large sent folder. I don't believe
05535         // this code worked anyway.
05536         else if (m_imapMailFolderSink && imapAction == nsIImapUrl::nsImapAppendDraftFromFile )
05537         {   // *** code me to search for the newly appended message
05538           // go to selected state
05539           AutoSubscribeToMailboxIfNecessary(mailboxName);
05540           nsCString messageId;
05541           rv = m_imapMailFolderSink->GetMessageId(m_runningUrl, messageId);
05542           if (NS_SUCCEEDED(rv) && !messageId.IsEmpty() &&
05543             GetServerStateParser().LastCommandSuccessful())
05544           {
05545             // if the appended to folder isn't selected in the connection,
05546             // select it.
05547             if (!FolderIsSelected(mailboxName))
05548               SelectMailbox(mailboxName);
05549             else
05550               Noop(); // See if this makes SEARCH work on the newly appended msg.
05551           
05552             if (GetServerStateParser().LastCommandSuccessful())
05553             {
05554               command = "SEARCH UNDELETED HEADER Message-ID ";
05555               command.Append(messageId);
05556             
05557               // Clean up result sequence before issuing the cmd.
05558               GetServerStateParser().ResetSearchResultSequence();
05559             
05560               Search(command.get(), PR_TRUE, PR_FALSE);
05561               if (GetServerStateParser().LastCommandSuccessful())
05562               {
05563                 nsMsgKey newkey = nsMsgKey_None;
05564                 nsImapSearchResultIterator *searchResult = 
05565                   GetServerStateParser().CreateSearchResultIterator();
05566                 newkey = searchResult->GetNextMessageNumber();
05567                 delete searchResult;
05568                 if (newkey != nsMsgKey_None)
05569                   m_imapMailFolderSink->SetAppendMsgUid(newkey, m_runningUrl);
05570               }
05571             }
05572           }
05573         }
05574       }
05575     }
05576   }
05577 done:
05578   PR_Free(dataBuffer);
05579   fileSpec->CloseStream();
05580   nsMemory::Free(escapedName);
05581 }
05582 
05583 //caller must free using PR_Free
05584 char * nsImapProtocol::OnCreateServerSourceFolderPathString()
05585 {
05586   char *sourceMailbox = nsnull;
05587   char hierarchyDelimiter = 0;
05588   char *onlineDelimiter = nsnull;
05589   m_runningUrl->GetOnlineSubDirSeparator(&hierarchyDelimiter);
05590   if (m_imapMailFolderSink)
05591       m_imapMailFolderSink->GetOnlineDelimiter(&onlineDelimiter);
05592   if (onlineDelimiter && *onlineDelimiter != kOnlineHierarchySeparatorUnknown
05593       && *onlineDelimiter != hierarchyDelimiter)
05594       m_runningUrl->SetOnlineSubDirSeparator (*onlineDelimiter);
05595   if (onlineDelimiter)
05596       nsCRT::free(onlineDelimiter);
05597 
05598   m_runningUrl->CreateServerSourceFolderPathString(&sourceMailbox);
05599 
05600   return sourceMailbox;
05601 }
05602 
05603 //caller must free using PR_Free, safe to call from ui thread
05604 char * nsImapProtocol::GetFolderPathString()
05605 {
05606   char *sourceMailbox = nsnull;
05607   char onlineSubDirDelimiter = 0;
05608   PRUnichar hierarchyDelimiter = 0;
05609   nsCOMPtr <nsIMsgFolder> msgFolder;
05610 
05611   m_runningUrl->GetOnlineSubDirSeparator(&onlineSubDirDelimiter);
05612   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
05613   mailnewsUrl->GetFolder(getter_AddRefs(msgFolder));
05614   if (msgFolder)
05615   {
05616     nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(msgFolder);
05617     if (imapFolder)
05618     {
05619       imapFolder->GetHierarchyDelimiter(&hierarchyDelimiter);
05620       if (hierarchyDelimiter != kOnlineHierarchySeparatorUnknown
05621           && onlineSubDirDelimiter != (char) hierarchyDelimiter)
05622           m_runningUrl->SetOnlineSubDirSeparator ((char) hierarchyDelimiter);
05623     }
05624   }
05625   m_runningUrl->CreateServerSourceFolderPathString(&sourceMailbox);
05626 
05627   return sourceMailbox;
05628 }
05629 
05630 nsresult nsImapProtocol::CreateServerSourceFolderPathString(char **result)
05631 {
05632   NS_ENSURE_ARG(result);
05633   *result = OnCreateServerSourceFolderPathString();
05634   return (*result) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
05635 }
05636 
05637 //caller must free using PR_Free
05638 char * nsImapProtocol::OnCreateServerDestinationFolderPathString()
05639 {
05640   char *destinationMailbox = nsnull;
05641   char hierarchyDelimiter = 0;
05642   char *onlineDelimiter = nsnull;
05643   m_runningUrl->GetOnlineSubDirSeparator(&hierarchyDelimiter);
05644   if (m_imapMailFolderSink)
05645       m_imapMailFolderSink->GetOnlineDelimiter(&onlineDelimiter);
05646   if (onlineDelimiter && *onlineDelimiter != kOnlineHierarchySeparatorUnknown
05647       && *onlineDelimiter != hierarchyDelimiter)
05648       m_runningUrl->SetOnlineSubDirSeparator (*onlineDelimiter);
05649   if (onlineDelimiter)
05650       nsCRT::free(onlineDelimiter);
05651 
05652   m_runningUrl->CreateServerDestinationFolderPathString(&destinationMailbox);
05653 
05654   return destinationMailbox;
05655 }
05656 
05657 void nsImapProtocol::OnCreateFolder(const char * aSourceMailbox)
05658 {
05659   PRBool created = CreateMailboxRespectingSubscriptions(aSourceMailbox);
05660   if (created)
05661   {
05662     m_hierarchyNameState = kListingForCreate;
05663     List(aSourceMailbox, PR_FALSE);
05664     m_hierarchyNameState = kNoOperationInProgress;
05665   }
05666   else
05667     FolderNotCreated(aSourceMailbox);
05668 }
05669 
05670 void nsImapProtocol::OnEnsureExistsFolder(const char * aSourceMailbox)
05671 {
05672 
05673   List(aSourceMailbox, PR_FALSE); // how to tell if that succeeded?
05674   PRBool exists = PR_FALSE;
05675 
05676   // try converting aSourceMailbox to canonical format
05677 
05678   nsIMAPNamespace *nsForMailbox = nsnull;
05679   m_hostSessionList->GetNamespaceForMailboxForHost(GetImapServerKey(),
05680                                                      aSourceMailbox, nsForMailbox);
05681   // NS_ASSERTION (nsForMailbox, "Oops .. null nsForMailbox\n");
05682 
05683   nsXPIDLCString name;
05684 
05685   if (nsForMailbox)
05686     m_runningUrl->AllocateCanonicalPath(aSourceMailbox,
05687                                             nsForMailbox->GetDelimiter(),
05688                                             getter_Copies(name));
05689   else
05690     m_runningUrl->AllocateCanonicalPath(aSourceMailbox,
05691                                             kOnlineHierarchySeparatorUnknown, 
05692                                             getter_Copies(name));
05693 
05694   if (m_imapServerSink)
05695     m_imapServerSink->FolderVerifiedOnline(name, &exists);
05696 
05697   if (exists)
05698   {
05699     Subscribe(aSourceMailbox);
05700   }
05701   else
05702   {
05703     PRBool created = CreateMailboxRespectingSubscriptions(aSourceMailbox);
05704     if (created)
05705     {
05706         List(aSourceMailbox, PR_FALSE);
05707     }
05708   }
05709   if (!GetServerStateParser().LastCommandSuccessful())
05710         FolderNotCreated(aSourceMailbox);
05711 }
05712 
05713 
05714 void nsImapProtocol::OnSubscribe(const char * sourceMailbox)
05715 {
05716   Subscribe(sourceMailbox);
05717 }
05718 
05719 void nsImapProtocol::OnUnsubscribe(const char * sourceMailbox)
05720 {
05721   // When we try to auto-unsubscribe from \Noselect folders,
05722   // some servers report errors if we were already unsubscribed
05723   // from them.
05724   PRBool lastReportingErrors = GetServerStateParser().GetReportingErrors();
05725   GetServerStateParser().SetReportingErrors(PR_FALSE);
05726   Unsubscribe(sourceMailbox);
05727   GetServerStateParser().SetReportingErrors(lastReportingErrors);
05728 }
05729 
05730 void nsImapProtocol::RefreshACLForFolderIfNecessary(const char *mailboxName)
05731 {
05732   if (GetServerStateParser().ServerHasACLCapability())
05733   {
05734     if (!m_folderNeedsACLRefreshed && m_imapMailFolderSink)
05735       m_imapMailFolderSink->GetFolderNeedsACLListed(&m_folderNeedsACLRefreshed);
05736     if (m_folderNeedsACLRefreshed)
05737     {
05738       RefreshACLForFolder(mailboxName);
05739       m_folderNeedsACLRefreshed = PR_FALSE;
05740     }
05741   }
05742 }
05743 
05744 void nsImapProtocol::RefreshACLForFolder(const char *mailboxName)
05745 {
05746   
05747   nsIMAPNamespace *ns = nsnull;
05748   m_hostSessionList->GetNamespaceForMailboxForHost(GetImapServerKey(), mailboxName, ns);
05749   if (ns)
05750   {
05751     switch (ns->GetType())
05752     {
05753     case kPersonalNamespace:
05754       // It's a personal folder, most likely.
05755       // I find it hard to imagine a server that supports ACL that doesn't support NAMESPACE,
05756       // so most likely we KNOW that this is a personal, rather than the default, namespace.
05757       
05758       // First, clear what we have.
05759       ClearAllFolderRights(mailboxName, ns);
05760       // Now, get the new one.
05761       GetMyRightsForFolder(mailboxName);
05762       if (m_imapMailFolderSink)
05763       {
05764         PRUint32 aclFlags = 0;
05765         if (NS_SUCCEEDED(m_imapMailFolderSink->GetAclFlags(&aclFlags)) && aclFlags & IMAP_ACL_ADMINISTER_FLAG)
05766             GetACLForFolder(mailboxName);
05767       }
05768           
05769       // We're all done, refresh the icon/flags for this folder
05770       RefreshFolderACLView(mailboxName, ns);
05771       break;
05772     default:
05773       // We know it's a public folder or other user's folder.
05774       // We only want our own rights
05775       
05776       // First, clear what we have
05777       ClearAllFolderRights(mailboxName, ns);
05778       // Now, get the new one.
05779       GetMyRightsForFolder(mailboxName);
05780       // We're all done, refresh the icon/flags for this folder
05781       RefreshFolderACLView(mailboxName, ns);
05782       break;
05783     }
05784   }
05785   else
05786   {
05787     // no namespace, not even default... can this happen?
05788     NS_ASSERTION(PR_FALSE, "couldn't get namespace");
05789   }
05790 }
05791 
05792 void nsImapProtocol::RefreshFolderACLView(const char *mailboxName, nsIMAPNamespace *nsForMailbox)
05793 {
05794   nsXPIDLCString canonicalMailboxName;
05795   
05796   if (nsForMailbox)
05797     m_runningUrl->AllocateCanonicalPath(mailboxName, nsForMailbox->GetDelimiter(), getter_Copies(canonicalMailboxName));
05798   else
05799     m_runningUrl->AllocateCanonicalPath(mailboxName, kOnlineHierarchySeparatorUnknown, getter_Copies(canonicalMailboxName));
05800   
05801   if (m_imapServerSink)
05802     m_imapServerSink->RefreshFolderRights(canonicalMailboxName);
05803 }
05804 
05805 void nsImapProtocol::GetACLForFolder(const char *mailboxName)
05806 {
05807   IncrementCommandTagNumber();
05808 
05809   nsCString command(GetServerCommandTag());
05810   char *escapedName = CreateEscapedMailboxName(mailboxName);
05811   command.Append(" getacl \"");
05812   command.Append(escapedName);
05813   command.Append("\"" CRLF);
05814             
05815   nsMemory::Free(escapedName);
05816             
05817   nsresult rv = SendData(command.get());
05818   if (NS_SUCCEEDED(rv))
05819     ParseIMAPandCheckForNewMail();
05820 }
05821 
05822 void nsImapProtocol::OnRefreshAllACLs()
05823 {
05824   m_hierarchyNameState = kListingForInfoOnly;
05825   nsIMAPMailboxInfo *mb = NULL;
05826   
05827   // This will fill in the list
05828   List("*", PR_TRUE);
05829   
05830   PRInt32 total = m_listedMailboxList.Count(), count = 0;
05831   GetServerStateParser().SetReportingErrors(PR_FALSE);
05832   for (PRInt32 i = 0; i < total; i++)
05833   {
05834     mb = (nsIMAPMailboxInfo *) m_listedMailboxList.ElementAt(i);
05835     if (mb) // paranoia
05836     {
05837       char *onlineName = nsnull;
05838       m_runningUrl->AllocateServerPath(mb->GetMailboxName(),
05839         mb->GetDelimiter(), &onlineName);
05840       if (onlineName)
05841       {
05842         RefreshACLForFolder(onlineName);
05843         nsCRT::free(onlineName);
05844       }
05845       PercentProgressUpdateEvent(NULL, count, total);
05846       delete mb;
05847       count++;
05848     }
05849   }
05850   m_listedMailboxList.Clear();
05851   
05852   PercentProgressUpdateEvent(NULL, 100, 100);
05853   GetServerStateParser().SetReportingErrors(PR_TRUE);
05854   m_hierarchyNameState = kNoOperationInProgress;
05855 }
05856 
05857 // any state commands
05858 void nsImapProtocol::Logout(PRBool shuttingDown /* = PR_FALSE */, 
05859                             PRBool waitForResponse /* = PR_TRUE */)
05860 {
05861   if (!shuttingDown)
05862     ProgressEventFunctionUsingId (IMAP_STATUS_LOGGING_OUT);
05863 
05864 /******************************************************************
05865  * due to the undo functionality we cannot issule a close when logout; there
05866  * is no way to do an undo if the message has been permanently expunge
05867  * jt - 07/12/1999
05868 
05869     PRBool closeNeeded = GetServerStateParser().GetIMAPstate() ==
05870         nsImapServerResponseParser::kFolderSelected;
05871 
05872     if (closeNeeded && GetDeleteIsMoveToTrash())
05873         Close();
05874 ********************/
05875 
05876   IncrementCommandTagNumber();
05877 
05878   nsCString command(GetServerCommandTag());
05879 
05880   command.Append(" logout" CRLF);
05881 
05882   nsresult rv = SendData(command.get());
05883   if (m_transport && shuttingDown)
05884     m_transport->SetTimeout(nsISocketTransport::TIMEOUT_READ_WRITE, 5);
05885   // the socket may be dead before we read the response, so drop it.
05886   if (NS_SUCCEEDED(rv) && waitForResponse)
05887       ParseIMAPandCheckForNewMail();
05888 }
05889 
05890 void nsImapProtocol::Noop()
05891 {
05892   //ProgressUpdateEvent("noop...");
05893   IncrementCommandTagNumber();
05894   nsCString command(GetServerCommandTag());
05895     
05896   command.Append(" noop" CRLF);
05897             
05898   nsresult rv = SendData(command.get());
05899   if (NS_SUCCEEDED(rv))
05900       ParseIMAPandCheckForNewMail();
05901 }
05902 
05903 void nsImapProtocol::XServerInfo()
05904 {
05905 
05906     ProgressEventFunctionUsingId (IMAP_GETTING_SERVER_INFO);
05907     IncrementCommandTagNumber();
05908     nsCString command(GetServerCommandTag());
05909   
05910   command.Append(" XSERVERINFO MANAGEACCOUNTURL MANAGELISTSURL MANAGEFILTERSURL" CRLF);
05911           
05912     nsresult rv = SendData(command.get());
05913     if (NS_SUCCEEDED(rv))
05914         ParseIMAPandCheckForNewMail();
05915 }
05916 
05917 void nsImapProtocol::Netscape()
05918 {
05919     ProgressEventFunctionUsingId (IMAP_GETTING_SERVER_INFO);
05920     IncrementCommandTagNumber();
05921     
05922     nsCString command(GetServerCommandTag());
05923   
05924   command.Append(" netscape" CRLF);
05925           
05926     nsresult rv = SendData(command.get());
05927     if (NS_SUCCEEDED(rv))
05928         ParseIMAPandCheckForNewMail();
05929 }
05930 
05931 
05932 
05933 void nsImapProtocol::XMailboxInfo(const char *mailboxName)
05934 {
05935 
05936     ProgressEventFunctionUsingId (IMAP_GETTING_MAILBOX_INFO);
05937     IncrementCommandTagNumber();
05938     nsCString command(GetServerCommandTag());
05939 
05940   command.Append(" XMAILBOXINFO \"");
05941   command.Append(mailboxName);
05942   command.Append("\" MANAGEURL POSTURL" CRLF);
05943             
05944     nsresult rv = SendData(command.get());
05945     if (NS_SUCCEEDED(rv))
05946         ParseIMAPandCheckForNewMail();
05947 }
05948 
05949 void nsImapProtocol::Namespace()
05950 {
05951 
05952     IncrementCommandTagNumber();
05953     
05954   nsCString command(GetServerCommandTag());
05955   command.Append(" namespace" CRLF);
05956             
05957     nsresult rv = SendData(command.get());
05958     if (NS_SUCCEEDED(rv))
05959         ParseIMAPandCheckForNewMail();
05960 }
05961 
05962 
05963 void nsImapProtocol::MailboxData()
05964 {
05965     IncrementCommandTagNumber();
05966 
05967   nsCString command(GetServerCommandTag());
05968   command.Append(" mailboxdata" CRLF);
05969             
05970     nsresult rv = SendData(command.get());
05971     if (NS_SUCCEEDED(rv))
05972         ParseIMAPandCheckForNewMail();
05973 }
05974 
05975 
05976 void nsImapProtocol::GetMyRightsForFolder(const char *mailboxName)
05977 {
05978   IncrementCommandTagNumber();
05979 
05980   nsCString command(GetServerCommandTag());
05981   char *escapedName = CreateEscapedMailboxName(mailboxName);
05982     
05983   if (MailboxIsNoSelectMailbox(escapedName))
05984     return; // Don't issue myrights on Noselect folder
05985 
05986   command.Append(" myrights \"");
05987   command.Append(escapedName);
05988   command.Append("\"" CRLF);
05989             
05990   nsMemory::Free(escapedName);
05991             
05992   nsresult rv = SendData(command.get());
05993   if (NS_SUCCEEDED(rv))
05994     ParseIMAPandCheckForNewMail();
05995 }
05996 
05997 PRBool nsImapProtocol::FolderIsSelected(const char *mailboxName)
05998 {
05999   return (GetServerStateParser().GetIMAPstate() ==
06000       nsImapServerResponseParser::kFolderSelected && GetServerStateParser().GetSelectedMailboxName() && 
06001       PL_strcmp(GetServerStateParser().GetSelectedMailboxName(),
06002                     mailboxName) == 0);
06003 }
06004 
06005 void nsImapProtocol::OnStatusForFolder(const char *mailboxName)
06006 {
06007 
06008   if (FolderIsSelected(mailboxName))
06009   {
06010     PRInt32 prevNumMessages = GetServerStateParser().NumberOfMessages();
06011     Noop();
06012     // OnNewIdleMessages will cause the ui thread to update the folder
06013     if (m_imapMailFolderSink && GetServerStateParser().NumberOfRecentMessages()
06014           || prevNumMessages != GetServerStateParser().NumberOfMessages())
06015       m_imapMailFolderSink->OnNewIdleMessages();
06016     return;
06017   }
06018 
06019   IncrementCommandTagNumber();
06020 
06021   nsCAutoString command(GetServerCommandTag());
06022   char *escapedName = CreateEscapedMailboxName(mailboxName);
06023   
06024   command.Append(" STATUS \"");
06025   command.Append(escapedName);
06026   command.Append("\" (UIDNEXT MESSAGES UNSEEN RECENT)" CRLF);
06027           
06028   nsMemory::Free(escapedName);
06029         
06030   nsresult rv = SendData(command.get());
06031   if (NS_SUCCEEDED(rv))
06032       ParseIMAPandCheckForNewMail();
06033 
06034   if (GetServerStateParser().LastCommandSuccessful())
06035   {
06036     nsImapMailboxSpec *new_spec = GetServerStateParser().CreateCurrentMailboxSpec(mailboxName);
06037     if (new_spec && m_imapMailFolderSink)
06038       m_imapMailFolderSink->UpdateImapMailboxStatus(this, new_spec);
06039     NS_IF_RELEASE(new_spec);
06040   }
06041 }
06042 
06043 
06044 void nsImapProtocol::OnListFolder(const char * aSourceMailbox, PRBool aBool)
06045 {
06046   List(aSourceMailbox, aBool);
06047 }
06048 
06049 
06050 // Returns PR_TRUE if the mailbox is a NoSelect mailbox.
06051 // If we don't know about it, returns PR_FALSE.
06052 PRBool nsImapProtocol::MailboxIsNoSelectMailbox(const char *mailboxName)
06053 {
06054   PRBool rv = PR_FALSE;
06055 
06056   nsIMAPNamespace *nsForMailbox = nsnull;
06057     m_hostSessionList->GetNamespaceForMailboxForHost(GetImapServerKey(),
06058                                                      mailboxName, nsForMailbox);
06059   // NS_ASSERTION (nsForMailbox, "Oops .. null nsForMailbox\n");
06060 
06061   char *name;
06062 
06063   if (nsForMailbox)
06064     m_runningUrl->AllocateCanonicalPath(mailboxName,
06065                                             nsForMailbox->GetDelimiter(),
06066                                             &name);
06067   else
06068     m_runningUrl->AllocateCanonicalPath(mailboxName,
06069                                             kOnlineHierarchySeparatorUnknown, 
06070                                             &name);
06071 
06072   if (!name)
06073     return PR_FALSE;
06074 
06075   NS_ASSERTION(m_imapServerSink, "unexpected, no imap server sink, see bug #194335");
06076   if (m_imapServerSink)
06077     m_imapServerSink->FolderIsNoSelect(name, &rv);
06078 
06079   PL_strfree(name);
06080   return rv;
06081 }
06082 
06083 nsresult nsImapProtocol::SetFolderAdminUrl(const char *mailboxName)
06084 {
06085   nsresult rv = NS_ERROR_NULL_POINTER; // if m_imapServerSink is null, rv will be this.
06086 
06087   nsIMAPNamespace *nsForMailbox = nsnull;
06088   m_hostSessionList->GetNamespaceForMailboxForHost(GetImapServerKey(),
06089                                                      mailboxName, nsForMailbox);
06090 
06091   nsXPIDLCString name;
06092 
06093   if (nsForMailbox)
06094     m_runningUrl->AllocateCanonicalPath(mailboxName,
06095                                             nsForMailbox->GetDelimiter(),
06096                                             getter_Copies(name));
06097   else
06098     m_runningUrl->AllocateCanonicalPath(mailboxName,
06099                                             kOnlineHierarchySeparatorUnknown, 
06100                                             getter_Copies(name));
06101 
06102   if (m_imapServerSink)
06103     rv = m_imapServerSink->SetFolderAdminURL(name, GetServerStateParser().GetManageFolderUrl());
06104   return rv;
06105 }
06106 // returns PR_TRUE is the delete succeeded (regardless of subscription changes)
06107 PRBool nsImapProtocol::DeleteMailboxRespectingSubscriptions(const char *mailboxName)
06108 {
06109   PRBool rv = PR_TRUE;
06110   if (!MailboxIsNoSelectMailbox(mailboxName))
06111   {
06112     // Only try to delete it if it really exists
06113     DeleteMailbox(mailboxName);
06114     rv = GetServerStateParser().LastCommandSuccessful();
06115   }
06116 
06117   // We can unsubscribe even if the mailbox doesn't exist.
06118   if (rv && m_autoUnsubscribe) // auto-unsubscribe is on
06119   {
06120     PRBool reportingErrors = GetServerStateParser().GetReportingErrors();
06121     GetServerStateParser().SetReportingErrors(PR_FALSE);
06122     Unsubscribe(mailboxName);
06123     GetServerStateParser().SetReportingErrors(reportingErrors);
06124 
06125   }
06126   return (rv);
06127 }
06128 
06129 // returns PR_TRUE is the rename succeeded (regardless of subscription changes)
06130 // reallyRename tells us if we should really do the rename (PR_TRUE) or if we should just move subscriptions (PR_FALSE)
06131 PRBool nsImapProtocol::RenameMailboxRespectingSubscriptions(const char *existingName, const char *newName, PRBool reallyRename)
06132 {
06133   PRBool rv = PR_TRUE;
06134   if (reallyRename && !MailboxIsNoSelectMailbox(existingName))
06135   {
06136     RenameMailbox(existingName, newName);
06137     rv = GetServerStateParser().LastCommandSuccessful();
06138   }
06139   
06140   if (rv)
06141   {
06142     if (m_autoSubscribe)  // if auto-subscribe is on
06143     {
06144       PRBool reportingErrors = GetServerStateParser().GetReportingErrors();
06145       GetServerStateParser().SetReportingErrors(PR_FALSE);
06146       Subscribe(newName);
06147       GetServerStateParser().SetReportingErrors(reportingErrors);
06148     }
06149     if (m_autoUnsubscribe) // if auto-unsubscribe is on
06150     {
06151       PRBool reportingErrors = GetServerStateParser().GetReportingErrors();
06152       GetServerStateParser().SetReportingErrors(PR_FALSE);
06153       Unsubscribe(existingName);
06154       GetServerStateParser().SetReportingErrors(reportingErrors);
06155     }
06156   }
06157   return (rv);
06158 }
06159 
06160 PRBool nsImapProtocol::RenameHierarchyByHand(const char *oldParentMailboxName,
06161                                              const char *newParentMailboxName)
06162 {
06163   PRBool renameSucceeded = PR_TRUE;
06164     char onlineDirSeparator = kOnlineHierarchySeparatorUnknown;
06165   m_deletableChildren = new nsVoidArray();
06166 
06167   PRBool nonHierarchicalRename = 
06168         ((GetServerStateParser().GetCapabilityFlag() & kNoHierarchyRename)
06169          || MailboxIsNoSelectMailbox(oldParentMailboxName));
06170 
06171   if (m_deletableChildren)
06172   {
06173     m_hierarchyNameState = kDeleteSubFoldersInProgress;
06174     nsIMAPNamespace *ns = nsnull;
06175         m_hostSessionList->GetNamespaceForMailboxForHost(GetImapServerKey(), 
06176                                                          oldParentMailboxName,
06177                                                          ns); // for delimiter
06178     if (!ns)
06179     {
06180       if (!PL_strcasecmp(oldParentMailboxName, "INBOX"))
06181                 m_hostSessionList->GetDefaultNamespaceOfTypeForHost(GetImapServerKey(), 
06182                                                                     kPersonalNamespace,
06183                                                                     ns);
06184     }
06185     if (ns)
06186     {
06187             nsCString pattern(oldParentMailboxName);
06188             pattern += ns->GetDelimiter();
06189             pattern += "*";
06190             PRBool isUsingSubscription = PR_FALSE;
06191             m_hostSessionList->GetHostIsUsingSubscription(GetImapServerKey(),
06192                                                           isUsingSubscription);
06193 
06194             if (isUsingSubscription)
06195                 Lsub(pattern.get(), PR_FALSE);
06196             else
06197                 List(pattern.get(), PR_FALSE);
06198     }
06199     m_hierarchyNameState = kNoOperationInProgress;
06200     
06201     if (GetServerStateParser().LastCommandSuccessful())
06202       renameSucceeded = // rename this, and move subscriptions 
06203                 RenameMailboxRespectingSubscriptions(oldParentMailboxName,
06204                                                      newParentMailboxName, PR_TRUE);
06205 
06206     PRInt32 numberToDelete = m_deletableChildren->Count();
06207         PRInt32 childIndex;
06208     
06209     for (childIndex = 0; 
06210              (childIndex < numberToDelete) && renameSucceeded; childIndex++)
06211     {
06212       // the imap parser has already converted to a non UTF7 string in the canonical
06213       // format so convert it back
06214         char *currentName = (char *) m_deletableChildren->ElementAt(childIndex);
06215         if (currentName)
06216         {
06217           char *serverName = nsnull;
06218           m_runningUrl->AllocateServerPath(currentName,
06219                                          onlineDirSeparator,
06220                                          &serverName);
06221           PR_FREEIF(currentName);
06222           currentName = serverName;
06223         }
06224         
06225         // calculate the new name and do the rename
06226         nsCString newChildName(newParentMailboxName);
06227         newChildName += (currentName + PL_strlen(oldParentMailboxName));
06228         RenameMailboxRespectingSubscriptions(currentName,
06229                                              newChildName.get(), 
06230                                              nonHierarchicalRename);  
06231         // pass in xNonHierarchicalRename to determine if we should really
06232         // reanme, or just move subscriptions
06233         renameSucceeded = GetServerStateParser().LastCommandSuccessful();
06234         PR_FREEIF(currentName);
06235     }
06236                 
06237     delete m_deletableChildren;
06238     m_deletableChildren = nsnull;
06239   }
06240   
06241   return renameSucceeded;
06242 }
06243 
06244 PRBool nsImapProtocol::DeleteSubFolders(const char* selectedMailbox, PRBool &aDeleteSelf)
06245 {
06246   PRBool deleteSucceeded = PR_TRUE;
06247   m_deletableChildren = new nsVoidArray();
06248   
06249   if (m_deletableChildren)
06250   {
06251     PRBool folderDeleted = PR_FALSE;
06252 
06253     m_hierarchyNameState = kDeleteSubFoldersInProgress;
06254         nsCString pattern(selectedMailbox);
06255         char onlineDirSeparator = kOnlineHierarchySeparatorUnknown;
06256         m_runningUrl->GetOnlineSubDirSeparator(&onlineDirSeparator);
06257         pattern.Append(onlineDirSeparator);
06258         pattern.Append('*');
06259 
06260     if (!pattern.IsEmpty())
06261     {
06262       List(pattern.get(), PR_FALSE);
06263     }
06264     m_hierarchyNameState = kNoOperationInProgress;
06265     
06266     // this should be a short list so perform a sequential search for the
06267     // longest name mailbox.  Deleting the longest first will hopefully
06268         // prevent the server from having problems about deleting parents
06269         // ** jt - why? I don't understand this.
06270     PRInt32 numberToDelete = m_deletableChildren->Count();
06271     PRInt32 outerIndex, innerIndex;
06272 
06273     // intelligently decide if myself(either plain format or following the dir-separator)
06274     // is in the sub-folder list
06275     PRBool folderInSubfolderList = PR_FALSE;     // For Performance
06276     char *selectedMailboxDir = nsnull;
06277     {
06278         PRInt32 length = strlen(selectedMailbox);
06279         selectedMailboxDir = (char *)PR_MALLOC(length+2);
06280         if( selectedMailboxDir )    // only do the intelligent test if there is enough memory
06281         {
06282             strcpy(selectedMailboxDir, selectedMailbox);
06283             selectedMailboxDir[length] = onlineDirSeparator;
06284             selectedMailboxDir[length+1] = '\0';
06285             PRInt32 i;
06286             for( i=0; i<numberToDelete && !folderInSubfolderList; i++ )
06287             {
06288                 char *currentName = (char *) m_deletableChildren->ElementAt(i);
06289                 if( !strcmp(currentName, selectedMailbox) || !strcmp(currentName, selectedMailboxDir) )
06290                     folderInSubfolderList = PR_TRUE;
06291             }
06292         }
06293     }
06294 
06295     deleteSucceeded = GetServerStateParser().LastCommandSuccessful();
06296     for (outerIndex = 0; 
06297          (outerIndex < numberToDelete) && deleteSucceeded;
06298          outerIndex++)
06299     {
06300         char* longestName = nsnull;
06301         PRInt32 longestIndex = 0; // fix bogus warning by initializing
06302         for (innerIndex = 0; 
06303              innerIndex < m_deletableChildren->Count();
06304              innerIndex++)
06305         {
06306             char *currentName = 
06307                 (char *) m_deletableChildren->ElementAt(innerIndex);
06308             if (!longestName || strlen(longestName) < strlen(currentName))
06309             {
06310                 longestName = currentName;
06311                 longestIndex = innerIndex;
06312             }
06313         }
06314         // the imap parser has already converted to a non UTF7 string in
06315         // the canonical format so convert it back
06316         if (longestName)
06317         {
06318             char *serverName = nsnull;
06319 
06320             m_deletableChildren->RemoveElementAt(longestIndex);
06321             m_runningUrl->AllocateServerPath(longestName,
06322                                              onlineDirSeparator,
06323                                              &serverName);
06324             PR_FREEIF(longestName);
06325             longestName = serverName;
06326         }
06327       
06328       // some imap servers include the selectedMailbox in the list of 
06329       // subfolders of the selectedMailbox.  Check for this so we don't
06330             // delete the selectedMailbox (usually the trash and doing an
06331             // empty trash)       
06332       // The Cyrus imap server ignores the "INBOX.Trash" constraining
06333             // string passed to the list command.  Be defensive and make sure
06334             // we only delete children of the trash
06335       if (longestName && 
06336         strcmp(selectedMailbox, longestName) &&
06337         !strncmp(selectedMailbox, longestName, strlen(selectedMailbox)))
06338       {
06339           if( selectedMailboxDir && !strcmp(selectedMailboxDir, longestName) )      // just myself
06340           {
06341               if( aDeleteSelf )
06342               {
06343                   PRBool deleted = DeleteMailboxRespectingSubscriptions(longestName);
06344                   if (deleted)
06345                       FolderDeleted(longestName);
06346                   folderDeleted = deleted;
06347                   deleteSucceeded = deleted;
06348               }
06349           }
06350           else
06351           {
06352               nsCOMPtr<nsIImapIncomingServer> imapServer = do_QueryReferent(m_server);
06353               if (imapServer)
06354                   imapServer->ResetConnection(longestName);
06355               PRBool deleted = PR_FALSE;
06356               if( folderInSubfolderList ) // for performance
06357               {
06358                   nsVoidArray* pDeletableChildren = m_deletableChildren;
06359                   m_deletableChildren = nsnull;
06360                   PRBool folderDeleted = PR_TRUE;
06361                   deleted = DeleteSubFolders(longestName, folderDeleted);
06362                   // longestName may have subfolder list including itself
06363                   if( !folderDeleted )
06364                   {
06365                       if (deleted)
06366                       deleted = DeleteMailboxRespectingSubscriptions(longestName);
06367                       if (deleted)
06368                           FolderDeleted(longestName);
06369                   }
06370                   m_deletableChildren = pDeletableChildren;
06371               }
06372               else
06373               {
06374                   deleted = DeleteMailboxRespectingSubscriptions(longestName);
06375                   if (deleted)
06376                       FolderDeleted(longestName);
06377               }
06378               deleteSucceeded = deleted;
06379           }
06380       }
06381       PR_FREEIF(longestName);
06382     }
06383 
06384     aDeleteSelf = folderDeleted;  // feedback if myself is deleted
06385     PR_Free(selectedMailboxDir);
06386 
06387     delete m_deletableChildren;
06388     m_deletableChildren = nsnull;
06389   }
06390   return deleteSucceeded;
06391 }
06392 
06393 void nsImapProtocol::FolderDeleted(const char *mailboxName)
06394 {
06395     char onlineDelimiter = kOnlineHierarchySeparatorUnknown;
06396     char *orphanedMailboxName = nsnull;
06397 
06398     if (mailboxName)
06399     {
06400         m_runningUrl->AllocateCanonicalPath(mailboxName, onlineDelimiter,
06401                                             &orphanedMailboxName);
06402     if (m_imapServerSink)
06403       m_imapServerSink->OnlineFolderDelete(orphanedMailboxName);
06404     }
06405 
06406     PR_FREEIF(orphanedMailboxName);
06407 }
06408 
06409 void nsImapProtocol::FolderNotCreated(const char *folderName)
06410 {
06411     if (folderName && m_imapServerSink)
06412         m_imapServerSink->OnlineFolderCreateFailed(folderName);
06413 }
06414 
06415 void nsImapProtocol::FolderRenamed(const char *oldName,
06416                                    const char *newName)
06417 {
06418   char onlineDelimiter = kOnlineHierarchySeparatorUnknown;
06419   
06420   if ((m_hierarchyNameState == kNoOperationInProgress) ||
06421     (m_hierarchyNameState == kListingForInfoAndDiscovery))
06422     
06423   {
06424     nsXPIDLCString canonicalOldName, canonicalNewName;
06425     m_runningUrl->AllocateCanonicalPath(oldName,
06426       onlineDelimiter,
06427       getter_Copies(canonicalOldName));
06428     m_runningUrl->AllocateCanonicalPath(newName,
06429       onlineDelimiter,
06430       getter_Copies(canonicalNewName));
06431     nsCOMPtr<nsIMsgWindow> msgWindow;
06432     GetMsgWindow(getter_AddRefs(msgWindow));
06433     m_imapServerSink->OnlineFolderRename(msgWindow, canonicalOldName, canonicalNewName);
06434   }
06435 }
06436 
06437 void nsImapProtocol::OnDeleteFolder(const char * sourceMailbox)
06438 {
06439     // intelligently delete the folder
06440     PRBool folderDeleted = PR_TRUE;
06441     PRBool deleted = DeleteSubFolders(sourceMailbox, folderDeleted);
06442     if( !folderDeleted )
06443     {
06444         if (deleted)
06445             deleted = DeleteMailboxRespectingSubscriptions(sourceMailbox);
06446         if (deleted)
06447             FolderDeleted(sourceMailbox);
06448     }
06449 }
06450 
06451 void nsImapProtocol::RemoveMsgsAndExpunge()
06452 {
06453   uint32 numberOfMessages = GetServerStateParser().NumberOfMessages();
06454   if (numberOfMessages)
06455   {
06456     // Remove all msgs and expunge the folder (ie, compact it).
06457     Store("1:*", "+FLAGS.SILENT (\\Deleted)", PR_FALSE);  // use sequence #'s  
06458     if (GetServerStateParser().LastCommandSuccessful())
06459       Expunge();
06460   }
06461 }
06462 
06463 void nsImapProtocol::DeleteFolderAndMsgs(const char * sourceMailbox)
06464 {
06465   RemoveMsgsAndExpunge();
06466   if (GetServerStateParser().LastCommandSuccessful())
06467   {
06468     // All msgs are deleted successfully - let's remove the folder itself.
06469     PRBool reportingErrors = GetServerStateParser().GetReportingErrors();
06470     GetServerStateParser().SetReportingErrors(PR_FALSE);
06471     OnDeleteFolder(sourceMailbox);
06472     GetServerStateParser().SetReportingErrors(reportingErrors);
06473   }
06474 }
06475 
06476 void nsImapProtocol::OnRenameFolder(const char * sourceMailbox)
06477 {
06478   char *destinationMailbox = OnCreateServerDestinationFolderPathString();
06479 
06480   if (destinationMailbox)
06481   {
06482     PRBool renamed = RenameHierarchyByHand(sourceMailbox, destinationMailbox);
06483     if (renamed)
06484       FolderRenamed(sourceMailbox, destinationMailbox);
06485         
06486     PR_Free( destinationMailbox);
06487   }
06488   else
06489     HandleMemoryFailure();
06490 }
06491 
06492 void nsImapProtocol::OnMoveFolderHierarchy(const char * sourceMailbox)
06493 {
06494   char *destinationMailbox = OnCreateServerDestinationFolderPathString();
06495 
06496     if (destinationMailbox)
06497     {
06498         nsCString newBoxName;
06499         char onlineDirSeparator = kOnlineHierarchySeparatorUnknown;
06500 
06501         m_runningUrl->GetOnlineSubDirSeparator(&onlineDirSeparator);
06502         newBoxName = destinationMailbox;
06503 
06504         nsCString oldBoxName(sourceMailbox);
06505         PRInt32 leafStart = oldBoxName.RFindChar(onlineDirSeparator);
06506         PRInt32 length = oldBoxName.Length();
06507         nsCString leafName;
06508 
06509         if (-1 == leafStart)
06510             leafName = oldBoxName;  // this is a root level box
06511         else
06512             oldBoxName.Right(leafName, length-(leafStart+1));
06513 
06514         if ( !newBoxName.IsEmpty() )
06515              newBoxName.Append(onlineDirSeparator);
06516         newBoxName.Append(leafName);
06517         PRBool  renamed = RenameHierarchyByHand(sourceMailbox,
06518                                                 newBoxName.get());
06519         if (renamed)
06520             FolderRenamed(sourceMailbox, newBoxName.get());
06521     }
06522     else
06523       HandleMemoryFailure();
06524 }
06525 
06526 void nsImapProtocol::FindMailboxesIfNecessary()
06527 {
06528     //PR_EnterMonitor(fFindingMailboxesMonitor);
06529     // biff should not discover mailboxes
06530   PRBool foundMailboxesAlready = PR_FALSE;
06531   nsImapAction imapAction;
06532   nsresult rv = NS_OK;
06533 
06534   // need to do this for every connection in order to see folders.
06535 #ifdef DOING_PSEUDO_MAILBOXES
06536   // check if this is an aol web mail server by checking for the host name the account wizard sets
06537   // up for an aol web mail account - the host name itself is not used, but that's what we set it to, 
06538   // so compare against it. A better solution would be to have the wizard set a special pref property on the
06539   // server and perhaps we should do that for RTM
06540   if (GetServerStateParser().ServerIsAOLServer() && GetImapHostName() && !nsCRT::strcmp(GetImapHostName(), "imap.mail.aol.com"))
06541   {
06542     PRBool suppressPseudoView = PR_FALSE;
06543     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(m_server);
06544     server->GetBoolAttribute("suppresspseudoview", &suppressPseudoView); 
06545     if (!suppressPseudoView)
06546       XAOL_Option("+READMBOX");
06547   }
06548 #endif
06549 
06550   rv = m_runningUrl->GetImapAction(&imapAction);
06551   rv = m_hostSessionList->GetHaveWeEverDiscoveredFoldersForHost(GetImapServerKey(), foundMailboxesAlready);
06552     if (NS_SUCCEEDED(rv) && !foundMailboxesAlready &&
06553     (imapAction != nsIImapUrl::nsImapBiff) &&
06554     (imapAction != nsIImapUrl::nsImapDiscoverAllBoxesUrl) &&
06555     (imapAction != nsIImapUrl::nsImapUpgradeToSubscription) &&
06556     !GetSubscribingNow())
06557     {
06558     DiscoverMailboxList();
06559 
06560     // If we decide to do it, here is where we should check to see if
06561     // a namespace exists (personal namespace only?) and possibly
06562     // create it if it doesn't exist.
06563   }
06564     //PR_ExitMonitor(fFindingMailboxesMonitor);
06565 }
06566 
06567 void nsImapProtocol::DiscoverAllAndSubscribedBoxes()
06568 {
06569   // used for subscribe pane
06570   // iterate through all namespaces
06571   PRUint32 count = 0;
06572   m_hostSessionList->GetNumberOfNamespacesForHost(GetImapServerKey(), count);
06573   
06574   for (PRUint32 i = 0; i < count; i++ )
06575   {
06576     nsIMAPNamespace *ns = nsnull;
06577     
06578     m_hostSessionList->GetNamespaceNumberForHost(GetImapServerKey(), i,
06579       ns);
06580     if (ns &&
06581       gHideOtherUsersFromList ? (ns->GetType() != kOtherUsersNamespace)
06582       : PR_TRUE)
06583     {
06584       const char *prefix = ns->GetPrefix();
06585       if (prefix)
06586       {
06587         if (!gHideUnusedNamespaces && *prefix &&
06588         PL_strcasecmp(prefix, "INBOX.")) /* only do it for
06589         non-empty namespace prefixes */
06590         {
06591           // Explicitly discover each Namespace, just so they're
06592           // there in the subscribe UI 
06593           nsImapMailboxSpec *boxSpec = new nsImapMailboxSpec;
06594           if (boxSpec)
06595           {
06596             NS_ADDREF(boxSpec);
06597             boxSpec->folderSelected = PR_FALSE;
06598             boxSpec->hostName = nsCRT::strdup(GetImapHostName());
06599             boxSpec->connection = this;
06600             boxSpec->flagState = nsnull;
06601             boxSpec->discoveredFromLsub = PR_TRUE;
06602             boxSpec->onlineVerified = PR_TRUE;
06603             boxSpec->box_flags = kNoselect;
06604             boxSpec->hierarchySeparator = ns->GetDelimiter();
06605             m_runningUrl->AllocateCanonicalPath(
06606               ns->GetPrefix(), ns->GetDelimiter(),
06607               &boxSpec->allocatedPathName);
06608             boxSpec->namespaceForFolder = ns;
06609             boxSpec->box_flags |= kNameSpace;
06610             
06611             switch (ns->GetType())
06612             {
06613             case kPersonalNamespace:
06614               boxSpec->box_flags |= kPersonalMailbox;
06615               break;
06616             case kPublicNamespace:
06617               boxSpec->box_flags |= kPublicMailbox;
06618               break;
06619             case kOtherUsersNamespace:
06620               boxSpec->box_flags |= kOtherUsersMailbox;
06621               break;
06622             default: // (kUnknownNamespace)
06623               break;
06624             }
06625             
06626             DiscoverMailboxSpec(boxSpec);
06627           }
06628           else
06629             HandleMemoryFailure();
06630         }
06631         
06632         nsCAutoString allPattern(prefix);
06633         allPattern += '*';
06634         
06635         nsCAutoString topLevelPattern(prefix);
06636         topLevelPattern += '%';
06637         
06638         nsCAutoString secondLevelPattern;
06639         
06640         char delimiter = ns->GetDelimiter();
06641         if (delimiter)
06642         {
06643           // Hierarchy delimiter might be NIL, in which case there's no hierarchy anyway
06644           secondLevelPattern = prefix;
06645           secondLevelPattern += '%';
06646           secondLevelPattern += delimiter;
06647           secondLevelPattern += '%';
06648         }
06649         
06650         nsresult rv;
06651         nsCOMPtr<nsIImapIncomingServer> imapServer = do_QueryReferent(m_server, &rv);
06652         if (NS_FAILED(rv) || !imapServer) return;
06653         
06654         if (!allPattern.IsEmpty())
06655         {
06656           imapServer->SetDoingLsub(PR_TRUE);
06657           Lsub(allPattern.get(), PR_TRUE);       // LSUB all the subscribed
06658         }
06659         if (!topLevelPattern.IsEmpty())
06660         {
06661           imapServer->SetDoingLsub(PR_FALSE);
06662           List(topLevelPattern.get(), PR_TRUE);  // LIST the top level
06663         }
06664         if (!secondLevelPattern.IsEmpty())
06665         {
06666           imapServer->SetDoingLsub(PR_FALSE);
06667           List(secondLevelPattern.get(), PR_TRUE);      // LIST the second level
06668         }
06669       }
06670     }
06671   }
06672 }
06673 
06674 // DiscoverMailboxList() is used to actually do the discovery of folders
06675 // for a host.  This is used both when we initially start up (and re-sync)
06676 // and also when the user manually requests a re-sync, by collapsing and
06677 // expanding a host in the folder pane.  This is not used for the subscribe
06678 // pane.
06679 // DiscoverMailboxList() also gets the ACLs for each newly discovered folder
06680 void nsImapProtocol::DiscoverMailboxList()
06681 {
06682   PRBool usingSubscription = PR_FALSE;
06683 
06684   SetMailboxDiscoveryStatus(eContinue);
06685   if (GetServerStateParser().ServerHasACLCapability())
06686     m_hierarchyNameState = kListingForInfoAndDiscovery;
06687   else
06688     m_hierarchyNameState = kNoOperationInProgress;
06689 
06690   // Pretend that the Trash folder doesn't exist, so we will rediscover it if we need to.
06691   m_hostSessionList->SetOnlineTrashFolderExistsForHost(GetImapServerKey(), PR_FALSE);
06692   m_hostSessionList->GetHostIsUsingSubscription(GetImapServerKey(), usingSubscription);
06693 
06694   // iterate through all namespaces and LSUB them.
06695   PRUint32 count = 0;
06696   m_hostSessionList->GetNumberOfNamespacesForHost(GetImapServerKey(), count);
06697   for (PRUint32 i = 0; i < count; i++ )
06698   {
06699     nsIMAPNamespace * ns = nsnull;
06700     m_hostSessionList->GetNamespaceNumberForHost(GetImapServerKey(),i,ns);
06701     if (ns)
06702     {
06703       const char *prefix = ns->GetPrefix();
06704       if (prefix)
06705       {
06706         // static PRBool gHideUnusedNamespaces = PR_TRUE;
06707         // mscott -> WARNING!!! i where are we going to get this
06708                 // global variable for unusued name spaces from??? *wince*
06709         // dmb - we should get this from a per-host preference,
06710         // I'd say. But for now, just make it PR_TRUE;
06711         if (!gHideUnusedNamespaces && *prefix &&
06712                     PL_strcasecmp(prefix, "INBOX."))  // only do it for
06713                     // non-empty namespace prefixes, and for non-INBOX prefix 
06714         {
06715           // Explicitly discover each Namespace, so that we can
06716                     // create subfolders of them,
06717           nsImapMailboxSpec *boxSpec = new nsImapMailboxSpec; 
06718           if (boxSpec)
06719           {
06720                      NS_ADDREF(boxSpec);
06721             boxSpec->folderSelected = PR_FALSE;
06722             boxSpec->hostName = nsCRT::strdup(GetImapHostName());
06723             boxSpec->connection = this;
06724             boxSpec->flagState = nsnull;
06725             boxSpec->discoveredFromLsub = PR_TRUE;
06726             boxSpec->onlineVerified = PR_TRUE;
06727             boxSpec->box_flags = kNoselect;
06728             boxSpec->hierarchySeparator = ns->GetDelimiter();
06729             m_runningUrl->AllocateCanonicalPath(
06730                             ns->GetPrefix(), ns->GetDelimiter(), 
06731                             &boxSpec->allocatedPathName);  
06732             boxSpec->namespaceForFolder = ns;
06733             boxSpec->box_flags |= kNameSpace;
06734 
06735             switch (ns->GetType())
06736             {
06737             case kPersonalNamespace:
06738               boxSpec->box_flags |= kPersonalMailbox;
06739               break;
06740             case kPublicNamespace:
06741               boxSpec->box_flags |= kPublicMailbox;
06742               break;
06743             case kOtherUsersNamespace:
06744               boxSpec->box_flags |= kOtherUsersMailbox;
06745               break;
06746             default:  // (kUnknownNamespace)
06747               break;
06748             }
06749 
06750             DiscoverMailboxSpec(boxSpec);
06751           }
06752           else
06753             HandleMemoryFailure();
06754         }
06755 
06756         // now do the folders within this namespace
06757         nsCString pattern;
06758         nsCString pattern2;
06759         if (usingSubscription)
06760         {
06761           pattern.Append(prefix);
06762           pattern.Append("*");
06763         }
06764         else
06765         {
06766           pattern.Append(prefix);
06767           pattern.Append("%"); // mscott just need one percent right?
06768           // pattern = PR_smprintf("%s%%", prefix);
06769           char delimiter = ns->GetDelimiter();
06770           if (delimiter)
06771           {
06772             // delimiter might be NIL, in which case there's no hierarchy anyway
06773             pattern2 = prefix;
06774             pattern2 += "%";
06775             pattern2 += delimiter;
06776             pattern2 += "%";
06777             // pattern2 = PR_smprintf("%s%%%c%%", prefix, delimiter);
06778           }
06779         }
06780 
06781 
06782         if (usingSubscription) // && !GetSubscribingNow())  should never get here from subscribe pane
06783           Lsub(pattern.get(), PR_TRUE);
06784         else
06785         {
06786           List(pattern.get(), PR_TRUE);
06787           List(pattern2.get(), PR_TRUE);
06788         }
06789       }
06790     }
06791   }
06792 
06793   // explicitly LIST the INBOX if (a) we're not using subscription, or (b) we are using subscription and
06794   // the user wants us to always show the INBOX.
06795   PRBool listInboxForHost = PR_FALSE;
06796   m_hostSessionList->GetShouldAlwaysListInboxForHost(GetImapServerKey(), listInboxForHost);
06797   if (!usingSubscription || listInboxForHost) 
06798     List("INBOX", PR_TRUE);
06799 
06800   m_hierarchyNameState = kNoOperationInProgress;
06801 
06802   MailboxDiscoveryFinished();
06803 
06804   // Get the ACLs for newly discovered folders
06805   if (GetServerStateParser().ServerHasACLCapability())
06806   {
06807     PRInt32 total = m_listedMailboxList.Count(), cnt = 0;
06808     // Let's not turn this off here, since we don't turn it on after
06809     // GetServerStateParser().SetReportingErrors(PR_FALSE);
06810     if (total)
06811     {
06812       ProgressEventFunctionUsingId(IMAP_GETTING_ACL_FOR_FOLDER);
06813       nsIMAPMailboxInfo * mb = nsnull;
06814       do
06815       {
06816         if (m_listedMailboxList.Count() == 0)
06817             break;
06818 
06819         mb = (nsIMAPMailboxInfo *) m_listedMailboxList[0]; // get top element
06820         m_listedMailboxList.RemoveElementAt(0); // XP_ListRemoveTopObject(fListedMailboxList);
06821         if (mb)
06822         {
06823           if (FolderNeedsACLInitialized(mb->GetMailboxName()))
06824           {
06825             char *onlineName = nsnull;
06826             m_runningUrl->AllocateServerPath(mb->GetMailboxName(), mb->GetDelimiter(), &onlineName);
06827             if (onlineName)
06828             {
06829               RefreshACLForFolder(onlineName);
06830               PR_Free(onlineName);
06831             }
06832           }
06833           PercentProgressUpdateEvent(NULL, cnt, total);
06834           delete mb;  // this is the last time we're using the list, so delete the entries here
06835           cnt++;
06836         }
06837       } while (mb && !DeathSignalReceived());
06838     }
06839   }
06840 }
06841 
06842 PRBool nsImapProtocol::FolderNeedsACLInitialized(const char *folderName)
06843 {
06844   PRBool rv = PR_FALSE;
06845 
06846   char *name = PL_strdup(folderName);
06847   if (!name)
06848     return PR_FALSE;
06849   m_imapServerSink->FolderNeedsACLInitialized(name, &rv);
06850 
06851   PR_Free(name);
06852   return rv;
06853 }
06854 
06855 void nsImapProtocol::MailboxDiscoveryFinished()
06856 {
06857   if (!DeathSignalReceived() && !GetSubscribingNow() &&
06858     ((m_hierarchyNameState == kNoOperationInProgress) || 
06859     (m_hierarchyNameState == kListingForInfoAndDiscovery)))
06860   {
06861     nsIMAPNamespace *ns = nsnull;
06862     m_hostSessionList->GetDefaultNamespaceOfTypeForHost(GetImapServerKey(), kPersonalNamespace, ns);
06863     const char *personalDir = ns ? ns->GetPrefix() : 0;
06864     
06865     PRBool trashFolderExists = PR_FALSE;
06866     PRBool usingSubscription = PR_FALSE;
06867     m_hostSessionList->GetOnlineTrashFolderExistsForHost(GetImapServerKey(), trashFolderExists);
06868     m_hostSessionList->GetHostIsUsingSubscription(GetImapServerKey(),usingSubscription);
06869     if (!trashFolderExists && GetDeleteIsMoveToTrash() && usingSubscription)
06870     {
06871       // maybe we're not subscribed to the Trash folder
06872       if (personalDir)
06873       {
06874         char *originalTrashName = CreatePossibleTrashName(personalDir);
06875         m_hierarchyNameState = kDiscoverTrashFolderInProgress;
06876         List(originalTrashName, PR_TRUE);
06877         m_hierarchyNameState = kNoOperationInProgress;
06878       }
06879     }
06880     
06881     // There is no Trash folder (either LIST'd or LSUB'd), and we're using the
06882     // Delete-is-move-to-Trash model, and there is a personal namespace
06883     if (!trashFolderExists && GetDeleteIsMoveToTrash() && ns)
06884     {
06885       char *trashName = CreatePossibleTrashName(ns->GetPrefix());
06886       if (trashName)
06887       {
06888         char *onlineTrashName = nsnull;
06889         m_runningUrl->AllocateServerPath(trashName, ns->GetDelimiter(), &onlineTrashName);
06890         if (onlineTrashName)
06891         {
06892           GetServerStateParser().SetReportingErrors(PR_FALSE);
06893           PRBool created = CreateMailboxRespectingSubscriptions(onlineTrashName);
06894           GetServerStateParser().SetReportingErrors(PR_TRUE);
06895           
06896           // force discovery of new trash folder.
06897           if (created)
06898           {
06899             m_hierarchyNameState = kDiscoverTrashFolderInProgress;
06900             List(onlineTrashName, PR_FALSE);
06901             m_hierarchyNameState = kNoOperationInProgress;
06902           }
06903           else
06904             m_hostSessionList->SetOnlineTrashFolderExistsForHost(GetImapServerKey(), PR_TRUE);
06905           PR_Free(onlineTrashName);
06906         }
06907         PR_Free(trashName);
06908       } // if trash name
06909     } //if trashg folder doesn't exist
06910     m_hostSessionList->SetHaveWeEverDiscoveredFoldersForHost(GetImapServerKey(), PR_TRUE);
06911     
06912     // notify front end that folder discovery is complete....
06913     if (m_imapServerSink)
06914       m_imapServerSink->DiscoveryDone();
06915   }
06916 }
06917 
06918 // returns PR_TRUE is the create succeeded (regardless of subscription changes)
06919 PRBool nsImapProtocol::CreateMailboxRespectingSubscriptions(const char *mailboxName)
06920 {
06921   CreateMailbox(mailboxName);
06922   PRBool rv = GetServerStateParser().LastCommandSuccessful();
06923   if (rv)
06924   {
06925     if (m_autoSubscribe) // auto-subscribe is on
06926     {
06927       // create succeeded - let's subscribe to it
06928       PRBool reportingErrors = GetServerStateParser().GetReportingErrors();
06929       GetServerStateParser().SetReportingErrors(PR_FALSE);
06930       OnSubscribe(mailboxName);
06931       GetServerStateParser().SetReportingErrors(reportingErrors);
06932     }
06933   }
06934   return (rv);
06935 }
06936 
06937 void nsImapProtocol::CreateMailbox(const char *mailboxName)
06938 {
06939   ProgressEventFunctionUsingId (IMAP_STATUS_CREATING_MAILBOX);
06940 
06941   IncrementCommandTagNumber();
06942   
06943   char *escapedName = CreateEscapedMailboxName(mailboxName);
06944   nsCString command(GetServerCommandTag());
06945   command += " create \"";
06946   command += escapedName;
06947   command += "\""CRLF;
06948                
06949   nsMemory::Free(escapedName);
06950 
06951   nsresult rv = SendData(command.get());
06952   if(NS_SUCCEEDED(rv))
06953     ParseIMAPandCheckForNewMail();
06954 }
06955 
06956 void nsImapProtocol::DeleteMailbox(const char *mailboxName)
06957 {
06958 
06959   // check if this connection currently has the folder to be deleted selected.
06960   // If so, we should close it because at least some UW servers don't like you deleting
06961   // a folder you have open.
06962   if (FolderIsSelected(mailboxName))
06963     Close();
06964   
06965   
06966   ProgressEventFunctionUsingIdWithString (IMAP_STATUS_DELETING_MAILBOX, mailboxName);
06967 
06968     IncrementCommandTagNumber();
06969     
06970     char *escapedName = CreateEscapedMailboxName(mailboxName);
06971     nsCString command(GetServerCommandTag());
06972     command += " delete \"";
06973     command += escapedName;
06974     command += "\"" CRLF;
06975     nsMemory::Free(escapedName);
06976     
06977     nsresult rv = SendData(command.get());
06978     if (NS_SUCCEEDED(rv))
06979         ParseIMAPandCheckForNewMail();
06980 }
06981 
06982 void nsImapProtocol::RenameMailbox(const char *existingName, 
06983                                    const char *newName)
06984 {
06985   // just like DeleteMailbox; Some UW servers don't like it.
06986   if (FolderIsSelected(existingName))
06987     Close();
06988 
06989   ProgressEventFunctionUsingIdWithString (IMAP_STATUS_RENAMING_MAILBOX, existingName);
06990     
06991   IncrementCommandTagNumber();
06992     
06993   char *escapedExistingName = CreateEscapedMailboxName(existingName);
06994   char *escapedNewName = CreateEscapedMailboxName(newName);
06995   nsCString command(GetServerCommandTag());
06996   command += " rename \"";
06997   command += escapedExistingName;
06998   command += "\" \"";
06999   command += escapedNewName;
07000   command += "\"" CRLF;
07001   nsMemory::Free(escapedExistingName);
07002   nsMemory::Free(escapedNewName);
07003           
07004   nsresult rv = SendData(command.get());
07005   if (NS_SUCCEEDED(rv))
07006     ParseIMAPandCheckForNewMail();
07007 }
07008 
07009 char * nsImapProtocol::CreatePossibleTrashName(const char *prefix)
07010 {
07011   nsCString returnTrash(prefix);
07012 
07013   returnTrash += GetTrashFolderName();
07014   return ToNewCString(returnTrash);
07015 }
07016 
07017 const char * nsImapProtocol::GetTrashFolderName()
07018 {
07019   if (m_trashFolderName.IsEmpty())
07020   {
07021     nsCOMPtr<nsIImapIncomingServer> server = do_QueryReferent(m_server);
07022     if (server)
07023     {
07024       nsXPIDLString trashFolderName;
07025       if (NS_SUCCEEDED(server->GetTrashFolderName(getter_Copies(trashFolderName))))
07026       {
07027         CopyUTF16toMUTF7(trashFolderName, m_trashFolderName);
07028       }
07029     }
07030   }
07031   
07032   return m_trashFolderName.get();
07033 }
07034 
07035 void nsImapProtocol::Lsub(const char *mailboxPattern, PRBool addDirectoryIfNecessary)
07036 {
07037   ProgressEventFunctionUsingId (IMAP_STATUS_LOOKING_FOR_MAILBOX);
07038 
07039   IncrementCommandTagNumber();
07040 
07041   char *boxnameWithOnlineDirectory = nsnull;
07042   if (addDirectoryIfNecessary)
07043     m_runningUrl->AddOnlineDirectoryIfNecessary(mailboxPattern, &boxnameWithOnlineDirectory);
07044 
07045   char *escapedPattern = CreateEscapedMailboxName(boxnameWithOnlineDirectory ? 
07046                         boxnameWithOnlineDirectory :
07047                         mailboxPattern);
07048 
07049   nsCString command (GetServerCommandTag());
07050   command += " lsub \"\" \"";
07051   command += escapedPattern;
07052   command += "\""CRLF;
07053 
07054   nsMemory::Free(escapedPattern);
07055   PR_Free(boxnameWithOnlineDirectory);
07056 
07057   nsresult rv = SendData(command.get());
07058   if (NS_SUCCEEDED(rv))
07059     ParseIMAPandCheckForNewMail();
07060 }
07061 
07062 void nsImapProtocol::List(const char *mailboxPattern, PRBool addDirectoryIfNecessary)
07063 {
07064   ProgressEventFunctionUsingId (IMAP_STATUS_LOOKING_FOR_MAILBOX);
07065 
07066   IncrementCommandTagNumber();
07067 
07068   char *boxnameWithOnlineDirectory = nsnull;
07069   if (addDirectoryIfNecessary)
07070     m_runningUrl->AddOnlineDirectoryIfNecessary(mailboxPattern, &boxnameWithOnlineDirectory);
07071 
07072   char *escapedPattern = CreateEscapedMailboxName(boxnameWithOnlineDirectory ? 
07073                         boxnameWithOnlineDirectory :
07074                         mailboxPattern);
07075 
07076   nsCString command (GetServerCommandTag());
07077   command += " list \"\" \"";
07078   command += escapedPattern;
07079   command += "\""CRLF;
07080 
07081   nsMemory::Free(escapedPattern);
07082   PR_Free(boxnameWithOnlineDirectory);
07083 
07084   nsresult rv = SendData(command.get());  
07085   if (NS_SUCCEEDED(rv))
07086     ParseIMAPandCheckForNewMail();
07087 }
07088 
07089 void nsImapProtocol::Subscribe(const char *mailboxName)
07090 {
07091   ProgressEventFunctionUsingIdWithString (IMAP_STATUS_SUBSCRIBE_TO_MAILBOX, mailboxName);
07092 
07093   IncrementCommandTagNumber();
07094     
07095   char *escapedName = CreateEscapedMailboxName(mailboxName);
07096 
07097   nsCString command (GetServerCommandTag());
07098   command += " subscribe \"";
07099   command += escapedName;
07100   command += "\""CRLF;
07101   nsMemory::Free(escapedName);
07102 
07103   nsresult rv = SendData(command.get());  
07104   if (NS_SUCCEEDED(rv))
07105     ParseIMAPandCheckForNewMail();
07106 }
07107 
07108 void nsImapProtocol::Unsubscribe(const char *mailboxName)
07109 {
07110   ProgressEventFunctionUsingIdWithString (IMAP_STATUS_UNSUBSCRIBE_MAILBOX, mailboxName);
07111   IncrementCommandTagNumber();
07112   
07113   char *escapedName = CreateEscapedMailboxName(mailboxName);
07114     
07115   nsCString command (GetServerCommandTag());
07116   command += " unsubscribe \"";
07117   command += escapedName;
07118   command += "\""CRLF;
07119   nsMemory::Free(escapedName);
07120 
07121   nsresult rv = SendData(command.get());  
07122   if (NS_SUCCEEDED(rv))
07123       ParseIMAPandCheckForNewMail();
07124 }
07125 
07126 void nsImapProtocol::Idle()
07127 {
07128   IncrementCommandTagNumber();
07129   
07130   if (m_urlInProgress)
07131     return;
07132   nsCAutoString command (GetServerCommandTag());
07133   command += " IDLE"CRLF;
07134   nsresult rv = SendData(command.get());  
07135   if (NS_SUCCEEDED(rv))
07136   {
07137       m_idle = PR_TRUE;
07138       // we'll just get back a continuation char at first.
07139       // + idling...
07140       ParseIMAPandCheckForNewMail();
07141       // this will cause us to get notified of data or the socket getting closed.
07142       // That notification will occur on the socket transport thread - we just
07143       // need to poke a monitor so the imap thread will do a blocking read
07144       // and parse the data.
07145       nsCOMPtr <nsIAsyncInputStream> asyncInputStream = do_QueryInterface(m_inputStream);
07146       if (asyncInputStream)
07147         asyncInputStream->AsyncWait(this, 0, 0, nsnull);
07148   }
07149 }
07150 
07151 // until we can fix the hang on shutdown waiting for server
07152 // responses, we need to not wait for the server response
07153 // on shutdown.
07154 void nsImapProtocol::EndIdle(PRBool waitForResponse /* = PR_TRUE */)
07155 {
07156   // clear the async wait - otherwise, we seem to have trouble doing a blocking read
07157   nsCOMPtr <nsIAsyncInputStream> asyncInputStream = do_QueryInterface(m_inputStream);
07158   if (asyncInputStream)
07159     asyncInputStream->AsyncWait(nsnull, 0, 0, nsnull);
07160   nsresult rv = SendData("DONE"CRLF);
07161   // set a short timeout if we don't want to wait for a response
07162   if (m_transport && !waitForResponse)
07163     m_transport->SetTimeout(nsISocketTransport::TIMEOUT_READ_WRITE, 5);
07164   if (NS_SUCCEEDED(rv))
07165   {
07166     m_idle = PR_FALSE;
07167     ParseIMAPandCheckForNewMail();
07168   }
07169   m_imapMailFolderSink = nsnull;
07170 }
07171 
07172 
07173 void nsImapProtocol::Search(const char * searchCriteria, 
07174                             PRBool useUID, 
07175                             PRBool notifyHit /* PR_TRUE */)
07176 {
07177   m_notifySearchHit = notifyHit;
07178   ProgressEventFunctionUsingId (IMAP_STATUS_SEARCH_MAILBOX);
07179   IncrementCommandTagNumber();
07180     
07181   nsCString protocolString(GetServerCommandTag());
07182   // the searchCriteria string contains the 'search ....' string
07183   if (useUID)
07184      protocolString.Append(" uid");
07185   protocolString.Append(" ");
07186   protocolString.Append(searchCriteria);
07187   // the search criteria can contain string literals, which means we
07188   // need to break up the protocol string by CRLF's, and after sending CRLF,
07189   // wait for the server to respond OK before sending more data
07190   nsresult rv;
07191   PRInt32 crlfIndex;
07192   while (crlfIndex = protocolString.Find(CRLF), crlfIndex != kNotFound && !DeathSignalReceived())
07193   {
07194     nsCAutoString tempProtocolString;
07195     protocolString.Left(tempProtocolString, crlfIndex + 2);
07196     rv = SendData(tempProtocolString.get());
07197     if (NS_FAILED(rv))
07198       return;
07199     ParseIMAPandCheckForNewMail();
07200     protocolString.Cut(0, crlfIndex + 2);
07201   }
07202   protocolString.Append(CRLF);
07203 
07204   rv = SendData(protocolString.get());
07205   if (NS_SUCCEEDED(rv))
07206      ParseIMAPandCheckForNewMail();
07207 }
07208 
07209 void nsImapProtocol::Copy(const char * messageList,
07210                           const char *destinationMailbox, 
07211                           PRBool idsAreUid)
07212 {
07213   IncrementCommandTagNumber();
07214   
07215   char *escapedDestination = CreateEscapedMailboxName(destinationMailbox);
07216 
07217   // turn messageList back into key array and then back into a message id list,
07218   // but use the flag state to handle ranges correctly.
07219   nsCString messageIdList;
07220   nsMsgKeyArray msgKeys;
07221   if (idsAreUid)
07222     ParseUidString(messageList, msgKeys);
07223 
07224   PRInt32 msgCountLeft = msgKeys.GetSize();
07225   PRUint32 msgsHandled = 0;
07226   const char *formatString;
07227   formatString = (idsAreUid)
07228       ? "%s uid store %s %s"CRLF
07229       : "%s store %s %s"CRLF;
07230 
07231   do 
07232   {
07233     nsCString idString;
07234 
07235     PRUint32 msgsToHandle = msgCountLeft;
07236     if (idsAreUid)
07237       AllocateImapUidString(msgKeys.GetArray() + msgsHandled, msgsToHandle, m_flagState, idString);
07238     else
07239       idString.Assign(messageList);
07240 
07241     msgsHandled += msgsToHandle;
07242     msgCountLeft -= msgsToHandle;
07243 
07244     IncrementCommandTagNumber();
07245     nsCAutoString protocolString(GetServerCommandTag());
07246     if (idsAreUid)
07247       protocolString.Append(" uid");
07248     // If it's a MOVE operation on aol servers then use 'xaol-move' cmd.
07249     if ((m_imapAction == nsIImapUrl::nsImapOnlineMove) &&
07250         GetServerStateParser().ServerIsAOLServer())
07251       protocolString.Append(" xaol-move ");
07252     else
07253       protocolString.Append(" copy ");
07254 
07255 
07256     protocolString.Append(idString);
07257     protocolString.Append(" \"");
07258     protocolString.Append(escapedDestination);
07259     protocolString.Append("\"" CRLF);
07260       
07261     nsresult rv = SendData(protocolString.get());
07262     if (NS_SUCCEEDED(rv))
07263        ParseIMAPandCheckForNewMail(protocolString.get());
07264   }
07265   while (msgCountLeft > 0 && !DeathSignalReceived());
07266 
07267         
07268   nsMemory::Free(escapedDestination);
07269 }
07270 
07271 void nsImapProtocol::NthLevelChildList(const char* onlineMailboxPrefix,
07272                                        PRInt32 depth)
07273 {
07274   NS_ASSERTION (depth >= 0, 
07275                   "Oops ... depth must be equal or greater than 0");
07276   if (depth < 0) return;
07277 
07278   nsCString truncatedPrefix (onlineMailboxPrefix);
07279   PRUnichar slash = '/';
07280   if (truncatedPrefix.Last() == slash)
07281         truncatedPrefix.SetLength(truncatedPrefix.Length()-1);
07282     
07283   nsCAutoString pattern(truncatedPrefix);
07284   nsCAutoString suffix;
07285   int count = 0;
07286   char separator = 0;
07287   m_runningUrl->GetOnlineSubDirSeparator(&separator);
07288   suffix.Assign(separator);
07289   suffix += '%';
07290   
07291   while (count < depth)
07292   {
07293       pattern += suffix;
07294       count++;
07295       List(pattern.get(), PR_FALSE);
07296   }
07297 }
07298 
07299 void nsImapProtocol::ProcessAuthenticatedStateURL()
07300 {
07301   nsImapAction imapAction;
07302   char * sourceMailbox = nsnull;
07303   m_runningUrl->GetImapAction(&imapAction);
07304 
07305   // switch off of the imap url action and take an appropriate action
07306   switch (imapAction)
07307   {
07308     case nsIImapUrl::nsImapLsubFolders:
07309       OnLSubFolders();
07310       break;
07311     case nsIImapUrl::nsImapAppendMsgFromFile:
07312       OnAppendMsgFromFile();
07313       break;
07314     case nsIImapUrl::nsImapDiscoverAllBoxesUrl:
07315       NS_ASSERTION (!GetSubscribingNow(),
07316                       "Oops ... should not get here from subscribe UI");
07317       DiscoverMailboxList();
07318       break;
07319     case nsIImapUrl::nsImapDiscoverAllAndSubscribedBoxesUrl:
07320       DiscoverAllAndSubscribedBoxes();
07321       break;
07322     case nsIImapUrl::nsImapCreateFolder:
07323       sourceMailbox = OnCreateServerSourceFolderPathString();
07324       OnCreateFolder(sourceMailbox);
07325       break;
07326     case nsIImapUrl::nsImapEnsureExistsFolder:
07327       sourceMailbox = OnCreateServerSourceFolderPathString();
07328       OnEnsureExistsFolder(sourceMailbox);
07329       break;
07330     case nsIImapUrl::nsImapDiscoverChildrenUrl:
07331       {
07332         char *canonicalParent = nsnull;
07333         m_runningUrl->CreateServerSourceFolderPathString(&canonicalParent);
07334         if (canonicalParent)
07335         {
07336           NthLevelChildList(canonicalParent, 2);
07337           PR_Free(canonicalParent);
07338         }
07339         break;
07340       }
07341     case nsIImapUrl::nsImapSubscribe:
07342       sourceMailbox = OnCreateServerSourceFolderPathString();
07343       OnSubscribe(sourceMailbox); // used to be called subscribe
07344 
07345       if (GetServerStateParser().LastCommandSuccessful())
07346       {
07347         PRBool shouldList;
07348         // if url is an external click url, then we should list the folder
07349         // after subscribing to it, so we can select it.
07350         m_runningUrl->GetExternalLinkUrl(&shouldList);
07351         if (shouldList)
07352           OnListFolder(sourceMailbox, PR_TRUE);
07353       }
07354       break;
07355     case nsIImapUrl::nsImapUnsubscribe: 
07356       sourceMailbox = OnCreateServerSourceFolderPathString();     
07357       OnUnsubscribe(sourceMailbox);
07358       break;
07359     case nsIImapUrl::nsImapRefreshACL:
07360       sourceMailbox = OnCreateServerSourceFolderPathString(); 
07361       RefreshACLForFolder(sourceMailbox);
07362       break;
07363     case nsIImapUrl::nsImapRefreshAllACLs:
07364       OnRefreshAllACLs();
07365       break;
07366     case nsIImapUrl::nsImapListFolder:
07367       sourceMailbox = OnCreateServerSourceFolderPathString();  
07368       OnListFolder(sourceMailbox, PR_FALSE);
07369       break;
07370     case nsIImapUrl::nsImapFolderStatus:
07371       sourceMailbox = OnCreateServerSourceFolderPathString(); 
07372       OnStatusForFolder(sourceMailbox);
07373       break;
07374     case nsIImapUrl::nsImapRefreshFolderUrls:
07375       sourceMailbox = OnCreateServerSourceFolderPathString();
07376       XMailboxInfo(sourceMailbox);
07377       if (GetServerStateParser().LastCommandSuccessful()) 
07378         SetFolderAdminUrl(sourceMailbox);
07379       break;
07380     case nsIImapUrl::nsImapDeleteFolder:
07381       sourceMailbox = OnCreateServerSourceFolderPathString();
07382       OnDeleteFolder(sourceMailbox);
07383       break;
07384     case nsIImapUrl::nsImapRenameFolder:
07385       sourceMailbox = OnCreateServerSourceFolderPathString();
07386       OnRenameFolder(sourceMailbox);
07387       break;
07388     case nsIImapUrl::nsImapMoveFolderHierarchy:
07389       sourceMailbox = OnCreateServerSourceFolderPathString();
07390       OnMoveFolderHierarchy(sourceMailbox);
07391       break;
07392     default:
07393       break;
07394   }
07395 
07396   PR_Free(sourceMailbox);
07397 }
07398