summaryrefslogtreecommitdiffstats
path: root/mailnews/local/src/nsPop3Protocol.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/local/src/nsPop3Protocol.cpp')
-rw-r--r--mailnews/local/src/nsPop3Protocol.cpp4176
1 files changed, 4176 insertions, 0 deletions
diff --git a/mailnews/local/src/nsPop3Protocol.cpp b/mailnews/local/src/nsPop3Protocol.cpp
new file mode 100644
index 000000000..825f45ab3
--- /dev/null
+++ b/mailnews/local/src/nsPop3Protocol.cpp
@@ -0,0 +1,4176 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/.
+ *
+ * This Original Code has been modified by IBM Corporation. Modifications made by IBM
+ * described herein are Copyright (c) International Business Machines Corporation, 2000.
+ * Modifications to Mozilla code or documentation identified per MPL Section 3.3
+ *
+ * Jason Eager <jce2@po.cwru.edu>
+ *
+ * Date Modified by Description of modification
+ * 04/20/2000 IBM Corp. OS/2 VisualAge build.
+ * 06/07/2000 Jason Eager Added check for out of disk space
+ */
+
+#include "nscore.h"
+#include "msgCore.h" // precompiled header...
+#include "nsNetUtil.h"
+#include "nspr.h"
+#include "plbase64.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsISafeOutputStream.h"
+#include "nsPop3Protocol.h"
+#include "MailNewsTypes.h"
+#include "nsStringGlue.h"
+#include "nsIPrompt.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsIMsgPluggableStore.h"
+#include "nsTextFormatter.h"
+#include "nsCOMPtr.h"
+#include "nsIMsgWindow.h"
+#include "nsIMsgFolder.h" // TO include biffState enum. Change to bool later...
+#include "nsIMsgLocalMailFolder.h"
+#include "nsIDocShell.h"
+#include "nsMsgUtils.h"
+#include "nsISocketTransport.h"
+#include "nsISSLSocketControl.h"
+#include "nsILineInputStream.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsMsgMessageFlags.h"
+#include "nsMsgBaseCID.h"
+#include "nsIProxyInfo.h"
+#include "nsCRT.h"
+#include "mozilla/Services.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Attributes.h"
+
+using namespace mozilla;
+
+PRLogModuleInfo *POP3LOGMODULE = nullptr;
+#define POP3LOG(str) "%s: [this=%p] " str, POP3LOGMODULE->name, this
+
+static int
+net_pop3_remove_messages_marked_delete(PLHashEntry* he,
+ int msgindex,
+ void *arg)
+{
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ return (uidlEntry->status == DELETE_CHAR)
+ ? HT_ENUMERATE_REMOVE : HT_ENUMERATE_NEXT;
+}
+
+uint32_t TimeInSecondsFromPRTime(PRTime prTime)
+{
+ return (uint32_t)(prTime / PR_USEC_PER_SEC);
+}
+
+static void
+put_hash(PLHashTable* table, const char* key, char value, uint32_t dateReceived)
+{
+ // don't put not used slots or empty uid into hash
+ if (key && *key)
+ {
+ Pop3UidlEntry* tmp = PR_NEWZAP(Pop3UidlEntry);
+ if (tmp)
+ {
+ tmp->uidl = PL_strdup(key);
+ if (tmp->uidl)
+ {
+ tmp->dateReceived = dateReceived;
+ tmp->status = value;
+ PL_HashTableAdd(table, (const void *)tmp->uidl, (void*) tmp);
+ }
+ else
+ PR_Free(tmp);
+ }
+ }
+}
+
+static int
+net_pop3_copy_hash_entries(PLHashEntry* he, int msgindex, void *arg)
+{
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ put_hash((PLHashTable *) arg, uidlEntry->uidl, uidlEntry->status, uidlEntry->dateReceived);
+ return HT_ENUMERATE_NEXT;
+}
+
+static void *
+AllocUidlTable(void * /* pool */, size_t size)
+{
+ return PR_MALLOC(size);
+}
+
+static void
+FreeUidlTable(void * /* pool */, void *item)
+{
+ PR_Free(item);
+}
+
+static PLHashEntry *
+AllocUidlInfo(void *pool, const void *key)
+{
+ return PR_NEWZAP(PLHashEntry);
+}
+
+static void
+FreeUidlInfo(void * /* pool */, PLHashEntry *he, unsigned flag)
+{
+ if (flag == HT_FREE_ENTRY)
+ {
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ if (uidlEntry)
+ {
+ PR_Free(uidlEntry->uidl);
+ PR_Free(uidlEntry);
+ }
+ PR_Free(he);
+ }
+}
+
+static PLHashAllocOps gHashAllocOps = {
+ AllocUidlTable, FreeUidlTable,
+ AllocUidlInfo, FreeUidlInfo
+};
+
+
+static Pop3UidlHost*
+net_pop3_load_state(const char* searchhost,
+ const char* searchuser,
+ nsIFile *mailDirectory)
+{
+ Pop3UidlHost* result = nullptr;
+ Pop3UidlHost* current = nullptr;
+ Pop3UidlHost* tmp;
+
+ result = PR_NEWZAP(Pop3UidlHost);
+ if (!result)
+ return nullptr;
+ result->host = PL_strdup(searchhost);
+ result->user = PL_strdup(searchuser);
+ result->hash = PL_NewHashTable(20, PL_HashString, PL_CompareStrings, PL_CompareValues, &gHashAllocOps, nullptr);
+
+ if (!result->host || !result->user || !result->hash)
+ {
+ PR_Free(result->host);
+ PR_Free(result->user);
+ if (result->hash)
+ PL_HashTableDestroy(result->hash);
+ PR_Free(result);
+ return nullptr;
+ }
+
+ nsCOMPtr <nsIFile> popState;
+ mailDirectory->Clone(getter_AddRefs(popState));
+ if (!popState)
+ return nullptr;
+ popState->AppendNative(NS_LITERAL_CSTRING("popstate.dat"));
+
+ nsCOMPtr<nsIInputStream> fileStream;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), popState);
+ NS_ENSURE_SUCCESS(rv, result);
+
+ nsCOMPtr<nsILineInputStream> lineInputStream(do_QueryInterface(fileStream, &rv));
+ NS_ENSURE_SUCCESS(rv, result);
+
+ bool more = true;
+ nsCString line;
+
+ while (more && NS_SUCCEEDED(rv))
+ {
+ lineInputStream->ReadLine(line, &more);
+ if (line.IsEmpty())
+ continue;
+ char firstChar = line.CharAt(0);
+ if (firstChar == '#')
+ continue;
+ if (firstChar == '*') {
+ /* It's a host&user line. */
+ current = nullptr;
+ char *lineBuf = line.BeginWriting() + 1; // ok because we know the line isn't empty
+ char *host = NS_strtok(" \t\r\n", &lineBuf);
+ /* without space to also get realnames - see bug 225332 */
+ char *user = NS_strtok("\t\r\n", &lineBuf);
+ if (!host || !user)
+ continue;
+ for (tmp = result ; tmp ; tmp = tmp->next)
+ {
+ if (!strcmp(host, tmp->host) && !strcmp(user, tmp->user))
+ {
+ current = tmp;
+ break;
+ }
+ }
+ if (!current)
+ {
+ current = PR_NEWZAP(Pop3UidlHost);
+ if (current)
+ {
+ current->host = strdup(host);
+ current->user = strdup(user);
+ current->hash = PL_NewHashTable(20, PL_HashString, PL_CompareStrings, PL_CompareValues, &gHashAllocOps, nullptr);
+ if (!current->host || !current->user || !current->hash)
+ {
+ PR_Free(current->host);
+ PR_Free(current->user);
+ if (current->hash)
+ PL_HashTableDestroy(current->hash);
+ PR_Free(current);
+ }
+ else
+ {
+ current->next = result->next;
+ result->next = current;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* It's a line with a UIDL on it. */
+ if (current)
+ {
+ for (int32_t pos = line.FindChar('\t'); pos != -1; pos = line.FindChar('\t', pos))
+ line.Replace(pos, 1, ' ');
+
+ nsTArray<nsCString> lineElems;
+ ParseString(line, ' ', lineElems);
+ if (lineElems.Length() < 2)
+ continue;
+ nsCString *flags = &lineElems[0];
+ nsCString *uidl = &lineElems[1];
+ uint32_t dateReceived = TimeInSecondsFromPRTime(PR_Now()); // if we don't find a date str, assume now.
+ if (lineElems.Length() > 2)
+ dateReceived = atoi(lineElems[2].get());
+ if (!flags->IsEmpty() && !uidl->IsEmpty())
+ {
+ char flag = flags->CharAt(0);
+ if ((flag == KEEP) || (flag == DELETE_CHAR) ||
+ (flag == TOO_BIG) || (flag == FETCH_BODY))
+ {
+ put_hash(current->hash, uidl->get(), flag, dateReceived);
+ }
+ else
+ {
+ NS_ASSERTION(false, "invalid flag in popstate.dat");
+ }
+ }
+ }
+ }
+ }
+ fileStream->Close();
+
+ return result;
+}
+
+static int
+hash_clear_mapper(PLHashEntry* he, int msgindex, void* arg)
+{
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ PR_Free(uidlEntry->uidl);
+ PR_Free(uidlEntry);
+ he->value = nullptr;
+
+ return HT_ENUMERATE_REMOVE;
+}
+
+static int
+hash_empty_mapper(PLHashEntry* he, int msgindex, void* arg)
+{
+ *((bool*) arg) = false;
+ return HT_ENUMERATE_STOP;
+}
+
+static bool
+hash_empty(PLHashTable* hash)
+{
+ bool result = true;
+ PL_HashTableEnumerateEntries(hash, hash_empty_mapper, (void *)&result);
+ return result;
+}
+
+
+static int
+net_pop3_write_mapper(PLHashEntry* he, int msgindex, void* arg)
+{
+ nsIOutputStream* file = (nsIOutputStream*) arg;
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ NS_ASSERTION((uidlEntry->status == KEEP) ||
+ (uidlEntry->status == DELETE_CHAR) ||
+ (uidlEntry->status == FETCH_BODY) ||
+ (uidlEntry->status == TOO_BIG), "invalid status");
+ char* tmpBuffer = PR_smprintf("%c %s %d" MSG_LINEBREAK, uidlEntry->status, (char*)
+ uidlEntry->uidl, uidlEntry->dateReceived);
+ PR_ASSERT(tmpBuffer);
+ uint32_t numBytesWritten;
+ file->Write(tmpBuffer, strlen(tmpBuffer), &numBytesWritten);
+ PR_Free(tmpBuffer);
+ return HT_ENUMERATE_NEXT;
+}
+
+static int
+net_pop3_delete_old_msgs_mapper(PLHashEntry* he, int msgindex, void* arg)
+{
+ PRTime cutOffDate = (PRTime) arg;
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ if (uidlEntry->dateReceived < cutOffDate)
+ uidlEntry->status = DELETE_CHAR; // mark for deletion
+ return HT_ENUMERATE_NEXT;
+}
+
+static void
+net_pop3_write_state(Pop3UidlHost* host, nsIFile *mailDirectory)
+{
+ int32_t len = 0;
+ nsCOMPtr <nsIFile> popState;
+
+ mailDirectory->Clone(getter_AddRefs(popState));
+ if (!popState)
+ return;
+ popState->AppendNative(NS_LITERAL_CSTRING("popstate.dat"));
+
+ nsCOMPtr<nsIOutputStream> fileOutputStream;
+ nsresult rv = MsgNewSafeBufferedFileOutputStream(getter_AddRefs(fileOutputStream), popState, -1, 00600);
+ if (NS_FAILED(rv))
+ return;
+
+ const char tmpBuffer[] =
+ "# POP3 State File" MSG_LINEBREAK
+ "# This is a generated file! Do not edit." MSG_LINEBREAK
+ MSG_LINEBREAK;
+
+ uint32_t numBytesWritten;
+ fileOutputStream->Write(tmpBuffer, strlen(tmpBuffer), &numBytesWritten);
+
+ for (; host && (len >= 0); host = host->next)
+ {
+ if (!hash_empty(host->hash))
+ {
+ fileOutputStream->Write("*", 1, &numBytesWritten);
+ fileOutputStream->Write(host->host, strlen(host->host), &numBytesWritten);
+ fileOutputStream->Write(" ", 1, &numBytesWritten);
+ fileOutputStream->Write(host->user, strlen(host->user), &numBytesWritten);
+ fileOutputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &numBytesWritten);
+ PL_HashTableEnumerateEntries(host->hash, net_pop3_write_mapper, (void *)fileOutputStream);
+ }
+ }
+ nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(fileOutputStream);
+ NS_ASSERTION(safeStream, "expected a safe output stream!");
+ if (safeStream) {
+ rv = safeStream->Finish();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("failed to save pop state! possible data loss");
+ }
+ }
+}
+
+static void
+net_pop3_free_state(Pop3UidlHost* host)
+{
+ Pop3UidlHost* h;
+ while (host)
+ {
+ h = host->next;
+ PR_Free(host->host);
+ PR_Free(host->user);
+ PL_HashTableDestroy(host->hash);
+ PR_Free(host);
+ host = h;
+ }
+}
+
+/*
+Look for a specific UIDL string in our hash tables, if we have it then we need
+to mark the message for deletion so that it can be deleted later. If the uidl of the
+message is not found, then the message was downloaded completely and already deleted
+from the server. So this only applies to messages kept on the server or too big
+for download. */
+/* static */
+void nsPop3Protocol::MarkMsgInHashTable(PLHashTable *hashTable, const Pop3UidlEntry *uidlE, bool *changed)
+{
+ if (uidlE->uidl)
+ {
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(hashTable, uidlE->uidl);
+ if (uidlEntry)
+ {
+ if (uidlEntry->status != uidlE->status)
+ {
+ uidlEntry->status = uidlE->status;
+ *changed = true;
+ }
+ }
+ }
+}
+
+/* static */
+nsresult
+nsPop3Protocol::MarkMsgForHost(const char *hostName, const char *userName,
+ nsIFile *mailDirectory,
+ nsTArray<Pop3UidlEntry*> &UIDLArray)
+{
+ if (!hostName || !userName || !mailDirectory)
+ return NS_ERROR_NULL_POINTER;
+
+ Pop3UidlHost *uidlHost = net_pop3_load_state(hostName, userName, mailDirectory);
+ if (!uidlHost)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ bool changed = false;
+
+ uint32_t count = UIDLArray.Length();
+ for (uint32_t i = 0; i < count; i++)
+ {
+ MarkMsgInHashTable(uidlHost->hash, UIDLArray[i], &changed);
+ }
+
+ if (changed)
+ net_pop3_write_state(uidlHost, mailDirectory);
+ net_pop3_free_state(uidlHost);
+ return NS_OK;
+}
+
+
+
+NS_IMPL_ADDREF_INHERITED(nsPop3Protocol, nsMsgProtocol)
+NS_IMPL_RELEASE_INHERITED(nsPop3Protocol, nsMsgProtocol)
+
+
+
+NS_INTERFACE_MAP_BEGIN(nsPop3Protocol)
+ NS_INTERFACE_MAP_ENTRY(nsIPop3Protocol)
+ NS_INTERFACE_MAP_ENTRY(nsIMsgAsyncPromptListener)
+NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol)
+
+// nsPop3Protocol class implementation
+
+nsPop3Protocol::nsPop3Protocol(nsIURI* aURL)
+: nsMsgProtocol(aURL),
+ m_bytesInMsgReceived(0),
+ m_totalFolderSize(0),
+ m_totalDownloadSize(0),
+ m_totalBytesReceived(0),
+ m_lineStreamBuffer(nullptr),
+ m_pop3ConData(nullptr)
+{
+}
+
+nsresult nsPop3Protocol::Initialize(nsIURI * aURL)
+{
+ nsresult rv = NS_OK;
+ if (!POP3LOGMODULE)
+ POP3LOGMODULE = PR_NewLogModule("POP3");
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Initialize()")));
+
+ m_pop3ConData = (Pop3ConData *)PR_NEWZAP(Pop3ConData);
+ if(!m_pop3ConData)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ m_totalBytesReceived = 0;
+ m_bytesInMsgReceived = 0;
+ m_totalFolderSize = 0;
+ m_totalDownloadSize = 0;
+ m_totalBytesReceived = 0;
+ m_tlsEnabled = false;
+ m_socketType = nsMsgSocketType::trySTARTTLS;
+ m_prefAuthMethods = POP3_AUTH_MECH_UNDEFINED;
+ m_failedAuthMethods = 0;
+ m_password_already_sent = false;
+ m_currentAuthMethod = POP3_AUTH_MECH_UNDEFINED;
+ m_needToRerunUrl = false;
+
+ if (aURL)
+ {
+ // extract out message feedback if there is any.
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(aURL);
+ if (mailnewsUrl)
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ mailnewsUrl->GetServer(getter_AddRefs(server));
+ NS_ENSURE_TRUE(server, NS_MSG_INVALID_OR_MISSING_SERVER);
+
+ rv = server->GetSocketType(&m_socketType);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ int32_t authMethod = 0;
+ rv = server->GetAuthMethod(&authMethod);
+ NS_ENSURE_SUCCESS(rv,rv);
+ InitPrefAuthMethods(authMethod);
+
+ m_pop3Server = do_QueryInterface(server);
+ if (m_pop3Server)
+ m_pop3Server->GetPop3CapabilityFlags(&m_pop3ConData->capability_flags);
+ }
+
+ m_url = do_QueryInterface(aURL);
+
+ // When we are making a secure connection, we need to make sure that we
+ // pass an interface requestor down to the socket transport so that PSM can
+ // retrieve a nsIPrompt instance if needed.
+ nsCOMPtr<nsIInterfaceRequestor> ir;
+ if (m_socketType != nsMsgSocketType::plain)
+ {
+ nsCOMPtr<nsIMsgWindow> msgwin;
+ mailnewsUrl->GetMsgWindow(getter_AddRefs(msgwin));
+ if (!msgwin)
+ GetTopmostMsgWindow(getter_AddRefs(msgwin));
+ if (msgwin)
+ {
+ nsCOMPtr<nsIDocShell> docshell;
+ msgwin->GetRootDocShell(getter_AddRefs(docshell));
+ ir = do_QueryInterface(docshell);
+ nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
+ msgwin->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
+ if (notificationCallbacks)
+ {
+ nsCOMPtr<nsIInterfaceRequestor> aggregrateIR;
+ MsgNewInterfaceRequestorAggregation(notificationCallbacks, ir, getter_AddRefs(aggregrateIR));
+ ir = aggregrateIR;
+ }
+ }
+ }
+
+ int32_t port = 0;
+ nsCString hostName;
+ aURL->GetPort(&port);
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server)
+ server->GetRealHostName(hostName);
+
+ nsCOMPtr<nsIProxyInfo> proxyInfo;
+ rv = MsgExamineForProxy(this, getter_AddRefs(proxyInfo));
+ if (NS_FAILED(rv)) proxyInfo = nullptr;
+
+ const char *connectionType = nullptr;
+ if (m_socketType == nsMsgSocketType::SSL)
+ connectionType = "ssl";
+ else if (m_socketType == nsMsgSocketType::trySTARTTLS ||
+ m_socketType == nsMsgSocketType::alwaysSTARTTLS)
+ connectionType = "starttls";
+
+ rv = OpenNetworkSocketWithInfo(hostName.get(), port, connectionType, proxyInfo, ir);
+ if (NS_FAILED(rv) && m_socketType == nsMsgSocketType::trySTARTTLS)
+ {
+ m_socketType = nsMsgSocketType::plain;
+ rv = OpenNetworkSocketWithInfo(hostName.get(), port, nullptr, proxyInfo, ir);
+ }
+
+ if(NS_FAILED(rv))
+ return rv;
+ } // if we got a url...
+
+ m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, true);
+ if(!m_lineStreamBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ return bundleService->CreateBundle("chrome://messenger/locale/localMsgs.properties", getter_AddRefs(mLocalBundle));
+}
+
+nsPop3Protocol::~nsPop3Protocol()
+{
+ Cleanup();
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("~nsPop3Protocol()")));
+}
+
+void nsPop3Protocol::Cleanup()
+{
+ if (m_pop3ConData->newuidl)
+ {
+ PL_HashTableDestroy(m_pop3ConData->newuidl);
+ m_pop3ConData->newuidl = nullptr;
+ }
+
+ net_pop3_free_state(m_pop3ConData->uidlinfo);
+
+ FreeMsgInfo();
+ PR_Free(m_pop3ConData->only_uidl);
+ PR_Free(m_pop3ConData);
+
+ delete m_lineStreamBuffer;
+ m_lineStreamBuffer = nullptr;
+}
+
+void nsPop3Protocol::SetCapFlag(uint32_t flag)
+{
+ m_pop3ConData->capability_flags |= flag;
+}
+
+void nsPop3Protocol::ClearCapFlag(uint32_t flag)
+{
+ m_pop3ConData->capability_flags &= ~flag;
+}
+
+bool nsPop3Protocol::TestCapFlag(uint32_t flag)
+{
+ return m_pop3ConData->capability_flags & flag;
+}
+
+uint32_t nsPop3Protocol::GetCapFlags()
+{
+ return m_pop3ConData->capability_flags;
+}
+
+nsresult nsPop3Protocol::FormatCounterString(const nsString &stringName,
+ uint32_t count1,
+ uint32_t count2,
+ nsString &resultString)
+{
+ nsAutoString count1String;
+ count1String.AppendInt(count1);
+
+ nsAutoString count2String;
+ count2String.AppendInt(count2);
+
+ const char16_t *formatStrings[] = {
+ count1String.get(),
+ count2String.get()
+ };
+
+ return mLocalBundle->FormatStringFromName(stringName.get(),
+ formatStrings, 2,
+ getter_Copies(resultString));
+}
+
+void nsPop3Protocol::UpdateStatus(const char16_t *aStatusName)
+{
+ nsString statusMessage;
+ mLocalBundle->GetStringFromName(aStatusName,
+ getter_Copies(statusMessage));
+ UpdateStatusWithString(statusMessage.get());
+}
+
+void nsPop3Protocol::UpdateStatusWithString(const char16_t *aStatusString)
+{
+ if (mProgressEventSink)
+ {
+ mozilla::DebugOnly<nsresult> rv =
+ mProgressEventSink->OnStatus(this, m_channelContext,
+ NS_OK, aStatusString); // XXX i18n message
+ NS_ASSERTION(NS_SUCCEEDED(rv), "dropping error result");
+ }
+}
+
+void nsPop3Protocol::UpdateProgressPercent(int64_t totalDone, int64_t total)
+{
+ if (mProgressEventSink)
+ mProgressEventSink->OnProgress(this, m_channelContext, totalDone, total);
+}
+
+// note: SetUsername() expects an unescaped string
+// do not pass in an escaped string
+void nsPop3Protocol::SetUsername(const char* name)
+{
+ NS_ASSERTION(name, "no name specified!");
+ if (name)
+ m_username = name;
+}
+
+nsresult nsPop3Protocol::RerunUrl()
+{
+ nsCOMPtr<nsIURI> url = do_QueryInterface(m_url);
+ ClearFlag(POP3_PASSWORD_FAILED);
+ m_pop3Server->SetRunningProtocol(nullptr);
+ Cleanup();
+ return LoadUrl(url, nullptr);
+}
+
+Pop3StatesEnum nsPop3Protocol::GetNextPasswordObtainState()
+{
+ switch (m_pop3ConData->next_state)
+ {
+ case POP3_OBTAIN_PASSWORD_EARLY:
+ return POP3_FINISH_OBTAIN_PASSWORD_EARLY;
+ case POP3_SEND_USERNAME:
+ case POP3_OBTAIN_PASSWORD_BEFORE_USERNAME:
+ return POP3_FINISH_OBTAIN_PASSWORD_BEFORE_USERNAME;
+ case POP3_SEND_PASSWORD:
+ case POP3_OBTAIN_PASSWORD_BEFORE_PASSWORD:
+ return POP3_FINISH_OBTAIN_PASSWORD_BEFORE_PASSWORD;
+ default:
+ // Should never get here.
+ NS_NOTREACHED("Invalid next_state in GetNextPasswordObtainState");
+ }
+ return POP3_ERROR_DONE;
+}
+
+nsresult nsPop3Protocol::StartGetAsyncPassword(Pop3StatesEnum aNextState)
+{
+ nsresult rv;
+
+ // Try and avoid going async if possible - if we haven't got into a password
+ // failure state and the server has a password stored for this session, then
+ // use it.
+ if (!TestFlag(POP3_PASSWORD_FAILED))
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server =
+ do_QueryInterface(m_pop3Server, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = server->GetPassword(m_passwordResult);
+ if (NS_SUCCEEDED(rv) && !m_passwordResult.IsEmpty())
+ {
+ m_pop3ConData->next_state = GetNextPasswordObtainState();
+ return NS_OK;
+ }
+ }
+
+ // We're now going to need to do something that will end up with us either
+ // poking the login manger or prompting the user. We need to ensure we only
+ // do one prompt at a time (and loging manager could cause a master password
+ // prompt), so we need to use the async prompter.
+ nsCOMPtr<nsIMsgAsyncPrompter> asyncPrompter =
+ do_GetService(NS_MSGASYNCPROMPTER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ m_pop3ConData->next_state = aNextState;
+
+ // Although we're not actually pausing for a read, we'll do so anyway to let
+ // the async prompt run. Once it is our turn again we'll call back into
+ // ProcessProtocolState.
+ m_pop3ConData->pause_for_read = true;
+
+ nsCString server("unknown");
+ m_url->GetPrePath(server);
+
+ rv = asyncPrompter->QueueAsyncAuthPrompt(server, false, this);
+ // Explict NS_ENSURE_SUCCESS for debug purposes as errors tend to get
+ // hidden.
+ NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
+}
+
+NS_IMETHODIMP nsPop3Protocol::OnPromptStart(bool *aResult)
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("OnPromptStart()")));
+
+ *aResult = false;
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString passwordResult;
+
+ // pass the failed password into the password prompt so that
+ // it will be pre-filled, in case it failed because of a
+ // server problem and not because it was wrong.
+ if (!m_lastPasswordSent.IsEmpty())
+ passwordResult = m_lastPasswordSent;
+
+ // Set up some items that we're going to need for the prompting.
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ if (mailnewsUrl)
+ mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
+
+ nsCString userName;
+ server->GetRealUsername(userName);
+
+ nsCString hostName;
+ server->GetRealHostName(hostName);
+
+ nsString passwordPrompt;
+ NS_ConvertUTF8toUTF16 userNameUTF16(userName);
+ NS_ConvertUTF8toUTF16 hostNameUTF16(hostName);
+ const char16_t* passwordParams[] = { userNameUTF16.get(),
+ hostNameUTF16.get() };
+
+ // if the last prompt got us a bad password then show a special dialog
+ if (TestFlag(POP3_PASSWORD_FAILED))
+ {
+ // Biff case (no msgWindow) shouldn't cause prompts or passwords to get forgotten at all
+ // TODO shouldn't we skip the new password prompt below as well for biff? Just exit here?
+ if (msgWindow)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Warning,
+ (POP3LOG("POP: ask user what to do (after password failed): new password, retry or cancel")));
+
+ int32_t buttonPressed = 0;
+ if (NS_SUCCEEDED(MsgPromptLoginFailed(msgWindow, hostName,
+ &buttonPressed)))
+ {
+ if (buttonPressed == 1) // Cancel button
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Warning, (POP3LOG("cancel button pressed")));
+ // Abort quickly and stop trying for now.
+
+ // If we haven't actually connected yet (i.e. we're doing an early
+ // attempt to get the username/password but we've previously failed
+ // for some reason), then skip straight to POP3_FREE as it isn't an
+ // error in this connection, and just ends up with us closing the
+ // socket and saying we've aborted the bind. Otherwise, pretend this
+ // is an error and move on.
+ m_pop3ConData->next_state =
+ m_pop3ConData->next_state == POP3_OBTAIN_PASSWORD_EARLY ?
+ POP3_FREE : POP3_ERROR_DONE;
+
+ // Clear the password we're going to return to force failure in
+ // the get mail instance.
+ passwordResult.Truncate();
+
+ // We also have to clear the password failed flag, otherwise we'll
+ // automatically try again.
+ ClearFlag(POP3_PASSWORD_FAILED);
+
+ // As we're async, calling ProcessProtocolState gets things going
+ // again.
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ return NS_OK;
+ }
+ else if (buttonPressed == 2) // "New password" button
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Warning, (POP3LOG("new password button pressed")));
+ // Forget the stored password
+ // and we'll prompt for a new one next time around.
+ rv = server->ForgetPassword();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // try all methods again with new password
+ ResetAuthMethods();
+ // ... apart from GSSAPI, which doesn't care about passwords
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_GSSAPI);
+ if (m_needToRerunUrl)
+ return RerunUrl();
+ }
+ else if (buttonPressed == 0) // "Retry" button
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Warning, (POP3LOG("retry button pressed")));
+ // try all methods again, including GSSAPI
+ ResetAuthMethods();
+ ClearFlag(POP3_PASSWORD_FAILED|POP3_AUTH_FAILURE);
+
+ if (m_needToRerunUrl)
+ return RerunUrl();
+
+ // It is a bit strange that we're going onto the next state that
+ // would essentially send the password. However in resetting the
+ // auth methods above, we're setting up SendUsername, SendPassword
+ // and friends to abort and return to the POP3_SEND_CAPA state.
+ // Hence we can do this safely.
+ m_pop3ConData->next_state = GetNextPasswordObtainState();
+ // As we're async, calling ProcessProtocolState gets things going
+ // again.
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ return NS_OK;
+ }
+ }
+ }
+ mLocalBundle->FormatStringFromName(
+ u"pop3PreviouslyEnteredPasswordIsInvalidPrompt",
+ passwordParams, 2, getter_Copies(passwordPrompt));
+ }
+ else
+ // Otherwise this is the first time we've asked about the server's
+ // password so show a first time prompt.
+ mLocalBundle->FormatStringFromName(
+ u"pop3EnterPasswordPrompt",
+ passwordParams, 2, getter_Copies(passwordPrompt));
+
+ nsString passwordTitle;
+ mLocalBundle->GetStringFromName(
+ u"pop3EnterPasswordPromptTitle",
+ getter_Copies(passwordTitle));
+
+ // Now go and get the password.
+ if (!passwordPrompt.IsEmpty() && !passwordTitle.IsEmpty())
+ rv = server->GetPasswordWithUI(passwordPrompt, passwordTitle,
+ msgWindow, passwordResult);
+ ClearFlag(POP3_PASSWORD_FAILED|POP3_AUTH_FAILURE);
+
+ // If it failed or the user cancelled the prompt, just abort the
+ // connection.
+ if (NS_FAILED(rv) ||
+ rv == NS_MSG_PASSWORD_PROMPT_CANCELLED)
+ {
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ m_passwordResult.Truncate();
+ *aResult = false;
+ }
+ else
+ {
+ m_passwordResult = passwordResult;
+ m_pop3ConData->next_state = GetNextPasswordObtainState();
+ *aResult = true;
+ }
+ // Because this was done asynchronously, now call back into
+ // ProcessProtocolState to get the protocol going again.
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Protocol::OnPromptAuthAvailable()
+{
+ NS_NOTREACHED("Did not expect to get POP3 protocol queuing up auth "
+ "connections for same server");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Protocol::OnPromptCanceled()
+{
+ // A prompt was cancelled, so just abort out the connection
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ // As we're async, calling ProcessProtocolState gets things going again.
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Protocol::OnTransportStatus(nsITransport *aTransport, nsresult aStatus, int64_t aProgress, int64_t aProgressMax)
+{
+ return nsMsgProtocol::OnTransportStatus(aTransport, aStatus, aProgress, aProgressMax);
+}
+
+// stop binding is a "notification" informing us that the stream associated with aURL is going away.
+NS_IMETHODIMP nsPop3Protocol::OnStopRequest(nsIRequest *aRequest, nsISupports * aContext, nsresult aStatus)
+{
+ // If the server dropped the connection, m_socketIsOpen will be true, before
+ // we call nsMsgProtocol::OnStopRequest. The call will force a close socket,
+ // but we still want to go through the state machine one more time to cleanup
+ // the protocol object.
+ if (m_socketIsOpen)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> msgUrl = do_QueryInterface(m_url);
+
+ // Check if the connection was dropped before getting back an auth error.
+ // If we got the auth error, the next state would be
+ // POP3_OBTAIN_PASSWORD_EARLY.
+ if ((m_pop3ConData->next_state_after_response == POP3_NEXT_AUTH_STEP ||
+ m_pop3ConData->next_state_after_response == POP3_AUTH_LOGIN_RESPONSE) &&
+ m_pop3ConData->next_state != POP3_OBTAIN_PASSWORD_EARLY)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("dropped connection before auth error")));
+ SetFlag(POP3_AUTH_FAILURE);
+ m_pop3ConData->command_succeeded = false;
+ m_needToRerunUrl = true;
+ m_pop3ConData->next_state = POP3_NEXT_AUTH_STEP;
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ }
+ // We can't call nsMsgProtocol::OnStopRequest because it calls SetUrlState,
+ // which notifies the URLListeners, but we need to do a bit of cleanup
+ // before running the url again.
+ CloseSocket();
+ if (m_loadGroup)
+ m_loadGroup->RemoveRequest(static_cast<nsIRequest *>(this), nullptr, aStatus);
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+
+ if (NS_FAILED(aStatus) && aStatus != NS_BINDING_ABORTED)
+ nsMsgProtocol::ShowAlertMessage(msgUrl, aStatus);
+
+ return NS_OK;
+ }
+ nsresult rv = nsMsgProtocol::OnStopRequest(aRequest, aContext, aStatus);
+
+ // turn off the server busy flag on stop request - we know we're done, right?
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("Clearing server busy in nsPop3Protocol::OnStopRequest")));
+ server->SetServerBusy(false); // the server is not busy
+ }
+ if(m_pop3ConData->list_done)
+ CommitState(true);
+ if (NS_FAILED(aStatus) && aStatus != NS_BINDING_ABORTED)
+ Abort();
+ return rv;
+}
+
+void nsPop3Protocol::Abort()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Abort")));
+
+ if(m_pop3ConData->msg_closure)
+ {
+ m_nsIPop3Sink->IncorporateAbort(m_pop3ConData->only_uidl != nullptr);
+ m_pop3ConData->msg_closure = nullptr;
+ }
+ // need this to close the stream on the inbox.
+ m_nsIPop3Sink->AbortMailDelivery(this);
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("Clearing running protocol in nsPop3Protocol::Abort()")));
+ m_pop3Server->SetRunningProtocol(nullptr);
+}
+
+NS_IMETHODIMP nsPop3Protocol::Cancel(nsresult status) // handle stop button
+{
+ Abort();
+ return nsMsgProtocol::Cancel(NS_BINDING_ABORTED);
+}
+
+
+nsresult nsPop3Protocol::LoadUrl(nsIURI* aURL, nsISupports * /* aConsumer */)
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("LoadUrl()")));
+
+ nsresult rv = Initialize(aURL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (aURL)
+ m_url = do_QueryInterface(aURL);
+ else
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIURL> url = do_QueryInterface(aURL, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ int32_t port;
+ rv = url->GetPort(&port);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_CheckPortSafety(port, "pop");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString queryPart;
+ rv = url->GetQuery(queryPart);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get the url spect");
+
+ m_pop3ConData->only_check_for_new_mail = (PL_strcasestr(queryPart.get(), "check") != nullptr);
+ m_pop3ConData->verify_logon = (PL_strcasestr(queryPart.get(), "verifyLogon") != nullptr);
+ m_pop3ConData->get_url = (PL_strcasestr(queryPart.get(), "gurl") != nullptr);
+
+ bool deleteByAgeFromServer = false;
+ int32_t numDaysToLeaveOnServer = -1;
+ if (!m_pop3ConData->verify_logon)
+ {
+ // Pick up pref setting regarding leave messages on server, message size limit
+
+ m_pop3Server->GetLeaveMessagesOnServer(&m_pop3ConData->leave_on_server);
+ m_pop3Server->GetHeadersOnly(&m_pop3ConData->headers_only);
+ bool limitMessageSize = false;
+
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server)
+ {
+ // size limits are superseded by headers_only mode
+ if (!m_pop3ConData->headers_only)
+ {
+ server->GetLimitOfflineMessageSize(&limitMessageSize);
+ if (limitMessageSize)
+ {
+ int32_t max_size = 0; // default size
+ server->GetMaxMessageSize(&max_size);
+ m_pop3ConData->size_limit = (max_size) ? max_size * 1024 : 50 * 1024;
+ }
+ }
+ m_pop3Server->GetDeleteByAgeFromServer(&deleteByAgeFromServer);
+ if (deleteByAgeFromServer)
+ m_pop3Server->GetNumDaysToLeaveOnServer(&numDaysToLeaveOnServer);
+ }
+ }
+
+ // UIDL stuff
+ nsCOMPtr<nsIPop3URL> pop3Url = do_QueryInterface(m_url);
+ if (pop3Url)
+ pop3Url->GetPop3Sink(getter_AddRefs(m_nsIPop3Sink));
+
+ nsCOMPtr<nsIFile> mailDirectory;
+
+ nsCString hostName;
+ nsCString userName;
+
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server)
+ {
+ rv = server->GetLocalPath(getter_AddRefs(mailDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+ server->SetServerBusy(true); // the server is now busy
+ server->GetHostName(hostName);
+ server->GetUsername(userName);
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
+ (POP3LOG("Connecting to server %s:%d"), hostName.get(), port));
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("Setting server busy in nsPop3Protocol::LoadUrl()")));
+ }
+
+ if (!m_pop3ConData->verify_logon)
+ m_pop3ConData->uidlinfo = net_pop3_load_state(hostName.get(), userName.get(), mailDirectory);
+
+ m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_NoMail;
+
+ if (m_pop3ConData->uidlinfo && numDaysToLeaveOnServer > 0)
+ {
+ uint32_t nowInSeconds = TimeInSecondsFromPRTime(PR_Now());
+ uint32_t cutOffDay = nowInSeconds - (60 * 60 * 24 * numDaysToLeaveOnServer);
+
+ PL_HashTableEnumerateEntries(m_pop3ConData->uidlinfo->hash, net_pop3_delete_old_msgs_mapper, (void *)(uintptr_t) cutOffDay);
+ }
+ const char* uidl = PL_strcasestr(queryPart.get(), "uidl=");
+ PR_FREEIF(m_pop3ConData->only_uidl);
+
+ if (uidl)
+ {
+ uidl += 5;
+ nsCString unescapedData;
+ MsgUnescapeString(nsDependentCString(uidl), 0, unescapedData);
+ m_pop3ConData->only_uidl = PL_strdup(unescapedData.get());
+
+ mSuppressListenerNotifications = true; // suppress on start and on stop because this url won't have any content to display
+ }
+
+ m_pop3ConData->next_state = POP3_START_CONNECT;
+ m_pop3ConData->next_state_after_response = POP3_FINISH_CONNECT;
+ if (NS_SUCCEEDED(rv))
+ {
+ m_pop3Server->SetRunningProtocol(this);
+ return nsMsgProtocol::LoadUrl(aURL);
+ }
+ else
+ return rv;
+}
+
+void
+nsPop3Protocol::FreeMsgInfo()
+{
+ int i;
+ if (m_pop3ConData->msg_info)
+ {
+ for (i=0 ; i<m_pop3ConData->number_of_messages ; i++)
+ {
+ if (m_pop3ConData->msg_info[i].uidl)
+ PR_Free(m_pop3ConData->msg_info[i].uidl);
+ m_pop3ConData->msg_info[i].uidl = nullptr;
+ }
+ PR_Free(m_pop3ConData->msg_info);
+ m_pop3ConData->msg_info = nullptr;
+ }
+}
+
+int32_t
+nsPop3Protocol::WaitForStartOfConnectionResponse(nsIInputStream* aInputStream,
+ uint32_t length)
+{
+ char * line = nullptr;
+ uint32_t line_length = 0;
+ bool pauseForMoreData = false;
+ nsresult rv;
+ line = m_lineStreamBuffer->ReadNextLine(aInputStream, line_length, pauseForMoreData, &rv);
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+ if (NS_FAILED(rv))
+ return -1;
+
+ if(pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true; /* pause */
+ PR_Free(line);
+ return(line_length);
+ }
+
+ if(*line == '+')
+ {
+ m_pop3ConData->command_succeeded = true;
+ if(PL_strlen(line) > 4)
+ m_commandResponse = line + 4;
+ else
+ m_commandResponse = line;
+
+ if (m_prefAuthMethods & POP3_HAS_AUTH_APOP)
+ {
+ if (NS_SUCCEEDED(GetApopTimestamp()))
+ SetCapFlag(POP3_HAS_AUTH_APOP);
+ }
+ else
+ ClearCapFlag(POP3_HAS_AUTH_APOP);
+
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ m_pop3ConData->pause_for_read = false; /* don't pause */
+ }
+
+ PR_Free(line);
+ return(1); /* everything ok */
+}
+
+int32_t
+nsPop3Protocol::WaitForResponse(nsIInputStream* inputStream, uint32_t length)
+{
+ char * line;
+ uint32_t ln = 0;
+ bool pauseForMoreData = false;
+ nsresult rv;
+ line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if(pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true; /* pause */
+
+ PR_Free(line);
+ return(ln);
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ if(*line == '+')
+ {
+ m_pop3ConData->command_succeeded = true;
+ if(PL_strlen(line) > 4)
+ {
+ if(!PL_strncasecmp(line, "+OK", 3))
+ m_commandResponse = line + 4;
+ else // challenge answer to AUTH CRAM-MD5 and LOGIN username/password
+ m_commandResponse = line + 2;
+ }
+ else
+ m_commandResponse = line;
+ }
+ else
+ {
+ m_pop3ConData->command_succeeded = false;
+ if(PL_strlen(line) > 5)
+ m_commandResponse = line + 5;
+ else
+ m_commandResponse = line;
+
+ // search for the response codes (RFC 2449, chapter 8 and RFC 3206)
+ if(TestCapFlag(POP3_HAS_RESP_CODES | POP3_HAS_AUTH_RESP_CODE))
+ {
+ // code for authentication failure due to the user's credentials
+ if(m_commandResponse.Find("[AUTH", true) >= 0)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("setting auth failure")));
+ SetFlag(POP3_AUTH_FAILURE);
+ }
+
+ // codes for failures due to other reasons
+ if(m_commandResponse.Find("[LOGIN-DELAY", true) >= 0 ||
+ m_commandResponse.Find("[IN-USE", true) >= 0 ||
+ m_commandResponse.Find("[SYS", true) >= 0)
+ SetFlag(POP3_STOPLOGIN);
+
+ // remove the codes from the response string presented to the user
+ int32_t i = m_commandResponse.FindChar(']');
+ if(i >= 0)
+ m_commandResponse.Cut(0, i + 2);
+ }
+ }
+
+ m_pop3ConData->next_state = m_pop3ConData->next_state_after_response;
+ m_pop3ConData->pause_for_read = false; /* don't pause */
+
+ PR_Free(line);
+ return(1); /* everything ok */
+}
+
+int32_t
+nsPop3Protocol::Error(const char* err_code,
+ const char16_t **params,
+ uint32_t length)
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("ERROR: %s"), err_code));
+
+ // the error code is just the resource name for the error string...
+ // so print out that error message!
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ nsString accountName;
+ nsresult rv = server->GetPrettyName(accountName);
+ NS_ENSURE_SUCCESS(rv, -1);
+ const char16_t *titleParams[] = { accountName.get() };
+ nsString dialogTitle;
+ mLocalBundle->FormatStringFromName(
+ u"pop3ErrorDialogTitle",
+ titleParams, 1, getter_Copies(dialogTitle));
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
+ // we handle "pop3TmpDownloadError" earlier...
+ if (strcmp(err_code, "pop3TmpDownloadError") && NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ nsCOMPtr<nsIPrompt> dialog;
+ rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow)); //it is ok to have null msgWindow, for example when biffing
+ if (NS_SUCCEEDED(rv) && msgWindow)
+ {
+ rv = msgWindow->GetPromptDialog(getter_AddRefs(dialog));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsString alertString;
+ // Format the alert string if parameter list isn't empty
+ if (params)
+ mLocalBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(err_code).get(),
+ params, length, getter_Copies(alertString));
+ else
+ mLocalBundle->GetStringFromName(NS_ConvertASCIItoUTF16(err_code).get(),
+ getter_Copies(alertString));
+ if (m_pop3ConData->command_succeeded) //not a server error message
+ dialog->Alert(dialogTitle.get(), alertString.get());
+ else
+ {
+ nsString serverSaidPrefix;
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ nsCString hostName;
+ // Fomat string with hostname.
+ if (server)
+ rv = server->GetRealHostName(hostName);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoString hostStr;
+ CopyASCIItoUTF16(hostName, hostStr);
+ const char16_t *params[] = { hostStr.get() };
+ mLocalBundle->FormatStringFromName(
+ u"pop3ServerSaid",
+ params, 1, getter_Copies(serverSaidPrefix));
+ }
+
+ nsAutoString message(alertString);
+ message.AppendLiteral(" ");
+ message.Append(serverSaidPrefix);
+ message.AppendLiteral(" ");
+ message.Append(NS_ConvertASCIItoUTF16(m_commandResponse));
+ dialog->Alert(dialogTitle.get(), message.get());
+ }
+ }
+ }
+ }
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ m_pop3ConData->pause_for_read = false;
+ return -1;
+}
+
+int32_t nsPop3Protocol::Pop3SendData(const char * dataBuffer, bool aSuppressLogging)
+{
+ // remove any leftover bytes in the line buffer
+ // this can happen if the last message line doesn't end with a (CR)LF
+ // or a server sent two reply lines
+ m_lineStreamBuffer->ClearBuffer();
+
+ nsresult result = nsMsgProtocol::SendData(dataBuffer);
+
+ if (!aSuppressLogging)
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("SEND: %s"), dataBuffer));
+ else
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
+ (POP3LOG("Logging suppressed for this command (it probably contained authentication information)")));
+
+ if (NS_SUCCEEDED(result))
+ {
+ m_pop3ConData->pause_for_read = true;
+ m_pop3ConData->next_state = POP3_WAIT_FOR_RESPONSE;
+ return 0;
+ }
+
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("Pop3SendData failed: %lx"), result));
+ return -1;
+}
+
+/*
+ * POP3 AUTH extension
+ */
+
+int32_t nsPop3Protocol::SendAuth()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendAuth()")));
+
+ if(!m_pop3ConData->command_succeeded)
+ return Error("pop3ServerError");
+
+ nsAutoCString command("AUTH" CRLF);
+
+ m_pop3ConData->next_state_after_response = POP3_AUTH_RESPONSE;
+ return Pop3SendData(command.get());
+}
+
+int32_t nsPop3Protocol::AuthResponse(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ char * line;
+ uint32_t ln = 0;
+ nsresult rv;
+
+ if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
+ {
+ ClearCapFlag(POP3_AUTH_MECH_UNDEFINED);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ if (!m_pop3ConData->command_succeeded)
+ {
+ /* AUTH command not implemented
+ * so no secure mechanisms available
+ */
+ m_pop3ConData->command_succeeded = true;
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ m_pop3ConData->next_state = POP3_SEND_CAPA;
+ return 0;
+ }
+
+ bool pauseForMoreData = false;
+ line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if(pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true; /* pause */
+ PR_Free(line);
+ return(0);
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ if (!PL_strcmp(line, "."))
+ {
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+
+ // now that we've read all the AUTH responses, go for it
+ m_pop3ConData->next_state = POP3_SEND_CAPA;
+ m_pop3ConData->pause_for_read = false; /* don't pause */
+ }
+ else if (!PL_strcasecmp (line, "CRAM-MD5"))
+ SetCapFlag(POP3_HAS_AUTH_CRAM_MD5);
+ else if (!PL_strcasecmp (line, "NTLM"))
+ SetCapFlag(POP3_HAS_AUTH_NTLM);
+ else if (!PL_strcasecmp (line, "MSN"))
+ SetCapFlag(POP3_HAS_AUTH_NTLM|POP3_HAS_AUTH_MSN);
+ else if (!PL_strcasecmp (line, "GSSAPI"))
+ SetCapFlag(POP3_HAS_AUTH_GSSAPI);
+ else if (!PL_strcasecmp (line, "PLAIN"))
+ SetCapFlag(POP3_HAS_AUTH_PLAIN);
+ else if (!PL_strcasecmp (line, "LOGIN"))
+ SetCapFlag(POP3_HAS_AUTH_LOGIN);
+
+ PR_Free(line);
+ return 0;
+}
+
+/*
+ * POP3 CAPA extension, see RFC 2449, chapter 5
+ */
+
+int32_t nsPop3Protocol::SendCapa()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendCapa()")));
+ if(!m_pop3ConData->command_succeeded)
+ return Error("pop3ServerError");
+
+ nsAutoCString command("CAPA" CRLF);
+
+ m_pop3ConData->next_state_after_response = POP3_CAPA_RESPONSE;
+ return Pop3SendData(command.get());
+}
+
+int32_t nsPop3Protocol::CapaResponse(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ char * line;
+ uint32_t ln = 0;
+
+ if (!m_pop3ConData->command_succeeded)
+ {
+ /* CAPA command not implemented */
+ m_pop3ConData->command_succeeded = true;
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ return 0;
+ }
+
+ bool pauseForMoreData = false;
+ nsresult rv;
+ line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if(pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true; /* pause */
+ PR_Free(line);
+ return(0);
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ if (!PL_strcmp(line, "."))
+ {
+ // now that we've read all the CAPA responses, go for it
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ m_pop3ConData->pause_for_read = false; /* don't pause */
+ }
+ else
+ if (!PL_strcasecmp(line, "XSENDER"))
+ {
+ SetCapFlag(POP3_HAS_XSENDER);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+ else
+ // see RFC 2449, chapter 6.4
+ if (!PL_strcasecmp(line, "RESP-CODES"))
+ {
+ SetCapFlag(POP3_HAS_RESP_CODES);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+ else
+ // see RFC 3206, chapter 6
+ if (!PL_strcasecmp(line, "AUTH-RESP-CODE"))
+ {
+ SetCapFlag(POP3_HAS_AUTH_RESP_CODE);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+ else
+ // see RFC 2595, chapter 4
+ if (!PL_strcasecmp(line, "STLS"))
+ {
+ SetCapFlag(POP3_HAS_STLS);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+ else
+ // see RFC 2449, chapter 6.3
+ if (!PL_strncasecmp(line, "SASL", 4) && strlen(line) > 6)
+ {
+ nsAutoCString responseLine;
+ responseLine.Assign(line + 5);
+
+ if (responseLine.Find("PLAIN", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_PLAIN);
+
+ if (responseLine.Find("LOGIN", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_LOGIN);
+
+ if (responseLine.Find("GSSAPI", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_GSSAPI);
+
+ if (responseLine.Find("CRAM-MD5", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_CRAM_MD5);
+
+ if (responseLine.Find("NTLM", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_NTLM);
+
+ if (responseLine.Find("MSN", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_NTLM|POP3_HAS_AUTH_MSN);
+
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ PR_Free(line);
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Capability entry processed")));
+ return 0;
+}
+
+int32_t nsPop3Protocol::SendTLSResponse()
+{
+ // only tear down our existing connection and open a new one if we received
+ // a +OK response from the pop server after we issued the STLS command
+ nsresult rv = NS_OK;
+ if (m_pop3ConData->command_succeeded)
+ {
+ nsCOMPtr<nsISupports> secInfo;
+ nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ rv = strans->GetSecurityInfo(getter_AddRefs(secInfo));
+
+ if (NS_SUCCEEDED(rv) && secInfo)
+ {
+ nsCOMPtr<nsISSLSocketControl> sslControl = do_QueryInterface(secInfo, &rv);
+
+ if (NS_SUCCEEDED(rv) && sslControl)
+ rv = sslControl->StartTLS();
+ }
+
+ if (NS_SUCCEEDED(rv))
+ {
+ m_pop3ConData->next_state = POP3_SEND_AUTH;
+ m_tlsEnabled = true;
+
+ // certain capabilities like POP3_HAS_AUTH_APOP should be
+ // preserved across the connections.
+ uint32_t preservedCapFlags = m_pop3ConData->capability_flags & POP3_HAS_AUTH_APOP;
+ m_pop3ConData->capability_flags = // resetting the flags
+ 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 |
+ preservedCapFlags;
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ return 0;
+ }
+ }
+
+ ClearFlag(POP3_HAS_STLS);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+
+ return (NS_SUCCEEDED(rv) ? 0 : -1);
+}
+
+void nsPop3Protocol::InitPrefAuthMethods(int32_t authMethodPrefValue)
+{
+ // for m_prefAuthMethods, using the same flags as server capablities.
+ switch (authMethodPrefValue)
+ {
+ case nsMsgAuthMethod::none:
+ m_prefAuthMethods = POP3_HAS_AUTH_NONE;
+ break;
+ case nsMsgAuthMethod::old:
+ m_prefAuthMethods = POP3_HAS_AUTH_USER;
+ break;
+ case nsMsgAuthMethod::passwordCleartext:
+ m_prefAuthMethods = POP3_HAS_AUTH_USER |
+ POP3_HAS_AUTH_LOGIN | POP3_HAS_AUTH_PLAIN;
+ break;
+ case nsMsgAuthMethod::passwordEncrypted:
+ m_prefAuthMethods = POP3_HAS_AUTH_CRAM_MD5 |
+ POP3_HAS_AUTH_APOP;
+ break;
+ case nsMsgAuthMethod::NTLM:
+ m_prefAuthMethods = POP3_HAS_AUTH_NTLM | POP3_HAS_AUTH_MSN;
+ break;
+ case nsMsgAuthMethod::GSSAPI:
+ m_prefAuthMethods = POP3_HAS_AUTH_GSSAPI;
+ break;
+ case nsMsgAuthMethod::secure:
+ m_prefAuthMethods = POP3_HAS_AUTH_APOP |
+ POP3_HAS_AUTH_CRAM_MD5 | POP3_HAS_AUTH_GSSAPI |
+ POP3_HAS_AUTH_NTLM | POP3_HAS_AUTH_MSN;
+ break;
+ default:
+ NS_ASSERTION(false, "POP: authMethod pref invalid");
+ // TODO log to error console
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
+ (POP3LOG("POP: bad pref authMethod = %d\n"), authMethodPrefValue));
+ // fall to any
+ MOZ_FALLTHROUGH;
+ case nsMsgAuthMethod::anything:
+ m_prefAuthMethods = POP3_HAS_AUTH_USER |
+ POP3_HAS_AUTH_LOGIN | POP3_HAS_AUTH_PLAIN |
+ POP3_HAS_AUTH_CRAM_MD5 | POP3_HAS_AUTH_APOP |
+ POP3_HAS_AUTH_GSSAPI |
+ POP3_HAS_AUTH_NTLM | POP3_HAS_AUTH_MSN;
+ // TODO needed?
+ break;
+ }
+ NS_ASSERTION(m_prefAuthMethods != POP3_AUTH_MECH_UNDEFINED,
+ "POP: InitPrefAuthMethods() didn't work");
+}
+
+/**
+ * Changes m_currentAuthMethod to pick the best one
+ * which is allowed by server and prefs and not marked failed.
+ * The order of preference and trying of auth methods is encoded here.
+ */
+nsresult nsPop3Protocol::ChooseAuthMethod()
+{
+ int32_t availCaps = GetCapFlags() & m_prefAuthMethods & ~m_failedAuthMethods;
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("POP auth: server caps 0x%X, pref 0x%X, failed 0x%X, avail caps 0x%X"),
+ GetCapFlags(), m_prefAuthMethods, m_failedAuthMethods, availCaps));
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("(GSSAPI = 0x%X, CRAM = 0x%X, APOP = 0x%X, NTLM = 0x%X, "
+ "MSN = 0x%X, PLAIN = 0x%X, LOGIN = 0x%X, USER/PASS = 0x%X)"),
+ POP3_HAS_AUTH_GSSAPI, POP3_HAS_AUTH_CRAM_MD5, POP3_HAS_AUTH_APOP,
+ POP3_HAS_AUTH_NTLM, POP3_HAS_AUTH_MSN, POP3_HAS_AUTH_PLAIN,
+ POP3_HAS_AUTH_LOGIN, POP3_HAS_AUTH_USER));
+
+ if (POP3_HAS_AUTH_GSSAPI & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_GSSAPI;
+ else if (POP3_HAS_AUTH_CRAM_MD5 & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_CRAM_MD5;
+ else if (POP3_HAS_AUTH_APOP & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_APOP;
+ else if (POP3_HAS_AUTH_NTLM & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_NTLM;
+ else if (POP3_HAS_AUTH_MSN & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_MSN;
+ else if (POP3_HAS_AUTH_PLAIN & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_PLAIN;
+ else if (POP3_HAS_AUTH_LOGIN & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_LOGIN;
+ else if (POP3_HAS_AUTH_USER & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_USER;
+ else
+ {
+ // there are no matching login schemes at all, per server and prefs
+ m_currentAuthMethod = POP3_AUTH_MECH_UNDEFINED;
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("no auth method remaining")));
+ return NS_ERROR_FAILURE;
+ }
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("trying auth method 0x%X"), m_currentAuthMethod));
+ return NS_OK;
+}
+
+void nsPop3Protocol::MarkAuthMethodAsFailed(int32_t failedAuthMethod)
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("marking auth method 0x%X failed"), failedAuthMethod));
+ m_failedAuthMethods |= failedAuthMethod;
+}
+
+/**
+ * Start over, trying all auth methods again
+ */
+void nsPop3Protocol::ResetAuthMethods()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("resetting (failed) auth methods")));
+ m_currentAuthMethod = POP3_AUTH_MECH_UNDEFINED;
+ m_failedAuthMethods = 0;
+}
+
+/**
+ * state POP3_PROCESS_AUTH
+ * Called when we should try to authenticate to the server.
+ * Also called when one auth method fails and we want to try and start
+ * the next best auth method.
+ */
+int32_t nsPop3Protocol::ProcessAuth()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("ProcessAuth()")));
+
+ // Try to upgrade to STARTTLS -- TODO move into its own function
+ if (!m_tlsEnabled)
+ {
+ if(TestCapFlag(POP3_HAS_STLS))
+ {
+ if (m_socketType == nsMsgSocketType::trySTARTTLS ||
+ m_socketType == nsMsgSocketType::alwaysSTARTTLS)
+ {
+ nsAutoCString command("STLS" CRLF);
+
+ m_pop3ConData->next_state_after_response = POP3_TLS_RESPONSE;
+ return Pop3SendData(command.get());
+ }
+ }
+ else if (m_socketType == nsMsgSocketType::alwaysSTARTTLS)
+ {
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ return Error("nsErrorCouldNotConnectViaTls");
+ }
+ }
+
+ m_password_already_sent = false;
+
+ nsresult rv = ChooseAuthMethod();
+ if (NS_FAILED(rv))
+ {
+ // Pref doesn't match server. Now, find an appropriate error msg.
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("ProcessAuth() early exit because no auth methods")));
+
+ // AuthGSSAPI* falls in here in case of an auth failure.
+ // If Kerberos was the only method, assume that
+ // the user is just not logged in yet, and show an appropriate error.
+ if (m_prefAuthMethods == POP3_HAS_AUTH_GSSAPI &&
+ m_failedAuthMethods == POP3_HAS_AUTH_GSSAPI)
+ return Error("pop3GssapiFailure");
+
+ // pref has plaintext pw & server claims to support encrypted pw
+ if (m_prefAuthMethods == (POP3_HAS_AUTH_USER | POP3_HAS_AUTH_LOGIN |
+ POP3_HAS_AUTH_PLAIN) &&
+ GetCapFlags() & (POP3_HAS_AUTH_CRAM_MD5 | POP3_HAS_AUTH_APOP))
+ // tell user to change to encrypted pw
+ return Error("pop3AuthChangePlainToEncrypt");
+ // pref has encrypted pw & server claims to support plaintext pw
+ else if (m_prefAuthMethods == (POP3_HAS_AUTH_CRAM_MD5 |
+ POP3_HAS_AUTH_APOP) &&
+ GetCapFlags() & (POP3_HAS_AUTH_USER | POP3_HAS_AUTH_LOGIN |
+ POP3_HAS_AUTH_PLAIN))
+ {
+ // have SSL
+ if (m_socketType == nsMsgSocketType::SSL ||
+ m_socketType == nsMsgSocketType::alwaysSTARTTLS)
+ // tell user to change to plaintext pw
+ return Error("pop3AuthChangeEncryptToPlainSSL");
+ else
+ // tell user to change to plaintext pw, with big warning
+ return Error("pop3AuthChangeEncryptToPlainNoSSL");
+ }
+ else
+ // just "change auth method"
+ return Error("pop3AuthMechNotSupported");
+ }
+
+ switch (m_currentAuthMethod)
+ {
+ case POP3_HAS_AUTH_GSSAPI:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP GSSAPI")));
+ m_pop3ConData->next_state = POP3_AUTH_GSSAPI;
+ break;
+ case POP3_HAS_AUTH_APOP:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP APOP")));
+ m_pop3ConData->next_state = POP3_SEND_PASSWORD;
+ break;
+ case POP3_HAS_AUTH_CRAM_MD5:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP CRAM")));
+ MOZ_FALLTHROUGH;
+ case POP3_HAS_AUTH_PLAIN:
+ case POP3_HAS_AUTH_USER:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP username")));
+ m_pop3ConData->next_state = POP3_SEND_USERNAME;
+ break;
+ case POP3_HAS_AUTH_LOGIN:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP AUTH=LOGIN")));
+ m_pop3ConData->next_state = POP3_AUTH_LOGIN;
+ break;
+ case POP3_HAS_AUTH_NTLM:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP NTLM")));
+ m_pop3ConData->next_state = POP3_AUTH_NTLM;
+ break;
+ case POP3_HAS_AUTH_NONE:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP no auth")));
+ m_pop3ConData->command_succeeded = true;
+ m_pop3ConData->next_state = POP3_NEXT_AUTH_STEP;
+ break;
+ default:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
+ (POP3LOG("POP: m_currentAuthMethod has unknown value")));
+ return Error("pop3AuthMechNotSupported");
+ }
+
+ m_pop3ConData->pause_for_read = false;
+
+ return 0;
+}
+
+/**
+ * state POP3_NEXT_AUTH_STEP
+ * This is called when we finished one auth step (e.g. sending username
+ * or password are separate steps, similarly for AUTH LOGIN, NTLM etc.)
+ * and want to proceed to the next one.
+ */
+int32_t nsPop3Protocol::NextAuthStep()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("NextAuthStep()")));
+ if (m_pop3ConData->command_succeeded)
+ {
+ if (m_password_already_sent || // (also true for GSSAPI)
+ m_currentAuthMethod == POP3_HAS_AUTH_NONE)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("login succeeded")));
+ m_nsIPop3Sink->SetUserAuthenticated(true);
+ ClearFlag(POP3_PASSWORD_FAILED);
+ if (m_pop3ConData->verify_logon)
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ else
+ m_pop3ConData->next_state = (m_pop3ConData->get_url)
+ ? POP3_SEND_GURL : POP3_SEND_STAT;
+ }
+ else
+ m_pop3ConData->next_state = POP3_SEND_PASSWORD;
+ }
+ else
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("command did not succeed")));
+ // response code received shows that login failed not because of
+ // wrong credential -> stop login without retry or pw dialog, only alert
+ // parameter list -> user
+ nsCString userName;
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ nsresult rv = server->GetRealUsername(userName);
+ NS_ENSURE_SUCCESS(rv, -1);
+ NS_ConvertUTF8toUTF16 userNameUTF16(userName);
+ const char16_t* params[] = { userNameUTF16.get() };
+ if (TestFlag(POP3_STOPLOGIN))
+ {
+ if (m_password_already_sent)
+ return Error("pop3PasswordFailed", params, 1);
+
+ return Error("pop3UsernameFailure");
+ }
+ // response code received shows that server is certain about the
+ // credential was wrong -> no fallback, show alert and pw dialog
+ if (TestFlag(POP3_AUTH_FAILURE))
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("auth failure, setting password failed")));
+ if (m_password_already_sent)
+ Error("pop3PasswordFailed", params, 1);
+ else
+ Error("pop3UsernameFailure");
+ SetFlag(POP3_PASSWORD_FAILED);
+ ClearFlag(POP3_AUTH_FAILURE);
+ return 0;
+ }
+
+ // We have no certain response code -> fallback and try again.
+ // Mark the auth method failed, to use a different method next round.
+ MarkAuthMethodAsFailed(m_currentAuthMethod);
+
+ if (m_currentAuthMethod == POP3_HAS_AUTH_USER &&
+ !m_password_already_sent)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("USER username failed")));
+ // if USER auth method failed before sending the password,
+ // the username was wrong.
+ // no fallback but return error
+ return Error("pop3UsernameFailure");
+ }
+
+ // If we have no auth method left, ask user to try with new password
+ rv = ChooseAuthMethod();
+ if (NS_FAILED(rv))
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
+ (POP3LOG("POP: no auth methods remaining, setting password failure")));
+ /* Sever the connection and go back to the `read password' state,
+ which, upon success, will re-open the connection. Set a flag
+ which causes the prompt to be different that time (to indicate
+ that the old password was bogus.)
+
+ But if we're just checking for new mail (biff) then don't bother
+ prompting the user for a password: just fail silently.
+ */
+ SetFlag(POP3_PASSWORD_FAILED);
+ Error("pop3PasswordFailed", params, 1);
+ return 0;
+ }
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("still have some auth methods to try")));
+
+ // TODO needed?
+ //m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+
+ m_pop3ConData->command_succeeded = true;
+
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ }
+
+ if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
+ {
+ ClearCapFlag(POP3_AUTH_MECH_UNDEFINED);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ m_pop3ConData->pause_for_read = false;
+
+ return 0;
+}
+
+// LOGIN consists of three steps not two as USER/PASS or CRAM-MD5,
+// so we've to start here and continue in SendUsername if the server
+// responds + to "AUTH LOGIN"
+int32_t nsPop3Protocol::AuthLogin()
+{
+ nsAutoCString command("AUTH LOGIN" CRLF);
+ m_pop3ConData->next_state_after_response = POP3_AUTH_LOGIN_RESPONSE;
+ m_pop3ConData->pause_for_read = true;
+
+ return Pop3SendData(command.get());
+}
+
+int32_t nsPop3Protocol::AuthLoginResponse()
+{
+ // need the test to be here instead in NextAuthStep() to
+ // differentiate between command AUTH LOGIN failed and
+ // sending username using LOGIN mechanism failed.
+ if (!m_pop3ConData->command_succeeded)
+ {
+ // we failed with LOGIN, remove it
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_LOGIN);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ }
+ else
+ m_pop3ConData->next_state = POP3_SEND_USERNAME;
+
+ m_pop3ConData->pause_for_read = false;
+
+ return 0;
+}
+
+// NTLM, like LOGIN consists of three steps not two as USER/PASS or CRAM-MD5,
+// so we've to start here and continue in SendUsername if the server
+// responds + to "AUTH NTLM"
+int32_t nsPop3Protocol::AuthNtlm()
+{
+ nsAutoCString command (m_currentAuthMethod == POP3_HAS_AUTH_MSN
+ ? "AUTH MSN" CRLF : "AUTH NTLM" CRLF);
+ m_pop3ConData->next_state_after_response = POP3_AUTH_NTLM_RESPONSE;
+ m_pop3ConData->pause_for_read = true;
+
+ return Pop3SendData(command.get());
+}
+
+int32_t nsPop3Protocol::AuthNtlmResponse()
+{
+ // need the test to be here instead in NextAuthStep() to
+ // differentiate between command AUTH NTLM failed and
+ // sending username using NTLM mechanism failed.
+ if (!m_pop3ConData->command_succeeded)
+ {
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_NTLM);
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_MSN);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ }
+ else
+ m_pop3ConData->next_state = POP3_SEND_USERNAME;
+
+ m_pop3ConData->pause_for_read = false;
+
+ return 0;
+}
+
+int32_t nsPop3Protocol::AuthGSSAPI()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("AuthGSSAPI()")));
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server) {
+ nsAutoCString cmd;
+ nsAutoCString service("pop@");
+ nsCString hostName;
+ nsresult rv;
+ server->GetRealHostName(hostName);
+ service.Append(hostName);
+ rv = DoGSSAPIStep1(service.get(), m_username.get(), cmd);
+ if (NS_SUCCEEDED(rv)) {
+ m_GSSAPICache.Assign(cmd);
+ m_pop3ConData->next_state_after_response = POP3_AUTH_GSSAPI_FIRST;
+ m_pop3ConData->pause_for_read = true;
+ return Pop3SendData("AUTH GSSAPI" CRLF);
+ }
+ }
+
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_GSSAPI);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ m_pop3ConData->pause_for_read = false;
+ return 0;
+}
+
+int32_t nsPop3Protocol::AuthGSSAPIResponse(bool first)
+{
+ if (!m_pop3ConData->command_succeeded)
+ {
+ if (first)
+ m_GSSAPICache.Truncate();
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_GSSAPI);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ m_pop3ConData->pause_for_read = false;
+ return 0;
+ }
+
+ int32_t result;
+
+ m_pop3ConData->next_state_after_response = POP3_AUTH_GSSAPI_STEP;
+ m_pop3ConData->pause_for_read = true;
+
+ if (first) {
+ m_GSSAPICache += CRLF;
+ result = Pop3SendData(m_GSSAPICache.get());
+ m_GSSAPICache.Truncate();
+ }
+ else {
+ nsAutoCString cmd;
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("GSSAPI step 2")));
+ nsresult rv = DoGSSAPIStep2(m_commandResponse, cmd);
+ if (NS_FAILED(rv))
+ cmd = "*";
+ if (rv == NS_SUCCESS_AUTH_FINISHED) {
+ m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
+ m_password_already_sent = true;
+ }
+ cmd += CRLF;
+ result = Pop3SendData(cmd.get());
+ }
+
+ return result;
+}
+
+int32_t nsPop3Protocol::SendUsername()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendUsername()")));
+ if(m_username.IsEmpty())
+ return Error("pop3UsernameUndefined");
+
+ // <copied from="SendPassword()">
+ // Needed for NTLM
+
+ // The POP3_SEND_PASSWORD/POP3_WAIT_SEND_PASSWORD states have already
+ // got the password - they will have cancelled if necessary.
+ // If the password is still empty here, don't try to go on.
+ if (m_passwordResult.IsEmpty())
+ {
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ return Error("pop3PasswordUndefined");
+ }
+ // </copied>
+
+ nsAutoCString cmd;
+
+ if (m_currentAuthMethod == POP3_HAS_AUTH_NTLM)
+ (void) DoNtlmStep1(m_username.get(), m_passwordResult.get(), cmd);
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_CRAM_MD5)
+ cmd = "AUTH CRAM-MD5";
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_PLAIN)
+ cmd = "AUTH PLAIN";
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_LOGIN)
+ {
+ char *base64Str = PL_Base64Encode(m_username.get(), m_username.Length(), nullptr);
+ cmd = base64Str;
+ PR_Free(base64Str);
+ }
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_USER)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("USER login")));
+ cmd = "USER ";
+ cmd += m_username;
+ }
+ else
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
+ (POP3LOG("In nsPop3Protocol::SendUsername(), m_currentAuthMethod is 0x%X, "
+ "but that is unexpected"), m_currentAuthMethod));
+ return Error("pop3AuthInternalError");
+ }
+
+ cmd += CRLF;
+
+ m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
+
+ m_pop3ConData->pause_for_read = true;
+
+ return Pop3SendData(cmd.get());
+}
+
+int32_t nsPop3Protocol::SendPassword()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendPassword()")));
+ if (m_username.IsEmpty())
+ return Error("pop3UsernameUndefined");
+
+ // <copied to="SendUsername()">
+ // Needed here, too, because APOP skips SendUsername()
+ // The POP3_SEND_PASSWORD/POP3_WAIT_SEND_PASSWORD states have already
+ // got the password - they will have cancelled if necessary.
+ // If the password is still empty here, don't try to go on.
+ if (m_passwordResult.IsEmpty())
+ {
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ return Error("pop3PasswordUndefined");
+ }
+ // </copied>
+
+ nsAutoCString cmd;
+ nsresult rv;
+
+ if (m_currentAuthMethod == POP3_HAS_AUTH_NTLM)
+ rv = DoNtlmStep2(m_commandResponse, cmd);
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_CRAM_MD5)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("CRAM login")));
+ char buffer[512]; // TODO nsAutoCString
+ unsigned char digest[DIGEST_LENGTH];
+
+ char *decodedChallenge = PL_Base64Decode(m_commandResponse.get(),
+ m_commandResponse.Length(), nullptr);
+
+ if (decodedChallenge)
+ rv = MSGCramMD5(decodedChallenge, strlen(decodedChallenge),
+ m_passwordResult.get(), m_passwordResult.Length(), digest);
+ else
+ rv = NS_ERROR_NULL_POINTER;
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString encodedDigest;
+ char hexVal[8];
+
+ for (uint32_t j = 0; j < 16; j++)
+ {
+ PR_snprintf (hexVal,8, "%.2x", 0x0ff & (unsigned short)digest[j]);
+ encodedDigest.Append(hexVal);
+ }
+
+ PR_snprintf(buffer, sizeof(buffer), "%s %s", m_username.get(),
+ encodedDigest.get());
+ char *base64Str = PL_Base64Encode(buffer, strlen(buffer), nullptr);
+ cmd = base64Str;
+ PR_Free(base64Str);
+ }
+
+ if (NS_FAILED(rv))
+ cmd = "*";
+ }
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_APOP)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("APOP login")));
+ char buffer[512];
+ unsigned char digest[DIGEST_LENGTH];
+
+ rv = MSGApopMD5(m_ApopTimestamp.get(), m_ApopTimestamp.Length(),
+ m_passwordResult.get(), m_passwordResult.Length(), digest);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString encodedDigest;
+ char hexVal[8];
+
+ for (uint32_t j=0; j<16; j++)
+ {
+ PR_snprintf (hexVal,8, "%.2x", 0x0ff & (unsigned short)digest[j]);
+ encodedDigest.Append(hexVal);
+ }
+
+ PR_snprintf(buffer, sizeof(buffer), "APOP %s %s", m_username.get(),
+ encodedDigest.get());
+ cmd = buffer;
+ }
+
+ if (NS_FAILED(rv))
+ cmd = "*";
+ }
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_PLAIN)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("PLAIN login")));
+ // workaround for IPswitch's IMail server software
+ // this server goes into LOGIN mode even if we send "AUTH PLAIN"
+ // "VXNlc" is the beginning of the base64 encoded prompt ("Username:") for LOGIN
+ if (StringBeginsWith(m_commandResponse, NS_LITERAL_CSTRING("VXNlc")))
+ {
+ // disable PLAIN and enable LOGIN (in case it's not already enabled)
+ ClearCapFlag(POP3_HAS_AUTH_PLAIN);
+ SetCapFlag(POP3_HAS_AUTH_LOGIN);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+
+ // reenter authentication again at LOGIN response handler
+ m_pop3ConData->next_state = POP3_AUTH_LOGIN_RESPONSE;
+ m_pop3ConData->pause_for_read = false;
+ return 0;
+ }
+
+ char plain_string[512]; // TODO nsCString
+ int len = 1; /* first <NUL> char */
+ memset(plain_string, 0, 512);
+ PR_snprintf(&plain_string[1], 510, "%s", m_username.get());
+ len += m_username.Length();
+ len++; /* second <NUL> char */
+ PR_snprintf(&plain_string[len], 511-len, "%s", m_passwordResult.get());
+ len += m_passwordResult.Length();
+
+ char *base64Str = PL_Base64Encode(plain_string, len, nullptr);
+ cmd = base64Str;
+ PR_Free(base64Str);
+ }
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_LOGIN)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("LOGIN password")));
+ char * base64Str =
+ PL_Base64Encode(m_passwordResult.get(), m_passwordResult.Length(),
+ nullptr);
+ cmd = base64Str;
+ PR_Free(base64Str);
+ }
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_USER)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("PASS password")));
+ cmd = "PASS ";
+ cmd += m_passwordResult;
+ }
+ else
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
+ (POP3LOG("In nsPop3Protocol::SendPassword(), m_currentAuthMethod is %X, "
+ "but that is unexpected"), m_currentAuthMethod));
+ return Error("pop3AuthInternalError");
+ }
+
+ cmd += CRLF;
+
+ // TODO needed?
+ //m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+
+ m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
+
+ m_pop3ConData->pause_for_read = true;
+
+ m_password_already_sent = true;
+ m_lastPasswordSent = m_passwordResult;
+ return Pop3SendData(cmd.get(), true);
+}
+
+int32_t nsPop3Protocol::SendStatOrGurl(bool sendStat)
+{
+ nsAutoCString cmd;
+ if (sendStat)
+ {
+ cmd = "STAT" CRLF;
+ m_pop3ConData->next_state_after_response = POP3_GET_STAT;
+ }
+ else
+ {
+ cmd = "GURL" CRLF;
+ m_pop3ConData->next_state_after_response = POP3_GURL_RESPONSE;
+ }
+ return Pop3SendData(cmd.get());
+}
+
+
+int32_t
+nsPop3Protocol::SendStat()
+{
+ return SendStatOrGurl(true);
+}
+
+
+int32_t
+nsPop3Protocol::GetStat()
+{
+ // check stat response
+ if (!m_pop3ConData->command_succeeded)
+ return Error("pop3StatFail");
+
+ /* stat response looks like: %d %d
+ * The first number is the number of articles
+ * The second number is the number of bytes
+ *
+ * grab the first and second arg of stat response
+ */
+ nsCString oldStr (m_commandResponse);
+ char *newStr = oldStr.BeginWriting();
+ char *num = NS_strtok(" ", &newStr); // msg num
+ if (num)
+ {
+ m_pop3ConData->number_of_messages = atol(num); // bytes
+ num = NS_strtok(" ", &newStr);
+ m_commandResponse = newStr;
+ if (num)
+ m_totalFolderSize = nsCRT::atoll(num); //we always initialize m_totalFolderSize to 0
+ }
+ else
+ m_pop3ConData->number_of_messages = 0;
+
+ m_pop3ConData->really_new_messages = 0;
+ m_pop3ConData->real_new_counter = 1;
+
+ m_totalDownloadSize = -1; // Means we need to calculate it, later.
+
+ if (m_pop3ConData->number_of_messages <= 0)
+ {
+ // We're all done. We know we have no mail.
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ PL_HashTableEnumerateEntries(m_pop3ConData->uidlinfo->hash, hash_clear_mapper, nullptr);
+ // Hack - use nsPop3Sink to wipe out any stale Partial messages
+ m_nsIPop3Sink->BeginMailDelivery(false, nullptr, nullptr);
+ m_nsIPop3Sink->AbortMailDelivery(this);
+ return(0);
+ }
+
+ /* We're just checking for new mail, and we're not playing any games that
+ involve keeping messages on the server. Therefore, we now know enough
+ to finish up. If we had no messages, that would have been handled
+ above; therefore, we know we have some new messages.
+ */
+ if (m_pop3ConData->only_check_for_new_mail && !m_pop3ConData->leave_on_server)
+ {
+ m_nsIPop3Sink->SetBiffStateAndUpdateFE(nsIMsgFolder::nsMsgBiffState_NewMail,
+ m_pop3ConData->number_of_messages,
+ true);
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ return(0);
+ }
+
+
+ if (!m_pop3ConData->only_check_for_new_mail)
+ {
+ /* The following was added to prevent the loss of Data when we try and
+ write to somewhere we don't have write access error to (See bug 62480)
+ (Note: This is only a temp hack until the underlying XPCOM is fixed
+ to return errors) */
+
+ nsresult rv;
+ nsCOMPtr <nsIMsgWindow> msgWindow;
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url);
+ if (mailnewsUrl)
+ rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
+// NS_ASSERTION(NS_SUCCEEDED(rv) && msgWindow, "no msg window");
+
+ rv = m_nsIPop3Sink->BeginMailDelivery(m_pop3ConData->only_uidl != nullptr, msgWindow,
+ &m_pop3ConData->msg_del_started);
+ if (NS_FAILED(rv))
+ {
+ m_nsIPop3Sink->AbortMailDelivery(this);
+ if (rv == NS_MSG_FOLDER_BUSY) {
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ nsString accountName;
+ rv = server->GetPrettyName(accountName);
+ NS_ENSURE_SUCCESS(rv, -1);
+
+ const char16_t *params[] = { accountName.get() };
+ return Error("pop3ServerBusy", params, 1);
+ }
+
+ return Error("pop3MessageWriteError");
+ }
+
+ if(!m_pop3ConData->msg_del_started)
+ return Error("pop3MessageWriteError");
+ }
+
+ m_pop3ConData->next_state = POP3_SEND_LIST;
+ return 0;
+}
+
+
+
+int32_t
+nsPop3Protocol::SendGurl()
+{
+ if (m_pop3ConData->capability_flags == POP3_CAPABILITY_UNDEFINED ||
+ TestCapFlag(POP3_GURL_UNDEFINED | POP3_HAS_GURL))
+ return SendStatOrGurl(false);
+ else
+ return -1;
+}
+
+
+int32_t
+nsPop3Protocol::GurlResponse()
+{
+ ClearCapFlag(POP3_GURL_UNDEFINED);
+
+ if (m_pop3ConData->command_succeeded)
+ {
+ SetCapFlag(POP3_HAS_GURL);
+ if (m_nsIPop3Sink)
+ m_nsIPop3Sink->SetMailAccountURL(m_commandResponse);
+ }
+ else
+ {
+ ClearCapFlag(POP3_HAS_GURL);
+ }
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+
+ return 0;
+}
+
+int32_t nsPop3Protocol::SendList()
+{
+ // check for server returning number of messages that will cause the calculation
+ // of the size of the block for msg_info to
+ // overflow a 32 bit int, in turn causing us to allocate a block of memory much
+ // smaller than we think we're allocating, and
+ // potentially allowing the server to make us overwrite memory outside our heap
+ // block.
+
+ if (m_pop3ConData->number_of_messages > (int) (0xFFFFF000 / sizeof(Pop3MsgInfo)))
+ return MK_OUT_OF_MEMORY;
+
+
+ m_pop3ConData->msg_info = (Pop3MsgInfo *)
+ PR_CALLOC(sizeof(Pop3MsgInfo) * m_pop3ConData->number_of_messages);
+ if (!m_pop3ConData->msg_info)
+ return(MK_OUT_OF_MEMORY);
+ m_pop3ConData->next_state_after_response = POP3_GET_LIST;
+ m_listpos = 0;
+ return Pop3SendData("LIST" CRLF);
+}
+
+
+
+int32_t
+nsPop3Protocol::GetList(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ /* check list response
+ * This will get called multiple times
+ * but it's alright since command_succeeded
+ * will remain constant
+ */
+ if(!m_pop3ConData->command_succeeded)
+ return Error("pop3ListFailure");
+
+ uint32_t ln = 0;
+ bool pauseForMoreData = false;
+ nsresult rv;
+ char *line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if (pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true;
+ PR_Free(line);
+ return(ln);
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ /* parse the line returned from the list command
+ * it looks like
+ * #msg_number #bytes
+ *
+ * list data is terminated by a ".CRLF" line
+ */
+ if (!PL_strcmp(line, "."))
+ {
+ // limit the list if fewer entries than given in STAT response
+ if(m_listpos < m_pop3ConData->number_of_messages)
+ m_pop3ConData->number_of_messages = m_listpos;
+ m_pop3ConData->next_state = POP3_SEND_UIDL_LIST;
+ m_pop3ConData->pause_for_read = false;
+ PR_Free(line);
+ return(0);
+ }
+
+ char *newStr = line;
+ char *token = NS_strtok(" ", &newStr);
+ if (token)
+ {
+ int32_t msg_num = atol(token);
+
+ if (++m_listpos <= m_pop3ConData->number_of_messages)
+ {
+ token = NS_strtok(" ", &newStr);
+ if (token)
+ {
+ m_pop3ConData->msg_info[m_listpos-1].size = atol(token);
+ m_pop3ConData->msg_info[m_listpos-1].msgnum = msg_num;
+ }
+ }
+ }
+
+ PR_Free(line);
+ return(0);
+}
+
+
+/* UIDL and XTND are both unsupported for this mail server.
+ If not enabled any advanced features, we're able to live
+ without them. We're simply downloading and deleting everything
+ on the server.
+
+ Advanced features are:
+ *'Keep Mail on Server' with aging or deletion support
+ *'Fetch Headers Only'
+ *'Limit Message Size'
+ *only download a specific UID
+
+ These require knowledge of of all messages UID's on the server at
+ least when it comes to deleting deleting messages on server that
+ have been deleted on client or vice versa. TOP doesn't help here
+ without generating huge traffic and is mostly not supported at all
+ if the server lacks UIDL and XTND XLST.
+
+ In other cases the user has to join the 20th century.
+ Tell the user this, and refuse to download any messages until
+ they've gone into preferences and turned off any of the above
+ prefs.
+*/
+int32_t nsPop3Protocol::HandleNoUidListAvailable()
+{
+ m_pop3ConData->pause_for_read = false;
+
+ if(!m_pop3ConData->leave_on_server &&
+ !m_pop3ConData->headers_only &&
+ m_pop3ConData->size_limit <= 0 &&
+ !m_pop3ConData->only_uidl)
+ {
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ return 0;
+ }
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ nsCString hostName;
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ nsresult rv = server->GetRealHostName(hostName);
+ NS_ENSURE_SUCCESS(rv, -1);
+ NS_ConvertASCIItoUTF16 hostNameUnicode(hostName);
+ const char16_t *params[] = { hostNameUnicode.get() };
+ return Error("pop3ServerDoesNotSupportUidlEtc", params, 1);
+}
+
+
+/* km
+ *
+ * net_pop3_send_xtnd_xlst_msgid
+ *
+ * Process state: POP3_SEND_XTND_XLST_MSGID
+ *
+ * If we get here then UIDL is not supported by the mail server.
+ * Some mail servers support a similar command:
+ *
+ * XTND XLST Message-Id
+ *
+ * Here is a sample transaction from a QUALCOMM server
+
+ >>XTND XLST Message-Id
+ <<+OK xlst command accepted; headers coming.
+ <<1 Message-ID: <3117E4DC.2699@netscape.invalid>
+ <<2 Message-Id: <199602062335.PAA19215@lemon.example.com>
+
+ * This function will send the xtnd command and put us into the
+ * POP3_GET_XTND_XLST_MSGID state
+ *
+*/
+int32_t nsPop3Protocol::SendXtndXlstMsgid()
+{
+ if (TestCapFlag(POP3_HAS_XTND_XLST | POP3_XTND_XLST_UNDEFINED))
+ {
+ m_pop3ConData->next_state_after_response = POP3_GET_XTND_XLST_MSGID;
+ m_pop3ConData->pause_for_read = true;
+ m_listpos = 0;
+ return Pop3SendData("XTND XLST Message-Id" CRLF);
+ }
+ else
+ return HandleNoUidListAvailable();
+}
+
+
+/* km
+ *
+ * net_pop3_get_xtnd_xlst_msgid
+ *
+ * This code was created from the net_pop3_get_uidl_list boiler plate.
+ * The difference is that the XTND reply strings have one more token per
+ * string than the UIDL reply strings do.
+ *
+ */
+
+int32_t
+nsPop3Protocol::GetXtndXlstMsgid(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ /* check list response
+ * This will get called multiple times
+ * but it's alright since command_succeeded
+ * will remain constant
+ */
+ ClearCapFlag(POP3_XTND_XLST_UNDEFINED);
+
+ if (!m_pop3ConData->command_succeeded)
+ {
+ ClearCapFlag(POP3_HAS_XTND_XLST);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ HandleNoUidListAvailable();
+ return(0);
+ }
+ else
+ {
+ SetCapFlag(POP3_HAS_XTND_XLST);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ uint32_t ln = 0;
+ bool pauseForMoreData = false;
+ nsresult rv;
+ char *line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if (pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true;
+ PR_Free(line);
+ return ln;
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ /* parse the line returned from the list command
+ * it looks like
+ * 1 Message-ID: <3117E4DC.2699@example.com>
+ *
+ * list data is terminated by a ".CRLF" line
+ */
+ if (!PL_strcmp(line, "."))
+ {
+ // limit the list if fewer entries than given in STAT response
+ if(m_listpos < m_pop3ConData->number_of_messages)
+ m_pop3ConData->number_of_messages = m_listpos;
+ m_pop3ConData->list_done = true;
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ m_pop3ConData->pause_for_read = false;
+ PR_Free(line);
+ return(0);
+ }
+
+ char *newStr = line;
+ char *token = NS_strtok(" ", &newStr); // msg num
+ if (token)
+ {
+ int32_t msg_num = atol(token);
+ if (++m_listpos <= m_pop3ConData->number_of_messages)
+ {
+ NS_strtok(" ", &newStr); // eat message ID token
+ const char *uid = NS_strtok(" ", &newStr); // not really a UID but a unique token -km
+ if (!uid)
+ /* This is bad. The server didn't give us a UIDL for this message.
+ I've seen this happen when somehow the mail spool has a message
+ that contains a header that reads "X-UIDL: \n". But how that got
+ there, I have no idea; must be a server bug. Or something. */
+ uid = "";
+
+ // seeking right entry, but try the one that should it be first
+ int32_t i;
+ if(m_pop3ConData->msg_info[m_listpos - 1].msgnum == msg_num)
+ i = m_listpos - 1;
+ else
+ for(i = 0; i < m_pop3ConData->number_of_messages &&
+ m_pop3ConData->msg_info[i].msgnum != msg_num; i++)
+ ;
+
+ // only if found a matching slot
+ if (i < m_pop3ConData->number_of_messages)
+ {
+ // to protect us from memory leak in case of getting a msg num twice
+ m_pop3ConData->msg_info[i].uidl = PL_strdup(uid);
+ if (!m_pop3ConData->msg_info[i].uidl)
+ {
+ PR_Free(line);
+ return MK_OUT_OF_MEMORY;
+ }
+ }
+ }
+ }
+
+ PR_Free(line);
+ return(0);
+}
+
+
+int32_t nsPop3Protocol::SendUidlList()
+{
+ if (TestCapFlag(POP3_HAS_UIDL | POP3_UIDL_UNDEFINED))
+ {
+ m_pop3ConData->next_state_after_response = POP3_GET_UIDL_LIST;
+ m_pop3ConData->pause_for_read = true;
+ m_listpos = 0;
+ return Pop3SendData("UIDL" CRLF);
+ }
+ else
+ return SendXtndXlstMsgid();
+}
+
+
+int32_t nsPop3Protocol::GetUidlList(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ /* check list response
+ * This will get called multiple times
+ * but it's alright since command_succeeded
+ * will remain constant
+ */
+ ClearCapFlag(POP3_UIDL_UNDEFINED);
+
+ if (!m_pop3ConData->command_succeeded)
+ {
+ m_pop3ConData->next_state = POP3_SEND_XTND_XLST_MSGID;
+ m_pop3ConData->pause_for_read = false;
+ ClearCapFlag(POP3_HAS_UIDL);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ return(0);
+ }
+ else
+ {
+ SetCapFlag(POP3_HAS_UIDL);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ uint32_t ln = 0;
+ bool pauseForMoreData = false;
+ nsresult rv;
+ char *line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if (pauseForMoreData || !line)
+ {
+ PR_Free(line);
+ m_pop3ConData->pause_for_read = true;
+ return ln;
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ /* parse the line returned from the list command
+ * it looks like
+ * #msg_number uidl
+ *
+ * list data is terminated by a ".CRLF" line
+ */
+ if (!PL_strcmp(line, "."))
+ {
+ // limit the list if fewer entries than given in STAT response
+ if (m_listpos < m_pop3ConData->number_of_messages)
+ m_pop3ConData->number_of_messages = m_listpos;
+ m_pop3ConData->list_done = true;
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ m_pop3ConData->pause_for_read = false;
+ PR_Free(line);
+ return(0);
+ }
+
+ char *newStr = line;
+ char *token = NS_strtok(" ", &newStr); // msg num
+ if (token)
+ {
+ int32_t msg_num = atol(token);
+ if (++m_listpos <= m_pop3ConData->number_of_messages)
+ {
+ const char *uid = NS_strtok(" ", &newStr); // UID
+ if (!uid)
+ /* This is bad. The server didn't give us a UIDL for this message.
+ I've seen this happen when somehow the mail spool has a message
+ that contains a header that reads "X-UIDL: \n". But how that got
+ there, I have no idea; must be a server bug. Or something. */
+ uid = "";
+
+ // seeking right entry, but try the one that should it be first
+ int32_t i;
+ if(m_pop3ConData->msg_info[m_listpos - 1].msgnum == msg_num)
+ i = m_listpos - 1;
+ else
+ for(i = 0; i < m_pop3ConData->number_of_messages &&
+ m_pop3ConData->msg_info[i].msgnum != msg_num; i++)
+ ;
+
+ // only if found a matching slot
+ if (i < m_pop3ConData->number_of_messages)
+ {
+ // to protect us from memory leak in case of getting a msg num twice
+ m_pop3ConData->msg_info[i].uidl = PL_strdup(uid);
+ if (!m_pop3ConData->msg_info[i].uidl)
+ {
+ PR_Free(line);
+ return MK_OUT_OF_MEMORY;
+ }
+ }
+ }
+ }
+ PR_Free(line);
+ return(0);
+}
+
+
+
+/* this function decides if we are going to do a
+ * normal RETR or a TOP. The first time, it also decides the total number
+ * of bytes we're probably going to get.
+ */
+int32_t nsPop3Protocol::GetMsg()
+{
+ int32_t popstateTimestamp = TimeInSecondsFromPRTime(PR_Now());
+
+ if (m_pop3ConData->last_accessed_msg >= m_pop3ConData->number_of_messages)
+ {
+ /* Oh, gee, we're all done. */
+ if(m_pop3ConData->msg_del_started)
+ {
+ if (!m_pop3ConData->only_uidl)
+ {
+ if (m_pop3ConData->only_check_for_new_mail)
+ m_nsIPop3Sink->SetBiffStateAndUpdateFE(m_pop3ConData->biffstate, m_pop3ConData->really_new_messages, true);
+ /* update old style biff */
+ else
+ m_nsIPop3Sink->SetBiffStateAndUpdateFE(nsIMsgFolder::nsMsgBiffState_NewMail, m_pop3ConData->really_new_messages, false);
+ }
+ m_nsIPop3Sink->EndMailDelivery(this);
+ }
+
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ return 0;
+ }
+
+ if (m_totalDownloadSize < 0)
+ {
+ /* First time. Figure out how many bytes we're about to get.
+ If we didn't get any message info, then we are going to get
+ everything, and it's easy. Otherwise, if we only want one
+ uidl, than that's the only one we'll get. Otherwise, go
+ through each message info, decide if we're going to get that
+ message, and add the number of bytes for it. When a message is too
+ large (per user's preferences) only add the size we are supposed
+ to get. */
+ m_pop3ConData->really_new_messages = 0;
+ m_pop3ConData->real_new_counter = 1;
+ if (m_pop3ConData->msg_info)
+ {
+ m_totalDownloadSize = 0;
+ for (int32_t i = 0; i < m_pop3ConData->number_of_messages; i++)
+ {
+ if (m_pop3ConData->only_uidl)
+ {
+ if (m_pop3ConData->msg_info[i].uidl &&
+ !PL_strcmp(m_pop3ConData->msg_info[i].uidl, m_pop3ConData->only_uidl))
+ {
+ m_totalDownloadSize = m_pop3ConData->msg_info[i].size;
+ m_pop3ConData->really_new_messages = 1;
+ // we are only getting one message
+ m_pop3ConData->real_new_counter = 1;
+ break;
+ }
+ continue;
+ }
+
+ char c = 0;
+ popstateTimestamp = TimeInSecondsFromPRTime(PR_Now());
+ if (m_pop3ConData->msg_info[i].uidl)
+ {
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->uidlinfo->hash,
+ m_pop3ConData->msg_info[i].uidl);
+ if (uidlEntry)
+ {
+ c = uidlEntry->status;
+ popstateTimestamp = uidlEntry->dateReceived;
+ }
+ }
+ if ((c == KEEP) && !m_pop3ConData->leave_on_server)
+ { /* This message has been downloaded but kept on server, we
+ * no longer want to keep it there */
+ if (!m_pop3ConData->newuidl)
+ {
+ m_pop3ConData->newuidl = PL_NewHashTable(20, PL_HashString, PL_CompareStrings,
+ PL_CompareValues, &gHashAllocOps, nullptr);
+ if (!m_pop3ConData->newuidl)
+ return MK_OUT_OF_MEMORY;
+ }
+ c = DELETE_CHAR;
+ // Mark message to be deleted in new table
+ put_hash(m_pop3ConData->newuidl,
+ m_pop3ConData->msg_info[i].uidl, DELETE_CHAR, popstateTimestamp);
+ // and old one too
+ put_hash(m_pop3ConData->uidlinfo->hash,
+ m_pop3ConData->msg_info[i].uidl, DELETE_CHAR, popstateTimestamp);
+ }
+ if ((c != KEEP) && (c != DELETE_CHAR) && (c != TOO_BIG))
+ { // message left on server
+ m_totalDownloadSize += m_pop3ConData->msg_info[i].size;
+ m_pop3ConData->really_new_messages++;
+ // a message we will really download
+ }
+ }
+ }
+ else
+ {
+ m_totalDownloadSize = m_totalFolderSize;
+ }
+ if (m_pop3ConData->only_check_for_new_mail)
+ {
+ if (m_totalDownloadSize > 0)
+ {
+ m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_NewMail;
+ m_nsIPop3Sink->SetBiffStateAndUpdateFE(nsIMsgFolder::nsMsgBiffState_NewMail, m_pop3ConData->really_new_messages, true);
+ }
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ return(0);
+ }
+ /* get the amount of available space on the drive
+ * and make sure there is enough
+ */
+ if (m_totalDownloadSize > 0) // skip all this if there aren't any messages
+ {
+ nsCOMPtr<nsIMsgFolder> folder;
+
+ // Get the current mailbox folder
+ NS_ENSURE_TRUE(m_nsIPop3Sink, -1);
+ nsresult rv = m_nsIPop3Sink->GetFolder(getter_AddRefs(folder));
+ if (NS_FAILED(rv))
+ return -1;
+
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
+ if (NS_FAILED(rv) || !mailnewsUrl)
+ return -1;
+
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ (void)mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
+
+ bool spaceNotAvailable = true;
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder(do_QueryInterface(folder, &rv));
+ if (NS_FAILED(rv) || !localFolder)
+ return -1;
+
+ // check if we have a reasonable amount of space left
+ rv = localFolder->WarnIfLocalFileTooBig(msgWindow, m_totalDownloadSize, &spaceNotAvailable);
+ if (NS_FAILED(rv) || spaceNotAvailable) {
+#ifdef DEBUG
+ printf("Not enough disk space! Raising error!\n");
+#endif
+ return -1;
+ }
+
+ // Here we know how many messages we're going to download, so let
+ // the pop3 sink know.
+ rv = m_nsIPop3Sink->SetMsgsToDownload(m_pop3ConData->really_new_messages);
+ }
+ }
+
+ /* Look at this message, and decide whether to ignore it, get it, just get
+ the TOP of it, or delete it. */
+
+ // if this is a message we've seen for the first time, we won't find it in
+ // m_pop3ConData-uidlinfo->hash. By default, we retrieve messages, unless they have a status,
+ // or are too big, in which case we figure out what to do.
+ if (m_prefAuthMethods != POP3_HAS_AUTH_USER && TestCapFlag(POP3_HAS_XSENDER))
+ m_pop3ConData->next_state = POP3_SEND_XSENDER;
+ else
+ m_pop3ConData->next_state = POP3_SEND_RETR;
+ m_pop3ConData->truncating_cur_msg = false;
+ m_pop3ConData->pause_for_read = false;
+ if (m_pop3ConData->msg_info)
+ {
+ Pop3MsgInfo* info = m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
+ if (m_pop3ConData->only_uidl)
+ {
+ if (info->uidl == NULL || PL_strcmp(info->uidl, m_pop3ConData->only_uidl))
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ else
+ m_pop3ConData->next_state = POP3_SEND_RETR;
+ }
+ else
+ {
+ char c = 0;
+ if (!m_pop3ConData->newuidl)
+ {
+ m_pop3ConData->newuidl = PL_NewHashTable(20, PL_HashString, PL_CompareStrings, PL_CompareValues, &gHashAllocOps, nullptr);
+ if (!m_pop3ConData->newuidl)
+ return MK_OUT_OF_MEMORY;
+ }
+ if (info->uidl)
+ {
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, info->uidl);
+ if (uidlEntry)
+ {
+ c = uidlEntry->status;
+ popstateTimestamp = uidlEntry->dateReceived;
+ }
+ }
+ if (c == DELETE_CHAR)
+ {
+ m_pop3ConData->next_state = POP3_SEND_DELE;
+ }
+ else if (c == KEEP)
+ {
+ // this is a message we've already downloaded and left on server;
+ // Advance to next message.
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ }
+ else if (c == FETCH_BODY)
+ {
+ m_pop3ConData->next_state = POP3_SEND_RETR;
+ PL_HashTableRemove (m_pop3ConData->uidlinfo->hash, (void*)info->uidl);
+ }
+ else if ((c != TOO_BIG) &&
+ (TestCapFlag(POP3_TOP_UNDEFINED | POP3_HAS_TOP)) &&
+ (m_pop3ConData->headers_only ||
+ ((m_pop3ConData->size_limit > 0) &&
+ (info->size > m_pop3ConData->size_limit) &&
+ !m_pop3ConData->only_uidl)) &&
+ info->uidl && *info->uidl)
+ {
+ // message is too big
+ m_pop3ConData->truncating_cur_msg = true;
+ m_pop3ConData->next_state = POP3_SEND_TOP;
+ put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG, popstateTimestamp);
+ }
+ else if (c == TOO_BIG)
+ {
+ /* message previously left on server, see if the max download size
+ has changed, because we may want to download the message this time
+ around. Otherwise ignore the message, we have the header. */
+ if ((m_pop3ConData->size_limit > 0) && (info->size <=
+ m_pop3ConData->size_limit))
+ PL_HashTableRemove (m_pop3ConData->uidlinfo->hash, (void*)info->uidl);
+ // remove from our table, and download
+ else
+ {
+ m_pop3ConData->truncating_cur_msg = true;
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ // ignore this message and get next one
+ put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG, popstateTimestamp);
+ }
+ }
+
+ if (m_pop3ConData->next_state != POP3_SEND_DELE &&
+ info->uidl)
+ {
+ /* This is a message we have decided to keep on the server. Notate
+ that now for the future. (Don't change the popstate file at all
+ if only_uidl is set; in that case, there might be brand new messages
+ on the server that we *don't* want to mark KEEP; we just want to
+ leave them around until the user next does a GetNewMail.) */
+
+ /* If this is a message we already know about (i.e., it was
+ in popstate.dat already), we need to maintain the original
+ date the message was downloaded. */
+ if (m_pop3ConData->truncating_cur_msg)
+ put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG, popstateTimestamp);
+ else
+ put_hash(m_pop3ConData->newuidl, info->uidl, KEEP, popstateTimestamp);
+ }
+ }
+ if (m_pop3ConData->next_state == POP3_GET_MSG)
+ m_pop3ConData->last_accessed_msg++;
+ // Make sure we check the next message next time!
+ }
+ return 0;
+}
+
+
+/* start retreiving just the first 20 lines
+ */
+int32_t nsPop3Protocol::SendTop()
+{
+ char * cmd = PR_smprintf( "TOP %ld %d" CRLF,
+ m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum,
+ m_pop3ConData->headers_only ? 0 : 20);
+ int32_t status = -1;
+ if (cmd)
+ {
+ m_pop3ConData->next_state_after_response = POP3_TOP_RESPONSE;
+ m_pop3ConData->cur_msg_size = -1;
+
+ /* zero the bytes received in message in preparation for
+ * the next
+ */
+ m_bytesInMsgReceived = 0;
+ status = Pop3SendData(cmd);
+ }
+ PR_Free(cmd);
+ return status;
+}
+
+/* send the xsender command
+ */
+int32_t nsPop3Protocol::SendXsender()
+{
+ char * cmd = PR_smprintf("XSENDER %ld" CRLF, m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
+ int32_t status = -1;
+ if (cmd)
+ {
+ m_pop3ConData->next_state_after_response = POP3_XSENDER_RESPONSE;
+ status = Pop3SendData(cmd);
+ PR_Free(cmd);
+ }
+ return status;
+}
+
+int32_t nsPop3Protocol::XsenderResponse()
+{
+ m_pop3ConData->seenFromHeader = false;
+ m_senderInfo = "";
+
+ if (m_pop3ConData->command_succeeded) {
+ if (m_commandResponse.Length() > 4)
+ m_senderInfo = m_commandResponse;
+ }
+ else {
+ ClearCapFlag(POP3_HAS_XSENDER);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ if (m_pop3ConData->truncating_cur_msg)
+ m_pop3ConData->next_state = POP3_SEND_TOP;
+ else
+ m_pop3ConData->next_state = POP3_SEND_RETR;
+ return 0;
+}
+
+/* retreive the whole message
+ */
+int32_t
+nsPop3Protocol::SendRetr()
+{
+
+ char * cmd = PR_smprintf("RETR %ld" CRLF, m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
+ int32_t status = -1;
+ if (cmd)
+ {
+ m_pop3ConData->next_state_after_response = POP3_RETR_RESPONSE;
+ m_pop3ConData->cur_msg_size = -1;
+
+
+ /* zero the bytes received in message in preparation for
+ * the next
+ */
+ m_bytesInMsgReceived = 0;
+
+ if (m_pop3ConData->only_uidl)
+ {
+ /* Display bytes if we're only downloading one message. */
+ PR_ASSERT(!m_pop3ConData->graph_progress_bytes_p);
+ UpdateProgressPercent(0, m_totalDownloadSize);
+ m_pop3ConData->graph_progress_bytes_p = true;
+ }
+ else
+ {
+ nsString finalString;
+ mozilla::DebugOnly<nsresult> rv =
+ FormatCounterString(NS_LITERAL_STRING("receivingMessages"),
+ m_pop3ConData->real_new_counter,
+ m_pop3ConData->really_new_messages,
+ finalString);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "couldn't format string");
+ if (mProgressEventSink) {
+ rv = mProgressEventSink->OnStatus(this, m_channelContext, NS_OK,
+ finalString.get());
+ NS_ASSERTION(NS_SUCCEEDED(rv), "dropping error result");
+ }
+ }
+
+ status = Pop3SendData(cmd);
+ } // if cmd
+ PR_Free(cmd);
+ return status;
+}
+
+/* digest the message
+ */
+int32_t
+nsPop3Protocol::RetrResponse(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ uint32_t buffer_size;
+ int32_t flags = 0;
+ char *uidl = NULL;
+ nsresult rv;
+ uint32_t status = 0;
+
+ if(m_pop3ConData->cur_msg_size == -1)
+ {
+ /* this is the beginning of a message
+ * get the response code and byte size
+ */
+ if(!m_pop3ConData->command_succeeded)
+ return Error("pop3RetrFailure");
+
+ /* a successful RETR response looks like: #num_bytes Junk
+ from TOP we only get the +OK and data
+ */
+ if (m_pop3ConData->truncating_cur_msg)
+ { /* TOP, truncated message */
+ flags |= nsMsgMessageFlags::Partial;
+ }
+ else
+ {
+ nsCString cmdResp(m_commandResponse);
+ char *newStr = cmdResp.BeginWriting();
+ char *num = NS_strtok( " ", &newStr);
+ if (num)
+ m_pop3ConData->cur_msg_size = atol(num);
+ m_commandResponse = newStr;
+ }
+
+ /* RETR complete message */
+ if (!m_senderInfo.IsEmpty())
+ flags |= nsMsgMessageFlags::SenderAuthed;
+
+ if(m_pop3ConData->cur_msg_size <= 0)
+ {
+ if (m_pop3ConData->msg_info)
+ m_pop3ConData->cur_msg_size = m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].size;
+ else
+ m_pop3ConData->cur_msg_size = 0;
+ }
+
+ if (m_pop3ConData->msg_info &&
+ m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].uidl)
+ uidl = m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].uidl;
+
+ m_pop3ConData->parsed_bytes = 0;
+ m_pop3ConData->pop3_size = m_pop3ConData->cur_msg_size;
+ m_pop3ConData->assumed_end = false;
+
+ m_pop3Server->GetDotFix(&m_pop3ConData->dot_fix);
+
+ MOZ_LOG(POP3LOGMODULE,LogLevel::Info,
+ (POP3LOG("Opening message stream: MSG_IncorporateBegin")));
+
+ /* open the message stream so we have someplace
+ * to put the data
+ */
+ m_pop3ConData->real_new_counter++;
+ /* (rb) count only real messages being downloaded */
+ rv = m_nsIPop3Sink->IncorporateBegin(uidl, m_url, flags,
+ &m_pop3ConData->msg_closure);
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("Done opening message stream!")));
+
+ if(!m_pop3ConData->msg_closure || NS_FAILED(rv))
+ return Error("pop3MessageWriteError");
+ }
+
+ m_pop3ConData->pause_for_read = true;
+
+ bool pauseForMoreData = false;
+ char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData, &rv, true);
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+ if (NS_FAILED(rv))
+ return -1;
+
+ buffer_size = status;
+
+ if (status == 0 && !line) // no bytes read in...
+ return (0);
+
+ if (m_pop3ConData->msg_closure) /* not done yet */
+ {
+ // buffer the line we just read in, and buffer all remaining lines in the stream
+ status = buffer_size;
+ do
+ {
+ if (m_pop3ConData->msg_closure)
+ {
+ rv = HandleLine(line, buffer_size);
+ if (NS_FAILED(rv))
+ return Error("pop3MessageWriteError");
+
+ // buffer_size already includes MSG_LINEBREAK_LEN so
+ // subtract and add CRLF
+ // but not really sure we always had CRLF in input since
+ // we also treat a single LF as line ending!
+ m_pop3ConData->parsed_bytes += buffer_size - MSG_LINEBREAK_LEN + 2;
+ }
+
+ // now read in the next line
+ PR_Free(line);
+ line = m_lineStreamBuffer->ReadNextLine(inputStream, buffer_size,
+ pauseForMoreData, &rv, true);
+ if (NS_FAILED(rv))
+ return -1;
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+ // buffer_size already includes MSG_LINEBREAK_LEN so
+ // subtract and add CRLF
+ // but not really sure we always had CRLF in input since
+ // we also treat a single LF as line ending!
+ status += buffer_size - MSG_LINEBREAK_LEN + 2;
+ } while (line);
+ }
+
+ buffer_size = status; // status holds # bytes we've actually buffered so far...
+
+ /* normal read. Yay! */
+ if ((int32_t) (m_bytesInMsgReceived + buffer_size) > m_pop3ConData->cur_msg_size)
+ buffer_size = m_pop3ConData->cur_msg_size - m_bytesInMsgReceived;
+
+ m_bytesInMsgReceived += buffer_size;
+ m_totalBytesReceived += buffer_size;
+
+ // *** jefft in case of the message size that server tells us is different
+ // from the actual message size
+ if (pauseForMoreData && m_pop3ConData->dot_fix &&
+ m_pop3ConData->assumed_end && m_pop3ConData->msg_closure)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ if (NS_SUCCEEDED(rv))
+ rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
+ rv = m_nsIPop3Sink->IncorporateComplete(msgWindow,
+ m_pop3ConData->truncating_cur_msg ? m_pop3ConData->cur_msg_size : 0);
+
+ // The following was added to prevent the loss of Data when we try
+ // and write to somewhere we don't have write access error to (See
+ // bug 62480)
+ // (Note: This is only a temp hack until the underlying XPCOM is
+ // fixed to return errors)
+
+ if (NS_FAILED(rv))
+ return Error((rv == NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD) ?
+ "pop3TmpDownloadError" :
+ "pop3MessageWriteError");
+
+ m_pop3ConData->msg_closure = nullptr;
+ }
+
+ if (!m_pop3ConData->msg_closure)
+ /* meaning _handle_line read ".\r\n" at end-of-msg */
+ {
+ m_pop3ConData->pause_for_read = false;
+
+ if (m_pop3ConData->truncating_cur_msg ||
+ m_pop3ConData->leave_on_server )
+ {
+ Pop3UidlEntry *uidlEntry = NULL;
+ Pop3MsgInfo* info = m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
+
+ /* Check for filter actions - FETCH or DELETE */
+ if ((m_pop3ConData->newuidl) && (info->uidl))
+ uidlEntry = (Pop3UidlEntry *)PL_HashTableLookup(m_pop3ConData->newuidl, info->uidl);
+
+ if (uidlEntry && uidlEntry->status == FETCH_BODY &&
+ m_pop3ConData->truncating_cur_msg)
+ {
+ /* A filter decided to retrieve this full msg.
+ Use GetMsg() so the popstate will update correctly,
+ but don't let this msg get counted twice. */
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ m_pop3ConData->real_new_counter--;
+ /* Make sure we don't try to come through here again. */
+ PL_HashTableRemove (m_pop3ConData->newuidl, (void*)info->uidl);
+ put_hash(m_pop3ConData->uidlinfo->hash, info->uidl, FETCH_BODY, uidlEntry->dateReceived);
+
+ } else if (uidlEntry && uidlEntry->status == DELETE_CHAR)
+ {
+ // A filter decided to delete this msg from the server
+ m_pop3ConData->next_state = POP3_SEND_DELE;
+ } else
+ {
+ /* We've retrieved all or part of this message, but we want to
+ keep it on the server. Go on to the next message. */
+ m_pop3ConData->last_accessed_msg++;
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ }
+ if (m_pop3ConData->only_uidl)
+ {
+ /* GetMsg didn't update this field. Do it now */
+ uidlEntry = (Pop3UidlEntry *)PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, m_pop3ConData->only_uidl);
+ NS_ASSERTION(uidlEntry, "uidl not found in uidlinfo");
+ if (uidlEntry)
+ put_hash(m_pop3ConData->uidlinfo->hash, m_pop3ConData->only_uidl, KEEP, uidlEntry->dateReceived);
+ }
+ }
+ else
+ {
+ m_pop3ConData->next_state = POP3_SEND_DELE;
+ }
+
+ /* if we didn't get the whole message add the bytes that we didn't get
+ to the bytes received part so that the progress percent stays sane.
+ */
+ if (m_bytesInMsgReceived < m_pop3ConData->cur_msg_size)
+ m_totalBytesReceived += (m_pop3ConData->cur_msg_size -
+ m_bytesInMsgReceived);
+ }
+
+ /* set percent done to portion of total bytes of all messages
+ that we're going to download. */
+ if (m_totalDownloadSize)
+ UpdateProgressPercent(m_totalBytesReceived, m_totalDownloadSize);
+
+ PR_Free(line);
+ return(0);
+}
+
+
+int32_t
+nsPop3Protocol::TopResponse(nsIInputStream* inputStream, uint32_t length)
+{
+ if (TestCapFlag(POP3_TOP_UNDEFINED))
+ {
+ ClearCapFlag(POP3_TOP_UNDEFINED);
+ if (m_pop3ConData->command_succeeded)
+ SetCapFlag(POP3_HAS_TOP);
+ else
+ ClearCapFlag(POP3_HAS_TOP);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ if(m_pop3ConData->cur_msg_size == -1 && /* first line after TOP command sent */
+ !m_pop3ConData->command_succeeded) /* and TOP command failed */
+ {
+ /* TOP doesn't work so we can't retrieve the first part of this msg.
+ So just go download the whole thing, and warn the user.
+
+ Note that the progress bar will not be accurate in this case.
+ Oops. #### */
+ m_pop3ConData->truncating_cur_msg = false;
+
+ nsString statusTemplate;
+ mLocalBundle->GetStringFromName(
+ u"pop3ServerDoesNotSupportTopCommand",
+ getter_Copies(statusTemplate));
+ if (!statusTemplate.IsEmpty())
+ {
+ nsAutoCString hostName;
+ char16_t * statusString = nullptr;
+ m_url->GetHost(hostName);
+
+ statusString = nsTextFormatter::smprintf(statusTemplate.get(), hostName.get());
+ UpdateStatusWithString(statusString);
+ nsTextFormatter::smprintf_free(statusString);
+ }
+
+ if (m_prefAuthMethods != POP3_HAS_AUTH_USER &&
+ TestCapFlag(POP3_HAS_XSENDER))
+ m_pop3ConData->next_state = POP3_SEND_XSENDER;
+ else
+ m_pop3ConData->next_state = POP3_SEND_RETR;
+ return(0);
+ }
+
+ /* If TOP works, we handle it in the same way as RETR. */
+ return RetrResponse(inputStream, length);
+}
+
+/* line is handed over as null-terminated string with MSG_LINEBREAK */
+nsresult
+nsPop3Protocol::HandleLine(char *line, uint32_t line_length)
+{
+ nsresult rv = NS_OK;
+
+ NS_ASSERTION(m_pop3ConData->msg_closure, "m_pop3ConData->msg_closure is null in nsPop3Protocol::HandleLine()");
+ if (!m_pop3ConData->msg_closure)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!m_senderInfo.IsEmpty() && !m_pop3ConData->seenFromHeader)
+ {
+ if (line_length > 6 && !PL_strncasecmp("From: ", line, 6))
+ {
+ m_pop3ConData->seenFromHeader = true;
+ if (PL_strstr(line, m_senderInfo.get()) == NULL)
+ m_nsIPop3Sink->SetSenderAuthedFlag(m_pop3ConData->msg_closure,
+ false);
+ }
+ }
+
+ // line contains only a single dot and linebreak -> message end
+ if (line_length == 1 + MSG_LINEBREAK_LEN && line[0] == '.')
+ {
+ m_pop3ConData->assumed_end = true; /* in case byte count from server is */
+ /* wrong, mark we may have had the end */
+ if (!m_pop3ConData->dot_fix || m_pop3ConData->truncating_cur_msg ||
+ (m_pop3ConData->parsed_bytes >= (m_pop3ConData->pop3_size -3)))
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ if (NS_SUCCEEDED(rv))
+ rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
+ rv = m_nsIPop3Sink->IncorporateComplete(msgWindow,
+ m_pop3ConData->truncating_cur_msg ? m_pop3ConData->cur_msg_size : 0);
+
+ // The following was added to prevent the loss of Data when we try
+ // and write to somewhere we don't have write access error to (See
+ // bug 62480)
+ // (Note: This is only a temp hack until the underlying XPCOM is
+ // fixed to return errors)
+
+ if (NS_FAILED(rv)) {
+ Error((rv == NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD) ?
+ "pop3TmpDownloadError" :
+ "pop3MessageWriteError");
+ return rv;
+ }
+
+ m_pop3ConData->msg_closure = nullptr;
+ return rv;
+ }
+ }
+ /* Check if the line begins with the termination octet. If so
+ and if another termination octet follows, we step over the
+ first occurence of it. */
+ else if (line_length > 1 && line[0] == '.' && line[1] == '.') {
+ line++;
+ line_length--;
+
+ }
+
+ return m_nsIPop3Sink->IncorporateWrite(line, line_length);
+}
+
+int32_t nsPop3Protocol::SendDele()
+{
+ /* increment the last accessed message since we have now read it
+ */
+ char * cmd = PR_smprintf("DELE %ld" CRLF, m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
+ m_pop3ConData->last_accessed_msg++;
+ int32_t status = -1;
+ if (cmd)
+ {
+ m_pop3ConData->next_state_after_response = POP3_DELE_RESPONSE;
+ status = Pop3SendData(cmd);
+ }
+ PR_Free(cmd);
+ return status;
+}
+
+int32_t nsPop3Protocol::DeleResponse()
+{
+ Pop3UidlHost *host = NULL;
+
+ host = m_pop3ConData->uidlinfo;
+
+ /* the return from the delete will come here
+ */
+ if(!m_pop3ConData->command_succeeded)
+ return Error("pop3DeleFailure");
+
+
+ /* ###chrisf
+ the delete succeeded. Write out state so that we
+ keep track of all the deletes which have not yet been
+ committed on the server. Flush this state upon successful
+ QUIT.
+
+ We will do this by adding each successfully deleted message id
+ to a list which we will write out to popstate.dat in
+ net_pop3_write_state().
+ */
+ if (host)
+ {
+ if (m_pop3ConData->msg_info &&
+ m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl)
+ {
+ if (m_pop3ConData->newuidl)
+ if (m_pop3ConData->leave_on_server)
+ {
+ PL_HashTableRemove(m_pop3ConData->newuidl, (void*)
+ m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl);
+ }
+ else
+ {
+ put_hash(m_pop3ConData->newuidl,
+ m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl, DELETE_CHAR, 0);
+ /* kill message in new hash table */
+ }
+ else
+ PL_HashTableRemove(host->hash,
+ (void*) m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl);
+ }
+ }
+
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ m_pop3ConData->pause_for_read = false;
+
+ return(0);
+}
+
+
+int32_t
+nsPop3Protocol::CommitState(bool remove_last_entry)
+{
+ // only use newuidl if we successfully finished looping through all the
+ // messages in the inbox.
+ if (m_pop3ConData->newuidl)
+ {
+ if (m_pop3ConData->last_accessed_msg >= m_pop3ConData->number_of_messages)
+ {
+ PL_HashTableDestroy(m_pop3ConData->uidlinfo->hash);
+ m_pop3ConData->uidlinfo->hash = m_pop3ConData->newuidl;
+ m_pop3ConData->newuidl = nullptr;
+ }
+ else
+ {
+ /* If we are leaving messages on the server, pull out the last
+ uidl from the hash, because it might have been put in there before
+ we got it into the database.
+ */
+ if (remove_last_entry && m_pop3ConData->msg_info &&
+ !m_pop3ConData->only_uidl && m_pop3ConData->newuidl->nentries > 0)
+ {
+ Pop3MsgInfo* info = m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
+ if (info && info->uidl)
+ {
+ mozilla::DebugOnly<bool> val = PL_HashTableRemove(m_pop3ConData->newuidl,
+ info->uidl);
+ NS_ASSERTION(val, "uidl not in hash table");
+ }
+ }
+
+ // Add the entries in newuidl to m_pop3ConData->uidlinfo->hash to keep
+ // track of the messages we *did* download in this session.
+ PL_HashTableEnumerateEntries(m_pop3ConData->newuidl, net_pop3_copy_hash_entries, (void *)m_pop3ConData->uidlinfo->hash);
+ }
+ }
+
+ if (!m_pop3ConData->only_check_for_new_mail)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIFile> mailDirectory;
+
+ // get the mail directory
+ nsCOMPtr<nsIMsgIncomingServer> server =
+ do_QueryInterface(m_pop3Server, &rv);
+ if (NS_FAILED(rv)) return -1;
+
+ rv = server->GetLocalPath(getter_AddRefs(mailDirectory));
+ if (NS_FAILED(rv)) return -1;
+
+ // write the state in the mail directory
+ net_pop3_write_state(m_pop3ConData->uidlinfo, mailDirectory.get());
+ }
+ return 0;
+}
+
+
+/* NET_process_Pop3 will control the state machine that
+ * loads messages from a pop3 server
+ *
+ * returns negative if the transfer is finished or error'd out
+ *
+ * returns zero or more if the transfer needs to be continued.
+ */
+nsresult nsPop3Protocol::ProcessProtocolState(nsIURI * url, nsIInputStream * aInputStream,
+ uint64_t sourceOffset, uint32_t aLength)
+{
+ int32_t status = 0;
+ bool urlStatusSet = false;
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_url);
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("Entering NET_ProcessPop3 %d"),
+ aLength));
+
+ m_pop3ConData->pause_for_read = false; /* already paused; reset */
+
+ if(m_username.IsEmpty())
+ {
+ // net_pop3_block = false;
+ Error("pop3UsernameUndefined");
+ return NS_MSG_SERVER_USERNAME_MISSING;
+ }
+
+ while(!m_pop3ConData->pause_for_read)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
+ (POP3LOG("Entering state: %d"), m_pop3ConData->next_state));
+
+ switch(m_pop3ConData->next_state)
+ {
+ case POP3_READ_PASSWORD:
+ // This is a separate state so that we're waiting for the user to type
+ // in a password while we don't actually have a connection to the pop
+ // server open; this saves us from having to worry about the server
+ // timing out on us while we wait for user input.
+ if (NS_FAILED(StartGetAsyncPassword(POP3_OBTAIN_PASSWORD_EARLY)))
+ status = -1;
+ break;
+ case POP3_FINISH_OBTAIN_PASSWORD_EARLY:
+ {
+ if (m_passwordResult.IsEmpty() || m_username.IsEmpty())
+ {
+ status = MK_POP3_PASSWORD_UNDEFINED;
+ m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_Unknown;
+ m_nsIPop3Sink->SetBiffStateAndUpdateFE(m_pop3ConData->biffstate, 0, false);
+
+ /* update old style biff */
+ m_pop3ConData->next_state = POP3_FREE;
+ m_pop3ConData->pause_for_read = false;
+ break;
+ }
+
+ m_pop3ConData->pause_for_read = false;
+ // we are already connected so just go on and send the username
+ if (m_prefAuthMethods == POP3_HAS_AUTH_USER)
+ {
+ m_currentAuthMethod = POP3_HAS_AUTH_USER;
+ m_pop3ConData->next_state = POP3_SEND_USERNAME;
+ }
+ else
+ {
+ if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
+ m_pop3ConData->next_state = POP3_SEND_AUTH;
+ else
+ m_pop3ConData->next_state = POP3_SEND_CAPA;
+ }
+ break;
+ }
+
+
+ case POP3_START_CONNECT:
+ {
+ m_pop3ConData->next_state = POP3_FINISH_CONNECT;
+ m_pop3ConData->pause_for_read = false;
+ break;
+ }
+
+ case POP3_FINISH_CONNECT:
+ {
+ m_pop3ConData->pause_for_read = false;
+ m_pop3ConData->next_state = POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE;
+ break;
+ }
+
+ case POP3_WAIT_FOR_RESPONSE:
+ status = WaitForResponse(aInputStream, aLength);
+ break;
+
+ case POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE:
+ {
+ status = WaitForStartOfConnectionResponse(aInputStream, aLength);
+
+ if(status)
+ {
+ if (m_prefAuthMethods == POP3_HAS_AUTH_USER)
+ {
+ m_currentAuthMethod = POP3_HAS_AUTH_USER;
+ m_pop3ConData->next_state = POP3_SEND_USERNAME;
+ }
+ else
+ {
+ if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
+ m_pop3ConData->next_state = POP3_SEND_AUTH;
+ else
+ m_pop3ConData->next_state = POP3_SEND_CAPA;
+ }
+ }
+
+ break;
+ }
+
+ case POP3_SEND_AUTH:
+ status = SendAuth();
+ break;
+
+ case POP3_AUTH_RESPONSE:
+ status = AuthResponse(aInputStream, aLength);
+ break;
+
+ case POP3_SEND_CAPA:
+ status = SendCapa();
+ break;
+
+ case POP3_CAPA_RESPONSE:
+ status = CapaResponse(aInputStream, aLength);
+ break;
+
+ case POP3_TLS_RESPONSE:
+ status = SendTLSResponse();
+ break;
+
+ case POP3_PROCESS_AUTH:
+ status = ProcessAuth();
+ break;
+
+ case POP3_NEXT_AUTH_STEP:
+ status = NextAuthStep();
+ break;
+
+ case POP3_AUTH_LOGIN:
+ status = AuthLogin();
+ break;
+
+ case POP3_AUTH_LOGIN_RESPONSE:
+ status = AuthLoginResponse();
+ break;
+
+ case POP3_AUTH_NTLM:
+ status = AuthNtlm();
+ break;
+
+ case POP3_AUTH_NTLM_RESPONSE:
+ status = AuthNtlmResponse();
+ break;
+
+ case POP3_AUTH_GSSAPI:
+ status = AuthGSSAPI();
+ break;
+
+ case POP3_AUTH_GSSAPI_FIRST:
+ UpdateStatus(u"hostContact");
+ status = AuthGSSAPIResponse(true);
+ break;
+
+ case POP3_AUTH_GSSAPI_STEP:
+ status = AuthGSSAPIResponse(false);
+ break;
+
+ case POP3_SEND_USERNAME:
+ if (NS_FAILED(StartGetAsyncPassword(POP3_OBTAIN_PASSWORD_BEFORE_USERNAME)))
+ status = -1;
+ break;
+
+ case POP3_OBTAIN_PASSWORD_BEFORE_USERNAME:
+ status = -1;
+ break;
+
+ case POP3_FINISH_OBTAIN_PASSWORD_BEFORE_USERNAME:
+ UpdateStatus(u"hostContact");
+ status = SendUsername();
+ break;
+
+ case POP3_SEND_PASSWORD:
+ if (NS_FAILED(StartGetAsyncPassword(POP3_OBTAIN_PASSWORD_BEFORE_PASSWORD)))
+ status = -1;
+ break;
+
+ case POP3_FINISH_OBTAIN_PASSWORD_BEFORE_PASSWORD:
+ status = SendPassword();
+ break;
+
+ case POP3_SEND_GURL:
+ status = SendGurl();
+ break;
+
+ case POP3_GURL_RESPONSE:
+ status = GurlResponse();
+ break;
+
+ case POP3_SEND_STAT:
+ status = SendStat();
+ break;
+
+ case POP3_GET_STAT:
+ status = GetStat();
+ break;
+
+ case POP3_SEND_LIST:
+ status = SendList();
+ break;
+
+ case POP3_GET_LIST:
+ status = GetList(aInputStream, aLength);
+ break;
+
+ case POP3_SEND_UIDL_LIST:
+ status = SendUidlList();
+ break;
+
+ case POP3_GET_UIDL_LIST:
+ status = GetUidlList(aInputStream, aLength);
+ break;
+
+ case POP3_SEND_XTND_XLST_MSGID:
+ status = SendXtndXlstMsgid();
+ break;
+
+ case POP3_GET_XTND_XLST_MSGID:
+ status = GetXtndXlstMsgid(aInputStream, aLength);
+ break;
+
+ case POP3_GET_MSG:
+ status = GetMsg();
+ break;
+
+ case POP3_SEND_TOP:
+ status = SendTop();
+ break;
+
+ case POP3_TOP_RESPONSE:
+ status = TopResponse(aInputStream, aLength);
+ break;
+
+ case POP3_SEND_XSENDER:
+ status = SendXsender();
+ break;
+
+ case POP3_XSENDER_RESPONSE:
+ status = XsenderResponse();
+ break;
+
+ case POP3_SEND_RETR:
+ status = SendRetr();
+ break;
+
+ case POP3_RETR_RESPONSE:
+ status = RetrResponse(aInputStream, aLength);
+ break;
+
+ case POP3_SEND_DELE:
+ status = SendDele();
+ break;
+
+ case POP3_DELE_RESPONSE:
+ status = DeleResponse();
+ break;
+
+ case POP3_SEND_QUIT:
+ /* attempt to send a server quit command. Since this means
+ everything went well, this is a good time to update the
+ status file and the FE's biff state.
+ */
+ if (!m_pop3ConData->only_uidl)
+ {
+ /* update old style biff */
+ if (!m_pop3ConData->only_check_for_new_mail)
+ {
+ /* We don't want to pop up a warning message any more (see
+ bug 54116), so instead we put the "no new messages" or
+ "retrieved x new messages"
+ in the status line. Unfortunately, this tends to be running
+ in a progress pane, so we try to get the real pane and
+ show the message there. */
+
+ if (m_totalDownloadSize <= 0)
+ {
+ UpdateStatus(u"noNewMessages");
+ /* There are no new messages. */
+ }
+ else
+ {
+ nsString statusString;
+ nsresult rv = FormatCounterString(NS_LITERAL_STRING("receivedMsgs"),
+ m_pop3ConData->real_new_counter - 1,
+ m_pop3ConData->really_new_messages,
+ statusString);
+ if (NS_SUCCEEDED(rv))
+ UpdateStatusWithString(statusString.get());
+ }
+ }
+ }
+
+ status = Pop3SendData("QUIT" CRLF);
+ m_pop3ConData->next_state = POP3_WAIT_FOR_RESPONSE;
+ m_pop3ConData->next_state_after_response = POP3_QUIT_RESPONSE;
+ break;
+
+ case POP3_QUIT_RESPONSE:
+ if(m_pop3ConData->command_succeeded)
+ {
+ /* the QUIT succeeded. We can now flush the state in popstate.dat which
+ keeps track of any uncommitted DELE's */
+
+ /* clear the hash of all our uncommitted deletes */
+ if (!m_pop3ConData->leave_on_server && m_pop3ConData->newuidl)
+ {
+ PL_HashTableEnumerateEntries(m_pop3ConData->newuidl,
+ net_pop3_remove_messages_marked_delete,
+ (void *)m_pop3ConData);
+ }
+ m_pop3ConData->next_state = POP3_DONE;
+ }
+ else
+ {
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ }
+ break;
+
+ case POP3_DONE:
+ CommitState(false);
+ m_pop3ConData->urlStatus = NS_OK;
+ urlStatusSet = true;
+ m_pop3ConData->next_state = POP3_FREE;
+ break;
+
+ case POP3_ERROR_DONE:
+ /* write out the state */
+ if(m_pop3ConData->list_done)
+ CommitState(true);
+
+ if(m_pop3ConData->msg_closure)
+ {
+ m_nsIPop3Sink->IncorporateAbort(m_pop3ConData->only_uidl != nullptr);
+ m_pop3ConData->msg_closure = NULL;
+ m_nsIPop3Sink->AbortMailDelivery(this);
+ }
+
+ if(m_pop3ConData->msg_del_started)
+ {
+ nsString statusString;
+ nsresult rv = FormatCounterString(NS_LITERAL_STRING("receivedMsgs"),
+ m_pop3ConData->real_new_counter - 1,
+ m_pop3ConData->really_new_messages,
+ statusString);
+ if (NS_SUCCEEDED(rv))
+ UpdateStatusWithString(statusString.get());
+
+ NS_ASSERTION (!TestFlag(POP3_PASSWORD_FAILED), "POP3_PASSWORD_FAILED set when del_started");
+ m_nsIPop3Sink->AbortMailDelivery(this);
+ }
+ { // this brace is to avoid compiler error about vars in switch case.
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+
+ if (mailnewsurl)
+ mailnewsurl->GetMsgWindow(getter_AddRefs(msgWindow));
+ // no msgWindow means no re-prompt, so treat as error.
+ if (TestFlag(POP3_PASSWORD_FAILED) && msgWindow)
+ {
+ // We get here because the password was wrong.
+ if (!m_socketIsOpen && mailnewsurl)
+ {
+ // The server dropped the connection, so we're going
+ // to re-run the url.
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("need to rerun url because connection dropped during auth")));
+ m_needToRerunUrl = true;
+ return NS_OK;
+ }
+ m_pop3ConData->next_state = POP3_READ_PASSWORD;
+ m_pop3ConData->command_succeeded = true;
+ status = 0;
+ break;
+ }
+ else
+ /* Else we got a "real" error, so finish up. */
+ m_pop3ConData->next_state = POP3_FREE;
+ }
+ m_pop3ConData->urlStatus = NS_ERROR_FAILURE;
+ urlStatusSet = true;
+ m_pop3ConData->pause_for_read = false;
+ break;
+
+ case POP3_FREE:
+ {
+ UpdateProgressPercent(0,0); // clear out the progress meter
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Clearing server busy in POP3_FREE")));
+ server->SetServerBusy(false); // the server is now not busy
+ }
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Clearing running protocol in POP3_FREE")));
+ CloseSocket();
+ m_pop3Server->SetRunningProtocol(nullptr);
+ if (mailnewsurl && urlStatusSet)
+ mailnewsurl->SetUrlState(false, m_pop3ConData->urlStatus);
+
+ m_url = nullptr;
+ return NS_OK;
+ }
+ default:
+ NS_ERROR("Got to unexpected state in nsPop3Protocol::ProcessProtocolState");
+ status = -1;
+ } /* end switch */
+
+ if((status < 0) && m_pop3ConData->next_state != POP3_FREE)
+ {
+ m_pop3ConData->pause_for_read = false;
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ }
+
+ } /* end while */
+
+ return NS_OK;
+
+}
+
+NS_IMETHODIMP nsPop3Protocol::MarkMessages(nsTArray<Pop3UidlEntry*> *aUIDLArray)
+{
+ NS_ENSURE_ARG_POINTER(aUIDLArray);
+ uint32_t count = aUIDLArray->Length();
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ bool changed;
+ if (m_pop3ConData->newuidl)
+ MarkMsgInHashTable(m_pop3ConData->newuidl, aUIDLArray->ElementAt(i), &changed);
+ if (m_pop3ConData->uidlinfo)
+ MarkMsgInHashTable(m_pop3ConData->uidlinfo->hash, aUIDLArray->ElementAt(i), &changed);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Protocol::CheckMessage(const char *aUidl, bool *aBool)
+{
+ Pop3UidlEntry *uidlEntry = nullptr;
+
+ if (aUidl)
+ {
+ if (m_pop3ConData->newuidl)
+ uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->newuidl, aUidl);
+ else if (m_pop3ConData->uidlinfo)
+ uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, aUidl);
+ }
+
+ *aBool = uidlEntry ? true : false;
+ return NS_OK;
+}
+
+
+/* Function for finding an APOP Timestamp and simple check
+ it for its validity. If returning NS_OK m_ApopTimestamp
+ contains the validated substring of m_commandResponse. */
+nsresult nsPop3Protocol::GetApopTimestamp()
+{
+ int32_t startMark = m_commandResponse.Length(), endMark = -1;
+
+ while (true)
+ {
+ // search for previous <
+ if ((startMark = MsgRFindChar(m_commandResponse, '<', startMark - 1)) < 0)
+ return NS_ERROR_FAILURE;
+
+ // search for next >
+ if ((endMark = m_commandResponse.FindChar('>', startMark)) < 0)
+ continue;
+
+ // look for an @ between start and end as a raw test
+ int32_t at = m_commandResponse.FindChar('@', startMark);
+ if (at < 0 || at >= endMark)
+ continue;
+
+ // now test if sub only consists of chars in ASCII range
+ nsCString sub(Substring(m_commandResponse, startMark, endMark - startMark + 1));
+ if (NS_IsAscii(sub.get()))
+ {
+ // set m_ApopTimestamp to the validated substring
+ m_ApopTimestamp.Assign(sub);
+ break;
+ }
+ }
+
+ return NS_OK;
+}