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 "prmem.h"
7 #include "plstr.h"
8 #include "prprf.h"
9 
10 #include "nsCOMPtr.h"
11 #include "nsIPop3IncomingServer.h"
12 #include "nsPop3IncomingServer.h"
13 #include "nsIPop3Service.h"
14 #include "nsMsgBaseCID.h"
15 #include "nsMsgLocalCID.h"
16 #include "nsMsgFolderFlags.h"
17 #include "nsPop3Protocol.h"
18 #include "nsIMsgLocalMailFolder.h"
19 #include "nsIMsgAccountManager.h"
20 #include "nsServiceManagerUtils.h"
21 #include "nsMsgUtils.h"
22 #include "nsMsgDBFolder.h"
23 #include "nsComponentManagerUtils.h"
24 #include "mozilla/Likely.h"
25 
26 static NS_DEFINE_CID(kCPop3ServiceCID, NS_POP3SERVICE_CID);
27 
28 class nsPop3GetMailChainer final : public nsIUrlListener {
29  public:
30   NS_DECL_ISUPPORTS
31   NS_DECL_NSIURLLISTENER
32 
33   nsPop3GetMailChainer();
34   nsresult GetNewMailForServers(
35       const nsTArray<RefPtr<nsIPop3IncomingServer>>& servers,
36       nsIMsgWindow* msgWindow, nsIMsgFolder* folderToDownloadTo,
37       nsIUrlListener* listener);
38   nsresult RunNextGetNewMail();
39 
40  protected:
41   ~nsPop3GetMailChainer();
42   nsCOMPtr<nsIMsgFolder> m_folderToDownloadTo;
43   nsCOMPtr<nsIMsgWindow> m_downloadingMsgWindow;
44   nsTArray<RefPtr<nsIPop3IncomingServer>> m_serversToGetNewMailFor;
45   nsCOMPtr<nsIUrlListener> m_listener;
46 };
47 
NS_IMPL_ISUPPORTS_INHERITED(nsPop3IncomingServer,nsMsgIncomingServer,nsIPop3IncomingServer,nsILocalMailIncomingServer)48 NS_IMPL_ISUPPORTS_INHERITED(nsPop3IncomingServer, nsMsgIncomingServer,
49                             nsIPop3IncomingServer, nsILocalMailIncomingServer)
50 
51 nsPop3IncomingServer::nsPop3IncomingServer() {
52   m_capabilityFlags = POP3_AUTH_MECH_UNDEFINED |
53                       POP3_HAS_AUTH_USER |  // should be always there
54                       POP3_GURL_UNDEFINED | POP3_UIDL_UNDEFINED |
55                       POP3_TOP_UNDEFINED | POP3_XTND_XLST_UNDEFINED;
56 
57   m_canHaveFilters = true;
58   m_authenticated = false;
59 }
60 
~nsPop3IncomingServer()61 nsPop3IncomingServer::~nsPop3IncomingServer() {}
62 
63 NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, LeaveMessagesOnServer,
64                         "leave_on_server")
65 
66 NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, HeadersOnly, "headers_only")
67 
68 NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, DeleteMailLeftOnServer,
69                         "delete_mail_left_on_server")
70 
71 NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, DotFix, "dot_fix")
72 
73 NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, DeleteByAgeFromServer,
74                         "delete_by_age_from_server")
75 
76 NS_IMPL_SERVERPREF_INT(nsPop3IncomingServer, NumDaysToLeaveOnServer,
77                        "num_days_to_leave_on_server")
78 
79 NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, DeferGetNewMail,
80                         "defer_get_new_mail")
81 
GetDeferredToAccount(nsACString & aRetVal)82 NS_IMETHODIMP nsPop3IncomingServer::GetDeferredToAccount(nsACString& aRetVal) {
83   nsresult rv = GetCharValue("deferred_to_account", aRetVal);
84   if (aRetVal.IsEmpty()) return rv;
85   // We need to repair broken profiles that defer to hidden or invalid servers,
86   // so find out if the deferred to account has a valid non-hidden server, and
87   // if not, defer to the local folders inbox.
88   nsCOMPtr<nsIMsgAccountManager> acctMgr =
89       do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID);
90   bool invalidAccount = true;
91   if (acctMgr) {
92     nsCOMPtr<nsIMsgAccount> account;
93     nsCOMPtr<nsIMsgIncomingServer> server;
94     rv = acctMgr->GetAccount(aRetVal, getter_AddRefs(account));
95     if (account) {
96       account->GetIncomingServer(getter_AddRefs(server));
97       if (server) server->GetHidden(&invalidAccount);
98     }
99     if (invalidAccount) {
100       nsCOMPtr<nsIMsgIncomingServer> localServer;
101       nsCOMPtr<nsIMsgAccount> localAccount;
102 
103       rv = acctMgr->GetLocalFoldersServer(getter_AddRefs(localServer));
104       NS_ENSURE_SUCCESS(rv, rv);
105       // Try to copy any folders that have been stranded in the hidden account
106       // into the local folders account.
107       if (server) {
108         nsCOMPtr<nsIMsgFolder> hiddenRootFolder;
109         nsCOMPtr<nsIMsgFolder> localFoldersRoot;
110         server->GetRootFolder(getter_AddRefs(hiddenRootFolder));
111         localServer->GetRootFolder(getter_AddRefs(localFoldersRoot));
112         if (hiddenRootFolder && localFoldersRoot) {
113           // We're going to iterate over the folders in Local Folders-1,
114           // though I suspect only the Inbox will have messages. I don't
115           // think Sent Mail could end up here, but if any folders have
116           // messages, might as well copy them to the real Local Folders
117           // account.
118           nsTArray<RefPtr<nsIMsgFolder>> subFolders;
119           rv = hiddenRootFolder->GetSubFolders(subFolders);
120           if (NS_SUCCEEDED(rv)) {
121             for (nsIMsgFolder* subFolder : subFolders) {
122               nsCOMPtr<nsIMsgDatabase> subFolderDB;
123               subFolder->GetMsgDatabase(getter_AddRefs(subFolderDB));
124               if (!subFolderDB) {
125                 continue;
126               }
127               // Copy any messages in this sub-folder of the hidden
128               // account to the corresponding folder in Local Folders.
129               nsTArray<nsMsgKey> keys;
130               rv = subFolderDB->ListAllKeys(keys);
131               if (NS_FAILED(rv)) {
132                 continue;  // Next subfolder.
133               }
134               nsTArray<RefPtr<nsIMsgDBHdr>> hdrsToCopy;
135               MsgGetHeadersFromKeys(subFolderDB, keys, hdrsToCopy);
136               if (!hdrsToCopy.IsEmpty()) {
137                 // Look for a folder with the same name in Local Folders.
138                 nsCOMPtr<nsIMsgFolder> dest;
139                 nsString folderName;
140                 subFolder->GetName(folderName);
141                 localFoldersRoot->GetChildNamed(folderName,
142                                                 getter_AddRefs(dest));
143                 if (dest) {
144                   dest->CopyMessages(subFolder, hdrsToCopy, false, nullptr,
145                                      nullptr, false, false);
146                 }
147                 // Should we copy the folder if the dest doesn't exist?
148               }
149             }
150           }
151         }
152       }
153       rv = acctMgr->FindAccountForServer(localServer,
154                                          getter_AddRefs(localAccount));
155       NS_ENSURE_SUCCESS(rv, rv);
156       if (!localAccount) return NS_ERROR_NOT_AVAILABLE;
157 
158       localAccount->GetKey(aRetVal);
159       // Can't call SetDeferredToAccount because it calls GetDeferredToAccount.
160       return SetCharValue("deferred_to_account", aRetVal);
161     }
162   }
163   return rv;
164 }
165 
SetDeferredToAccount(const nsACString & aAccountKey)166 NS_IMETHODIMP nsPop3IncomingServer::SetDeferredToAccount(
167     const nsACString& aAccountKey) {
168   nsCString deferredToAccount;
169   GetDeferredToAccount(deferredToAccount);
170   m_rootMsgFolder = nullptr;  // clear this so we'll recalculate it on demand.
171   // Notify listeners who listen to every folder
172 
173   nsresult rv = SetCharValue("deferred_to_account", aAccountKey);
174   NS_ENSURE_SUCCESS(rv, rv);
175   nsCOMPtr<nsIFolderListener> folderListenerManager =
176       do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
177   if (NS_SUCCEEDED(rv)) {
178     nsCOMPtr<nsIMsgFolder> rootFolder;
179     // use GetRootFolder, because that returns the real
180     // root, not the deferred to root.
181     rv = GetRootFolder(getter_AddRefs(rootFolder));
182     if (rootFolder) {
183       // if isDeferred state has changed, send notification
184       if (aAccountKey.IsEmpty() != deferredToAccount.IsEmpty()) {
185         folderListenerManager->OnItemBoolPropertyChanged(
186             rootFolder, kIsDeferred, !deferredToAccount.IsEmpty(),
187             deferredToAccount.IsEmpty());
188         folderListenerManager->OnItemBoolPropertyChanged(
189             rootFolder, kCanFileMessages, deferredToAccount.IsEmpty(),
190             !deferredToAccount.IsEmpty());
191 
192         // this hack causes the account manager ds to send notifications to the
193         // xul content builder that make the changed acct appear or disappear
194         // from the folder pane and related menus.
195         nsCOMPtr<nsIMsgAccountManager> acctMgr =
196             do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID);
197         if (acctMgr) {
198           acctMgr->NotifyServerUnloaded(this);
199           acctMgr->NotifyServerLoaded(this);
200           // check if this newly deferred to account is the local folders
201           // account and needs to have a newly created INBOX.
202           if (!aAccountKey.IsEmpty()) {
203             nsCOMPtr<nsIMsgAccount> account;
204             acctMgr->GetAccount(aAccountKey, getter_AddRefs(account));
205             if (account) {
206               nsCOMPtr<nsIMsgIncomingServer> server;
207               account->GetIncomingServer(getter_AddRefs(server));
208               if (server) {
209                 nsCOMPtr<nsILocalMailIncomingServer> incomingLocalServer =
210                     do_QueryInterface(server);
211                 if (incomingLocalServer) {
212                   nsCOMPtr<nsIMsgFolder> rootFolder;
213                   rv = server->GetRootFolder(getter_AddRefs(rootFolder));
214                   NS_ENSURE_SUCCESS(rv, rv);
215                   // this will fail if it already exists, which is fine.
216                   rootFolder->CreateSubfolder(u"Inbox"_ns, nullptr);
217                 }
218               }
219             }
220           }
221         }
222       }
223     }
224   }
225   return rv;
226 }
227 
228 // NS_IMPL_GETSET(nsPop3IncomingServer, Authenticated, bool, m_authenticated);
229 
GetAuthenticated(bool * aAuthenticated)230 NS_IMETHODIMP nsPop3IncomingServer::GetAuthenticated(bool* aAuthenticated) {
231   NS_ENSURE_ARG_POINTER(aAuthenticated);
232   *aAuthenticated = m_authenticated;
233   return NS_OK;
234 }
235 
SetAuthenticated(bool aAuthenticated)236 NS_IMETHODIMP nsPop3IncomingServer::SetAuthenticated(bool aAuthenticated) {
237   m_authenticated = aAuthenticated;
238   return NS_OK;
239 }
240 
GetPop3CapabilityFlags(uint32_t * flags)241 nsresult nsPop3IncomingServer::GetPop3CapabilityFlags(uint32_t* flags) {
242   *flags = m_capabilityFlags;
243   return NS_OK;
244 }
245 
SetPop3CapabilityFlags(uint32_t flags)246 nsresult nsPop3IncomingServer::SetPop3CapabilityFlags(uint32_t flags) {
247   m_capabilityFlags = flags;
248   return NS_OK;
249 }
250 
251 NS_IMETHODIMP
GetRootMsgFolder(nsIMsgFolder ** aRootMsgFolder)252 nsPop3IncomingServer::GetRootMsgFolder(nsIMsgFolder** aRootMsgFolder) {
253   NS_ENSURE_ARG_POINTER(aRootMsgFolder);
254   nsresult rv = NS_OK;
255   if (!m_rootMsgFolder) {
256     nsCString deferredToAccount;
257     GetDeferredToAccount(deferredToAccount);
258     if (deferredToAccount.IsEmpty()) {
259       rv = CreateRootFolder();
260       m_rootMsgFolder = m_rootFolder;
261     } else {
262       nsCOMPtr<nsIMsgAccountManager> accountManager =
263           do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
264       NS_ENSURE_SUCCESS(rv, rv);
265       nsCOMPtr<nsIMsgAccount> account;
266       rv = accountManager->GetAccount(deferredToAccount,
267                                       getter_AddRefs(account));
268       NS_ENSURE_SUCCESS(rv, rv);
269       if (account) {
270         nsCOMPtr<nsIMsgIncomingServer> incomingServer;
271         rv = account->GetIncomingServer(getter_AddRefs(incomingServer));
272         NS_ENSURE_SUCCESS(rv, rv);
273         // make sure we're not deferred to ourself...
274         if (incomingServer && incomingServer != this)
275           rv =
276               incomingServer->GetRootMsgFolder(getter_AddRefs(m_rootMsgFolder));
277         else
278           rv = NS_ERROR_FAILURE;
279       }
280     }
281   }
282 
283   NS_IF_ADDREF(*aRootMsgFolder = m_rootMsgFolder);
284   return m_rootMsgFolder ? rv : NS_ERROR_FAILURE;
285 }
286 
GetInbox(nsIMsgWindow * msgWindow,nsIMsgFolder ** inbox)287 nsresult nsPop3IncomingServer::GetInbox(nsIMsgWindow* msgWindow,
288                                         nsIMsgFolder** inbox) {
289   NS_ENSURE_ARG_POINTER(inbox);
290   nsCOMPtr<nsIMsgFolder> rootFolder;
291   nsresult rv = GetRootMsgFolder(getter_AddRefs(rootFolder));
292   if (NS_SUCCEEDED(rv) && rootFolder) {
293     rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox, inbox);
294   }
295 
296   nsCOMPtr<nsIMsgLocalMailFolder> localInbox = do_QueryInterface(*inbox, &rv);
297   if (NS_SUCCEEDED(rv) && localInbox) {
298     nsCOMPtr<nsIMsgDatabase> db;
299     rv = (*inbox)->GetMsgDatabase(getter_AddRefs(db));
300     if (NS_FAILED(rv)) {
301       (*inbox)->SetMsgDatabase(nullptr);
302       (void)localInbox->SetCheckForNewMessagesAfterParsing(true);
303       // this will cause a reparse of the mail folder.
304       localInbox->GetDatabaseWithReparse(nullptr, msgWindow,
305                                          getter_AddRefs(db));
306       rv = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
307     }
308   }
309   return rv;
310 }
311 
PerformBiff(nsIMsgWindow * aMsgWindow)312 NS_IMETHODIMP nsPop3IncomingServer::PerformBiff(nsIMsgWindow* aMsgWindow) {
313   nsresult rv;
314   nsCOMPtr<nsIPop3Service> pop3Service(do_GetService(kCPop3ServiceCID, &rv));
315   NS_ENSURE_SUCCESS(rv, rv);
316 
317   nsCOMPtr<nsIMsgFolder> inbox;
318   nsCOMPtr<nsIMsgFolder> rootMsgFolder;
319   nsCOMPtr<nsIUrlListener> urlListener;
320   rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
321   NS_ENSURE_TRUE(rootMsgFolder, NS_ERROR_FAILURE);
322 
323   rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
324                                     getter_AddRefs(inbox));
325   if (!inbox) return NS_ERROR_FAILURE;
326 
327   nsCOMPtr<nsIMsgIncomingServer> server;
328   inbox->GetServer(getter_AddRefs(server));
329 
330   server->SetPerformingBiff(true);
331 
332   urlListener = do_QueryInterface(inbox);
333 
334   bool downloadOnBiff = false;
335   rv = GetDownloadOnBiff(&downloadOnBiff);
336   if (downloadOnBiff) {
337     nsCOMPtr<nsIMsgLocalMailFolder> localInbox = do_QueryInterface(inbox, &rv);
338     if (localInbox && NS_SUCCEEDED(rv)) {
339       bool valid = false;
340       nsCOMPtr<nsIMsgDatabase> db;
341       rv = inbox->GetMsgDatabase(getter_AddRefs(db));
342       if (NS_SUCCEEDED(rv) && db) rv = db->GetSummaryValid(&valid);
343       if (NS_SUCCEEDED(rv) && valid)
344         rv = pop3Service->GetNewMail(aMsgWindow, urlListener, inbox, this,
345                                      nullptr);
346       else {
347         bool isLocked;
348         inbox->GetLocked(&isLocked);
349         if (!isLocked)
350           rv = localInbox->GetDatabaseWithReparse(urlListener, aMsgWindow,
351                                                   getter_AddRefs(db));
352         if (NS_SUCCEEDED(rv))
353           rv = localInbox->SetCheckForNewMessagesAfterParsing(true);
354       }
355     }
356   } else
357     rv = pop3Service->CheckForNewMail(aMsgWindow, urlListener, inbox, this,
358                                       nullptr);
359   return NS_OK;
360 }
361 
362 NS_IMETHODIMP
SetFlagsOnDefaultMailboxes()363 nsPop3IncomingServer::SetFlagsOnDefaultMailboxes() {
364   nsCOMPtr<nsIMsgFolder> rootFolder;
365   nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
366   NS_ENSURE_SUCCESS(rv, rv);
367 
368   nsCOMPtr<nsIMsgLocalMailFolder> localFolder =
369       do_QueryInterface(rootFolder, &rv);
370   NS_ENSURE_SUCCESS(rv, rv);
371 
372   // pop3 gets an inbox, but no queue (unsent messages)
373   localFolder->SetFlagsOnDefaultMailboxes(nsMsgFolderFlags::SpecialUse &
374                                           ~nsMsgFolderFlags::Queue);
375   return NS_OK;
376 }
377 
CreateDefaultMailboxes()378 NS_IMETHODIMP nsPop3IncomingServer::CreateDefaultMailboxes() {
379   nsresult rv = CreateLocalFolder(u"Inbox"_ns);
380   NS_ENSURE_SUCCESS(rv, rv);
381 
382   return CreateLocalFolder(u"Trash"_ns);
383 }
384 
385 // override this so we can say that deferred accounts can't have messages
386 // filed to them, which will remove them as targets of all the move/copy
387 // menu items.
388 NS_IMETHODIMP
GetCanFileMessagesOnServer(bool * aCanFileMessagesOnServer)389 nsPop3IncomingServer::GetCanFileMessagesOnServer(
390     bool* aCanFileMessagesOnServer) {
391   NS_ENSURE_ARG_POINTER(aCanFileMessagesOnServer);
392 
393   nsCString deferredToAccount;
394   GetDeferredToAccount(deferredToAccount);
395   *aCanFileMessagesOnServer = deferredToAccount.IsEmpty();
396   return NS_OK;
397 }
398 
399 NS_IMETHODIMP
GetCanCreateFoldersOnServer(bool * aCanCreateFoldersOnServer)400 nsPop3IncomingServer::GetCanCreateFoldersOnServer(
401     bool* aCanCreateFoldersOnServer) {
402   NS_ENSURE_ARG_POINTER(aCanCreateFoldersOnServer);
403 
404   nsCString deferredToAccount;
405   GetDeferredToAccount(deferredToAccount);
406   *aCanCreateFoldersOnServer = deferredToAccount.IsEmpty();
407   return NS_OK;
408 }
409 
410 NS_IMETHODIMP
VerifyLogon(nsIUrlListener * aUrlListener,nsIMsgWindow * aMsgWindow,nsIURI ** aURL)411 nsPop3IncomingServer::VerifyLogon(nsIUrlListener* aUrlListener,
412                                   nsIMsgWindow* aMsgWindow, nsIURI** aURL) {
413   nsresult rv;
414   nsCOMPtr<nsIPop3Service> pop3Service = do_GetService(kCPop3ServiceCID, &rv);
415   NS_ENSURE_SUCCESS(rv, rv);
416   return pop3Service->VerifyLogon(this, aUrlListener, aMsgWindow, aURL);
417 }
418 
DownloadMailFromServers(const nsTArray<RefPtr<nsIPop3IncomingServer>> & aServers,nsIMsgWindow * aMsgWindow,nsIMsgFolder * aFolder,nsIUrlListener * aUrlListener)419 NS_IMETHODIMP nsPop3IncomingServer::DownloadMailFromServers(
420     const nsTArray<RefPtr<nsIPop3IncomingServer>>& aServers,
421     nsIMsgWindow* aMsgWindow, nsIMsgFolder* aFolder,
422     nsIUrlListener* aUrlListener) {
423   RefPtr<nsPop3GetMailChainer> getMailChainer = new nsPop3GetMailChainer;
424   return getMailChainer->GetNewMailForServers(aServers, aMsgWindow, aFolder,
425                                               aUrlListener);
426 }
427 
GetNewMail(nsIMsgWindow * aMsgWindow,nsIUrlListener * aUrlListener,nsIMsgFolder * aInbox,nsIURI ** aResult)428 NS_IMETHODIMP nsPop3IncomingServer::GetNewMail(nsIMsgWindow* aMsgWindow,
429                                                nsIUrlListener* aUrlListener,
430                                                nsIMsgFolder* aInbox,
431                                                nsIURI** aResult) {
432   nsresult rv;
433   nsCOMPtr<nsIPop3Service> pop3Service = do_GetService(kCPop3ServiceCID, &rv);
434   NS_ENSURE_SUCCESS(rv, rv);
435   return pop3Service->GetNewMail(aMsgWindow, aUrlListener, aInbox, this,
436                                  aResult);
437 }
438 
439 // user has clicked get new messages on this server. If other servers defer to
440 // this server, we need to get new mail for them. But if this server defers to
441 // an other server, I think we only get new mail for this server.
442 NS_IMETHODIMP
GetNewMessages(nsIMsgFolder * aFolder,nsIMsgWindow * aMsgWindow,nsIUrlListener * aUrlListener)443 nsPop3IncomingServer::GetNewMessages(nsIMsgFolder* aFolder,
444                                      nsIMsgWindow* aMsgWindow,
445                                      nsIUrlListener* aUrlListener) {
446   nsresult rv;
447 
448   nsCOMPtr<nsIPop3Service> pop3Service = do_GetService(kCPop3ServiceCID, &rv);
449   NS_ENSURE_SUCCESS(rv, rv);
450 
451   nsCOMPtr<nsIMsgFolder> inbox;
452   rv = GetInbox(aMsgWindow, getter_AddRefs(inbox));
453   NS_ENSURE_SUCCESS(rv, rv);
454   nsCOMPtr<nsIURI> url;
455   nsCOMPtr<nsIMsgIncomingServer> server;
456   nsTArray<RefPtr<nsIPop3IncomingServer>> deferredServers;
457   nsCString deferredToAccount;
458   GetDeferredToAccount(deferredToAccount);
459 
460   if (deferredToAccount.IsEmpty()) {
461     aFolder->GetServer(getter_AddRefs(server));
462     GetDeferredServers(server, deferredServers);
463   }
464   if (deferredToAccount.IsEmpty() && !deferredServers.IsEmpty()) {
465     RefPtr<nsPop3GetMailChainer> getMailChainer = new nsPop3GetMailChainer;
466     deferredServers.InsertElementAt(0, this);
467     return getMailChainer->GetNewMailForServers(deferredServers, aMsgWindow,
468                                                 inbox, aUrlListener);
469   }
470   if (m_runningProtocol) return NS_MSG_FOLDER_BUSY;
471 
472   return pop3Service->GetNewMail(aMsgWindow, aUrlListener, inbox, this,
473                                  getter_AddRefs(url));
474 }
475 
476 NS_IMETHODIMP
GetDownloadMessagesAtStartup(bool * getMessagesAtStartup)477 nsPop3IncomingServer::GetDownloadMessagesAtStartup(bool* getMessagesAtStartup) {
478   NS_ENSURE_ARG_POINTER(getMessagesAtStartup);
479   // GetMessages is not automatically done for pop servers at startup.
480   // We need to trigger that action. Return true.
481   *getMessagesAtStartup = true;
482   return NS_OK;
483 }
484 
485 NS_IMETHODIMP
GetCanBeDefaultServer(bool * canBeDefaultServer)486 nsPop3IncomingServer::GetCanBeDefaultServer(bool* canBeDefaultServer) {
487   NS_ENSURE_ARG_POINTER(canBeDefaultServer);
488   *canBeDefaultServer = true;
489   return NS_OK;
490 }
491 
492 NS_IMETHODIMP
GetCanSearchMessages(bool * canSearchMessages)493 nsPop3IncomingServer::GetCanSearchMessages(bool* canSearchMessages) {
494   // this will return false if this server is deferred, which is what we want.
495   return GetCanFileMessagesOnServer(canSearchMessages);
496 }
497 
498 NS_IMETHODIMP
CloseCachedConnections()499 nsPop3IncomingServer::CloseCachedConnections() {
500   nsCOMPtr<nsIRequest> channel = do_QueryInterface(m_runningProtocol);
501   if (channel) channel->Cancel(NS_ERROR_ABORT);
502   return NS_OK;
503 }
504 
505 NS_IMETHODIMP
GetOfflineSupportLevel(int32_t * aSupportLevel)506 nsPop3IncomingServer::GetOfflineSupportLevel(int32_t* aSupportLevel) {
507   NS_ENSURE_ARG_POINTER(aSupportLevel);
508 
509   nsresult rv;
510   rv = GetIntValue("offline_support_level", aSupportLevel);
511   if (*aSupportLevel != OFFLINE_SUPPORT_LEVEL_UNDEFINED) return rv;
512 
513   // set default value
514   *aSupportLevel = OFFLINE_SUPPORT_LEVEL_NONE;
515   return NS_OK;
516 }
517 
518 NS_IMETHODIMP
SetRunningProtocol(nsIPop3Protocol * aProtocol)519 nsPop3IncomingServer::SetRunningProtocol(nsIPop3Protocol* aProtocol) {
520   NS_ASSERTION(!aProtocol || !m_runningProtocol, "overriding running protocol");
521   m_runningProtocol = aProtocol;
522   return NS_OK;
523 }
524 
GetRunningProtocol(nsIPop3Protocol ** aProtocol)525 NS_IMETHODIMP nsPop3IncomingServer::GetRunningProtocol(
526     nsIPop3Protocol** aProtocol) {
527   NS_ENSURE_ARG_POINTER(aProtocol);
528   NS_IF_ADDREF(*aProtocol = m_runningProtocol);
529   return NS_OK;
530 }
531 
AddUidlToMark(const char * aUidl,int32_t aMark)532 NS_IMETHODIMP nsPop3IncomingServer::AddUidlToMark(const char* aUidl,
533                                                   int32_t aMark) {
534   NS_ENSURE_ARG_POINTER(aUidl);
535 
536   Pop3UidlEntry* uidlEntry = PR_NEWZAP(Pop3UidlEntry);
537   NS_ENSURE_TRUE(uidlEntry, NS_ERROR_OUT_OF_MEMORY);
538 
539   uidlEntry->uidl = strdup(aUidl);
540   if (MOZ_UNLIKELY(!uidlEntry->uidl)) {
541     PR_Free(uidlEntry);
542     return NS_ERROR_OUT_OF_MEMORY;
543   }
544 
545   uidlEntry->status = (aMark == POP3_DELETE)       ? DELETE_CHAR
546                       : (aMark == POP3_FETCH_BODY) ? FETCH_BODY
547                                                    : KEEP;
548   m_uidlsToMark.AppendElement(uidlEntry);
549   return NS_OK;
550 }
551 
MarkMessages()552 NS_IMETHODIMP nsPop3IncomingServer::MarkMessages() {
553   nsresult rv;
554   if (m_runningProtocol)
555     rv = m_runningProtocol->MarkMessages(&m_uidlsToMark);
556   else {
557     nsCString hostName;
558     nsCString userName;
559     nsCOMPtr<nsIFile> localPath;
560 
561     GetLocalPath(getter_AddRefs(localPath));
562 
563     GetHostName(hostName);
564     GetUsername(userName);
565     // do it all in one fell swoop
566     rv = nsPop3Protocol::MarkMsgForHost(hostName.get(), userName.get(),
567                                         localPath, m_uidlsToMark);
568   }
569   uint32_t count = m_uidlsToMark.Length();
570   for (uint32_t i = 0; i < count; i++) {
571     Pop3UidlEntry* ue = m_uidlsToMark[i];
572     PR_Free(ue->uidl);
573     PR_Free(ue);
574   }
575   m_uidlsToMark.Clear();
576   return rv;
577 }
578 
NS_IMPL_ISUPPORTS(nsPop3GetMailChainer,nsIUrlListener)579 NS_IMPL_ISUPPORTS(nsPop3GetMailChainer, nsIUrlListener)
580 
581 nsPop3GetMailChainer::nsPop3GetMailChainer() {}
~nsPop3GetMailChainer()582 nsPop3GetMailChainer::~nsPop3GetMailChainer() {}
583 
GetNewMailForServers(const nsTArray<RefPtr<nsIPop3IncomingServer>> & servers,nsIMsgWindow * msgWindow,nsIMsgFolder * folderToDownloadTo,nsIUrlListener * listener)584 nsresult nsPop3GetMailChainer::GetNewMailForServers(
585     const nsTArray<RefPtr<nsIPop3IncomingServer>>& servers,
586     nsIMsgWindow* msgWindow, nsIMsgFolder* folderToDownloadTo,
587     nsIUrlListener* listener) {
588   NS_ENSURE_ARG_POINTER(folderToDownloadTo);
589 
590   m_serversToGetNewMailFor = servers.Clone();
591   m_serversToGetNewMailFor.Reverse();
592   m_folderToDownloadTo = folderToDownloadTo;
593   m_downloadingMsgWindow = msgWindow;
594   m_listener = listener;
595   nsCOMPtr<nsIMsgDatabase> destFolderDB;
596 
597   nsresult rv =
598       folderToDownloadTo->GetMsgDatabase(getter_AddRefs(destFolderDB));
599   if (NS_FAILED(rv) || !destFolderDB) {
600     nsCOMPtr<nsIMsgLocalMailFolder> localFolder =
601         do_QueryInterface(folderToDownloadTo);
602     if (localFolder) {
603       localFolder->GetDatabaseWithReparse(this, msgWindow,
604                                           getter_AddRefs(destFolderDB));
605       return NS_OK;
606     }
607   }
608   return RunNextGetNewMail();
609 }
610 
611 NS_IMETHODIMP
OnStartRunningUrl(nsIURI * url)612 nsPop3GetMailChainer::OnStartRunningUrl(nsIURI* url) { return NS_OK; }
613 
614 NS_IMETHODIMP
OnStopRunningUrl(nsIURI * aUrl,nsresult aExitCode)615 nsPop3GetMailChainer::OnStopRunningUrl(nsIURI* aUrl, nsresult aExitCode) {
616   return RunNextGetNewMail();
617 }
618 
RunNextGetNewMail()619 nsresult nsPop3GetMailChainer::RunNextGetNewMail() {
620   nsresult rv;
621 
622   while (!m_serversToGetNewMailFor.IsEmpty()) {
623     RefPtr<nsIPop3IncomingServer> popServer(
624         m_serversToGetNewMailFor.PopLastElement());
625     if (popServer) {
626       bool deferGetNewMail = false;
627       nsCOMPtr<nsIMsgIncomingServer> downloadingToServer;
628       m_folderToDownloadTo->GetServer(getter_AddRefs(downloadingToServer));
629       popServer->GetDeferGetNewMail(&deferGetNewMail);
630       nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(popServer);
631       nsCOMPtr<nsIPop3Protocol> protocol;
632       popServer->GetRunningProtocol(getter_AddRefs(protocol));
633       if ((deferGetNewMail || downloadingToServer == server) && !protocol) {
634         // have to call routine that just gets mail for one server,
635         // and ignores deferred servers...
636         if (server) {
637           nsCOMPtr<nsIURI> url;
638           nsCOMPtr<nsIPop3Service> pop3Service =
639               do_GetService(kCPop3ServiceCID, &rv);
640           NS_ENSURE_SUCCESS(rv, rv);
641           return pop3Service->GetNewMail(m_downloadingMsgWindow, this,
642                                          m_folderToDownloadTo, popServer,
643                                          getter_AddRefs(url));
644         }
645       }
646     }
647   }
648   rv = m_listener ? m_listener->OnStopRunningUrl(nullptr, NS_OK) : NS_OK;
649   return rv;
650 }
651