1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 *
7 * This Original Code has been modified by IBM Corporation. Modifications made
8 * by IBM described herein are Copyright (c) International Business Machines
9 * Corporation, 2000. Modifications to Mozilla code or documentation identified
10 * per MPL Section 3.3
11 *
12 * Jason Eager <jce2@po.cwru.edu>
13 *
14 * Date Modified by Description of modification
15 * 04/20/2000 IBM Corp. OS/2 VisualAge build.
16 * 06/07/2000 Jason Eager Added check for out of disk space
17 */
18
19 #include "nscore.h"
20 #include "msgCore.h" // precompiled header...
21 #include "nsNetUtil.h"
22 #include "nspr.h"
23 #include "plbase64.h"
24 #include "nsIMsgMailNewsUrl.h"
25 #include "nsISafeOutputStream.h"
26 #include "nsPop3Protocol.h"
27 #include "nsIPop3URL.h"
28 #include "MailNewsTypes.h"
29 #include "nsString.h"
30 #include "nsIPrompt.h"
31 #include "nsIMsgIncomingServer.h"
32 #include "nsTextFormatter.h"
33 #include "nsCOMPtr.h"
34 #include "nsIMsgWindow.h"
35 #include "nsIMsgFolder.h" // TO include biffState enum. Change to bool later...
36 #include "nsIMsgLocalMailFolder.h"
37 #include "nsIDocShell.h"
38 #include "nsMsgUtils.h"
39 #include "nsISocketTransport.h"
40 #include "nsISSLSocketControl.h"
41 #include "nsITransportSecurityInfo.h"
42 #include "nsILineInputStream.h"
43 #include "nsIInterfaceRequestor.h"
44 #include "nsICancelable.h"
45 #include "nsMsgMessageFlags.h"
46 #include "nsMsgBaseCID.h"
47 #include "nsIProxyInfo.h"
48 #include "nsCRT.h"
49 #include "mozilla/Services.h"
50 #include "mozilla/Logging.h"
51 #include "mozilla/Attributes.h"
52 #include "mozilla/Preferences.h"
53
54 using namespace mozilla;
55
56 LazyLogModule POP3LOGMODULE("POP3");
57 #define POP3LOG(str) "[this=%p] " str, this
58
net_pop3_remove_messages_marked_delete(PLHashEntry * he,int msgindex,void * arg)59 static int net_pop3_remove_messages_marked_delete(PLHashEntry* he, int msgindex,
60 void* arg) {
61 Pop3UidlEntry* uidlEntry = (Pop3UidlEntry*)he->value;
62 return (uidlEntry->status == DELETE_CHAR) ? HT_ENUMERATE_REMOVE
63 : HT_ENUMERATE_NEXT;
64 }
65
TimeInSecondsFromPRTime(PRTime prTime)66 uint32_t TimeInSecondsFromPRTime(PRTime prTime) {
67 return (uint32_t)(prTime / PR_USEC_PER_SEC);
68 }
69
put_hash(PLHashTable * table,const char * key,char value,uint32_t dateReceived)70 static void put_hash(PLHashTable* table, const char* key, char value,
71 uint32_t dateReceived) {
72 // don't put not used slots or empty uid into hash
73 if (key && *key) {
74 Pop3UidlEntry* tmp = PR_NEWZAP(Pop3UidlEntry);
75 if (tmp) {
76 tmp->uidl = PL_strdup(key);
77 if (tmp->uidl) {
78 tmp->dateReceived = dateReceived;
79 tmp->status = value;
80 PL_HashTableAdd(table, (const void*)tmp->uidl, (void*)tmp);
81 } else
82 PR_Free(tmp);
83 }
84 }
85 }
86
net_pop3_copy_hash_entries(PLHashEntry * he,int msgindex,void * arg)87 static int net_pop3_copy_hash_entries(PLHashEntry* he, int msgindex,
88 void* arg) {
89 Pop3UidlEntry* uidlEntry = (Pop3UidlEntry*)he->value;
90 put_hash((PLHashTable*)arg, uidlEntry->uidl, uidlEntry->status,
91 uidlEntry->dateReceived);
92 return HT_ENUMERATE_NEXT;
93 }
94
AllocUidlTable(void *,size_t size)95 static void* AllocUidlTable(void* /* pool */, size_t size) {
96 return PR_MALLOC(size);
97 }
98
FreeUidlTable(void *,void * item)99 static void FreeUidlTable(void* /* pool */, void* item) { PR_Free(item); }
100
AllocUidlInfo(void * pool,const void * key)101 static PLHashEntry* AllocUidlInfo(void* pool, const void* key) {
102 return PR_NEWZAP(PLHashEntry);
103 }
104
FreeUidlInfo(void *,PLHashEntry * he,unsigned flag)105 static void FreeUidlInfo(void* /* pool */, PLHashEntry* he, unsigned flag) {
106 if (flag == HT_FREE_ENTRY) {
107 Pop3UidlEntry* uidlEntry = (Pop3UidlEntry*)he->value;
108 if (uidlEntry) {
109 PR_Free(uidlEntry->uidl);
110 PR_Free(uidlEntry);
111 }
112 PR_Free(he);
113 }
114 }
115
116 static PLHashAllocOps gHashAllocOps = {AllocUidlTable, FreeUidlTable,
117 AllocUidlInfo, FreeUidlInfo};
118
net_pop3_load_state(const char * searchhost,const char * searchuser,nsIFile * mailDirectory)119 static Pop3UidlHost* net_pop3_load_state(const char* searchhost,
120 const char* searchuser,
121 nsIFile* mailDirectory) {
122 Pop3UidlHost* result = nullptr;
123 Pop3UidlHost* current = nullptr;
124 Pop3UidlHost* tmp;
125
126 result = PR_NEWZAP(Pop3UidlHost);
127 if (!result) return nullptr;
128 result->host = PL_strdup(searchhost);
129 result->user = PL_strdup(searchuser);
130 result->hash = PL_NewHashTable(20, PL_HashString, PL_CompareStrings,
131 PL_CompareValues, &gHashAllocOps, nullptr);
132
133 if (!result->host || !result->user || !result->hash) {
134 PR_Free(result->host);
135 PR_Free(result->user);
136 if (result->hash) PL_HashTableDestroy(result->hash);
137 PR_Free(result);
138 return nullptr;
139 }
140
141 nsCOMPtr<nsIFile> popState;
142 mailDirectory->Clone(getter_AddRefs(popState));
143 if (!popState) return nullptr;
144 popState->AppendNative("popstate.dat"_ns);
145
146 nsCOMPtr<nsIInputStream> fileStream;
147 nsresult rv =
148 NS_NewLocalFileInputStream(getter_AddRefs(fileStream), popState);
149 // It is OK if the file doesn't exist. No state is stored yet.
150 // Return empty list without warning.
151 if (rv == NS_ERROR_FILE_NOT_FOUND) return result;
152 // Warn for other errors.
153 NS_ENSURE_SUCCESS(rv, result);
154
155 nsCOMPtr<nsILineInputStream> lineInputStream(
156 do_QueryInterface(fileStream, &rv));
157 NS_ENSURE_SUCCESS(rv, result);
158
159 bool more = true;
160 nsCString line;
161
162 while (more && NS_SUCCEEDED(rv)) {
163 lineInputStream->ReadLine(line, &more);
164 if (line.IsEmpty()) continue;
165 char firstChar = line.CharAt(0);
166 if (firstChar == '#') continue;
167 if (firstChar == '*') {
168 /* It's a host&user line. */
169 current = nullptr;
170 char* lineBuf =
171 line.BeginWriting() + 1; // ok because we know the line isn't empty
172 char* host = NS_strtok(" \t\r\n", &lineBuf);
173 /* without space to also get realnames - see bug 225332 */
174 char* user = NS_strtok("\t\r\n", &lineBuf);
175 if (!host || !user) continue;
176 for (tmp = result; tmp; tmp = tmp->next) {
177 if (!strcmp(host, tmp->host) && !strcmp(user, tmp->user)) {
178 current = tmp;
179 break;
180 }
181 }
182 if (!current) {
183 current = PR_NEWZAP(Pop3UidlHost);
184 if (current) {
185 current->host = strdup(host);
186 current->user = strdup(user);
187 current->hash =
188 PL_NewHashTable(20, PL_HashString, PL_CompareStrings,
189 PL_CompareValues, &gHashAllocOps, nullptr);
190 if (!current->host || !current->user || !current->hash) {
191 PR_Free(current->host);
192 PR_Free(current->user);
193 if (current->hash) PL_HashTableDestroy(current->hash);
194 PR_Free(current);
195 } else {
196 current->next = result->next;
197 result->next = current;
198 }
199 }
200 }
201 } else {
202 /* It's a line with a UIDL on it. */
203 if (current) {
204 for (int32_t pos = line.FindChar('\t'); pos != -1;
205 pos = line.FindChar('\t', pos))
206 line.Replace(pos, 1, ' ');
207
208 nsTArray<nsCString> lineElems;
209 ParseString(line, ' ', lineElems);
210 if (lineElems.Length() < 2) continue;
211 nsCString* flags = &lineElems[0];
212 nsCString* uidl = &lineElems[1];
213 uint32_t dateReceived = TimeInSecondsFromPRTime(
214 PR_Now()); // if we don't find a date str, assume now.
215 if (lineElems.Length() > 2) dateReceived = atoi(lineElems[2].get());
216 if (!flags->IsEmpty() && !uidl->IsEmpty()) {
217 char flag = flags->CharAt(0);
218 if ((flag == KEEP) || (flag == DELETE_CHAR) || (flag == TOO_BIG) ||
219 (flag == FETCH_BODY)) {
220 put_hash(current->hash, uidl->get(), flag, dateReceived);
221 } else {
222 NS_ASSERTION(false, "invalid flag in popstate.dat");
223 }
224 }
225 }
226 }
227 }
228 fileStream->Close();
229
230 return result;
231 }
232
hash_clear_mapper(PLHashEntry * he,int msgindex,void * arg)233 static int hash_clear_mapper(PLHashEntry* he, int msgindex, void* arg) {
234 Pop3UidlEntry* uidlEntry = (Pop3UidlEntry*)he->value;
235 PR_Free(uidlEntry->uidl);
236 PR_Free(uidlEntry);
237 he->value = nullptr;
238
239 return HT_ENUMERATE_REMOVE;
240 }
241
hash_empty_mapper(PLHashEntry * he,int msgindex,void * arg)242 static int hash_empty_mapper(PLHashEntry* he, int msgindex, void* arg) {
243 *((bool*)arg) = false;
244 return HT_ENUMERATE_STOP;
245 }
246
hash_empty(PLHashTable * hash)247 static bool hash_empty(PLHashTable* hash) {
248 bool result = true;
249 PL_HashTableEnumerateEntries(hash, hash_empty_mapper, (void*)&result);
250 return result;
251 }
252
net_pop3_write_mapper(PLHashEntry * he,int msgindex,void * arg)253 static int net_pop3_write_mapper(PLHashEntry* he, int msgindex, void* arg) {
254 nsIOutputStream* file = (nsIOutputStream*)arg;
255 Pop3UidlEntry* uidlEntry = (Pop3UidlEntry*)he->value;
256 NS_ASSERTION(
257 (uidlEntry->status == KEEP) || (uidlEntry->status == DELETE_CHAR) ||
258 (uidlEntry->status == FETCH_BODY) || (uidlEntry->status == TOO_BIG),
259 "invalid status");
260 char* tmpBuffer =
261 PR_smprintf("%c %s %d" MSG_LINEBREAK, uidlEntry->status,
262 (char*)uidlEntry->uidl, uidlEntry->dateReceived);
263 PR_ASSERT(tmpBuffer);
264 uint32_t numBytesWritten;
265 file->Write(tmpBuffer, strlen(tmpBuffer), &numBytesWritten);
266 PR_Free(tmpBuffer);
267 return HT_ENUMERATE_NEXT;
268 }
269
net_pop3_delete_old_msgs_mapper(PLHashEntry * he,int msgindex,void * arg)270 static int net_pop3_delete_old_msgs_mapper(PLHashEntry* he, int msgindex,
271 void* arg) {
272 PRTime cutOffDate = (PRTime)arg;
273 Pop3UidlEntry* uidlEntry = (Pop3UidlEntry*)he->value;
274 if (uidlEntry->dateReceived < cutOffDate)
275 uidlEntry->status = DELETE_CHAR; // mark for deletion
276 return HT_ENUMERATE_NEXT;
277 }
278
net_pop3_write_state(Pop3UidlHost * host,nsIFile * mailDirectory)279 static void net_pop3_write_state(Pop3UidlHost* host, nsIFile* mailDirectory) {
280 int32_t len = 0;
281 nsCOMPtr<nsIFile> popState;
282
283 mailDirectory->Clone(getter_AddRefs(popState));
284 if (!popState) return;
285 popState->AppendNative("popstate.dat"_ns);
286
287 nsCOMPtr<nsIOutputStream> fileOutputStream;
288 nsresult rv = MsgNewSafeBufferedFileOutputStream(
289 getter_AddRefs(fileOutputStream), popState, -1, 00600);
290 if (NS_FAILED(rv)) return;
291
292 const char tmpBuffer[] =
293 "# POP3 State File" MSG_LINEBREAK
294 "# This is a generated file! Do not edit." MSG_LINEBREAK MSG_LINEBREAK;
295
296 uint32_t numBytesWritten;
297 fileOutputStream->Write(tmpBuffer, strlen(tmpBuffer), &numBytesWritten);
298
299 for (; host && (len >= 0); host = host->next) {
300 if (!hash_empty(host->hash)) {
301 fileOutputStream->Write("*", 1, &numBytesWritten);
302 fileOutputStream->Write(host->host, strlen(host->host), &numBytesWritten);
303 fileOutputStream->Write(" ", 1, &numBytesWritten);
304 fileOutputStream->Write(host->user, strlen(host->user), &numBytesWritten);
305 fileOutputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN,
306 &numBytesWritten);
307 PL_HashTableEnumerateEntries(host->hash, net_pop3_write_mapper,
308 (void*)fileOutputStream);
309 }
310 }
311 nsCOMPtr<nsISafeOutputStream> safeStream =
312 do_QueryInterface(fileOutputStream);
313 NS_ASSERTION(safeStream, "expected a safe output stream!");
314 if (safeStream) {
315 rv = safeStream->Finish();
316 if (NS_FAILED(rv)) {
317 NS_WARNING("failed to save pop state! possible data loss");
318 }
319 }
320 }
321
net_pop3_free_state(Pop3UidlHost * host)322 static void net_pop3_free_state(Pop3UidlHost* host) {
323 Pop3UidlHost* h;
324 while (host) {
325 h = host->next;
326 PR_Free(host->host);
327 PR_Free(host->user);
328 PL_HashTableDestroy(host->hash);
329 PR_Free(host);
330 host = h;
331 }
332 }
333
334 /*
335 Look for a specific UIDL string in our hash tables, if we have it then we
336 need to mark the message for deletion so that it can be deleted later. If the
337 uidl of the message is not found, then the message was downloaded completely
338 and already deleted from the server. So this only applies to messages kept on
339 the server or too big for download.
340 */
341 /* static */
MarkMsgInHashTable(PLHashTable * hashTable,const Pop3UidlEntry * uidlE,bool * changed)342 void nsPop3Protocol::MarkMsgInHashTable(PLHashTable* hashTable,
343 const Pop3UidlEntry* uidlE,
344 bool* changed) {
345 if (uidlE->uidl) {
346 Pop3UidlEntry* uidlEntry =
347 (Pop3UidlEntry*)PL_HashTableLookup(hashTable, uidlE->uidl);
348 if (uidlEntry) {
349 if (uidlEntry->status != uidlE->status) {
350 uidlEntry->status = uidlE->status;
351 *changed = true;
352 }
353 }
354 }
355 }
356
357 /* static */
MarkMsgForHost(const char * hostName,const char * userName,nsIFile * mailDirectory,nsTArray<Pop3UidlEntry * > & UIDLArray)358 nsresult nsPop3Protocol::MarkMsgForHost(const char* hostName,
359 const char* userName,
360 nsIFile* mailDirectory,
361 nsTArray<Pop3UidlEntry*>& UIDLArray) {
362 if (!hostName || !userName || !mailDirectory) return NS_ERROR_NULL_POINTER;
363
364 Pop3UidlHost* uidlHost =
365 net_pop3_load_state(hostName, userName, mailDirectory);
366 if (!uidlHost) return NS_ERROR_OUT_OF_MEMORY;
367
368 bool changed = false;
369
370 uint32_t count = UIDLArray.Length();
371 for (uint32_t i = 0; i < count; i++) {
372 MarkMsgInHashTable(uidlHost->hash, UIDLArray[i], &changed);
373 }
374
375 if (changed) net_pop3_write_state(uidlHost, mailDirectory);
376 net_pop3_free_state(uidlHost);
377 return NS_OK;
378 }
379
380 // nsPop3Protocol class implementation
381
NS_IMPL_ADDREF_INHERITED(nsPop3Protocol,nsMsgProtocol)382 NS_IMPL_ADDREF_INHERITED(nsPop3Protocol, nsMsgProtocol)
383 NS_IMPL_RELEASE_INHERITED(nsPop3Protocol, nsMsgProtocol)
384
385 NS_INTERFACE_MAP_BEGIN(nsPop3Protocol)
386 NS_INTERFACE_MAP_ENTRY(nsIPop3Protocol)
387 NS_INTERFACE_MAP_ENTRY(msgIOAuth2ModuleListener)
388 NS_INTERFACE_MAP_ENTRY(nsIMsgAsyncPromptListener)
389 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
390 NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol)
391
392 nsPop3Protocol::nsPop3Protocol(nsIURI* aURL)
393 : nsMsgProtocol(aURL),
394 m_bytesInMsgReceived(0),
395 m_totalFolderSize(0),
396 m_totalDownloadSize(0),
397 m_totalBytesReceived(0),
398 m_pop3ConData(nullptr) {}
399
Initialize(nsIURI * aURL)400 nsresult nsPop3Protocol::Initialize(nsIURI* aURL) {
401 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Initialize()")));
402
403 m_pop3ConData = (Pop3ConData*)PR_NEWZAP(Pop3ConData);
404 if (!m_pop3ConData) return NS_ERROR_OUT_OF_MEMORY;
405
406 m_totalBytesReceived = 0;
407 m_bytesInMsgReceived = 0;
408 m_totalFolderSize = 0;
409 m_totalDownloadSize = 0;
410 m_totalBytesReceived = 0;
411 m_tlsEnabled = false;
412 m_socketType = nsMsgSocketType::trySTARTTLS;
413 m_prefAuthMethods = POP3_AUTH_MECH_UNDEFINED;
414 m_failedAuthMethods = 0;
415 m_password_already_sent = false;
416 m_currentAuthMethod = POP3_AUTH_MECH_UNDEFINED;
417 m_needToRerunUrl = false;
418
419 m_url = aURL;
420
421 m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, true);
422
423 nsCOMPtr<nsIStringBundleService> bundleService =
424 mozilla::services::GetStringBundleService();
425 NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
426 return bundleService->CreateBundle(
427 "chrome://messenger/locale/localMsgs.properties",
428 getter_AddRefs(mLocalBundle));
429 }
430
431 // nsIProtocolProxyCallback
432 NS_IMETHODIMP
OnProxyAvailable(nsICancelable * aRequest,nsIChannel * aChannel,nsIProxyInfo * aProxyInfo,nsresult aStatus)433 nsPop3Protocol::OnProxyAvailable(nsICancelable* aRequest, nsIChannel* aChannel,
434 nsIProxyInfo* aProxyInfo, nsresult aStatus) {
435 // If we're called with NS_BINDING_ABORTED, we came here via Cancel().
436 // Otherwise, no checking of 'aStatus' here, see
437 // nsHttpChannel::OnProxyAvailable(). Status is non-fatal and we just kick on.
438 if (aStatus == NS_BINDING_ABORTED) return NS_ERROR_FAILURE;
439
440 nsresult rv = InitializeInternal(aProxyInfo);
441 if (NS_FAILED(rv)) {
442 Cancel(rv);
443 return rv;
444 }
445
446 rv = LoadUrlInternal(m_url);
447 if (NS_FAILED(rv)) {
448 Cancel(rv);
449 }
450
451 return rv;
452 }
453
InitializeInternal(nsIProxyInfo * aProxyInfo)454 nsresult nsPop3Protocol::InitializeInternal(nsIProxyInfo* aProxyInfo) {
455 nsresult rv;
456 m_proxyRequest = nullptr;
457
458 NS_ENSURE_TRUE(m_url, NS_ERROR_NOT_INITIALIZED);
459
460 // extract out message feedback if there is any.
461 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url);
462 if (mailnewsUrl) {
463 nsCOMPtr<nsIMsgIncomingServer> server;
464 mailnewsUrl->GetServer(getter_AddRefs(server));
465 NS_ENSURE_TRUE(server, NS_MSG_INVALID_OR_MISSING_SERVER);
466
467 // Query for OAuth2 support. If the POP server preferences don't allow
468 // for OAuth2, then don't carry around the OAuth2 module any longer
469 // since we won't need it.
470 mOAuth2Support = do_CreateInstance(MSGIOAUTH2MODULE_CONTRACTID);
471 if (mOAuth2Support) {
472 bool supportsOAuth = false;
473 mOAuth2Support->InitFromMail(server, &supportsOAuth);
474 if (!supportsOAuth) mOAuth2Support = nullptr;
475 }
476
477 rv = server->GetSocketType(&m_socketType);
478 NS_ENSURE_SUCCESS(rv, rv);
479
480 int32_t authMethod = 0;
481 rv = server->GetAuthMethod(&authMethod);
482 NS_ENSURE_SUCCESS(rv, rv);
483 InitPrefAuthMethods(authMethod);
484
485 m_pop3Server = do_QueryInterface(server);
486 if (m_pop3Server)
487 m_pop3Server->GetPop3CapabilityFlags(&m_pop3ConData->capability_flags);
488 }
489
490 // When we are making a secure connection, we need to make sure that we
491 // pass an interface requestor down to the socket transport so that PSM can
492 // retrieve a nsIPrompt instance if needed.
493 nsCOMPtr<nsIInterfaceRequestor> ir;
494 if (m_socketType != nsMsgSocketType::plain) {
495 nsCOMPtr<nsIMsgWindow> msgwin;
496 if (mailnewsUrl) mailnewsUrl->GetMsgWindow(getter_AddRefs(msgwin));
497 if (!msgwin) GetTopmostMsgWindow(getter_AddRefs(msgwin));
498 if (msgwin) {
499 nsCOMPtr<nsIDocShell> docshell;
500 msgwin->GetRootDocShell(getter_AddRefs(docshell));
501 ir = do_QueryInterface(docshell);
502 nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
503 msgwin->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
504 if (notificationCallbacks) {
505 nsCOMPtr<nsIInterfaceRequestor> aggregrateIR;
506 NS_NewInterfaceRequestorAggregation(notificationCallbacks, ir,
507 getter_AddRefs(aggregrateIR));
508 ir = aggregrateIR;
509 }
510 }
511 }
512
513 int32_t port = 0;
514 m_url->GetPort(&port);
515
516 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
517 nsAutoCString hostName;
518 if (server) server->GetRealHostName(hostName);
519
520 const char* connectionType = nullptr;
521 if (m_socketType == nsMsgSocketType::SSL)
522 connectionType = "ssl";
523 else if (m_socketType == nsMsgSocketType::trySTARTTLS ||
524 m_socketType == nsMsgSocketType::alwaysSTARTTLS)
525 connectionType = "starttls";
526
527 rv = OpenNetworkSocketWithInfo(hostName.get(), port, connectionType,
528 aProxyInfo, ir);
529 if (NS_FAILED(rv) && m_socketType == nsMsgSocketType::trySTARTTLS) {
530 m_socketType = nsMsgSocketType::plain;
531 rv = OpenNetworkSocketWithInfo(hostName.get(), port, nullptr, aProxyInfo,
532 ir);
533 }
534
535 return rv;
536 }
537
~nsPop3Protocol()538 nsPop3Protocol::~nsPop3Protocol() {
539 Cleanup();
540 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("~nsPop3Protocol()")));
541 }
542
Cleanup()543 void nsPop3Protocol::Cleanup() {
544 if (m_pop3ConData->newuidl) {
545 PL_HashTableDestroy(m_pop3ConData->newuidl);
546 m_pop3ConData->newuidl = nullptr;
547 }
548
549 net_pop3_free_state(m_pop3ConData->uidlinfo);
550
551 FreeMsgInfo();
552 PR_Free(m_pop3ConData->only_uidl);
553 PR_Free(m_pop3ConData);
554 }
555
SetCapFlag(uint32_t flag)556 void nsPop3Protocol::SetCapFlag(uint32_t flag) {
557 m_pop3ConData->capability_flags |= flag;
558 }
559
ClearCapFlag(uint32_t flag)560 void nsPop3Protocol::ClearCapFlag(uint32_t flag) {
561 m_pop3ConData->capability_flags &= ~flag;
562 }
563
TestCapFlag(uint32_t flag)564 bool nsPop3Protocol::TestCapFlag(uint32_t flag) {
565 return m_pop3ConData->capability_flags & flag;
566 }
567
GetCapFlags()568 uint32_t nsPop3Protocol::GetCapFlags() {
569 return m_pop3ConData->capability_flags;
570 }
571
FormatCounterString(const nsString & stringName,uint32_t count1,uint32_t count2,nsString & resultString)572 nsresult nsPop3Protocol::FormatCounterString(const nsString& stringName,
573 uint32_t count1, uint32_t count2,
574 nsString& resultString) {
575 AutoTArray<nsString, 2> formatStrings;
576 formatStrings.AppendElement()->AppendInt(count1);
577 formatStrings.AppendElement()->AppendInt(count2);
578
579 return mLocalBundle->FormatStringFromName(
580 NS_ConvertUTF16toUTF8(stringName).get(), formatStrings, resultString);
581 }
582
UpdateStatus(const char * aStatusName)583 void nsPop3Protocol::UpdateStatus(const char* aStatusName) {
584 nsString statusMessage;
585 mLocalBundle->GetStringFromName(aStatusName, statusMessage);
586 UpdateStatusWithString(statusMessage.get());
587 }
588
UpdateStatusWithString(const char16_t * aStatusString)589 void nsPop3Protocol::UpdateStatusWithString(const char16_t* aStatusString) {
590 if (mProgressEventSink) {
591 mozilla::DebugOnly<nsresult> rv = mProgressEventSink->OnStatus(
592 this, NS_OK, aStatusString); // XXX i18n message
593 NS_ASSERTION(NS_SUCCEEDED(rv), "dropping error result");
594 }
595 }
596
UpdateProgressPercent(int64_t totalDone,int64_t total)597 void nsPop3Protocol::UpdateProgressPercent(int64_t totalDone, int64_t total) {
598 if (mProgressEventSink)
599 mProgressEventSink->OnProgress(this, totalDone, total);
600 }
601
602 // note: SetUsername() expects an unescaped string
603 // do not pass in an escaped string
SetUsername(const char * name)604 void nsPop3Protocol::SetUsername(const char* name) {
605 NS_ASSERTION(name, "no name specified!");
606 if (name) m_username = name;
607 }
608
RerunUrl()609 nsresult nsPop3Protocol::RerunUrl() {
610 ClearFlag(POP3_PASSWORD_FAILED);
611 m_pop3Server->SetRunningProtocol(nullptr);
612 Cleanup();
613 return LoadUrl(m_url, nullptr);
614 }
615
GetNextPasswordObtainState()616 Pop3StatesEnum nsPop3Protocol::GetNextPasswordObtainState() {
617 switch (m_pop3ConData->next_state) {
618 case POP3_OBTAIN_PASSWORD_EARLY:
619 return POP3_FINISH_OBTAIN_PASSWORD_EARLY;
620 case POP3_SEND_USERNAME:
621 case POP3_OBTAIN_PASSWORD_BEFORE_USERNAME:
622 return POP3_FINISH_OBTAIN_PASSWORD_BEFORE_USERNAME;
623 case POP3_SEND_PASSWORD:
624 case POP3_OBTAIN_PASSWORD_BEFORE_PASSWORD:
625 return POP3_FINISH_OBTAIN_PASSWORD_BEFORE_PASSWORD;
626 default:
627 // Should never get here.
628 MOZ_ASSERT_UNREACHABLE(
629 "Invalid next_state in GetNextPasswordObtainState");
630 }
631 return POP3_ERROR_DONE;
632 }
633
StartGetAsyncPassword(Pop3StatesEnum aNextState)634 nsresult nsPop3Protocol::StartGetAsyncPassword(Pop3StatesEnum aNextState) {
635 nsresult rv;
636
637 // Try and avoid going async if possible - if we haven't got into a password
638 // failure state and the server has a password stored for this session, then
639 // use it.
640 if (!TestFlag(POP3_PASSWORD_FAILED)) {
641 nsCOMPtr<nsIMsgIncomingServer> server =
642 do_QueryInterface(m_pop3Server, &rv);
643 NS_ENSURE_SUCCESS(rv, rv);
644
645 rv = server->GetPassword(m_passwordResult);
646 if (NS_SUCCEEDED(rv) && !m_passwordResult.IsEmpty()) {
647 m_pop3ConData->next_state = GetNextPasswordObtainState();
648 return NS_OK;
649 }
650 }
651
652 // We're now going to need to do something that will end up with us either
653 // poking the login manager or prompting the user. We need to ensure we only
654 // do one prompt at a time (and login manager could cause a master password
655 // prompt), so we need to use the async prompter.
656 nsCOMPtr<nsIMsgAsyncPrompter> asyncPrompter =
657 do_GetService(NS_MSGASYNCPROMPTER_CONTRACTID, &rv);
658 NS_ENSURE_SUCCESS(rv, rv);
659
660 m_pop3ConData->next_state = aNextState;
661
662 // Although we're not actually pausing for a read, we'll do so anyway to let
663 // the async prompt run. Once it is our turn again we'll call back into
664 // ProcessProtocolState.
665 m_pop3ConData->pause_for_read = true;
666
667 nsCString server("unknown");
668 m_url->GetPrePath(server);
669
670 rv = asyncPrompter->QueueAsyncAuthPrompt(server, false, this);
671 // Explicit NS_ENSURE_SUCCESS for debug purposes as errors tend to get
672 // hidden.
673 NS_ENSURE_SUCCESS(rv, rv);
674 return rv;
675 }
676
OnPromptStartAsync(nsIMsgAsyncPromptCallback * aCallback)677 NS_IMETHODIMP nsPop3Protocol::OnPromptStartAsync(
678 nsIMsgAsyncPromptCallback* aCallback) {
679 bool result = false;
680 OnPromptStart(&result);
681 return aCallback->OnAuthResult(result);
682 }
683
OnPromptStart(bool * aResult)684 NS_IMETHODIMP nsPop3Protocol::OnPromptStart(bool* aResult) {
685 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("OnPromptStart()")));
686
687 *aResult = false;
688
689 nsresult rv;
690 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server, &rv);
691 NS_ENSURE_SUCCESS(rv, rv);
692
693 nsAutoString passwordResult;
694
695 // pass the failed password into the password prompt so that
696 // it will be pre-filled, in case it failed because of a
697 // server problem and not because it was wrong.
698 if (!m_lastPasswordSent.IsEmpty()) passwordResult = m_lastPasswordSent;
699
700 // Set up some items that we're going to need for the prompting.
701 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
702 nsCOMPtr<nsIMsgWindow> msgWindow;
703 if (mailnewsUrl) mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
704
705 nsCString userName;
706 server->GetRealUsername(userName);
707
708 nsCString hostName;
709 server->GetRealHostName(hostName);
710
711 nsString accountName;
712 server->GetPrettyName(accountName);
713
714 nsString passwordPrompt;
715 AutoTArray<nsString, 2> passwordParams;
716 CopyUTF8toUTF16(userName, *passwordParams.AppendElement());
717 CopyUTF8toUTF16(hostName, *passwordParams.AppendElement());
718
719 // if the last prompt got us a bad password then show a special dialog
720 if (TestFlag(POP3_PASSWORD_FAILED)) {
721 // Biff case (no msgWindow) shouldn't cause prompts or passwords to get
722 // forgotten at all
723 // TODO shouldn't we skip the new password prompt below as well for biff?
724 // Just exit here?
725 if (msgWindow) {
726 MOZ_LOG(POP3LOGMODULE, LogLevel::Warning,
727 (POP3LOG("POP: ask user what to do (after password failed): new "
728 "password, retry or cancel")));
729
730 int32_t buttonPressed = 0;
731 if (NS_SUCCEEDED(MsgPromptLoginFailed(msgWindow, hostName, userName,
732 accountName, &buttonPressed))) {
733 if (buttonPressed == 1) // Cancel button
734 {
735 MOZ_LOG(POP3LOGMODULE, LogLevel::Warning,
736 (POP3LOG("cancel button pressed")));
737 // Abort quickly and stop trying for now.
738
739 // If we haven't actually connected yet (i.e. we're doing an early
740 // attempt to get the username/password but we've previously failed
741 // for some reason), then skip straight to POP3_FREE as it isn't an
742 // error in this connection, and just ends up with us closing the
743 // socket and saying we've aborted the bind. Otherwise, pretend this
744 // is an error and move on.
745 m_pop3ConData->next_state =
746 m_pop3ConData->next_state == POP3_OBTAIN_PASSWORD_EARLY
747 ? POP3_FREE
748 : POP3_ERROR_DONE;
749
750 // Clear the password we're going to return to force failure in
751 // the get mail instance.
752 passwordResult.Truncate();
753
754 // We also have to clear the password failed flag, otherwise we'll
755 // automatically try again.
756 ClearFlag(POP3_PASSWORD_FAILED);
757
758 // As we're async, calling ProcessProtocolState gets things going
759 // again.
760 ProcessProtocolState(nullptr, nullptr, 0, 0);
761 return NS_OK;
762 } else if (buttonPressed == 2) // "New password" button
763 {
764 MOZ_LOG(POP3LOGMODULE, LogLevel::Warning,
765 (POP3LOG("new password button pressed")));
766 // Forget the stored password
767 // and we'll prompt for a new one next time around.
768 rv = server->ForgetPassword();
769 NS_ENSURE_SUCCESS(rv, rv);
770
771 // try all methods again with new password
772 ResetAuthMethods();
773 // ... apart from GSSAPI, which doesn't care about passwords
774 MarkAuthMethodAsFailed(POP3_HAS_AUTH_GSSAPI);
775 if (m_needToRerunUrl) return RerunUrl();
776 } else if (buttonPressed == 0) // "Retry" button
777 {
778 MOZ_LOG(POP3LOGMODULE, LogLevel::Warning,
779 (POP3LOG("retry button pressed")));
780 // try all methods again, including GSSAPI
781 ResetAuthMethods();
782 ClearFlag(POP3_PASSWORD_FAILED | POP3_AUTH_FAILURE);
783
784 if (m_needToRerunUrl) return RerunUrl();
785
786 // It is a bit strange that we're going onto the next state that
787 // would essentially send the password. However in resetting the
788 // auth methods above, we're setting up SendUsername, SendPassword
789 // and friends to abort and return to the POP3_SEND_CAPA state.
790 // Hence we can do this safely.
791 m_pop3ConData->next_state = GetNextPasswordObtainState();
792 // As we're async, calling ProcessProtocolState gets things going
793 // again.
794 ProcessProtocolState(nullptr, nullptr, 0, 0);
795 return NS_OK;
796 }
797 }
798 }
799 mLocalBundle->FormatStringFromName(
800 "pop3PreviouslyEnteredPasswordIsInvalidPrompt", passwordParams,
801 passwordPrompt);
802 } else
803 // Otherwise this is the first time we've asked about the server's
804 // password so show a first time prompt.
805 mLocalBundle->FormatStringFromName("pop3EnterPasswordPrompt",
806 passwordParams, passwordPrompt);
807
808 nsString passwordTitle;
809 mLocalBundle->FormatStringFromName("pop3EnterPasswordPromptTitleWithUsername",
810 passwordParams, passwordTitle);
811
812 // Now go and get the password.
813 if (!passwordPrompt.IsEmpty() && !passwordTitle.IsEmpty())
814 rv = server->GetPasswordWithUI(passwordPrompt, passwordTitle, msgWindow,
815 passwordResult);
816 ClearFlag(POP3_PASSWORD_FAILED | POP3_AUTH_FAILURE);
817
818 // If it failed or the user cancelled the prompt, just abort the
819 // connection.
820 if (NS_FAILED(rv) || rv == NS_MSG_PASSWORD_PROMPT_CANCELLED) {
821 m_pop3ConData->next_state = POP3_ERROR_DONE;
822 m_passwordResult.Truncate();
823 *aResult = false;
824 } else {
825 m_passwordResult = passwordResult;
826 m_pop3ConData->next_state = GetNextPasswordObtainState();
827 *aResult = true;
828 }
829 // Because this was done asynchronously, now call back into
830 // ProcessProtocolState to get the protocol going again.
831 ProcessProtocolState(nullptr, nullptr, 0, 0);
832 return NS_OK;
833 }
834
OnPromptAuthAvailable()835 NS_IMETHODIMP nsPop3Protocol::OnPromptAuthAvailable() {
836 MOZ_ASSERT_UNREACHABLE(
837 "Did not expect to get POP3 protocol queuing up auth "
838 "connections for same server");
839 return NS_OK;
840 }
841
OnPromptCanceled()842 NS_IMETHODIMP nsPop3Protocol::OnPromptCanceled() {
843 // A prompt was cancelled, so just abort out the connection
844 m_pop3ConData->next_state = POP3_ERROR_DONE;
845 // As we're async, calling ProcessProtocolState gets things going again.
846 ProcessProtocolState(nullptr, nullptr, 0, 0);
847 return NS_OK;
848 }
849
OnTransportStatus(nsITransport * aTransport,nsresult aStatus,int64_t aProgress,int64_t aProgressMax)850 NS_IMETHODIMP nsPop3Protocol::OnTransportStatus(nsITransport* aTransport,
851 nsresult aStatus,
852 int64_t aProgress,
853 int64_t aProgressMax) {
854 return nsMsgProtocol::OnTransportStatus(aTransport, aStatus, aProgress,
855 aProgressMax);
856 }
857
858 // stop binding is a "notification" informing us that the stream associated with
859 // aURL is going away.
OnStopRequest(nsIRequest * aRequest,nsresult aStatus)860 NS_IMETHODIMP nsPop3Protocol::OnStopRequest(nsIRequest* aRequest,
861 nsresult aStatus) {
862 // If the server dropped the connection, m_socketIsOpen will be true, before
863 // we call nsMsgProtocol::OnStopRequest. The call will force a close socket,
864 // but we still want to go through the state machine one more time to cleanup
865 // the protocol object.
866 if (m_socketIsOpen) {
867 nsCOMPtr<nsIMsgMailNewsUrl> msgUrl = do_QueryInterface(m_url);
868
869 if (NS_FAILED(aStatus)) {
870 // Stash the socket transport securityInfo on the url failedSecInfo
871 // attribute, so it'll be there for nsIUrlListener.OnStopRunningUrl().
872 nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport);
873 if (strans) {
874 nsCOMPtr<nsISupports> secInfo;
875 if (NS_SUCCEEDED(strans->GetSecurityInfo(getter_AddRefs(secInfo)))) {
876 if (nsCOMPtr<nsITransportSecurityInfo> transportSecInfo =
877 do_QueryInterface(secInfo)) {
878 msgUrl->SetFailedSecInfo(transportSecInfo);
879 }
880 }
881 }
882 }
883
884 // Check if the connection was dropped before getting back an auth error.
885 // If we got the auth error, the next state would be
886 // POP3_OBTAIN_PASSWORD_EARLY.
887 if ((m_pop3ConData->next_state_after_response == POP3_NEXT_AUTH_STEP ||
888 m_pop3ConData->next_state_after_response ==
889 POP3_AUTH_LOGIN_RESPONSE) &&
890 m_pop3ConData->next_state != POP3_OBTAIN_PASSWORD_EARLY) {
891 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
892 (POP3LOG("dropped connection before auth error")));
893 SetFlag(POP3_AUTH_FAILURE);
894 m_pop3ConData->command_succeeded = false;
895 m_needToRerunUrl = true;
896 m_pop3ConData->next_state = POP3_NEXT_AUTH_STEP;
897 ProcessProtocolState(nullptr, nullptr, 0, 0);
898 }
899 // We can't call nsMsgProtocol::OnStopRequest because it calls SetUrlState,
900 // which notifies the URLListeners, but we need to do a bit of cleanup
901 // before running the url again.
902 CloseSocket();
903 if (m_loadGroup)
904 m_loadGroup->RemoveRequest(static_cast<nsIRequest*>(this), nullptr,
905 aStatus);
906 m_pop3ConData->next_state = POP3_ERROR_DONE;
907 if (NS_FAILED(aStatus)) {
908 m_pop3ConData->urlStatus = aStatus;
909 }
910 ProcessProtocolState(nullptr, nullptr, 0, 0);
911
912 if (NS_FAILED(aStatus) && aStatus != NS_BINDING_ABORTED)
913 nsMsgProtocol::ShowAlertMessage(msgUrl, aStatus);
914
915 return NS_OK;
916 }
917 nsresult rv = nsMsgProtocol::OnStopRequest(aRequest, aStatus);
918
919 // turn off the server busy flag on stop request - we know we're done, right?
920 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
921 if (server) {
922 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
923 (POP3LOG("Clearing server busy in nsPop3Protocol::OnStopRequest")));
924 server->SetServerBusy(false); // the server is not busy
925 }
926 if (m_pop3ConData->list_done) CommitState(true);
927 if (NS_FAILED(aStatus) && aStatus != NS_BINDING_ABORTED) Abort();
928 return rv;
929 }
930
Abort()931 void nsPop3Protocol::Abort() {
932 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Abort")));
933
934 if (m_pop3ConData->msg_closure) {
935 m_nsIPop3Sink->IncorporateAbort(m_pop3ConData->only_uidl != nullptr);
936 m_pop3ConData->msg_closure = nullptr;
937 }
938 // Need this to close the stream on the inbox. It's possible that
939 // we abort before the POP3 sink was set.
940 if (m_nsIPop3Sink) m_nsIPop3Sink->AbortMailDelivery(this);
941 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
942 (POP3LOG("Clearing running protocol in nsPop3Protocol::Abort()")));
943 if (m_pop3Server) m_pop3Server->SetRunningProtocol(nullptr);
944 }
945
Cancel(nsresult status)946 NS_IMETHODIMP nsPop3Protocol::Cancel(nsresult status) // handle stop button
947 {
948 if (m_proxyRequest) {
949 m_proxyRequest->Cancel(NS_BINDING_ABORTED);
950
951 // Note that nsMsgProtocol::Cancel() also calls
952 // nsProtocolProxyService::Cancel(), so no need to call it twice
953 // (although it self-protects against multiple calls).
954 m_proxyRequest = nullptr;
955 }
956
957 Abort();
958 return nsMsgProtocol::Cancel(NS_BINDING_ABORTED);
959 }
960
LoadUrl(nsIURI * aURL,nsISupports *)961 nsresult nsPop3Protocol::LoadUrl(nsIURI* aURL, nsISupports* /* aConsumer */) {
962 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("LoadUrl()")));
963
964 nsresult rv = Initialize(aURL);
965 NS_ENSURE_SUCCESS(rv, rv);
966
967 if (aURL) {
968 rv = MsgExamineForProxyAsync(this, this, getter_AddRefs(m_proxyRequest));
969 if (NS_FAILED(rv)) {
970 rv = InitializeInternal(nullptr);
971 NS_ENSURE_SUCCESS(rv, rv);
972
973 rv = LoadUrlInternal(m_url);
974 }
975 }
976
977 return rv;
978 }
979
LoadUrlInternal(nsIURI * aURL)980 nsresult nsPop3Protocol::LoadUrlInternal(nsIURI* aURL) {
981 nsresult rv;
982
983 nsCOMPtr<nsIURL> url = do_QueryInterface(aURL, &rv);
984 if (NS_FAILED(rv)) return rv;
985
986 int32_t port;
987 rv = url->GetPort(&port);
988 NS_ENSURE_SUCCESS(rv, rv);
989
990 rv = NS_CheckPortSafety(port, "pop");
991 NS_ENSURE_SUCCESS(rv, rv);
992
993 nsAutoCString queryPart;
994 rv = url->GetQuery(queryPart);
995 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get the url spect");
996
997 m_pop3ConData->only_check_for_new_mail =
998 (PL_strcasestr(queryPart.get(), "check") != nullptr);
999 m_pop3ConData->verify_logon =
1000 (PL_strcasestr(queryPart.get(), "verifyLogon") != nullptr);
1001 m_pop3ConData->get_url = (PL_strcasestr(queryPart.get(), "gurl") != nullptr);
1002
1003 bool deleteByAgeFromServer = false;
1004 int32_t numDaysToLeaveOnServer = -1;
1005 if (!m_pop3ConData->verify_logon) {
1006 // Pick up pref setting regarding leave messages on server, message size
1007 // limit
1008
1009 m_pop3Server->GetLeaveMessagesOnServer(&m_pop3ConData->leave_on_server);
1010 m_pop3Server->GetHeadersOnly(&m_pop3ConData->headers_only);
1011 bool limitMessageSize = false;
1012
1013 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
1014 if (server) {
1015 // size limits are superseded by headers_only mode
1016 if (!m_pop3ConData->headers_only) {
1017 server->GetLimitOfflineMessageSize(&limitMessageSize);
1018 if (limitMessageSize) {
1019 int32_t max_size = 0; // default size
1020 server->GetMaxMessageSize(&max_size);
1021 m_pop3ConData->size_limit = (max_size) ? max_size * 1024 : 50 * 1024;
1022 }
1023 }
1024 m_pop3Server->GetDeleteByAgeFromServer(&deleteByAgeFromServer);
1025 if (deleteByAgeFromServer)
1026 m_pop3Server->GetNumDaysToLeaveOnServer(&numDaysToLeaveOnServer);
1027 }
1028 }
1029
1030 // UIDL stuff
1031 nsCOMPtr<nsIPop3URL> pop3Url = do_QueryInterface(m_url);
1032 if (pop3Url) pop3Url->GetPop3Sink(getter_AddRefs(m_nsIPop3Sink));
1033
1034 nsCOMPtr<nsIFile> mailDirectory;
1035
1036 nsCString hostName;
1037 nsCString userName;
1038
1039 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
1040 if (server) {
1041 rv = server->GetLocalPath(getter_AddRefs(mailDirectory));
1042 NS_ENSURE_SUCCESS(rv, rv);
1043 server->SetServerBusy(true); // the server is now busy
1044 server->GetHostName(hostName);
1045 server->GetUsername(userName);
1046 MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
1047 (POP3LOG("Connecting to server %s:%d"), hostName.get(), port));
1048
1049 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1050 (POP3LOG("Setting server busy in nsPop3Protocol::LoadUrl()")));
1051 }
1052
1053 if (!m_pop3ConData->verify_logon)
1054 m_pop3ConData->uidlinfo =
1055 net_pop3_load_state(hostName.get(), userName.get(), mailDirectory);
1056
1057 m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_NoMail;
1058
1059 if (m_pop3ConData->uidlinfo && numDaysToLeaveOnServer > 0) {
1060 uint32_t nowInSeconds = TimeInSecondsFromPRTime(PR_Now());
1061 uint32_t cutOffDay = nowInSeconds - (60 * 60 * 24 * numDaysToLeaveOnServer);
1062
1063 PL_HashTableEnumerateEntries(m_pop3ConData->uidlinfo->hash,
1064 net_pop3_delete_old_msgs_mapper,
1065 (void*)(uintptr_t)cutOffDay);
1066 }
1067 const char* uidl = PL_strcasestr(queryPart.get(), "uidl=");
1068 PR_FREEIF(m_pop3ConData->only_uidl);
1069
1070 if (uidl) {
1071 uidl += 5;
1072 nsCString unescapedData;
1073 MsgUnescapeString(nsDependentCString(uidl), 0, unescapedData);
1074 m_pop3ConData->only_uidl = PL_strdup(unescapedData.get());
1075
1076 // Suppress on start and on stop because this url won't have any
1077 // content to display.
1078 mSuppressListenerNotifications = true;
1079 }
1080
1081 m_pop3ConData->next_state = POP3_START_CONNECT;
1082 m_pop3ConData->next_state_after_response = POP3_FINISH_CONNECT;
1083 if (NS_SUCCEEDED(rv)) {
1084 m_pop3Server->SetRunningProtocol(this);
1085 return nsMsgProtocol::LoadUrl(aURL);
1086 } else
1087 return rv;
1088 }
1089
FreeMsgInfo()1090 void nsPop3Protocol::FreeMsgInfo() {
1091 int i;
1092 if (m_pop3ConData->msg_info) {
1093 for (i = 0; i < m_pop3ConData->number_of_messages; i++) {
1094 if (m_pop3ConData->msg_info[i].uidl)
1095 PR_Free(m_pop3ConData->msg_info[i].uidl);
1096 m_pop3ConData->msg_info[i].uidl = nullptr;
1097 }
1098 PR_Free(m_pop3ConData->msg_info);
1099 m_pop3ConData->msg_info = nullptr;
1100 }
1101 }
1102
WaitForStartOfConnectionResponse(nsIInputStream * aInputStream,uint32_t length)1103 int32_t nsPop3Protocol::WaitForStartOfConnectionResponse(
1104 nsIInputStream* aInputStream, uint32_t length) {
1105 char* line = nullptr;
1106 uint32_t line_length = 0;
1107 bool pauseForMoreData = false;
1108 nsresult rv;
1109 line = m_lineStreamBuffer->ReadNextLine(aInputStream, line_length,
1110 pauseForMoreData, &rv);
1111
1112 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
1113 if (NS_FAILED(rv)) return -1;
1114
1115 if (pauseForMoreData || !line) {
1116 m_pop3ConData->pause_for_read = true; /* pause */
1117 PR_Free(line);
1118 return (line_length);
1119 }
1120
1121 if (*line == '+') {
1122 m_pop3ConData->command_succeeded = true;
1123 if (PL_strlen(line) > 4)
1124 m_commandResponse = line + 4;
1125 else
1126 m_commandResponse = line;
1127
1128 if (m_prefAuthMethods & POP3_HAS_AUTH_APOP) {
1129 if (NS_SUCCEEDED(GetApopTimestamp())) SetCapFlag(POP3_HAS_AUTH_APOP);
1130 } else
1131 ClearCapFlag(POP3_HAS_AUTH_APOP);
1132
1133 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1134
1135 m_pop3ConData->next_state = POP3_PROCESS_AUTH;
1136 m_pop3ConData->pause_for_read = false; /* don't pause */
1137 }
1138
1139 PR_Free(line);
1140 return (1); /* everything ok */
1141 }
1142
WaitForResponse(nsIInputStream * inputStream,uint32_t length)1143 int32_t nsPop3Protocol::WaitForResponse(nsIInputStream* inputStream,
1144 uint32_t length) {
1145 char* line;
1146 uint32_t ln = 0;
1147 bool pauseForMoreData = false;
1148 nsresult rv;
1149 line =
1150 m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
1151 if (NS_FAILED(rv)) return -1;
1152
1153 if (pauseForMoreData || !line) {
1154 m_pop3ConData->pause_for_read = true; /* pause */
1155
1156 PR_Free(line);
1157 return (ln);
1158 }
1159
1160 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
1161 m_pop3ConData->command_temp_fail = false;
1162
1163 if (*line == '+') {
1164 m_pop3ConData->command_succeeded = true;
1165 if (PL_strlen(line) > 4) {
1166 if (!PL_strncasecmp(line, "+OK", 3))
1167 m_commandResponse = line + 4;
1168 else // challenge answer to AUTH CRAM-MD5 and LOGIN username/password
1169 m_commandResponse = line + 2;
1170 } else
1171 m_commandResponse = line;
1172 } else {
1173 m_pop3ConData->command_succeeded = false;
1174 if (PL_strlen(line) > 5)
1175 m_commandResponse = line + 5;
1176 else
1177 m_commandResponse = line;
1178
1179 // search for the response codes (RFC 2449, chapter 8 and RFC 3206)
1180 if (TestCapFlag(POP3_HAS_RESP_CODES | POP3_HAS_AUTH_RESP_CODE)) {
1181 // code for authentication failure due to the user's credentials
1182 if (m_commandResponse.Find("[AUTH", true) >= 0) {
1183 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1184 (POP3LOG("setting auth failure")));
1185 SetFlag(POP3_AUTH_FAILURE);
1186 }
1187
1188 // codes for failures due to other reasons
1189 if (m_commandResponse.Find("[LOGIN-DELAY", true) >= 0 ||
1190 m_commandResponse.Find("[IN-USE", true) >= 0 ||
1191 m_commandResponse.Find("[SYS", true) >= 0)
1192 SetFlag(POP3_STOPLOGIN);
1193
1194 if (m_commandResponse.Find("[SYS/TEMP", true) >= 0) {
1195 m_pop3ConData->command_temp_fail = true;
1196 }
1197
1198 // remove the codes from the response string presented to the user
1199 int32_t i = m_commandResponse.FindChar(']');
1200 if (i >= 0) m_commandResponse.Cut(0, i + 2);
1201 }
1202 }
1203
1204 m_pop3ConData->next_state = m_pop3ConData->next_state_after_response;
1205 m_pop3ConData->pause_for_read = false; /* don't pause */
1206
1207 PR_Free(line);
1208 return (1); /* everything ok */
1209 }
1210
Error(const char * err_code,const char16_t * param)1211 int32_t nsPop3Protocol::Error(const char* err_code, const char16_t* param) {
1212 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("ERROR: %s"), err_code));
1213 nsresult rv = NS_OK;
1214
1215 // the error code is just the resource name for the error string...
1216 // so print out that error message!
1217 nsAutoString message;
1218 // Format the alert string if parameter list isn't empty
1219 if (param) {
1220 AutoTArray<nsString, 1> params = {nsDependentString(param)};
1221 mLocalBundle->FormatStringFromName(err_code, params, message);
1222 } else {
1223 mLocalBundle->GetStringFromName(err_code, message);
1224 }
1225 if (!m_pop3ConData->command_succeeded) {
1226 // Server error message
1227 nsString serverSaidPrefix;
1228 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
1229 nsCString hostName;
1230 // Format string with hostname.
1231 if (server) {
1232 rv = server->GetRealHostName(hostName);
1233 }
1234 if (NS_SUCCEEDED(rv)) {
1235 AutoTArray<nsString, 1> params;
1236 CopyASCIItoUTF16(hostName, *params.AppendElement());
1237 mLocalBundle->FormatStringFromName("pop3ServerSaid", params,
1238 serverSaidPrefix);
1239 }
1240
1241 message.Append(' ');
1242 message.Append(serverSaidPrefix);
1243 message.Append(' ');
1244 message.Append(NS_ConvertASCIItoUTF16(m_commandResponse));
1245 }
1246
1247 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
1248 mailnewsUrl->SetErrorCode(nsDependentCString(err_code));
1249 mailnewsUrl->SetErrorMessage(message);
1250
1251 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
1252 nsString accountName;
1253 rv = server->GetPrettyName(accountName);
1254 NS_ENSURE_SUCCESS(rv, -1);
1255 AutoTArray<nsString, 1> titleParams = {accountName};
1256 nsString dialogTitle;
1257 mLocalBundle->FormatStringFromName("pop3ErrorDialogTitle", titleParams,
1258 dialogTitle);
1259 // we handle "pop3TmpDownloadError" earlier...
1260 if (strcmp(err_code, "pop3TmpDownloadError") && NS_SUCCEEDED(rv)) {
1261 nsCOMPtr<nsIMsgWindow> msgWindow;
1262 nsCOMPtr<nsIPrompt> dialog;
1263 rv = mailnewsUrl->GetMsgWindow(
1264 getter_AddRefs(msgWindow)); // it is ok to have null msgWindow, for
1265 // example when biffing
1266 if (NS_SUCCEEDED(rv) && msgWindow) {
1267 rv = msgWindow->GetPromptDialog(getter_AddRefs(dialog));
1268 if (NS_SUCCEEDED(rv)) {
1269 dialog->Alert(dialogTitle.get(), message.get());
1270 }
1271 }
1272 }
1273 m_pop3ConData->next_state = POP3_ERROR_DONE;
1274 m_pop3ConData->pause_for_read = false;
1275 return -1;
1276 }
1277
Pop3SendData(const char * dataBuffer,bool aSuppressLogging)1278 int32_t nsPop3Protocol::Pop3SendData(const char* dataBuffer,
1279 bool aSuppressLogging) {
1280 // remove any leftover bytes in the line buffer
1281 // this can happen if the last message line doesn't end with a (CR)LF
1282 // or a server sent two reply lines
1283 m_lineStreamBuffer->ClearBuffer();
1284
1285 nsresult result = nsMsgProtocol::SendData(dataBuffer);
1286
1287 if (!aSuppressLogging)
1288 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("SEND: %s"), dataBuffer));
1289 else
1290 MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
1291 (POP3LOG("Logging suppressed for this command (it probably "
1292 "contained authentication information)")));
1293
1294 if (NS_SUCCEEDED(result)) {
1295 m_pop3ConData->pause_for_read = true;
1296 m_pop3ConData->next_state = POP3_WAIT_FOR_RESPONSE;
1297 return 0;
1298 }
1299
1300 m_pop3ConData->next_state = POP3_ERROR_DONE;
1301 MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
1302 (POP3LOG("Pop3SendData failed: %" PRIx32),
1303 static_cast<uint32_t>(result)));
1304 return -1;
1305 }
1306
1307 /**
1308 * POP3 AUTH extension
1309 */
SendAuth()1310 int32_t nsPop3Protocol::SendAuth() {
1311 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendAuth()")));
1312
1313 if (!m_pop3ConData->command_succeeded) return Error("pop3ServerError");
1314
1315 nsAutoCString command("AUTH" CRLF);
1316
1317 m_pop3ConData->next_state_after_response = POP3_AUTH_RESPONSE;
1318 return Pop3SendData(command.get());
1319 }
1320
AuthResponse(nsIInputStream * inputStream,uint32_t length)1321 int32_t nsPop3Protocol::AuthResponse(nsIInputStream* inputStream,
1322 uint32_t length) {
1323 char* line;
1324 uint32_t ln = 0;
1325 nsresult rv;
1326
1327 if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED)) {
1328 ClearCapFlag(POP3_AUTH_MECH_UNDEFINED);
1329 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1330 }
1331
1332 if (!m_pop3ConData->command_succeeded) {
1333 /* AUTH command not implemented
1334 * so no secure mechanisms available
1335 */
1336 m_pop3ConData->command_succeeded = true;
1337 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1338 m_pop3ConData->next_state = POP3_SEND_CAPA;
1339 return 0;
1340 }
1341
1342 bool pauseForMoreData = false;
1343 line =
1344 m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
1345 if (NS_FAILED(rv)) return -1;
1346
1347 if (pauseForMoreData || !line) {
1348 m_pop3ConData->pause_for_read = true; /* pause */
1349 PR_Free(line);
1350 return (0);
1351 }
1352
1353 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
1354
1355 if (!PL_strcmp(line, ".")) {
1356 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1357
1358 // now that we've read all the AUTH responses, go for it
1359 m_pop3ConData->next_state = POP3_SEND_CAPA;
1360 m_pop3ConData->pause_for_read = false; /* don't pause */
1361 } else if (!PL_strcasecmp(line, "CRAM-MD5"))
1362 SetCapFlag(POP3_HAS_AUTH_CRAM_MD5);
1363 else if (!PL_strcasecmp(line, "NTLM"))
1364 SetCapFlag(POP3_HAS_AUTH_NTLM);
1365 else if (!PL_strcasecmp(line, "MSN"))
1366 SetCapFlag(POP3_HAS_AUTH_NTLM | POP3_HAS_AUTH_MSN);
1367 else if (!PL_strcasecmp(line, "GSSAPI"))
1368 SetCapFlag(POP3_HAS_AUTH_GSSAPI);
1369 else if (!PL_strcasecmp(line, "PLAIN"))
1370 SetCapFlag(POP3_HAS_AUTH_PLAIN);
1371 else if (!PL_strcasecmp(line, "LOGIN"))
1372 SetCapFlag(POP3_HAS_AUTH_LOGIN);
1373 else if (!PL_strcasecmp(line, "XOAUTH2"))
1374 SetCapFlag(POP3_HAS_AUTH_XOAUTH2);
1375
1376 PR_Free(line);
1377 return 0;
1378 }
1379
1380 /*
1381 * POP3 CAPA extension, see RFC 2449, chapter 5
1382 */
1383
SendCapa()1384 int32_t nsPop3Protocol::SendCapa() {
1385 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendCapa()")));
1386 if (!m_pop3ConData->command_succeeded) return Error("pop3ServerError");
1387
1388 nsAutoCString command("CAPA" CRLF);
1389
1390 m_pop3ConData->next_state_after_response = POP3_CAPA_RESPONSE;
1391 return Pop3SendData(command.get());
1392 }
1393
CapaResponse(nsIInputStream * inputStream,uint32_t length)1394 int32_t nsPop3Protocol::CapaResponse(nsIInputStream* inputStream,
1395 uint32_t length) {
1396 char* line;
1397 uint32_t ln = 0;
1398
1399 if (!m_pop3ConData->command_succeeded) {
1400 /* CAPA command not implemented */
1401 m_pop3ConData->command_succeeded = true;
1402 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1403 m_pop3ConData->next_state = POP3_PROCESS_AUTH;
1404 return 0;
1405 }
1406
1407 bool pauseForMoreData = false;
1408 nsresult rv;
1409 line =
1410 m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
1411 if (NS_FAILED(rv)) return -1;
1412
1413 if (pauseForMoreData || !line) {
1414 m_pop3ConData->pause_for_read = true; /* pause */
1415 PR_Free(line);
1416 return (0);
1417 }
1418
1419 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
1420
1421 if (!PL_strcmp(line, ".")) {
1422 // now that we've read all the CAPA responses, go for it
1423 m_pop3ConData->next_state = POP3_PROCESS_AUTH;
1424 m_pop3ConData->pause_for_read = false; /* don't pause */
1425 } else if (!PL_strcasecmp(line, "XSENDER")) {
1426 SetCapFlag(POP3_HAS_XSENDER);
1427 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1428 } else if (!PL_strcasecmp(line, "RESP-CODES") ||
1429 !PL_strcasecmp(line, "RESP_CODES")) {
1430 // see RFC 2449, chapter 6.4 -- some non-compliant servers use underscore
1431 // instead of hyphen so accept underscore too.
1432 SetCapFlag(POP3_HAS_RESP_CODES);
1433 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1434 } else if (!PL_strcasecmp(line, "AUTH-RESP-CODE") ||
1435 !PL_strcasecmp(line, "AUTH_RESP_CODE")) {
1436 // see RFC 3206, chapter 6 -- some non-compliant servers use underscore
1437 // instead of hyphen so accept underscore too.
1438 SetCapFlag(POP3_HAS_AUTH_RESP_CODE);
1439 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1440 } else if (!PL_strcasecmp(line, "STLS")) {
1441 // see RFC 2595, chapter 4
1442 SetCapFlag(POP3_HAS_STLS);
1443 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1444 } else if (!PL_strncasecmp(line, "SASL", 4) && strlen(line) > 6) {
1445 // see RFC 2449, chapter 6.3
1446 nsAutoCString responseLine;
1447 responseLine.Assign(line + 5);
1448
1449 if (responseLine.Find("PLAIN", /* ignoreCase = */ true) >= 0)
1450 SetCapFlag(POP3_HAS_AUTH_PLAIN);
1451
1452 if (responseLine.Find("LOGIN", /* ignoreCase = */ true) >= 0)
1453 SetCapFlag(POP3_HAS_AUTH_LOGIN);
1454
1455 if (responseLine.Find("GSSAPI", /* ignoreCase = */ true) >= 0)
1456 SetCapFlag(POP3_HAS_AUTH_GSSAPI);
1457
1458 if (responseLine.Find("CRAM-MD5", /* ignoreCase = */ true) >= 0)
1459 SetCapFlag(POP3_HAS_AUTH_CRAM_MD5);
1460
1461 if (responseLine.Find("NTLM", /* ignoreCase = */ true) >= 0)
1462 SetCapFlag(POP3_HAS_AUTH_NTLM);
1463
1464 if (responseLine.Find("MSN", /* ignoreCase = */ true) >= 0)
1465 SetCapFlag(POP3_HAS_AUTH_NTLM | POP3_HAS_AUTH_MSN);
1466
1467 if (responseLine.Find("XOAUTH2", /* ignoreCase = */ true) >= 0)
1468 SetCapFlag(POP3_HAS_AUTH_XOAUTH2);
1469
1470 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1471 }
1472
1473 PR_Free(line);
1474 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1475 (POP3LOG("Capability entry processed")));
1476 return 0;
1477 }
1478
SendTLSResponse()1479 int32_t nsPop3Protocol::SendTLSResponse() {
1480 // only tear down our existing connection and open a new one if we received
1481 // a +OK response from the pop server after we issued the STLS command
1482 nsresult rv = NS_OK;
1483 if (m_pop3ConData->command_succeeded) {
1484 nsCOMPtr<nsISupports> secInfo;
1485 nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport, &rv);
1486 if (NS_FAILED(rv)) return -1;
1487
1488 rv = strans->GetSecurityInfo(getter_AddRefs(secInfo));
1489
1490 if (NS_SUCCEEDED(rv) && secInfo) {
1491 nsCOMPtr<nsISSLSocketControl> sslControl =
1492 do_QueryInterface(secInfo, &rv);
1493
1494 if (NS_SUCCEEDED(rv) && sslControl) rv = sslControl->StartTLS();
1495 }
1496
1497 if (NS_SUCCEEDED(rv)) {
1498 m_pop3ConData->next_state = POP3_SEND_AUTH;
1499 m_tlsEnabled = true;
1500
1501 // certain capabilities like POP3_HAS_AUTH_APOP should be
1502 // preserved across the connections.
1503 uint32_t preservedCapFlags =
1504 m_pop3ConData->capability_flags & POP3_HAS_AUTH_APOP;
1505 m_pop3ConData->capability_flags = // resetting the flags
1506 POP3_AUTH_MECH_UNDEFINED |
1507 POP3_HAS_AUTH_USER | // should be always there
1508 POP3_GURL_UNDEFINED | POP3_UIDL_UNDEFINED | POP3_TOP_UNDEFINED |
1509 POP3_XTND_XLST_UNDEFINED | preservedCapFlags;
1510 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1511 return 0;
1512 }
1513 }
1514
1515 ClearFlag(POP3_HAS_STLS);
1516 m_pop3ConData->next_state = POP3_PROCESS_AUTH;
1517
1518 return (NS_SUCCEEDED(rv) ? 0 : -1);
1519 }
1520
InitPrefAuthMethods(int32_t authMethodPrefValue)1521 void nsPop3Protocol::InitPrefAuthMethods(int32_t authMethodPrefValue) {
1522 // for m_prefAuthMethods, using the same flags as server capabilities.
1523 switch (authMethodPrefValue) {
1524 case nsMsgAuthMethod::none:
1525 m_prefAuthMethods = POP3_HAS_AUTH_NONE;
1526 break;
1527 case nsMsgAuthMethod::old:
1528 m_prefAuthMethods = POP3_HAS_AUTH_USER;
1529 break;
1530 case nsMsgAuthMethod::passwordCleartext:
1531 m_prefAuthMethods =
1532 POP3_HAS_AUTH_USER | POP3_HAS_AUTH_LOGIN | POP3_HAS_AUTH_PLAIN;
1533 break;
1534 case nsMsgAuthMethod::passwordEncrypted:
1535 m_prefAuthMethods = POP3_HAS_AUTH_CRAM_MD5 | POP3_HAS_AUTH_APOP;
1536 break;
1537 case nsMsgAuthMethod::NTLM:
1538 m_prefAuthMethods = POP3_HAS_AUTH_NTLM | POP3_HAS_AUTH_MSN;
1539 break;
1540 case nsMsgAuthMethod::GSSAPI:
1541 m_prefAuthMethods = POP3_HAS_AUTH_GSSAPI;
1542 break;
1543 case nsMsgAuthMethod::OAuth2:
1544 m_prefAuthMethods = POP3_HAS_AUTH_XOAUTH2;
1545 break;
1546 case nsMsgAuthMethod::secure:
1547 m_prefAuthMethods = POP3_HAS_AUTH_APOP | POP3_HAS_AUTH_CRAM_MD5 |
1548 POP3_HAS_AUTH_GSSAPI | POP3_HAS_AUTH_NTLM |
1549 POP3_HAS_AUTH_MSN;
1550 break;
1551 default:
1552 NS_ASSERTION(false, "POP: authMethod pref invalid");
1553 // TODO log to error console
1554 MOZ_LOG(
1555 POP3LOGMODULE, LogLevel::Error,
1556 (POP3LOG("POP: bad pref authMethod = %d\n"), authMethodPrefValue));
1557 // fall to any
1558 [[fallthrough]];
1559 case nsMsgAuthMethod::anything:
1560 m_prefAuthMethods =
1561 POP3_HAS_AUTH_USER | POP3_HAS_AUTH_LOGIN | POP3_HAS_AUTH_PLAIN |
1562 POP3_HAS_AUTH_CRAM_MD5 | POP3_HAS_AUTH_APOP | POP3_HAS_AUTH_GSSAPI |
1563 POP3_HAS_AUTH_NTLM | POP3_HAS_AUTH_MSN | POP3_HAS_AUTH_XOAUTH2;
1564 // TODO needed?
1565 break;
1566 }
1567
1568 // Only enable OAuth2 support if we can do the lookup.
1569 if ((m_prefAuthMethods & POP3_HAS_AUTH_XOAUTH2) && !mOAuth2Support) {
1570 m_prefAuthMethods &= ~POP3_HAS_AUTH_XOAUTH2; // disable it
1571 }
1572
1573 NS_ASSERTION(m_prefAuthMethods != POP3_AUTH_MECH_UNDEFINED,
1574 "POP: InitPrefAuthMethods() didn't work");
1575 }
1576
1577 /**
1578 * Changes m_currentAuthMethod to pick the best one
1579 * which is allowed by server and prefs and not marked failed.
1580 * The order of preference and trying of auth methods is encoded here.
1581 */
ChooseAuthMethod()1582 nsresult nsPop3Protocol::ChooseAuthMethod() {
1583 int32_t availCaps = GetCapFlags() & m_prefAuthMethods & ~m_failedAuthMethods;
1584
1585 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1586 (POP3LOG("POP auth: server caps 0x%X, pref 0x%X, failed 0x%X, avail "
1587 "caps 0x%X"),
1588 GetCapFlags(), m_prefAuthMethods, m_failedAuthMethods, availCaps));
1589 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1590 (POP3LOG("(GSSAPI = 0x%X, CRAM = 0x%X, APOP = 0x%X, NTLM = 0x%X, "
1591 "MSN = 0x%X, PLAIN = 0x%X, LOGIN = 0x%X, USER/PASS = 0x%X, "
1592 "XOAUTH2 = 0x%X)"),
1593 POP3_HAS_AUTH_GSSAPI, POP3_HAS_AUTH_CRAM_MD5, POP3_HAS_AUTH_APOP,
1594 POP3_HAS_AUTH_NTLM, POP3_HAS_AUTH_MSN, POP3_HAS_AUTH_PLAIN,
1595 POP3_HAS_AUTH_LOGIN, POP3_HAS_AUTH_USER, POP3_HAS_AUTH_XOAUTH2));
1596
1597 MOZ_LOG(
1598 POP3LOGMODULE, LogLevel::Debug,
1599 (POP3LOG("(Enabled - GSSAPI=%d, CRAM=%d, APOP=%d, NTLM=%d, "
1600 "MSN=%d, PLAIN=%d, LOGIN=%d, USER/PASS=%d, "
1601 "XOAUTH2=%d)"),
1602 !!(POP3_HAS_AUTH_GSSAPI & availCaps),
1603 !!(POP3_HAS_AUTH_CRAM_MD5 & availCaps),
1604 !!(POP3_HAS_AUTH_APOP & availCaps), !!(POP3_HAS_AUTH_NTLM & availCaps),
1605 !!(POP3_HAS_AUTH_MSN & availCaps), !!(POP3_HAS_AUTH_PLAIN & availCaps),
1606 !!(POP3_HAS_AUTH_LOGIN & availCaps), !!(POP3_HAS_AUTH_USER & availCaps),
1607 !!(POP3_HAS_AUTH_XOAUTH2 & availCaps)));
1608
1609 if (POP3_HAS_AUTH_GSSAPI & availCaps)
1610 m_currentAuthMethod = POP3_HAS_AUTH_GSSAPI;
1611 else if (POP3_HAS_AUTH_CRAM_MD5 & availCaps)
1612 m_currentAuthMethod = POP3_HAS_AUTH_CRAM_MD5;
1613 else if (POP3_HAS_AUTH_APOP & availCaps)
1614 m_currentAuthMethod = POP3_HAS_AUTH_APOP;
1615 else if (POP3_HAS_AUTH_NTLM & availCaps)
1616 m_currentAuthMethod = POP3_HAS_AUTH_NTLM;
1617 else if (POP3_HAS_AUTH_MSN & availCaps)
1618 m_currentAuthMethod = POP3_HAS_AUTH_MSN;
1619 else if (POP3_HAS_AUTH_PLAIN & availCaps)
1620 m_currentAuthMethod = POP3_HAS_AUTH_PLAIN;
1621 else if (POP3_HAS_AUTH_LOGIN & availCaps)
1622 m_currentAuthMethod = POP3_HAS_AUTH_LOGIN;
1623 else if (POP3_HAS_AUTH_USER & availCaps)
1624 m_currentAuthMethod = POP3_HAS_AUTH_USER;
1625 else if (POP3_HAS_AUTH_XOAUTH2 & availCaps)
1626 m_currentAuthMethod = POP3_HAS_AUTH_XOAUTH2;
1627
1628 else {
1629 // there are no matching login schemes at all, per server and prefs
1630 m_currentAuthMethod = POP3_AUTH_MECH_UNDEFINED;
1631 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1632 (POP3LOG("No auth method remaining")));
1633 return NS_ERROR_FAILURE;
1634 }
1635 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1636 (POP3LOG("Trying auth method 0x%X"), m_currentAuthMethod));
1637 return NS_OK;
1638 }
1639
MarkAuthMethodAsFailed(int32_t failedAuthMethod)1640 void nsPop3Protocol::MarkAuthMethodAsFailed(int32_t failedAuthMethod) {
1641 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1642 (POP3LOG("Marking auth method 0x%X failed"), failedAuthMethod));
1643 m_failedAuthMethods |= failedAuthMethod;
1644 }
1645
1646 /**
1647 * Start over, trying all auth methods again
1648 */
ResetAuthMethods()1649 void nsPop3Protocol::ResetAuthMethods() {
1650 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1651 (POP3LOG("Resetting (failed) auth methods")));
1652 m_currentAuthMethod = POP3_AUTH_MECH_UNDEFINED;
1653 m_failedAuthMethods = 0;
1654 }
1655
1656 /**
1657 * state POP3_PROCESS_AUTH
1658 * Called when we should try to authenticate to the server.
1659 * Also called when one auth method fails and we want to try and start
1660 * the next best auth method.
1661 */
ProcessAuth()1662 int32_t nsPop3Protocol::ProcessAuth() {
1663 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("ProcessAuth()")));
1664
1665 // Try to upgrade to STARTTLS -- TODO move into its own function
1666 if (!m_tlsEnabled) {
1667 if (TestCapFlag(POP3_HAS_STLS)) {
1668 if (m_socketType == nsMsgSocketType::trySTARTTLS ||
1669 m_socketType == nsMsgSocketType::alwaysSTARTTLS) {
1670 nsAutoCString command("STLS" CRLF);
1671
1672 m_pop3ConData->next_state_after_response = POP3_TLS_RESPONSE;
1673 return Pop3SendData(command.get());
1674 }
1675 } else if (m_socketType == nsMsgSocketType::alwaysSTARTTLS) {
1676 m_pop3ConData->next_state = POP3_ERROR_DONE;
1677 return Error("nsErrorCouldNotConnectViaTls");
1678 }
1679 }
1680
1681 m_password_already_sent = false;
1682
1683 nsresult rv = ChooseAuthMethod();
1684 if (NS_FAILED(rv)) {
1685 // Pref doesn't match server. Now, find an appropriate error msg.
1686 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1687 (POP3LOG("ProcessAuth() early exit because no auth methods")));
1688
1689 // AuthGSSAPI* falls in here in case of an auth failure.
1690 // If Kerberos was the only method, assume that
1691 // the user is just not logged in yet, and show an appropriate error.
1692 if (m_prefAuthMethods == POP3_HAS_AUTH_GSSAPI &&
1693 m_failedAuthMethods == POP3_HAS_AUTH_GSSAPI)
1694 return Error("pop3GssapiFailure");
1695
1696 // pref has plaintext pw & server claims to support encrypted pw
1697 if (m_prefAuthMethods ==
1698 (POP3_HAS_AUTH_USER | POP3_HAS_AUTH_LOGIN | POP3_HAS_AUTH_PLAIN) &&
1699 GetCapFlags() & (POP3_HAS_AUTH_CRAM_MD5 | POP3_HAS_AUTH_APOP))
1700 // tell user to change to encrypted pw
1701 return Error("pop3AuthChangePlainToEncrypt");
1702 // pref has encrypted pw & server claims to support plaintext pw
1703 else if (m_prefAuthMethods ==
1704 (POP3_HAS_AUTH_CRAM_MD5 | POP3_HAS_AUTH_APOP) &&
1705 GetCapFlags() & (POP3_HAS_AUTH_USER | POP3_HAS_AUTH_LOGIN |
1706 POP3_HAS_AUTH_PLAIN)) {
1707 // have SSL
1708 if (m_socketType == nsMsgSocketType::SSL ||
1709 m_socketType == nsMsgSocketType::alwaysSTARTTLS)
1710 // tell user to change to plaintext pw
1711 return Error("pop3AuthChangeEncryptToPlainSSL");
1712 else
1713 // tell user to change to plaintext pw, with big warning
1714 return Error("pop3AuthChangeEncryptToPlainNoSSL");
1715 } else
1716 // just "change auth method"
1717 return Error("pop3AuthMechNotSupported");
1718 }
1719
1720 switch (m_currentAuthMethod) {
1721 case POP3_HAS_AUTH_GSSAPI:
1722 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP GSSAPI")));
1723 m_pop3ConData->next_state = POP3_AUTH_GSSAPI;
1724 break;
1725 case POP3_HAS_AUTH_APOP:
1726 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP APOP")));
1727 m_pop3ConData->next_state = POP3_SEND_PASSWORD;
1728 break;
1729 case POP3_HAS_AUTH_CRAM_MD5:
1730 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP CRAM")));
1731 [[fallthrough]];
1732 case POP3_HAS_AUTH_PLAIN:
1733 case POP3_HAS_AUTH_USER:
1734 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP username")));
1735 m_pop3ConData->next_state = POP3_SEND_USERNAME;
1736 break;
1737 case POP3_HAS_AUTH_LOGIN:
1738 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP AUTH=LOGIN")));
1739 m_pop3ConData->next_state = POP3_AUTH_LOGIN;
1740 break;
1741 case POP3_HAS_AUTH_NTLM:
1742 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP NTLM")));
1743 m_pop3ConData->next_state = POP3_AUTH_NTLM;
1744 break;
1745 case POP3_HAS_AUTH_NONE:
1746 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP no auth")));
1747 m_pop3ConData->command_succeeded = true;
1748 m_pop3ConData->next_state = POP3_NEXT_AUTH_STEP;
1749 break;
1750 case POP3_HAS_AUTH_XOAUTH2:
1751 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP XOAUTH2")));
1752 m_pop3ConData->command_succeeded = true;
1753 m_pop3ConData->next_state = POP3_AUTH_OAUTH2_RESPONSE;
1754 break;
1755 default:
1756 MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
1757 (POP3LOG("POP: m_currentAuthMethod has unknown value")));
1758 return Error("pop3AuthMechNotSupported");
1759 }
1760
1761 m_pop3ConData->pause_for_read = false;
1762 return 0;
1763 }
1764
AuthOAuth2Response()1765 int32_t nsPop3Protocol::AuthOAuth2Response() {
1766 if (!mOAuth2Support) {
1767 return Error("pop3AuthMechNotSupported");
1768 }
1769 nsresult rv = mOAuth2Support->Connect(true, this);
1770 if (NS_FAILED(rv)) {
1771 MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
1772 (POP3LOG("OAuth2 authorizattion failed")));
1773 return -1;
1774 }
1775 m_pop3ConData->pause_for_read = true;
1776 return 0;
1777 }
1778
OAuth2AuthStep()1779 int32_t nsPop3Protocol::OAuth2AuthStep() {
1780 MOZ_ASSERT(mOAuth2Support, "Can't do anything without OAuth2 support");
1781
1782 if (!m_pop3ConData->command_succeeded) {
1783 m_OAuth2String.Truncate();
1784 m_pop3ConData->next_state = POP3_ERROR_DONE;
1785 return -1;
1786 }
1787
1788 nsAutoCString cmdLine2;
1789 cmdLine2 += m_OAuth2String;
1790 cmdLine2 += CRLF;
1791
1792 m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
1793 m_password_already_sent = true;
1794 m_OAuth2String.Truncate();
1795 if (Pop3SendData(cmdLine2.get(), true)) {
1796 MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
1797 (POP3LOG("POP: XOAUTH2 authentication (second step) failed")));
1798 m_pop3ConData->next_state = POP3_ERROR_DONE;
1799 }
1800 return 0;
1801 }
1802
1803 /** msgIOAuth2ModuleListener implementation */
OnSuccess(const nsACString & aOAuth2String)1804 nsresult nsPop3Protocol::OnSuccess(const nsACString& aOAuth2String) {
1805 MOZ_ASSERT(mOAuth2Support, "Can't do anything without OAuth2 support");
1806 // Send the AUTH XOAUTH2 command, and then siphon us back to the regular
1807 // authentication login stream.
1808 nsAutoCString cmd;
1809 cmd.AppendLiteral("AUTH XOAUTH2 ");
1810 cmd += aOAuth2String;
1811 cmd += CRLF;
1812 // RFC 2449 limits command length to 255 octets. If our command is over that,
1813 // send AUTH XOAUTH2 on one line, and the actual data on the next.
1814 // The second line is treated as a blob allowing arbitrary size content.
1815 if (cmd.Length() <= 255) {
1816 m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
1817 m_password_already_sent = true;
1818 if (Pop3SendData(cmd.get(), true)) {
1819 MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
1820 (POP3LOG("POP: XOAUTH2 authentication failed")));
1821 m_pop3ConData->next_state = POP3_ERROR_DONE;
1822 }
1823 } else {
1824 // Stash the string away, in POP3_AUTH_OAUTH2_AUTH_STEP we'll send this too.
1825 m_OAuth2String.Assign(aOAuth2String);
1826
1827 nsAutoCString cmdLine1;
1828 cmdLine1.AppendLiteral("AUTH XOAUTH2");
1829 cmdLine1 += CRLF;
1830 m_pop3ConData->next_state = POP3_WAIT_FOR_RESPONSE;
1831 m_pop3ConData->pause_for_read = true;
1832 m_pop3ConData->next_state_after_response = POP3_AUTH_OAUTH2_AUTH_STEP;
1833 if (Pop3SendData(cmdLine1.get(), false)) {
1834 MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
1835 (POP3LOG("POP: XOAUTH2 authentication (line1) failed")));
1836 m_pop3ConData->next_state = POP3_ERROR_DONE;
1837 ProcessProtocolState(nullptr, nullptr, 0, 0);
1838 return NS_ERROR_FAILURE;
1839 }
1840 }
1841 ProcessProtocolState(nullptr, nullptr, 0, 0);
1842 return NS_OK;
1843 }
1844
1845 /** msgIOAuth2ModuleListener implementation */
OnFailure(nsresult aError)1846 nsresult nsPop3Protocol::OnFailure(nsresult aError) {
1847 MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
1848 ("OAuth2 login error %08x", (uint32_t)aError));
1849 m_pop3ConData->next_state = POP3_ERROR_DONE;
1850 return ProcessProtocolState(nullptr, nullptr, 0, 0);
1851 }
1852
1853 /**
1854 * state POP3_NEXT_AUTH_STEP
1855 * This is called when we finished one auth step (e.g. sending username
1856 * or password are separate steps, similarly for AUTH LOGIN, NTLM etc.)
1857 * and want to proceed to the next one.
1858 */
NextAuthStep()1859 int32_t nsPop3Protocol::NextAuthStep() {
1860 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("NextAuthStep()")));
1861 if (m_pop3ConData->command_succeeded) {
1862 if (m_password_already_sent || // (also true for GSSAPI)
1863 m_currentAuthMethod == POP3_HAS_AUTH_NONE) {
1864 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Login succeeded")));
1865 m_nsIPop3Sink->SetUserAuthenticated(true);
1866 ClearFlag(POP3_PASSWORD_FAILED);
1867 if (m_pop3ConData->verify_logon)
1868 m_pop3ConData->next_state = POP3_SEND_QUIT;
1869 else
1870 m_pop3ConData->next_state =
1871 (m_pop3ConData->get_url) ? POP3_SEND_GURL : POP3_SEND_STAT;
1872 } else
1873 m_pop3ConData->next_state = POP3_SEND_PASSWORD;
1874 } else {
1875 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1876 (POP3LOG("command did not succeed")));
1877 // response code received shows that login failed not because of
1878 // wrong credential -> stop login without retry or pw dialog, only alert
1879 // parameter list -> user
1880 nsCString userName;
1881 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
1882 nsresult rv = server->GetRealUsername(userName);
1883 NS_ENSURE_SUCCESS(rv, -1);
1884 NS_ConvertUTF8toUTF16 userNameUTF16(userName);
1885 if (TestFlag(POP3_STOPLOGIN)) {
1886 if (m_password_already_sent)
1887 return Error("pop3PasswordFailed", userNameUTF16.get());
1888
1889 return Error("pop3UsernameFailure");
1890 }
1891 // response code received shows that server is certain about the
1892 // credential was wrong -> no fallback, show alert and pw dialog
1893 if (TestFlag(POP3_AUTH_FAILURE)) {
1894 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1895 (POP3LOG("auth failure, setting password failed")));
1896 if (m_password_already_sent)
1897 Error("pop3PasswordFailed", userNameUTF16.get());
1898 else
1899 Error("pop3UsernameFailure");
1900 SetFlag(POP3_PASSWORD_FAILED);
1901 ClearFlag(POP3_AUTH_FAILURE);
1902 return 0;
1903 }
1904
1905 // We have no certain response code -> fallback and try again.
1906 // Mark the auth method failed, to use a different method next round.
1907 MarkAuthMethodAsFailed(m_currentAuthMethod);
1908
1909 if (m_currentAuthMethod == POP3_HAS_AUTH_USER && !m_password_already_sent) {
1910 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1911 (POP3LOG("USER username failed")));
1912 // if USER auth method failed before sending the password,
1913 // the username was wrong.
1914 // no fallback but return error
1915 return Error("pop3UsernameFailure");
1916 }
1917
1918 // If we have no auth method left, ask user to try with new password
1919 rv = ChooseAuthMethod();
1920 if (NS_FAILED(rv)) {
1921 MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
1922 (POP3LOG(
1923 "POP: no auth methods remaining, setting password failure")));
1924 /* Sever the connection and go back to the `read password' state,
1925 which, upon success, will re-open the connection. Set a flag
1926 which causes the prompt to be different that time (to indicate
1927 that the old password was bogus.)
1928
1929 But if we're just checking for new mail (biff) then don't bother
1930 prompting the user for a password: just fail silently.
1931 */
1932 SetFlag(POP3_PASSWORD_FAILED);
1933 Error("pop3PasswordFailed", userNameUTF16.get());
1934 return 0;
1935 }
1936 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
1937 (POP3LOG("still have some auth methods to try")));
1938
1939 // TODO needed?
1940 // m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1941
1942 m_pop3ConData->command_succeeded = true;
1943
1944 m_pop3ConData->next_state = POP3_PROCESS_AUTH;
1945 }
1946
1947 if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED)) {
1948 ClearCapFlag(POP3_AUTH_MECH_UNDEFINED);
1949 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
1950 }
1951
1952 m_pop3ConData->pause_for_read = false;
1953
1954 return 0;
1955 }
1956
1957 // LOGIN consists of three steps not two as USER/PASS or CRAM-MD5,
1958 // so we've to start here and continue in SendUsername if the server
1959 // responds + to "AUTH LOGIN"
AuthLogin()1960 int32_t nsPop3Protocol::AuthLogin() {
1961 nsAutoCString command("AUTH LOGIN" CRLF);
1962 m_pop3ConData->next_state_after_response = POP3_AUTH_LOGIN_RESPONSE;
1963 m_pop3ConData->pause_for_read = true;
1964
1965 return Pop3SendData(command.get());
1966 }
1967
AuthLoginResponse()1968 int32_t nsPop3Protocol::AuthLoginResponse() {
1969 // need the test to be here instead in NextAuthStep() to
1970 // differentiate between command AUTH LOGIN failed and
1971 // sending username using LOGIN mechanism failed.
1972 if (!m_pop3ConData->command_succeeded) {
1973 // we failed with LOGIN, remove it
1974 MarkAuthMethodAsFailed(POP3_HAS_AUTH_LOGIN);
1975 m_pop3ConData->next_state = POP3_PROCESS_AUTH;
1976 } else
1977 m_pop3ConData->next_state = POP3_SEND_USERNAME;
1978
1979 m_pop3ConData->pause_for_read = false;
1980
1981 return 0;
1982 }
1983
1984 // NTLM, like LOGIN consists of three steps not two as USER/PASS or CRAM-MD5,
1985 // so we've to start here and continue in SendUsername if the server
1986 // responds + to "AUTH NTLM"
AuthNtlm()1987 int32_t nsPop3Protocol::AuthNtlm() {
1988 nsAutoCString command(m_currentAuthMethod == POP3_HAS_AUTH_MSN
1989 ? "AUTH MSN" CRLF
1990 : "AUTH NTLM" CRLF);
1991 m_pop3ConData->next_state_after_response = POP3_AUTH_NTLM_RESPONSE;
1992 m_pop3ConData->pause_for_read = true;
1993
1994 return Pop3SendData(command.get());
1995 }
1996
AuthNtlmResponse()1997 int32_t nsPop3Protocol::AuthNtlmResponse() {
1998 // need the test to be here instead in NextAuthStep() to
1999 // differentiate between command AUTH NTLM failed and
2000 // sending username using NTLM mechanism failed.
2001 if (!m_pop3ConData->command_succeeded) {
2002 MarkAuthMethodAsFailed(POP3_HAS_AUTH_NTLM);
2003 MarkAuthMethodAsFailed(POP3_HAS_AUTH_MSN);
2004 m_pop3ConData->next_state = POP3_PROCESS_AUTH;
2005 } else
2006 m_pop3ConData->next_state = POP3_SEND_USERNAME;
2007
2008 m_pop3ConData->pause_for_read = false;
2009
2010 return 0;
2011 }
2012
AuthGSSAPI()2013 int32_t nsPop3Protocol::AuthGSSAPI() {
2014 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("AuthGSSAPI()")));
2015 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
2016 if (server) {
2017 nsAutoCString cmd;
2018 nsAutoCString service("pop@");
2019 nsCString hostName;
2020 nsresult rv;
2021 server->GetRealHostName(hostName);
2022 service.Append(hostName);
2023 rv = DoGSSAPIStep1(service.get(), m_username.get(), cmd);
2024 if (NS_SUCCEEDED(rv)) {
2025 m_GSSAPICache.Assign(cmd);
2026 m_pop3ConData->next_state_after_response = POP3_AUTH_GSSAPI_FIRST;
2027 m_pop3ConData->pause_for_read = true;
2028 return Pop3SendData("AUTH GSSAPI" CRLF);
2029 }
2030 }
2031
2032 MarkAuthMethodAsFailed(POP3_HAS_AUTH_GSSAPI);
2033 m_pop3ConData->next_state = POP3_PROCESS_AUTH;
2034 m_pop3ConData->pause_for_read = false;
2035 return 0;
2036 }
2037
AuthGSSAPIResponse(bool first)2038 int32_t nsPop3Protocol::AuthGSSAPIResponse(bool first) {
2039 if (!m_pop3ConData->command_succeeded) {
2040 if (first) m_GSSAPICache.Truncate();
2041 MarkAuthMethodAsFailed(POP3_HAS_AUTH_GSSAPI);
2042 m_pop3ConData->next_state = POP3_PROCESS_AUTH;
2043 m_pop3ConData->pause_for_read = false;
2044 return 0;
2045 }
2046
2047 int32_t result;
2048
2049 m_pop3ConData->next_state_after_response = POP3_AUTH_GSSAPI_STEP;
2050 m_pop3ConData->pause_for_read = true;
2051
2052 if (first) {
2053 m_GSSAPICache += CRLF;
2054 result = Pop3SendData(m_GSSAPICache.get());
2055 m_GSSAPICache.Truncate();
2056 } else {
2057 nsAutoCString cmd;
2058 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("GSSAPI step 2")));
2059 nsresult rv = DoGSSAPIStep2(m_commandResponse, cmd);
2060 if (NS_FAILED(rv)) cmd = "*";
2061 if (rv == NS_SUCCESS_AUTH_FINISHED) {
2062 m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
2063 m_password_already_sent = true;
2064 }
2065 cmd += CRLF;
2066 result = Pop3SendData(cmd.get());
2067 }
2068
2069 return result;
2070 }
2071
SendUsername()2072 int32_t nsPop3Protocol::SendUsername() {
2073 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendUsername()")));
2074 if (m_username.IsEmpty()) return Error("pop3UsernameUndefined");
2075
2076 // <copied from="SendPassword()">
2077 // Needed for NTLM
2078
2079 // The POP3_SEND_PASSWORD/POP3_WAIT_SEND_PASSWORD states have already
2080 // got the password - they will have cancelled if necessary.
2081 // If the password is still empty here, don't try to go on.
2082 if (m_passwordResult.IsEmpty()) {
2083 m_pop3ConData->next_state = POP3_ERROR_DONE;
2084 return Error("pop3PasswordUndefined");
2085 }
2086 // </copied>
2087
2088 nsAutoCString cmd;
2089
2090 if (m_currentAuthMethod == POP3_HAS_AUTH_NTLM)
2091 (void)DoNtlmStep1(m_username, m_passwordResult, cmd);
2092 else if (m_currentAuthMethod == POP3_HAS_AUTH_CRAM_MD5)
2093 cmd = "AUTH CRAM-MD5";
2094 else if (m_currentAuthMethod == POP3_HAS_AUTH_PLAIN)
2095 cmd = "AUTH PLAIN";
2096 else if (m_currentAuthMethod == POP3_HAS_AUTH_LOGIN) {
2097 char* base64Str =
2098 PL_Base64Encode(m_username.get(), m_username.Length(), nullptr);
2099 cmd = base64Str;
2100 PR_Free(base64Str);
2101 } else if (m_currentAuthMethod == POP3_HAS_AUTH_USER) {
2102 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("USER login")));
2103 cmd = "USER ";
2104 cmd += m_username;
2105 } else {
2106 MOZ_LOG(
2107 POP3LOGMODULE, LogLevel::Error,
2108 (POP3LOG(
2109 "In nsPop3Protocol::SendUsername(), m_currentAuthMethod is 0x%X, "
2110 "but that is unexpected"),
2111 m_currentAuthMethod));
2112 return Error("pop3AuthInternalError");
2113 }
2114
2115 cmd += CRLF;
2116
2117 m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
2118
2119 m_pop3ConData->pause_for_read = true;
2120
2121 return Pop3SendData(cmd.get());
2122 }
2123
SendPassword()2124 int32_t nsPop3Protocol::SendPassword() {
2125 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendPassword()")));
2126 if (m_username.IsEmpty()) return Error("pop3UsernameUndefined");
2127
2128 // <copied to="SendUsername()">
2129 // Needed here, too, because APOP skips SendUsername()
2130 // The POP3_SEND_PASSWORD/POP3_WAIT_SEND_PASSWORD states have already
2131 // got the password - they will have cancelled if necessary.
2132 // If the password is still empty here, don't try to go on.
2133 if (m_passwordResult.IsEmpty()) {
2134 m_pop3ConData->next_state = POP3_ERROR_DONE;
2135 return Error("pop3PasswordUndefined");
2136 }
2137 // </copied>
2138
2139 nsAutoCString cmd;
2140 nsresult rv;
2141 NS_ConvertUTF16toUTF8 passwordUTF8(m_passwordResult);
2142
2143 if (m_currentAuthMethod == POP3_HAS_AUTH_NTLM)
2144 rv = DoNtlmStep2(m_commandResponse, cmd);
2145 else if (m_currentAuthMethod == POP3_HAS_AUTH_CRAM_MD5) {
2146 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("CRAM login")));
2147 char buffer[255 + 1 + 2 * DIGEST_LENGTH + 1];
2148 unsigned char digest[DIGEST_LENGTH];
2149
2150 char* decodedChallenge = PL_Base64Decode(
2151 m_commandResponse.get(), m_commandResponse.Length(), nullptr);
2152
2153 if (decodedChallenge)
2154 rv = MSGCramMD5(decodedChallenge, strlen(decodedChallenge),
2155 passwordUTF8.get(), passwordUTF8.Length(), digest);
2156 else
2157 rv = NS_ERROR_NULL_POINTER;
2158
2159 if (NS_SUCCEEDED(rv)) {
2160 // The encoded digest is the hexadecimal representation of
2161 // DIGEST_LENGTH characters, so it will be twice that length.
2162 nsAutoCStringN<2 * DIGEST_LENGTH> encodedDigest;
2163
2164 for (uint32_t j = 0; j < DIGEST_LENGTH; j++) {
2165 char hexVal[3];
2166 PR_snprintf(hexVal, 3, "%.2x", 0x0ff & (unsigned short)digest[j]);
2167 encodedDigest.Append(hexVal);
2168 }
2169
2170 PR_snprintf(buffer, sizeof(buffer), "%.255s %s", m_username.get(),
2171 encodedDigest.get());
2172 char* base64Str = PL_Base64Encode(buffer, strlen(buffer), nullptr);
2173 cmd.Adopt(base64Str);
2174 }
2175
2176 if (NS_FAILED(rv)) cmd = "*";
2177 } else if (m_currentAuthMethod == POP3_HAS_AUTH_APOP) {
2178 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("APOP login")));
2179 char buffer[5 + 255 + 1 + 2 * DIGEST_LENGTH + 1];
2180 unsigned char digest[DIGEST_LENGTH];
2181
2182 rv = MSGApopMD5(m_ApopTimestamp.get(), m_ApopTimestamp.Length(),
2183 passwordUTF8.get(), passwordUTF8.Length(), digest);
2184
2185 if (NS_SUCCEEDED(rv)) {
2186 // The encoded digest is the hexadecimal representation of
2187 // DIGEST_LENGTH characters, so it will be twice that length.
2188 nsAutoCStringN<2 * DIGEST_LENGTH> encodedDigest;
2189
2190 for (uint32_t j = 0; j < DIGEST_LENGTH; j++) {
2191 char hexVal[3];
2192 PR_snprintf(hexVal, 3, "%.2x", 0x0ff & (unsigned short)digest[j]);
2193 encodedDigest.Append(hexVal);
2194 }
2195
2196 PR_snprintf(buffer, sizeof(buffer), "APOP %.255s %s", m_username.get(),
2197 encodedDigest.get());
2198 cmd = buffer;
2199 }
2200
2201 if (NS_FAILED(rv)) cmd = "*";
2202 } else if (m_currentAuthMethod == POP3_HAS_AUTH_PLAIN) {
2203 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("PLAIN login")));
2204 // workaround for IPswitch's IMail server software
2205 // this server goes into LOGIN mode even if we send "AUTH PLAIN"
2206 // "VXNlc" is the beginning of the base64 encoded prompt ("Username:") for
2207 // LOGIN
2208 if (StringBeginsWith(m_commandResponse, "VXNlc"_ns)) {
2209 // disable PLAIN and enable LOGIN (in case it's not already enabled)
2210 ClearCapFlag(POP3_HAS_AUTH_PLAIN);
2211 SetCapFlag(POP3_HAS_AUTH_LOGIN);
2212 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
2213
2214 // reenter authentication again at LOGIN response handler
2215 m_pop3ConData->next_state = POP3_AUTH_LOGIN_RESPONSE;
2216 m_pop3ConData->pause_for_read = false;
2217 return 0;
2218 }
2219
2220 char plain_string[513];
2221 memset(plain_string, 0, 513);
2222 PR_snprintf(&plain_string[1], 256, "%.255s", m_username.get());
2223 uint32_t len = std::min<uint32_t>(m_username.Length(), 255u) +
2224 2; // We include two <NUL> characters.
2225 if (passwordUTF8.Length() > 255) // RFC 4616: passwd; up to 255 octets
2226 passwordUTF8.Truncate(255);
2227 PR_snprintf(&plain_string[len], 256, "%s", passwordUTF8.get());
2228 len += passwordUTF8.Length();
2229
2230 char* base64Str = PL_Base64Encode(plain_string, len, nullptr);
2231 cmd.Adopt(base64Str);
2232 } else if (m_currentAuthMethod == POP3_HAS_AUTH_LOGIN) {
2233 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("LOGIN password")));
2234 char* base64Str =
2235 PL_Base64Encode(passwordUTF8.get(), passwordUTF8.Length(), nullptr);
2236 cmd.Adopt(base64Str);
2237 } else if (m_currentAuthMethod == POP3_HAS_AUTH_USER) {
2238 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("PASS password")));
2239 cmd = "PASS ";
2240 bool useLatin1 = mozilla::Preferences::GetBool(
2241 "mail.smtp_login_pop3_user_pass_auth_is_latin1", true);
2242 if (useLatin1)
2243 cmd += NS_LossyConvertUTF16toASCII(m_passwordResult);
2244 else
2245 cmd += passwordUTF8;
2246 } else {
2247 MOZ_LOG(
2248 POP3LOGMODULE, LogLevel::Error,
2249 (POP3LOG(
2250 "In nsPop3Protocol::SendPassword(), m_currentAuthMethod is %X, "
2251 "but that is unexpected"),
2252 m_currentAuthMethod));
2253 return Error("pop3AuthInternalError");
2254 }
2255
2256 cmd += CRLF;
2257
2258 // TODO needed?
2259 // m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
2260
2261 m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
2262
2263 m_pop3ConData->pause_for_read = true;
2264
2265 m_password_already_sent = true;
2266 m_lastPasswordSent = m_passwordResult;
2267 return Pop3SendData(cmd.get(), true);
2268 }
2269
SendStatOrGurl(bool sendStat)2270 int32_t nsPop3Protocol::SendStatOrGurl(bool sendStat) {
2271 nsAutoCString cmd;
2272 if (sendStat) {
2273 cmd = "STAT" CRLF;
2274 m_pop3ConData->next_state_after_response = POP3_GET_STAT;
2275 } else {
2276 cmd = "GURL" CRLF;
2277 m_pop3ConData->next_state_after_response = POP3_GURL_RESPONSE;
2278 }
2279 return Pop3SendData(cmd.get());
2280 }
2281
SendStat()2282 int32_t nsPop3Protocol::SendStat() { return SendStatOrGurl(true); }
2283
GetStat()2284 int32_t nsPop3Protocol::GetStat() {
2285 // check stat response
2286 if (!m_pop3ConData->command_succeeded) return Error("pop3StatFail");
2287
2288 /* stat response looks like: %d %d
2289 * The first number is the number of articles
2290 * The second number is the number of bytes
2291 *
2292 * grab the first and second arg of stat response
2293 */
2294 nsCString oldStr(m_commandResponse);
2295 char* newStr = oldStr.BeginWriting();
2296 char* num = NS_strtok(" ", &newStr); // msg num
2297 if (num) {
2298 m_pop3ConData->number_of_messages = atol(num); // bytes
2299 num = NS_strtok(" ", &newStr);
2300 m_commandResponse = newStr;
2301 if (num)
2302 m_totalFolderSize =
2303 nsCRT::atoll(num); // we always initialize m_totalFolderSize to 0
2304 } else
2305 m_pop3ConData->number_of_messages = 0;
2306
2307 m_pop3ConData->really_new_messages = 0;
2308 m_pop3ConData->real_new_counter = 1;
2309
2310 m_totalDownloadSize = -1; // Means we need to calculate it, later.
2311
2312 if (m_pop3ConData->number_of_messages <= 0) {
2313 // We're all done. We know we have no mail.
2314 m_pop3ConData->next_state = POP3_SEND_QUIT;
2315 PL_HashTableEnumerateEntries(m_pop3ConData->uidlinfo->hash,
2316 hash_clear_mapper, nullptr);
2317 // Hack - use nsPop3Sink to wipe out any stale Partial messages
2318 m_nsIPop3Sink->BeginMailDelivery(false, nullptr, nullptr);
2319 m_nsIPop3Sink->AbortMailDelivery(this);
2320 return (0);
2321 }
2322
2323 /* We're just checking for new mail, and we're not playing any games that
2324 involve keeping messages on the server. Therefore, we now know enough
2325 to finish up. If we had no messages, that would have been handled
2326 above; therefore, we know we have some new messages.
2327 */
2328 if (m_pop3ConData->only_check_for_new_mail &&
2329 !m_pop3ConData->leave_on_server) {
2330 m_nsIPop3Sink->SetBiffStateAndUpdateFE(nsIMsgFolder::nsMsgBiffState_NewMail,
2331 m_pop3ConData->number_of_messages,
2332 true);
2333 m_pop3ConData->next_state = POP3_SEND_QUIT;
2334 return (0);
2335 }
2336
2337 if (!m_pop3ConData->only_check_for_new_mail) {
2338 /* The following was added to prevent the loss of Data when we try and
2339 write to somewhere we don't have write access error to (See bug 62480)
2340 (Note: This is only a temp hack until the underlying XPCOM is fixed
2341 to return errors) */
2342
2343 nsresult rv;
2344 nsCOMPtr<nsIMsgWindow> msgWindow;
2345 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url);
2346 if (mailnewsUrl) rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
2347 // NS_ASSERTION(NS_SUCCEEDED(rv) && msgWindow, "no msg window");
2348
2349 rv = m_nsIPop3Sink->BeginMailDelivery(m_pop3ConData->only_uidl != nullptr,
2350 msgWindow,
2351 &m_pop3ConData->msg_del_started);
2352 if (NS_FAILED(rv)) {
2353 m_nsIPop3Sink->AbortMailDelivery(this);
2354 if (rv == NS_MSG_FOLDER_BUSY) {
2355 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
2356 nsString accountName;
2357 rv = server->GetPrettyName(accountName);
2358 NS_ENSURE_SUCCESS(rv, -1);
2359
2360 return Error("pop3ServerBusy", accountName.get());
2361 }
2362
2363 return Error("pop3MessageWriteError");
2364 }
2365
2366 if (!m_pop3ConData->msg_del_started) return Error("pop3MessageWriteError");
2367 }
2368
2369 m_pop3ConData->next_state = POP3_SEND_LIST;
2370 return 0;
2371 }
2372
SendGurl()2373 int32_t nsPop3Protocol::SendGurl() {
2374 if (m_pop3ConData->capability_flags == POP3_CAPABILITY_UNDEFINED ||
2375 TestCapFlag(POP3_GURL_UNDEFINED | POP3_HAS_GURL))
2376 return SendStatOrGurl(false);
2377 else
2378 return -1;
2379 }
2380
GurlResponse()2381 int32_t nsPop3Protocol::GurlResponse() {
2382 ClearCapFlag(POP3_GURL_UNDEFINED);
2383
2384 if (m_pop3ConData->command_succeeded) {
2385 SetCapFlag(POP3_HAS_GURL);
2386 if (m_nsIPop3Sink) m_nsIPop3Sink->SetMailAccountURL(m_commandResponse);
2387 } else {
2388 ClearCapFlag(POP3_HAS_GURL);
2389 }
2390 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
2391 m_pop3ConData->next_state = POP3_SEND_QUIT;
2392
2393 return 0;
2394 }
2395
SendList()2396 int32_t nsPop3Protocol::SendList() {
2397 // check for server returning number of messages that will cause the
2398 // calculation of the size of the block for msg_info to overflow a 32 bit int,
2399 // in turn causing us to allocate a block of memory much smaller than we think
2400 // we're allocating, and potentially allowing the server to make us overwrite
2401 // memory outside our heap block.
2402
2403 if (m_pop3ConData->number_of_messages >
2404 (int)(0xFFFFF000 / sizeof(Pop3MsgInfo)))
2405 return MK_OUT_OF_MEMORY;
2406
2407 m_pop3ConData->msg_info = (Pop3MsgInfo*)PR_CALLOC(
2408 sizeof(Pop3MsgInfo) * m_pop3ConData->number_of_messages);
2409 if (!m_pop3ConData->msg_info) return (MK_OUT_OF_MEMORY);
2410 m_pop3ConData->next_state_after_response = POP3_GET_LIST;
2411 m_listpos = 0;
2412 return Pop3SendData("LIST" CRLF);
2413 }
2414
GetList(nsIInputStream * inputStream,uint32_t length)2415 int32_t nsPop3Protocol::GetList(nsIInputStream* inputStream, uint32_t length) {
2416 /* check list response
2417 * This will get called multiple times
2418 * but it's alright since command_succeeded
2419 * will remain constant
2420 */
2421 if (!m_pop3ConData->command_succeeded) return Error("pop3ListFailure");
2422
2423 uint32_t ln = 0;
2424 bool pauseForMoreData = false;
2425 nsresult rv;
2426 char* line =
2427 m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
2428 if (NS_FAILED(rv)) return -1;
2429
2430 if (pauseForMoreData || !line) {
2431 m_pop3ConData->pause_for_read = true;
2432 PR_Free(line);
2433 return (ln);
2434 }
2435
2436 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
2437
2438 /* parse the line returned from the list command
2439 * it looks like
2440 * #msg_number #bytes
2441 *
2442 * list data is terminated by a ".CRLF" line
2443 */
2444 if (!PL_strcmp(line, ".")) {
2445 // limit the list if fewer entries than given in STAT response
2446 if (m_listpos < m_pop3ConData->number_of_messages)
2447 m_pop3ConData->number_of_messages = m_listpos;
2448 m_pop3ConData->next_state = POP3_SEND_UIDL_LIST;
2449 m_pop3ConData->pause_for_read = false;
2450 PR_Free(line);
2451 return (0);
2452 }
2453
2454 char* newStr = line;
2455 char* token = NS_strtok(" ", &newStr);
2456 if (token) {
2457 int32_t msg_num = atol(token);
2458
2459 if (++m_listpos <= m_pop3ConData->number_of_messages) {
2460 token = NS_strtok(" ", &newStr);
2461 if (token) {
2462 m_pop3ConData->msg_info[m_listpos - 1].size = atol(token);
2463 m_pop3ConData->msg_info[m_listpos - 1].msgnum = msg_num;
2464 }
2465 }
2466 }
2467
2468 PR_Free(line);
2469 return (0);
2470 }
2471
2472 /* UIDL and XTND are both unsupported for this mail server.
2473 If not enabled any advanced features, we're able to live
2474 without them. We're simply downloading and deleting everything
2475 on the server.
2476
2477 Advanced features are:
2478 *'Keep Mail on Server' with aging or deletion support
2479 *'Fetch Headers Only'
2480 *'Limit Message Size'
2481 *only download a specific UID
2482
2483 These require knowledge of of all messages UID's on the server at
2484 least when it comes to deleting deleting messages on server that
2485 have been deleted on client or vice versa. TOP doesn't help here
2486 without generating huge traffic and is mostly not supported at all
2487 if the server lacks UIDL and XTND XLST.
2488
2489 In other cases the user has to join the 20th century.
2490 Tell the user this, and refuse to download any messages until
2491 they've gone into preferences and turned off any of the above
2492 prefs.
2493 */
HandleNoUidListAvailable()2494 int32_t nsPop3Protocol::HandleNoUidListAvailable() {
2495 m_pop3ConData->pause_for_read = false;
2496
2497 if (!m_pop3ConData->leave_on_server && !m_pop3ConData->headers_only &&
2498 m_pop3ConData->size_limit <= 0 && !m_pop3ConData->only_uidl) {
2499 m_pop3ConData->next_state = POP3_GET_MSG;
2500 return 0;
2501 }
2502 m_pop3ConData->next_state = POP3_SEND_QUIT;
2503 nsCString hostName;
2504 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
2505 nsresult rv = server->GetRealHostName(hostName);
2506 NS_ENSURE_SUCCESS(rv, -1);
2507 NS_ConvertASCIItoUTF16 hostNameUnicode(hostName);
2508 return Error("pop3ServerDoesNotSupportUidlEtc", hostNameUnicode.get());
2509 }
2510
2511 /* km
2512 *
2513 * net_pop3_send_xtnd_xlst_msgid
2514 *
2515 * Process state: POP3_SEND_XTND_XLST_MSGID
2516 *
2517 * If we get here then UIDL is not supported by the mail server.
2518 * Some mail servers support a similar command:
2519 *
2520 * XTND XLST Message-Id
2521 *
2522 * Here is a sample transaction from a QUALCOMM server
2523
2524 >>XTND XLST Message-Id
2525 <<+OK xlst command accepted; headers coming.
2526 <<1 Message-ID: <3117E4DC.2699@netscape.invalid>
2527 <<2 Message-Id: <199602062335.PAA19215@lemon.example.com>
2528
2529 * This function will send the xtnd command and put us into the
2530 * POP3_GET_XTND_XLST_MSGID state
2531 *
2532 */
SendXtndXlstMsgid()2533 int32_t nsPop3Protocol::SendXtndXlstMsgid() {
2534 if (TestCapFlag(POP3_HAS_XTND_XLST | POP3_XTND_XLST_UNDEFINED)) {
2535 m_pop3ConData->next_state_after_response = POP3_GET_XTND_XLST_MSGID;
2536 m_pop3ConData->pause_for_read = true;
2537 m_listpos = 0;
2538 return Pop3SendData("XTND XLST Message-Id" CRLF);
2539 } else
2540 return HandleNoUidListAvailable();
2541 }
2542
2543 /* km
2544 *
2545 * net_pop3_get_xtnd_xlst_msgid
2546 *
2547 * This code was created from the net_pop3_get_uidl_list boiler plate.
2548 * The difference is that the XTND reply strings have one more token per
2549 * string than the UIDL reply strings do.
2550 *
2551 */
2552
GetXtndXlstMsgid(nsIInputStream * inputStream,uint32_t length)2553 int32_t nsPop3Protocol::GetXtndXlstMsgid(nsIInputStream* inputStream,
2554 uint32_t length) {
2555 /* check list response
2556 * This will get called multiple times
2557 * but it's alright since command_succeeded
2558 * will remain constant
2559 */
2560 ClearCapFlag(POP3_XTND_XLST_UNDEFINED);
2561
2562 if (!m_pop3ConData->command_succeeded) {
2563 ClearCapFlag(POP3_HAS_XTND_XLST);
2564 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
2565 HandleNoUidListAvailable();
2566 return (0);
2567 } else {
2568 SetCapFlag(POP3_HAS_XTND_XLST);
2569 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
2570 }
2571
2572 uint32_t ln = 0;
2573 bool pauseForMoreData = false;
2574 nsresult rv;
2575 char* line =
2576 m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
2577 if (NS_FAILED(rv)) return -1;
2578
2579 if (pauseForMoreData || !line) {
2580 m_pop3ConData->pause_for_read = true;
2581 PR_Free(line);
2582 return ln;
2583 }
2584
2585 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
2586
2587 /* parse the line returned from the list command
2588 * it looks like
2589 * 1 Message-ID: <3117E4DC.2699@example.com>
2590 *
2591 * list data is terminated by a ".CRLF" line
2592 */
2593 if (!PL_strcmp(line, ".")) {
2594 // limit the list if fewer entries than given in STAT response
2595 if (m_listpos < m_pop3ConData->number_of_messages)
2596 m_pop3ConData->number_of_messages = m_listpos;
2597 m_pop3ConData->list_done = true;
2598 m_pop3ConData->next_state = POP3_GET_MSG;
2599 m_pop3ConData->pause_for_read = false;
2600 PR_Free(line);
2601 return (0);
2602 }
2603
2604 char* newStr = line;
2605 char* token = NS_strtok(" ", &newStr); // msg num
2606 if (token) {
2607 int32_t msg_num = atol(token);
2608 if (++m_listpos <= m_pop3ConData->number_of_messages) {
2609 NS_strtok(" ", &newStr); // eat message ID token
2610 const char* uid =
2611 NS_strtok(" ", &newStr); // not really a UID but a unique token -km
2612 if (!uid)
2613 /* This is bad. The server didn't give us a UIDL for this message.
2614 I've seen this happen when somehow the mail spool has a message
2615 that contains a header that reads "X-UIDL: \n". But how that got
2616 there, I have no idea; must be a server bug. Or something. */
2617 uid = "";
2618
2619 // seeking right entry, but try the one that should it be first
2620 int32_t i;
2621 if (m_pop3ConData->msg_info[m_listpos - 1].msgnum == msg_num)
2622 i = m_listpos - 1;
2623 else
2624 for (i = 0; i < m_pop3ConData->number_of_messages &&
2625 m_pop3ConData->msg_info[i].msgnum != msg_num;
2626 i++)
2627 ;
2628
2629 // only if found a matching slot
2630 if (i < m_pop3ConData->number_of_messages) {
2631 // to protect us from memory leak in case of getting a msg num twice
2632 m_pop3ConData->msg_info[i].uidl = PL_strdup(uid);
2633 if (!m_pop3ConData->msg_info[i].uidl) {
2634 PR_Free(line);
2635 return MK_OUT_OF_MEMORY;
2636 }
2637 }
2638 }
2639 }
2640
2641 PR_Free(line);
2642 return (0);
2643 }
2644
SendUidlList()2645 int32_t nsPop3Protocol::SendUidlList() {
2646 if (TestCapFlag(POP3_HAS_UIDL | POP3_UIDL_UNDEFINED)) {
2647 m_pop3ConData->next_state_after_response = POP3_GET_UIDL_LIST;
2648 m_pop3ConData->pause_for_read = true;
2649 m_listpos = 0;
2650 return Pop3SendData("UIDL" CRLF);
2651 } else
2652 return SendXtndXlstMsgid();
2653 }
2654
GetUidlList(nsIInputStream * inputStream,uint32_t length)2655 int32_t nsPop3Protocol::GetUidlList(nsIInputStream* inputStream,
2656 uint32_t length) {
2657 if (m_pop3ConData->command_temp_fail) {
2658 nsCString hostName;
2659 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
2660 nsresult rv = server->GetRealHostName(hostName);
2661 NS_ENSURE_SUCCESS(rv, -1);
2662 NS_ConvertASCIItoUTF16 hostNameUnicode(hostName);
2663 return Error("pop3TempServerError", hostNameUnicode.get());
2664 }
2665
2666 /* check list response
2667 * This will get called multiple times
2668 * but it's alright since command_succeeded
2669 * will remain constant
2670 */
2671 ClearCapFlag(POP3_UIDL_UNDEFINED);
2672
2673 if (!m_pop3ConData->command_succeeded) {
2674 m_pop3ConData->next_state = POP3_SEND_XTND_XLST_MSGID;
2675 m_pop3ConData->pause_for_read = false;
2676 ClearCapFlag(POP3_HAS_UIDL);
2677 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
2678 return (0);
2679 } else {
2680 SetCapFlag(POP3_HAS_UIDL);
2681 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
2682 }
2683
2684 uint32_t ln = 0;
2685 bool pauseForMoreData = false;
2686 nsresult rv;
2687 char* line =
2688 m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
2689 if (NS_FAILED(rv)) return -1;
2690
2691 if (pauseForMoreData || !line) {
2692 PR_Free(line);
2693 m_pop3ConData->pause_for_read = true;
2694 return ln;
2695 }
2696
2697 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
2698
2699 /* parse the line returned from the list command
2700 * it looks like
2701 * #msg_number uidl
2702 *
2703 * list data is terminated by a ".CRLF" line
2704 */
2705 if (!PL_strcmp(line, ".")) {
2706 // limit the list if fewer entries than given in STAT response
2707 if (m_listpos < m_pop3ConData->number_of_messages)
2708 m_pop3ConData->number_of_messages = m_listpos;
2709 m_pop3ConData->list_done = true;
2710 m_pop3ConData->next_state = POP3_GET_MSG;
2711 m_pop3ConData->pause_for_read = false;
2712 PR_Free(line);
2713 return (0);
2714 }
2715
2716 char* newStr = line;
2717 char* token = NS_strtok(" ", &newStr); // msg num
2718 if (token) {
2719 int32_t msg_num = atol(token);
2720 if (++m_listpos <= m_pop3ConData->number_of_messages) {
2721 const char* uid = NS_strtok(" ", &newStr); // UID
2722 if (!uid)
2723 /* This is bad. The server didn't give us a UIDL for this message.
2724 I've seen this happen when somehow the mail spool has a message
2725 that contains a header that reads "X-UIDL: \n". But how that got
2726 there, I have no idea; must be a server bug. Or something. */
2727 uid = "";
2728
2729 // seeking right entry, but try the one that should it be first
2730 int32_t i;
2731 if (m_pop3ConData->msg_info[m_listpos - 1].msgnum == msg_num)
2732 i = m_listpos - 1;
2733 else
2734 for (i = 0; i < m_pop3ConData->number_of_messages &&
2735 m_pop3ConData->msg_info[i].msgnum != msg_num;
2736 i++)
2737 ;
2738
2739 // only if found a matching slot
2740 if (i < m_pop3ConData->number_of_messages) {
2741 // to protect us from memory leak in case of getting a msg num twice
2742 m_pop3ConData->msg_info[i].uidl = PL_strdup(uid);
2743 if (!m_pop3ConData->msg_info[i].uidl) {
2744 PR_Free(line);
2745 return MK_OUT_OF_MEMORY;
2746 }
2747 }
2748 }
2749 }
2750 PR_Free(line);
2751 return (0);
2752 }
2753
2754 /* this function decides if we are going to do a
2755 * normal RETR or a TOP. The first time, it also decides the total number
2756 * of bytes we're probably going to get.
2757 */
GetMsg()2758 int32_t nsPop3Protocol::GetMsg() {
2759 int32_t popstateTimestamp = TimeInSecondsFromPRTime(PR_Now());
2760
2761 if (m_pop3ConData->last_accessed_msg >= m_pop3ConData->number_of_messages) {
2762 /* Oh, gee, we're all done. */
2763 if (m_pop3ConData->msg_del_started) {
2764 if (!m_pop3ConData->only_uidl) {
2765 if (m_pop3ConData->only_check_for_new_mail)
2766 m_nsIPop3Sink->SetBiffStateAndUpdateFE(
2767 m_pop3ConData->biffstate, m_pop3ConData->really_new_messages,
2768 true);
2769 /* update old style biff */
2770 else
2771 m_nsIPop3Sink->SetBiffStateAndUpdateFE(
2772 nsIMsgFolder::nsMsgBiffState_NewMail,
2773 m_pop3ConData->really_new_messages, false);
2774 }
2775 m_nsIPop3Sink->EndMailDelivery(this);
2776 }
2777
2778 m_pop3ConData->next_state = POP3_SEND_QUIT;
2779 return 0;
2780 }
2781
2782 if (m_totalDownloadSize < 0) {
2783 /* First time. Figure out how many bytes we're about to get.
2784 If we didn't get any message info, then we are going to get
2785 everything, and it's easy. Otherwise, if we only want one
2786 uidl, than that's the only one we'll get. Otherwise, go
2787 through each message info, decide if we're going to get that
2788 message, and add the number of bytes for it. When a message is too
2789 large (per user's preferences) only add the size we are supposed
2790 to get. */
2791 m_pop3ConData->really_new_messages = 0;
2792 m_pop3ConData->real_new_counter = 1;
2793 if (m_pop3ConData->msg_info) {
2794 m_totalDownloadSize = 0;
2795 for (int32_t i = 0; i < m_pop3ConData->number_of_messages; i++) {
2796 if (m_pop3ConData->only_uidl) {
2797 if (m_pop3ConData->msg_info[i].uidl &&
2798 !PL_strcmp(m_pop3ConData->msg_info[i].uidl,
2799 m_pop3ConData->only_uidl)) {
2800 m_totalDownloadSize = m_pop3ConData->msg_info[i].size;
2801 m_pop3ConData->really_new_messages = 1;
2802 // we are only getting one message
2803 m_pop3ConData->real_new_counter = 1;
2804 break;
2805 }
2806 continue;
2807 }
2808
2809 char c = 0;
2810 popstateTimestamp = TimeInSecondsFromPRTime(PR_Now());
2811 if (m_pop3ConData->msg_info[i].uidl) {
2812 Pop3UidlEntry* uidlEntry = (Pop3UidlEntry*)PL_HashTableLookup(
2813 m_pop3ConData->uidlinfo->hash, m_pop3ConData->msg_info[i].uidl);
2814 if (uidlEntry) {
2815 c = uidlEntry->status;
2816 popstateTimestamp = uidlEntry->dateReceived;
2817 }
2818 }
2819 if ((c == KEEP) &&
2820 !m_pop3ConData
2821 ->leave_on_server) { /* This message has been downloaded but
2822 * kept on server, we no longer want to
2823 * keep it there */
2824 if (!m_pop3ConData->newuidl) {
2825 m_pop3ConData->newuidl =
2826 PL_NewHashTable(20, PL_HashString, PL_CompareStrings,
2827 PL_CompareValues, &gHashAllocOps, nullptr);
2828 if (!m_pop3ConData->newuidl) return MK_OUT_OF_MEMORY;
2829 }
2830 c = DELETE_CHAR;
2831 // Mark message to be deleted in new table
2832 put_hash(m_pop3ConData->newuidl, m_pop3ConData->msg_info[i].uidl,
2833 DELETE_CHAR, popstateTimestamp);
2834 // and old one too
2835 put_hash(m_pop3ConData->uidlinfo->hash,
2836 m_pop3ConData->msg_info[i].uidl, DELETE_CHAR,
2837 popstateTimestamp);
2838 }
2839 if ((c != KEEP) && (c != DELETE_CHAR) &&
2840 (c != TOO_BIG)) { // message left on server
2841 m_totalDownloadSize += m_pop3ConData->msg_info[i].size;
2842 m_pop3ConData->really_new_messages++;
2843 // a message we will really download
2844 }
2845 }
2846 } else {
2847 m_totalDownloadSize = m_totalFolderSize;
2848 }
2849 if (m_pop3ConData->only_check_for_new_mail) {
2850 if (m_totalDownloadSize > 0) {
2851 m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_NewMail;
2852 m_nsIPop3Sink->SetBiffStateAndUpdateFE(
2853 nsIMsgFolder::nsMsgBiffState_NewMail,
2854 m_pop3ConData->really_new_messages, true);
2855 }
2856 m_pop3ConData->next_state = POP3_SEND_QUIT;
2857 return (0);
2858 }
2859 /* get the amount of available space on the drive
2860 * and make sure there is enough
2861 */
2862 if (m_totalDownloadSize > 0) // skip all this if there aren't any messages
2863 {
2864 nsCOMPtr<nsIMsgFolder> folder;
2865
2866 // Get the current mailbox folder
2867 NS_ENSURE_TRUE(m_nsIPop3Sink, -1);
2868 nsresult rv = m_nsIPop3Sink->GetFolder(getter_AddRefs(folder));
2869 if (NS_FAILED(rv)) return -1;
2870
2871 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
2872 if (NS_FAILED(rv) || !mailnewsUrl) return -1;
2873
2874 nsCOMPtr<nsIMsgWindow> msgWindow;
2875 (void)mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
2876
2877 bool spaceNotAvailable = true;
2878 nsCOMPtr<nsIMsgLocalMailFolder> localFolder(
2879 do_QueryInterface(folder, &rv));
2880 if (NS_FAILED(rv) || !localFolder) return -1;
2881
2882 // check if we have a reasonable amount of space left
2883 rv = localFolder->WarnIfLocalFileTooBig(msgWindow, m_totalDownloadSize,
2884 &spaceNotAvailable);
2885 if (NS_FAILED(rv) || spaceNotAvailable) {
2886 #ifdef DEBUG
2887 printf("Not enough disk space! Raising error!\n");
2888 #endif
2889 return -1;
2890 }
2891
2892 // Here we know how many messages we're going to download, so let
2893 // the pop3 sink know.
2894 rv = m_nsIPop3Sink->SetMsgsToDownload(m_pop3ConData->really_new_messages);
2895 }
2896 }
2897
2898 /* Look at this message, and decide whether to ignore it, get it, just get
2899 the TOP of it, or delete it. */
2900
2901 // if this is a message we've seen for the first time, we won't find it in
2902 // m_pop3ConData-uidlinfo->hash. By default, we retrieve messages, unless
2903 // they have a status, or are too big, in which case we figure out what to do.
2904 if (m_prefAuthMethods != POP3_HAS_AUTH_USER && TestCapFlag(POP3_HAS_XSENDER))
2905 m_pop3ConData->next_state = POP3_SEND_XSENDER;
2906 else
2907 m_pop3ConData->next_state = POP3_SEND_RETR;
2908 m_pop3ConData->truncating_cur_msg = false;
2909 m_pop3ConData->pause_for_read = false;
2910 if (m_pop3ConData->msg_info) {
2911 Pop3MsgInfo* info =
2912 m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
2913 if (m_pop3ConData->only_uidl) {
2914 if (info->uidl == NULL || PL_strcmp(info->uidl, m_pop3ConData->only_uidl))
2915 m_pop3ConData->next_state = POP3_GET_MSG;
2916 else
2917 m_pop3ConData->next_state = POP3_SEND_RETR;
2918 } else {
2919 char c = 0;
2920 if (!m_pop3ConData->newuidl) {
2921 m_pop3ConData->newuidl =
2922 PL_NewHashTable(20, PL_HashString, PL_CompareStrings,
2923 PL_CompareValues, &gHashAllocOps, nullptr);
2924 if (!m_pop3ConData->newuidl) return MK_OUT_OF_MEMORY;
2925 }
2926 if (info->uidl) {
2927 Pop3UidlEntry* uidlEntry = (Pop3UidlEntry*)PL_HashTableLookup(
2928 m_pop3ConData->uidlinfo->hash, info->uidl);
2929 if (uidlEntry) {
2930 c = uidlEntry->status;
2931 popstateTimestamp = uidlEntry->dateReceived;
2932 }
2933 }
2934 if (c == DELETE_CHAR) {
2935 m_pop3ConData->next_state = POP3_SEND_DELE;
2936 } else if (c == KEEP) {
2937 // this is a message we've already downloaded and left on server;
2938 // Advance to next message.
2939 m_pop3ConData->next_state = POP3_GET_MSG;
2940 } else if (c == FETCH_BODY) {
2941 m_pop3ConData->next_state = POP3_SEND_RETR;
2942 PL_HashTableRemove(m_pop3ConData->uidlinfo->hash, (void*)info->uidl);
2943 } else if ((c != TOO_BIG) &&
2944 (TestCapFlag(POP3_TOP_UNDEFINED | POP3_HAS_TOP)) &&
2945 (m_pop3ConData->headers_only ||
2946 ((m_pop3ConData->size_limit > 0) &&
2947 (info->size > m_pop3ConData->size_limit) &&
2948 !m_pop3ConData->only_uidl)) &&
2949 info->uidl && *info->uidl) {
2950 // message is too big
2951 m_pop3ConData->truncating_cur_msg = true;
2952 m_pop3ConData->next_state = POP3_SEND_TOP;
2953 put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG,
2954 popstateTimestamp);
2955 } else if (c == TOO_BIG) {
2956 /* message previously left on server, see if the max download size
2957 has changed, because we may want to download the message this time
2958 around. Otherwise ignore the message, we have the header. */
2959 if ((m_pop3ConData->size_limit > 0) &&
2960 (info->size <= m_pop3ConData->size_limit))
2961 PL_HashTableRemove(m_pop3ConData->uidlinfo->hash, (void*)info->uidl);
2962 // remove from our table, and download
2963 else {
2964 m_pop3ConData->truncating_cur_msg = true;
2965 m_pop3ConData->next_state = POP3_GET_MSG;
2966 // ignore this message and get next one
2967 put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG,
2968 popstateTimestamp);
2969 }
2970 }
2971
2972 if (m_pop3ConData->next_state != POP3_SEND_DELE && info->uidl) {
2973 /* This is a message we have decided to keep on the server. Notate
2974 that now for the future. (Don't change the popstate file at all
2975 if only_uidl is set; in that case, there might be brand new messages
2976 on the server that we *don't* want to mark KEEP; we just want to
2977 leave them around until the user next does a GetNewMail.) */
2978
2979 /* If this is a message we already know about (i.e., it was
2980 in popstate.dat already), we need to maintain the original
2981 date the message was downloaded. */
2982 if (m_pop3ConData->truncating_cur_msg)
2983 put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG,
2984 popstateTimestamp);
2985 else
2986 put_hash(m_pop3ConData->newuidl, info->uidl, KEEP, popstateTimestamp);
2987 }
2988 }
2989 if (m_pop3ConData->next_state == POP3_GET_MSG)
2990 m_pop3ConData->last_accessed_msg++;
2991 // Make sure we check the next message next time!
2992 }
2993 return 0;
2994 }
2995
2996 /* start retrieving just the first 20 lines
2997 */
SendTop()2998 int32_t nsPop3Protocol::SendTop() {
2999 char* cmd = PR_smprintf(
3000 "TOP %ld %d" CRLF,
3001 m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum,
3002 m_pop3ConData->headers_only ? 0 : 20);
3003 int32_t status = -1;
3004 if (cmd) {
3005 m_pop3ConData->next_state_after_response = POP3_TOP_RESPONSE;
3006 m_pop3ConData->cur_msg_size = -1;
3007
3008 /* zero the bytes received in message in preparation for
3009 * the next
3010 */
3011 m_bytesInMsgReceived = 0;
3012 status = Pop3SendData(cmd);
3013 }
3014 PR_Free(cmd);
3015 return status;
3016 }
3017
3018 /* send the xsender command
3019 */
SendXsender()3020 int32_t nsPop3Protocol::SendXsender() {
3021 char* cmd = PR_smprintf(
3022 "XSENDER %ld" CRLF,
3023 m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
3024 int32_t status = -1;
3025 if (cmd) {
3026 m_pop3ConData->next_state_after_response = POP3_XSENDER_RESPONSE;
3027 status = Pop3SendData(cmd);
3028 PR_Free(cmd);
3029 }
3030 return status;
3031 }
3032
XsenderResponse()3033 int32_t nsPop3Protocol::XsenderResponse() {
3034 m_pop3ConData->seenFromHeader = false;
3035 m_senderInfo = "";
3036
3037 if (m_pop3ConData->command_succeeded) {
3038 if (m_commandResponse.Length() > 4) m_senderInfo = m_commandResponse;
3039 } else {
3040 ClearCapFlag(POP3_HAS_XSENDER);
3041 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
3042 }
3043
3044 if (m_pop3ConData->truncating_cur_msg)
3045 m_pop3ConData->next_state = POP3_SEND_TOP;
3046 else
3047 m_pop3ConData->next_state = POP3_SEND_RETR;
3048 return 0;
3049 }
3050
3051 /* retrieve the whole message
3052 */
SendRetr()3053 int32_t nsPop3Protocol::SendRetr() {
3054 char* cmd = PR_smprintf(
3055 "RETR %ld" CRLF,
3056 m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
3057 int32_t status = -1;
3058 if (cmd) {
3059 m_pop3ConData->next_state_after_response = POP3_RETR_RESPONSE;
3060 m_pop3ConData->cur_msg_size = -1;
3061
3062 /* zero the bytes received in message in preparation for
3063 * the next
3064 */
3065 m_bytesInMsgReceived = 0;
3066
3067 if (m_pop3ConData->only_uidl) {
3068 /* Display bytes if we're only downloading one message. */
3069 PR_ASSERT(!m_pop3ConData->graph_progress_bytes_p);
3070 UpdateProgressPercent(0, m_totalDownloadSize);
3071 m_pop3ConData->graph_progress_bytes_p = true;
3072 } else {
3073 nsString finalString;
3074 mozilla::DebugOnly<nsresult> rv = FormatCounterString(
3075 u"receivingMessages"_ns, m_pop3ConData->real_new_counter,
3076 m_pop3ConData->really_new_messages, finalString);
3077 NS_ASSERTION(NS_SUCCEEDED(rv), "couldn't format string");
3078 if (mProgressEventSink) {
3079 rv = mProgressEventSink->OnStatus(this, NS_OK, finalString.get());
3080 NS_ASSERTION(NS_SUCCEEDED(rv), "dropping error result");
3081 }
3082 }
3083
3084 status = Pop3SendData(cmd);
3085 } // if cmd
3086 PR_Free(cmd);
3087 return status;
3088 }
3089
3090 /* digest the message
3091 */
RetrResponse(nsIInputStream * inputStream,uint32_t length)3092 int32_t nsPop3Protocol::RetrResponse(nsIInputStream* inputStream,
3093 uint32_t length) {
3094 uint32_t buffer_size;
3095 int32_t flags = 0;
3096 char* uidl = NULL;
3097 nsresult rv;
3098 uint32_t status = 0;
3099
3100 if (m_pop3ConData->cur_msg_size == -1) {
3101 /* this is the beginning of a message
3102 * get the response code and byte size
3103 */
3104 if (!m_pop3ConData->command_succeeded) return Error("pop3RetrFailure");
3105
3106 /* a successful RETR response looks like: #num_bytes Junk
3107 from TOP we only get the +OK and data
3108 */
3109 if (m_pop3ConData->truncating_cur_msg) { /* TOP, truncated message */
3110 flags |= nsMsgMessageFlags::Partial;
3111 } else {
3112 nsCString cmdResp(m_commandResponse);
3113 char* newStr = cmdResp.BeginWriting();
3114 char* num = NS_strtok(" ", &newStr);
3115 if (num) m_pop3ConData->cur_msg_size = atol(num);
3116 m_commandResponse = newStr;
3117 }
3118
3119 /* RETR complete message */
3120 if (!m_senderInfo.IsEmpty()) flags |= nsMsgMessageFlags::SenderAuthed;
3121
3122 if (m_pop3ConData->cur_msg_size <= 0) {
3123 if (m_pop3ConData->msg_info)
3124 m_pop3ConData->cur_msg_size =
3125 m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].size;
3126 else
3127 m_pop3ConData->cur_msg_size = 0;
3128 }
3129
3130 if (m_pop3ConData->msg_info &&
3131 m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].uidl)
3132 uidl = m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].uidl;
3133
3134 m_pop3ConData->parsed_bytes = 0;
3135 m_pop3ConData->pop3_size = m_pop3ConData->cur_msg_size;
3136 m_pop3ConData->assumed_end = false;
3137
3138 m_pop3Server->GetDotFix(&m_pop3ConData->dot_fix);
3139
3140 MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
3141 (POP3LOG("Opening message stream: MSG_IncorporateBegin")));
3142
3143 /* open the message stream so we have someplace
3144 * to put the data
3145 */
3146 m_pop3ConData->real_new_counter++;
3147 /* (rb) count only real messages being downloaded */
3148 rv = m_nsIPop3Sink->IncorporateBegin(uidl, m_url, flags,
3149 &m_pop3ConData->msg_closure);
3150
3151 MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
3152 (POP3LOG("Done opening message stream!")));
3153
3154 if (!m_pop3ConData->msg_closure || NS_FAILED(rv))
3155 return Error("pop3MessageWriteError");
3156 }
3157
3158 m_pop3ConData->pause_for_read = true;
3159
3160 bool pauseForMoreData = false;
3161 char* line = m_lineStreamBuffer->ReadNextLine(inputStream, status,
3162 pauseForMoreData, &rv, true);
3163 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
3164 if (NS_FAILED(rv)) return -1;
3165
3166 buffer_size = status;
3167
3168 if (status == 0 && !line) // no bytes read in...
3169 return (0);
3170
3171 if (m_pop3ConData->msg_closure) /* not done yet */
3172 {
3173 // buffer the line we just read in, and buffer all remaining lines in the
3174 // stream
3175 status = buffer_size;
3176 do {
3177 if (m_pop3ConData->msg_closure) {
3178 rv = HandleLine(line, buffer_size);
3179 if (NS_FAILED(rv)) return Error("pop3MessageWriteError");
3180
3181 // buffer_size already includes MSG_LINEBREAK_LEN so
3182 // subtract and add CRLF
3183 // but not really sure we always had CRLF in input since
3184 // we also treat a single LF as line ending!
3185 m_pop3ConData->parsed_bytes += buffer_size - MSG_LINEBREAK_LEN + 2;
3186 }
3187
3188 // now read in the next line
3189 PR_Free(line);
3190 line = m_lineStreamBuffer->ReadNextLine(inputStream, buffer_size,
3191 pauseForMoreData, &rv, true);
3192 if (NS_FAILED(rv)) return -1;
3193
3194 MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
3195 // buffer_size already includes MSG_LINEBREAK_LEN so
3196 // subtract and add CRLF
3197 // but not really sure we always had CRLF in input since
3198 // we also treat a single LF as line ending!
3199 status += buffer_size - MSG_LINEBREAK_LEN + 2;
3200 } while (line);
3201 }
3202
3203 buffer_size =
3204 status; // status holds # bytes we've actually buffered so far...
3205
3206 /* normal read. Yay! */
3207 if ((int32_t)(m_bytesInMsgReceived + buffer_size) >
3208 m_pop3ConData->cur_msg_size)
3209 buffer_size = m_pop3ConData->cur_msg_size - m_bytesInMsgReceived;
3210
3211 m_bytesInMsgReceived += buffer_size;
3212 m_totalBytesReceived += buffer_size;
3213
3214 // *** jefft in case of the message size that server tells us is different
3215 // from the actual message size
3216 if (pauseForMoreData && m_pop3ConData->dot_fix &&
3217 m_pop3ConData->assumed_end && m_pop3ConData->msg_closure) {
3218 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
3219 nsCOMPtr<nsIMsgWindow> msgWindow;
3220 if (NS_SUCCEEDED(rv))
3221 rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
3222 rv = m_nsIPop3Sink->IncorporateComplete(
3223 msgWindow,
3224 m_pop3ConData->truncating_cur_msg ? m_pop3ConData->cur_msg_size : 0);
3225
3226 // The following was added to prevent the loss of Data when we try
3227 // and write to somewhere we don't have write access error to (See
3228 // bug 62480)
3229 // (Note: This is only a temp hack until the underlying XPCOM is
3230 // fixed to return errors)
3231
3232 if (NS_FAILED(rv))
3233 return Error((rv == NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD)
3234 ? "pop3TmpDownloadError"
3235 : "pop3MessageWriteError");
3236
3237 m_pop3ConData->msg_closure = nullptr;
3238 }
3239
3240 if (!m_pop3ConData->msg_closure)
3241 /* meaning _handle_line read ".\r\n" at end-of-msg */
3242 {
3243 m_pop3ConData->pause_for_read = false;
3244
3245 if (m_pop3ConData->truncating_cur_msg || m_pop3ConData->leave_on_server) {
3246 Pop3UidlEntry* uidlEntry = NULL;
3247 Pop3MsgInfo* info =
3248 m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
3249
3250 /* Check for filter actions - FETCH or DELETE */
3251 if ((m_pop3ConData->newuidl) && (info->uidl))
3252 uidlEntry = (Pop3UidlEntry*)PL_HashTableLookup(m_pop3ConData->newuidl,
3253 info->uidl);
3254
3255 if (uidlEntry && uidlEntry->status == FETCH_BODY &&
3256 m_pop3ConData->truncating_cur_msg) {
3257 /* A filter decided to retrieve this full msg.
3258 Use GetMsg() so the popstate will update correctly,
3259 but don't let this msg get counted twice. */
3260 m_pop3ConData->next_state = POP3_GET_MSG;
3261 m_pop3ConData->real_new_counter--;
3262 /* Make sure we don't try to come through here again. */
3263 PL_HashTableRemove(m_pop3ConData->newuidl, (void*)info->uidl);
3264 put_hash(m_pop3ConData->uidlinfo->hash, info->uidl, FETCH_BODY,
3265 uidlEntry->dateReceived);
3266
3267 } else if (uidlEntry && uidlEntry->status == DELETE_CHAR) {
3268 // A filter decided to delete this msg from the server
3269 m_pop3ConData->next_state = POP3_SEND_DELE;
3270 } else {
3271 /* We've retrieved all or part of this message, but we want to
3272 keep it on the server. Go on to the next message. */
3273 m_pop3ConData->last_accessed_msg++;
3274 m_pop3ConData->next_state = POP3_GET_MSG;
3275 }
3276 if (m_pop3ConData->only_uidl) {
3277 /* GetMsg didn't update this field. Do it now */
3278 uidlEntry = (Pop3UidlEntry*)PL_HashTableLookup(
3279 m_pop3ConData->uidlinfo->hash, m_pop3ConData->only_uidl);
3280 NS_ASSERTION(uidlEntry, "uidl not found in uidlinfo");
3281 if (uidlEntry)
3282 put_hash(m_pop3ConData->uidlinfo->hash, m_pop3ConData->only_uidl,
3283 KEEP, uidlEntry->dateReceived);
3284 }
3285 } else {
3286 m_pop3ConData->next_state = POP3_SEND_DELE;
3287 }
3288
3289 /* if we didn't get the whole message add the bytes that we didn't get
3290 to the bytes received part so that the progress percent stays sane.
3291 */
3292 if (m_bytesInMsgReceived < m_pop3ConData->cur_msg_size)
3293 m_totalBytesReceived +=
3294 (m_pop3ConData->cur_msg_size - m_bytesInMsgReceived);
3295 }
3296
3297 /* set percent done to portion of total bytes of all messages
3298 that we're going to download. */
3299 if (m_totalDownloadSize)
3300 UpdateProgressPercent(m_totalBytesReceived, m_totalDownloadSize);
3301
3302 PR_Free(line);
3303 return (0);
3304 }
3305
TopResponse(nsIInputStream * inputStream,uint32_t length)3306 int32_t nsPop3Protocol::TopResponse(nsIInputStream* inputStream,
3307 uint32_t length) {
3308 if (TestCapFlag(POP3_TOP_UNDEFINED)) {
3309 ClearCapFlag(POP3_TOP_UNDEFINED);
3310 if (m_pop3ConData->command_succeeded)
3311 SetCapFlag(POP3_HAS_TOP);
3312 else
3313 ClearCapFlag(POP3_HAS_TOP);
3314 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
3315 }
3316
3317 if (m_pop3ConData->cur_msg_size ==
3318 -1 && /* first line after TOP command sent */
3319 !m_pop3ConData->command_succeeded) /* and TOP command failed */
3320 {
3321 /* TOP doesn't work so we can't retrieve the first part of this msg.
3322 So just go download the whole thing, and warn the user.
3323
3324 Note that the progress bar will not be accurate in this case.
3325 Oops. #### */
3326 m_pop3ConData->truncating_cur_msg = false;
3327
3328 nsString statusTemplate;
3329 mLocalBundle->GetStringFromName("pop3ServerDoesNotSupportTopCommand",
3330 statusTemplate);
3331 if (!statusTemplate.IsEmpty()) {
3332 nsAutoCString hostName;
3333 nsString statusString;
3334 m_url->GetHost(hostName);
3335
3336 nsTextFormatter::ssprintf(statusString, statusTemplate.get(),
3337 NS_ConvertUTF8toUTF16(hostName).get());
3338 UpdateStatusWithString(statusString.get());
3339 }
3340
3341 if (m_prefAuthMethods != POP3_HAS_AUTH_USER &&
3342 TestCapFlag(POP3_HAS_XSENDER))
3343 m_pop3ConData->next_state = POP3_SEND_XSENDER;
3344 else
3345 m_pop3ConData->next_state = POP3_SEND_RETR;
3346 return (0);
3347 }
3348
3349 /* If TOP works, we handle it in the same way as RETR. */
3350 return RetrResponse(inputStream, length);
3351 }
3352
3353 /* line is handed over as null-terminated string with MSG_LINEBREAK */
HandleLine(char * line,uint32_t line_length)3354 nsresult nsPop3Protocol::HandleLine(char* line, uint32_t line_length) {
3355 nsresult rv = NS_OK;
3356
3357 NS_ASSERTION(
3358 m_pop3ConData->msg_closure,
3359 "m_pop3ConData->msg_closure is null in nsPop3Protocol::HandleLine()");
3360 if (!m_pop3ConData->msg_closure) return NS_ERROR_NULL_POINTER;
3361
3362 if (!m_senderInfo.IsEmpty() && !m_pop3ConData->seenFromHeader) {
3363 if (line_length > 6 && !PL_strncasecmp("From: ", line, 6)) {
3364 m_pop3ConData->seenFromHeader = true;
3365 if (PL_strstr(line, m_senderInfo.get()) == NULL)
3366 m_nsIPop3Sink->SetSenderAuthedFlag(m_pop3ConData->msg_closure, false);
3367 }
3368 }
3369
3370 // line contains only a single dot and linebreak -> message end
3371 if (line_length == 1 + MSG_LINEBREAK_LEN && line[0] == '.') {
3372 m_pop3ConData->assumed_end = true; /* in case byte count from server is */
3373 /* wrong, mark we may have had the end */
3374 if (!m_pop3ConData->dot_fix || m_pop3ConData->truncating_cur_msg ||
3375 (m_pop3ConData->parsed_bytes >= (m_pop3ConData->pop3_size - 3))) {
3376 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
3377 nsCOMPtr<nsIMsgWindow> msgWindow;
3378 if (NS_SUCCEEDED(rv))
3379 rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
3380 rv = m_nsIPop3Sink->IncorporateComplete(
3381 msgWindow,
3382 m_pop3ConData->truncating_cur_msg ? m_pop3ConData->cur_msg_size : 0);
3383
3384 // The following was added to prevent the loss of Data when we try
3385 // and write to somewhere we don't have write access error to (See
3386 // bug 62480)
3387 // (Note: This is only a temp hack until the underlying XPCOM is
3388 // fixed to return errors)
3389
3390 if (NS_FAILED(rv)) {
3391 Error((rv == NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD)
3392 ? "pop3TmpDownloadError"
3393 : "pop3MessageWriteError");
3394 return rv;
3395 }
3396
3397 m_pop3ConData->msg_closure = nullptr;
3398 return rv;
3399 }
3400 }
3401 /* Check if the line begins with the termination octet. If so
3402 and if another termination octet follows, we step over the
3403 first occurrence of it. */
3404 else if (line_length > 1 && line[0] == '.' && line[1] == '.') {
3405 line++;
3406 line_length--;
3407 }
3408
3409 return m_nsIPop3Sink->IncorporateWrite(line, line_length);
3410 }
3411
SendDele()3412 int32_t nsPop3Protocol::SendDele() {
3413 /* increment the last accessed message since we have now read it
3414 */
3415 char* cmd = PR_smprintf(
3416 "DELE %ld" CRLF,
3417 m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
3418 m_pop3ConData->last_accessed_msg++;
3419 int32_t status = -1;
3420 if (cmd) {
3421 m_pop3ConData->next_state_after_response = POP3_DELE_RESPONSE;
3422 status = Pop3SendData(cmd);
3423 }
3424 PR_Free(cmd);
3425 return status;
3426 }
3427
DeleResponse()3428 int32_t nsPop3Protocol::DeleResponse() {
3429 Pop3UidlHost* host = NULL;
3430
3431 host = m_pop3ConData->uidlinfo;
3432
3433 /* the return from the delete will come here
3434 */
3435 if (!m_pop3ConData->command_succeeded) return Error("pop3DeleFailure");
3436
3437 /* ###chrisf
3438 the delete succeeded. Write out state so that we
3439 keep track of all the deletes which have not yet been
3440 committed on the server. Flush this state upon successful
3441 QUIT.
3442
3443 We will do this by adding each successfully deleted message id
3444 to a list which we will write out to popstate.dat in
3445 net_pop3_write_state().
3446 */
3447 if (host) {
3448 if (m_pop3ConData->msg_info &&
3449 m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg - 1].uidl) {
3450 if (m_pop3ConData->newuidl)
3451 if (m_pop3ConData->leave_on_server) {
3452 PL_HashTableRemove(
3453 m_pop3ConData->newuidl,
3454 (void*)m_pop3ConData
3455 ->msg_info[m_pop3ConData->last_accessed_msg - 1]
3456 .uidl);
3457 } else {
3458 put_hash(m_pop3ConData->newuidl,
3459 m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg - 1]
3460 .uidl,
3461 DELETE_CHAR, 0);
3462 /* kill message in new hash table */
3463 }
3464 else
3465 PL_HashTableRemove(
3466 host->hash,
3467 (void*)m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg - 1]
3468 .uidl);
3469 }
3470 }
3471
3472 m_pop3ConData->next_state = POP3_GET_MSG;
3473 m_pop3ConData->pause_for_read = false;
3474
3475 return (0);
3476 }
3477
CommitState(bool remove_last_entry)3478 int32_t nsPop3Protocol::CommitState(bool remove_last_entry) {
3479 // only use newuidl if we successfully finished looping through all the
3480 // messages in the inbox.
3481 if (m_pop3ConData->newuidl) {
3482 if (m_pop3ConData->last_accessed_msg >= m_pop3ConData->number_of_messages) {
3483 PL_HashTableDestroy(m_pop3ConData->uidlinfo->hash);
3484 m_pop3ConData->uidlinfo->hash = m_pop3ConData->newuidl;
3485 m_pop3ConData->newuidl = nullptr;
3486 } else {
3487 /* If we are leaving messages on the server, pull out the last
3488 uidl from the hash, because it might have been put in there before
3489 we got it into the database.
3490 */
3491 if (remove_last_entry && m_pop3ConData->msg_info &&
3492 !m_pop3ConData->only_uidl && m_pop3ConData->newuidl->nentries > 0) {
3493 Pop3MsgInfo* info =
3494 m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
3495 if (info && info->uidl) {
3496 mozilla::DebugOnly<bool> val =
3497 PL_HashTableRemove(m_pop3ConData->newuidl, info->uidl);
3498 NS_ASSERTION(val, "uidl not in hash table");
3499 }
3500 }
3501
3502 // Add the entries in newuidl to m_pop3ConData->uidlinfo->hash to keep
3503 // track of the messages we *did* download in this session.
3504 PL_HashTableEnumerateEntries(m_pop3ConData->newuidl,
3505 net_pop3_copy_hash_entries,
3506 (void*)m_pop3ConData->uidlinfo->hash);
3507 }
3508 }
3509
3510 if (!m_pop3ConData->only_check_for_new_mail) {
3511 nsresult rv;
3512 nsCOMPtr<nsIFile> mailDirectory;
3513
3514 // get the mail directory
3515 nsCOMPtr<nsIMsgIncomingServer> server =
3516 do_QueryInterface(m_pop3Server, &rv);
3517 if (NS_FAILED(rv)) return -1;
3518
3519 rv = server->GetLocalPath(getter_AddRefs(mailDirectory));
3520 if (NS_FAILED(rv)) return -1;
3521
3522 // write the state in the mail directory
3523 net_pop3_write_state(m_pop3ConData->uidlinfo, mailDirectory.get());
3524 }
3525 return 0;
3526 }
3527
3528 /* NET_process_Pop3 will control the state machine that
3529 * loads messages from a pop3 server
3530 *
3531 * returns negative if the transfer is finished or error'd out
3532 *
3533 * returns zero or more if the transfer needs to be continued.
3534 */
ProcessProtocolState(nsIURI * url,nsIInputStream * aInputStream,uint64_t sourceOffset,uint32_t aLength)3535 nsresult nsPop3Protocol::ProcessProtocolState(nsIURI* url,
3536 nsIInputStream* aInputStream,
3537 uint64_t sourceOffset,
3538 uint32_t aLength) {
3539 int32_t status = 0;
3540 bool urlStatusSet = false;
3541 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_url);
3542
3543 MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
3544 (POP3LOG("Entering NET_ProcessPop3 %d"), aLength));
3545
3546 m_pop3ConData->pause_for_read = false; /* already paused; reset */
3547
3548 if (m_username.IsEmpty()) {
3549 // net_pop3_block = false;
3550 Error("pop3UsernameUndefined");
3551 return NS_MSG_SERVER_USERNAME_MISSING;
3552 }
3553
3554 while (!m_pop3ConData->pause_for_read) {
3555 MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
3556 (POP3LOG("Entering state: %d"), m_pop3ConData->next_state));
3557
3558 switch (m_pop3ConData->next_state) {
3559 case POP3_READ_PASSWORD:
3560 // This is a separate state so that we're waiting for the user to type
3561 // in a password while we don't actually have a connection to the pop
3562 // server open; this saves us from having to worry about the server
3563 // timing out on us while we wait for user input.
3564 if (NS_FAILED(StartGetAsyncPassword(POP3_OBTAIN_PASSWORD_EARLY)))
3565 status = -1;
3566 break;
3567 case POP3_FINISH_OBTAIN_PASSWORD_EARLY: {
3568 if (m_passwordResult.IsEmpty() || m_username.IsEmpty()) {
3569 status = MK_POP3_PASSWORD_UNDEFINED;
3570 m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_Unknown;
3571 m_nsIPop3Sink->SetBiffStateAndUpdateFE(m_pop3ConData->biffstate, 0,
3572 false);
3573
3574 /* update old style biff */
3575 m_pop3ConData->next_state = POP3_FREE;
3576 m_pop3ConData->pause_for_read = false;
3577 break;
3578 }
3579
3580 m_pop3ConData->pause_for_read = false;
3581 // we are already connected so just go on and send the username
3582 if (m_prefAuthMethods == POP3_HAS_AUTH_USER) {
3583 m_currentAuthMethod = POP3_HAS_AUTH_USER;
3584 m_pop3ConData->next_state = POP3_SEND_USERNAME;
3585 } else {
3586 if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
3587 m_pop3ConData->next_state = POP3_SEND_AUTH;
3588 else
3589 m_pop3ConData->next_state = POP3_SEND_CAPA;
3590 }
3591 break;
3592 }
3593
3594 case POP3_START_CONNECT: {
3595 m_pop3ConData->next_state = POP3_FINISH_CONNECT;
3596 m_pop3ConData->pause_for_read = false;
3597 break;
3598 }
3599
3600 case POP3_FINISH_CONNECT: {
3601 m_pop3ConData->pause_for_read = false;
3602 m_pop3ConData->next_state = POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE;
3603 break;
3604 }
3605
3606 case POP3_WAIT_FOR_RESPONSE:
3607 status = WaitForResponse(aInputStream, aLength);
3608 break;
3609
3610 case POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE: {
3611 status = WaitForStartOfConnectionResponse(aInputStream, aLength);
3612
3613 if (status) {
3614 if (m_prefAuthMethods == POP3_HAS_AUTH_USER) {
3615 m_currentAuthMethod = POP3_HAS_AUTH_USER;
3616 m_pop3ConData->next_state = POP3_SEND_USERNAME;
3617 } else {
3618 if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
3619 m_pop3ConData->next_state = POP3_SEND_AUTH;
3620 else
3621 m_pop3ConData->next_state = POP3_SEND_CAPA;
3622 }
3623 }
3624
3625 break;
3626 }
3627
3628 case POP3_SEND_AUTH:
3629 status = SendAuth();
3630 break;
3631
3632 case POP3_AUTH_RESPONSE:
3633 status = AuthResponse(aInputStream, aLength);
3634 break;
3635
3636 case POP3_SEND_CAPA:
3637 status = SendCapa();
3638 break;
3639
3640 case POP3_CAPA_RESPONSE:
3641 status = CapaResponse(aInputStream, aLength);
3642 break;
3643
3644 case POP3_TLS_RESPONSE:
3645 status = SendTLSResponse();
3646 break;
3647
3648 case POP3_PROCESS_AUTH:
3649 status = ProcessAuth();
3650 break;
3651
3652 case POP3_NEXT_AUTH_STEP:
3653 status = NextAuthStep();
3654 break;
3655
3656 case POP3_AUTH_LOGIN:
3657 status = AuthLogin();
3658 break;
3659
3660 case POP3_AUTH_LOGIN_RESPONSE:
3661 status = AuthLoginResponse();
3662 break;
3663
3664 case POP3_AUTH_NTLM:
3665 status = AuthNtlm();
3666 break;
3667
3668 case POP3_AUTH_NTLM_RESPONSE:
3669 status = AuthNtlmResponse();
3670 break;
3671
3672 case POP3_AUTH_GSSAPI:
3673 status = AuthGSSAPI();
3674 break;
3675
3676 case POP3_AUTH_GSSAPI_FIRST:
3677 UpdateStatus("hostContact");
3678 status = AuthGSSAPIResponse(true);
3679 break;
3680
3681 case POP3_AUTH_GSSAPI_STEP:
3682 status = AuthGSSAPIResponse(false);
3683 break;
3684
3685 case POP3_AUTH_OAUTH2_AUTH_STEP:
3686 status = OAuth2AuthStep();
3687 break;
3688
3689 case POP3_AUTH_OAUTH2_RESPONSE:
3690 status = AuthOAuth2Response();
3691 break;
3692
3693 case POP3_SEND_USERNAME:
3694 if (NS_FAILED(
3695 StartGetAsyncPassword(POP3_OBTAIN_PASSWORD_BEFORE_USERNAME)))
3696 status = -1;
3697 break;
3698
3699 case POP3_OBTAIN_PASSWORD_BEFORE_USERNAME:
3700 status = -1;
3701 break;
3702
3703 case POP3_FINISH_OBTAIN_PASSWORD_BEFORE_USERNAME:
3704 UpdateStatus("hostContact");
3705 status = SendUsername();
3706 break;
3707
3708 case POP3_SEND_PASSWORD:
3709 if (NS_FAILED(
3710 StartGetAsyncPassword(POP3_OBTAIN_PASSWORD_BEFORE_PASSWORD)))
3711 status = -1;
3712 break;
3713
3714 case POP3_FINISH_OBTAIN_PASSWORD_BEFORE_PASSWORD:
3715 status = SendPassword();
3716 break;
3717
3718 case POP3_SEND_GURL:
3719 status = SendGurl();
3720 break;
3721
3722 case POP3_GURL_RESPONSE:
3723 status = GurlResponse();
3724 break;
3725
3726 case POP3_SEND_STAT:
3727 status = SendStat();
3728 break;
3729
3730 case POP3_GET_STAT:
3731 status = GetStat();
3732 break;
3733
3734 case POP3_SEND_LIST:
3735 status = SendList();
3736 break;
3737
3738 case POP3_GET_LIST:
3739 status = GetList(aInputStream, aLength);
3740 break;
3741
3742 case POP3_SEND_UIDL_LIST:
3743 status = SendUidlList();
3744 break;
3745
3746 case POP3_GET_UIDL_LIST:
3747 status = GetUidlList(aInputStream, aLength);
3748 break;
3749
3750 case POP3_SEND_XTND_XLST_MSGID:
3751 status = SendXtndXlstMsgid();
3752 break;
3753
3754 case POP3_GET_XTND_XLST_MSGID:
3755 status = GetXtndXlstMsgid(aInputStream, aLength);
3756 break;
3757
3758 case POP3_GET_MSG:
3759 status = GetMsg();
3760 break;
3761
3762 case POP3_SEND_TOP:
3763 status = SendTop();
3764 break;
3765
3766 case POP3_TOP_RESPONSE:
3767 status = TopResponse(aInputStream, aLength);
3768 break;
3769
3770 case POP3_SEND_XSENDER:
3771 status = SendXsender();
3772 break;
3773
3774 case POP3_XSENDER_RESPONSE:
3775 status = XsenderResponse();
3776 break;
3777
3778 case POP3_SEND_RETR:
3779 status = SendRetr();
3780 break;
3781
3782 case POP3_RETR_RESPONSE:
3783 status = RetrResponse(aInputStream, aLength);
3784 break;
3785
3786 case POP3_SEND_DELE:
3787 status = SendDele();
3788 break;
3789
3790 case POP3_DELE_RESPONSE:
3791 status = DeleResponse();
3792 break;
3793
3794 case POP3_SEND_QUIT:
3795 /* attempt to send a server quit command. Since this means
3796 everything went well, this is a good time to update the
3797 status file and the FE's biff state.
3798 */
3799 if (!m_pop3ConData->only_uidl) {
3800 /* update old style biff */
3801 if (!m_pop3ConData->only_check_for_new_mail) {
3802 /* We don't want to pop up a warning message any more (see
3803 bug 54116), so instead we put the "no new messages" or
3804 "retrieved x new messages"
3805 in the status line. Unfortunately, this tends to be running
3806 in a progress pane, so we try to get the real pane and
3807 show the message there. */
3808
3809 if (m_totalDownloadSize <= 0) {
3810 UpdateStatus("noNewMessages");
3811 /* There are no new messages. */
3812 } else {
3813 nsString statusString;
3814 nsresult rv = FormatCounterString(
3815 u"receivedMsgs"_ns, m_pop3ConData->real_new_counter - 1,
3816 m_pop3ConData->really_new_messages, statusString);
3817 if (NS_SUCCEEDED(rv)) UpdateStatusWithString(statusString.get());
3818 }
3819 }
3820 }
3821
3822 status = Pop3SendData("QUIT" CRLF);
3823 m_pop3ConData->next_state = POP3_WAIT_FOR_RESPONSE;
3824 m_pop3ConData->next_state_after_response = POP3_QUIT_RESPONSE;
3825 break;
3826
3827 case POP3_QUIT_RESPONSE:
3828 if (m_pop3ConData->command_succeeded) {
3829 /* the QUIT succeeded. We can now flush the state in popstate.dat
3830 which keeps track of any uncommitted DELE's */
3831
3832 /* clear the hash of all our uncommitted deletes */
3833 if (!m_pop3ConData->leave_on_server && m_pop3ConData->newuidl) {
3834 PL_HashTableEnumerateEntries(m_pop3ConData->newuidl,
3835 net_pop3_remove_messages_marked_delete,
3836 (void*)m_pop3ConData);
3837 }
3838 m_pop3ConData->next_state = POP3_DONE;
3839 } else {
3840 m_pop3ConData->next_state = POP3_ERROR_DONE;
3841 }
3842 break;
3843
3844 case POP3_DONE:
3845 CommitState(false);
3846 m_pop3ConData->urlStatus = NS_OK;
3847 urlStatusSet = true;
3848 m_pop3ConData->next_state = POP3_FREE;
3849 break;
3850
3851 case POP3_ERROR_DONE:
3852 /* write out the state */
3853 if (m_pop3ConData->list_done) CommitState(true);
3854
3855 if (m_pop3ConData->msg_closure) {
3856 m_nsIPop3Sink->IncorporateAbort(m_pop3ConData->only_uidl != nullptr);
3857 m_pop3ConData->msg_closure = NULL;
3858 m_nsIPop3Sink->AbortMailDelivery(this);
3859 }
3860
3861 if (m_pop3ConData->msg_del_started) {
3862 nsString statusString;
3863 nsresult rv = FormatCounterString(
3864 u"receivedMsgs"_ns, m_pop3ConData->real_new_counter - 1,
3865 m_pop3ConData->really_new_messages, statusString);
3866 if (NS_SUCCEEDED(rv)) UpdateStatusWithString(statusString.get());
3867
3868 NS_ASSERTION(!TestFlag(POP3_PASSWORD_FAILED),
3869 "POP3_PASSWORD_FAILED set when del_started");
3870 m_nsIPop3Sink->AbortMailDelivery(this);
3871 }
3872 { // this brace is to avoid compiler error about vars in switch case.
3873 nsCOMPtr<nsIMsgWindow> msgWindow;
3874
3875 if (mailnewsurl) mailnewsurl->GetMsgWindow(getter_AddRefs(msgWindow));
3876 // no msgWindow means no re-prompt, so treat as error.
3877 if (TestFlag(POP3_PASSWORD_FAILED) && msgWindow) {
3878 // We get here because the password was wrong.
3879 if (!m_socketIsOpen && mailnewsurl) {
3880 // The server dropped the connection, so we're going
3881 // to re-run the url.
3882 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
3883 (POP3LOG("need to rerun url because connection dropped "
3884 "during auth")));
3885 m_needToRerunUrl = true;
3886 return NS_OK;
3887 }
3888 m_pop3ConData->next_state = POP3_READ_PASSWORD;
3889 m_pop3ConData->command_succeeded = true;
3890 status = 0;
3891 } else {
3892 /* Else we got a "real" error, so finish up. */
3893 m_pop3ConData->next_state = POP3_FREE;
3894 // If we don't have a specific error already, set a generic one.
3895 if (NS_SUCCEEDED(m_pop3ConData->urlStatus)) {
3896 m_pop3ConData->urlStatus = NS_ERROR_FAILURE;
3897 }
3898 urlStatusSet = true;
3899 m_pop3ConData->pause_for_read = false;
3900 }
3901 }
3902 break;
3903
3904 case POP3_FREE: {
3905 UpdateProgressPercent(0, 0); // clear out the progress meter
3906 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
3907 if (server) {
3908 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
3909 (POP3LOG("Clearing server busy in POP3_FREE")));
3910 server->SetServerBusy(false); // the server is now not busy
3911 }
3912 MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
3913 (POP3LOG("Clearing running protocol in POP3_FREE")));
3914 CloseSocket();
3915 m_pop3Server->SetRunningProtocol(nullptr);
3916 if (mailnewsurl && urlStatusSet)
3917 mailnewsurl->SetUrlState(false, m_pop3ConData->urlStatus);
3918
3919 m_url = nullptr;
3920 return NS_OK;
3921 }
3922 default:
3923 NS_ERROR(
3924 "Got to unexpected state in nsPop3Protocol::ProcessProtocolState");
3925 status = -1;
3926 } /* end switch */
3927
3928 if ((status < 0) && m_pop3ConData->next_state != POP3_FREE) {
3929 m_pop3ConData->pause_for_read = false;
3930 m_pop3ConData->next_state = POP3_ERROR_DONE;
3931 }
3932
3933 } /* end while */
3934
3935 return NS_OK;
3936 }
3937
MarkMessages(nsTArray<Pop3UidlEntry * > * aUIDLArray)3938 NS_IMETHODIMP nsPop3Protocol::MarkMessages(
3939 nsTArray<Pop3UidlEntry*>* aUIDLArray) {
3940 NS_ENSURE_ARG_POINTER(aUIDLArray);
3941 uint32_t count = aUIDLArray->Length();
3942
3943 for (uint32_t i = 0; i < count; i++) {
3944 bool changed;
3945 if (m_pop3ConData->newuidl)
3946 MarkMsgInHashTable(m_pop3ConData->newuidl, aUIDLArray->ElementAt(i),
3947 &changed);
3948 if (m_pop3ConData->uidlinfo)
3949 MarkMsgInHashTable(m_pop3ConData->uidlinfo->hash,
3950 aUIDLArray->ElementAt(i), &changed);
3951 }
3952 return NS_OK;
3953 }
3954
CheckMessage(const char * aUidl,bool * aBool)3955 NS_IMETHODIMP nsPop3Protocol::CheckMessage(const char* aUidl, bool* aBool) {
3956 Pop3UidlEntry* uidlEntry = nullptr;
3957
3958 if (aUidl) {
3959 if (m_pop3ConData->newuidl)
3960 uidlEntry =
3961 (Pop3UidlEntry*)PL_HashTableLookup(m_pop3ConData->newuidl, aUidl);
3962 else if (m_pop3ConData->uidlinfo)
3963 uidlEntry = (Pop3UidlEntry*)PL_HashTableLookup(
3964 m_pop3ConData->uidlinfo->hash, aUidl);
3965 }
3966
3967 *aBool = uidlEntry ? true : false;
3968 return NS_OK;
3969 }
3970
3971 /* Function for finding an APOP Timestamp and simple check
3972 it for its validity. If returning NS_OK m_ApopTimestamp
3973 contains the validated substring of m_commandResponse. */
GetApopTimestamp()3974 nsresult nsPop3Protocol::GetApopTimestamp() {
3975 int32_t startMark = m_commandResponse.Length(), endMark = -1;
3976
3977 while (true) {
3978 // search for previous <
3979 if ((startMark = m_commandResponse.RFindChar('<', startMark - 1)) < 0)
3980 return NS_ERROR_FAILURE;
3981
3982 // search for next >
3983 if ((endMark = m_commandResponse.FindChar('>', startMark)) < 0) continue;
3984
3985 // look for an @ between start and end as a raw test
3986 int32_t at = m_commandResponse.FindChar('@', startMark);
3987 if (at < 0 || at >= endMark) continue;
3988
3989 // now test if sub only consists of chars in ASCII range
3990 nsCString sub(
3991 Substring(m_commandResponse, startMark, endMark - startMark + 1));
3992 if (NS_IsAscii(sub.get())) {
3993 // set m_ApopTimestamp to the validated substring
3994 m_ApopTimestamp.Assign(sub);
3995 break;
3996 }
3997 }
3998
3999 return NS_OK;
4000 }
4001