Back to index

lightning-sunbird  0.9+nobinonly
nsPop3Sink.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; 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) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   bienvenu@nventure.com
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "msgCore.h"    // precompiled header...
00041 #include "nsPop3Sink.h"
00042 #include "prprf.h"
00043 #include "prlog.h"
00044 #include "nscore.h"
00045 #include <stdio.h>
00046 #include <time.h>
00047 #include "nsIFileSpec.h"
00048 #include "nsParseMailbox.h"
00049 #include "nsIMsgLocalMailFolder.h"
00050 #include "nsIMsgIncomingServer.h"
00051 #include "nsLocalUtils.h"
00052 #include "nsMsgLocalFolderHdrs.h"
00053 #include "nsIMsgFolder.h" // TO include biffState enum. Change to bool later...
00054 #include "nsReadableUtils.h"
00055 #include "nsMailHeaders.h"
00056 #include "nsIMsgAccountManager.h"
00057 #include "nsILineInputStream.h"
00058 #include "nsIPop3Protocol.h"
00059 #include "nsLocalMailFolder.h"
00060 #include "nsIPrefBranch.h"
00061 #include "nsIPrefService.h"
00062 #include "nsSpecialSystemDirectory.h"
00063 #include "nsIMsgStringService.h"
00064 #include "nsIPrompt.h"
00065 #include "nsIPromptService.h"
00066 #include "nsIInterfaceRequestor.h"
00067 #include "nsIInterfaceRequestorUtils.h"
00068 #include "nsIDocShell.h"
00069 #include "nsIDOMWindowInternal.h"
00070 #include "nsEmbedCID.h"
00071 
00072 NS_IMPL_THREADSAFE_ISUPPORTS1(nsPop3Sink, nsIPop3Sink)
00073 
00074 nsPop3Sink::nsPop3Sink()
00075 {
00076     m_authed = PR_FALSE;
00077     m_downloadingToTempFile = PR_FALSE;
00078     m_accountUrl = nsnull;
00079     m_biffState = 0;
00080     m_numNewMessages = 0;
00081     m_numNewMessagesInFolder = 0;
00082     m_senderAuthed = PR_FALSE;
00083     m_outputBuffer = nsnull;
00084     m_outputBufferSize = 0;
00085     m_newMailParser = nsnull;
00086 #ifdef DEBUG
00087     m_fileCounter = 0;
00088 #endif
00089     m_popServer = nsnull;
00090     m_outFileStream = nsnull;
00091     m_folder = nsnull;
00092     m_buildMessageUri = PR_FALSE;
00093 }
00094 
00095 nsPop3Sink::~nsPop3Sink()
00096 {
00097     PR_Free(m_accountUrl);
00098     PR_Free(m_outputBuffer);
00099     NS_IF_RELEASE(m_popServer);
00100     ReleaseFolderLock();
00101     NS_IF_RELEASE(m_folder);
00102     NS_IF_RELEASE(m_newMailParser);
00103 }
00104 
00105 nsresult
00106 nsPop3Sink::SetUserAuthenticated(PRBool authed)
00107 {
00108   m_authed = authed;
00109   m_popServer->SetAuthenticated(authed);
00110   if (authed)
00111   {
00112     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
00113     if (!server) 
00114       return NS_ERROR_UNEXPECTED;
00115     return server->StorePassword();
00116   }
00117   return NS_OK;
00118 }
00119 
00120 nsresult
00121 nsPop3Sink::GetUserAuthenticated(PRBool* authed)
00122 {
00123   return m_popServer->GetAuthenticated(authed);
00124 }
00125 
00126 nsresult
00127 nsPop3Sink::SetSenderAuthedFlag(void* closure, PRBool authed)
00128 {
00129   m_authed = authed;
00130   return NS_OK;   
00131 }
00132 
00133 nsresult 
00134 nsPop3Sink::SetMailAccountURL(const char* urlString)
00135 {
00136   if (urlString)
00137   {
00138     PR_Free(m_accountUrl);
00139     m_accountUrl = PL_strdup(urlString);
00140   }
00141 
00142   return NS_OK;
00143 }
00144 
00145 nsresult
00146 nsPop3Sink::GetMailAccountURL(char* *urlString)
00147 {
00148   NS_ASSERTION(urlString, "null getter in getMailAccountURL");
00149   if (!urlString) return NS_ERROR_NULL_POINTER;
00150 
00151   *urlString = nsCRT::strdup(m_accountUrl);
00152   return NS_OK;
00153 }
00154 
00155 struct partialRecord
00156 {
00157   partialRecord();
00158   ~partialRecord();
00159 
00160   nsCOMPtr<nsIMsgDBHdr> m_msgDBHdr;
00161   nsCString m_uidl;
00162 };
00163 
00164 partialRecord::partialRecord() :
00165   m_msgDBHdr(nsnull)
00166 {
00167 }
00168 
00169 partialRecord::~partialRecord()
00170 {
00171 }
00172   
00173 // Walk through all the messages in this folder and look for any
00174 // PARTIAL messages. For each of those, dig thru the mailbox and
00175 // find the Account that the message belongs to. If that Account
00176 // matches the current Account, then look for the Uidl and save
00177 // this message for later processing.
00178 nsresult
00179 nsPop3Sink::FindPartialMessages(nsFileSpec *fileSpec)
00180 {
00181   nsresult rv;
00182 
00183   nsCOMPtr<nsISimpleEnumerator> messages;
00184   PRBool hasMore = PR_FALSE;
00185   PRBool isOpen = PR_FALSE;
00186   nsLocalFolderScanState folderScanState;
00187   nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
00188 
00189   if (!localFolder)
00190     return NS_ERROR_FAILURE;  // we need it to grub thru the folder
00191 
00192   rv = m_newMailParser->m_mailDB->EnumerateMessages(getter_AddRefs(messages));
00193   if (messages)
00194     messages->HasMoreElements(&hasMore);
00195   while(hasMore && NS_SUCCEEDED(rv))
00196   {
00197     nsCOMPtr<nsISupports> aSupport;
00198     PRUint32 flags = 0;
00199     rv = messages->GetNext(getter_AddRefs(aSupport));
00200     nsCOMPtr<nsIMsgDBHdr> msgDBHdr(do_QueryInterface(aSupport, &rv));
00201     msgDBHdr->GetFlags(&flags);
00202     if (flags & MSG_FLAG_PARTIAL)
00203     {
00204       // Open the various streams we need to seek and read from the mailbox
00205       if (!isOpen)
00206       {
00207         folderScanState.m_fileSpec = fileSpec;
00208         rv = localFolder->GetFolderScanState(&folderScanState);
00209         if (NS_SUCCEEDED(rv))
00210           isOpen = PR_TRUE;
00211         else
00212           break;
00213       }
00214       rv = localFolder->GetUidlFromFolder(&folderScanState, msgDBHdr);
00215       if (!NS_SUCCEEDED(rv))
00216         break;
00217 
00218       // If we got the uidl, see if this partial message belongs to this
00219       // account. Add it to the array if so...
00220       if (folderScanState.m_uidl && 
00221         !nsCRT::strcasecmp(folderScanState.m_accountKey.get(), m_accountKey))
00222       {
00223         partialRecord *partialMsg = new partialRecord();
00224         if (partialMsg)
00225         {
00226           partialMsg->m_uidl = folderScanState.m_uidl;
00227           partialMsg->m_msgDBHdr = msgDBHdr;
00228           m_partialMsgsArray.AppendElement(partialMsg);
00229         }
00230       }
00231     }
00232     messages->HasMoreElements(&hasMore);
00233   }
00234   if (isOpen)
00235     folderScanState.m_fileStream->Close();
00236   return rv;
00237 }
00238 
00239 // For all the partial messages saved by FindPartialMessages,
00240 // ask the protocol handler if they still exist on the server.
00241 // Any messages that don't exist any more are deleted from the
00242 // msgDB.
00243 void
00244 nsPop3Sink::CheckPartialMessages(nsIPop3Protocol *protocol)
00245 {
00246   PRUint32 count = m_partialMsgsArray.Count();
00247   PRBool deleted = PR_FALSE;
00248 
00249   for (PRUint32 i = 0; i < count; i++)
00250   {
00251     partialRecord *partialMsg;
00252     PRBool found = PR_TRUE;
00253     partialMsg = NS_STATIC_CAST(partialRecord *,m_partialMsgsArray.ElementAt(i));
00254     protocol->CheckMessage(partialMsg->m_uidl.get(), &found);
00255     if (!found) {
00256       m_newMailParser->m_mailDB->DeleteHeader(partialMsg->m_msgDBHdr, nsnull, PR_FALSE, PR_TRUE);
00257       deleted = PR_TRUE;
00258     }
00259     delete partialMsg;
00260   }
00261   m_partialMsgsArray.Clear();
00262   if (deleted) {
00263     nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
00264     if (localFolder) {
00265       localFolder->NotifyDelete();
00266     }
00267   }
00268 }
00269 
00270 nsresult 
00271 nsPop3Sink::BeginMailDelivery(PRBool uidlDownload, nsIMsgWindow *aMsgWindow, PRBool* aBool)
00272 {
00273 #ifdef DEBUG
00274     m_fileCounter++;
00275 #endif
00276 
00277     nsresult rv;
00278     
00279     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
00280     if (!server) 
00281       return NS_ERROR_UNEXPECTED;
00282 
00283     nsCOMPtr <nsIMsgAccountManager> acctMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
00284     nsCOMPtr <nsIMsgAccount> account;
00285     NS_ENSURE_SUCCESS(rv, rv);
00286     acctMgr->FindAccountForServer(server, getter_AddRefs(account));
00287     if (account)
00288       account->GetKey(getter_Copies(m_accountKey));
00289     nsFileSpec fileSpec;
00290     // ### if we're doing a UIDL, then the fileSpec needs to be for the current folder
00291 
00292     PRBool isLocked;
00293     nsCOMPtr <nsISupports> supports = do_QueryInterface(NS_STATIC_CAST(nsIPop3Sink*, this));
00294     m_folder->GetLocked(&isLocked);
00295     if(!isLocked)
00296       m_folder->AcquireSemaphore(supports);
00297     else
00298       return NS_MSG_FOLDER_BUSY;
00299 
00300     nsCOMPtr<nsIFileSpec> path;
00301 
00302     m_folder->GetPath(getter_AddRefs(path));
00303     path->GetFileSpec(&fileSpec);
00304 
00305     nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00306     if (pPrefBranch)
00307        pPrefBranch->GetBoolPref("mailnews.downloadToTempFile", &m_downloadingToTempFile);
00308 
00309     if (m_downloadingToTempFile)
00310     {
00311       // need to create an nsIOFileStream from a temp file...
00312       nsCOMPtr <nsIFileSpec> tmpDownloadFile;
00313       nsSpecialSystemDirectory tmpFile(nsSpecialSystemDirectory::OS_TemporaryDirectory);
00314       tmpFile += "newmsg";
00315 
00316       rv = NS_NewFileSpecWithSpec(tmpFile, getter_AddRefs(tmpDownloadFile));
00317 
00318       NS_ASSERTION(NS_SUCCEEDED(rv),"writing tmp pop3 download file: failed to append filename");
00319       if (NS_FAILED(rv)) 
00320         return rv;
00321 
00322       rv = tmpDownloadFile->MakeUnique();  //need a unique tmp file to prevent dataloss in multiuser environment
00323       NS_ENSURE_SUCCESS(rv, rv);
00324 
00325       rv = tmpDownloadFile->GetFileSpec(&m_tmpDownloadFileSpec);
00326 
00327       if (NS_SUCCEEDED(rv))
00328         m_outFileStream = new nsIOFileStream(m_tmpDownloadFileSpec);
00329     }
00330     else
00331     {
00332       m_outFileStream = new nsIOFileStream(fileSpec /*, PR_CREATE_FILE */);
00333     }
00334     // The following (!m_outFileStream etc) was added to make sure that we don't write somewhere 
00335     // where for some reason or another we can't write to and lose the messages
00336     // See bug 62480
00337     if (!m_outFileStream)
00338         return NS_ERROR_OUT_OF_MEMORY;
00339  
00340     m_outFileStream->seek(PR_SEEK_END, 0);
00341  
00342     if (!m_outFileStream->is_open())
00343         return NS_ERROR_FAILURE;
00344     
00345     // create a new mail parser
00346     m_newMailParser = new nsParseNewMailState;
00347     NS_IF_ADDREF(m_newMailParser);
00348     if (m_newMailParser == nsnull)
00349       return NS_ERROR_OUT_OF_MEMORY;
00350 
00351     m_folder->GetNumNewMessages(PR_FALSE, &m_numNewMessagesInFolder);
00352     nsCOMPtr <nsIMsgFolder> serverFolder;
00353     rv = GetServerFolder(getter_AddRefs(serverFolder));
00354     if (NS_FAILED(rv)) return rv;
00355 
00356     rv = m_newMailParser->Init(serverFolder, m_folder, (m_downloadingToTempFile) ? m_tmpDownloadFileSpec : fileSpec, 
00357                               m_outFileStream, aMsgWindow, m_downloadingToTempFile);
00358        // if we failed to initialize the parser, then just don't use it!!!
00359        // we can still continue without one...
00360 
00361     if (NS_FAILED(rv))
00362     {
00363       NS_IF_RELEASE(m_newMailParser);
00364       rv = NS_OK;
00365     }
00366     else 
00367     {
00368       // Share the inbox fileStream so that moz-status-line flags can be set in the Inbox 
00369       m_newMailParser->SetDBFolderStream(m_outFileStream); 
00370       if (m_downloadingToTempFile)
00371       // Tell the parser to use the offset that will be in the dest folder,
00372       // not the temp folder, so that the msg hdr will start off with
00373       // the correct mdb oid
00374       m_newMailParser->SetEnvelopePos(fileSpec.GetFileSize());
00375     }
00376     if (m_newMailParser)
00377     {
00378       if (uidlDownload)
00379         m_newMailParser->DisableFilters();
00380       else
00381         FindPartialMessages(&fileSpec);
00382     }
00383 
00384 
00385 #ifdef DEBUG
00386     printf("Begin mail message delivery.\n");
00387 #endif 
00388     if (aBool)
00389         *aBool = PR_TRUE;
00390     return NS_OK;
00391 }
00392 
00393 nsresult
00394 nsPop3Sink::EndMailDelivery(nsIPop3Protocol *protocol)
00395 {
00396   CheckPartialMessages(protocol);
00397 
00398   if (m_newMailParser)
00399   {
00400     if (m_outFileStream)
00401       m_outFileStream->flush();    // try this.
00402     m_newMailParser->OnStopRequest(nsnull, nsnull, NS_OK);
00403     m_newMailParser->SetDBFolderStream(nsnull); // stream is going away
00404     m_newMailParser->EndMsgDownload();
00405   }
00406   if (m_outFileStream)
00407   {
00408     m_outFileStream->close();
00409     delete m_outFileStream;
00410     m_outFileStream = 0;
00411   }
00412 
00413   if (m_downloadingToTempFile)
00414     m_tmpDownloadFileSpec.Delete(PR_FALSE);
00415 
00416   // tell the parser to mark the db valid *after* closing the mailbox.
00417   if (m_newMailParser)
00418     m_newMailParser->UpdateDBFolderInfo();
00419 
00420   nsresult rv = ReleaseFolderLock();
00421   NS_ASSERTION(NS_SUCCEEDED(rv),"folder lock not released successfully");
00422 
00423   PRBool filtersRun;
00424   m_folder->CallFilterPlugins(nsnull, &filtersRun); // ??? do we need msgWindow?
00425   PRInt32 numNewMessagesInFolder;
00426   // if filters have marked msgs read or deleted, the num new messages count  
00427   // will go negative by the number of messages marked read or deleted,
00428   // so if we add that number to the number of msgs downloaded, that will give
00429   // us the number of actual new messages.
00430   m_folder->GetNumNewMessages(PR_FALSE, &numNewMessagesInFolder);
00431   m_numNewMessages -= (m_numNewMessagesInFolder  - numNewMessagesInFolder);
00432   m_folder->SetNumNewMessages(m_numNewMessages); // we'll adjust this for spam later
00433   if (!filtersRun && m_numNewMessages > 0)
00434   {
00435     nsCOMPtr <nsIMsgIncomingServer> server;
00436     m_folder->GetServer(getter_AddRefs(server));
00437     if (server)
00438     {
00439       server->SetPerformingBiff(PR_TRUE);
00440       m_folder->SetBiffState(m_biffState);
00441       server->SetPerformingBiff(PR_FALSE);
00442     }
00443   }
00444   // note that size on disk has possibly changed.
00445   nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
00446   if (localFolder)
00447     (void) localFolder->RefreshSizeOnDisk();
00448   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
00449   if (server) 
00450   {
00451     nsCOMPtr <nsIMsgFilterList> filterList;
00452     rv = server->GetFilterList(nsnull, getter_AddRefs(filterList)); 
00453     NS_ENSURE_SUCCESS(rv,rv);
00454 
00455     if (filterList)
00456       (void) filterList->FlushLogIfNecessary();
00457   }
00458 
00459   // fix for bug #161999
00460   // we should update the summary totals for the folder (inbox)
00461   // in case it's not the open folder
00462   m_folder->UpdateSummaryTotals(PR_TRUE);
00463  
00464   // check if the folder open in this window is not the current folder, and if it has new
00465   // message, in which case we need to try to run the filter plugin.
00466   if (m_newMailParser)
00467   {
00468     nsCOMPtr <nsIMsgWindow> msgWindow;
00469     m_newMailParser->GetMsgWindow(getter_AddRefs(msgWindow));
00470     // this breaks down if it's biff downloading new mail because
00471     // there's no msgWindow...
00472     if (msgWindow)
00473     {
00474       nsCOMPtr <nsIMsgFolder> openFolder;
00475       (void) msgWindow->GetOpenFolder(getter_AddRefs(openFolder));
00476       if (openFolder && openFolder != m_folder)
00477       {
00478         // only call filter plugins if folder is a local folder, because only
00479         // local folders get messages filtered into them synchronously by pop3.
00480         nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(openFolder);
00481         if (localFolder)
00482         {
00483           PRBool hasNew, isLocked;
00484           (void) openFolder->GetHasNewMessages(&hasNew);
00485           if (hasNew)
00486           {
00487             // if the open folder is locked, we shouldn't run the spam filters
00488             // on it because someone is using the folder. see 218433.
00489             // Ideally, the filter plugin code would try to grab the folder lock
00490             // and hold onto it until done, but that's more difficult and I think
00491             // this will actually fix the problem.
00492             openFolder->GetLocked(&isLocked);
00493             if(!isLocked)
00494               openFolder->CallFilterPlugins(nsnull, &filtersRun);
00495           }
00496         }
00497       }
00498     }
00499   }
00500 #ifdef DEBUG
00501   printf("End mail message delivery.\n");
00502 #endif 
00503   return NS_OK;
00504 }
00505 
00506 nsresult 
00507 nsPop3Sink::ReleaseFolderLock()
00508 {
00509   nsresult result = NS_OK;
00510   if (!m_folder) 
00511     return result;
00512   PRBool haveSemaphore;
00513   nsCOMPtr <nsISupports> supports = do_QueryInterface(NS_STATIC_CAST(nsIPop3Sink*, this));
00514   result = m_folder->TestSemaphore(supports, &haveSemaphore);
00515   if(NS_SUCCEEDED(result) && haveSemaphore)
00516     result = m_folder->ReleaseSemaphore(supports);
00517   return result;
00518 }
00519 
00520 nsresult 
00521 nsPop3Sink::AbortMailDelivery(nsIPop3Protocol *protocol)
00522 {
00523   CheckPartialMessages(protocol);
00524 
00525   if (m_newMailParser)
00526     m_newMailParser->SetDBFolderStream(nsnull); //stream is going away
00527 
00528   if (m_outFileStream)
00529   {
00530     if (m_outFileStream->is_open())
00531       m_outFileStream->close();
00532     delete m_outFileStream;
00533     m_outFileStream = 0;
00534   }
00535 
00536   if (m_downloadingToTempFile)
00537     m_tmpDownloadFileSpec.Delete(PR_FALSE);
00538 
00539   /* tell the parser to mark the db valid *after* closing the mailbox.
00540   we have truncated the inbox, so berkeley mailbox and msf file are in sync*/
00541   if (m_newMailParser)
00542     m_newMailParser->UpdateDBFolderInfo();
00543 #ifdef DEBUG
00544   nsresult rv =
00545 #endif
00546     ReleaseFolderLock();
00547   NS_ASSERTION(NS_SUCCEEDED(rv),"folder lock not released successfully");
00548 
00549 #ifdef DEBUG
00550     printf("Abort mail message delivery.\n");
00551 #endif 
00552   return NS_OK;
00553 }
00554 
00555 NS_IMETHODIMP
00556 nsPop3Sink::IncorporateBegin(const char* uidlString,
00557                              nsIURI* aURL,
00558                              PRUint32 flags,
00559                              void** closure)
00560 {
00561 #ifdef DEBUG
00562     printf("Incorporate message begin:\n");
00563     if (uidlString)
00564         printf("uidl string: %s\n", uidlString);
00565 #endif 
00566     if (closure)
00567         *closure = (void*) this;
00568     
00569     m_msgOffset = m_outFileStream->tell();
00570     char *dummyEnvelope = GetDummyEnvelope();
00571     
00572     nsresult rv = WriteLineToMailbox(dummyEnvelope);
00573     if (NS_FAILED(rv)) return rv;
00574     // write out account-key before UIDL so the code that looks for 
00575     // UIDL will find the account first and know it can stop looking
00576     // once it finds the UIDL line.
00577     if (!m_accountKey.IsEmpty())
00578     {
00579       nsCAutoString outputString(NS_LITERAL_CSTRING(HEADER_X_MOZILLA_ACCOUNT_KEY ": ") + m_accountKey
00580         + NS_LITERAL_CSTRING(MSG_LINEBREAK));
00581       WriteLineToMailbox(outputString.get());
00582     }
00583     if (uidlString)
00584     {
00585         nsCAutoString uidlCString("X-UIDL: ");
00586         uidlCString += uidlString;
00587         uidlCString += MSG_LINEBREAK;
00588         rv = WriteLineToMailbox(NS_CONST_CAST(char*, uidlCString.get()));
00589         if (NS_FAILED(rv)) return rv;
00590     }
00591     // WriteLineToMailbox("X-Mozilla-Status: 8000" MSG_LINEBREAK);
00592     char *statusLine = PR_smprintf(X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, flags);
00593     rv = WriteLineToMailbox(statusLine);
00594     if (NS_FAILED(rv)) return rv;
00595     rv = WriteLineToMailbox("X-Mozilla-Status2: 00000000" MSG_LINEBREAK);
00596     if (NS_FAILED(rv)) return rv;
00597     // leave space for 60 bytes worth of keys/tags
00598     rv = WriteLineToMailbox(X_MOZILLA_KEYWORDS);
00599     PR_smprintf_free(statusLine);
00600     return NS_OK;
00601 }
00602 
00603 nsresult
00604 nsPop3Sink::SetPopServer(nsIPop3IncomingServer *server)
00605 {
00606   NS_IF_RELEASE(m_popServer);
00607   m_popServer=server;
00608   NS_ADDREF(m_popServer);
00609   
00610   return NS_OK;
00611 }
00612 
00613 nsresult
00614 nsPop3Sink::GetPopServer(nsIPop3IncomingServer* *server)
00615 {
00616     if (!server) return NS_ERROR_NULL_POINTER;
00617     *server = m_popServer;
00618     if (*server) NS_ADDREF(*server);
00619     return NS_OK;
00620 }
00621 
00622 nsresult nsPop3Sink::GetFolder(nsIMsgFolder * *folder)
00623 {
00624   NS_ENSURE_ARG_POINTER(folder);
00625   NS_IF_ADDREF(*folder = m_folder);
00626   return NS_OK;
00627 }
00628 
00629 nsresult nsPop3Sink::SetFolder(nsIMsgFolder * folder)
00630 {
00631   NS_IF_RELEASE(m_folder);
00632   m_folder=folder;
00633   NS_IF_ADDREF(m_folder);
00634   
00635   return NS_OK;
00636 }
00637 
00638 nsresult
00639 nsPop3Sink::GetServerFolder(nsIMsgFolder **aFolder)
00640 {
00641   if (!aFolder) 
00642     return NS_ERROR_NULL_POINTER;
00643   if (m_popServer)
00644   {
00645     // not sure what this is used for - might be wrong if we have a deferred account.
00646     nsCOMPtr <nsIMsgIncomingServer> incomingServer = do_QueryInterface(m_popServer);
00647     if (incomingServer)
00648       return incomingServer->GetRootFolder(aFolder);
00649   }
00650   *aFolder = nsnull;
00651   return NS_ERROR_NULL_POINTER;
00652 }
00653 
00654 char*
00655 nsPop3Sink::GetDummyEnvelope(void)
00656 {
00657   static char result[75];
00658   char *ct;
00659   time_t now = time ((time_t *) 0);
00660 #if defined (XP_WIN)
00661   if (now < 0 || now > 0x7FFFFFFF)
00662          now = 0x7FFFFFFF;
00663 #endif
00664   ct = ctime(&now);
00665   PR_ASSERT(ct[24] == nsCRT::CR || ct[24] == nsCRT::LF);
00666   ct[24] = 0;
00667   /* This value must be in ctime() format, with English abbreviations.
00668         strftime("... %c ...") is no good, because it is localized. */
00669   PL_strcpy(result, "From - ");
00670   PL_strcpy(result + 7, ct);
00671   PL_strcpy(result + 7 + 24, MSG_LINEBREAK);
00672   return result;
00673 }
00674 
00675 nsresult
00676 nsPop3Sink::IncorporateWrite(const char* block,
00677                              PRInt32 length)
00678 {
00679   PRInt32 blockOffset = 0;
00680   if (!strncmp(block, "From ", 5))
00681   {
00682     length++;
00683     blockOffset = 1;
00684   }
00685   if (!m_outputBuffer || length > m_outputBufferSize)
00686   {
00687     if (!m_outputBuffer)
00688       m_outputBuffer = (char*) PR_MALLOC(length+1);
00689     else
00690       m_outputBuffer = (char*) PR_REALLOC(m_outputBuffer, length+1);
00691     
00692     m_outputBufferSize = length;
00693   }
00694   if (m_outputBuffer)
00695   {
00696     if (blockOffset == 1)
00697       *m_outputBuffer = '>';
00698     memcpy(m_outputBuffer + blockOffset, block, length - blockOffset);
00699     *(m_outputBuffer + length) = 0;
00700     nsresult rv = WriteLineToMailbox (m_outputBuffer);
00701     if (NS_FAILED(rv)) return rv;
00702   }
00703   return NS_OK;
00704 }
00705 
00706 nsresult nsPop3Sink::WriteLineToMailbox(const char *buffer)
00707 {
00708   
00709   if (buffer)
00710   {
00711     PRInt32 bufferLen = PL_strlen(buffer);
00712     if (m_newMailParser) // HandleLine should really take a const char *...
00713       m_newMailParser->HandleLine((char *) buffer, bufferLen);
00714     // The following (!m_outFileStream etc) was added to make sure that we don't write somewhere 
00715     // where for some reason or another we can't write to and lose the messages
00716     // See bug 62480
00717     if (!m_outFileStream)
00718       return NS_ERROR_OUT_OF_MEMORY;
00719     
00720     NS_ASSERTION(m_outFileStream->eof(), "we are not writing to end-of-file");
00721     
00722     if (!m_outFileStream->eof())
00723       m_outFileStream->seek(PR_SEEK_END, 0);
00724     PRInt32 bytes = m_outFileStream->write(buffer,bufferLen);
00725     if (bytes != bufferLen) return NS_ERROR_FAILURE;
00726   }
00727   return NS_OK;
00728 }
00729 
00730 nsresult nsPop3Sink::HandleTempDownloadFailed(nsIMsgWindow *msgWindow)
00731 {
00732 
00733   nsCOMPtr<nsIMsgStringService> stringService = do_GetService(NS_MSG_POPSTRINGSERVICE_CONTRACTID);
00734   nsXPIDLString fromStr, subjectStr, confirmString;
00735   m_newMailParser->m_newMsgHdr->GetMime2DecodedSubject(getter_Copies(subjectStr));
00736   m_newMailParser->m_newMsgHdr->GetMime2DecodedAuthor(getter_Copies(fromStr));
00737   const PRUnichar *params[] = { fromStr.get(), subjectStr.get() };
00738   nsCOMPtr<nsIStringBundle> bundle;
00739   nsresult rv = stringService->GetBundle(getter_AddRefs(bundle));
00740   if (NS_SUCCEEDED(rv))
00741     bundle->FormatStringFromID(POP3_TMP_DOWNLOAD_FAILED, params, 2, getter_Copies(confirmString));
00742   nsCOMPtr<nsIDOMWindowInternal> parentWindow;
00743   nsCOMPtr<nsIPromptService> promptService =
00744       do_GetService(NS_PROMPTSERVICE_CONTRACTID);
00745   nsCOMPtr<nsIDocShell> docShell;
00746   if (msgWindow)
00747   {
00748     (void) msgWindow->GetRootDocShell(getter_AddRefs(docShell));
00749     parentWindow = do_QueryInterface(docShell);
00750   }
00751   if (promptService && confirmString)
00752   {
00753     PRInt32 dlgResult  = -1;
00754     rv = promptService->ConfirmEx(parentWindow, nsnull, confirmString,
00755                       (nsIPromptService::BUTTON_TITLE_YES * 
00756                       nsIPromptService::BUTTON_POS_0) +
00757                       (nsIPromptService::BUTTON_TITLE_NO * 
00758                       nsIPromptService::BUTTON_POS_1),
00759                       nsnull,
00760                       nsnull,
00761                       nsnull,
00762                       nsnull,
00763                       nsnull,
00764                       &dlgResult);
00765     m_newMailParser->m_newMsgHdr = nsnull;
00766 
00767     return (dlgResult == 0) ? NS_OK : NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD;
00768   }
00769   return rv;
00770 }
00771 
00772 
00773 NS_IMETHODIMP
00774 nsPop3Sink::IncorporateComplete(nsIMsgWindow *aMsgWindow, PRInt32 aSize)
00775 {
00776   if (m_buildMessageUri && m_baseMessageUri)
00777   {
00778       PRUint32 msgKey;
00779       m_newMailParser->GetEnvelopePos(&msgKey);
00780       m_messageUri.SetLength(0);
00781       nsBuildLocalMessageURI(m_baseMessageUri, msgKey, m_messageUri);
00782   }
00783 
00784   nsresult rv = WriteLineToMailbox(MSG_LINEBREAK);
00785   if (NS_FAILED(rv)) return rv;
00786   rv = m_outFileStream->flush();   //to make sure the message is written to the disk
00787   if (NS_FAILED(rv)) return rv;
00788   NS_ASSERTION(m_newMailParser, "could not get m_newMailParser");
00789   if (m_newMailParser)
00790   {
00791     // PublishMsgHdr clears m_newMsgHdr, so we need a comptr to 
00792     // hold onto it.
00793     nsCOMPtr <nsIMsgDBHdr> hdr = m_newMailParser->m_newMsgHdr;
00794     nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
00795     PRBool doSelect = PR_FALSE;
00796 
00797     // aSize is only set for partial messages. For full messages,
00798     // check to see if we're replacing an old partial message.
00799     if (!aSize && localFolder)
00800       (void) localFolder->DeleteDownloadMsg(hdr, &doSelect);
00801 
00802     if (m_downloadingToTempFile)
00803     {
00804       PRBool moved = PR_FALSE;
00805       // close file to give virus checkers a chance to do their thing...
00806       m_outFileStream->flush();
00807       m_outFileStream->close();
00808       m_newMailParser->FinishHeader();
00809       // need to re-open the inbox file stream.
00810       if (!m_tmpDownloadFileSpec.Exists())
00811         return HandleTempDownloadFailed(aMsgWindow);   
00812 
00813       m_outFileStream->Open(m_tmpDownloadFileSpec, (PR_RDWR | PR_CREATE_FILE));
00814       nsMsgKey saveMsgKey;
00815       hdr->GetMessageKey(&saveMsgKey);
00816       // this is the offset in the temp file, which we need to be correct
00817       // when applying filters;
00818       hdr->SetMessageKey(0); 
00819       m_newMailParser->ApplyFilters(&moved, aMsgWindow, 0);
00820       // restore the msg key so that we don't confuse the msg hdr
00821       // use cache, which requires the hdr to have the same msg key when put
00822       // in the use cache as when it is deleted and hence removed
00823       // from the use cache.
00824       hdr->SetMessageKey(saveMsgKey);
00825       if (!moved)
00826       {
00827         if (m_outFileStream->is_open())
00828         {
00829           nsFileSpec destFolderSpec;
00830 
00831           nsCOMPtr<nsIFileSpec> path;
00832 
00833           m_folder->GetPath(getter_AddRefs(path));
00834           path->GetFileSpec(&destFolderSpec);
00835           PRUint32 newMsgPos = destFolderSpec.GetFileSize();
00836           PRUint32 msgSize;
00837           hdr->GetMessageSize(&msgSize);
00838           hdr->SetMessageKey(newMsgPos);
00839           if (msgSize > m_tmpDownloadFileSpec.GetFileSize())
00840             rv = NS_MSG_ERROR_WRITING_MAIL_FOLDER;   
00841           else
00842             rv = m_newMailParser->AppendMsgFromFile(m_outFileStream, 0, msgSize, destFolderSpec);
00843           if (NS_FAILED(rv))
00844             return HandleTempDownloadFailed(aMsgWindow);   
00845 
00846           // if we have made it this far then the message has successfully been written to the new folder
00847           // now add the header to the destMailDB.
00848           if (NS_SUCCEEDED(rv) && m_newMailParser->m_mailDB)
00849           {
00850             PRUint32 newFlags;
00851             hdr->GetFlags(&newFlags);
00852             if (! (newFlags & MSG_FLAG_READ))
00853             {
00854               nsXPIDLCString junkScoreStr;
00855               (void) hdr->GetStringProperty("junkscore", getter_Copies(junkScoreStr));
00856               if (atoi(junkScoreStr.get()) < 50)
00857               {
00858                 hdr->OrFlags(MSG_FLAG_NEW, &newFlags);
00859                 m_newMailParser->m_mailDB->AddToNewList(newMsgPos);
00860               }
00861             }
00862             m_newMailParser->m_mailDB->AddNewHdrToDB(hdr, PR_TRUE);
00863             m_newMailParser->NotifyGlobalListeners(hdr);
00864           }
00865         }
00866         else
00867         {
00868             return HandleTempDownloadFailed(aMsgWindow);   
00869           // need to give an error here.
00870         }
00871       }
00872       else
00873       {
00874         nsFileSpec destFolderSpec;
00875         nsCOMPtr<nsIFileSpec> path;
00876         // cleanup after mailHdr in source DB because we moved the message.
00877         m_newMailParser->m_mailDB->RemoveHeaderMdbRow(hdr);
00878 
00879         // if the filter moved the message, it called nsParseMailMessageState::Init
00880         // to truncate the source folder, which resets m_envelopePos and m_position.
00881         // So set the envelopePos explicitly here.
00882         m_folder->GetPath(getter_AddRefs(path));
00883         path->GetFileSpec(&destFolderSpec);
00884         m_newMailParser->SetEnvelopePos(destFolderSpec.GetFileSize());
00885       }
00886       m_newMailParser->m_newMsgHdr = nsnull;
00887       m_outFileStream->close(); // close so we can truncate.
00888       m_tmpDownloadFileSpec.Truncate(0);
00889       m_outFileStream->Open(m_tmpDownloadFileSpec, (PR_RDWR | PR_CREATE_FILE));
00890       m_outFileStream->seek(PR_SEEK_END, 0);
00891     } else
00892        m_newMailParser->PublishMsgHeader(aMsgWindow); 
00893     // run any reply/forward filter after we've finished with the
00894     // temp quarantine file, and/or moved the message to another folder.
00895     m_newMailParser->ApplyForwardAndReplyFilter(aMsgWindow);
00896     if (aSize)
00897       hdr->SetUint32Property("onlineSize", aSize);
00898 
00899     // if DeleteDownloadMsg requested it, select the new message
00900     else if (doSelect)
00901       (void) localFolder->SelectDownloadMsg();
00902   }
00903 
00904 #ifdef DEBUG
00905   printf("Incorporate message complete.\n");
00906 #endif
00907   return NS_OK;
00908 }
00909 
00910 NS_IMETHODIMP
00911 nsPop3Sink::IncorporateAbort(PRBool uidlDownload)
00912 {
00913   nsresult rv;
00914   rv = m_outFileStream->close();   //need to close so that the file can be truncated.
00915   NS_ENSURE_SUCCESS(rv,rv);
00916   if (m_msgOffset >= 0 && !m_downloadingToTempFile)
00917   {
00918      nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
00919      NS_ASSERTION(server, "Could not get the pop server !!");
00920      nsCOMPtr<nsIFileSpec> mailDirectory;
00921      if (uidlDownload)
00922         m_folder->GetPath(getter_AddRefs(mailDirectory));
00923      else
00924      {
00925        rv = server->GetLocalPath(getter_AddRefs(mailDirectory));
00926        NS_ENSURE_SUCCESS(rv,rv);
00927        rv = mailDirectory->AppendRelativeUnixPath("Inbox");
00928        NS_ENSURE_SUCCESS(rv,rv);
00929      }
00930      rv = mailDirectory->Truncate(m_msgOffset);
00931      NS_ENSURE_SUCCESS(rv,rv);
00932   }
00933 #ifdef DEBUG
00934     printf("Incorporate message abort.\n");
00935 #endif 
00936     return rv;
00937 }
00938 
00939 nsresult
00940 nsPop3Sink::BiffGetNewMail()
00941 {
00942 #ifdef DEBUG
00943     printf("Biff get new mail.\n");
00944 #endif 
00945     return NS_OK;
00946 }
00947 
00948 nsresult
00949 nsPop3Sink::SetBiffStateAndUpdateFE(PRUint32 aBiffState, PRInt32 numNewMessages, PRBool notify)
00950 {
00951   m_biffState = aBiffState;
00952   if (m_newMailParser)
00953     numNewMessages -= m_newMailParser->m_numNotNewMessages;
00954 
00955   if (notify && m_folder && numNewMessages > 0 && numNewMessages != m_numNewMessages 
00956       && aBiffState == nsIMsgFolder::nsMsgBiffState_NewMail)
00957   {
00958     m_folder->SetNumNewMessages(numNewMessages);
00959     m_folder->SetBiffState(aBiffState);
00960   }
00961   m_numNewMessages = numNewMessages;
00962 
00963   return NS_OK;
00964 }
00965 
00966 NS_IMETHODIMP
00967 nsPop3Sink::GetBuildMessageUri(PRBool *bVal)
00968 {
00969     if (!bVal)
00970         return NS_ERROR_NULL_POINTER;
00971     *bVal = m_buildMessageUri;
00972     return NS_OK;
00973 }
00974 
00975 NS_IMETHODIMP
00976 nsPop3Sink::SetBuildMessageUri(PRBool bVal)
00977 {
00978     m_buildMessageUri = bVal;
00979     return NS_OK;
00980 }
00981 
00982 NS_IMETHODIMP
00983 nsPop3Sink::GetMessageUri(char **messageUri)
00984 {
00985     if (!messageUri || m_messageUri.Length() <= 0) 
00986         return NS_ERROR_NULL_POINTER;
00987     *messageUri = ToNewCString(m_messageUri);
00988     return NS_OK;
00989 }
00990 
00991 NS_IMETHODIMP
00992 nsPop3Sink::SetMessageUri(const char *messageUri)
00993 {
00994     if (!messageUri)
00995         return NS_ERROR_NULL_POINTER;
00996     m_messageUri = messageUri;
00997     return NS_OK;
00998 }
00999 
01000 NS_IMETHODIMP
01001 nsPop3Sink::GetBaseMessageUri(char **baseMessageUri)
01002 {
01003     if (!baseMessageUri || !m_baseMessageUri)
01004         return NS_ERROR_NULL_POINTER;
01005     *baseMessageUri = nsCRT::strdup((const char *) m_baseMessageUri);
01006     return NS_OK;
01007 }
01008 
01009 NS_IMETHODIMP
01010 nsPop3Sink::SetBaseMessageUri(const char *baseMessageUri)
01011 {
01012     if (!baseMessageUri)
01013         return NS_ERROR_NULL_POINTER;
01014     m_baseMessageUri.Adopt(nsCRT::strdup(baseMessageUri));
01015     return NS_OK;
01016 }