Back to index

lightning-sunbird  0.9+nobinonly
nsPop3Protocol.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Howard Chu <hyc@highlandsun.com>
00025  *   David Bienvenu <bienvenu@nventure.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK *****
00040  *
00041  * This Original Code has been modified by IBM Corporation. Modifications made by IBM 
00042  * described herein are Copyright (c) International Business Machines Corporation, 2000.
00043  * Modifications to Mozilla code or documentation identified per MPL Section 3.3
00044  *
00045  * Jason Eager <jce2@po.cwru.edu>
00046  *
00047  * Date             Modified by     Description of modification
00048  * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
00049  * 06/07/2000       Jason Eager    Added check for out of disk space
00050  */
00051 
00052 #ifdef MOZ_LOGGING
00053 #define FORCE_PR_LOG
00054 #endif 
00055 
00056 #include "nscore.h"
00057 #include "msgCore.h"    // precompiled header...
00058 #include "nsNetUtil.h"
00059 #include "nspr.h"
00060 #include "nsCRT.h"
00061 #include "plbase64.h"
00062 #include "nsIMsgMailNewsUrl.h"
00063 #include "nsPop3Protocol.h"
00064 #include "nsFileSpec.h"
00065 #include "nsFileStream.h"
00066 #include "MailNewsTypes.h"
00067 #include "nsString.h"
00068 #include "nsXPIDLString.h"
00069 #include "nsIPrompt.h"
00070 #include "nsIMsgIncomingServer.h"
00071 #include "nsLocalStringBundle.h"
00072 #include "nsTextFormatter.h"
00073 #include "nsCOMPtr.h"
00074 #include "nsIMsgWindow.h"
00075 #include "nsIMsgFolder.h" // TO include biffState enum. Change to bool later...
00076 #include "nsIDocShell.h"
00077 #include "nsEscape.h"
00078 #include "nsMsgUtils.h"
00079 #include "nsISignatureVerifier.h"
00080 #include "nsIPrefBranch.h"
00081 #include "nsIPrefService.h"
00082 #include "nsIPrefLocalizedString.h"
00083 #include "nsISocketTransport.h"
00084 #include "nsISSLSocketControl.h"
00085 
00086 #define EXTRA_SAFETY_SPACE 3096
00087 
00088 static PRLogModuleInfo *POP3LOGMODULE = nsnull;
00089 
00090 /* km
00091  *
00092  *
00093  *  Process state: POP3_START_USE_TOP_FOR_FAKE_UIDL
00094  *
00095  *     If we get here then UIDL and XTND are not supported by the mail server.
00096  * 
00097  *     Now we will walk backwards, from higher message numbers to lower, 
00098  *  using the TOP command. Along the way, keep track of messages to down load
00099  *  by POP3_GET_MSG.
00100  *
00101  *  Stop when we reach a msg that we knew about before or we run out of
00102  *  messages.
00103  *
00104  *     There is also conditional code to handle:
00105  *            BIFF
00106  *            Pop3ConData->only_uidl == true (fetching one message only)
00107  *      emptying message hash table if all messages are new
00108  *            Deleting messages that are marked deleted.
00109  *
00110  *
00111 */
00112 
00113 /* this function gets called for each hash table entry.  If it finds a message
00114    marked delete, then we will have to traverse all of the server messages
00115    with TOP in order to delete those that are marked delete.
00116 
00117 
00118    If UIDL or XTND XLST are supported then this function will never get called.
00119    
00120    A pointer to this function is passed to XP_Maphash     -km */
00121 
00122 static PRIntn PR_CALLBACK
00123 net_pop3_check_for_hash_messages_marked_delete(PLHashEntry* he,
00124                                                PRIntn msgindex, 
00125                                                void *arg)
00126 {
00127   Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
00128   if (uidlEntry->status == DELETE_CHAR)
00129   {
00130     ((Pop3ConData *) arg)->delete_server_message_during_top_traversal = PR_TRUE;
00131     return HT_ENUMERATE_STOP;      /* XP_Maphash will stop traversing hash table now */
00132   }
00133   
00134   return HT_ENUMERATE_NEXT;        /* XP_Maphash will continue traversing the hash */
00135 }
00136 
00137 static PRIntn PR_CALLBACK
00138 net_pop3_remove_messages_marked_delete(PLHashEntry* he,
00139                                        PRIntn msgindex, 
00140                                        void *arg)
00141 {
00142   Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
00143   return (uidlEntry->status == DELETE_CHAR) 
00144     ? HT_ENUMERATE_REMOVE : HT_ENUMERATE_NEXT;     
00145 }
00146 
00147 PRUint32 TimeInSecondsFromPRTime(PRTime prTime)
00148 {
00149   PRUint32 retTimeInSeconds;
00150 
00151   PRInt64 microSecondsPerSecond, intermediateResult;
00152   LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC);
00153   LL_DIV(intermediateResult, prTime, microSecondsPerSecond);
00154   LL_L2UI(retTimeInSeconds, intermediateResult);
00155   return retTimeInSeconds;
00156 }
00157 
00158 static void
00159 put_hash(PLHashTable* table, const char* key, char value, PRTime dateReceived)
00160 {
00161   Pop3UidlEntry* tmp;
00162 
00163   tmp = PR_NEWZAP(Pop3UidlEntry);
00164   if (tmp) 
00165   {
00166     tmp->uidl = PL_strdup(key);
00167     if (tmp->uidl) 
00168     {
00169       tmp->dateReceived = dateReceived;
00170       tmp->status = value;
00171       PL_HashTableAdd(table, (const void *)tmp->uidl, (void*) tmp);
00172     }
00173     else 
00174     {
00175       PR_Free(tmp);
00176     }
00177   }
00178 }
00179 
00180 
00181 static PRIntn PR_CALLBACK
00182 net_pop3_copy_hash_entries(PLHashEntry* he, PRIntn msgindex, void *arg)
00183 {
00184   Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
00185   put_hash((PLHashTable *) arg, uidlEntry->uidl, uidlEntry->status, uidlEntry->dateReceived);
00186   return HT_ENUMERATE_NEXT;
00187 }
00188 
00189 
00190 static void * PR_CALLBACK
00191 AllocUidlTable(void * /* pool */, PRSize size)
00192 {
00193   return PR_MALLOC(size);
00194 }
00195 
00196 static void PR_CALLBACK
00197 FreeUidlTable(void * /* pool */, void *item)
00198 {
00199     PR_Free(item);
00200 }
00201 
00202 static PLHashEntry * PR_CALLBACK
00203 AllocUidlInfo(void *pool, const void *key)
00204 {
00205     return PR_NEWZAP(PLHashEntry);
00206 }
00207 
00208 static void PR_CALLBACK
00209 FreeUidlInfo(void * /* pool */, PLHashEntry *he, PRUintn flag)
00210 {
00211   if (flag == HT_FREE_ENTRY)
00212   {
00213     Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
00214     if (uidlEntry)
00215     {
00216       PR_Free(uidlEntry->uidl);
00217       PR_Free(uidlEntry);
00218     }
00219     PR_Free(he);
00220   }
00221 }
00222 
00223 static PLHashAllocOps gHashAllocOps = {
00224     AllocUidlTable, FreeUidlTable,
00225     AllocUidlInfo, FreeUidlInfo
00226 };
00227 
00228 
00229 static Pop3UidlHost* 
00230 net_pop3_load_state(const char* searchhost, 
00231                     const char* searchuser,
00232                     nsIFileSpec *mailDirectory)
00233 {
00234   char* buf;
00235   char* newStr;
00236   char* host;
00237   char* user;
00238   char* uidl;
00239   char* flags;
00240   char *dateReceivedStr;
00241   Pop3UidlHost* result = nsnull;
00242   Pop3UidlHost* current = nsnull;
00243   Pop3UidlHost* tmp;
00244   
00245   result = PR_NEWZAP(Pop3UidlHost);
00246   if (!result) return nsnull;
00247   result->host = PL_strdup(searchhost);
00248   result->user = PL_strdup(searchuser);
00249   result->hash = PL_NewHashTable(20, PL_HashString, PL_CompareStrings, PL_CompareValues, &gHashAllocOps, nsnull);
00250   
00251   if (!result->host || !result->user || !result->hash) 
00252   {
00253     PR_Free(result->host);
00254     PR_Free(result->user);
00255     if (result->hash) 
00256       PL_HashTableDestroy(result->hash);
00257     PR_Free(result);
00258     return nsnull;
00259   }
00260   
00261   nsFileSpec fileSpec;
00262   mailDirectory->GetFileSpec(&fileSpec);
00263   fileSpec += "popstate.dat";
00264   
00265   nsInputFileStream fileStream(fileSpec);
00266   
00267   buf = (char*)PR_CALLOC(512);
00268   if (buf) 
00269   {
00270     while (!fileStream.eof() && !fileStream.failed() && fileStream.is_open())
00271     {
00272       fileStream.readline(buf, 512);
00273       if (*buf == '#' || *buf == nsCRT::CR || *buf == nsCRT::LF || *buf == 0)
00274         continue;
00275       if (buf[0] == '*') {
00276         /* It's a host&user line. */
00277         current = NULL;
00278         /* XP_FileReadLine uses LF on all platforms */
00279         host = nsCRT::strtok(buf + 1, " \t\r\n", &newStr);
00280         /* without space to also get realnames - see bug 225332 */
00281         user = nsCRT::strtok(newStr, "\t\r\n", &newStr);
00282         if (host == NULL || user == NULL) continue;
00283         for (tmp = result ; tmp ; tmp = tmp->next) 
00284         {
00285           if (PL_strcmp(host, tmp->host) == 0 &&
00286             PL_strcmp(user, tmp->user) == 0) 
00287           {
00288             current = tmp;
00289             break;
00290           }
00291         }
00292         if (!current) 
00293         {
00294           current = PR_NEWZAP(Pop3UidlHost);
00295           if (current) 
00296           {
00297             current->host = PL_strdup(host);
00298             current->user = PL_strdup(user);
00299             current->hash = PL_NewHashTable(20, PL_HashString, PL_CompareStrings, PL_CompareValues, &gHashAllocOps, nsnull);
00300             if (!current->host || !current->user || !current->hash) 
00301             {
00302               PR_Free(current->host);
00303               PR_Free(current->user);
00304               if (current->hash) 
00305                 PL_HashTableDestroy(current->hash);
00306               PR_Free(current);
00307             }
00308             else 
00309             {
00310               current->next = result->next;
00311               result->next = current;
00312             }
00313           }
00314         }
00315       } 
00316       else 
00317       {
00318         /* It's a line with a UIDL on it. */
00319         if (current) 
00320         {
00321           flags = nsCRT::strtok(buf, " \t\r\n", &newStr);             /* XP_FileReadLine uses LF on all platforms */
00322           uidl = nsCRT::strtok(newStr, " \t\r\n", &newStr);
00323           dateReceivedStr = nsCRT::strtok(newStr, " \t\r\n", &newStr);
00324           PRTime dateReceived = PR_Now(); // if we don't find a date str, assume now.
00325           if (dateReceivedStr)
00326             dateReceived = atoi(dateReceivedStr);
00327           if (flags && uidl) 
00328           {
00329             if ((flags[0] == KEEP) || (flags[0] == DELETE_CHAR) ||
00330               (flags[0] == TOO_BIG) || (flags[0] == FETCH_BODY)) 
00331             {
00332               put_hash(current->hash, uidl, flags[0], dateReceived);
00333             }
00334             else
00335             {
00336               NS_ASSERTION(PR_FALSE, "invalid flag in popstate.dat"); 
00337             }
00338           }
00339         }
00340       }
00341     }
00342     
00343     PR_Free(buf);
00344   }
00345   if (fileStream.is_open())
00346     fileStream.close();
00347   
00348   return result;
00349 }
00350 
00351 static PRIntn PR_CALLBACK
00352 hash_clear_mapper(PLHashEntry* he, PRIntn msgindex, void* arg)
00353 {
00354   Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
00355   PR_Free(uidlEntry->uidl);
00356   PR_Free(uidlEntry);
00357   he->value = nsnull;
00358 
00359   return HT_ENUMERATE_REMOVE;
00360 }
00361 
00362 static PRIntn PR_CALLBACK
00363 hash_empty_mapper(PLHashEntry* he, PRIntn msgindex, void* arg)
00364 {
00365   *((PRBool*) arg) = PR_FALSE;
00366   return HT_ENUMERATE_STOP;
00367 }
00368 
00369 static PRBool
00370 hash_empty(PLHashTable* hash)
00371 {
00372   PRBool result = PR_TRUE;
00373   PL_HashTableEnumerateEntries(hash, hash_empty_mapper, (void *)&result);
00374   return result;
00375 }
00376 
00377 
00378 static PRIntn PR_CALLBACK
00379 net_pop3_write_mapper(PLHashEntry* he, PRIntn msgindex, void* arg)
00380 {
00381   nsOutputFileStream* file = (nsOutputFileStream*) arg;
00382   Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
00383   NS_ASSERTION((uidlEntry->status == KEEP) ||
00384     (uidlEntry->status ==  DELETE_CHAR) ||
00385     (uidlEntry->status ==  FETCH_BODY) ||
00386     (uidlEntry->status == TOO_BIG), "invalid status");
00387   char* tmpBuffer = PR_smprintf("%c %s %d" MSG_LINEBREAK, uidlEntry->status, (char*)
00388     uidlEntry->uidl, uidlEntry->dateReceived);
00389   PR_ASSERT(tmpBuffer);
00390   *file << tmpBuffer;
00391   PR_Free(tmpBuffer);
00392   return HT_ENUMERATE_NEXT;
00393 }
00394 
00395 static PRIntn PR_CALLBACK
00396 net_pop3_delete_old_msgs_mapper(PLHashEntry* he, PRIntn msgindex, void* arg)
00397 {
00398   PRTime cutOffDate = (PRTime) arg;
00399   Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
00400   if (uidlEntry->dateReceived < cutOffDate)
00401     uidlEntry->status = DELETE_CHAR; // mark for deletion
00402   return HT_ENUMERATE_NEXT;
00403 }
00404 
00405 static void
00406 net_pop3_write_state(Pop3UidlHost* host, nsIFileSpec *mailDirectory)
00407 {
00408   PRInt32 len = 0;
00409   
00410   nsFileSpec fileSpec;
00411   mailDirectory->GetFileSpec(&fileSpec);
00412   fileSpec += "popstate.dat";
00413   
00414   nsOutputFileStream outFileStream(fileSpec, PR_WRONLY | PR_CREATE_FILE |
00415     PR_TRUNCATE);
00416   const char tmpBuffer[] =
00417     "# POP3 State File" MSG_LINEBREAK
00418     "# This is a generated file!  Do not edit." MSG_LINEBREAK
00419     MSG_LINEBREAK;
00420   
00421   outFileStream << tmpBuffer;
00422   
00423   for (; host && (len >= 0); host = host->next)
00424   {
00425     if (!hash_empty(host->hash))
00426     {
00427       outFileStream << "*";
00428       outFileStream << host->host;
00429       outFileStream << " ";
00430       outFileStream << host->user;
00431       outFileStream << MSG_LINEBREAK;
00432       PL_HashTableEnumerateEntries(host->hash, net_pop3_write_mapper, (void *)&outFileStream);
00433     }
00434   }
00435   if (outFileStream.is_open())
00436   {
00437     outFileStream.flush();
00438     outFileStream.close();
00439   }
00440 }
00441 
00442 static void
00443 net_pop3_free_state(Pop3UidlHost* host) 
00444 {
00445   Pop3UidlHost* h;
00446   while (host) 
00447   {
00448     h = host->next;
00449     PR_Free(host->host);
00450     PR_Free(host->user);
00451     PL_HashTableDestroy(host->hash);
00452     PR_Free(host);
00453     host = h;
00454   }
00455 }
00456 
00457 /*
00458 Look for a specific UIDL string in our hash tables, if we have it then we need
00459 to mark the message for deletion so that it can be deleted later. If the uidl of the
00460 message is not found, then the message was downloaded completly and already deleted
00461 from the server. So this only applies to messages kept on the server or too big
00462 for download. */
00463 /* static */
00464 void nsPop3Protocol::MarkMsgInHashTable(PLHashTable *hashTable, const Pop3UidlEntry *uidlE, PRBool *changed)
00465 {
00466   if (uidlE->uidl)
00467   {
00468     Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(hashTable, uidlE->uidl);
00469     if (uidlEntry)
00470     {
00471       if (uidlEntry->status != uidlE->status)
00472       {
00473         uidlEntry->status = uidlE->status;
00474         *changed = PR_TRUE;
00475       }
00476     }
00477   }
00478 }
00479 
00480 /* static */ 
00481 nsresult 
00482 nsPop3Protocol::MarkMsgForHost(const char *hostName, const char *userName,
00483                                       nsIFileSpec *mailDirectory, 
00484                                        nsVoidArray &UIDLArray)
00485 {
00486   if (!hostName || !userName || !mailDirectory)
00487     return NS_ERROR_NULL_POINTER;
00488 
00489   Pop3UidlHost *uidlHost = net_pop3_load_state(hostName, userName, mailDirectory);
00490   if (!uidlHost)
00491     return NS_ERROR_OUT_OF_MEMORY;
00492 
00493   PRBool changed = PR_FALSE;
00494 
00495   PRUint32 count = UIDLArray.Count();
00496   for (PRUint32 i = 0; i < count; i++)
00497   {
00498     MarkMsgInHashTable(uidlHost->hash,
00499       NS_STATIC_CAST(Pop3UidlEntry*,UIDLArray[i]), &changed);
00500   }
00501 
00502   if (changed)
00503     net_pop3_write_state(uidlHost, mailDirectory);
00504   net_pop3_free_state(uidlHost);
00505   return NS_OK;
00506 }
00507 
00508 
00509 
00510 NS_IMPL_ADDREF_INHERITED(nsPop3Protocol, nsMsgProtocol)
00511 NS_IMPL_RELEASE_INHERITED(nsPop3Protocol, nsMsgProtocol)
00512 
00513 NS_INTERFACE_MAP_BEGIN(nsPop3Protocol)
00514   NS_INTERFACE_MAP_ENTRY(nsIPop3Protocol)
00515 NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol)
00516 
00517 // nsPop3Protocol class implementation
00518 
00519 nsPop3Protocol::nsPop3Protocol(nsIURI* aURL) 
00520 : nsMsgProtocol(aURL),
00521   m_bytesInMsgReceived(0), 
00522   m_totalFolderSize(0),    
00523   m_totalDownloadSize(0),
00524   m_totalBytesReceived(0),
00525   m_lineStreamBuffer(nsnull),
00526   m_pop3ConData(nsnull),
00527   m_password_already_sent(PR_FALSE)
00528 {
00529 }
00530 
00531 nsresult nsPop3Protocol::Initialize(nsIURI * aURL)
00532 {
00533   nsresult rv = NS_OK;
00534 
00535   m_pop3ConData = (Pop3ConData *)PR_NEWZAP(Pop3ConData);
00536   if(!m_pop3ConData)
00537     return NS_ERROR_OUT_OF_MEMORY;
00538 
00539   m_totalBytesReceived = 0;
00540   m_bytesInMsgReceived = 0; 
00541   m_totalFolderSize = 0;    
00542   m_totalDownloadSize = 0;
00543   m_totalBytesReceived = 0;
00544   m_tlsEnabled = PR_FALSE;
00545   m_socketType = nsIMsgIncomingServer::tryTLS;
00546 
00547   if (aURL)
00548   {
00549     // extract out message feedback if there is any.
00550     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(aURL);
00551     if (mailnewsUrl)
00552     {
00553       nsCOMPtr<nsIMsgIncomingServer> server;
00554       mailnewsUrl->GetStatusFeedback(getter_AddRefs(m_statusFeedback));
00555       mailnewsUrl->GetServer(getter_AddRefs(server));
00556       NS_ENSURE_TRUE(server, NS_MSG_INVALID_OR_MISSING_SERVER);
00557 
00558       rv = server->GetSocketType(&m_socketType);
00559       NS_ENSURE_SUCCESS(rv,rv);
00560 
00561       rv = server->GetUseSecAuth(&m_useSecAuth);
00562       NS_ENSURE_SUCCESS(rv,rv);
00563 
00564       m_pop3Server = do_QueryInterface(server);
00565       if (m_pop3Server)
00566         m_pop3Server->GetPop3CapabilityFlags(&m_pop3ConData->capability_flags);
00567     }
00568 
00569     m_url = do_QueryInterface(aURL);
00570 
00571     // When we are making a secure connection, we need to make sure that we
00572     // pass an interface requestor down to the socket transport so that PSM can
00573     // retrieve a nsIPrompt instance if needed.
00574     nsCOMPtr<nsIInterfaceRequestor> ir;
00575     if (m_socketType != nsIMsgIncomingServer::defaultSocket)
00576     {
00577       nsCOMPtr<nsIMsgWindow> msgwin;
00578       mailnewsUrl->GetMsgWindow(getter_AddRefs(msgwin));
00579       if (msgwin) 
00580       {
00581         nsCOMPtr<nsIDocShell> docshell;
00582         msgwin->GetRootDocShell(getter_AddRefs(docshell));
00583         ir = do_QueryInterface(docshell);
00584       }
00585     }
00586 
00587     PRInt32 port = 0;
00588     nsXPIDLCString hostName;
00589     aURL->GetPort(&port);
00590     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
00591     if (server)
00592       server->GetRealHostName(getter_Copies(hostName));
00593 
00594     nsCOMPtr<nsIProxyInfo> proxyInfo;
00595     rv = NS_ExamineForProxy("pop", hostName.get(), port, getter_AddRefs(proxyInfo));
00596     if (NS_FAILED(rv)) proxyInfo = nsnull;
00597 
00598     const char *connectionType = nsnull;
00599     if (m_socketType == nsIMsgIncomingServer::useSSL)
00600       connectionType = "ssl";
00601     else if (m_socketType == nsIMsgIncomingServer::tryTLS ||
00602           m_socketType == nsIMsgIncomingServer::alwaysUseTLS)
00603         connectionType = "starttls";
00604 
00605     rv = OpenNetworkSocketWithInfo(hostName.get(), port, connectionType, proxyInfo, ir);
00606     if (NS_FAILED(rv) && m_socketType == nsIMsgIncomingServer::tryTLS)
00607     {
00608       m_socketType = nsIMsgIncomingServer::defaultSocket;
00609       rv = OpenNetworkSocketWithInfo(hostName.get(), port, nsnull, proxyInfo, ir);
00610     }
00611 
00612     if(NS_FAILED(rv))
00613       return rv;
00614   } // if we got a url...
00615   
00616   if (!POP3LOGMODULE)
00617       POP3LOGMODULE = PR_NewLogModule("POP3");
00618 
00619   m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, PR_TRUE);
00620   if(!m_lineStreamBuffer)
00621     return NS_ERROR_OUT_OF_MEMORY;
00622 
00623   mStringService = do_GetService(NS_MSG_POPSTRINGSERVICE_CONTRACTID);
00624   return rv;
00625 }
00626 
00627 nsPop3Protocol::~nsPop3Protocol()
00628 {
00629   if (m_pop3ConData->newuidl) 
00630     PL_HashTableDestroy(m_pop3ConData->newuidl);
00631 
00632   net_pop3_free_state(m_pop3ConData->uidlinfo);
00633     
00634   FreeMsgInfo(); 
00635   PR_Free(m_pop3ConData->only_uidl);
00636   PR_Free(m_pop3ConData);
00637   
00638   delete m_lineStreamBuffer;
00639 }
00640 
00641 void nsPop3Protocol::SetCapFlag(PRUint32 flag)
00642 {
00643     m_pop3ConData->capability_flags |= flag;
00644 }
00645 
00646 void nsPop3Protocol::ClearCapFlag(PRUint32 flag)
00647 {
00648     m_pop3ConData->capability_flags &= ~flag;
00649 }
00650 
00651 PRBool nsPop3Protocol::TestCapFlag(PRUint32 flag)
00652 {
00653     return m_pop3ConData->capability_flags & flag;
00654 }
00655 
00656 void nsPop3Protocol::UpdateStatus(PRInt32 aStatusID)
00657 {
00658   if (m_statusFeedback)
00659   {
00660     PRUnichar * statusString = nsnull;
00661     mStringService->GetStringByID(aStatusID, &statusString);
00662     UpdateStatusWithString(statusString);
00663     nsCRT::free(statusString);
00664   }
00665 }
00666 
00667 void nsPop3Protocol::UpdateStatusWithString(const PRUnichar * aStatusString)
00668 {
00669     nsresult rv;
00670     if (mProgressEventSink) 
00671     {
00672         rv = mProgressEventSink->OnStatus(this, m_channelContext, NS_OK, aStatusString);      // XXX i18n message
00673         NS_ASSERTION(NS_SUCCEEDED(rv), "dropping error result");
00674     }
00675 }
00676 
00677 void nsPop3Protocol::UpdateProgressPercent (PRUint32 totalDone, PRUint32 total)
00678 {
00679   // XXX 64-bit
00680   if (mProgressEventSink)
00681     mProgressEventSink->OnProgress(this, m_channelContext, nsUint64(totalDone), nsUint64(total)); 
00682 }
00683 
00684 // note:  SetUsername() expects an unescaped string
00685 // do not pass in an escaped string
00686 void nsPop3Protocol::SetUsername(const char* name)
00687 {
00688   NS_ASSERTION(name, "no name specified!");
00689     if (name) 
00690       m_username = name;
00691 }
00692 
00693 nsresult nsPop3Protocol::GetPassword(char ** aPassword, PRBool *okayValue)
00694 {
00695   nsresult rv = NS_OK;
00696   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
00697   
00698   if (server)
00699   {
00700     PRBool isAuthenticated;
00701     m_nsIPop3Sink->GetUserAuthenticated(&isAuthenticated);
00702 
00703     // pass the failed password into the password prompt so that
00704     // it will be pre-filled, in case it failed because of a
00705     // server problem and not because it was wrong.
00706     if (!m_lastPasswordSent.IsEmpty())
00707       *aPassword = ToNewCString(m_lastPasswordSent);
00708 
00709     // clear the password if the last one failed
00710     if (TestFlag(POP3_PASSWORD_FAILED))
00711     {
00712       // if we've already gotten a password and it wasn't correct..clear
00713       // out the password and try again.
00714       rv = server->SetPassword("");
00715     }
00716     
00717     // first, figure out the correct prompt text to use...
00718     nsXPIDLCString hostName;
00719     nsXPIDLCString userName;
00720     PRUnichar *passwordPromptString =nsnull;
00721     
00722     server->GetRealHostName(getter_Copies(hostName));
00723     server->GetRealUsername(getter_Copies(userName));
00724     nsXPIDLString passwordTemplate;
00725     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
00726     nsCOMPtr<nsIMsgWindow> msgWindow;
00727     if (mailnewsUrl)
00728       mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
00729     // if the last prompt got us a bad password then show a special dialog
00730     if (TestFlag(POP3_PASSWORD_FAILED))
00731     {
00732       // if we haven't successfully logged onto the server in this session
00733       // and tried at least twice or if the server threw the specific error,
00734       // forget the password. 
00735       // Only do this if it's not check for new mail, since biff shouldn't
00736       // cause passwords to get forgotten at all. 
00737       if ((!isAuthenticated || m_pop3ConData->logonFailureCount > 2) && msgWindow)
00738         rv = server->ForgetPassword();
00739       if (NS_FAILED(rv)) return rv;
00740       mStringService->GetStringByID(POP3_PREVIOUSLY_ENTERED_PASSWORD_IS_INVALID_ETC, getter_Copies(passwordTemplate));
00741     } // otherwise this is the first time we've asked about the server's password so show a first time prompt
00742     else
00743       mStringService->GetStringByID(POP3_ENTER_PASSWORD_PROMPT, getter_Copies(passwordTemplate));
00744     if (passwordTemplate)
00745       passwordPromptString = nsTextFormatter::smprintf(passwordTemplate, (const char *) userName, (const char *) hostName);
00746     // now go get the password!!!!
00747     nsXPIDLString passwordTitle;
00748     mStringService->GetStringByID(POP3_ENTER_PASSWORD_PROMPT_TITLE, getter_Copies(passwordTitle));
00749     if (passwordPromptString)
00750     {
00751       if (passwordTitle)
00752         rv =  server->GetPasswordWithUI(passwordPromptString, passwordTitle.get(),
00753                                         msgWindow, okayValue, aPassword);
00754       nsTextFormatter::smprintf_free(passwordPromptString);
00755       ClearFlag(POP3_PASSWORD_FAILED|POP3_AUTH_FAILURE);
00756     }
00757     
00758     if (NS_FAILED(rv))
00759       m_pop3ConData->next_state = POP3_ERROR_DONE;
00760   } // if we have a server
00761   else
00762     rv = NS_MSG_INVALID_OR_MISSING_SERVER;
00763   
00764   return rv;
00765 }
00766 
00767 NS_IMETHODIMP nsPop3Protocol::OnTransportStatus(nsITransport *aTransport, nsresult aStatus, PRUint32 aProgress, PRUint32 aProgressMax)
00768 {
00769   return nsMsgProtocol::OnTransportStatus(aTransport, aStatus, aProgress, aProgressMax);
00770 }
00771 
00772 // stop binding is a "notification" informing us that the stream associated with aURL is going away. 
00773 NS_IMETHODIMP nsPop3Protocol::OnStopRequest(nsIRequest *request, nsISupports * aContext, nsresult aStatus)
00774 {
00775   nsresult rv = nsMsgProtocol::OnStopRequest(request, aContext, aStatus);
00776   // turn off the server busy flag on stop request - we know we're done, right?
00777   if (m_pop3Server)
00778   {
00779     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
00780     if (server)
00781       server->SetServerBusy(PR_FALSE); // the server is not busy
00782   }
00783   if(m_pop3ConData->list_done)
00784     CommitState(PR_TRUE);
00785   if (NS_FAILED(aStatus) && aStatus != NS_BINDING_ABORTED)
00786     Abort();
00787   return rv;
00788 }
00789 
00790 void nsPop3Protocol::Abort()
00791 {
00792   if(m_pop3ConData->msg_closure)
00793   {
00794       m_nsIPop3Sink->IncorporateAbort(m_pop3ConData->only_uidl != nsnull);
00795       m_pop3ConData->msg_closure = nsnull;
00796   }
00797   // need this to close the stream on the inbox.
00798   m_nsIPop3Sink->AbortMailDelivery(this);
00799   m_pop3Server->SetRunningProtocol(nsnull);
00800 
00801 }
00802 
00803 NS_IMETHODIMP nsPop3Protocol::Cancel(nsresult status)  // handle stop button
00804 {
00805   Abort();
00806   return nsMsgProtocol::Cancel(NS_BINDING_ABORTED);
00807 }
00808 
00809 
00810 nsresult nsPop3Protocol::LoadUrl(nsIURI* aURL, nsISupports * /* aConsumer */)
00811 {
00812   nsresult rv = 0;
00813   
00814   if (aURL)
00815     m_url = do_QueryInterface(aURL);
00816   else
00817     return NS_ERROR_FAILURE;
00818   
00819   nsCOMPtr<nsIURL> url = do_QueryInterface(aURL, &rv);
00820   if (NS_FAILED(rv)) return rv;
00821   
00822   PRInt32 port;
00823   rv = url->GetPort(&port);
00824   if (NS_FAILED(rv))
00825     return rv;
00826   
00827   rv = NS_CheckPortSafety(port, "pop");
00828   if (NS_FAILED(rv))
00829     return rv;
00830   
00831   nsCAutoString queryPart;
00832   rv = url->GetQuery(queryPart);
00833   NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get the url spect");
00834   
00835   m_pop3ConData->only_check_for_new_mail = (PL_strcasestr(queryPart.get(), "check") != nsnull);
00836   
00837   m_pop3ConData->get_url = (PL_strcasestr(queryPart.get(), "gurl") != nsnull);
00838   
00839   PRBool deleteByAgeFromServer = PR_FALSE;
00840   PRInt32 numDaysToLeaveOnServer = -1;
00841   if (!m_pop3ConData->only_check_for_new_mail)
00842   {
00843     // Pick up pref setting regarding leave messages on server, message
00844     // size limit
00845     
00846     m_pop3Server->GetLeaveMessagesOnServer(&m_pop3ConData->leave_on_server);
00847     m_pop3Server->GetHeadersOnly(&m_pop3ConData->headers_only);
00848     PRBool limitMessageSize = PR_FALSE;
00849 
00850     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
00851     if (server)
00852     {
00853       // size limits are superseded by headers_only mode
00854       if (!m_pop3ConData->headers_only)
00855       {
00856         server->GetLimitOfflineMessageSize(&limitMessageSize);
00857         if (limitMessageSize)
00858         {
00859           PRInt32 max_size = 0; // default size
00860           server->GetMaxMessageSize(&max_size);
00861           m_pop3ConData->size_limit = (max_size) ? max_size * 1024 : 50 * 1024;
00862        }
00863       }
00864       m_pop3Server->GetDeleteByAgeFromServer(&deleteByAgeFromServer);
00865       if (deleteByAgeFromServer)
00866         m_pop3Server->GetNumDaysToLeaveOnServer(&numDaysToLeaveOnServer);
00867     }
00868   }
00869   
00870   // UIDL stuff
00871   nsCOMPtr<nsIPop3URL> pop3Url = do_QueryInterface(m_url);
00872   if (pop3Url)
00873     pop3Url->GetPop3Sink(getter_AddRefs(m_nsIPop3Sink));
00874   
00875   nsCOMPtr<nsIFileSpec> mailDirectory;
00876   
00877   nsXPIDLCString hostName;
00878   nsXPIDLCString userName;
00879   
00880   nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
00881   if (server)
00882   {
00883     rv = server->GetLocalPath(getter_AddRefs(mailDirectory));
00884     server->SetServerBusy(PR_TRUE); // the server is now busy
00885     server->GetHostName(getter_Copies(hostName));
00886     server->GetUsername(getter_Copies(userName));
00887   }
00888   
00889   m_pop3ConData->uidlinfo = net_pop3_load_state(hostName, userName, mailDirectory);
00890   m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_NoMail;
00891   
00892   if (m_pop3ConData->uidlinfo && numDaysToLeaveOnServer > 0)
00893   {
00894     PRUint32 nowInSeconds = TimeInSecondsFromPRTime(PR_Now());
00895     PRUint32 cutOffDay = nowInSeconds - (60 * 60 * 24 * numDaysToLeaveOnServer);
00896   
00897     PL_HashTableEnumerateEntries(m_pop3ConData->uidlinfo->hash, net_pop3_delete_old_msgs_mapper, (void *) cutOffDay);
00898   }
00899   const char* uidl = PL_strcasestr(queryPart.get(), "uidl=");
00900   PR_FREEIF(m_pop3ConData->only_uidl);
00901 
00902   if (uidl)
00903   {
00904     uidl += 5;
00905     char *only_uidl = PL_strdup(uidl);
00906     m_pop3ConData->only_uidl = nsUnescape(only_uidl);
00907     mSuppressListenerNotifications = PR_TRUE; // suppress on start and on stop because this url won't have any content to display
00908   }
00909   
00910   m_pop3ConData->next_state = POP3_START_CONNECT;
00911   m_pop3ConData->next_state_after_response = POP3_FINISH_CONNECT;
00912   if (NS_SUCCEEDED(rv))
00913   {
00914     m_pop3Server->SetRunningProtocol(this);
00915     return nsMsgProtocol::LoadUrl(aURL);
00916   }
00917   else
00918     return rv;
00919 }
00920 
00921 void
00922 nsPop3Protocol::FreeMsgInfo()
00923 {
00924   int i;
00925   if (m_pop3ConData->msg_info) 
00926   {
00927     for (i=0 ; i<m_pop3ConData->number_of_messages ; i++) 
00928     {
00929       if (m_pop3ConData->msg_info[i].uidl)
00930         PR_Free(m_pop3ConData->msg_info[i].uidl);
00931       m_pop3ConData->msg_info[i].uidl = nsnull;
00932     }
00933     PR_Free(m_pop3ConData->msg_info);
00934     m_pop3ConData->msg_info = nsnull;
00935   }
00936 }
00937 
00938 PRInt32
00939 nsPop3Protocol::WaitForStartOfConnectionResponse(nsIInputStream* aInputStream,
00940                                                  PRUint32 length)
00941 {
00942   char * line = nsnull;
00943   PRUint32 line_length = 0;
00944   PRBool pauseForMoreData = PR_FALSE;
00945   nsresult rv;
00946   line = m_lineStreamBuffer->ReadNextLine(aInputStream, line_length, pauseForMoreData, &rv);
00947   if (NS_FAILED(rv))
00948     return -1;
00949 
00950   PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
00951 
00952   if(pauseForMoreData || !line)
00953   {
00954     m_pop3ConData->pause_for_read = PR_TRUE; /* pause */
00955     PR_Free(line);
00956     return(line_length);
00957   }
00958 
00959   if(*line == '+')
00960   {
00961     m_pop3ConData->command_succeeded = PR_TRUE;
00962     if(PL_strlen(line) > 4)
00963       m_commandResponse = line + 4;
00964     else
00965       m_commandResponse = line;
00966 
00967     if (m_useSecAuth)
00968     {
00969         nsresult rv;
00970         nsCOMPtr<nsISignatureVerifier> verifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv);
00971         // this checks if psm is installed...
00972         if (NS_SUCCEEDED(rv))
00973         {
00974           if (NS_SUCCEEDED(GetApopTimestamp()))
00975             SetCapFlag(POP3_HAS_AUTH_APOP);
00976         }
00977     }
00978     else
00979       ClearCapFlag(POP3_HAS_AUTH_APOP);
00980 
00981     m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
00982 
00983     m_pop3ConData->next_state = POP3_PROCESS_AUTH;
00984     m_pop3ConData->pause_for_read = PR_FALSE; /* don't pause */
00985   }
00986 
00987   PR_Free(line);
00988   return(1);  /* everything ok */
00989 }
00990 
00991 PRInt32
00992 nsPop3Protocol::WaitForResponse(nsIInputStream* inputStream, PRUint32 length)
00993 {
00994   char * line;
00995   PRUint32 ln = 0;
00996   PRBool pauseForMoreData = PR_FALSE;
00997   nsresult rv;
00998   line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
00999   if (NS_FAILED(rv))
01000     return -1;
01001   
01002   if(pauseForMoreData || !line)
01003   {
01004     m_pop3ConData->pause_for_read = PR_TRUE; /* pause */
01005 
01006     PR_Free(line);
01007     return(ln);
01008   }
01009 
01010   PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
01011 
01012   if(*line == '+')
01013   {
01014     m_pop3ConData->command_succeeded = PR_TRUE;
01015     if(PL_strlen(line) > 4)
01016     {
01017       if(!PL_strncasecmp(line, "+OK", 3))
01018         m_commandResponse = line + 4;
01019       else  // challenge answer to AUTH CRAM-MD5 and LOGIN username/password
01020         m_commandResponse = line + 2;
01021     }
01022     else
01023       m_commandResponse = line;
01024   }
01025   else
01026   {
01027     m_pop3ConData->command_succeeded = PR_FALSE;
01028     if(PL_strlen(line) > 5)
01029       m_commandResponse = line + 5;
01030     else
01031       m_commandResponse  = line;
01032 
01033     // search for the response codes (RFC 2449, chapter 8 and RFC 3206)
01034     if(TestCapFlag(POP3_HAS_RESP_CODES | POP3_HAS_AUTH_RESP_CODE))
01035     {
01036         // code for authentication failure due to the user's credentials
01037         if(m_commandResponse.Find("[AUTH", PR_TRUE) >= 0)
01038           SetFlag(POP3_AUTH_FAILURE);
01039 
01040         // codes for failures due to other reasons
01041         if(m_commandResponse.Find("[LOGIN-DELAY", PR_TRUE) >= 0 ||
01042            m_commandResponse.Find("[IN-USE", PR_TRUE) >= 0 ||
01043            m_commandResponse.Find("[SYS", PR_TRUE) >= 0)
01044       SetFlag(POP3_STOPLOGIN);
01045 
01046       // remove the codes from the response string presented to the user
01047       PRInt32 i = m_commandResponse.FindChar(']');
01048       if(i >= 0)
01049         m_commandResponse.Cut(0, i + 2);
01050     }
01051   }
01052   
01053   m_pop3ConData->next_state = m_pop3ConData->next_state_after_response;
01054   m_pop3ConData->pause_for_read = PR_FALSE; /* don't pause */
01055   
01056   PR_Free(line);
01057   return(1);  /* everything ok */
01058 }
01059 
01060 PRInt32
01061 nsPop3Protocol::Error(PRInt32 err_code)
01062 {
01063     // the error code is just the resource id for the error string...
01064     // so print out that error message!
01065     nsresult rv = NS_OK;
01066     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
01067     // we handle POP3_TMP_DOWNLOAD_FAILED earlier...
01068     if (err_code != POP3_TMP_DOWNLOAD_FAILED && NS_SUCCEEDED(rv))
01069     {
01070         nsCOMPtr<nsIMsgWindow> msgWindow;
01071         nsCOMPtr<nsIPrompt> dialog;
01072         rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow)); //it is ok to have null msgWindow, for example when biffing
01073         if (NS_SUCCEEDED(rv) && msgWindow)
01074         {
01075             rv = msgWindow->GetPromptDialog(getter_AddRefs(dialog));
01076             if (NS_SUCCEEDED(rv))
01077             {
01078               nsXPIDLString alertString;
01079               mStringService->GetStringByID(err_code, getter_Copies(alertString));
01080               if (m_pop3ConData->command_succeeded)  //not a server error message
01081                 dialog->Alert(nsnull, alertString.get()); 
01082               else
01083               {
01084                 nsXPIDLString serverSaidPrefix;
01085                 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
01086                 nsXPIDLCString hostName;
01087                 // Fomat string with hostname.
01088                 if (server)
01089                   rv = server->GetRealHostName(getter_Copies(hostName));
01090                 if (NS_SUCCEEDED(rv))
01091                 {
01092                   nsAutoString hostStr;
01093                   hostStr.AssignWithConversion(hostName.get());
01094                   const PRUnichar *params[] = { hostStr.get() };
01095                   nsCOMPtr<nsIStringBundle> bundle;
01096                   rv = mStringService->GetBundle(getter_AddRefs(bundle));
01097                   if (NS_SUCCEEDED(rv))
01098                     bundle->FormatStringFromID(POP3_SERVER_SAID, params, 1, getter_Copies(serverSaidPrefix));
01099                 }
01100 
01101                 nsAutoString message(alertString + NS_LITERAL_STRING(" ") +
01102                                      serverSaidPrefix + NS_LITERAL_STRING(" "));
01103                 AppendASCIItoUTF16(m_commandResponse, message);
01104                 dialog->Alert(nsnull,message.get()); 
01105               }
01106             }
01107         }
01108     }
01109     m_pop3ConData->next_state = POP3_ERROR_DONE;
01110     m_pop3ConData->pause_for_read = PR_FALSE;
01111     return -1;
01112 }
01113 
01114 PRInt32 nsPop3Protocol::SendData(nsIURI * aURL, const char * dataBuffer, PRBool aSuppressLogging)
01115 {
01116   // remove any leftover bytes in the line buffer
01117   // this can happen if the last message line doesn't end with a (CR)LF
01118   // or a server sent two reply lines
01119   m_lineStreamBuffer->ClearBuffer();
01120 
01121   PRInt32 result = nsMsgProtocol::SendData(aURL, dataBuffer);
01122 
01123   if (!aSuppressLogging) 
01124       PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS, ("SEND: %s", dataBuffer));        
01125   else 
01126       PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS, ("Logging suppressed for this command (it probably contained authentication information)"));
01127 
01128   if (result >= 0) // yeah this sucks...i need an error code....
01129   {
01130     m_pop3ConData->pause_for_read = PR_TRUE;
01131     m_pop3ConData->next_state = POP3_WAIT_FOR_RESPONSE;
01132   }
01133   else
01134     m_pop3ConData->next_state = POP3_ERROR_DONE;
01135 
01136   return 0;
01137 }
01138 
01139 /*
01140  * POP3 AUTH extension
01141  */
01142 
01143 PRInt32 nsPop3Protocol::SendAuth()
01144 {
01145   if(!m_pop3ConData->command_succeeded)
01146     return(Error(POP3_SERVER_ERROR));
01147 
01148   nsCAutoString command("AUTH" CRLF);
01149 
01150   m_pop3ConData->next_state_after_response = POP3_AUTH_RESPONSE;
01151   return SendData(m_url, command.get());
01152 }
01153 
01154 PRInt32 nsPop3Protocol::AuthResponse(nsIInputStream* inputStream, 
01155                              PRUint32 length)
01156 {
01157     char * line;
01158     PRUint32 ln = 0;
01159     nsresult rv;
01160 
01161     if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
01162     {
01163         ClearCapFlag(POP3_AUTH_MECH_UNDEFINED);
01164         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01165     }
01166     
01167     if (!m_pop3ConData->command_succeeded) 
01168     {
01169         /* AUTH command not implemented 
01170          * so no secure mechanisms available
01171          */
01172         m_pop3ConData->command_succeeded = PR_TRUE;
01173         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01174         m_pop3ConData->next_state = POP3_SEND_CAPA;
01175         return 0;
01176     }
01177     
01178     PRBool pauseForMoreData = PR_FALSE;
01179     line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
01180     if (NS_FAILED(rv))
01181       return -1;
01182 
01183     if(pauseForMoreData || !line) 
01184     {
01185         m_pop3ConData->pause_for_read = PR_TRUE; /* pause */
01186         PR_Free(line);
01187         return(0);
01188     }
01189     
01190     PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
01191 
01192     if (!PL_strcmp(line, ".")) 
01193     {
01194         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01195 
01196         // now that we've read all the AUTH responses, go for it
01197         m_pop3ConData->next_state = POP3_SEND_CAPA;
01198         m_pop3ConData->pause_for_read = PR_FALSE; /* don't pause */
01199     }
01200     else if (!PL_strcasecmp (line, "CRAM-MD5")) 
01201     {
01202         nsCOMPtr<nsISignatureVerifier> verifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv);
01203         // this checks if psm is installed...
01204         if (NS_SUCCEEDED(rv))
01205             SetCapFlag(POP3_HAS_AUTH_CRAM_MD5);
01206     }
01207     else if (!PL_strcasecmp (line, "NTLM")) 
01208     {
01209         nsCOMPtr<nsISignatureVerifier> verifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv);
01210         // this checks if psm is installed...
01211         if (NS_SUCCEEDED(rv))
01212             SetCapFlag(POP3_HAS_AUTH_NTLM);
01213     }
01214     else if (!PL_strcasecmp (line, "MSN"))
01215     {
01216         nsCOMPtr<nsISignatureVerifier> verifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv);
01217         // this checks if psm is installed...
01218         if (NS_SUCCEEDED(rv))
01219             SetCapFlag(POP3_HAS_AUTH_NTLM|POP3_HAS_AUTH_MSN);
01220     }
01221     else if (!PL_strcasecmp (line, "GSSAPI"))
01222         SetCapFlag(POP3_HAS_AUTH_GSSAPI);
01223     else if (!PL_strcasecmp (line, "PLAIN")) 
01224         SetCapFlag(POP3_HAS_AUTH_PLAIN);
01225     else if (!PL_strcasecmp (line, "LOGIN")) 
01226         SetCapFlag(POP3_HAS_AUTH_LOGIN);
01227 
01228     PR_Free(line);
01229     return 0;
01230 }
01231 
01232 /*
01233  * POP3 CAPA extension, see RFC 2449, chapter 5
01234  */
01235 
01236 PRInt32 nsPop3Protocol::SendCapa()
01237 {
01238     if(!m_pop3ConData->command_succeeded)
01239         return(Error(POP3_SERVER_ERROR));
01240 
01241     // for use after mechs disabled fallbacks when login failed
01242     // should better live in AuthResponse(), but it would only
01243     // be called the first time then
01244     BackupAuthFlags();
01245 
01246     nsCAutoString command("CAPA" CRLF);
01247 
01248     m_pop3ConData->next_state_after_response = POP3_CAPA_RESPONSE;
01249     return SendData(m_url, command.get());
01250 }
01251 
01252 PRInt32 nsPop3Protocol::CapaResponse(nsIInputStream* inputStream, 
01253                              PRUint32 length)
01254 {
01255     char * line;
01256     PRUint32 ln = 0;
01257 
01258     if (!m_pop3ConData->command_succeeded) 
01259     {
01260         /* CAPA command not implemented */
01261         m_pop3ConData->command_succeeded = PR_TRUE;
01262         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01263         m_pop3ConData->next_state = POP3_PROCESS_AUTH;
01264         return 0;
01265     }
01266 
01267     PRBool pauseForMoreData = PR_FALSE;
01268     nsresult rv;
01269     line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
01270     if (NS_FAILED(rv))
01271       return -1;
01272 
01273     if(pauseForMoreData || !line) 
01274     {
01275         m_pop3ConData->pause_for_read = PR_TRUE; /* pause */
01276         PR_Free(line);
01277         return(0);
01278     }
01279     
01280     PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
01281 
01282     if (!PL_strcmp(line, ".")) 
01283     {
01284         // now that we've read all the CAPA responses, go for it
01285         m_pop3ConData->next_state = POP3_PROCESS_AUTH;
01286         m_pop3ConData->pause_for_read = PR_FALSE; /* don't pause */
01287     }
01288     else
01289     if (!PL_strcasecmp(line, "XSENDER")) 
01290     {
01291         SetCapFlag(POP3_HAS_XSENDER);
01292         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01293     }
01294     else
01295     // see RFC 2449, chapter 6.4
01296     if (!PL_strcasecmp(line, "RESP-CODES")) 
01297     {
01298         SetCapFlag(POP3_HAS_RESP_CODES);
01299         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01300     }
01301     else
01302     // see RFC 3206, chapter 6
01303     if (!PL_strcasecmp(line, "AUTH-RESP-CODE")) 
01304     {
01305         SetCapFlag(POP3_HAS_AUTH_RESP_CODE);
01306         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01307     }
01308     else
01309     // see RFC 2595, chapter 4
01310     if (!PL_strcasecmp(line, "STLS")) 
01311     {
01312         nsresult rv;
01313         nsCOMPtr<nsISignatureVerifier> verifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv);
01314         // this checks if psm is installed...
01315         if (NS_SUCCEEDED(rv))
01316         {
01317             SetCapFlag(POP3_HAS_STLS);
01318             m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01319         }
01320     }
01321     else
01322     // see RFC 2449, chapter 6.3
01323     if (!PL_strncasecmp(line, "SASL", 4) && strlen(line) > 6)
01324     {
01325         nsCAutoString responseLine;
01326         responseLine.Assign(line + 5);
01327 
01328         if (responseLine.Find("PLAIN", PR_TRUE) >= 0)  
01329             SetCapFlag(POP3_HAS_AUTH_PLAIN);
01330 
01331         if (responseLine.Find("LOGIN", PR_TRUE) >= 0)  
01332             SetCapFlag(POP3_HAS_AUTH_LOGIN);
01333 
01334         if (responseLine.Find("GSSAPI", PR_TRUE) >= 0)
01335             SetCapFlag(POP3_HAS_AUTH_GSSAPI);
01336 
01337         nsresult rv;
01338         nsCOMPtr<nsISignatureVerifier> verifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv);
01339         // this checks if psm is installed...
01340         if (NS_SUCCEEDED(rv))
01341         {
01342             if (responseLine.Find("CRAM-MD5", PR_TRUE) >= 0)
01343                 SetCapFlag(POP3_HAS_AUTH_CRAM_MD5);
01344 
01345             if (responseLine.Find("NTLM", PR_TRUE) >= 0)  
01346                 SetCapFlag(POP3_HAS_AUTH_NTLM);
01347 
01348             if (responseLine.Find("MSN", PR_TRUE) >= 0)
01349                 SetCapFlag(POP3_HAS_AUTH_NTLM|POP3_HAS_AUTH_MSN);
01350         }
01351 
01352         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01353         // for use after mechs disabled fallbacks when login failed
01354         BackupAuthFlags();
01355     }
01356 
01357     PR_Free(line);
01358     return 0;
01359 }
01360 
01361 PRInt32 nsPop3Protocol::SendTLSResponse()
01362 {
01363   // only tear down our existing connection and open a new one if we received
01364   // a +OK response from the pop server after we issued the STLS command
01365   nsresult rv = NS_OK;
01366   if (m_pop3ConData->command_succeeded) 
01367   {
01368       nsCOMPtr<nsISupports> secInfo;
01369       nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport, &rv);
01370       if (NS_FAILED(rv)) return rv;
01371 
01372       rv = strans->GetSecurityInfo(getter_AddRefs(secInfo));
01373 
01374       if (NS_SUCCEEDED(rv) && secInfo)
01375       {
01376           nsCOMPtr<nsISSLSocketControl> sslControl = do_QueryInterface(secInfo, &rv);
01377 
01378           if (NS_SUCCEEDED(rv) && sslControl)
01379               rv = sslControl->StartTLS();
01380       }
01381 
01382     if (NS_SUCCEEDED(rv))
01383     {
01384       m_pop3ConData->next_state = POP3_SEND_AUTH;
01385       m_tlsEnabled = PR_TRUE;
01386 
01387       // certain capabilities like POP3_HAS_AUTH_APOP should be 
01388       // preserved across the connections.
01389       PRUint32 preservedCapFlags = m_pop3ConData->capability_flags & POP3_HAS_AUTH_APOP;
01390       m_pop3ConData->capability_flags =     // resetting the flags
01391         POP3_AUTH_MECH_UNDEFINED |
01392         POP3_HAS_AUTH_USER |                // should be always there
01393         POP3_GURL_UNDEFINED |
01394         POP3_UIDL_UNDEFINED |
01395         POP3_TOP_UNDEFINED |
01396         POP3_XTND_XLST_UNDEFINED | 
01397         preservedCapFlags;
01398       m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01399       return rv;
01400     }
01401   }
01402 
01403   ClearFlag(POP3_HAS_STLS);
01404   m_pop3ConData->next_state = POP3_PROCESS_AUTH;
01405 
01406   return rv;
01407 }
01408 
01409 PRInt32 nsPop3Protocol::ProcessAuth()
01410 {
01411     if (!m_tlsEnabled)
01412     {
01413       if(TestCapFlag(POP3_HAS_STLS))
01414       {
01415         if (m_socketType == nsIMsgIncomingServer::tryTLS ||
01416             m_socketType == nsIMsgIncomingServer::alwaysUseTLS)
01417         {
01418             nsCAutoString command("STLS" CRLF);
01419 
01420             m_pop3ConData->next_state_after_response = POP3_TLS_RESPONSE;
01421             return SendData(m_url, command.get());
01422         }
01423       }
01424       else if (m_socketType == nsIMsgIncomingServer::alwaysUseTLS)
01425       {
01426           m_pop3ConData->next_state = POP3_ERROR_DONE;
01427           return(Error(NS_ERROR_COULD_NOT_CONNECT_VIA_TLS));
01428       }
01429     }
01430 
01431     m_password_already_sent = PR_FALSE;
01432 
01433     if(m_useSecAuth)
01434     {
01435       if (TestCapFlag(POP3_HAS_AUTH_GSSAPI))
01436           m_pop3ConData->next_state = POP3_AUTH_GSSAPI;
01437       else if (TestCapFlag(POP3_HAS_AUTH_CRAM_MD5))
01438           m_pop3ConData->next_state = POP3_SEND_USERNAME;
01439       else
01440         if (TestCapFlag(POP3_HAS_AUTH_NTLM))
01441             m_pop3ConData->next_state = POP3_AUTH_NTLM;
01442         else
01443         if (TestCapFlag(POP3_HAS_AUTH_APOP))
01444             m_pop3ConData->next_state = POP3_SEND_PASSWORD;
01445         else
01446           return(Error(CANNOT_PROCESS_SECURE_AUTH));
01447     }
01448     else
01449     {
01450         if (TestCapFlag(POP3_HAS_AUTH_PLAIN))
01451             m_pop3ConData->next_state = POP3_SEND_USERNAME;
01452         else
01453         if (TestCapFlag(POP3_HAS_AUTH_LOGIN))
01454             m_pop3ConData->next_state = POP3_AUTH_LOGIN;
01455         else
01456         if (TestCapFlag(POP3_HAS_AUTH_USER))
01457             m_pop3ConData->next_state = POP3_SEND_USERNAME;
01458         else
01459             return(Error(POP3_SERVER_ERROR));
01460     }
01461 
01462     m_pop3ConData->pause_for_read = PR_FALSE;
01463 
01464     return 0;
01465 }
01466 
01467 void nsPop3Protocol::BackupAuthFlags()
01468 {
01469   m_origAuthFlags = m_pop3ConData->capability_flags &
01470                     (POP3_HAS_AUTH_ANY | POP3_HAS_AUTH_ANY_SEC);
01471 }
01472 
01473 void nsPop3Protocol::RestoreAuthFlags()
01474 {
01475   m_pop3ConData->capability_flags |= m_origAuthFlags;
01476 }
01477 
01478 PRInt32 nsPop3Protocol::AuthFallback()
01479 {
01480     if (m_pop3ConData->command_succeeded)
01481         if(m_password_already_sent)
01482         {
01483             m_nsIPop3Sink->SetUserAuthenticated(PR_TRUE);
01484             ClearFlag(POP3_PASSWORD_FAILED);
01485             m_pop3ConData->next_state = (m_pop3ConData->get_url) 
01486               ? POP3_SEND_GURL : POP3_SEND_STAT;
01487         }
01488         else
01489             m_pop3ConData->next_state = POP3_SEND_PASSWORD;
01490     else
01491     {
01492         // response code received shows that login failed not because of
01493         // wrong credential -> stop login without retry or pw dialog, only alert
01494         if (TestFlag(POP3_STOPLOGIN))
01495             return(Error((m_password_already_sent) 
01496                          ? POP3_PASSWORD_FAILURE : POP3_USERNAME_FAILURE));
01497 
01498         // response code received shows that server is certain about the
01499         // credential was wrong, or fallback has been disabled by pref
01500         // -> no fallback, show alert and pw dialog
01501         PRBool logonFallback = PR_TRUE;
01502         nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
01503         if (server)
01504           server->GetLogonFallback(&logonFallback);
01505         if (!logonFallback)
01506           SetFlag(POP3_AUTH_FAILURE);
01507 
01508         if (TestFlag(POP3_AUTH_FAILURE))
01509         {
01510             Error((m_password_already_sent) 
01511                          ? POP3_PASSWORD_FAILURE : POP3_USERNAME_FAILURE);
01512             SetFlag(POP3_PASSWORD_FAILED);
01513             ClearFlag(POP3_AUTH_FAILURE);
01514             m_pop3ConData->logonFailureCount++;
01515             return 0;
01516         }
01517 
01518         // we have no certain response code -> fallback and try again
01519         if (m_useSecAuth)
01520         {
01521             // If one authentication failed, we're going to
01522             // fall back on a less secure login method.
01523             if (TestCapFlag(POP3_HAS_AUTH_GSSAPI))
01524                 ClearCapFlag(POP3_HAS_AUTH_GSSAPI);
01525             else if (TestCapFlag(POP3_HAS_AUTH_CRAM_MD5))
01526                 // if CRAM-MD5 enabled, disable it
01527                 ClearCapFlag(POP3_HAS_AUTH_CRAM_MD5);
01528             else if (TestCapFlag(POP3_HAS_AUTH_NTLM))
01529                 // if NTLM enabled, disable it
01530                 ClearCapFlag(POP3_HAS_AUTH_NTLM|POP3_HAS_AUTH_MSN);
01531             else if (TestCapFlag(POP3_HAS_AUTH_APOP))
01532             {
01533                 // if APOP enabled, disable it
01534                 ClearCapFlag(POP3_HAS_AUTH_APOP);
01535                 // unsure because APOP failed and we can't determine why
01536                 Error(CANNOT_PROCESS_APOP_AUTH);
01537             }
01538         }
01539         else
01540         {
01541             // If one authentication failed, we're going to
01542             // fall back on a less secure login method.
01543             if (TestCapFlag(POP3_HAS_AUTH_PLAIN))
01544                 // if PLAIN enabled, disable it
01545                 ClearCapFlag(POP3_HAS_AUTH_PLAIN);
01546             else if(TestCapFlag(POP3_HAS_AUTH_LOGIN))
01547                 // if LOGIN enabled, disable it
01548                 ClearCapFlag(POP3_HAS_AUTH_LOGIN);
01549             else if(TestCapFlag(POP3_HAS_AUTH_USER))
01550             {
01551                 if(m_password_already_sent)
01552                     // if USER enabled, disable it
01553                     ClearCapFlag(POP3_HAS_AUTH_USER);
01554                 else
01555                     // if USER enabled,
01556                     // it was the username which was wrong -
01557                     // no fallback but return error
01558                     return(Error(POP3_USERNAME_FAILURE));
01559             }
01560         }
01561 
01562         // Only forget the password if we've no mechanism left.
01563         if (m_useSecAuth && !TestCapFlag(POP3_HAS_AUTH_ANY_SEC) ||
01564             !m_useSecAuth && !TestCapFlag(POP3_HAS_AUTH_ANY))
01565         {
01566             // Let's restore the original auth flags from AuthResponse so we can
01567             // try them again with new password and username
01568             RestoreAuthFlags();
01569             m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01570 
01571             Error(POP3_PASSWORD_FAILURE);
01572             /* The password failed.
01573       
01574                Sever the connection and go back to the `read password' state,
01575                which, upon success, will re-open the connection.  Set a flag
01576                which causes the prompt to be different that time (to indicate
01577                that the old password was bogus.)
01578         
01579                But if we're just checking for new mail (biff) then don't bother
01580                prompting the user for a password: just fail silently. 
01581             */
01582 
01583             SetFlag(POP3_PASSWORD_FAILED);
01584             m_pop3ConData->logonFailureCount++;
01585 
01586             if (m_nsIPop3Sink) 
01587                 m_nsIPop3Sink->SetMailAccountURL(NULL);
01588 
01589             return 0;
01590         }
01591 
01592         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01593 
01594         m_pop3ConData->command_succeeded = PR_TRUE;
01595 
01596         m_pop3ConData->next_state = POP3_PROCESS_AUTH;
01597     }
01598 
01599     if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
01600     {
01601         ClearCapFlag(POP3_AUTH_MECH_UNDEFINED);
01602         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01603     }
01604     
01605     m_pop3ConData->pause_for_read = PR_FALSE;
01606 
01607     return 0;
01608 }
01609 
01610 // LOGIN consists of three steps not two as USER/PASS or CRAM-MD5,
01611 // so we've to start here and continue in SendUsername if the server
01612 // responds + to "AUTH LOGIN"
01613 PRInt32 nsPop3Protocol::AuthLogin()
01614 {
01615     nsCAutoString command("AUTH LOGIN" CRLF);
01616     m_pop3ConData->next_state_after_response = POP3_AUTH_LOGIN_RESPONSE;
01617     m_pop3ConData->pause_for_read = PR_TRUE;
01618 
01619     return SendData(m_url, command.get());
01620 }
01621 
01622 PRInt32 nsPop3Protocol::AuthLoginResponse()
01623 {
01624     // need the test to be here instead in AuthFallback() to
01625     // differentiate between command AUTH LOGIN failed and
01626     // sending username using LOGIN mechanism failed.
01627     if (!m_pop3ConData->command_succeeded) 
01628     {
01629         // we failed with LOGIN, remove it
01630         ClearCapFlag(POP3_HAS_AUTH_LOGIN);
01631 
01632         m_pop3ConData->next_state = POP3_PROCESS_AUTH;
01633     }
01634     else
01635         m_pop3ConData->next_state = POP3_SEND_USERNAME;
01636 
01637     m_pop3ConData->pause_for_read = PR_FALSE;
01638 
01639     return 0;
01640 }
01641 
01642 // NTLM, like LOGIN consists of three steps not two as USER/PASS or CRAM-MD5,
01643 // so we've to start here and continue in SendUsername if the server
01644 // responds + to "AUTH NTLM"
01645 PRInt32 nsPop3Protocol::AuthNtlm()
01646 {
01647     nsCAutoString command (TestCapFlag(POP3_HAS_AUTH_MSN) ? "AUTH MSN" CRLF :
01648                                                             "AUTH NTLM" CRLF);
01649     m_pop3ConData->next_state_after_response = POP3_AUTH_NTLM_RESPONSE;
01650     m_pop3ConData->pause_for_read = PR_TRUE;
01651 
01652     return SendData(m_url, command.get());
01653 }
01654 
01655 PRInt32 nsPop3Protocol::AuthNtlmResponse()
01656 {
01657     // need the test to be here instead in AuthFallback() to
01658     // differentiate between command AUTH NTLM failed and
01659     // sending username using NTLM mechanism failed.
01660     if (!m_pop3ConData->command_succeeded) 
01661     {
01662         // we failed with NTLM, remove it
01663         ClearCapFlag(POP3_HAS_AUTH_NTLM|POP3_HAS_AUTH_MSN);
01664 
01665         m_pop3ConData->next_state = POP3_PROCESS_AUTH;
01666     }
01667     else
01668         m_pop3ConData->next_state = POP3_SEND_USERNAME;
01669 
01670     m_pop3ConData->pause_for_read = PR_FALSE;
01671 
01672     return 0;
01673 }
01674 
01675 PRInt32 nsPop3Protocol::AuthGSSAPI()
01676 {
01677     nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
01678     if (server) {
01679         nsCAutoString cmd;
01680         nsCAutoString service("pop@");
01681         nsXPIDLCString hostName;
01682         nsresult rv;
01683 
01684         server->GetRealHostName(getter_Copies(hostName));
01685         service.Append(hostName);
01686         rv = DoGSSAPIStep1(service.get(), m_username.get(), cmd);
01687         if (NS_SUCCEEDED(rv)) {
01688             m_GSSAPICache.Assign(cmd);
01689             m_pop3ConData->next_state_after_response = POP3_AUTH_GSSAPI_FIRST;
01690             m_pop3ConData->pause_for_read = PR_TRUE;
01691             return SendData(m_url, "AUTH GSSAPI" CRLF);
01692         }
01693     }
01694 
01695     ClearCapFlag(POP3_HAS_AUTH_GSSAPI);
01696     m_pop3ConData->next_state = POP3_PROCESS_AUTH;
01697     m_pop3ConData->pause_for_read = PR_FALSE;
01698     return NS_OK;
01699 }
01700 
01701 PRInt32 nsPop3Protocol::AuthGSSAPIResponse(PRBool first)
01702 {
01703     if (!m_pop3ConData->command_succeeded)
01704     {
01705         if (first)
01706             m_GSSAPICache.Truncate();
01707         ClearCapFlag(POP3_HAS_AUTH_GSSAPI);
01708         m_pop3ConData->next_state = POP3_PROCESS_AUTH;
01709         m_pop3ConData->pause_for_read = PR_FALSE;
01710         return NS_OK;
01711     }
01712     
01713     nsresult rv;
01714 
01715     m_pop3ConData->next_state_after_response = POP3_AUTH_GSSAPI_STEP;
01716     m_pop3ConData->pause_for_read = PR_TRUE;
01717 
01718     if (first) {
01719         m_GSSAPICache += CRLF;
01720         rv = SendData(m_url, m_GSSAPICache.get());
01721         m_GSSAPICache.Truncate();
01722     }
01723     else {
01724         nsCAutoString cmd;
01725         rv = DoGSSAPIStep2(m_commandResponse, cmd);
01726         if (NS_FAILED(rv))
01727             cmd = "*";
01728         if (rv == NS_SUCCESS_AUTH_FINISHED) {
01729             m_pop3ConData->next_state_after_response = POP3_AUTH_FALLBACK;
01730             m_password_already_sent = PR_TRUE;
01731         }
01732         cmd += CRLF;
01733         rv = SendData(m_url, cmd.get());
01734     }
01735 
01736     return rv;
01737 }
01738 
01739 PRInt32 nsPop3Protocol::SendUsername()
01740 {
01741     if(m_username.IsEmpty())
01742         return(Error(POP3_USERNAME_UNDEFINED));
01743 
01744     nsXPIDLCString password;
01745     PRBool okayValue = PR_TRUE;
01746     nsresult rv = GetPassword(getter_Copies(password), &okayValue);
01747     if (NS_SUCCEEDED(rv) && !okayValue)
01748     {
01749         // user has canceled the password prompt
01750         m_pop3ConData->next_state = POP3_ERROR_DONE;
01751         return NS_ERROR_ABORT;
01752     }
01753     else if (NS_FAILED(rv) || !password)
01754     {
01755       return Error(POP3_PASSWORD_UNDEFINED);
01756     }
01757 
01758     nsCAutoString cmd;
01759 
01760     if (m_useSecAuth)
01761     {
01762         if (TestCapFlag(POP3_HAS_AUTH_CRAM_MD5))
01763             cmd = "AUTH CRAM-MD5";
01764         else if (TestCapFlag(POP3_HAS_AUTH_NTLM))
01765             rv = DoNtlmStep1(m_username.get(), password.get(), cmd);
01766     }
01767     else
01768     {
01769         if (TestCapFlag(POP3_HAS_AUTH_PLAIN))
01770             cmd = "AUTH PLAIN";
01771         else if (TestCapFlag(POP3_HAS_AUTH_LOGIN))
01772         {
01773             char *base64Str = PL_Base64Encode(m_username.get(), m_username.Length(), nsnull);
01774             cmd = base64Str;
01775             PR_Free(base64Str);
01776         }
01777         else
01778         {
01779             cmd = "USER ";
01780             cmd += m_username;
01781         }
01782     }
01783     cmd += CRLF;
01784 
01785     m_pop3ConData->next_state_after_response = POP3_AUTH_FALLBACK;
01786 
01787     m_pop3ConData->pause_for_read = PR_TRUE;
01788 
01789     return SendData(m_url, cmd.get());
01790 }
01791 
01792 PRInt32 nsPop3Protocol::SendPassword()
01793 {
01794     if (m_username.IsEmpty())
01795         return(Error(POP3_USERNAME_UNDEFINED));
01796 
01797     nsXPIDLCString password;
01798     PRBool okayValue = PR_TRUE;
01799     nsresult rv = GetPassword(getter_Copies(password), &okayValue);
01800     if (NS_SUCCEEDED(rv) && !okayValue)
01801     {
01802         // user has canceled the password prompt
01803         m_pop3ConData->next_state = POP3_ERROR_DONE;
01804         return NS_ERROR_ABORT;
01805     }
01806     else if (NS_FAILED(rv) || !password)
01807     {
01808       return Error(POP3_PASSWORD_UNDEFINED);
01809     }
01810 
01811     nsCAutoString cmd;
01812     if (m_useSecAuth)
01813     {
01814         if (TestCapFlag(POP3_HAS_AUTH_CRAM_MD5))
01815         {
01816             char buffer[512];
01817             unsigned char digest[DIGEST_LENGTH];
01818 
01819             char *decodedChallenge = PL_Base64Decode(m_commandResponse.get(), 
01820             m_commandResponse.Length(), nsnull);
01821 
01822             if (decodedChallenge)
01823                 rv = MSGCramMD5(decodedChallenge, strlen(decodedChallenge), password.get(), password.Length(), digest);
01824             else
01825                 rv = NS_ERROR_FAILURE;
01826 
01827             if (NS_SUCCEEDED(rv) && digest)
01828             {
01829                 nsCAutoString encodedDigest;
01830                 char hexVal[8];
01831 
01832                 for (PRUint32 j=0; j<16; j++) 
01833                 {
01834                     PR_snprintf (hexVal,8, "%.2x", 0x0ff & (unsigned short)digest[j]);
01835                     encodedDigest.Append(hexVal); 
01836                 }
01837 
01838                 PR_snprintf(buffer, sizeof(buffer), "%s %s", m_username.get(), encodedDigest.get());
01839                 char *base64Str = PL_Base64Encode(buffer, strlen(buffer), nsnull);
01840                 cmd = base64Str;
01841                 PR_Free(base64Str);
01842             }
01843 
01844             if (NS_FAILED(rv))
01845                 cmd = "*";
01846         }
01847         else if (TestCapFlag(POP3_HAS_AUTH_NTLM))
01848             rv = DoNtlmStep2(m_commandResponse, cmd);
01849         else if (TestCapFlag(POP3_HAS_AUTH_APOP))
01850         {
01851             char buffer[512];
01852             unsigned char digest[DIGEST_LENGTH];
01853 
01854             rv = MSGApopMD5(m_ApopTimestamp.get(), m_ApopTimestamp.Length(), password.get(), password.Length(), digest);
01855 
01856             if (NS_SUCCEEDED(rv) && digest)
01857             {
01858                 nsCAutoString encodedDigest;
01859                 char hexVal[8];
01860 
01861                 for (PRUint32 j=0; j<16; j++) 
01862                 {
01863                     PR_snprintf (hexVal,8, "%.2x", 0x0ff & (unsigned short)digest[j]);
01864                     encodedDigest.Append(hexVal); 
01865                 }
01866 
01867                 PR_snprintf(buffer, sizeof(buffer), "APOP %s %s", m_username.get(), encodedDigest.get());
01868                 cmd = buffer;
01869             }
01870 
01871             if (NS_FAILED(rv))
01872                 cmd = "*";
01873         }
01874     }
01875     else
01876     {
01877         if (TestCapFlag(POP3_HAS_AUTH_PLAIN))
01878         {
01879             // workaround for IPswitch's IMail server software
01880             // this server goes into LOGIN mode even if we send "AUTH PLAIN"
01881             // "VXNlc" is the begin of the base64 encoded prompt for LOGIN
01882             if (m_commandResponse.Compare("VXNlc", PR_FALSE, 5) == 0)
01883             {
01884                 // disable PLAIN and enable LOGIN (in case it's not already enabled)
01885                 ClearCapFlag(POP3_HAS_AUTH_PLAIN);
01886                 SetCapFlag(POP3_HAS_AUTH_LOGIN);
01887                 m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01888 
01889                 // reenter authentication again at LOGIN response handler
01890                 m_pop3ConData->next_state = POP3_AUTH_LOGIN_RESPONSE;
01891                 m_pop3ConData->pause_for_read = PR_FALSE;
01892                 return 0;
01893             }
01894 
01895             char plain_string[512];
01896             int len = 1; /* first <NUL> char */
01897 
01898             memset(plain_string, 0, 512);
01899             PR_snprintf(&plain_string[1], 510, "%s", m_username.get());
01900             len += m_username.Length();
01901             len++; /* second <NUL> char */
01902             PR_snprintf(&plain_string[len], 511-len, "%s", password.get());
01903             len += password.Length();
01904 
01905             char *base64Str = PL_Base64Encode(plain_string, len, nsnull);
01906             cmd = base64Str;
01907             PR_Free(base64Str);
01908         }
01909         else if (TestCapFlag(POP3_HAS_AUTH_LOGIN)) 
01910         {
01911             char * base64Str = 
01912                 PL_Base64Encode(password, PL_strlen(password), nsnull);
01913             cmd = base64Str;
01914             PR_Free(base64Str);
01915         }
01916         else
01917         {
01918             cmd = "PASS ";
01919             cmd += password;    
01920         }
01921     }
01922     cmd += CRLF;
01923 
01924     m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
01925 
01926     m_pop3ConData->next_state_after_response = POP3_AUTH_FALLBACK;
01927 
01928     m_pop3ConData->pause_for_read = PR_TRUE;
01929 
01930     m_password_already_sent = PR_TRUE;
01931     m_lastPasswordSent = password;
01932     return SendData(m_url, cmd.get(), PR_TRUE);
01933 }
01934 
01935 PRInt32 nsPop3Protocol::SendStatOrGurl(PRBool sendStat)
01936 {
01937   nsCAutoString cmd;
01938   if (sendStat) 
01939   {
01940     cmd  = "STAT" CRLF;
01941     m_pop3ConData->next_state_after_response = POP3_GET_STAT;
01942   }
01943   else 
01944   {
01945     cmd = "GURL" CRLF;
01946     m_pop3ConData->next_state_after_response = POP3_GURL_RESPONSE;
01947   }
01948   return SendData(m_url, cmd.get());
01949 }
01950 
01951 
01952 PRInt32
01953 nsPop3Protocol::SendStat()
01954 {
01955   return SendStatOrGurl(PR_TRUE);
01956 }
01957 
01958 
01959 PRInt32
01960 nsPop3Protocol::GetStat()
01961 {
01962   char *num;
01963   char* newStr;
01964   char* oldStr;
01965 
01966     /* check stat response */
01967   if(!m_pop3ConData->command_succeeded)
01968     return(Error(POP3_STAT_FAILURE));
01969 
01970     /* stat response looks like:  %d %d
01971      * The first number is the number of articles
01972      * The second number is the number of bytes
01973      *
01974      *  grab the first and second arg of stat response
01975      */
01976   oldStr = ToNewCString(m_commandResponse);
01977   num = nsCRT::strtok(oldStr, " ", &newStr);
01978   if (num)
01979   {
01980     m_pop3ConData->number_of_messages = atol(num);
01981     num = nsCRT::strtok(newStr, " ", &newStr);
01982     m_commandResponse = newStr;
01983     if (num)
01984       m_totalFolderSize = (PRInt32) atol(num);  //we always initialize m_totalFolderSize to 0
01985   }
01986   else
01987     m_pop3ConData->number_of_messages = 0;
01988 
01989   PR_Free(oldStr);
01990   m_pop3ConData->really_new_messages = 0;
01991   m_pop3ConData->real_new_counter = 1;
01992 
01993   m_totalDownloadSize = -1; /* Means we need to calculate it, later. */
01994 
01995   if(m_pop3ConData->number_of_messages <= 0) 
01996   {
01997       /* We're all done.  We know we have no mail. */
01998      m_pop3ConData->next_state = POP3_SEND_QUIT;
01999      PL_HashTableEnumerateEntries(m_pop3ConData->uidlinfo->hash, hash_clear_mapper, nsnull);
02000      /* Hack - use nsPop3Sink to wipe out any stale Partial messages */
02001       m_nsIPop3Sink->BeginMailDelivery(PR_FALSE, nsnull, nsnull);
02002       m_nsIPop3Sink->AbortMailDelivery(this);
02003      return(0);
02004   }
02005 
02006   if (m_pop3ConData->only_check_for_new_mail && !m_pop3ConData->leave_on_server &&
02007       m_pop3ConData->size_limit < 0) 
02008   {
02009       /* We're just checking for new mail, and we're not playing any games that
02010          involve keeping messages on the server.  Therefore, we now know enough
02011          to finish up.  If we had no messages, that would have been handled
02012          above; therefore, we know we have some new messages. */
02013       m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_NewMail;
02014       m_pop3ConData->next_state = POP3_SEND_QUIT;
02015       return(0);
02016   }
02017 
02018 
02019   if (!m_pop3ConData->only_check_for_new_mail) 
02020   {
02021       // The following was added to prevent the loss of Data when we try and
02022       // write to somewhere we dont have write access error to (See bug 62480)
02023       // (Note: This is only a temp hack until the underlying XPCOM is fixed
02024       // to return errors)
02025 
02026       nsresult rv;
02027       nsCOMPtr <nsIMsgWindow> msgWindow;
02028       nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url);
02029       if (mailnewsUrl)
02030         rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
02031 //         NS_ASSERTION(NS_SUCCEEDED(rv) && msgWindow, "no msg window");
02032 
02033       rv = m_nsIPop3Sink->BeginMailDelivery(m_pop3ConData->only_uidl != nsnull, msgWindow,
02034                                                     &m_pop3ConData->msg_del_started);
02035       if (NS_FAILED(rv))
02036         if (rv == NS_MSG_FOLDER_BUSY)
02037           return(Error(POP3_MESSAGE_FOLDER_BUSY));
02038         else
02039           return(Error(POP3_MESSAGE_WRITE_ERROR));
02040       if(!m_pop3ConData->msg_del_started)
02041       {
02042         return(Error(POP3_MESSAGE_WRITE_ERROR));
02043       }
02044   }
02045 
02046   m_pop3ConData->next_state = POP3_SEND_LIST;
02047   return 0;
02048 }
02049 
02050 
02051 
02052 PRInt32
02053 nsPop3Protocol::SendGurl()
02054 {
02055     if (m_pop3ConData->capability_flags == POP3_CAPABILITY_UNDEFINED ||
02056         TestCapFlag(POP3_GURL_UNDEFINED | POP3_HAS_GURL))
02057         return SendStatOrGurl(PR_FALSE);
02058     else 
02059         return -1;
02060 }
02061 
02062 
02063 PRInt32
02064 nsPop3Protocol::GurlResponse()
02065 {
02066     ClearCapFlag(POP3_GURL_UNDEFINED);
02067     
02068     if (m_pop3ConData->command_succeeded) 
02069     {
02070         SetCapFlag(POP3_HAS_GURL);
02071         if (m_nsIPop3Sink)
02072             m_nsIPop3Sink->SetMailAccountURL(m_commandResponse.get());
02073     }
02074     else 
02075     {
02076         ClearCapFlag(POP3_HAS_GURL);
02077     }
02078     m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
02079     m_pop3ConData->next_state = POP3_SEND_QUIT;
02080 
02081     return 0;
02082 }
02083 
02084 PRInt32 nsPop3Protocol::SendList()
02085 {
02086     // check for server returning number of messages that will cause the calculation
02087     // of the size of the block for msg_info to
02088     // overflow a 32 bit int, in turn causing us to allocate a block of memory much
02089     // smaller than we think we're allocating, and
02090     // potentially allowing the server to make us overwrite memory outside our heap
02091     // block.
02092 
02093     if (m_pop3ConData->number_of_messages > (0xFFFFF000 / sizeof(Pop3MsgInfo)))
02094         return MK_OUT_OF_MEMORY; 
02095 
02096 
02097     m_pop3ConData->msg_info = (Pop3MsgInfo *) 
02098       PR_CALLOC(sizeof(Pop3MsgInfo) * m_pop3ConData->number_of_messages);
02099     if (!m_pop3ConData->msg_info)
02100         return(MK_OUT_OF_MEMORY);
02101     m_pop3ConData->next_state_after_response = POP3_GET_LIST;
02102     m_listpos = 0;
02103     return SendData(m_url, "LIST"CRLF);
02104 }
02105 
02106 
02107 
02108 PRInt32
02109 nsPop3Protocol::GetList(nsIInputStream* inputStream, 
02110                         PRUint32 length)
02111 {
02112   char * line;
02113   PRUint32 ln = 0;
02114   PRInt32 msg_num;
02115   
02116   /* check list response 
02117   * This will get called multiple times
02118   * but it's alright since command_succeeded
02119   * will remain constant
02120   */
02121   if(!m_pop3ConData->command_succeeded)
02122     return(Error(POP3_LIST_FAILURE));
02123   
02124   PRBool pauseForMoreData = PR_FALSE;
02125   nsresult rv;
02126   line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
02127   if (NS_FAILED(rv))
02128     return -1;
02129   
02130   if(pauseForMoreData || !line)
02131   {
02132     m_pop3ConData->pause_for_read = PR_TRUE;
02133     PR_Free(line);
02134     return(ln);
02135   }
02136   
02137   PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
02138   
02139   /* parse the line returned from the list command 
02140   * it looks like
02141   * #msg_number #bytes
02142   *
02143   * list data is terminated by a ".CRLF" line
02144   */
02145   if(!PL_strcmp(line, "."))
02146   {
02147     // limit the list if fewer entries than given in STAT response
02148     if(m_listpos < m_pop3ConData->number_of_messages)
02149       m_pop3ConData->number_of_messages = m_listpos;
02150     m_pop3ConData->next_state = POP3_SEND_UIDL_LIST;
02151     m_pop3ConData->pause_for_read = PR_FALSE;
02152     PR_Free(line);
02153     return(0);
02154   }
02155   
02156   char *token, *newStr;
02157   token = nsCRT::strtok(line, " ", &newStr);
02158   if (token)
02159   {
02160     msg_num = atol(token);
02161     m_listpos++;
02162     
02163     if(m_listpos <= m_pop3ConData->number_of_messages && m_listpos > 0)
02164     {
02165       token = nsCRT::strtok(newStr, " ", &newStr);
02166       if (token)
02167       {
02168         m_pop3ConData->msg_info[m_listpos-1].size = atol(token);
02169         m_pop3ConData->msg_info[m_listpos-1].msgnum = msg_num;
02170       }
02171     }
02172   }
02173 
02174   PR_Free(line);
02175   return(0);
02176 }
02177 
02178 PRInt32 nsPop3Protocol::SendFakeUidlTop()
02179 {
02180   char * cmd = PR_smprintf("TOP %ld 1" CRLF, m_pop3ConData->msg_info[m_pop3ConData->current_msg_to_top - 1].msgnum);
02181   PRInt32 status = -1;
02182   if (cmd)
02183   {
02184     m_pop3ConData->next_state_after_response = POP3_GET_FAKE_UIDL_TOP;
02185     m_pop3ConData->pause_for_read = PR_TRUE;
02186     m_parsingMultiLineMessageId = PR_FALSE;
02187     status = SendData(m_url, cmd);
02188   }
02189   
02190   PR_Free(cmd);
02191   return status;
02192 }
02193 
02194 PRInt32 nsPop3Protocol::StartUseTopForFakeUidl()
02195 {
02196     m_pop3ConData->current_msg_to_top = m_pop3ConData->number_of_messages;
02197     m_pop3ConData->number_of_messages_not_seen_before = 0;
02198     m_pop3ConData->found_new_message_boundary = PR_FALSE;
02199     m_pop3ConData->delete_server_message_during_top_traversal = PR_FALSE;
02200 
02201     /* may set delete_server_message_during_top_traversal to true */
02202     PL_HashTableEnumerateEntries(m_pop3ConData->uidlinfo->hash, 
02203                                   net_pop3_check_for_hash_messages_marked_delete,
02204                                   (void *)m_pop3ConData);
02205 
02206     return (SendFakeUidlTop());
02207 }
02208 
02209 // This function is used when we have a server that doesn't support UIDL,
02210 // and we send the TOP command to use the MESSAGE-ID header as a replacement
02211 // for the UIDL. It parses the the data from the TOP command, looking
02212 // for the MESSAGE-ID: header, and then determines the next state to go to 
02213 // in the state machine.
02214 PRInt32 nsPop3Protocol::GetFakeUidlTop(nsIInputStream* inputStream, 
02215                                        PRUint32 length)
02216 {
02217   char * line, *newStr;
02218   PRUint32 ln = 0;
02219   
02220   /* check list response 
02221   * This will get called multiple times
02222   * but it's alright since command_succeeded
02223   * will remain constant
02224   */
02225   nsresult rv;
02226   if(!m_pop3ConData->command_succeeded) 
02227   {
02228     
02229     /* UIDL, XTND and TOP are all unsupported for this mail server.
02230        Tell the user to join the 20th century.
02231     
02232        Tell the user this, and refuse to download any messages until they've
02233        gone into preferences and turned off the `Keep Mail on Server' and
02234        `Maximum Message Size' prefs.  Some people really get their panties
02235        in a bunch if we download their mail anyway. (bug 11561)
02236     */
02237     
02238     // set up status first, so if the rest fails, state is ok
02239     m_pop3ConData->next_state = POP3_ERROR_DONE;
02240     m_pop3ConData->pause_for_read = PR_FALSE;
02241     
02242     // get the hostname first, convert to unicode
02243     nsCAutoString hostName;
02244     m_url->GetHost(hostName);
02245     
02246     NS_ConvertUTF8toUCS2 hostNameUnicode(hostName);
02247     
02248     const PRUnichar *formatStrings[] =
02249     {
02250       hostNameUnicode.get(),
02251     };
02252     
02253     // get the strings for the format
02254     nsCOMPtr<nsIStringBundle> bundle;
02255     rv = mStringService->GetBundle(getter_AddRefs(bundle));
02256     NS_ENSURE_SUCCESS(rv, -1);
02257     
02258     nsXPIDLString statusString;
02259     rv = bundle->FormatStringFromID(POP3_SERVER_DOES_NOT_SUPPORT_UIDL_ETC,
02260       formatStrings, 1,
02261       getter_Copies(statusString));
02262     NS_ENSURE_SUCCESS(rv, -1);
02263     
02264     
02265     UpdateStatusWithString(statusString);
02266     
02267     return -1;
02268     
02269   }
02270   
02271   PRBool pauseForMoreData = PR_FALSE;
02272   line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
02273   if (NS_FAILED(rv))
02274     return -1;
02275   
02276   if(pauseForMoreData || !line)
02277   {
02278     m_pop3ConData->pause_for_read = PR_TRUE;
02279     PR_Free(line);
02280     return 0;
02281   }
02282   
02283   PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
02284   
02285   if(!PL_strcmp(line, "."))
02286   {
02287     m_pop3ConData->current_msg_to_top--;
02288     if (!m_pop3ConData->current_msg_to_top || 
02289       (m_pop3ConData->found_new_message_boundary &&
02290       !m_pop3ConData->delete_server_message_during_top_traversal))
02291     {
02292       /* we either ran out of messages or reached the edge of new
02293       messages and no messages are marked deleted */
02294       if (m_pop3ConData->only_check_for_new_mail)
02295       {
02296         m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_NewMail;
02297         m_nsIPop3Sink->SetBiffStateAndUpdateFE(nsIMsgFolder::nsMsgBiffState_NewMail, m_pop3ConData->really_new_messages, PR_TRUE);
02298         m_pop3ConData->next_state = POP3_SEND_QUIT;
02299       }
02300       else
02301       {
02302         m_pop3ConData->list_done = PR_TRUE;
02303         m_pop3ConData->next_state = POP3_GET_MSG;
02304       }
02305       m_pop3ConData->pause_for_read = PR_FALSE;
02306       
02307       /* if all of the messages are new, toss all hash table entries */
02308       if (!m_pop3ConData->current_msg_to_top &&
02309         !m_pop3ConData->found_new_message_boundary)
02310         PL_HashTableEnumerateEntries(m_pop3ConData->uidlinfo->hash, hash_clear_mapper, nsnull);
02311     }
02312     else
02313     {
02314       /* this message is done, go to the next */
02315       m_pop3ConData->next_state = POP3_SEND_FAKE_UIDL_TOP;
02316       m_pop3ConData->pause_for_read = PR_FALSE;
02317     }
02318   }
02319   else
02320   {
02321     if(m_parsingMultiLineMessageId && !(*line == ' ' || *line == '\t'))
02322     {
02323       // We already read the Message-Id-line, but didn't get
02324       // a message id. Treat it as not present and continue.
02325       m_pop3ConData->number_of_messages_not_seen_before++;
02326       m_parsingMultiLineMessageId = PR_FALSE;
02327     }
02328 
02329     int state = 0;
02330 
02331     /* we are looking for a string of the form
02332     Message-Id: <199602071806.KAA14787@neon.netscape.com> */
02333     if (!PL_strncasecmp(line, "MESSAGE-ID:", 11) || m_parsingMultiLineMessageId)
02334     {
02335       if(m_parsingMultiLineMessageId)
02336       {
02337         m_parsingMultiLineMessageId = PR_FALSE;
02338         // skip leading whitespace of folded header
02339         newStr = line;
02340         while(*newStr == ' ' || *newStr == '\t')
02341           newStr++;
02342       }
02343       else
02344         // skip "MESSAGE-ID:"
02345         newStr = line + 11;
02346 
02347       char *message_id_token = nsCRT::strtok(newStr, " ", &newStr);
02348       if (message_id_token)
02349       {
02350         Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, message_id_token);
02351         if (uidlEntry)
02352           state = uidlEntry->status;
02353       }
02354       else
02355       {
02356         m_parsingMultiLineMessageId = PR_TRUE;
02357         return 0;
02358       }
02359 
02360       if (!m_pop3ConData->only_uidl && message_id_token && (state == 0))
02361       { /* we have not seen this message before */
02362         m_pop3ConData->number_of_messages_not_seen_before++;
02363         m_pop3ConData->msg_info[m_pop3ConData->current_msg_to_top-1].uidl = 
02364           PL_strdup(message_id_token);
02365         if (!m_pop3ConData->msg_info[m_pop3ConData->current_msg_to_top-1].uidl)
02366         {
02367           PR_Free(line);
02368           return MK_OUT_OF_MEMORY;
02369         }
02370       }
02371       else if (m_pop3ConData->only_uidl && message_id_token &&
02372         !PL_strcmp(m_pop3ConData->only_uidl, message_id_token))
02373       {
02374         m_pop3ConData->last_accessed_msg = m_pop3ConData->current_msg_to_top - 1;
02375         m_pop3ConData->found_new_message_boundary = PR_TRUE;
02376         m_pop3ConData->msg_info[m_pop3ConData->current_msg_to_top-1].uidl =
02377           PL_strdup(message_id_token);
02378         if (!m_pop3ConData->msg_info[m_pop3ConData->current_msg_to_top-1].uidl)
02379         {
02380           PR_Free(line);
02381           return MK_OUT_OF_MEMORY;
02382         }
02383       }
02384       else if (!m_pop3ConData->only_uidl)
02385       { /* we have seen this message and we care about the edge,
02386         stop looking for new ones */
02387         if (m_pop3ConData->number_of_messages_not_seen_before != 0)
02388         {
02389           m_pop3ConData->last_accessed_msg =
02390             m_pop3ConData->current_msg_to_top;
02391           m_pop3ConData->found_new_message_boundary = PR_TRUE;
02392           /* we stay in this state so we can process the rest of the
02393           lines in the top message */
02394         }
02395         else
02396         {
02397           m_pop3ConData->next_state = POP3_SEND_QUIT;
02398           m_pop3ConData->pause_for_read = PR_FALSE;
02399         }
02400       }
02401     }
02402   }
02403   
02404   PR_Free(line);
02405   return 0;
02406 }
02407 
02408 
02409 /* km
02410  *
02411  *     net_pop3_send_xtnd_xlst_msgid
02412  *
02413  *  Process state: POP3_SEND_XTND_XLST_MSGID
02414  *
02415  *     If we get here then UIDL is not supported by the mail server.
02416  *  Some mail servers support a similar command:
02417  *
02418  *            XTND XLST Message-Id
02419  *
02420  *     Here is a sample transaction from a QUALCOMM server
02421  
02422  >>XTND XLST Message-Id
02423  <<+OK xlst command accepted; headers coming.
02424  <<1 Message-ID: <3117E4DC.2699@netscape.com>
02425  <<2 Message-Id: <199602062335.PAA19215@lemon.mcom.com>
02426  
02427  * This function will send the xtnd command and put us into the
02428  * POP3_GET_XTND_XLST_MSGID state
02429  *
02430 */
02431 PRInt32 nsPop3Protocol::SendXtndXlstMsgid()
02432 {
02433   if (TestCapFlag(POP3_HAS_XTND_XLST | POP3_XTND_XLST_UNDEFINED))
02434   {
02435     m_pop3ConData->next_state_after_response = POP3_GET_XTND_XLST_MSGID;
02436     m_pop3ConData->pause_for_read = PR_TRUE;
02437     m_listpos = 0;
02438     return SendData(m_url, "XTND XLST Message-Id" CRLF);
02439   }
02440   else
02441       return StartUseTopForFakeUidl();
02442 }
02443 
02444 
02445 /* km
02446  *
02447  *     net_pop3_get_xtnd_xlst_msgid
02448  *
02449  *  This code was created from the net_pop3_get_uidl_list boiler plate.
02450  *     The difference is that the XTND reply strings have one more token per
02451  *  string than the UIDL reply strings do.
02452  *
02453  */
02454 
02455 PRInt32
02456 nsPop3Protocol::GetXtndXlstMsgid(nsIInputStream* inputStream, 
02457                                  PRUint32 length)
02458 {
02459   char * line;
02460   PRUint32 ln = 0;
02461   PRInt32 msg_num;
02462   
02463   /* check list response 
02464   * This will get called multiple times
02465   * but it's alright since command_succeeded
02466   * will remain constant
02467   */
02468   ClearCapFlag(POP3_XTND_XLST_UNDEFINED);
02469   
02470   if(!m_pop3ConData->command_succeeded) {
02471     ClearCapFlag(POP3_HAS_XTND_XLST);
02472     m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
02473     m_pop3ConData->next_state = POP3_START_USE_TOP_FOR_FAKE_UIDL;
02474     m_pop3ConData->pause_for_read = PR_FALSE;
02475     return(0);
02476   }
02477   else
02478   {
02479     SetCapFlag(POP3_HAS_XTND_XLST);
02480     m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
02481   }        
02482   
02483   PRBool pauseForMoreData = PR_FALSE;
02484   nsresult rv;
02485   line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
02486   if (NS_FAILED(rv))
02487     return -1;
02488   
02489   if(pauseForMoreData || !line)
02490   {
02491     m_pop3ConData->pause_for_read = PR_TRUE;
02492     PR_Free(line);
02493     return ln;
02494   }
02495   
02496   PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
02497   
02498   /* parse the line returned from the list command 
02499   * it looks like
02500   * 1 Message-ID: <3117E4DC.2699@netscape.com>
02501   *
02502   * list data is terminated by a ".CRLF" line
02503   */
02504   if(!PL_strcmp(line, "."))
02505   {
02506     // limit the list if fewer entries than given in STAT response
02507     if(m_listpos < m_pop3ConData->number_of_messages)
02508       m_pop3ConData->number_of_messages = m_listpos;
02509     m_pop3ConData->list_done = PR_TRUE;
02510     m_pop3ConData->next_state = POP3_GET_MSG;
02511     m_pop3ConData->pause_for_read = PR_FALSE;
02512     PR_Free(line);
02513     return(0);
02514   }
02515   
02516   char *newStr;
02517   char *token = nsCRT::strtok(line, " ", &newStr);
02518   if (token)
02519   {
02520     msg_num = atol(token);
02521     m_listpos++;
02522 
02523     if(m_listpos <= m_pop3ConData->number_of_messages && m_listpos > 0) 
02524     {
02525       char *eatMessageIdToken = nsCRT::strtok(newStr, " ", &newStr);
02526       char *uidl = nsCRT::strtok(newStr, " ", &newStr); /* not really a uidl but a unique token -km */
02527       
02528       if (!uidl)
02529         /* This is bad.  The server didn't give us a UIDL for this message.
02530         I've seen this happen when somehow the mail spool has a message
02531         that contains a header that reads "X-UIDL: \n".  But how that got
02532         there, I have no idea; must be a server bug.  Or something. */
02533         uidl = "";
02534 
02535       // seeking right entry, but try the one that should it be first
02536       PRInt32 i;
02537       if(m_pop3ConData->msg_info[m_listpos - 1].msgnum == msg_num)
02538         i = m_listpos - 1;
02539       else
02540         for(i = 0; i < m_pop3ConData->number_of_messages &&
02541                    m_pop3ConData->msg_info[i].msgnum != msg_num; i++)
02542           ;
02543       
02544       m_pop3ConData->msg_info[i].uidl = PL_strdup(uidl);
02545       if (!m_pop3ConData->msg_info[i].uidl)
02546       {
02547         PR_Free(line);
02548         return MK_OUT_OF_MEMORY;
02549       }
02550     }
02551   }
02552   
02553   PR_Free(line);
02554   return(0);
02555 }
02556 
02557 
02558 PRInt32 nsPop3Protocol::SendUidlList()
02559 {
02560     if (TestCapFlag(POP3_HAS_UIDL | POP3_UIDL_UNDEFINED))
02561     {
02562         m_pop3ConData->next_state_after_response = POP3_GET_UIDL_LIST;
02563         m_pop3ConData->pause_for_read = PR_TRUE;
02564         m_listpos = 0;
02565         return SendData(m_url,"UIDL" CRLF);
02566     }
02567     else
02568         return SendXtndXlstMsgid();
02569 }
02570 
02571 
02572 PRInt32 nsPop3Protocol::GetUidlList(nsIInputStream* inputStream, 
02573                             PRUint32 length)
02574 {
02575     char * line;
02576     PRUint32 ln;
02577     PRInt32 msg_num;
02578 
02579     /* check list response 
02580      * This will get called multiple times
02581      * but it's alright since command_succeeded
02582      * will remain constant
02583      */
02584     ClearCapFlag(POP3_UIDL_UNDEFINED);
02585 
02586     if(!m_pop3ConData->command_succeeded) 
02587     {
02588         m_pop3ConData->next_state = POP3_SEND_XTND_XLST_MSGID;
02589         m_pop3ConData->pause_for_read = PR_FALSE;
02590         ClearCapFlag(POP3_HAS_UIDL);
02591         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
02592         return(0);
02593     }
02594     else
02595     {
02596         SetCapFlag(POP3_HAS_UIDL);
02597         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
02598     }
02599     
02600     PRBool pauseForMoreData = PR_FALSE;
02601     nsresult rv;
02602     line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
02603    if (NS_FAILED(rv))
02604      return -1;
02605 
02606     if(pauseForMoreData || !line)
02607     {
02608         PR_Free(line);
02609         m_pop3ConData->pause_for_read = PR_TRUE;
02610         return ln;
02611     }
02612 
02613     PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
02614 
02615     /* parse the line returned from the list command 
02616      * it looks like
02617      * #msg_number uidl
02618      *
02619      * list data is terminated by a ".CRLF" line
02620      */
02621     if(!PL_strcmp(line, "."))
02622     {
02623         // limit the list if fewer entries than given in STAT response
02624         if(m_listpos < m_pop3ConData->number_of_messages)
02625           m_pop3ConData->number_of_messages = m_listpos;
02626         m_pop3ConData->list_done = PR_TRUE;
02627         m_pop3ConData->next_state = POP3_GET_MSG;
02628         m_pop3ConData->pause_for_read = PR_FALSE;
02629         PR_Free(line);
02630         return(0);
02631     }
02632     
02633     char *newStr;
02634     char *token = nsCRT::strtok(line, " ", &newStr);
02635     if (token)
02636     {
02637       msg_num = atol(token);
02638       m_listpos++;
02639 
02640       if(m_listpos <= m_pop3ConData->number_of_messages && m_listpos > 0) 
02641       {
02642         char *uidl = nsCRT::strtok(newStr, " ", &newStr);
02643 
02644         if (!uidl)
02645             /* This is bad.  The server didn't give us a UIDL for this message.
02646                I've seen this happen when somehow the mail spool has a message
02647                that contains a header that reads "X-UIDL: \n".  But how that got
02648                there, I have no idea; must be a server bug.  Or something. */
02649             uidl = "";
02650 
02651         // seeking right entry, but try the one that should it be first
02652         PRInt32 i;
02653         if(m_pop3ConData->msg_info[m_listpos - 1].msgnum == msg_num)
02654           i = m_listpos - 1;
02655         else
02656           for(i = 0; i < m_pop3ConData->number_of_messages &&
02657                      m_pop3ConData->msg_info[i].msgnum != msg_num; i++)
02658             ;
02659 
02660         m_pop3ConData->msg_info[i].uidl = PL_strdup(uidl);
02661         if (!m_pop3ConData->msg_info[i].uidl)
02662         {
02663           PR_Free(line);
02664           return MK_OUT_OF_MEMORY;
02665         }
02666       }
02667     }
02668     PR_Free(line);
02669     return(0);
02670 }
02671 
02672 
02673 
02674 
02675 /* this function decides if we are going to do a
02676  * normal RETR or a TOP.  The first time, it also decides the total number
02677  * of bytes we're probably going to get.
02678  */
02679 PRInt32 
02680 nsPop3Protocol::GetMsg()
02681 {
02682   char c = 0;
02683   int i;
02684   PRBool prefBool = PR_FALSE;
02685   PRInt32 popstateTimestamp = TimeInSecondsFromPRTime(PR_Now());
02686   
02687   if(m_pop3ConData->last_accessed_msg >= m_pop3ConData->number_of_messages) 
02688   {
02689     /* Oh, gee, we're all done. */
02690     if(m_pop3ConData->msg_del_started)
02691     {
02692       if (!m_pop3ConData->only_uidl) 
02693       {
02694         if (m_pop3ConData->only_check_for_new_mail)
02695           m_nsIPop3Sink->SetBiffStateAndUpdateFE(m_pop3ConData->biffstate, m_pop3ConData->really_new_messages, PR_TRUE);      
02696         /* update old style biff */
02697         else 
02698             m_nsIPop3Sink->SetBiffStateAndUpdateFE(nsIMsgFolder::nsMsgBiffState_NewMail, m_pop3ConData->really_new_messages, PR_FALSE);
02699       }
02700       m_nsIPop3Sink->EndMailDelivery(this);
02701     }
02702     
02703     m_pop3ConData->next_state = POP3_SEND_QUIT;
02704     return 0;
02705   }
02706   
02707   if (m_totalDownloadSize < 0) {
02708   /* First time.  Figure out how many bytes we're about to get.
02709   If we didn't get any message info, then we are going to get
02710   everything, and it's easy.  Otherwise, if we only want one
02711   uidl, than that's the only one we'll get.  Otherwise, go
02712   through each message info, decide if we're going to get that
02713   message, and add the number of bytes for it. When a message is too
02714   large (per user's preferences) only add the size we are supposed
02715     to get. */
02716     m_pop3ConData->really_new_messages = 0;
02717     m_pop3ConData->real_new_counter = 1;
02718     if (m_pop3ConData->msg_info) {
02719       m_totalDownloadSize = 0;
02720       // init i with last_accessed_msg to prevend inspecting unpopulated
02721       // msg_info[i].uidl when coming from GetFakeUidlTop() -
02722       // because that would result in incorrect really_new_messages
02723       for (i = m_pop3ConData->last_accessed_msg;
02724            i < m_pop3ConData->number_of_messages; i++) 
02725       {
02726         c = 0;
02727         popstateTimestamp = TimeInSecondsFromPRTime(PR_Now());
02728         if (m_pop3ConData->only_uidl) {
02729           if (m_pop3ConData->msg_info[i].uidl &&
02730             PL_strcmp(m_pop3ConData->msg_info[i].uidl, 
02731             m_pop3ConData->only_uidl) == 0) 
02732           {
02733             /*if (m_pop3ConData->msg_info[i].size > m_pop3ConData->size_limit)
02734             m_totalDownloadSize = m_pop3ConData->size_limit;   */     /* if more than max, only count max */
02735             /*else*/
02736               m_totalDownloadSize = m_pop3ConData->msg_info[i].size;
02737               m_pop3ConData->really_new_messages = 1;
02738               /* we are only getting one message */ 
02739               m_pop3ConData->real_new_counter = 1;
02740               break;
02741           }
02742           continue;
02743         }
02744         if (m_pop3ConData->msg_info[i].uidl)
02745         {
02746           Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, m_pop3ConData->msg_info[i].uidl);
02747           if (uidlEntry)
02748           {
02749             c = uidlEntry->status;
02750             popstateTimestamp = uidlEntry->dateReceived;
02751           }
02752         }
02753         if ((c == KEEP) && !m_pop3ConData->leave_on_server)
02754         {   /* This message has been downloaded but kept on server, we
02755           * no longer want to keep it there */ 
02756           if (m_pop3ConData->newuidl == NULL)
02757           {
02758             m_pop3ConData->newuidl = PL_NewHashTable(20,
02759               PL_HashString, 
02760               PL_CompareStrings,
02761               PL_CompareValues,
02762               &gHashAllocOps,
02763               nsnull);
02764             if (!m_pop3ConData->newuidl)
02765               return MK_OUT_OF_MEMORY;
02766           }
02767           c = DELETE_CHAR;
02768           put_hash(m_pop3ConData->newuidl,
02769             m_pop3ConData->msg_info[i].uidl, DELETE_CHAR, popstateTimestamp);
02770           /*Mark message to be deleted in new table */ 
02771           put_hash(m_pop3ConData->uidlinfo->hash,
02772             m_pop3ConData->msg_info[i].uidl, DELETE_CHAR, popstateTimestamp);
02773           /*and old one too */ 
02774         }
02775         if ((c != KEEP) && (c != DELETE_CHAR) && (c != TOO_BIG)) 
02776         { /* message left on server */
02777           /*if (m_pop3ConData->msg_info[i].size > m_pop3ConData->size_limit)
02778                 m_totalDownloadSize +=
02779           m_pop3ConData->size_limit;      */     
02780           /* if more than max, only count max */
02781           /*else*/
02782           m_totalDownloadSize += m_pop3ConData->msg_info[i].size; 
02783           m_pop3ConData->really_new_messages++;
02784           /* a message we will really download */
02785         }
02786       }
02787     }
02788     else 
02789     {
02790       m_totalDownloadSize = m_totalFolderSize;
02791     }
02792     if (m_pop3ConData->only_check_for_new_mail) 
02793     {
02794       if (m_totalDownloadSize > 0)
02795       {
02796         m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_NewMail; 
02797         m_nsIPop3Sink->SetBiffStateAndUpdateFE(nsIMsgFolder::nsMsgBiffState_NewMail, m_pop3ConData->really_new_messages, PR_TRUE);
02798       }
02799       m_pop3ConData->next_state = POP3_SEND_QUIT;
02800       return(0);
02801     }
02802     /* get the amount of available space on the drive
02803     * and make sure there is enough
02804     */ 
02805     if(m_totalDownloadSize > 0) // skip all this if there aren't any messages
02806     {
02807       nsresult rv;
02808       PRInt64 mailboxSpaceLeft = LL_Zero();
02809       nsCOMPtr <nsIMsgFolder> folder;
02810       nsCOMPtr <nsIFileSpec> path;
02811       
02812       // Get the path to the current mailbox
02813       //      
02814       NS_ENSURE_TRUE(m_nsIPop3Sink, NS_ERROR_UNEXPECTED); 
02815       rv = m_nsIPop3Sink->GetFolder(getter_AddRefs(folder));
02816       if (NS_FAILED(rv)) return rv;
02817       rv = folder->GetPath(getter_AddRefs(path));
02818       if (NS_FAILED(rv)) return rv;
02819       
02820       // call GetDiskSpaceAvailable
02821       rv = path->GetDiskSpaceAvailable(&mailboxSpaceLeft);
02822       if (NS_FAILED(rv))
02823       {
02824         // The call to GetDiskSpaceAvailable FAILED!
02825         // This will happen on certain platforms where GetDiskSpaceAvailable
02826         // is not implemented. Since people on those platforms still need
02827         // to check mail, we will simply bypass the disk-space check.
02828         // 
02829         // We'll leave a debug message to warn people.
02830         
02831 #ifdef DEBUG
02832         printf("Call to GetDiskSpaceAvailable FAILED! \n");
02833 #endif
02834       }
02835       else
02836       {
02837 #ifdef DEBUG
02838         printf("GetDiskSpaceAvailable returned: %d bytes\n", mailboxSpaceLeft);
02839 #endif
02840         
02841         // Original comment from old implementation follows...
02842         /* When checking for disk space available, take into consideration
02843         * possible database 
02844         * changes, therefore ask for a little more than what the message
02845         * size is. Also, due to disk sector sizes, allocation blocks,
02846         * etc. The space "available" may be greater than the actual space
02847         * usable. */
02848         
02849         // The big if statement                  
02850         PRInt64 llResult;
02851         PRInt64 llExtraSafetySpace;
02852         PRInt64 llTotalDownloadSize;
02853         LL_I2L(llExtraSafetySpace, EXTRA_SAFETY_SPACE);
02854         LL_I2L(llTotalDownloadSize, m_totalDownloadSize);
02855         
02856         LL_ADD(llResult, llTotalDownloadSize, llExtraSafetySpace);
02857         if (LL_CMP(llResult, >, mailboxSpaceLeft))             
02858         {
02859           // Not enough disk space!
02860 #ifdef DEBUG
02861           printf("Not enough disk space! Raising error! \n");
02862 #endif
02863           // Should raise an error at this point.
02864           // First, we need to delete our references to the two interfaces..
02865           
02866           return (Error(MK_POP3_OUT_OF_DISK_SPACE));
02867         }
02868       }
02869       // Delete our references to the two interfaces..
02870     }
02871     }
02872     
02873     
02874     /* Look at this message, and decide whether to ignore it, get it, just get
02875     the TOP of it, or delete it. */
02876     
02877     m_pop3Server->GetAuthLogin(&prefBool);
02878     
02879     if (prefBool && (TestCapFlag(POP3_HAS_XSENDER)))
02880       m_pop3ConData->next_state = POP3_SEND_XSENDER;
02881     else
02882       m_pop3ConData->next_state = POP3_SEND_RETR;
02883     m_pop3ConData->truncating_cur_msg = PR_FALSE;
02884     m_pop3ConData->pause_for_read = PR_FALSE;
02885     if (m_pop3ConData->msg_info) 
02886     {
02887       Pop3MsgInfo* info = m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
02888       if (m_pop3ConData->only_uidl) 
02889       {
02890         if (info->uidl == NULL || PL_strcmp(info->uidl, m_pop3ConData->only_uidl))
02891           m_pop3ConData->next_state = POP3_GET_MSG;
02892         else
02893           m_pop3ConData->next_state = POP3_SEND_RETR;
02894       }
02895       else 
02896       {
02897         c = 0;
02898         if (m_pop3ConData->newuidl == NULL) 
02899         {
02900           m_pop3ConData->newuidl = PL_NewHashTable(20, PL_HashString, PL_CompareStrings, PL_CompareValues, &gHashAllocOps, nsnull);
02901           if (!m_pop3ConData->newuidl)
02902             return MK_OUT_OF_MEMORY;
02903         }
02904         if (info->uidl) 
02905         {
02906           Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, info->uidl);
02907           if (uidlEntry)
02908           {
02909             c = uidlEntry->status;
02910             popstateTimestamp = uidlEntry->dateReceived;
02911           }
02912         }
02913         m_pop3ConData->truncating_cur_msg = PR_FALSE;
02914         if (c == DELETE_CHAR) 
02915         {
02916           m_pop3ConData->next_state = POP3_SEND_DELE;
02917         }
02918         else if (c == KEEP) 
02919         {
02920           m_pop3ConData->next_state = POP3_GET_MSG;
02921         }
02922         else if (c == FETCH_BODY) 
02923         {
02924           m_pop3ConData->next_state = POP3_SEND_RETR;
02925          PL_HashTableRemove (m_pop3ConData->uidlinfo->hash, (void*)
02926               info->uidl);
02927         }
02928         else if ((c != TOO_BIG) &&
02929           (TestCapFlag(POP3_TOP_UNDEFINED | POP3_HAS_TOP)) &&
02930          (m_pop3ConData->headers_only ||
02931          ((m_pop3ConData->size_limit > 0) &&
02932           (info->size > m_pop3ConData->size_limit) && 
02933           !m_pop3ConData->only_uidl))) 
02934         { 
02935           /* message is too big */
02936           m_pop3ConData->truncating_cur_msg = PR_TRUE;
02937           m_pop3ConData->next_state = POP3_SEND_TOP;
02938           put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG, popstateTimestamp); 
02939          } 
02940          else if (c == TOO_BIG) 
02941          {
02942          /* message previously left on server, see if the max download size 
02943             has changed, because we may want to download the message this time 
02944             around. Otherwise ignore the message, we have the header. */
02945             if ((m_pop3ConData->size_limit > 0) && (info->size <=
02946               m_pop3ConData->size_limit)) 
02947               PL_HashTableRemove (m_pop3ConData->uidlinfo->hash, (void*)
02948               info->uidl);
02949             /* remove from our table, and download */
02950             else
02951             {
02952               m_pop3ConData->truncating_cur_msg = PR_TRUE;
02953               m_pop3ConData->next_state = POP3_GET_MSG;
02954               /* ignore this message and get next one */
02955               put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG, popstateTimestamp);
02956             }
02957           }
02958       }
02959       if (m_pop3ConData->next_state != POP3_SEND_DELE) 
02960       { 
02961         
02962         /* This is a message we have decided to keep on the server.  Notate
02963         that now for the future.  (Don't change the popstate file at all
02964         if only_uidl is set; in that case, there might be brand new messages
02965         on the server that we *don't* want to mark KEEP; we just want to
02966         leave them around until the user next does a GetNewMail.) */
02967         
02968         // if this is a message we already know about (i.e., it was in popstate.dat already),
02969         // we need to maintain the original date the message was downloaded.
02970         if (info->uidl && !m_pop3ConData->only_uidl && !m_pop3ConData->truncating_cur_msg)
02971             /* message already marked as too_big */
02972             put_hash(m_pop3ConData->newuidl, info->uidl, KEEP, popstateTimestamp); 
02973       }
02974       if (m_pop3ConData->next_state == POP3_GET_MSG) 
02975         m_pop3ConData->last_accessed_msg++; 
02976         /* Make sure we check the next message next time! */
02977     }
02978     return 0;
02979 }
02980 
02981 
02982 /* start retreiving just the first 20 lines
02983  */
02984 PRInt32 nsPop3Protocol::SendTop()
02985 {
02986    char * cmd = PR_smprintf( "TOP %ld %d" CRLF,
02987      m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum,
02988      m_pop3ConData->headers_only ? 0 : 20);
02989    PRInt32 status = -1;
02990    if (cmd)
02991    {
02992      m_pop3ConData->next_state_after_response = POP3_TOP_RESPONSE;    
02993      m_pop3ConData->cur_msg_size = -1;
02994      
02995      /* zero the bytes received in message in preparation for
02996      * the next
02997      */
02998      m_bytesInMsgReceived = 0;
02999      status = SendData(m_url,cmd);
03000    }
03001    PR_Free(cmd);
03002    return status;
03003 }
03004  
03005 /* send the xsender command
03006  */
03007 PRInt32 nsPop3Protocol::SendXsender()
03008 {
03009   char * cmd = PR_smprintf("XSENDER %ld" CRLF, m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
03010   PRInt32 status = -1;
03011   if (cmd)
03012   {  
03013     m_pop3ConData->next_state_after_response = POP3_XSENDER_RESPONSE;
03014     status = SendData(m_url, cmd);
03015     PR_Free(cmd);
03016   }
03017   return status;
03018 }
03019 
03020 PRInt32 nsPop3Protocol::XsenderResponse()
03021 {
03022     m_pop3ConData->seenFromHeader = PR_FALSE;
03023     m_senderInfo = "";
03024 
03025     if (m_pop3ConData->command_succeeded) {
03026         if (m_commandResponse.Length() > 4)
03027             m_senderInfo = m_commandResponse;
03028     }
03029     else {
03030         ClearCapFlag(POP3_HAS_XSENDER);
03031         m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
03032     }
03033 
03034     if (m_pop3ConData->truncating_cur_msg)
03035         m_pop3ConData->next_state = POP3_SEND_TOP;
03036     else
03037         m_pop3ConData->next_state = POP3_SEND_RETR;
03038     return 0;
03039 }
03040 
03041 /* retreive the whole message
03042  */
03043 PRInt32
03044 nsPop3Protocol::SendRetr()
03045 {
03046   
03047   char * cmd = PR_smprintf("RETR %ld" CRLF, m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
03048   PRInt32 status = -1;
03049   if (cmd)
03050   {
03051     m_pop3ConData->next_state_after_response = POP3_RETR_RESPONSE;    
03052     m_pop3ConData->cur_msg_size = -1;
03053     
03054     
03055     /* zero the bytes received in message in preparation for
03056     * the next
03057     */
03058     m_bytesInMsgReceived = 0;
03059     
03060     if (m_pop3ConData->only_uidl)
03061     {
03062       /* Display bytes if we're only downloading one message. */
03063       PR_ASSERT(!m_pop3ConData->graph_progress_bytes_p);
03064       UpdateProgressPercent(0, m_totalDownloadSize);
03065       m_pop3ConData->graph_progress_bytes_p = PR_TRUE;
03066     }
03067     else
03068     {
03069       nsresult rv;
03070       
03071       nsAutoString realNewString;
03072       realNewString.AppendInt(m_pop3ConData->real_new_counter);
03073       
03074       nsAutoString reallyNewMessages;
03075       reallyNewMessages.AppendInt(m_pop3ConData->really_new_messages);
03076       
03077       nsCOMPtr<nsIStringBundle> bundle;
03078       rv = mStringService->GetBundle(getter_AddRefs(bundle));
03079       NS_ASSERTION(NS_SUCCEEDED(rv), "couldn't get bundle");
03080       
03081       if (bundle)
03082       {
03083         const PRUnichar *formatStrings[] = {
03084           realNewString.get(),
03085             reallyNewMessages.get(),
03086         };
03087         
03088         nsXPIDLString finalString;
03089         rv = bundle->FormatStringFromID(LOCAL_STATUS_RECEIVING_MESSAGE_OF,
03090           formatStrings, 2,
03091           getter_Copies(finalString));
03092         NS_ASSERTION(NS_SUCCEEDED(rv), "couldn't format string");
03093         
03094         if (m_statusFeedback)
03095           m_statusFeedback->ShowStatusString(finalString);
03096       }
03097     }
03098     
03099     status = SendData(m_url, cmd);
03100   } // if cmd
03101   PR_Free(cmd);
03102   return status;
03103 }
03104 
03105 /* digest the message
03106  */
03107 PRInt32
03108 nsPop3Protocol::RetrResponse(nsIInputStream* inputStream, 
03109                              PRUint32 length)
03110 {
03111     PRUint32 buffer_size;
03112     PRInt32 flags = 0;
03113     char *uidl = NULL;
03114     nsresult rv;
03115 #if 0
03116     PRInt32 old_bytes_received = m_totalBytesReceived;
03117 #endif
03118     PRUint32 status = 0;
03119 
03120     if(m_pop3ConData->cur_msg_size == -1)
03121     {
03122         /* this is the beginning of a message
03123          * get the response code and byte size
03124          */
03125         if(!m_pop3ConData->command_succeeded)
03126             return Error(POP3_RETR_FAILURE);
03127         
03128         /* a successful RETR response looks like: #num_bytes Junk
03129            from TOP we only get the +OK and data
03130            */
03131         if (m_pop3ConData->truncating_cur_msg)
03132         { /* TOP, truncated message */
03133             flags |= MSG_FLAG_PARTIAL;
03134         }
03135         else
03136         {
03137           char *newStr;
03138           char * oldStr = ToNewCString(m_commandResponse);
03139           char *num = nsCRT::strtok(oldStr, " ", &newStr);
03140           if (num)
03141             m_pop3ConData->cur_msg_size = atol(num);
03142           m_commandResponse = newStr;
03143           PR_Free(oldStr);
03144         }
03145 
03146         /* RETR complete message */
03147         if (!m_senderInfo.IsEmpty())
03148             flags |= MSG_FLAG_SENDER_AUTHED;
03149         
03150         if(m_pop3ConData->cur_msg_size <= 0)
03151         {
03152           if (m_pop3ConData->msg_info)
03153             m_pop3ConData->cur_msg_size = m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].size;
03154           else
03155             m_pop3ConData->cur_msg_size = 0;
03156         }
03157 
03158         if (m_pop3ConData->msg_info && 
03159             m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].uidl)
03160             uidl = m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].uidl;
03161 
03162         m_pop3ConData->parsed_bytes = 0;
03163         m_pop3ConData->pop3_size = m_pop3ConData->cur_msg_size;
03164         m_pop3ConData->assumed_end = PR_FALSE;
03165         
03166         m_pop3Server->GetDotFix(&m_pop3ConData->dot_fix);
03167 
03168         PR_LOG(POP3LOGMODULE,PR_LOG_ALWAYS, 
03169                ("Opening message stream: MSG_IncorporateBegin"));
03170 
03171         /* open the message stream so we have someplace
03172          * to put the data
03173          */
03174         m_pop3ConData->real_new_counter++;
03175         /* (rb) count only real messages being downloaded */
03176         rv = m_nsIPop3Sink->IncorporateBegin(uidl, m_url, flags,
03177                                         &m_pop3ConData->msg_closure); 
03178 
03179         PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS, ("Done opening message stream!"));
03180 
03181         if(!m_pop3ConData->msg_closure || NS_FAILED(rv))
03182             return(Error(POP3_MESSAGE_WRITE_ERROR));
03183     }
03184     
03185     m_pop3ConData->pause_for_read = PR_TRUE;
03186 
03187     PRBool pauseForMoreData = PR_FALSE;
03188     char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData, &rv, PR_TRUE);
03189     if (NS_FAILED(rv))
03190       return -1;
03191 
03192     PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
03193     buffer_size = status;
03194 
03195     if (status == 0 && !line)  // no bytes read in...
03196       return (0);
03197 
03198     if (m_pop3ConData->msg_closure) /* not done yet */
03199     {
03200       // buffer the line we just read in, and buffer all remaining lines in the stream
03201       status = buffer_size;
03202       do
03203       {
03204         if (m_pop3ConData->msg_closure)
03205         {
03206           rv = HandleLine(line, buffer_size);
03207           if (NS_FAILED(rv))
03208             return (Error(POP3_MESSAGE_WRITE_ERROR));
03209 
03210           // buffer_size already includes MSG_LINEBREAK_LEN so
03211           // subtract and add CRLF
03212           // but not really sure we always had CRLF in input since
03213           // we also treat a single LF as line ending!
03214           m_pop3ConData->parsed_bytes += buffer_size - MSG_LINEBREAK_LEN + 2;
03215         }
03216 
03217         // now read in the next line
03218         PR_Free(line);
03219         line = m_lineStreamBuffer->ReadNextLine(inputStream, buffer_size,
03220                                                 pauseForMoreData, &rv, PR_TRUE);
03221         PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS,("RECV: %s", line));
03222         // buffer_size already includes MSG_LINEBREAK_LEN so
03223         // subtract and add CRLF
03224         // but not really sure we always had CRLF in input since
03225         // we also treat a single LF as line ending!
03226         status += buffer_size - MSG_LINEBREAK_LEN + 2;
03227       } while (line);
03228     }
03229 
03230     buffer_size = status;  // status holds # bytes we've actually buffered so far...
03231   
03232     /* normal read. Yay! */
03233     if ((PRInt32) (m_bytesInMsgReceived + buffer_size) >
03234         m_pop3ConData->cur_msg_size) 
03235         buffer_size = m_pop3ConData->cur_msg_size -
03236             m_bytesInMsgReceived; 
03237     
03238     m_bytesInMsgReceived += buffer_size;
03239     m_totalBytesReceived += buffer_size;
03240 
03241     // *** jefft in case of the message size that server tells us is different
03242     // from the actual message size
03243     if (pauseForMoreData && m_pop3ConData->dot_fix &&
03244         m_pop3ConData->assumed_end && m_pop3ConData->msg_closure)
03245     {
03246         nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
03247         nsCOMPtr<nsIMsgWindow> msgWindow;
03248         if (NS_SUCCEEDED(rv))
03249           rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
03250         rv = m_nsIPop3Sink->IncorporateComplete(msgWindow,
03251           m_pop3ConData->truncating_cur_msg ? m_pop3ConData->cur_msg_size : 0);
03252 
03253         // The following was added to prevent the loss of Data when we try
03254         // and write to somewhere we dont have write access error to (See
03255         // bug 62480)
03256         // (Note: This is only a temp hack until the underlying XPCOM is
03257         // fixed to return errors)
03258 
03259         if (NS_FAILED(rv))
03260             return (Error((rv == NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD)
03261                            ? POP3_TMP_DOWNLOAD_FAILED 
03262                            : POP3_MESSAGE_WRITE_ERROR));
03263 
03264         m_pop3ConData->msg_closure = nsnull;
03265     }
03266     
03267     if (!m_pop3ConData->msg_closure)
03268         /* meaning _handle_line read ".\r\n" at end-of-msg */
03269     {
03270         m_pop3ConData->pause_for_read = PR_FALSE;
03271 
03272         if (m_pop3ConData->truncating_cur_msg ||
03273             m_pop3ConData->leave_on_server )
03274         {
03275             Pop3UidlEntry *uidlEntry = NULL;
03276             Pop3MsgInfo* info = m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg; 
03277 
03278             /* Check for filter actions - FETCH or DELETE */
03279             if ((m_pop3ConData->newuidl) && (info->uidl))
03280               uidlEntry = (Pop3UidlEntry *)PL_HashTableLookup(m_pop3ConData->newuidl, info->uidl);
03281 
03282             if (uidlEntry && uidlEntry->status == FETCH_BODY &&
03283                 m_pop3ConData->truncating_cur_msg)
03284             {
03285             /* A filter decided to retrieve this full msg.
03286                Use GetMsg() so the popstate will update correctly,
03287                but don't let this msg get counted twice. */
03288                m_pop3ConData->next_state = POP3_GET_MSG;
03289                m_pop3ConData->real_new_counter--;
03290             /* Make sure we don't try to come through here again. */
03291                PL_HashTableRemove (m_pop3ConData->newuidl, (void*)info->uidl);
03292                put_hash(m_pop3ConData->uidlinfo->hash, info->uidl, FETCH_BODY, uidlEntry->dateReceived);
03293 
03294             } else if (uidlEntry && uidlEntry->status == DELETE_CHAR)
03295             {
03296             // A filter decided to delete this msg from the server
03297                m_pop3ConData->next_state = POP3_SEND_DELE;
03298             } else
03299             {
03300             /* We've retrieved all or part of this message, but we want to
03301                keep it on the server.  Go on to the next message. */
03302                m_pop3ConData->last_accessed_msg++;
03303                m_pop3ConData->next_state = POP3_GET_MSG;
03304             }
03305             if (m_pop3ConData->only_uidl)
03306             {
03307             /* GetMsg didn't update this field. Do it now */
03308                uidlEntry = (Pop3UidlEntry *)PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, m_pop3ConData->only_uidl);
03309                NS_ASSERTION(uidlEntry, "uidl not found in uidlinfo");
03310                if (uidlEntry)
03311              put_hash(m_pop3ConData->uidlinfo->hash, m_pop3ConData->only_uidl, KEEP, uidlEntry->dateReceived);
03312             }
03313         } else
03314         {
03315             m_pop3ConData->next_state = POP3_SEND_DELE;
03316         }
03317         
03318         /* if we didn't get the whole message add the bytes that we didn't get
03319            to the bytes received part so that the progress percent stays sane.
03320            */
03321         if(m_bytesInMsgReceived < m_pop3ConData->cur_msg_size)
03322             m_totalBytesReceived += (m_pop3ConData->cur_msg_size -
03323                                    m_bytesInMsgReceived);
03324     }
03325 
03326     /* set percent done to portion of total bytes of all messages
03327        that we're going to download. */
03328     if (m_totalDownloadSize)
03329       UpdateProgressPercent(m_totalBytesReceived, m_totalDownloadSize);
03330 
03331     PR_Free(line);    
03332     return(0);
03333 }
03334 
03335 
03336 PRInt32
03337 nsPop3Protocol::TopResponse(nsIInputStream* inputStream, PRUint32 length)
03338 {
03339   if (TestCapFlag(POP3_TOP_UNDEFINED))
03340   {
03341     ClearCapFlag(POP3_TOP_UNDEFINED);
03342     if (m_pop3ConData->command_succeeded)
03343       SetCapFlag(POP3_HAS_TOP);
03344     else
03345       ClearCapFlag(POP3_HAS_TOP);
03346     m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
03347   }
03348   
03349   if(m_pop3ConData->cur_msg_size == -1 &&  /* first line after TOP command sent */
03350     !m_pop3ConData->command_succeeded)    /* and TOP command failed */
03351   {
03352   /* TOP doesn't work so we can't retrieve the first part of this msg.
03353   So just go download the whole thing, and warn the user.
03354   
03355     Note that the progress bar will not be accurate in this case.
03356     Oops. #### */
03357     PRBool prefBool = PR_FALSE;
03358     m_pop3ConData->truncating_cur_msg = PR_FALSE;
03359     
03360     PRUnichar * statusTemplate = nsnull;
03361     mStringService->GetStringByID(POP3_SERVER_DOES_NOT_SUPPORT_THE_TOP_COMMAND, &statusTemplate);
03362     if (statusTemplate)
03363     {
03364       nsCAutoString hostName;
03365       PRUnichar * statusString = nsnull;
03366       m_url->GetHost(hostName);
03367       
03368       statusString = nsTextFormatter::smprintf(statusTemplate, hostName.get());
03369       UpdateStatusWithString(statusString);
03370       nsTextFormatter::smprintf_free(statusString);
03371       nsCRT::free(statusTemplate);
03372     }
03373     
03374     m_pop3Server->GetAuthLogin(&prefBool);
03375     
03376     if (prefBool && 
03377       (TestCapFlag(POP3_HAS_XSENDER)))
03378       m_pop3ConData->next_state = POP3_SEND_XSENDER;
03379     else
03380       m_pop3ConData->next_state = POP3_SEND_RETR;
03381     return(0);
03382   }
03383   
03384   /* If TOP works, we handle it in the same way as RETR. */
03385   return RetrResponse(inputStream, length);
03386 }
03387 
03388 /* line is handed over as null-terminated string with MSG_LINEBREAK */
03389 nsresult
03390 nsPop3Protocol::HandleLine(char *line, PRUint32 line_length)
03391 {
03392     nsresult rv = NS_OK;
03393     
03394     NS_ASSERTION(m_pop3ConData->msg_closure, "m_pop3ConData->msg_closure is null in nsPop3Protocol::HandleLine()");
03395     if (!m_pop3ConData->msg_closure)
03396         return NS_ERROR_NULL_POINTER;
03397     
03398     if (!m_senderInfo.IsEmpty() && !m_pop3ConData->seenFromHeader)
03399     {
03400         if (line_length > 6 && !PL_strncasecmp("From: ", line, 6))
03401         {
03402             m_pop3ConData->seenFromHeader = PR_TRUE;
03403             if (PL_strstr(line, m_senderInfo.get()) == NULL)
03404                 m_nsIPop3Sink->SetSenderAuthedFlag(m_pop3ConData->msg_closure,
03405                                                      PR_FALSE);
03406         }
03407     }
03408 
03409     // line contains only a single dot and linebreak -> message end
03410     if (line_length == 1 + MSG_LINEBREAK_LEN && line[0] == '.')
03411     {
03412         m_pop3ConData->assumed_end = PR_TRUE;    /* in case byte count from server is */
03413                                     /* wrong, mark we may have had the end */ 
03414         if (!m_pop3ConData->dot_fix || m_pop3ConData->truncating_cur_msg ||
03415             (m_pop3ConData->parsed_bytes >= (m_pop3ConData->pop3_size -3))) 
03416         {
03417             nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
03418             nsCOMPtr<nsIMsgWindow> msgWindow;
03419             if (NS_SUCCEEDED(rv))
03420               rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
03421             rv = m_nsIPop3Sink->IncorporateComplete(msgWindow,
03422               m_pop3ConData->truncating_cur_msg ? m_pop3ConData->cur_msg_size : 0);
03423 
03424             // The following was added to prevent the loss of Data when we try
03425             // and write to somewhere we dont have write access error to (See
03426             // bug 62480)
03427             // (Note: This is only a temp hack until the underlying XPCOM is
03428             // fixed to return errors)
03429 
03430             if (NS_FAILED(rv))
03431               return (Error((rv == NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD)
03432                              ? POP3_TMP_DOWNLOAD_FAILED 
03433                              : POP3_MESSAGE_WRITE_ERROR));
03434 
03435             m_pop3ConData->msg_closure = nsnull;
03436             return rv;
03437         }
03438     }
03439     /* Check if the line begins with the termination octet. If so
03440        and if another termination octet follows, we step over the
03441        first occurence of it. */
03442     else if (line_length > 1 && line[0] == '.' && line[1] == '.') {
03443         line++;
03444         line_length--;
03445     
03446     }
03447 
03448     return m_nsIPop3Sink->IncorporateWrite(line, line_length);
03449 }
03450 
03451 PRInt32 nsPop3Protocol::SendDele()
03452 {
03453     /* increment the last accessed message since we have now read it
03454      */
03455     char * cmd = PR_smprintf("DELE %ld" CRLF, m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
03456     m_pop3ConData->last_accessed_msg++;
03457        PRInt32 status = -1;
03458        if (cmd)
03459        {
03460               m_pop3ConData->next_state_after_response = POP3_DELE_RESPONSE;
03461               status = SendData(m_url, cmd);
03462        }
03463        PR_Free(cmd);
03464        return status;
03465 }
03466 
03467 PRInt32 nsPop3Protocol::DeleResponse()
03468 {
03469     Pop3UidlHost *host = NULL;
03470        
03471     host = m_pop3ConData->uidlinfo;
03472 
03473     /* the return from the delete will come here
03474      */
03475     if(!m_pop3ConData->command_succeeded)
03476         return(Error(POP3_DELE_FAILURE));
03477     
03478     
03479     /* ###chrisf
03480         the delete succeeded.  Write out state so that we
03481         keep track of all the deletes which have not yet been
03482         committed on the server.  Flush this state upon successful
03483         QUIT.
03484         
03485         We will do this by adding each successfully deleted message id
03486         to a list which we will write out to popstate.dat in 
03487         net_pop3_write_state().
03488         */
03489     if (host)
03490     {
03491         if (m_pop3ConData->msg_info &&
03492             m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl)
03493         { 
03494             if (m_pop3ConData->newuidl)
03495               if (m_pop3ConData->leave_on_server)
03496               {
03497                 PL_HashTableRemove(m_pop3ConData->newuidl, (void*)
03498                   m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl); 
03499               }
03500               else
03501               {
03502                 put_hash(m_pop3ConData->newuidl,
03503                   m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl, DELETE_CHAR, 0);   
03504                 /* kill message in new hash table */
03505               }
03506             else
03507                 PL_HashTableRemove(host->hash, 
03508               (void*) m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl);
03509         }
03510     }
03511 
03512     m_pop3ConData->next_state = POP3_GET_MSG;
03513 
03514     m_pop3ConData->pause_for_read = PR_FALSE;
03515     
03516     return(0);
03517 }
03518 
03519 
03520 PRInt32
03521 nsPop3Protocol::CommitState(PRBool remove_last_entry)
03522 {
03523   /* If we are leaving messages on the server, pull out the last
03524     uidl from the hash, because it might have been put in there before
03525     we got it into the database. 
03526   */
03527   if (remove_last_entry && m_pop3ConData->msg_info && 
03528       m_pop3ConData->last_accessed_msg < m_pop3ConData->number_of_messages) 
03529   {
03530     Pop3MsgInfo* info = m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg; 
03531     if (info && info->uidl && !m_pop3ConData->only_uidl &&
03532       m_pop3ConData->newuidl && m_pop3ConData->newuidl->nentries > 0) 
03533     {  
03534       PRBool val = PL_HashTableRemove (m_pop3ConData->newuidl, info->uidl);
03535       NS_ASSERTION(val, "uidl not in hash table");
03536     }
03537   }
03538   
03539   // only use newuidl if we successfully finished looping through all the
03540   // messages in the inbox.
03541   if (m_pop3ConData->newuidl)
03542   {
03543     if (m_pop3ConData->last_accessed_msg >= m_pop3ConData->number_of_messages)
03544     {
03545       PL_HashTableDestroy(m_pop3ConData->uidlinfo->hash);
03546       m_pop3ConData->uidlinfo->hash = m_pop3ConData->newuidl;
03547       m_pop3ConData->newuidl = nsnull;
03548     }
03549     else
03550     {
03551       /* If we are leaving messages on the server, pull out the last
03552         uidl from the hash, because it might have been put in there before
03553         we got it into the database.
03554       */
03555       if (remove_last_entry && m_pop3ConData->msg_info &&
03556           !m_pop3ConData->only_uidl && m_pop3ConData->newuidl->nentries > 0)
03557       {
03558         Pop3MsgInfo* info = m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
03559         if (info && info->uidl)
03560         {
03561           PRBool val = PL_HashTableRemove(m_pop3ConData->newuidl, info->uidl);
03562           NS_ASSERTION(val, "uidl not in hash table");
03563         }
03564       }
03565 
03566       // Add the entries in newuidl to m_pop3ConData->uidlinfo->hash to keep
03567       // track of the messages we *did* download in this session.
03568       PL_HashTableEnumerateEntries(m_pop3ConData->newuidl, net_pop3_copy_hash_entries, (void *)m_pop3ConData->uidlinfo->hash);
03569     }
03570   }
03571   
03572   if (!m_pop3ConData->only_check_for_new_mail) 
03573   {
03574     nsresult rv;
03575     nsCOMPtr<nsIFileSpec> mailDirectory;
03576     
03577     // get the mail directory
03578     nsCOMPtr<nsIMsgIncomingServer> server =
03579       do_QueryInterface(m_pop3Server, &rv);
03580     if (NS_FAILED(rv)) return -1;
03581     
03582     rv = server->GetLocalPath(getter_AddRefs(mailDirectory));
03583     if (NS_FAILED(rv)) return -1;
03584     
03585     // write the state in the mail directory
03586     net_pop3_write_state(m_pop3ConData->uidlinfo,
03587       mailDirectory);
03588   }
03589   return 0;
03590 }
03591 
03592 
03593 /* NET_process_Pop3  will control the state machine that
03594  * loads messages from a pop3 server
03595  *
03596  * returns negative if the transfer is finished or error'd out
03597  *
03598  * returns zero or more if the transfer needs to be continued.
03599  */
03600 nsresult nsPop3Protocol::ProcessProtocolState(nsIURI * url, nsIInputStream * aInputStream, 
03601                                               PRUint32 sourceOffset, PRUint32 aLength)
03602 {
03603   PRInt32 status = 0;
03604   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_url);
03605   
03606   PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS, ("Entering NET_ProcessPop3 %d",
03607     aLength));
03608   
03609   m_pop3ConData->pause_for_read = PR_FALSE; /* already paused; reset */
03610   
03611   if(m_username.IsEmpty())
03612   {
03613     // net_pop3_block = PR_FALSE;
03614     return(Error(POP3_USERNAME_UNDEFINED));
03615   }
03616   
03617   while(!m_pop3ConData->pause_for_read)
03618   {
03619     PR_LOG(POP3LOGMODULE, PR_LOG_ALWAYS, 
03620       ("POP3: Entering state: %d", m_pop3ConData->next_state));
03621     
03622     switch(m_pop3ConData->next_state)
03623     {
03624     case POP3_READ_PASSWORD:
03625     /* This is a separate state so that we're waiting for the
03626     user to type in a password while we don't actually have
03627     a connection to the pop server open; this saves us from
03628     having to worry about the server timing out on us while
03629       we wait for user input. */
03630       {
03631       /* If we're just checking for new mail (biff) then don't
03632       prompt the user for a password; just tell him we don't
03633         know whether he has new mail. */
03634         nsXPIDLCString password;
03635         PRBool okayValue;
03636         GetPassword(getter_Copies(password), &okayValue);
03637         const char * pwd = (const char *) password;
03638         if (!password || m_username.IsEmpty())
03639         {
03640           status = MK_POP3_PASSWORD_UNDEFINED;
03641           m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_Unknown;
03642           m_nsIPop3Sink->SetBiffStateAndUpdateFE(m_pop3ConData->biffstate, 0, PR_FALSE);   
03643           
03644           /* update old style biff */
03645           m_pop3ConData->next_state = POP3_FREE;
03646           m_pop3ConData->pause_for_read = PR_FALSE;
03647           break;
03648         }
03649         
03650         if (m_username.IsEmpty() || !pwd)
03651         {
03652           m_pop3ConData->next_state = POP3_ERROR_DONE;
03653           m_pop3ConData->pause_for_read = PR_FALSE;
03654         }
03655         else
03656         {
03657           // we are already connected so just go on and send the username
03658           PRBool prefBool = PR_FALSE; 
03659           m_pop3ConData->pause_for_read = PR_FALSE;
03660           m_pop3Server->GetAuthLogin(&prefBool);
03661           
03662           if (prefBool) 
03663           {
03664             if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
03665               m_pop3ConData->next_state = POP3_SEND_AUTH;
03666             else
03667               m_pop3ConData->next_state = POP3_SEND_CAPA;
03668           }
03669           else
03670             m_pop3ConData->next_state = POP3_SEND_USERNAME;
03671         }
03672         break;
03673       }
03674       
03675       
03676     case POP3_START_CONNECT:
03677       {
03678         m_pop3ConData->next_state = POP3_FINISH_CONNECT;
03679         m_pop3ConData->pause_for_read = PR_FALSE;
03680         break;
03681       }
03682       
03683     case POP3_FINISH_CONNECT:
03684       {
03685         m_pop3ConData->pause_for_read = PR_FALSE;
03686         m_pop3ConData->next_state = POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE;
03687         break;
03688       }
03689       
03690     case POP3_WAIT_FOR_RESPONSE:
03691       status = WaitForResponse(aInputStream, aLength);
03692       break;
03693       
03694     case POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE:
03695       {
03696         status = WaitForStartOfConnectionResponse(aInputStream, aLength);
03697         
03698         if(status)
03699         {
03700           PRBool prefBool = PR_FALSE;
03701           m_pop3Server->GetAuthLogin(&prefBool);
03702           
03703           if (prefBool)
03704           {
03705             if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
03706               m_pop3ConData->next_state = POP3_SEND_AUTH;
03707             else
03708               m_pop3ConData->next_state = POP3_SEND_CAPA;
03709           }
03710           else
03711             m_pop3ConData->next_state = POP3_SEND_USERNAME;
03712         }
03713         
03714         break;
03715       }
03716       
03717     case POP3_SEND_AUTH:
03718       status = SendAuth();
03719       break;
03720       
03721     case POP3_AUTH_RESPONSE:
03722       status = AuthResponse(aInputStream, aLength);
03723       break;
03724       
03725    case POP3_SEND_CAPA:
03726       status = SendCapa();
03727       break;
03728       
03729     case POP3_CAPA_RESPONSE:
03730       status = CapaResponse(aInputStream, aLength);
03731       break;
03732       
03733     case POP3_TLS_RESPONSE:
03734       status = SendTLSResponse();
03735       break;
03736 
03737     case POP3_PROCESS_AUTH:
03738       status = ProcessAuth();
03739       break;
03740       
03741     case POP3_AUTH_FALLBACK:
03742       status = AuthFallback();
03743       break;
03744       
03745     case POP3_AUTH_LOGIN:
03746       status = AuthLogin();
03747       break;
03748       
03749     case POP3_AUTH_LOGIN_RESPONSE:
03750       status = AuthLoginResponse();
03751       break;
03752       
03753     case POP3_AUTH_NTLM:
03754       status = AuthNtlm();
03755       break;
03756       
03757     case POP3_AUTH_NTLM_RESPONSE:
03758       status = AuthNtlmResponse();
03759       break;
03760       
03761     case POP3_AUTH_GSSAPI:
03762       status = AuthGSSAPI();
03763       break;
03764 
03765     case POP3_AUTH_GSSAPI_FIRST:
03766       UpdateStatus(POP3_CONNECT_HOST_CONTACTED_SENDING_LOGIN_INFORMATION);
03767       status = AuthGSSAPIResponse(true);
03768       break;
03769  
03770     case POP3_AUTH_GSSAPI_STEP:
03771       status = AuthGSSAPIResponse(false);
03772       break;
03773 
03774     case POP3_SEND_USERNAME:
03775       UpdateStatus(POP3_CONNECT_HOST_CONTACTED_SENDING_LOGIN_INFORMATION);
03776       status = SendUsername();
03777       break;
03778       
03779     case POP3_SEND_PASSWORD:
03780       status = SendPassword();
03781       break;
03782       
03783     case POP3_SEND_GURL:
03784       status = SendGurl();
03785       break;
03786       
03787     case POP3_GURL_RESPONSE:
03788       status = GurlResponse();
03789       break;
03790       
03791     case POP3_SEND_STAT:
03792       status = SendStat();
03793       break;
03794       
03795     case POP3_GET_STAT:
03796       status = GetStat();
03797       break;
03798       
03799     case POP3_SEND_LIST:
03800       status = SendList();
03801       break;
03802       
03803     case POP3_GET_LIST:
03804       status = GetList(aInputStream, aLength);
03805       break;
03806       
03807     case POP3_SEND_UIDL_LIST:
03808       status = SendUidlList();
03809       break;
03810       
03811     case POP3_GET_UIDL_LIST:
03812       status = GetUidlList(aInputStream, aLength);
03813       break;
03814       
03815     case POP3_SEND_XTND_XLST_MSGID:
03816       status = SendXtndXlstMsgid();
03817       break;
03818       
03819     case POP3_GET_XTND_XLST_MSGID:
03820       status = GetXtndXlstMsgid(aInputStream, aLength);
03821       break;
03822       
03823     case POP3_START_USE_TOP_FOR_FAKE_UIDL:
03824       status = StartUseTopForFakeUidl();
03825       break;
03826       
03827     case POP3_SEND_FAKE_UIDL_TOP:
03828       status = SendFakeUidlTop();
03829       break;
03830       
03831     case POP3_GET_FAKE_UIDL_TOP:
03832       status = GetFakeUidlTop(aInputStream, aLength);
03833       break;
03834       
03835     case POP3_GET_MSG:
03836       status = GetMsg();
03837       break;
03838       
03839     case POP3_SEND_TOP:
03840       status = SendTop();
03841       break;
03842       
03843     case POP3_TOP_RESPONSE:
03844       status = TopResponse(aInputStream, aLength);
03845       break;
03846       
03847     case POP3_SEND_XSENDER:
03848       status = SendXsender();
03849       break;
03850       
03851     case POP3_XSENDER_RESPONSE:
03852       status = XsenderResponse();
03853       break;
03854       
03855     case POP3_SEND_RETR:
03856       status = SendRetr();
03857       break;
03858       
03859     case POP3_RETR_RESPONSE:
03860       status = RetrResponse(aInputStream, aLength);
03861       break;
03862       
03863     case POP3_SEND_DELE:
03864       status = SendDele();
03865       break;
03866       
03867     case POP3_DELE_RESPONSE:
03868       status = DeleResponse();
03869       break;
03870       
03871     case POP3_SEND_QUIT:
03872     /* attempt to send a server quit command.  Since this means
03873     everything went well, this is a good time to update the
03874     status file and the FE's biff state.
03875       */
03876       if (!m_pop3ConData->only_uidl) 
03877       {
03878         /* update old style biff */
03879         if (!m_pop3ConData->only_check_for_new_mail)
03880         {
03881         /* We don't want to pop up a warning message any more (see
03882         bug 54116), so instead we put the "no new messages" or
03883         "retrieved x new messages" 
03884         in the status line.  Unfortunately, this tends to be running
03885         in a progress pane, so we try to get the real pane and
03886           show the message there. */
03887           
03888           if (m_totalDownloadSize <= 0) 
03889           {
03890             UpdateStatus(POP3_NO_MESSAGES);
03891             /* There are no new messages.  */
03892           }
03893           else 
03894           {
03895             PRUnichar * statusTemplate = nsnull;
03896             mStringService->GetStringByID(POP3_DOWNLOAD_COUNT, &statusTemplate);
03897             if (statusTemplate)
03898             {
03899               PRUnichar * statusString = nsTextFormatter::smprintf(statusTemplate, 
03900                 m_pop3ConData->real_new_counter - 1,
03901                 m_pop3ConData->really_new_messages);  
03902               UpdateStatusWithString(statusString);
03903               nsTextFormatter::smprintf_free(statusString);
03904               nsCRT::free(statusTemplate);
03905               
03906             }
03907           }
03908         }
03909       }
03910       
03911       status = SendData(mailnewsurl, "QUIT" CRLF);
03912       m_pop3ConData->next_state = POP3_WAIT_FOR_RESPONSE;
03913       m_pop3ConData->next_state_after_response = POP3_QUIT_RESPONSE;
03914       break;
03915       
03916     case POP3_QUIT_RESPONSE:
03917       if(m_pop3ConData->command_succeeded)
03918       {
03919       /*  the QUIT succeeded.  We can now flush the state in popstate.dat which
03920         keeps track of any uncommitted DELE's */
03921         
03922         /* here we need to clear the hash of all our 
03923         uncommitted deletes */
03924         /*
03925         if (m_pop3ConData->uidlinfo &&
03926         m_pop3ConData->uidlinfo->uncommitted_deletes) 
03927         XP_Clrhash (m_pop3ConData->uidlinfo->uncommitted_deletes);*/
03928         //delete the uidl because deletes are committed
03929         if (!m_pop3ConData->leave_on_server && m_pop3ConData->newuidl){  
03930           PL_HashTableEnumerateEntries(m_pop3ConData->newuidl, 
03931                                                         net_pop3_remove_messages_marked_delete,
03932                                                                 (void *)m_pop3ConData);   
03933         }
03934         
03935         
03936         m_pop3ConData->next_state = POP3_DONE;
03937         
03938       }
03939       else
03940       {
03941         m_pop3ConData->next_state = POP3_ERROR_DONE;
03942       }
03943       break;
03944       
03945     case POP3_DONE:
03946       CommitState(PR_FALSE);
03947       
03948       if (mailnewsurl)
03949         mailnewsurl->SetUrlState(PR_FALSE, NS_OK);
03950       m_pop3ConData->next_state = POP3_FREE;
03951       break;
03952       
03953     case POP3_INTERRUPTED:
03954       SendData(mailnewsurl, "QUIT" CRLF);
03955       m_pop3ConData->pause_for_read = PR_FALSE;
03956       m_pop3ConData->next_state = POP3_ERROR_DONE;
03957       break;
03958       
03959     case POP3_ERROR_DONE:
03960       /*  write out the state */
03961       if(m_pop3ConData->list_done)
03962         CommitState(PR_TRUE);
03963       
03964       if(m_pop3ConData->msg_closure)
03965       {
03966         m_nsIPop3Sink->IncorporateAbort(m_pop3ConData->only_uidl != nsnull);
03967         m_pop3ConData->msg_closure = NULL;
03968         m_nsIPop3Sink->AbortMailDelivery(this);
03969       }
03970       
03971       if(m_pop3ConData->msg_del_started)
03972       {
03973         PRUnichar * statusTemplate = nsnull;
03974         mStringService->GetStringByID(POP3_DOWNLOAD_COUNT, &statusTemplate);
03975         if (statusTemplate)
03976         {
03977           PRUnichar * statusString = nsTextFormatter::smprintf(statusTemplate, 
03978             m_pop3ConData->real_new_counter - 1,
03979             m_pop3ConData->really_new_messages);  
03980           UpdateStatusWithString(statusString);
03981           nsTextFormatter::smprintf_free(statusString);
03982           nsCRT::free(statusTemplate);
03983         }
03984         
03985         NS_ASSERTION (!TestFlag(POP3_PASSWORD_FAILED), "POP3_PASSWORD_FAILED set when del_started");
03986         m_nsIPop3Sink->AbortMailDelivery(this);
03987       }
03988       
03989       if (TestFlag(POP3_PASSWORD_FAILED) && m_pop3ConData->logonFailureCount < 6)
03990       {
03991       /* We got here because the password was wrong, so go
03992         read a new one and re-open the connection. */
03993         m_pop3ConData->next_state = POP3_READ_PASSWORD;
03994         m_pop3ConData->command_succeeded = PR_TRUE;
03995         status = 0;
03996         break;
03997       }
03998       else
03999         /* Else we got a "real" error, so finish up. */
04000         m_pop3ConData->next_state = POP3_FREE;       
04001       
04002       if (mailnewsurl)
04003         mailnewsurl->SetUrlState(PR_FALSE, NS_ERROR_FAILURE);
04004       m_pop3ConData->pause_for_read = PR_FALSE;
04005       break;
04006       
04007     case POP3_FREE:
04008       UpdateProgressPercent(0,0); // clear out the progress meter
04009       NS_ASSERTION(m_nsIPop3Sink, "with no sink, can't clear busy flag");
04010       if (m_nsIPop3Sink)
04011       {
04012         nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
04013         if (server)
04014           server->SetServerBusy(PR_FALSE); // the server is now not busy
04015       }
04016       m_pop3Server->SetRunningProtocol(nsnull);
04017       
04018       CloseSocket();
04019       return NS_OK;
04020       break;
04021       
04022     default:
04023       PR_ASSERT(0);
04024       
04025     }  /* end switch */
04026               
04027     if((status < 0) && m_pop3ConData->next_state != POP3_FREE)
04028     {
04029       m_pop3ConData->pause_for_read = PR_FALSE;
04030       m_pop3ConData->next_state = POP3_ERROR_DONE;
04031     }
04032     
04033   }  /* end while */
04034   
04035   return NS_OK;
04036           
04037 }
04038 
04039 nsresult nsPop3Protocol::CloseSocket()
04040 {
04041     nsresult rv = nsMsgProtocol::CloseSocket();
04042     m_url = nsnull;
04043     return rv;
04044 }
04045 
04046 NS_IMETHODIMP nsPop3Protocol::MarkMessages(nsVoidArray *aUIDLArray)
04047 {
04048   NS_ENSURE_ARG_POINTER(aUIDLArray);
04049   PRUint32 count = aUIDLArray->Count();
04050 
04051   for (PRUint32 i = 0; i < count; i++)
04052   {
04053     PRBool changed;
04054     if (m_pop3ConData->newuidl) 
04055       MarkMsgInHashTable(m_pop3ConData->newuidl, NS_STATIC_CAST(Pop3UidlEntry*,aUIDLArray->ElementAt(i)), &changed);
04056     if (m_pop3ConData->uidlinfo)
04057       MarkMsgInHashTable(m_pop3ConData->uidlinfo->hash, NS_STATIC_CAST(Pop3UidlEntry*,aUIDLArray->ElementAt(i)), &changed);
04058   }
04059   return NS_OK;
04060 }
04061 
04062 NS_IMETHODIMP nsPop3Protocol::CheckMessage(const char *aUidl, PRBool *aBool)
04063 {
04064   Pop3UidlEntry *uidlEntry = nsnull;
04065 
04066   if (aUidl)
04067   {
04068     if (m_pop3ConData->newuidl)
04069       uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->newuidl, aUidl);
04070     else if (m_pop3ConData->uidlinfo)
04071       uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, aUidl);
04072   }
04073 
04074   *aBool = uidlEntry ? PR_TRUE : PR_FALSE;
04075   return NS_OK;
04076 }
04077 
04078 
04079 /* Function for finding an APOP Timestamp and simple check
04080    it for its validity. If returning NS_OK m_ApopTimestamp
04081    contains the validated substring of m_commandResponse. */
04082 nsresult nsPop3Protocol::GetApopTimestamp()
04083 {
04084   PRInt32 startMark = m_commandResponse.Length(), endMark = -1;
04085 
04086   while (PR_TRUE)
04087   {
04088     // search for previous <
04089     if ((startMark = m_commandResponse.RFindChar('<', startMark - 1)) < 0)
04090       return NS_ERROR_FAILURE;
04091 
04092     // search for next >
04093     if ((endMark = m_commandResponse.FindChar('>', startMark)) < 0)
04094       continue;
04095 
04096     // look for an @ between start and end as a raw test
04097     PRInt32 at = m_commandResponse.FindChar('@', startMark);
04098     if (at < 0 || at >= endMark)
04099       continue;
04100 
04101     // now test if sub only consists of chars in ASCII range
04102     nsCString sub(Substring(m_commandResponse, startMark, endMark - startMark + 1));
04103     if (nsCRT::IsAscii(sub.get()))
04104     {
04105       // set m_ApopTimestamp to the validated substring
04106       m_ApopTimestamp.Assign(sub);
04107       break;
04108     }
04109   }
04110 
04111   return NS_OK;
04112 }