1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "msgCore.h"  // precompiled header...
7 #include "nsPop3Sink.h"
8 #include "prprf.h"
9 #include "prlog.h"
10 #include "nscore.h"
11 #include <stdio.h>
12 #include <time.h>
13 #include "nsParseMailbox.h"
14 #include "nsIMsgLocalMailFolder.h"
15 #include "nsIMsgIncomingServer.h"
16 #include "nsLocalUtils.h"
17 #include "nsMsgLocalFolderHdrs.h"
18 #include "nsIMsgFolder.h"  // TO include biffState enum. Change to bool later...
19 #include "nsMsgMessageFlags.h"
20 #include "nsMailHeaders.h"
21 #include "nsIMsgAccountManager.h"
22 #include "nsIPop3Protocol.h"
23 #include "nsLocalMailFolder.h"
24 #include "nsIInputStream.h"
25 #include "nsIPrefBranch.h"
26 #include "nsIPrefService.h"
27 #include "nsDirectoryServiceDefs.h"
28 #include "nsIPromptService.h"
29 #include "nsIDocShell.h"
30 #include "mozIDOMWindow.h"
31 #include "nsEmbedCID.h"
32 #include "nsMsgUtils.h"
33 #include "nsMsgBaseCID.h"
34 #include "nsServiceManagerUtils.h"
35 #include "nsIPop3Service.h"
36 #include "nsMsgLocalCID.h"
37 #include "mozilla/Services.h"
38 #include "mozilla/Logging.h"
39 
40 /* for logging to Error Console */
41 #include "nsIScriptError.h"
42 
43 extern mozilla::LazyLogModule POP3LOGMODULE;  // defined in nsPop3Protocol.cpp
44 #define POP3LOG(str) "sink: [this=%p] " str, this
45 
NS_IMPL_ISUPPORTS(nsPop3Sink,nsIPop3Sink)46 NS_IMPL_ISUPPORTS(nsPop3Sink, nsIPop3Sink)
47 
48 nsPop3Sink::nsPop3Sink() {
49   m_authed = false;
50   m_downloadingToTempFile = false;
51   m_biffState = 0;
52   m_numNewMessages = 0;
53   m_numNewMessagesInFolder = 0;
54   m_numMsgsDownloaded = 0;
55   m_senderAuthed = false;
56   m_outFileStream = nullptr;
57   m_uidlDownload = false;
58   m_buildMessageUri = false;
59 }
60 
~nsPop3Sink()61 nsPop3Sink::~nsPop3Sink() {
62   MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
63           (POP3LOG("Calling ReleaseFolderLock from ~nsPop3Sink")));
64   ReleaseFolderLock();
65 }
66 
SetUserAuthenticated(bool authed)67 nsresult nsPop3Sink::SetUserAuthenticated(bool authed) {
68   m_authed = authed;
69   m_popServer->SetAuthenticated(authed);
70   return NS_OK;
71 }
72 
GetUserAuthenticated(bool * authed)73 nsresult nsPop3Sink::GetUserAuthenticated(bool* authed) {
74   return m_popServer->GetAuthenticated(authed);
75 }
76 
SetSenderAuthedFlag(void * closure,bool authed)77 nsresult nsPop3Sink::SetSenderAuthedFlag(void* closure, bool authed) {
78   m_authed = authed;
79   return NS_OK;
80 }
81 
SetMailAccountURL(const nsACString & urlString)82 nsresult nsPop3Sink::SetMailAccountURL(const nsACString& urlString) {
83   m_accountUrl.Assign(urlString);
84   return NS_OK;
85 }
86 
GetMailAccountURL(nsACString & urlString)87 nsresult nsPop3Sink::GetMailAccountURL(nsACString& urlString) {
88   urlString.Assign(m_accountUrl);
89   return NS_OK;
90 }
91 
partialRecord()92 partialRecord::partialRecord() : m_msgDBHdr(nullptr) {}
93 
~partialRecord()94 partialRecord::~partialRecord() {}
95 
96 // Walk through all the messages in this folder and look for any
97 // PARTIAL messages. For each of those, dig through the mailbox and
98 // find the Account that the message belongs to. If that Account
99 // matches the current Account, then look for the Uidl and save
100 // this message for later processing.
FindPartialMessages()101 nsresult nsPop3Sink::FindPartialMessages() {
102   nsCOMPtr<nsIMsgEnumerator> messages;
103   bool hasMore = false;
104   bool isOpen = false;
105   nsLocalFolderScanState folderScanState;
106   nsCOMPtr<nsIMsgDatabase> db;
107   nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
108   m_folder->GetMsgDatabase(getter_AddRefs(db));
109   if (!localFolder || !db)
110     return NS_ERROR_FAILURE;  // we need it to grub through the folder
111 
112   nsresult rv = db->EnumerateMessages(getter_AddRefs(messages));
113   if (messages) messages->HasMoreElements(&hasMore);
114   while (hasMore && NS_SUCCEEDED(rv)) {
115     uint32_t flags = 0;
116     nsCOMPtr<nsIMsgDBHdr> msgDBHdr;
117     rv = messages->GetNext(getter_AddRefs(msgDBHdr));
118     if (!NS_SUCCEEDED(rv)) break;
119     msgDBHdr->GetFlags(&flags);
120     if (flags & nsMsgMessageFlags::Partial) {
121       // Open the various streams we need to seek and read from the mailbox
122       if (!isOpen) {
123         rv = localFolder->GetFolderScanState(&folderScanState);
124         if (NS_SUCCEEDED(rv))
125           isOpen = true;
126         else
127           break;
128       }
129       rv = localFolder->GetUidlFromFolder(&folderScanState, msgDBHdr);
130       if (!NS_SUCCEEDED(rv)) break;
131 
132       // If we got the uidl, see if this partial message belongs to this
133       // account. Add it to the array if so...
134       if (folderScanState.m_uidl &&
135           m_accountKey.Equals(folderScanState.m_accountKey,
136                               nsCaseInsensitiveCStringComparator)) {
137         partialRecord* partialMsg = new partialRecord();
138         if (partialMsg) {
139           partialMsg->m_uidl = folderScanState.m_uidl;
140           partialMsg->m_msgDBHdr = msgDBHdr;
141           m_partialMsgsArray.AppendElement(partialMsg);
142         }
143       }
144     }
145     messages->HasMoreElements(&hasMore);
146   }
147   if (isOpen && folderScanState.m_inputStream)
148     folderScanState.m_inputStream->Close();
149   return rv;
150 }
151 
152 // For all the partial messages saved by FindPartialMessages,
153 // ask the protocol handler if they still exist on the server.
154 // Any messages that don't exist any more are deleted from the
155 // msgDB.
CheckPartialMessages(nsIPop3Protocol * protocol)156 void nsPop3Sink::CheckPartialMessages(nsIPop3Protocol* protocol) {
157   uint32_t count = m_partialMsgsArray.Length();
158   bool deleted = false;
159 
160   for (uint32_t i = 0; i < count; i++) {
161     partialRecord* partialMsg;
162     bool found = true;
163     partialMsg = m_partialMsgsArray.ElementAt(i);
164     protocol->CheckMessage(partialMsg->m_uidl.get(), &found);
165     if (!found && partialMsg->m_msgDBHdr) {
166       if (m_newMailParser)
167         m_newMailParser->m_mailDB->DeleteHeader(partialMsg->m_msgDBHdr, nullptr,
168                                                 false, true);
169       deleted = true;
170     }
171     delete partialMsg;
172   }
173   m_partialMsgsArray.Clear();
174   if (deleted) {
175     nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
176     if (localFolder) localFolder->NotifyDelete();
177   }
178 }
179 
BeginMailDelivery(bool uidlDownload,nsIMsgWindow * aMsgWindow,bool * aBool)180 nsresult nsPop3Sink::BeginMailDelivery(bool uidlDownload,
181                                        nsIMsgWindow* aMsgWindow, bool* aBool) {
182   nsresult rv;
183 
184   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
185   if (!server) return NS_ERROR_UNEXPECTED;
186 
187   m_window = aMsgWindow;
188 
189   nsCOMPtr<nsIMsgAccountManager> acctMgr =
190       do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
191   nsCOMPtr<nsIMsgAccount> account;
192   NS_ENSURE_SUCCESS(rv, rv);
193   acctMgr->FindAccountForServer(server, getter_AddRefs(account));
194   if (account) account->GetKey(m_accountKey);
195 
196   bool isLocked;
197   nsCOMPtr<nsISupports> supports =
198       do_QueryInterface(static_cast<nsIPop3Sink*>(this));
199   m_folder->GetLocked(&isLocked);
200   if (!isLocked) {
201     MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
202             (POP3LOG("BeginMailDelivery acquiring semaphore")));
203     m_folder->AcquireSemaphore(supports);
204   } else {
205     MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
206             (POP3LOG("BeginMailDelivery folder locked")));
207     return NS_MSG_FOLDER_BUSY;
208   }
209   m_uidlDownload = uidlDownload;
210   if (!uidlDownload) FindPartialMessages();
211 
212   m_folder->GetNumNewMessages(false, &m_numNewMessagesInFolder);
213 
214 #ifdef DEBUG
215   printf("Begin mail message delivery.\n");
216 #endif
217   nsCOMPtr<nsIPop3Service> pop3Service(
218       do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
219   NS_ENSURE_SUCCESS(rv, rv);
220   pop3Service->NotifyDownloadStarted(m_folder);
221   if (aBool) *aBool = true;
222   return NS_OK;
223 }
224 
EndMailDelivery(nsIPop3Protocol * protocol)225 nsresult nsPop3Sink::EndMailDelivery(nsIPop3Protocol* protocol) {
226   CheckPartialMessages(protocol);
227 
228   if (m_newMailParser) {
229     if (m_outFileStream) m_outFileStream->Flush();  // try this.
230     m_newMailParser->OnStopRequest(nullptr, NS_OK);
231     m_newMailParser->EndMsgDownload();
232   }
233   if (m_outFileStream) {
234     m_outFileStream->Close();
235     m_outFileStream = nullptr;
236   }
237 
238   if (m_downloadingToTempFile) m_tmpDownloadFile->Remove(false);
239 
240   // tell the parser to mark the db valid *after* closing the mailbox.
241   if (m_newMailParser) m_newMailParser->UpdateDBFolderInfo();
242 
243   MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
244           (POP3LOG("Calling ReleaseFolderLock from EndMailDelivery")));
245   nsresult rv = ReleaseFolderLock();
246   NS_ASSERTION(NS_SUCCEEDED(rv), "folder lock not released successfully");
247 
248   bool filtersRun;
249   m_folder->CallFilterPlugins(nullptr,
250                               &filtersRun);  // ??? do we need msgWindow?
251   int32_t numNewMessagesInFolder;
252   // if filters have marked msgs read or deleted, the num new messages count
253   // will go negative by the number of messages marked read or deleted,
254   // so if we add that number to the number of msgs downloaded, that will give
255   // us the number of actual new messages.
256   m_folder->GetNumNewMessages(false, &numNewMessagesInFolder);
257   m_numNewMessages -= (m_numNewMessagesInFolder - numNewMessagesInFolder);
258   m_folder->SetNumNewMessages(
259       m_numNewMessages);  // we'll adjust this for spam later
260   if (!filtersRun && m_numNewMessages > 0) {
261     nsCOMPtr<nsIMsgIncomingServer> server;
262     m_folder->GetServer(getter_AddRefs(server));
263     if (server) {
264       server->SetPerformingBiff(true);
265       m_folder->SetBiffState(m_biffState);
266       server->SetPerformingBiff(false);
267     }
268   }
269   // note that size on disk has possibly changed.
270   nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
271   if (localFolder) (void)localFolder->RefreshSizeOnDisk();
272   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
273   if (server) {
274     nsCOMPtr<nsIMsgFilterList> filterList;
275     rv = server->GetFilterList(nullptr, getter_AddRefs(filterList));
276     NS_ENSURE_SUCCESS(rv, rv);
277 
278     if (filterList) (void)filterList->FlushLogIfNecessary();
279   }
280 
281   // fix for bug #161999
282   // we should update the summary totals for the folder (inbox)
283   // in case it's not the open folder
284   m_folder->UpdateSummaryTotals(true);
285 
286   // check if the folder open in this window is not the current folder, and if
287   // it has new message, in which case we need to try to run the filter plugin.
288   if (m_newMailParser) {
289     nsCOMPtr<nsIMsgWindow> msgWindow;
290     m_newMailParser->GetMsgWindow(getter_AddRefs(msgWindow));
291     // this breaks down if it's biff downloading new mail because
292     // there's no msgWindow...
293     if (msgWindow) {
294       nsCOMPtr<nsIMsgFolder> openFolder;
295       (void)msgWindow->GetOpenFolder(getter_AddRefs(openFolder));
296       if (openFolder && openFolder != m_folder) {
297         // only call filter plugins if folder is a local folder, because only
298         // local folders get messages filtered into them synchronously by pop3.
299         nsCOMPtr<nsIMsgLocalMailFolder> localFolder =
300             do_QueryInterface(openFolder);
301         if (localFolder) {
302           bool hasNew, isLocked;
303           (void)openFolder->GetHasNewMessages(&hasNew);
304           if (hasNew) {
305             // if the open folder is locked, we shouldn't run the spam filters
306             // on it because someone is using the folder. see 218433.
307             // Ideally, the filter plugin code would try to grab the folder lock
308             // and hold onto it until done, but that's more difficult and I
309             // think this will actually fix the problem.
310             openFolder->GetLocked(&isLocked);
311             if (!isLocked) openFolder->CallFilterPlugins(nullptr, &filtersRun);
312           }
313         }
314       }
315     }
316   }
317 #ifdef DEBUG
318   printf("End mail message delivery.\n");
319 #endif
320   nsCOMPtr<nsIPop3Service> pop3Service(
321       do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
322   NS_ENSURE_SUCCESS(rv, rv);
323   pop3Service->NotifyDownloadCompleted(m_folder, m_numNewMessages);
324   return NS_OK;
325 }
326 
ReleaseFolderLock()327 nsresult nsPop3Sink::ReleaseFolderLock() {
328   nsresult result = NS_OK;
329   if (!m_folder) return result;
330   bool haveSemaphore;
331   nsCOMPtr<nsISupports> supports =
332       do_QueryInterface(static_cast<nsIPop3Sink*>(this));
333   result = m_folder->TestSemaphore(supports, &haveSemaphore);
334   MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
335           (POP3LOG("ReleaseFolderLock haveSemaphore = %s"),
336            haveSemaphore ? "TRUE" : "FALSE"));
337 
338   if (NS_SUCCEEDED(result) && haveSemaphore)
339     result = m_folder->ReleaseSemaphore(supports);
340   return result;
341 }
342 
AbortMailDelivery(nsIPop3Protocol * protocol)343 nsresult nsPop3Sink::AbortMailDelivery(nsIPop3Protocol* protocol) {
344   CheckPartialMessages(protocol);
345 
346   // ### PS TODO - discard any new message?
347 
348   if (m_outFileStream) {
349     m_outFileStream->Close();
350     m_outFileStream = nullptr;
351   }
352 
353   if (m_downloadingToTempFile && m_tmpDownloadFile)
354     m_tmpDownloadFile->Remove(false);
355 
356   /* tell the parser to mark the db valid *after* closing the mailbox.
357   we have truncated the inbox, so berkeley mailbox and msf file are in sync*/
358   if (m_newMailParser) m_newMailParser->UpdateDBFolderInfo();
359   MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
360           (POP3LOG("Calling ReleaseFolderLock from AbortMailDelivery")));
361 
362   nsresult rv = ReleaseFolderLock();
363   NS_ASSERTION(NS_SUCCEEDED(rv), "folder lock not released successfully");
364 
365 #ifdef DEBUG
366   printf("Abort mail message delivery.\n");
367 #endif
368   nsCOMPtr<nsIPop3Service> pop3Service(
369       do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
370   NS_ENSURE_SUCCESS(rv, rv);
371   pop3Service->NotifyDownloadCompleted(m_folder, 0);
372   return NS_OK;
373 }
374 
375 NS_IMETHODIMP
IncorporateBegin(const char * uidlString,nsIURI * aURL,uint32_t flags,void ** closure)376 nsPop3Sink::IncorporateBegin(const char* uidlString, nsIURI* aURL,
377                              uint32_t flags, void** closure) {
378 #ifdef DEBUG
379   printf("Incorporate message begin:\n");
380   if (uidlString) printf("uidl string: %s\n", uidlString);
381 #endif
382   nsCOMPtr<nsIFile> path;
383 
384   m_folder->GetFilePath(getter_AddRefs(path));
385 
386   nsresult rv;
387   nsCOMPtr<nsIPrefBranch> pPrefBranch(
388       do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
389   if (pPrefBranch) {
390     nsCOMPtr<nsIMsgIncomingServer> server;
391     m_folder->GetServer(getter_AddRefs(server));
392     if (server) {
393       nsCString plugStoreContract;
394       server->GetCharValue("storeContractID", plugStoreContract);
395       // Maildir doesn't care about quaranting, but other stores besides
396       // berkeley mailbox might. We should probably make this an attribute on
397       // the pluggable store, though.
398       if (plugStoreContract.Equals("@mozilla.org/msgstore/berkeleystore;1"_ns))
399         pPrefBranch->GetBoolPref("mailnews.downloadToTempFile",
400                                  &m_downloadingToTempFile);
401     }
402   }
403 
404   nsCOMPtr<nsIMsgDBHdr> newHdr;
405 
406   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
407   if (!server) return NS_ERROR_UNEXPECTED;
408 
409   if (m_downloadingToTempFile) {
410     // need to create an nsIOFileStream from a temp file...
411     nsCOMPtr<nsIFile> tmpDownloadFile;
412     rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR, "newmsg",
413                                          getter_AddRefs(tmpDownloadFile));
414 
415     NS_ASSERTION(NS_SUCCEEDED(rv),
416                  "writing tmp pop3 download file: failed to append filename");
417     if (NS_FAILED(rv)) return rv;
418 
419     if (!m_tmpDownloadFile) {
420       // need a unique tmp file to prevent dataloss in multiuser environment
421       rv = tmpDownloadFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
422       NS_ENSURE_SUCCESS(rv, rv);
423 
424       m_tmpDownloadFile = tmpDownloadFile;
425     }
426     rv = MsgGetFileStream(m_tmpDownloadFile, getter_AddRefs(m_outFileStream));
427     NS_ENSURE_SUCCESS(rv, rv);
428   } else {
429     rv = server->GetMsgStore(getter_AddRefs(m_msgStore));
430     bool reusable;
431     NS_ENSURE_SUCCESS(rv, rv);
432     m_msgStore->GetNewMsgOutputStream(m_folder, getter_AddRefs(newHdr),
433                                       &reusable,
434                                       getter_AddRefs(m_outFileStream));
435   }
436   // The following (!m_outFileStream etc) was added to make sure that we don't
437   // write somewhere where for some reason or another we can't write to and
438   // lose the messages. See bug 62480
439   if (!m_outFileStream) return NS_ERROR_OUT_OF_MEMORY;
440 
441   // create a new mail parser
442   if (!m_newMailParser) m_newMailParser = new nsParseNewMailState;
443   NS_ENSURE_TRUE(m_newMailParser, NS_ERROR_OUT_OF_MEMORY);
444   if (m_uidlDownload) m_newMailParser->DisableFilters();
445 
446   nsCOMPtr<nsIMsgFolder> serverFolder;
447   rv = GetServerFolder(getter_AddRefs(serverFolder));
448   if (NS_FAILED(rv)) return rv;
449 
450   rv = m_newMailParser->Init(serverFolder, m_folder, m_window, newHdr,
451                              m_outFileStream);
452   // If we failed to initialize the parser, then just don't use it!!!
453   // We can still continue without one.
454 
455   if (NS_FAILED(rv)) {
456     m_newMailParser = nullptr;
457     rv = NS_OK;
458   }
459 
460   if (closure) *closure = (void*)this;
461 
462   nsCString outputString(GetDummyEnvelope());
463   rv = WriteLineToMailbox(outputString);
464   NS_ENSURE_SUCCESS(rv, rv);
465   // Write out account-key before UIDL so the code that looks for
466   // UIDL will find the account first and know it can stop looking
467   // once it finds the UIDL line.
468   if (!m_accountKey.IsEmpty()) {
469     outputString.AssignLiteral(HEADER_X_MOZILLA_ACCOUNT_KEY ": ");
470     outputString.Append(m_accountKey);
471     outputString.AppendLiteral(MSG_LINEBREAK);
472     rv = WriteLineToMailbox(outputString);
473     NS_ENSURE_SUCCESS(rv, rv);
474   }
475   if (uidlString) {
476     outputString.AssignLiteral("X-UIDL: ");
477     outputString.Append(uidlString);
478     outputString.AppendLiteral(MSG_LINEBREAK);
479     rv = WriteLineToMailbox(outputString);
480     NS_ENSURE_SUCCESS(rv, rv);
481   }
482 
483   // WriteLineToMailbox("X-Mozilla-Status: 8000" MSG_LINEBREAK);
484   char* statusLine = PR_smprintf(X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, flags);
485   outputString.Assign(statusLine);
486   rv = WriteLineToMailbox(outputString);
487   PR_smprintf_free(statusLine);
488   NS_ENSURE_SUCCESS(rv, rv);
489 
490   rv = WriteLineToMailbox("X-Mozilla-Status2: 00000000"_ns MSG_LINEBREAK);
491   NS_ENSURE_SUCCESS(rv, rv);
492 
493   // leave space for 60 bytes worth of keys/tags
494   rv = WriteLineToMailbox(nsLiteralCString(X_MOZILLA_KEYWORDS));
495   return NS_OK;
496 }
497 
498 NS_IMETHODIMP
SetPopServer(nsIPop3IncomingServer * server)499 nsPop3Sink::SetPopServer(nsIPop3IncomingServer* server) {
500   m_popServer = server;
501   return NS_OK;
502 }
503 
504 NS_IMETHODIMP
GetPopServer(nsIPop3IncomingServer ** aServer)505 nsPop3Sink::GetPopServer(nsIPop3IncomingServer** aServer) {
506   NS_ENSURE_ARG_POINTER(aServer);
507   NS_IF_ADDREF(*aServer = m_popServer);
508   return NS_OK;
509 }
510 
GetFolder(nsIMsgFolder ** aFolder)511 NS_IMETHODIMP nsPop3Sink::GetFolder(nsIMsgFolder** aFolder) {
512   NS_ENSURE_ARG_POINTER(aFolder);
513   NS_IF_ADDREF(*aFolder = m_folder);
514   return NS_OK;
515 }
516 
SetFolder(nsIMsgFolder * aFolder)517 NS_IMETHODIMP nsPop3Sink::SetFolder(nsIMsgFolder* aFolder) {
518   m_folder = aFolder;
519   return NS_OK;
520 }
521 
GetServerFolder(nsIMsgFolder ** aFolder)522 nsresult nsPop3Sink::GetServerFolder(nsIMsgFolder** aFolder) {
523   NS_ENSURE_ARG_POINTER(aFolder);
524 
525   if (m_popServer) {
526     // not sure what this is used for - might be wrong if we have a deferred
527     // account.
528     nsCOMPtr<nsIMsgIncomingServer> incomingServer =
529         do_QueryInterface(m_popServer);
530     if (incomingServer) return incomingServer->GetRootFolder(aFolder);
531   }
532   *aFolder = nullptr;
533   return NS_ERROR_NULL_POINTER;
534 }
535 
SetMsgsToDownload(uint32_t aNumMessages)536 NS_IMETHODIMP nsPop3Sink::SetMsgsToDownload(uint32_t aNumMessages) {
537   m_numNewMessages = aNumMessages;
538   return NS_OK;
539 }
540 
GetDummyEnvelope(void)541 char* nsPop3Sink::GetDummyEnvelope(void) {
542   static char result[75];
543   char* ct;
544   time_t now = time((time_t*)0);
545 #if defined(XP_WIN)
546   if (now < 0 || now > 0x7FFFFFFF) now = 0x7FFFFFFF;
547 #endif
548   ct = ctime(&now);
549   PR_ASSERT(ct[24] == '\r' || ct[24] == '\n');
550   ct[24] = 0;
551   /* This value must be in ctime() format, with English abbreviations.
552    strftime("... %c ...") is no good, because it is localized. */
553   PL_strcpy(result, "From - ");
554   PL_strcpy(result + 7, ct);
555   PL_strcpy(result + 7 + 24, MSG_LINEBREAK);
556   return result;
557 }
558 
IncorporateWrite(const char * block,int32_t length)559 nsresult nsPop3Sink::IncorporateWrite(const char* block, int32_t length) {
560   m_outputBuffer.Truncate();
561   if (!strncmp(block, "From ", 5)) m_outputBuffer.Assign('>');
562 
563   m_outputBuffer.Append(block);
564 
565   return WriteLineToMailbox(m_outputBuffer);
566 }
567 
WriteLineToMailbox(const nsACString & buffer)568 nsresult nsPop3Sink::WriteLineToMailbox(const nsACString& buffer) {
569   if (!buffer.IsEmpty()) {
570     uint32_t bufferLen = buffer.Length();
571     if (m_newMailParser)
572       m_newMailParser->HandleLine(buffer.BeginReading(), bufferLen);
573     // The following (!m_outFileStream etc) was added to make sure that we don't
574     // write somewhere where for some reason or another we can't write to and
575     // lose the messages See bug 62480
576     NS_ENSURE_TRUE(m_outFileStream, NS_ERROR_OUT_OF_MEMORY);
577 
578     // To remove seeking to the end for each line to be written, remove the
579     // following line. See bug 1116055 for details.
580 #define SEEK_TO_END
581 #ifdef SEEK_TO_END
582     // seek to the end in case someone else has sought elsewhere in our stream.
583     nsCOMPtr<nsISeekableStream> seekableOutStream =
584         do_QueryInterface(m_outFileStream);
585 
586     if (seekableOutStream) {
587       int64_t before_seek_pos;
588       nsresult rv2 = seekableOutStream->Tell(&before_seek_pos);
589       MOZ_ASSERT(NS_SUCCEEDED(rv2),
590                  "seekableOutStream->Tell(&before_seek_pos) failed");
591 
592       // XXX Handle error such as network error for remote file system.
593       seekableOutStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
594 
595       int64_t after_seek_pos;
596       nsresult rv3 = seekableOutStream->Tell(&after_seek_pos);
597       MOZ_ASSERT(NS_SUCCEEDED(rv3),
598                  "seekableOutStream->Tell(&after_seek_pos) failed");
599 
600       if (NS_SUCCEEDED(rv2) && NS_SUCCEEDED(rv3)) {
601         if (before_seek_pos != after_seek_pos) {
602           nsString folderName;
603           if (m_folder) m_folder->GetPrettyName(folderName);
604           // This merits a console message, it's poor man's telemetry.
605           MsgLogToConsole4(
606               u"Unexpected file position change detected"_ns +
607                   (folderName.IsEmpty() ? EmptyString() : u" in folder "_ns) +
608                   (folderName.IsEmpty() ? EmptyString() : folderName) +
609                   u". "
610                   "If you can reliably reproduce this, please report the "
611                   "steps you used to dev-apps-thunderbird@lists.mozilla.org "
612                   "or to bug 1308335 at bugzilla.mozilla.org. "
613                   "Resolving this problem will allow speeding up message "
614                   "downloads."_ns,
615               NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
616               nsIScriptError::errorFlag);
617 #  ifdef DEBUG
618           // Debugging, see bug 1116055.
619           if (!folderName.IsEmpty()) {
620             fprintf(stderr,
621                     "(seekdebug) WriteLineToMailbox() detected an unexpected "
622                     "file position change in folder %s.\n",
623                     NS_ConvertUTF16toUTF8(folderName).get());
624           } else {
625             fprintf(stderr,
626                     "(seekdebug) WriteLineToMailbox() detected an unexpected "
627                     "file position change.\n");
628           }
629           fprintf(stderr,
630                   "(seekdebug) before_seek_pos=0x%016llx, "
631                   "after_seek_pos=0x%016llx\n",
632                   (long long unsigned int)before_seek_pos,
633                   (long long unsigned int)after_seek_pos);
634 #  endif
635         }
636       }
637     }
638 #endif
639 
640     uint32_t bytesWritten;
641     m_outFileStream->Write(buffer.BeginReading(), bufferLen, &bytesWritten);
642     NS_ENSURE_TRUE(bytesWritten == bufferLen, NS_ERROR_FAILURE);
643   }
644   return NS_OK;
645 }
646 
HandleTempDownloadFailed(nsIMsgWindow * msgWindow)647 nsresult nsPop3Sink::HandleTempDownloadFailed(nsIMsgWindow* msgWindow) {
648   nsresult rv;
649   nsCOMPtr<nsIStringBundleService> bundleService =
650       mozilla::services::GetStringBundleService();
651   NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
652   nsCOMPtr<nsIStringBundle> bundle;
653   rv = bundleService->CreateBundle(
654       "chrome://messenger/locale/localMsgs.properties", getter_AddRefs(bundle));
655   NS_ENSURE_SUCCESS(rv, rv);
656   nsString fromStr, subjectStr, confirmString;
657 
658   m_newMailParser->m_newMsgHdr->GetMime2DecodedSubject(subjectStr);
659   m_newMailParser->m_newMsgHdr->GetMime2DecodedAuthor(fromStr);
660   AutoTArray<nsString, 2> params = {fromStr, subjectStr};
661   bundle->FormatStringFromName("pop3TmpDownloadError", params, confirmString);
662   nsCOMPtr<mozIDOMWindowProxy> parentWindow;
663   nsCOMPtr<nsIPromptService> promptService =
664       do_GetService(NS_PROMPTSERVICE_CONTRACTID);
665   nsCOMPtr<nsIDocShell> docShell;
666   if (msgWindow) {
667     (void)msgWindow->GetRootDocShell(getter_AddRefs(docShell));
668     parentWindow = do_QueryInterface(docShell);
669   }
670   if (promptService && !confirmString.IsEmpty()) {
671     int32_t dlgResult = -1;
672     bool dummyValue = false;
673     rv = promptService->ConfirmEx(parentWindow, nullptr, confirmString.get(),
674                                   nsIPromptService::STD_YES_NO_BUTTONS, nullptr,
675                                   nullptr, nullptr, nullptr, &dummyValue,
676                                   &dlgResult);
677     m_newMailParser->m_newMsgHdr = nullptr;
678 
679     return (dlgResult == 0) ? NS_OK : NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD;
680   }
681   return rv;
682 }
683 
684 NS_IMETHODIMP
IncorporateComplete(nsIMsgWindow * aMsgWindow,int32_t aSize)685 nsPop3Sink::IncorporateComplete(nsIMsgWindow* aMsgWindow, int32_t aSize) {
686   if (m_buildMessageUri && !m_baseMessageUri.IsEmpty() && m_newMailParser &&
687       m_newMailParser->m_newMsgHdr) {
688     nsMsgKey msgKey;
689     m_newMailParser->m_newMsgHdr->GetMessageKey(&msgKey);
690     m_messageUri.Truncate();
691     nsBuildLocalMessageURI(m_baseMessageUri, msgKey, m_messageUri);
692   }
693 
694   nsresult rv = WriteLineToMailbox(nsLiteralCString(MSG_LINEBREAK));
695   NS_ENSURE_SUCCESS(rv, rv);
696   bool leaveOnServer = false;
697   m_popServer->GetLeaveMessagesOnServer(&leaveOnServer);
698   // We need to flush the output stream, in case mail filters move
699   // the new message, which relies on all the data being flushed.
700   rv =
701       m_outFileStream->Flush();  // Make sure the message is written to the disk
702   NS_ENSURE_SUCCESS(rv, rv);
703   NS_ASSERTION(m_newMailParser, "could not get m_newMailParser");
704   if (m_newMailParser) {
705     // PublishMsgHdr clears m_newMsgHdr, so we need a comptr to
706     // hold onto it.
707     nsCOMPtr<nsIMsgDBHdr> hdr = m_newMailParser->m_newMsgHdr;
708     NS_ASSERTION(hdr, "m_newMailParser->m_newMsgHdr wasn't set");
709     if (!hdr) return NS_ERROR_FAILURE;
710 
711     nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
712     bool doSelect = false;
713 
714     // aSize is only set for partial messages. For full messages,
715     // check to see if we're replacing an old partial message.
716     if (!aSize && localFolder)
717       (void)localFolder->DeleteDownloadMsg(hdr, &doSelect);
718 
719     // If a header already exists for this message (for example, when
720     // getting a complete message when a partial exists), then update the new
721     // header from the old.
722     if (!m_origMessageUri.IsEmpty() && localFolder) {
723       nsCOMPtr<nsIMsgDBHdr> oldMsgHdr;
724       rv = GetMsgDBHdrFromURI(m_origMessageUri, getter_AddRefs(oldMsgHdr));
725       if (NS_SUCCEEDED(rv) && oldMsgHdr)
726         localFolder->UpdateNewMsgHdr(oldMsgHdr, hdr);
727     }
728 
729     if (m_downloadingToTempFile) {
730       // close file to give virus checkers a chance to do their thing...
731       m_outFileStream->Flush();
732       m_outFileStream->Close();
733       m_newMailParser->FinishHeader();
734       // need to re-open the inbox file stream.
735       bool exists;
736       m_tmpDownloadFile->Exists(&exists);
737       if (!exists) return HandleTempDownloadFailed(aMsgWindow);
738 
739       nsCOMPtr<nsIInputStream> inboxInputStream =
740           do_QueryInterface(m_outFileStream);
741       rv = MsgReopenFileStream(m_tmpDownloadFile, inboxInputStream);
742       NS_ENSURE_SUCCESS(rv, HandleTempDownloadFailed(aMsgWindow));
743       if (m_outFileStream) {
744         int64_t tmpDownloadFileSize;
745         uint32_t msgSize;
746         hdr->GetMessageSize(&msgSize);
747         // we need to clone because nsLocalFileUnix caches its stat result,
748         // so it doesn't realize the file has changed size.
749         nsCOMPtr<nsIFile> tmpClone;
750         rv = m_tmpDownloadFile->Clone(getter_AddRefs(tmpClone));
751         NS_ENSURE_SUCCESS(rv, rv);
752         tmpClone->GetFileSize(&tmpDownloadFileSize);
753 
754         if (msgSize > tmpDownloadFileSize)
755           rv = NS_MSG_ERROR_WRITING_MAIL_FOLDER;
756         else
757           rv = m_newMailParser->AppendMsgFromStream(inboxInputStream, hdr,
758                                                     msgSize, m_folder);
759         if (NS_FAILED(rv)) return HandleTempDownloadFailed(aMsgWindow);
760 
761         m_outFileStream->Close();  // close so we can truncate.
762         m_tmpDownloadFile->SetFileSize(0);
763       } else {
764         return HandleTempDownloadFailed(aMsgWindow);
765         // need to give an error here.
766       }
767     } else {
768       m_msgStore->FinishNewMessage(m_outFileStream, hdr);
769     }
770     m_newMailParser->PublishMsgHeader(aMsgWindow);
771     // run any reply/forward filter after we've finished with the
772     // temp quarantine file, and/or moved the message to another folder.
773     m_newMailParser->ApplyForwardAndReplyFilter(aMsgWindow);
774     if (aSize) hdr->SetUint32Property("onlineSize", aSize);
775 
776     // if DeleteDownloadMsg requested it, select the new message
777     else if (doSelect)
778       (void)localFolder->SelectDownloadMsg();
779   }
780 
781 #ifdef DEBUG
782   printf("Incorporate message complete.\n");
783 #endif
784   nsCOMPtr<nsIPop3Service> pop3Service(
785       do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
786   NS_ENSURE_SUCCESS(rv, rv);
787   pop3Service->NotifyDownloadProgress(m_folder, ++m_numMsgsDownloaded,
788                                       m_numNewMessages);
789   return NS_OK;
790 }
791 
792 NS_IMETHODIMP
IncorporateAbort(bool uidlDownload)793 nsPop3Sink::IncorporateAbort(bool uidlDownload) {
794   nsresult rv = m_outFileStream->Close();
795   NS_ENSURE_SUCCESS(rv, rv);
796   if (!m_downloadingToTempFile && m_msgStore && m_newMailParser &&
797       m_newMailParser->m_newMsgHdr) {
798     m_msgStore->DiscardNewMessage(m_outFileStream,
799                                   m_newMailParser->m_newMsgHdr);
800   }
801 #ifdef DEBUG
802   printf("Incorporate message abort.\n");
803 #endif
804   return rv;
805 }
806 
BiffGetNewMail()807 nsresult nsPop3Sink::BiffGetNewMail() {
808 #ifdef DEBUG
809   printf("Biff get new mail.\n");
810 #endif
811   return NS_OK;
812 }
813 
SetBiffStateAndUpdateFE(uint32_t aBiffState,int32_t numNewMessages,bool notify)814 nsresult nsPop3Sink::SetBiffStateAndUpdateFE(uint32_t aBiffState,
815                                              int32_t numNewMessages,
816                                              bool notify) {
817   m_biffState = aBiffState;
818   if (m_newMailParser) numNewMessages -= m_newMailParser->m_numNotNewMessages;
819 
820   if (notify && m_folder && numNewMessages > 0 &&
821       numNewMessages != m_numNewMessages &&
822       aBiffState == nsIMsgFolder::nsMsgBiffState_NewMail) {
823     m_folder->SetNumNewMessages(numNewMessages);
824     m_folder->SetBiffState(aBiffState);
825   }
826   m_numNewMessages = numNewMessages;
827 
828   return NS_OK;
829 }
830 
831 NS_IMETHODIMP
GetBuildMessageUri(bool * bVal)832 nsPop3Sink::GetBuildMessageUri(bool* bVal) {
833   NS_ENSURE_ARG_POINTER(bVal);
834   *bVal = m_buildMessageUri;
835   return NS_OK;
836 }
837 
838 NS_IMETHODIMP
SetBuildMessageUri(bool bVal)839 nsPop3Sink::SetBuildMessageUri(bool bVal) {
840   m_buildMessageUri = bVal;
841   return NS_OK;
842 }
843 
844 NS_IMETHODIMP
GetMessageUri(nsACString & messageUri)845 nsPop3Sink::GetMessageUri(nsACString& messageUri) {
846   NS_ENSURE_TRUE(!m_messageUri.IsEmpty(), NS_ERROR_FAILURE);
847   messageUri = m_messageUri;
848   return NS_OK;
849 }
850 
851 NS_IMETHODIMP
SetMessageUri(const nsACString & messageUri)852 nsPop3Sink::SetMessageUri(const nsACString& messageUri) {
853   m_messageUri = messageUri;
854   return NS_OK;
855 }
856 
857 NS_IMETHODIMP
GetBaseMessageUri(nsACString & baseMessageUri)858 nsPop3Sink::GetBaseMessageUri(nsACString& baseMessageUri) {
859   NS_ENSURE_TRUE(!m_baseMessageUri.IsEmpty(), NS_ERROR_FAILURE);
860   baseMessageUri = m_baseMessageUri;
861   return NS_OK;
862 }
863 
864 NS_IMETHODIMP
SetBaseMessageUri(const nsACString & baseMessageUri)865 nsPop3Sink::SetBaseMessageUri(const nsACString& baseMessageUri) {
866   m_baseMessageUri = baseMessageUri;
867   return NS_OK;
868 }
869 
870 NS_IMETHODIMP
GetOrigMessageUri(nsACString & aOrigMessageUri)871 nsPop3Sink::GetOrigMessageUri(nsACString& aOrigMessageUri) {
872   aOrigMessageUri.Assign(m_origMessageUri);
873   return NS_OK;
874 }
875 
876 NS_IMETHODIMP
SetOrigMessageUri(const nsACString & aOrigMessageUri)877 nsPop3Sink::SetOrigMessageUri(const nsACString& aOrigMessageUri) {
878   m_origMessageUri.Assign(aOrigMessageUri);
879   return NS_OK;
880 }
881