/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "msgCore.h" // precompiled header... #include "nsPop3Service.h" #include "nsIMsgIncomingServer.h" #include "nsIPop3IncomingServer.h" #include "nsPop3URL.h" #include "nsPop3Sink.h" #include "nsPop3Protocol.h" #include "nsMsgLocalCID.h" #include "nsMsgBaseCID.h" #include "nsCOMPtr.h" #include "nsIMsgWindow.h" #include "nsINetUtil.h" #include "nsIRDFService.h" #include "nsRDFCID.h" #include "nsIDirectoryService.h" #include "nsMailDirServiceDefs.h" #include "prprf.h" #include "nsMsgUtils.h" #include "nsIMsgAccountManager.h" #include "nsIMsgAccount.h" #include "nsLocalMailFolder.h" #include "nsIMailboxUrl.h" #include "nsIPrompt.h" #include "nsINetUtil.h" #include "nsComponentManagerUtils.h" #include "nsServiceManagerUtils.h" #include "mozilla/Services.h" #define PREF_MAIL_ROOT_POP3 "mail.root.pop3" // old - for backward compatibility only #define PREF_MAIL_ROOT_POP3_REL "mail.root.pop3-rel" static NS_DEFINE_CID(kPop3UrlCID, NS_POP3URL_CID); static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); nsPop3Service::nsPop3Service() { } nsPop3Service::~nsPop3Service() {} NS_IMPL_ISUPPORTS(nsPop3Service, nsIPop3Service, nsIProtocolHandler, nsIMsgProtocolInfo) NS_IMETHODIMP nsPop3Service::CheckForNewMail(nsIMsgWindow* aMsgWindow, nsIUrlListener *aUrlListener, nsIMsgFolder *aInbox, nsIPop3IncomingServer *aPopServer, nsIURI **aURL) { return GetMail(false /* don't download, just check */, aMsgWindow, aUrlListener, aInbox, aPopServer, aURL); } nsresult nsPop3Service::GetNewMail(nsIMsgWindow *aMsgWindow, nsIUrlListener *aUrlListener, nsIMsgFolder *aInbox, nsIPop3IncomingServer *aPopServer, nsIURI **aURL) { return GetMail(true /* download */, aMsgWindow, aUrlListener, aInbox, aPopServer, aURL); } nsresult nsPop3Service::GetMail(bool downloadNewMail, nsIMsgWindow *aMsgWindow, nsIUrlListener *aUrlListener, nsIMsgFolder *aInbox, nsIPop3IncomingServer *aPopServer, nsIURI **aURL) { NS_ENSURE_ARG_POINTER(aInbox); int32_t popPort = -1; nsCOMPtr<nsIMsgIncomingServer> server; nsCOMPtr<nsIURI> url; server = do_QueryInterface(aPopServer); NS_ENSURE_TRUE(server, NS_MSG_INVALID_OR_MISSING_SERVER); nsCOMPtr<nsIMsgLocalMailFolder> destLocalFolder = do_QueryInterface(aInbox); if (destLocalFolder) { // We don't know the needed size yet, so at least check // if there is some free space (1MB) in the message store. bool destFolderTooBig; destLocalFolder->WarnIfLocalFileTooBig(aMsgWindow, 0xFFFF, &destFolderTooBig); if (destFolderTooBig) return NS_MSG_ERROR_WRITING_MAIL_FOLDER; } nsCString popHost; nsCString popUser; nsresult rv = server->GetHostName(popHost); NS_ENSURE_SUCCESS(rv, rv); if (popHost.IsEmpty()) return NS_MSG_INVALID_OR_MISSING_SERVER; rv = server->GetPort(&popPort); NS_ENSURE_SUCCESS(rv, rv); rv = server->GetUsername(popUser); NS_ENSURE_SUCCESS(rv, rv); if (popUser.IsEmpty()) return NS_MSG_SERVER_USERNAME_MISSING; nsCString escapedUsername; MsgEscapeString(popUser, nsINetUtil::ESCAPE_XALPHAS, escapedUsername); if (NS_SUCCEEDED(rv) && aPopServer) { // now construct a pop3 url... // we need to escape the username because it may contain // characters like / % or @ char * urlSpec = (downloadNewMail) ? PR_smprintf("pop3://%s@%s:%d", escapedUsername.get(), popHost.get(), popPort) : PR_smprintf("pop3://%s@%s:%d/?check", escapedUsername.get(), popHost.get(), popPort); rv = BuildPop3Url(urlSpec, aInbox, aPopServer, aUrlListener, getter_AddRefs(url), aMsgWindow); PR_smprintf_free(urlSpec); NS_ENSURE_SUCCESS(rv, rv); } NS_ENSURE_TRUE(url, rv); if (NS_SUCCEEDED(rv)) rv = RunPopUrl(server, url); if (aURL) // we already have a ref count on pop3url... NS_IF_ADDREF(*aURL = url); return rv; } NS_IMETHODIMP nsPop3Service::VerifyLogon(nsIMsgIncomingServer *aServer, nsIUrlListener *aUrlListener, nsIMsgWindow *aMsgWindow, nsIURI **aURL) { NS_ENSURE_ARG_POINTER(aServer); nsCString popHost; nsCString popUser; int32_t popPort = -1; nsresult rv = aServer->GetHostName(popHost); NS_ENSURE_SUCCESS(rv, rv); if (popHost.IsEmpty()) return NS_MSG_INVALID_OR_MISSING_SERVER; rv = aServer->GetPort(&popPort); NS_ENSURE_SUCCESS(rv, rv); rv = aServer->GetUsername(popUser); NS_ENSURE_SUCCESS(rv, rv); if (popUser.IsEmpty()) return NS_MSG_SERVER_USERNAME_MISSING; nsCString escapedUsername; MsgEscapeString(popUser, nsINetUtil::ESCAPE_XALPHAS, escapedUsername); nsCOMPtr<nsIPop3IncomingServer> popServer = do_QueryInterface(aServer, &rv); NS_ENSURE_SUCCESS(rv, rv); // now construct a pop3 url... // we need to escape the username because it may contain // characters like / % or @ char *urlSpec = PR_smprintf("pop3://%s@%s:%d/?verifyLogon", escapedUsername.get(), popHost.get(), popPort); NS_ENSURE_TRUE(urlSpec, NS_ERROR_OUT_OF_MEMORY); nsCOMPtr<nsIURI> url; rv = BuildPop3Url(urlSpec, nullptr, popServer, aUrlListener, getter_AddRefs(url), aMsgWindow); PR_smprintf_free(urlSpec); if (NS_SUCCEEDED(rv) && url) { rv = RunPopUrl(aServer, url); if (NS_SUCCEEDED(rv) && aURL) url.forget(aURL); } return rv; } nsresult nsPop3Service::BuildPop3Url(const char *urlSpec, nsIMsgFolder *inbox, nsIPop3IncomingServer *server, nsIUrlListener *aUrlListener, nsIURI **aUrl, nsIMsgWindow *aMsgWindow) { nsresult rv; nsPop3Sink *pop3Sink = new nsPop3Sink(); NS_ENSURE_TRUE(pop3Sink, NS_ERROR_OUT_OF_MEMORY); pop3Sink->SetPopServer(server); pop3Sink->SetFolder(inbox); // now create a pop3 url and a protocol instance to run the url.... nsCOMPtr<nsIPop3URL> pop3Url = do_CreateInstance(kPop3UrlCID, &rv); NS_ENSURE_SUCCESS(rv,rv); pop3Url->SetPop3Sink(pop3Sink); rv = CallQueryInterface(pop3Url, aUrl); NS_ENSURE_SUCCESS(rv,rv); rv = (*aUrl)->SetSpec(nsDependentCString(urlSpec)); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(pop3Url); if (mailnewsurl) { if (aUrlListener) mailnewsurl->RegisterListener(aUrlListener); if (aMsgWindow) mailnewsurl->SetMsgWindow(aMsgWindow); } return rv; } nsresult nsPop3Service::RunPopUrl(nsIMsgIncomingServer *aServer, nsIURI *aUrlToRun) { NS_ENSURE_ARG_POINTER(aServer); NS_ENSURE_ARG_POINTER(aUrlToRun); nsCString userName; // load up required server information // we store the username unescaped in the server // so there is no need to unescape it nsresult rv = aServer->GetRealUsername(userName); // find out if the server is busy or not...if the server is busy, we are // *NOT* going to run the url bool serverBusy = false; rv = aServer->GetServerBusy(&serverBusy); NS_ENSURE_SUCCESS(rv, rv); if (!serverBusy) { RefPtr<nsPop3Protocol> protocol = new nsPop3Protocol(aUrlToRun); if (protocol) { // the protocol stores the unescaped username, so there is no need to escape it. protocol->SetUsername(userName.get()); rv = protocol->LoadUrl(aUrlToRun); if (NS_FAILED(rv)) aServer->SetServerBusy(false); } } else { nsCOMPtr<nsIMsgMailNewsUrl> url = do_QueryInterface(aUrlToRun); if (url) AlertServerBusy(url); rv = NS_ERROR_FAILURE; } return rv; } NS_IMETHODIMP nsPop3Service::GetScheme(nsACString &aScheme) { aScheme.AssignLiteral("pop3"); return NS_OK; } NS_IMETHODIMP nsPop3Service::GetDefaultPort(int32_t *aDefaultPort) { NS_ENSURE_ARG_POINTER(aDefaultPort); *aDefaultPort = nsIPop3URL::DEFAULT_POP3_PORT; return NS_OK; } NS_IMETHODIMP nsPop3Service::AllowPort(int32_t port, const char *scheme, bool *_retval) { *_retval = true; // allow pop on any port return NS_OK; } NS_IMETHODIMP nsPop3Service::GetDefaultDoBiff(bool *aDoBiff) { NS_ENSURE_ARG_POINTER(aDoBiff); // by default, do biff for POP3 servers *aDoBiff = true; return NS_OK; } NS_IMETHODIMP nsPop3Service::GetProtocolFlags(uint32_t *result) { NS_ENSURE_ARG_POINTER(result); *result = URI_NORELATIVE | URI_DANGEROUS_TO_LOAD | ALLOWS_PROXY | URI_FORBIDS_COOKIE_ACCESS; return NS_OK; } NS_IMETHODIMP nsPop3Service::NewURI(const nsACString &aSpec, const char *aOriginCharset, // ignored nsIURI *aBaseURI, nsIURI **_retval) { NS_ENSURE_ARG_POINTER(_retval); nsAutoCString folderUri(aSpec); nsCOMPtr<nsIRDFResource> resource; int32_t offset = folderUri.FindChar('?'); if (offset != kNotFound) folderUri.SetLength(offset); const char *uidl = PL_strstr(nsCString(aSpec).get(), "uidl="); NS_ENSURE_TRUE(uidl, NS_ERROR_FAILURE); nsresult rv; nsCOMPtr<nsIRDFService> rdfService(do_GetService(kRDFServiceCID, &rv)); NS_ENSURE_SUCCESS(rv, rv); rv = rdfService->GetResource(folderUri, getter_AddRefs(resource)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(resource, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIMsgIncomingServer> server; nsLocalFolderScanState folderScanState; nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(folder); nsCOMPtr<nsIMailboxUrl> mailboxUrl = do_QueryInterface(aBaseURI); if (mailboxUrl && localFolder) { rv = localFolder->GetFolderScanState(&folderScanState); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIMsgDBHdr> msgHdr; nsMsgKey msgKey; mailboxUrl->GetMessageKey(&msgKey); folder->GetMessageHeader(msgKey, getter_AddRefs(msgHdr)); // we do this to get the account key if (msgHdr) localFolder->GetUidlFromFolder(&folderScanState, msgHdr); if (!folderScanState.m_accountKey.IsEmpty()) { nsCOMPtr<nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); if (accountManager) { nsCOMPtr<nsIMsgAccount> account; accountManager->GetAccount(folderScanState.m_accountKey, getter_AddRefs(account)); if (account) account->GetIncomingServer(getter_AddRefs(server)); } } } if (!server) rv = folder->GetServer(getter_AddRefs(server)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIPop3IncomingServer> popServer = do_QueryInterface(server,&rv); NS_ENSURE_SUCCESS(rv, rv); nsCString hostname; nsCString username; server->GetHostName(hostname); server->GetUsername(username); int32_t port; server->GetPort(&port); if (port == -1) port = nsIPop3URL::DEFAULT_POP3_PORT; // we need to escape the username because it may contain // characters like / % or @ nsCString escapedUsername; MsgEscapeString(username, nsINetUtil::ESCAPE_XALPHAS, escapedUsername); nsAutoCString popSpec("pop://"); popSpec += escapedUsername; popSpec += "@"; popSpec += hostname; popSpec += ":"; popSpec.AppendInt(port); popSpec += "?"; popSpec += uidl; nsCOMPtr<nsIUrlListener> urlListener = do_QueryInterface(folder, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = BuildPop3Url(popSpec.get(), folder, popServer, urlListener, _retval, nullptr); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(*_retval, &rv); if (NS_SUCCEEDED(rv)) { // escape the username before we call SetUsername(). we do this because GetUsername() // will unescape the username mailnewsurl->SetUsername(escapedUsername); } nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(mailnewsurl, &rv); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString messageUri (aSpec); if (!strncmp(messageUri.get(), "mailbox:", 8)) messageUri.Replace(0, 8, "mailbox-message:"); offset = messageUri.Find("?number="); if (offset != kNotFound) messageUri.Replace(offset, 8, "#"); offset = messageUri.FindChar('&'); if (offset != kNotFound) messageUri.SetLength(offset); popurl->SetMessageUri(messageUri.get()); nsCOMPtr<nsIPop3Sink> pop3Sink; rv = popurl->GetPop3Sink(getter_AddRefs(pop3Sink)); NS_ENSURE_SUCCESS(rv, rv); pop3Sink->SetBuildMessageUri(true); return NS_OK; } void nsPop3Service::AlertServerBusy(nsIMsgMailNewsUrl *url) { nsresult rv; nsCOMPtr<nsIStringBundleService> bundleService = mozilla::services::GetStringBundleService(); if (!bundleService) return; nsCOMPtr<nsIStringBundle> bundle; rv = bundleService->CreateBundle("chrome://messenger/locale/localMsgs.properties", getter_AddRefs(bundle)); NS_ENSURE_SUCCESS_VOID(rv); nsCOMPtr<nsIMsgWindow> msgWindow; nsCOMPtr<nsIPrompt> dialog; rv = url->GetMsgWindow(getter_AddRefs(msgWindow)); //it is ok to have null msgWindow, for example when biffing if (NS_FAILED(rv) || !msgWindow) return; rv = msgWindow->GetPromptDialog(getter_AddRefs(dialog)); NS_ENSURE_SUCCESS_VOID(rv); nsString accountName; nsCOMPtr<nsIMsgIncomingServer> server; rv = url->GetServer(getter_AddRefs(server)); NS_ENSURE_SUCCESS_VOID(rv); rv = server->GetPrettyName(accountName); NS_ENSURE_SUCCESS_VOID(rv); const char16_t *params[] = { accountName.get() }; nsString alertString; nsString dialogTitle; bundle->FormatStringFromName( u"pop3ServerBusy", params, 1, getter_Copies(alertString)); bundle->FormatStringFromName( u"pop3ErrorDialogTitle", params, 1, getter_Copies(dialogTitle)); if (!alertString.IsEmpty()) dialog->Alert(dialogTitle.get(), alertString.get()); } NS_IMETHODIMP nsPop3Service::NewChannel(nsIURI *aURI, nsIChannel **_retval) { return NewChannel2(aURI, nullptr, _retval); } NS_IMETHODIMP nsPop3Service::NewChannel2(nsIURI *aURI, nsILoadInfo *aLoadInfo, nsIChannel **_retval) { NS_ENSURE_ARG_POINTER(aURI); nsresult rv; nsCOMPtr<nsIMsgMailNewsUrl> url = do_QueryInterface(aURI, &rv); nsCString realUserName; if (NS_SUCCEEDED(rv) && url) { nsCOMPtr<nsIMsgIncomingServer> server; url->GetServer(getter_AddRefs(server)); if (server) { // find out if the server is busy or not...if the server is busy, we are // *NOT* going to run the url. The error code isn't quite right... // We might want to put up an error right here. bool serverBusy = false; rv = server->GetServerBusy(&serverBusy); if (serverBusy) { AlertServerBusy(url); return NS_MSG_FOLDER_BUSY; } server->GetRealUsername(realUserName); } } RefPtr<nsPop3Protocol> protocol = new nsPop3Protocol(aURI); NS_ENSURE_TRUE(protocol, NS_ERROR_OUT_OF_MEMORY); rv = protocol->Initialize(aURI); NS_ENSURE_SUCCESS(rv, rv); rv = protocol->SetLoadInfo(aLoadInfo); NS_ENSURE_SUCCESS(rv, rv); protocol->SetUsername(realUserName.get()); return CallQueryInterface(protocol, _retval); } NS_IMETHODIMP nsPop3Service::SetDefaultLocalPath(nsIFile *aPath) { NS_ENSURE_ARG(aPath); return NS_SetPersistentFile(PREF_MAIL_ROOT_POP3_REL, PREF_MAIL_ROOT_POP3, aPath); } NS_IMETHODIMP nsPop3Service::GetDefaultLocalPath(nsIFile **aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = nullptr; bool havePref; nsCOMPtr<nsIFile> localFile; nsresult rv = NS_GetPersistentFile(PREF_MAIL_ROOT_POP3_REL, PREF_MAIL_ROOT_POP3, NS_APP_MAIL_50_DIR, havePref, getter_AddRefs(localFile)); NS_ENSURE_SUCCESS(rv, rv); bool exists; rv = localFile->Exists(&exists); if (NS_SUCCEEDED(rv) && !exists) rv = localFile->Create(nsIFile::DIRECTORY_TYPE, 0775); NS_ENSURE_SUCCESS(rv, rv); if (!havePref || !exists) { rv = NS_SetPersistentFile(PREF_MAIL_ROOT_POP3_REL, PREF_MAIL_ROOT_POP3, localFile); NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to set root dir pref."); } NS_IF_ADDREF(*aResult = localFile); return NS_OK; } NS_IMETHODIMP nsPop3Service::GetServerIID(nsIID **aServerIID) { *aServerIID = new nsIID(NS_GET_IID(nsIPop3IncomingServer)); return NS_OK; } NS_IMETHODIMP nsPop3Service::GetRequiresUsername(bool *aRequiresUsername) { NS_ENSURE_ARG_POINTER(aRequiresUsername); *aRequiresUsername = true; return NS_OK; } NS_IMETHODIMP nsPop3Service::GetPreflightPrettyNameWithEmailAddress(bool *aPreflightPrettyNameWithEmailAddress) { NS_ENSURE_ARG_POINTER(aPreflightPrettyNameWithEmailAddress); *aPreflightPrettyNameWithEmailAddress = true; return NS_OK; } NS_IMETHODIMP nsPop3Service::GetCanLoginAtStartUp(bool *aCanLoginAtStartUp) { NS_ENSURE_ARG_POINTER(aCanLoginAtStartUp); *aCanLoginAtStartUp = true; return NS_OK; } NS_IMETHODIMP nsPop3Service::GetCanDelete(bool *aCanDelete) { NS_ENSURE_ARG_POINTER(aCanDelete); *aCanDelete = true; return NS_OK; } NS_IMETHODIMP nsPop3Service::GetCanDuplicate(bool *aCanDuplicate) { NS_ENSURE_ARG_POINTER(aCanDuplicate); *aCanDuplicate = true; return NS_OK; } NS_IMETHODIMP nsPop3Service::GetCanGetMessages(bool *aCanGetMessages) { NS_ENSURE_ARG_POINTER(aCanGetMessages); *aCanGetMessages = true; return NS_OK; } NS_IMETHODIMP nsPop3Service::GetCanGetIncomingMessages(bool *aCanGetIncomingMessages) { NS_ENSURE_ARG_POINTER(aCanGetIncomingMessages); *aCanGetIncomingMessages = true; return NS_OK; } NS_IMETHODIMP nsPop3Service::GetShowComposeMsgLink(bool *showComposeMsgLink) { NS_ENSURE_ARG_POINTER(showComposeMsgLink); *showComposeMsgLink = true; return NS_OK; } NS_IMETHODIMP nsPop3Service::GetFoldersCreatedAsync(bool *aAsyncCreation) { NS_ENSURE_ARG_POINTER(aAsyncCreation); *aAsyncCreation = false; return NS_OK; } NS_IMETHODIMP nsPop3Service::GetDefaultServerPort(bool isSecure, int32_t *aPort) { NS_ENSURE_ARG_POINTER(aPort); if (!isSecure) return GetDefaultPort(aPort); *aPort = nsIPop3URL::DEFAULT_POP3S_PORT; return NS_OK; } NS_IMETHODIMP nsPop3Service::NotifyDownloadStarted(nsIMsgFolder *aFolder) { nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener> >::ForwardIterator iter(mListeners); nsCOMPtr<nsIPop3ServiceListener> listener; while (iter.HasMore()) { listener = iter.GetNext(); listener->OnDownloadStarted(aFolder); } return NS_OK; } NS_IMETHODIMP nsPop3Service::NotifyDownloadProgress(nsIMsgFolder *aFolder, uint32_t aNumMessages, uint32_t aNumTotalMessages) { nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener> >::ForwardIterator iter(mListeners); nsCOMPtr<nsIPop3ServiceListener> listener; while (iter.HasMore()) { listener = iter.GetNext(); listener->OnDownloadProgress(aFolder, aNumMessages, aNumTotalMessages); } return NS_OK; } NS_IMETHODIMP nsPop3Service::NotifyDownloadCompleted(nsIMsgFolder *aFolder, uint32_t aNumMessages) { nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener> >::ForwardIterator iter(mListeners); nsCOMPtr<nsIPop3ServiceListener> listener; while (iter.HasMore()) { listener = iter.GetNext(); listener->OnDownloadCompleted(aFolder, aNumMessages); } return NS_OK; } NS_IMETHODIMP nsPop3Service::AddListener(nsIPop3ServiceListener *aListener) { NS_ENSURE_ARG_POINTER(aListener); mListeners.AppendElementUnlessExists(aListener); return NS_OK; } NS_IMETHODIMP nsPop3Service::RemoveListener(nsIPop3ServiceListener *aListener) { NS_ENSURE_ARG_POINTER(aListener); mListeners.RemoveElement(aListener); return NS_OK; }