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