/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "prmem.h" #include "plstr.h" #include "prprf.h" #include "nsCOMPtr.h" #include "nsXPIDLString.h" #include "nsIStringBundle.h" #include "nsIPop3IncomingServer.h" #include "nsPop3IncomingServer.h" #include "nsIPop3Service.h" #include "nsMsgBaseCID.h" #include "nsMsgLocalCID.h" #include "nsMsgFolderFlags.h" #include "nsIFileSpec.h" #include "nsPop3Protocol.h" #include "nsIMsgLocalMailFolder.h" #include "nsIMsgAccountManager.h" #include "nsIMsgMailNewsUrl.h" #include "nsIRDFResource.h" #include "nsIRDFService.h" #include "nsRDFCID.h" static NS_DEFINE_CID(kCPop3ServiceCID, NS_POP3SERVICE_CID); class nsPop3GetMailChainer : public nsIUrlListener { public: NS_DECL_ISUPPORTS NS_DECL_NSIURLLISTENER nsPop3GetMailChainer(); ~nsPop3GetMailChainer(); nsresult GetNewMailForServers(nsISupportsArray *servers, nsIMsgWindow *msgWindow, nsIMsgFolder *folderToDownloadTo, nsIUrlListener *listener); nsresult RunNextGetNewMail(); protected: nsCOMPtr m_folderToDownloadTo; nsCOMPtr m_downloadingMsgWindow; nsCOMPtr m_serversToGetNewMailFor; nsCOMPtr m_listener; }; NS_IMPL_ISUPPORTS_INHERITED2(nsPop3IncomingServer, nsMsgIncomingServer, nsIPop3IncomingServer, nsILocalMailIncomingServer) nsPop3IncomingServer::nsPop3IncomingServer() { m_capabilityFlags = POP3_AUTH_MECH_UNDEFINED | POP3_HAS_AUTH_USER | // should be always there POP3_GURL_UNDEFINED | POP3_UIDL_UNDEFINED | POP3_TOP_UNDEFINED | POP3_XTND_XLST_UNDEFINED; m_canHaveFilters = PR_TRUE; m_authenticated = PR_FALSE; } nsPop3IncomingServer::~nsPop3IncomingServer() { } NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, LeaveMessagesOnServer, "leave_on_server") NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, HeadersOnly, "headers_only") NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, DeleteMailLeftOnServer, "delete_mail_left_on_server") NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, AuthLogin, "auth_login") NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, DotFix, "dot_fix") NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, DeleteByAgeFromServer, "delete_by_age_from_server") NS_IMPL_SERVERPREF_INT(nsPop3IncomingServer, NumDaysToLeaveOnServer, "num_days_to_leave_on_server") NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer, DeferGetNewMail, "defer_get_new_mail") NS_IMETHODIMP nsPop3IncomingServer::GetDeferredToAccount(char **aRetVal) { return GetCharValue("deferred_to_account", aRetVal); } NS_IMETHODIMP nsPop3IncomingServer::SetDeferredToAccount(const char *aAccountKey) { nsXPIDLCString deferredToAccount; GetDeferredToAccount(getter_Copies(deferredToAccount)); m_rootMsgFolder = nsnull; // clear this so we'll recalculate it on demand. //Notify listeners who listen to every folder nsresult rv = SetCharValue("deferred_to_account", aAccountKey); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr folderListenerManager = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); if (NS_SUCCEEDED(rv)) { nsCOMPtr rootFolder; // use GetRootFolder, because that returns the real // root, not the deferred to root. rv = GetRootFolder(getter_AddRefs(rootFolder)); if (rootFolder) { // if isDeferred state has changed, send notification if (((aAccountKey && *aAccountKey) == deferredToAccount.IsEmpty())) { nsCOMPtr folderRes = do_QueryInterface(rootFolder); nsCOMPtr deferAtom = getter_AddRefs(NS_NewAtom("isDeferred")); nsCOMPtr canFileAtom = getter_AddRefs(NS_NewAtom("CanFileMessages")); folderListenerManager->OnItemBoolPropertyChanged(folderRes, deferAtom, !deferredToAccount.IsEmpty(), deferredToAccount.IsEmpty()); folderListenerManager->OnItemBoolPropertyChanged(folderRes, canFileAtom, deferredToAccount.IsEmpty(), !deferredToAccount.IsEmpty()); // this hack causes the account manager ds to send notifications to the // xul content builder that make the changed acct appear or disappear // from the folder pane and related menus. nsCOMPtr acctMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID); if (acctMgr) { acctMgr->NotifyServerUnloaded(this); acctMgr->NotifyServerLoaded(this); // check if this newly deferred to account is the local folders account // and needs to have a newly created INBOX. if (aAccountKey && *aAccountKey) { nsCOMPtr account; acctMgr->GetAccount(aAccountKey, getter_AddRefs(account)); if (account) { nsCOMPtr server; account->GetIncomingServer(getter_AddRefs(server)); if (server) { nsCOMPtr incomingLocalServer = do_QueryInterface(server); if (incomingLocalServer) { nsCOMPtr rootFolder; rv = server->GetRootFolder(getter_AddRefs(rootFolder)); NS_ENSURE_SUCCESS(rv, rv); // this will fail if it already exists, which is fine. rootFolder->CreateSubfolder(NS_LITERAL_STRING("Inbox").get(), nsnull); } } } } } } } } return rv; } //NS_IMPL_GETSET(nsPop3IncomingServer, Authenticated, PRBool, m_authenticated); NS_IMETHODIMP nsPop3IncomingServer::GetAuthenticated(PRBool *aAuthenticated) { NS_ENSURE_ARG_POINTER(aAuthenticated); *aAuthenticated = m_authenticated; return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::SetAuthenticated(PRBool aAuthenticated) { m_authenticated = aAuthenticated; return NS_OK; } nsresult nsPop3IncomingServer::GetPop3CapabilityFlags(PRUint32 *flags) { *flags = m_capabilityFlags; return NS_OK; } nsresult nsPop3IncomingServer::SetPop3CapabilityFlags(PRUint32 flags) { m_capabilityFlags = flags; return NS_OK; } nsresult nsPop3IncomingServer::GetLocalStoreType(char **type) { NS_ENSURE_ARG_POINTER(type); *type = strdup("mailbox"); return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::GetRootMsgFolder(nsIMsgFolder **aRootMsgFolder) { NS_ENSURE_ARG_POINTER(aRootMsgFolder); nsresult rv = NS_OK; if (!m_rootMsgFolder) { nsXPIDLCString deferredToAccount; GetDeferredToAccount(getter_Copies(deferredToAccount)); if (deferredToAccount.IsEmpty()) { rv = CreateRootFolder(); m_rootMsgFolder = m_rootFolder; } else { nsCOMPtr accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr account; rv = accountManager->GetAccount(deferredToAccount, getter_AddRefs(account)); if (account) { nsCOMPtr incomingServer; rv = account->GetIncomingServer(getter_AddRefs(incomingServer)); // make sure we're not deferred to ourself... if (incomingServer && incomingServer != this) rv = incomingServer->GetRootMsgFolder(getter_AddRefs(m_rootMsgFolder)); } } } NS_IF_ADDREF(*aRootMsgFolder = m_rootMsgFolder); return rv; } nsresult nsPop3IncomingServer::GetInbox(nsIMsgWindow *msgWindow, nsIMsgFolder **inbox) { nsCOMPtr rootFolder; nsresult rv = GetRootMsgFolder(getter_AddRefs(rootFolder)); if(NS_SUCCEEDED(rv) && rootFolder) { PRUint32 numFolders; rv = rootFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, 1, &numFolders, inbox); if (!*inbox) return NS_ERROR_NULL_POINTER; } nsCOMPtr localInbox = do_QueryInterface(*inbox, &rv); if (NS_SUCCEEDED(rv) && localInbox) { nsCOMPtr db; rv = (*inbox)->GetMsgDatabase(msgWindow, getter_AddRefs(db)); if (NS_FAILED(rv)) { (*inbox)->SetMsgDatabase(nsnull); (void) localInbox->SetCheckForNewMessagesAfterParsing(PR_TRUE); // this will cause a reparse of the mail folder. localInbox->GetDatabaseWithReparse(nsnull, msgWindow, getter_AddRefs(db)); return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE; } } return rv; } NS_IMETHODIMP nsPop3IncomingServer::PerformBiff(nsIMsgWindow *aMsgWindow) { nsresult rv; nsCOMPtr pop3Service(do_GetService(kCPop3ServiceCID, &rv)); if (NS_FAILED(rv)) return rv; nsCOMPtr inbox; nsCOMPtr rootMsgFolder; nsCOMPtr urlListener; rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder)); if(NS_SUCCEEDED(rv) && rootMsgFolder) { PRUint32 numFolders; rv = rootMsgFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_INBOX, 1, &numFolders, getter_AddRefs(inbox)); if (NS_FAILED(rv) || numFolders != 1) return rv; } nsCOMPtr server; inbox->GetServer(getter_AddRefs(server)); server->SetPerformingBiff(PR_TRUE); urlListener = do_QueryInterface(inbox); PRBool downloadOnBiff = PR_FALSE; rv = GetDownloadOnBiff(&downloadOnBiff); if (downloadOnBiff) { nsCOMPtr localInbox = do_QueryInterface(inbox, &rv); if (localInbox && NS_SUCCEEDED(rv)) { PRBool valid = PR_FALSE; nsCOMPtr db; rv = inbox->GetMsgDatabase(aMsgWindow, getter_AddRefs(db)); if (NS_SUCCEEDED(rv) && db) rv = db->GetSummaryValid(&valid); if (NS_SUCCEEDED(rv) && valid) rv = pop3Service->GetNewMail(aMsgWindow, urlListener, inbox, this, nsnull); else { PRBool isLocked; inbox->GetLocked(&isLocked); if (!isLocked) { rv = localInbox->ParseFolder(aMsgWindow, urlListener); } if (NS_SUCCEEDED(rv)) rv = localInbox->SetCheckForNewMessagesAfterParsing(PR_TRUE); } } } else rv = pop3Service->CheckForNewMail(nsnull, urlListener, inbox, this, nsnull); // it's important to pass in null for the msg window if we are performing biff // this makes sure that we don't show any kind of UI during biff. return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::SetFlagsOnDefaultMailboxes() { nsCOMPtr rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr localFolder = do_QueryInterface(rootFolder, &rv); NS_ENSURE_SUCCESS(rv, rv); // pop3 gets an inbox, but no queue (unsent messages) localFolder->SetFlagsOnDefaultMailboxes(MSG_FOLDER_FLAG_INBOX | MSG_FOLDER_FLAG_SENTMAIL | MSG_FOLDER_FLAG_DRAFTS | MSG_FOLDER_FLAG_TEMPLATES | MSG_FOLDER_FLAG_TRASH | MSG_FOLDER_FLAG_JUNK); return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::CreateDefaultMailboxes(nsIFileSpec *path) { if (!path) return NS_ERROR_NULL_POINTER; (void) path->AppendRelativeUnixPath("Inbox"); nsresult rv = CreateLocalFolder(path, "Inbox"); if (NS_FAILED(rv)) return rv; rv = CreateLocalFolder(path, "Trash"); if (NS_FAILED(rv)) return rv; rv = CreateLocalFolder(path, "Sent"); if (NS_FAILED(rv)) return rv; rv = CreateLocalFolder(path, "Drafts"); if (NS_FAILED(rv)) return rv; return CreateLocalFolder(path, "Templates"); } // override this so we can say that deferred accounts can't have messages // filed to them, which will remove them as targets of all the move/copy // menu items. NS_IMETHODIMP nsPop3IncomingServer::GetCanFileMessagesOnServer(PRBool *aCanFileMessagesOnServer) { NS_ENSURE_ARG_POINTER(aCanFileMessagesOnServer); nsXPIDLCString deferredToAccount; GetDeferredToAccount(getter_Copies(deferredToAccount)); *aCanFileMessagesOnServer = deferredToAccount.IsEmpty(); return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::GetCanCreateFoldersOnServer(PRBool *aCanCreateFoldersOnServer) { NS_ENSURE_ARG_POINTER(aCanCreateFoldersOnServer); nsXPIDLCString deferredToAccount; GetDeferredToAccount(getter_Copies(deferredToAccount)); *aCanCreateFoldersOnServer = deferredToAccount.IsEmpty(); return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::DownloadMailFromServers(nsISupportsArray *aServers, nsIMsgWindow *aMsgWindow, nsIMsgFolder *aFolder, nsIUrlListener *aUrlListener) { nsPop3GetMailChainer *getMailChainer = new nsPop3GetMailChainer; if (!getMailChainer) return NS_ERROR_OUT_OF_MEMORY; getMailChainer->AddRef(); // this object owns itself and releases when done. return getMailChainer->GetNewMailForServers(aServers, aMsgWindow, aFolder, aUrlListener); } NS_IMETHODIMP nsPop3IncomingServer::GetNewMail(nsIMsgWindow *aMsgWindow, nsIUrlListener *aUrlListener, nsIMsgFolder *aInbox, nsIURI **aResult) { nsresult rv; nsCOMPtr pop3Service = do_GetService(kCPop3ServiceCID, &rv); NS_ENSURE_SUCCESS(rv,rv); return pop3Service->GetNewMail(aMsgWindow, aUrlListener, aInbox, this, aResult); } // user has clicked get new messages on this server. If other servers defer to this server, // we need to get new mail for them. But if this server defers to an other server, // I think we only get new mail for this server. NS_IMETHODIMP nsPop3IncomingServer::GetNewMessages(nsIMsgFolder *aFolder, nsIMsgWindow *aMsgWindow, nsIUrlListener *aUrlListener) { nsresult rv; nsCOMPtr pop3Service = do_GetService(kCPop3ServiceCID, &rv); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr inbox; rv = GetInbox(aMsgWindow, getter_AddRefs(inbox)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr url; nsCOMPtr server; nsCOMPtr deferredServers; nsXPIDLCString deferredToAccount; GetDeferredToAccount(getter_Copies(deferredToAccount)); if (deferredToAccount.IsEmpty()) { aFolder->GetServer(getter_AddRefs(server)); GetDeferredServers(server, getter_AddRefs(deferredServers)); } PRUint32 numDeferredServers; if (deferredToAccount.IsEmpty() && deferredServers && NS_SUCCEEDED(deferredServers->Count(&numDeferredServers)) && numDeferredServers > 0) { nsPop3GetMailChainer *getMailChainer = new nsPop3GetMailChainer; getMailChainer->AddRef(); // this object owns itself and releases when done. nsCOMPtr supports; this->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(supports)); deferredServers->InsertElementAt(supports, 0); rv = getMailChainer->GetNewMailForServers(deferredServers, aMsgWindow, inbox, aUrlListener); } else rv = pop3Service->GetNewMail(aMsgWindow, aUrlListener, inbox, this, getter_AddRefs(url)); return rv; } NS_IMETHODIMP nsPop3IncomingServer::GetDownloadMessagesAtStartup(PRBool *getMessagesAtStartup) { // GetMessages is not automatically done for pop servers at startup. // We need to trigger that action. Return true. *getMessagesAtStartup = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::GetCanBeDefaultServer(PRBool *canBeDefaultServer) { *canBeDefaultServer = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::GetCanSearchMessages(PRBool *canSearchMessages) { // this will return false if this server is deferred, which is what we want. return GetCanFileMessagesOnServer(canSearchMessages); } NS_IMETHODIMP nsPop3IncomingServer::GetOfflineSupportLevel(PRInt32 *aSupportLevel) { NS_ENSURE_ARG_POINTER(aSupportLevel); nsresult rv; rv = GetIntValue("offline_support_level", aSupportLevel); if (*aSupportLevel != OFFLINE_SUPPORT_LEVEL_UNDEFINED) return rv; // set default value *aSupportLevel = OFFLINE_SUPPORT_LEVEL_NONE; return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::SetRunningProtocol(nsIPop3Protocol *aProtocol) { NS_ASSERTION(!aProtocol || !m_runningProtocol, "overriding running protocol"); m_runningProtocol = aProtocol; return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::GetRunningProtocol(nsIPop3Protocol **aProtocol) { NS_ENSURE_ARG_POINTER(aProtocol); NS_IF_ADDREF(*aProtocol = m_runningProtocol); return NS_OK; } NS_IMETHODIMP nsPop3IncomingServer::AddUidlToMark(const char *aUidl, PRInt32 aMark) { Pop3UidlEntry *uidlEntry; nsresult rv = NS_ERROR_OUT_OF_MEMORY; uidlEntry = PR_NEWZAP(Pop3UidlEntry); if (uidlEntry) { uidlEntry->uidl = strdup(aUidl); if (uidlEntry->uidl) { uidlEntry->status = (aMark == POP3_DELETE) ? DELETE_CHAR : (aMark == POP3_FETCH_BODY) ? FETCH_BODY : KEEP; m_uidlsToMark.AppendElement(uidlEntry); rv = NS_OK; } else { PR_Free(uidlEntry); } } return rv; } NS_IMETHODIMP nsPop3IncomingServer::MarkMessages() { nsresult rv; if (m_runningProtocol) { rv = m_runningProtocol->MarkMessages(&m_uidlsToMark); } else { nsXPIDLCString hostName; nsXPIDLCString userName; nsCOMPtr localPath; GetLocalPath(getter_AddRefs(localPath)); GetHostName(getter_Copies(hostName)); GetUsername(getter_Copies(userName)); // do it all in one fell swoop rv = nsPop3Protocol::MarkMsgForHost(hostName, userName, localPath, m_uidlsToMark); } PRUint32 count = m_uidlsToMark.Count(); for (PRUint32 i = 0; i < count; i++) { Pop3UidlEntry *ue = NS_STATIC_CAST(Pop3UidlEntry*,m_uidlsToMark[i]); PR_Free(ue->uidl); PR_Free(ue); } m_uidlsToMark.Clear(); return rv; } NS_IMPL_ISUPPORTS1(nsPop3GetMailChainer, nsIUrlListener) nsPop3GetMailChainer::nsPop3GetMailChainer() { } nsPop3GetMailChainer::~nsPop3GetMailChainer() { } nsresult nsPop3GetMailChainer::GetNewMailForServers(nsISupportsArray *servers, nsIMsgWindow *msgWindow, nsIMsgFolder *folderToDownloadTo, nsIUrlListener *listener) { NS_ENSURE_ARG_POINTER(folderToDownloadTo); m_serversToGetNewMailFor = servers; m_folderToDownloadTo = folderToDownloadTo; m_downloadingMsgWindow = msgWindow; m_listener = listener; nsCOMPtr destFolderDB; nsresult rv = folderToDownloadTo->GetMsgDatabase(msgWindow, getter_AddRefs(destFolderDB)); if (NS_FAILED(rv) || !destFolderDB) { nsCOMPtr localFolder = do_QueryInterface(folderToDownloadTo); if (localFolder) { localFolder->GetDatabaseWithReparse(this, msgWindow, getter_AddRefs(destFolderDB)); return NS_OK; } } return RunNextGetNewMail(); } NS_IMETHODIMP nsPop3GetMailChainer::OnStartRunningUrl(nsIURI *url) { return NS_OK; } NS_IMETHODIMP nsPop3GetMailChainer::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode) { return RunNextGetNewMail(); } nsresult nsPop3GetMailChainer::RunNextGetNewMail() { nsresult rv; PRUint32 numServersLeft; m_serversToGetNewMailFor->Count(&numServersLeft); for (; numServersLeft > 0;) { nsCOMPtr popServer (do_QueryElementAt(m_serversToGetNewMailFor, 0)); m_serversToGetNewMailFor->RemoveElementAt(0); numServersLeft--; if (popServer) { PRBool deferGetNewMail = PR_FALSE; nsCOMPtr downloadingToServer; m_folderToDownloadTo->GetServer(getter_AddRefs(downloadingToServer)); popServer->GetDeferGetNewMail(&deferGetNewMail); nsCOMPtr server = do_QueryInterface(popServer); if (deferGetNewMail || downloadingToServer == server) { // have to call routine that just gets mail for one server, // and ignores deferred servers... if (server) { nsCOMPtr url; nsCOMPtr pop3Service = do_GetService(kCPop3ServiceCID, &rv); NS_ENSURE_SUCCESS(rv,rv); return pop3Service->GetNewMail(m_downloadingMsgWindow, this, m_folderToDownloadTo, popServer, getter_AddRefs(url)); } } } } rv = (m_listener) ? m_listener->OnStopRunningUrl(nsnull, NS_OK) : NS_OK; Release(); // release ref to ourself. return rv; }