Back to index

lightning-sunbird  0.9+nobinonly
nsSmtpProtocol.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #ifdef MOZ_LOGGING
00039 // sorry, this has to be before the pre-compiled header
00040 #define FORCE_PR_LOG /* Allow logging in the release build */
00041 #endif
00042 
00043 #include "msgCore.h"
00044 #include "nsSmtpProtocol.h"
00045 #include "nscore.h"
00046 #include "nsIStreamListener.h"
00047 #include "nsIInputStream.h"
00048 #include "nsIOutputStream.h"
00049 #include "nsISocketTransport.h"
00050 #include "nsIMsgHeaderParser.h"
00051 #include "nsFileStream.h"
00052 #include "nsIMsgMailNewsUrl.h"
00053 #include "nsMsgBaseCID.h"
00054 #include "nsMsgCompCID.h"
00055 #include "nsIPrompt.h"
00056 #include "nsIAuthPrompt.h"
00057 #include "nsString.h"
00058 #include "nsTextFormatter.h"
00059 #include "nsIMsgIdentity.h"
00060 #include "nsISmtpServer.h"
00061 #include "nsMsgComposeStringBundle.h"
00062 #include "prtime.h"
00063 #include "prlog.h"
00064 #include "prerror.h"
00065 #include "prprf.h"
00066 #include "prmem.h"
00067 #include "plbase64.h"
00068 #include "prnetdb.h"
00069 #include "prsystem.h"
00070 #include "nsEscape.h"
00071 #include "nsMsgUtils.h"
00072 #include "nsIPipe.h"
00073 #include "nsMsgSimulateError.h"
00074 #include "nsNetUtil.h"
00075 #include "nsIPrefService.h"
00076 #include "nsISignatureVerifier.h"
00077 #include "nsISSLSocketControl.h"
00078 #include "nsPrintfCString.h"
00079 
00080 #ifndef XP_UNIX
00081 #include <stdarg.h>
00082 #endif /* !XP_UNIX */
00083 
00084 static PRLogModuleInfo *SMTPLogModule = nsnull;
00085 
00086 /* the output_buffer_size must be larger than the largest possible line
00087  * 2000 seems good for news
00088  *
00089  * jwz: I increased this to 4k since it must be big enough to hold the
00090  * entire button-bar HTML, and with the new "mailto" format, that can
00091  * contain arbitrarily long header fields like "references".
00092  *
00093  * fortezza: proxy auth is huge, buffer increased to 8k (sigh).
00094  */
00095 #define OUTPUT_BUFFER_SIZE (4096*2)
00096 
00098 // TEMPORARY HARD CODED FUNCTIONS 
00100 
00101 /* based on in NET_ExplainErrorDetails in mkmessag.c */
00102 nsresult nsExplainErrorDetails(nsISmtpUrl * aSmtpUrl, int code, ...)
00103 {
00104   NS_ENSURE_ARG(aSmtpUrl);
00105 
00106   nsresult rv = NS_OK;
00107   va_list args;
00108 
00109   nsCOMPtr<nsIPrompt> dialog;
00110   aSmtpUrl->GetPrompt(getter_AddRefs(dialog));
00111   NS_ENSURE_TRUE(dialog, NS_ERROR_FAILURE);
00112 
00113   PRUnichar *  msg;
00114   nsXPIDLString eMsg;
00115   nsCOMPtr<nsIMsgStringService> smtpBundle = do_GetService(NS_MSG_SMTPSTRINGSERVICE_CONTRACTID);
00116   
00117   va_start (args, code);
00118   
00119   switch (code)
00120   {
00121       case NS_ERROR_SMTP_SERVER_ERROR:
00122       case NS_ERROR_TCP_READ_ERROR:
00123       case NS_ERROR_SMTP_TEMP_SIZE_EXCEEDED:
00124       case NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_1:
00125       case NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_2:
00126       case NS_ERROR_SENDING_FROM_COMMAND:
00127       case NS_ERROR_SENDING_RCPT_COMMAND:
00128       case NS_ERROR_SENDING_DATA_COMMAND:
00129       case NS_ERROR_SENDING_MESSAGE:
00130       case NS_ERROR_SMTP_GREETING:
00131            smtpBundle->GetStringByID(code, getter_Copies(eMsg));
00132            msg = nsTextFormatter::vsmprintf(eMsg, args);
00133            break;
00134       default:
00135            smtpBundle->GetStringByID(NS_ERROR_COMMUNICATIONS_ERROR, getter_Copies(eMsg));
00136            msg = nsTextFormatter::smprintf(eMsg, code);
00137            break;
00138   }
00139   
00140   if (msg) 
00141   {
00142     rv = dialog->Alert(nsnull, msg);
00143     nsTextFormatter::smprintf_free(msg);
00144   }
00145   
00146   va_end (args);
00147   
00148   return rv;
00149 }
00150 
00151 /* RFC 1891 -- extended smtp value encoding scheme
00152 
00153   5. Additional parameters for RCPT and MAIL commands
00154 
00155      The extended RCPT and MAIL commands are issued by a client when it wishes to request a DSN from the
00156      server, under certain conditions, for a particular recipient. The extended RCPT and MAIL commands are
00157      identical to the RCPT and MAIL commands defined in [1], except that one or more of the following parameters
00158      appear after the sender or recipient address, respectively. The general syntax for extended SMTP commands is
00159      defined in [4]. 
00160 
00161      NOTE: Although RFC 822 ABNF is used to describe the syntax of these parameters, they are not, in the
00162      language of that document, "structured field bodies". Therefore, while parentheses MAY appear within an
00163      emstp-value, they are not recognized as comment delimiters. 
00164 
00165      The syntax for "esmtp-value" in [4] does not allow SP, "=", control characters, or characters outside the
00166      traditional ASCII range of 1- 127 decimal to be transmitted in an esmtp-value. Because the ENVID and
00167      ORCPT parameters may need to convey values outside this range, the esmtp-values for these parameters are
00168      encoded as "xtext". "xtext" is formally defined as follows: 
00169 
00170      xtext = *( xchar / hexchar ) 
00171 
00172      xchar = any ASCII CHAR between "!" (33) and "~" (126) inclusive, except for "+" and "=". 
00173 
00174        ; "hexchar"s are intended to encode octets that cannot appear
00175        ; as ASCII characters within an esmtp-value.
00176 
00177                hexchar = ASCII "+" immediately followed by two upper case hexadecimal digits 
00178 
00179        When encoding an octet sequence as xtext:
00180 
00181        + Any ASCII CHAR between "!" and "~" inclusive, except for "+" and "=",
00182                MAY be encoded as itself. (A CHAR in this range MAY instead be encoded as a "hexchar", at the
00183                implementor's discretion.) 
00184 
00185        + ASCII CHARs that fall outside the range above must be encoded as
00186                "hexchar". 
00187 
00188  */
00189 /* caller must free the return buffer */
00190 static char *
00191 esmtp_value_encode(char *addr)
00192 {
00193   char *buffer = (char *) PR_Malloc(512); /* esmpt ORCPT allow up to 500 chars encoded addresses */
00194   char *bp = buffer, *bpEnd = buffer+500;
00195   int len, i;
00196   
00197   if (!buffer) return NULL;
00198   
00199   *bp=0;
00200   if (! addr || *addr == 0) /* this will never happen */
00201     return buffer;
00202   
00203   for (i=0, len=PL_strlen(addr); i < len && bp < bpEnd; i++)
00204   {
00205     if (*addr >= 0x21 && 
00206       *addr <= 0x7E &&
00207       *addr != '+' &&
00208       *addr != '=')
00209     {
00210       *bp++ = *addr++;
00211     }
00212     else
00213     {
00214       PR_snprintf(bp, bpEnd-bp, "+%.2X", ((int)*addr++));
00215       bp += PL_strlen(bp);
00216     }
00217   }
00218   *bp=0;
00219   return buffer;
00220 }
00221 
00223 // END OF TEMPORARY HARD CODED FUNCTIONS 
00225 
00226 NS_IMPL_ADDREF_INHERITED(nsSmtpProtocol, nsMsgAsyncWriteProtocol)
00227 NS_IMPL_RELEASE_INHERITED(nsSmtpProtocol, nsMsgAsyncWriteProtocol)
00228 
00229 NS_INTERFACE_MAP_BEGIN(nsSmtpProtocol)
00230     NS_INTERFACE_MAP_ENTRY(nsIMsgLogonRedirectionRequester)
00231 NS_INTERFACE_MAP_END_INHERITING(nsMsgAsyncWriteProtocol)
00232 
00233 nsSmtpProtocol::nsSmtpProtocol(nsIURI * aURL)
00234     : nsMsgAsyncWriteProtocol(aURL)
00235 {
00236     Initialize(aURL);
00237 }
00238 
00239 nsSmtpProtocol::~nsSmtpProtocol()
00240 {
00241   // free our local state
00242   PR_Free(m_addressCopy);
00243   PR_Free(m_verifyAddress);
00244   PR_Free(m_dataBuf);
00245   delete m_lineStreamBuffer;
00246 }
00247 
00248 void nsSmtpProtocol::Initialize(nsIURI * aURL)
00249 {
00250     NS_PRECONDITION(aURL, "invalid URL passed into Smtp Protocol");
00251     nsresult rv = NS_OK;
00252 
00253     m_flags = 0;
00254     m_origAuthFlags = 0;
00255     m_prefAuthMethod = PREF_AUTH_NONE;
00256     m_usernamePrompted = PR_FALSE;
00257     m_prefTrySSL = PREF_SECURE_TRY_STARTTLS;
00258     m_prefTrySecAuth = PR_TRUE;
00259     m_tlsInitiated = PR_FALSE;
00260 
00261     m_urlErrorState = NS_ERROR_FAILURE;
00262 
00263     if (!SMTPLogModule)
00264         SMTPLogModule = PR_NewLogModule("SMTP");
00265     
00266     if (aURL) 
00267         m_runningURL = do_QueryInterface(aURL);
00268 
00269     if (!mSmtpBundle)
00270         mSmtpBundle = do_GetService(NS_MSG_SMTPSTRINGSERVICE_CONTRACTID);
00271     
00272     // extract out message feedback if there is any.
00273     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(aURL);
00274     if (mailnewsUrl)
00275         mailnewsUrl->GetStatusFeedback(getter_AddRefs(m_statusFeedback));
00276 
00277     m_dataBuf = (char *) PR_Malloc(sizeof(char) * OUTPUT_BUFFER_SIZE);
00278     m_dataBufSize = OUTPUT_BUFFER_SIZE;
00279 
00280     m_nextState = SMTP_START_CONNECT;
00281     m_nextStateAfterResponse = SMTP_START_CONNECT;
00282     m_responseCode = 0;
00283     m_previousResponseCode = 0;
00284     m_continuationResponse = -1; 
00285     m_tlsEnabled = PR_FALSE;
00286     m_addressCopy = nsnull;
00287     m_addresses = nsnull;
00288 
00289     m_addressesLeft = nsnull;
00290     m_verifyAddress = nsnull;
00291 #ifdef UNREADY_CODE 
00292     m_totalAmountWritten = 0;
00293 #endif /* UNREADY_CODE */
00294 
00295     m_sendDone = PR_FALSE;
00296 
00297     m_sizelimit = 0;
00298     m_totalMessageSize = 0;
00299     nsCOMPtr<nsIFileSpec> fileSpec;
00300     m_runningURL->GetPostMessageFile(getter_AddRefs(fileSpec));
00301     if (fileSpec)
00302         fileSpec->GetFileSize(&m_totalMessageSize);
00303 
00304     m_originalContentLength = 0;
00305     m_totalAmountRead = 0;
00306 
00307     m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, PR_TRUE);
00308     // ** may want to consider caching the server capability to save lots of
00309     // round trip communication between the client and server
00310     nsCOMPtr<nsISmtpServer> smtpServer;
00311     m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
00312     if (smtpServer) {
00313         smtpServer->GetAuthMethod(&m_prefAuthMethod);
00314         smtpServer->GetTrySSL(&m_prefTrySSL);
00315         smtpServer->GetTrySecAuth(&m_prefTrySecAuth);
00316         smtpServer->GetHelloArgument(getter_Copies(m_helloArgument));
00317     }
00318 
00319     rv = RequestOverrideInfo(smtpServer);
00320     // if we aren't waiting for a login override, then go ahead an
00321     // open the network connection like we normally would have.
00322     if (NS_SUCCEEDED(rv) && TestFlag(SMTP_WAIT_FOR_REDIRECTION))
00323         return;
00324 
00325 #if defined(PR_LOGGING)
00326     nsCAutoString hostName;
00327     aURL->GetAsciiHost(hostName);
00328     PR_LOG(SMTPLogModule, PR_LOG_ALWAYS, ("SMTP Connecting to: %s", hostName.get()));
00329 #endif
00330     
00331     // When we are making a secure connection, we need to make sure that we
00332     // pass an interface requestor down to the socket transport so that PSM can
00333     // retrieve a nsIPrompt instance if needed.
00334     nsCOMPtr<nsIInterfaceRequestor> callbacks;
00335     nsCOMPtr<nsISmtpUrl> smtpUrl(do_QueryInterface(aURL));
00336     if (smtpUrl)
00337         smtpUrl->GetNotificationCallbacks(getter_AddRefs(callbacks));
00338 
00339     if (m_prefTrySSL == PREF_SECURE_ALWAYS_SMTPS)
00340         rv = OpenNetworkSocket(aURL, "ssl", callbacks);  
00341     else if (m_prefTrySSL != PREF_SECURE_NEVER)
00342     {
00343         rv = OpenNetworkSocket(aURL, "starttls", callbacks);  
00344         if (NS_FAILED(rv) && m_prefTrySSL == PREF_SECURE_TRY_STARTTLS)
00345         {
00346             m_prefTrySSL = PREF_SECURE_NEVER;
00347             rv = OpenNetworkSocket(aURL, nsnull, callbacks);
00348         }
00349     }
00350     else
00351         rv = OpenNetworkSocket(aURL, nsnull, callbacks);
00352     
00353     if (NS_FAILED(rv))
00354         return;
00355 }
00356 
00357 void nsSmtpProtocol::AppendHelloArgument(nsACString& aResult)
00358 {
00359   nsresult rv;
00360   
00361   // is a custom EHLO/HELO argument configured for the transport to be used?
00362   if (!m_helloArgument.IsEmpty())
00363   {
00364       aResult += m_helloArgument;
00365   }
00366   else
00367   {
00368       // is a FQDN known for this system?
00369       char hostName[256];
00370       PR_GetSystemInfo(PR_SI_HOSTNAME_UNTRUNCATED, hostName, sizeof hostName);
00371       if ((hostName[0] != '\0') && (strchr(hostName, '.') != NULL))
00372       {
00373           aResult += hostName;
00374       }
00375       else
00376       {
00377           PRNetAddr iaddr; // IP address for this connection
00378           // our transport is always a nsISocketTransport
00379           nsCOMPtr<nsISocketTransport> socketTransport = do_QueryInterface(m_transport); 
00380           // should return the interface ip of the SMTP connection
00381           // minimum case - see bug 68877 and RFC 2821, chapter 4.1.1.1
00382           rv = socketTransport->GetSelfAddr(&iaddr);
00383 
00384           if (NS_SUCCEEDED(rv))
00385           {
00386               // turn it into a string
00387               char ipAddressString[64];
00388               if (PR_NetAddrToString(&iaddr, ipAddressString, sizeof(ipAddressString)) == PR_SUCCESS) 
00389               {
00390                   NS_ASSERTION(PR_IsNetAddrType(&iaddr, PR_IpAddrV4Mapped) == PR_FALSE,
00391                                "unexpected IPv4-mapped IPv6 address");
00392 
00393                   if (iaddr.raw.family == PR_AF_INET6)   // IPv6 style address?
00394                       aResult.AppendLiteral("[IPv6:");
00395                   else
00396                       aResult.AppendLiteral("[");
00397 
00398                   aResult.Append(nsDependentCString(ipAddressString) + NS_LITERAL_CSTRING("]"));
00399               }
00400           }
00401       }
00402   }
00403 }
00404 
00406 // we suppport the nsIStreamListener interface 
00408 
00409 // stop binding is a "notification" informing us that the stream
00410 // associated with aURL is going away. 
00411 NS_IMETHODIMP nsSmtpProtocol::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
00412 nsresult aStatus)
00413 {
00414   if (aStatus == NS_OK && !m_sendDone) {
00415     // if we are getting OnStopRequest() with NS_OK, 
00416     // but we haven't finished clean, that's spells trouble.
00417     // it means that the server has dropped us before we could send the whole mail
00418     // for example, see bug #200647
00419     PR_LOG(SMTPLogModule, PR_LOG_ALWAYS,
00420  ("SMTP connection dropped after %ld total bytes read", m_totalAmountRead));
00421     nsMsgAsyncWriteProtocol::OnStopRequest(nsnull, ctxt, NS_ERROR_NET_INTERRUPT);
00422   }
00423   else
00424     nsMsgAsyncWriteProtocol::OnStopRequest(nsnull, ctxt, aStatus);
00425 
00426   // okay, we've been told that the send is done and the connection is going away. So 
00427   // we need to release all of our state
00428   return nsMsgAsyncWriteProtocol::CloseSocket();
00429 }
00430 
00432 // End of nsIStreamListenerSupport
00434 
00435 void nsSmtpProtocol::UpdateStatus(PRInt32 aStatusID)
00436 {
00437   if (m_statusFeedback)
00438   {
00439     nsXPIDLString msg;
00440     mSmtpBundle->GetStringByID(aStatusID, getter_Copies(msg));
00441     UpdateStatusWithString(msg);
00442   }
00443 }
00444 
00445 void nsSmtpProtocol::UpdateStatusWithString(const PRUnichar * aStatusString)
00446 {
00447   if (m_statusFeedback && aStatusString)
00448     m_statusFeedback->ShowStatusString(aStatusString);
00449 }
00450 
00452 // Begin protocol state machine functions...
00454 
00455 /*
00456  * gets the response code from the SMTP server and the
00457  * response line
00458  *
00459  * returns the TCP return code from the read
00460  */
00461 PRInt32 nsSmtpProtocol::SmtpResponse(nsIInputStream * inputStream, PRUint32 length)
00462 {
00463   char * line = nsnull;
00464   char cont_char;
00465   PRUint32 ln = 0;
00466   PRBool pauseForMoreData = PR_FALSE;
00467   
00468   if (!m_lineStreamBuffer)
00469     return -1; // this will force an error and at least we won't crash
00470   
00471   line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData);
00472   
00473   if (pauseForMoreData || !line)
00474   {
00475     SetFlag(SMTP_PAUSE_FOR_READ); /* pause */
00476     PR_Free(line);
00477     return(ln);
00478   }
00479   
00480   m_totalAmountRead += ln;
00481   
00482   PR_LOG(SMTPLogModule, PR_LOG_ALWAYS, ("SMTP Response: %s", line));
00483   cont_char = ' '; /* default */
00484   // sscanf() doesn't update m_responseCode if line doesn't start
00485   // with a number. That can be dangerous. So be sure to set
00486   // m_responseCode to 0 if no items read.
00487   if (PR_sscanf(line, "%d%c", &m_responseCode, &cont_char) <= 0)
00488     m_responseCode = 0;
00489   
00490   if (m_continuationResponse == -1)
00491   {
00492     if (cont_char == '-')  /* begin continuation */
00493       m_continuationResponse = m_responseCode;
00494 
00495     // display the whole message if no valid response code or
00496     // message shorter than 4 chars
00497     m_responseText = (m_responseCode >= 100 && PL_strlen(line) > 3) ? line + 4 : line;
00498   }
00499   else
00500   { /* have to continue */
00501     if (m_continuationResponse == m_responseCode && cont_char == ' ')
00502       m_continuationResponse = -1;    /* ended */
00503     
00504     if (m_responseText.IsEmpty() || m_responseText.Last() != '\n')
00505       m_responseText += "\n";
00506 
00507     m_responseText += (PL_strlen(line) > 3) ? line + 4 : line;
00508   }
00509   
00510   if (m_responseCode == 220 && m_responseText.Length() && !m_tlsInitiated)
00511   { 
00512     m_nextStateAfterResponse = SMTP_EXTN_LOGIN_RESPONSE;
00513   }
00514   
00515   if (m_continuationResponse == -1)  /* all done with this response? */
00516   {
00517     m_nextState = m_nextStateAfterResponse;
00518     ClearFlag(SMTP_PAUSE_FOR_READ); /* don't pause */
00519   }
00520   
00521   PR_Free(line);
00522   return(0);  /* everything ok */
00523 }
00524 
00525 PRInt32 nsSmtpProtocol::ExtensionLoginResponse(nsIInputStream * inputStream, PRUint32 length) 
00526 {
00527   PRInt32 status = 0;
00528   
00529   if (m_responseCode != 220)
00530   {
00531 #ifdef DEBUG
00532     nsresult rv = 
00533 #endif
00534     nsExplainErrorDetails(m_runningURL, NS_ERROR_SMTP_GREETING,
00535                           m_responseText.get());
00536     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
00537 
00538     m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
00539     return(NS_ERROR_COULD_NOT_LOGIN_TO_SMTP_SERVER);
00540   }
00541   
00542   nsCAutoString buffer("EHLO ");
00543   AppendHelloArgument(buffer);
00544   buffer += CRLF;
00545   
00546   nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
00547 
00548   status = SendData(url, buffer.get());
00549   
00550   m_nextState = SMTP_RESPONSE;
00551   m_nextStateAfterResponse = SMTP_SEND_EHLO_RESPONSE;
00552   SetFlag(SMTP_PAUSE_FOR_READ);
00553   
00554   return(status);
00555 }
00556 
00557 PRInt32 nsSmtpProtocol::SendHeloResponse(nsIInputStream * inputStream, PRUint32 length)
00558 {
00559   PRInt32 status = 0;
00560   nsCAutoString buffer;
00561   nsresult rv;
00562   
00563   if (m_responseCode != 250)
00564   {
00565 #ifdef DEBUG
00566     nsresult rv =
00567 #endif
00568     nsExplainErrorDetails(m_runningURL, NS_ERROR_SMTP_SERVER_ERROR,
00569                           m_responseText.get());
00570     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
00571 
00572     m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
00573     return(NS_ERROR_COULD_NOT_LOGIN_TO_SMTP_SERVER);
00574   }
00575 
00576   // extract the email address from the identity
00577   nsXPIDLCString emailAddress;
00578   nsCOMPtr <nsIMsgIdentity> senderIdentity;
00579   rv = m_runningURL->GetSenderIdentity(getter_AddRefs(senderIdentity));
00580   if (NS_FAILED(rv) || !senderIdentity)
00581   {
00582     m_urlErrorState = NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS;
00583     return(NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS);
00584   }
00585   else 
00586   {
00587     senderIdentity->GetEmail(getter_Copies(emailAddress));
00588   }
00589   
00590   if(!((const char *)emailAddress) || CHECK_SIMULATED_ERROR(SIMULATED_SEND_ERROR_16))
00591   {
00592     m_urlErrorState = NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS;
00593     return(NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS);
00594   }
00595   
00596   if(m_verifyAddress)
00597   {
00598     buffer += "VRFY";
00599     buffer += m_verifyAddress;
00600     buffer += CRLF;
00601   }
00602   else
00603   {
00604     /* else send the MAIL FROM: command */
00605     nsCOMPtr<nsIMsgHeaderParser> parser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
00606     char *fullAddress = nsnull;
00607     if (parser) 
00608     {
00609       // pass nsnull for the name, since we just want the email.
00610       //
00611       // seems a little weird that we are passing in the emailAddress
00612       // when that's the out parameter 
00613       parser->MakeFullAddress(nsnull, nsnull /* name */, emailAddress /* address */, &fullAddress);
00614     }
00615 #ifdef UNREADY_CODE         
00616     if (CE_URL_S->msg_pane) 
00617     {
00618       if (MSG_RequestForReturnReceipt(CE_URL_S->msg_pane)) 
00619       {
00620         if (TestFlag(SMTP_EHLO_DSN_ENABLED)) 
00621         {
00622           PR_snprintf(buffer, sizeof(buffer), 
00623             "MAIL FROM:<%.256s> RET=FULL ENVID=NS40112696JT" CRLF, fullAddress);
00624         }
00625         else 
00626         {
00627           FE_Alert (CE_WINDOW_ID, XP_GetString(XP_RETURN_RECEIPT_NOT_SUPPORT));
00628           PR_snprintf(buffer, sizeof(buffer), "MAIL FROM:<%.256s>" CRLF, fullAddress);
00629         }
00630       }
00631       else if (MSG_SendingMDNInProgress(CE_URL_S->msg_pane)) 
00632       {
00633         PR_snprintf(buffer, sizeof(buffer), "MAIL FROM:<%.256s>" CRLF, "");
00634       }
00635       else 
00636       {
00637         PR_snprintf(buffer, sizeof(buffer), "MAIL FROM:<%.256s>" CRLF, fullAddress);
00638       }
00639     }
00640     else 
00641 #endif
00642     {
00643       buffer = "MAIL FROM:<";
00644       buffer += fullAddress;
00645       buffer += ">";
00646       if(TestFlag(SMTP_EHLO_SIZE_ENABLED))
00647         buffer += nsPrintfCString(" SIZE=%d", m_totalMessageSize);
00648       buffer += CRLF;
00649     }
00650     PR_Free (fullAddress);
00651   }
00652   
00653   nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
00654   status = SendData(url, buffer.get());
00655   
00656   m_nextState = SMTP_RESPONSE;
00657   
00658   if(m_verifyAddress)
00659     m_nextStateAfterResponse = SMTP_SEND_VRFY_RESPONSE;
00660   else
00661     m_nextStateAfterResponse = SMTP_SEND_MAIL_RESPONSE;
00662   SetFlag(SMTP_PAUSE_FOR_READ);
00663   
00664   return(status);
00665 }
00666 
00667 
00668 PRInt32 nsSmtpProtocol::SendEhloResponse(nsIInputStream * inputStream, PRUint32 length)
00669 {
00670     PRInt32 status = 0;
00671     nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
00672 
00673     if (m_responseCode != 250)
00674     {
00675         /* EHLO must not be implemented by the server, so fall back to the HELO case
00676          * if command is unrecognized or unimplemented.
00677          */
00678         if (m_responseCode == 500 || m_responseCode == 502)
00679         {
00680             /* STARTTLS is only available when advertised which requires EHLO */
00681             if (m_prefTrySSL == PREF_SECURE_ALWAYS_STARTTLS)
00682             {
00683                 m_nextState = SMTP_ERROR_DONE;
00684                 m_urlErrorState = NS_ERROR_COULD_NOT_LOGIN_TO_SMTP_SERVER_WITH_STARTTLS1;
00685                 return(NS_ERROR_COULD_NOT_LOGIN_TO_SMTP_SERVER);
00686             }
00687 
00688             nsCAutoString buffer("HELO ");
00689             AppendHelloArgument(buffer);
00690             buffer += CRLF;
00691 
00692             status = SendData(url, buffer.get());
00693 
00694             m_nextState = SMTP_RESPONSE;
00695             m_nextStateAfterResponse = SMTP_SEND_HELO_RESPONSE;
00696             SetFlag(SMTP_PAUSE_FOR_READ);
00697             return (status);
00698         }
00699         /* e.g. getting 421 "Server says unauthorized, bye" or
00700          * 501 "Syntax error in EHLOs parameters or arguments"
00701          */
00702         else
00703         {
00704 #ifdef DEBUG
00705             nsresult rv = 
00706 #endif
00707             nsExplainErrorDetails(m_runningURL, NS_ERROR_SMTP_SERVER_ERROR,
00708                                   m_responseText.get());
00709             NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
00710 
00711             m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
00712             return(NS_ERROR_COULD_NOT_LOGIN_TO_SMTP_SERVER);
00713         }
00714     }
00715 
00716     PRInt32 responseLength = m_responseText.Length();
00717     PRInt32 startPos = 0;
00718     PRInt32 endPos;
00719     do
00720     {
00721         endPos = m_responseText.FindChar('\n', startPos + 1);
00722         nsCAutoString responseLine;
00723         responseLine.Assign(Substring(m_responseText, startPos, 
00724             (endPos >= 0 ? endPos : responseLength) - startPos));
00725         responseLine.CompressWhitespace();
00726 
00727         if (responseLine.Compare("STARTTLS", PR_TRUE) == 0)
00728         {
00729             SetFlag(SMTP_EHLO_STARTTLS_ENABLED);
00730         }
00731         else if (responseLine.Compare("DSN", PR_TRUE) == 0)
00732         {
00733             SetFlag(SMTP_EHLO_DSN_ENABLED);
00734         }
00735         else if (responseLine.Compare("AUTH", PR_TRUE, 4) == 0)
00736         {
00737             if (responseLine.Find("PLAIN", PR_TRUE, 5) >= 0)  
00738                 SetFlag(SMTP_AUTH_PLAIN_ENABLED);
00739 
00740             if (responseLine.Find("LOGIN", PR_TRUE, 5) >= 0)  
00741                 SetFlag(SMTP_AUTH_LOGIN_ENABLED);
00742 
00743             if (responseLine.Find("EXTERNAL", PR_TRUE, 5) >= 0)  
00744                 SetFlag(SMTP_AUTH_EXTERNAL_ENABLED);
00745 
00746             if(m_prefTrySecAuth)
00747             {
00748                 if (responseLine.Find("GSSAPI", PR_TRUE, 5) >= 0)
00749                     SetFlag(SMTP_AUTH_GSSAPI_ENABLED);
00750 
00751                 nsresult rv;
00752                 nsCOMPtr<nsISignatureVerifier> verifier = do_GetService(SIGNATURE_VERIFIER_CONTRACTID, &rv);
00753                 // this checks if psm is installed...
00754                 if (NS_SUCCEEDED(rv))
00755                 {
00756                     if (responseLine.Find("CRAM-MD5", PR_TRUE, 5) >= 0)
00757                       SetFlag(SMTP_AUTH_CRAM_MD5_ENABLED);
00758 
00759                     if (responseLine.Find("NTLM", PR_TRUE, 5) >= 0)  
00760                         SetFlag(SMTP_AUTH_NTLM_ENABLED);
00761 
00762                     if (responseLine.Find("MSN", PR_TRUE, 5) >= 0)
00763                         SetFlag(SMTP_AUTH_MSN_ENABLED);
00764                 }
00765             }
00766 
00767             // for use after mechs disabled fallbacks when login failed
00768             BackupAuthFlags();
00769         }
00770         else if (responseLine.Compare("SIZE", PR_TRUE, 4) == 0)
00771         {
00772             SetFlag(SMTP_EHLO_SIZE_ENABLED);
00773 
00774             m_sizelimit = atol((responseLine.get()) + 4);
00775         }
00776 
00777         startPos = endPos + 1;
00778     } while (endPos >= 0);
00779 
00780     if(TestFlag(SMTP_EHLO_SIZE_ENABLED) &&
00781        m_sizelimit > 0 && (PRInt32)m_totalMessageSize > m_sizelimit)
00782     {
00783 #ifdef DEBUG
00784         nsresult rv = 
00785 #endif
00786         nsExplainErrorDetails(m_runningURL,
00787                       NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_1, m_sizelimit);
00788         NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
00789 
00790         m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
00791         return(NS_ERROR_SENDING_FROM_COMMAND);
00792     }
00793 
00794     m_nextState = SMTP_AUTH_PROCESS_STATE;
00795     return status;
00796 }
00797 
00798 
00799 PRInt32 nsSmtpProtocol::SendTLSResponse()
00800 {
00801   // only tear down our existing connection and open a new one if we received a 220 response
00802   // from the smtp server after we issued the STARTTLS 
00803   nsresult rv = NS_OK;
00804   if (m_responseCode == 220) 
00805   {
00806       nsCOMPtr<nsISupports> secInfo;
00807       nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport, &rv);
00808       if (NS_FAILED(rv)) return rv;
00809 
00810       rv = strans->GetSecurityInfo(getter_AddRefs(secInfo));
00811 
00812       if (NS_SUCCEEDED(rv) && secInfo) {
00813           nsCOMPtr<nsISSLSocketControl> sslControl = do_QueryInterface(secInfo, &rv);
00814 
00815           if (NS_SUCCEEDED(rv) && sslControl)
00816               rv = sslControl->StartTLS();
00817       }
00818 
00819     if (NS_SUCCEEDED(rv))
00820     {
00821       m_nextState = SMTP_EXTN_LOGIN_RESPONSE;
00822       m_nextStateAfterResponse = SMTP_EXTN_LOGIN_RESPONSE;
00823       m_tlsEnabled = PR_TRUE;
00824       m_flags = 0; // resetting the flags
00825       BackupAuthFlags();
00826       return rv;
00827     }
00828   }
00829 
00830   ClearFlag(SMTP_EHLO_STARTTLS_ENABLED);
00831   m_tlsInitiated = PR_FALSE;
00832   m_nextState = SMTP_AUTH_PROCESS_STATE;
00833 
00834   return rv;
00835 }
00836 
00837 PRInt32 nsSmtpProtocol::ProcessAuth()
00838 {
00839     PRInt32 status = 0;
00840     nsCAutoString buffer;
00841     nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
00842 
00843     if (!m_tlsEnabled)
00844     {
00845         if(TestFlag(SMTP_EHLO_STARTTLS_ENABLED))
00846         {
00847             // Do not try to combine SMTPS with STARTTLS.
00848             // If PREF_SECURE_ALWAYS_SMTPS is set,
00849             // we are alrady using a secure connection.
00850             // Do not attempt to do STARTTLS,
00851             // even if server offers it.
00852             if (m_prefTrySSL == PREF_SECURE_TRY_STARTTLS ||
00853                 m_prefTrySSL == PREF_SECURE_ALWAYS_STARTTLS)
00854             {
00855                 buffer = "STARTTLS";
00856                 buffer += CRLF;
00857 
00858                 status = SendData(url, buffer.get());
00859 
00860                 m_tlsInitiated = PR_TRUE;
00861 
00862                 m_nextState = SMTP_RESPONSE;
00863                 m_nextStateAfterResponse = SMTP_TLS_RESPONSE;
00864                 SetFlag(SMTP_PAUSE_FOR_READ);
00865                 return status;
00866             }
00867         }
00868         else if (m_prefTrySSL == PREF_SECURE_ALWAYS_STARTTLS)
00869         {
00870             m_nextState = SMTP_ERROR_DONE;
00871             m_urlErrorState = NS_ERROR_COULD_NOT_LOGIN_TO_SMTP_SERVER_WITH_STARTTLS2;
00872             return(NS_ERROR_COULD_NOT_LOGIN_TO_SMTP_SERVER);
00873         }
00874     }
00875 
00876     if (TestFlag(SMTP_AUTH_EXTERNAL_ENABLED))
00877     {
00878         buffer = "AUTH EXTERNAL =";
00879         buffer += CRLF;
00880         SendData(url, buffer.get());
00881         m_nextState = SMTP_RESPONSE;
00882         m_nextStateAfterResponse = SMTP_AUTH_EXTERNAL_RESPONSE;
00883         SetFlag(SMTP_PAUSE_FOR_READ);
00884         return NS_OK;
00885     }
00886     else
00887     if (m_prefAuthMethod == PREF_AUTH_ANY)
00888     {
00889         if (TestFlag(SMTP_AUTH_GSSAPI_ENABLED)) 
00890             m_nextState = SMTP_SEND_AUTH_GSSAPI_FIRST;
00891         else if (TestFlag(SMTP_AUTH_CRAM_MD5_ENABLED) ||
00892             TestFlag(SMTP_AUTH_NTLM_ENABLED) ||
00893             TestFlag(SMTP_AUTH_PLAIN_ENABLED))
00894             m_nextState = SMTP_SEND_AUTH_LOGIN_STEP1;
00895         else if (TestFlag(SMTP_AUTH_LOGIN_ENABLED) ||
00896             TestFlag(SMTP_AUTH_MSN_ENABLED))
00897             m_nextState = SMTP_SEND_AUTH_LOGIN_STEP0;
00898         /* potential security flaw when using DIGEST_MD5 (and maybe GSSAPI)
00899          * where not only the client is authenticated by the server
00900          * but also vice versa. Faked server could just not advertise
00901          * any mechanism to bypass authentication process.
00902          */
00903         else
00904         {
00905             m_nextState = SMTP_SEND_HELO_RESPONSE;
00906             // fake to 250 because SendHeloResponse() tests for this
00907             m_responseCode = 250;
00908         }
00909     }
00910     else
00911     {
00912         m_nextState = SMTP_SEND_HELO_RESPONSE;
00913         // fake to 250 because SendHeloResponse() tests for this
00914         m_responseCode = 250;
00915     }
00916 
00917     return NS_OK;
00918 }
00919 
00920 
00921 void nsSmtpProtocol::BackupAuthFlags()
00922 {
00923   m_origAuthFlags = m_flags & SMTP_AUTH_ANY_ENABLED;
00924 }
00925 
00926 void nsSmtpProtocol::RestoreAuthFlags()
00927 {
00928   m_flags |= m_origAuthFlags;
00929 }
00930 
00931 
00932 PRInt32 nsSmtpProtocol::AuthLoginResponse(nsIInputStream * stream, PRUint32 length)
00933 {
00934   PRInt32 status = 0;
00935   nsCOMPtr<nsISmtpServer> smtpServer;
00936   m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
00937 
00938   switch (m_responseCode/100) 
00939   {
00940     case 2:
00941       m_nextState = SMTP_SEND_HELO_RESPONSE;
00942       // fake to 250 because SendHeloResponse() tests for this
00943       m_responseCode = 250;
00944       break;
00945     case 3:
00946       m_nextState = SMTP_SEND_AUTH_LOGIN_STEP2;
00947       break;
00948     case 5:
00949     default:
00950       if (smtpServer)
00951       {
00952         // If one authentication failed, we're going to
00953         // fall back on a less secure login method.
00954         if(TestFlag(SMTP_AUTH_GSSAPI_ENABLED))
00955           ClearFlag(SMTP_AUTH_GSSAPI_ENABLED);
00956         else if(TestFlag(SMTP_AUTH_DIGEST_MD5_ENABLED))
00957           // if DIGEST-MD5 enabled, clear it if we failed.
00958           ClearFlag(SMTP_AUTH_DIGEST_MD5_ENABLED);
00959         else if(TestFlag(SMTP_AUTH_CRAM_MD5_ENABLED))
00960           // if CRAM-MD5 enabled, clear it if we failed. 
00961           ClearFlag(SMTP_AUTH_CRAM_MD5_ENABLED);
00962         else if(TestFlag(SMTP_AUTH_NTLM_ENABLED))
00963           // if NTLM enabled, clear it if we failed. 
00964           ClearFlag(SMTP_AUTH_NTLM_ENABLED);
00965         else if(TestFlag(SMTP_AUTH_MSN_ENABLED))
00966           // if MSN enabled, clear it if we failed. 
00967           ClearFlag(SMTP_AUTH_MSN_ENABLED);
00968         else if(TestFlag(SMTP_AUTH_PLAIN_ENABLED))
00969           // if PLAIN enabled, clear it if we failed. 
00970           ClearFlag(SMTP_AUTH_PLAIN_ENABLED);
00971         else if(TestFlag(SMTP_AUTH_LOGIN_ENABLED))
00972           // if LOGIN enabled, clear it if we failed. 
00973           ClearFlag(SMTP_AUTH_LOGIN_ENABLED);
00974 
00975         // Only forget the password if we didn't get here from the redirection
00976         // and if we've no mechanism left.
00977         if (!TestFlag(SMTP_AUTH_ANY_ENABLED) && mLogonCookie.IsEmpty())
00978         {
00979             smtpServer->ForgetPassword();
00980             if (m_usernamePrompted)
00981                 smtpServer->SetUsername("");
00982 
00983             // Let's restore the original auth flags from SendEhloResponse so we can
00984             // try them again with new password and username
00985             RestoreAuthFlags();
00986             // except for gssapi, which doesn't care about the new password.
00987             ClearFlag(SMTP_AUTH_GSSAPI_ENABLED);
00988         }
00989 
00990         m_nextState = SMTP_AUTH_PROCESS_STATE;
00991       }
00992       else
00993           status = NS_ERROR_SMTP_PASSWORD_UNDEFINED;
00994       break;
00995   }
00996   
00997   return (status);
00998 }
00999 
01000 // GSSAPI may consist of multiple round trips
01001 
01002 PRInt32 nsSmtpProtocol::AuthGSSAPIFirst()
01003 {
01004   nsCAutoString command("AUTH GSSAPI ");
01005   nsCAutoString resp;
01006   nsCAutoString service("smtp@");
01007   nsXPIDLCString hostName;
01008   nsXPIDLCString userName;
01009   nsresult rv;
01010   nsCOMPtr<nsISmtpServer> smtpServer;
01011   rv = m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
01012   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01013 
01014   rv = smtpServer->GetUsername(getter_Copies(userName));
01015   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01016   rv = smtpServer->GetHostname(getter_Copies(hostName));
01017   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01018 
01019  service.Append(hostName);
01020   rv = DoGSSAPIStep1(service.get(), userName, resp);
01021   if (NS_FAILED(rv))
01022   {
01023     m_nextState = SMTP_AUTH_PROCESS_STATE;
01024     ClearFlag(SMTP_AUTH_GSSAPI_ENABLED);
01025     return 0;
01026   }
01027   else
01028     command.Append(resp);
01029   command.Append(CRLF);
01030   m_nextState = SMTP_RESPONSE;
01031   m_nextStateAfterResponse = SMTP_SEND_AUTH_GSSAPI_STEP;
01032   SetFlag(SMTP_PAUSE_FOR_READ);
01033   nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
01034   return SendData(url, command.get());
01035 }
01036 
01037 PRInt32 nsSmtpProtocol::AuthGSSAPIStep()
01038 {
01039   nsresult rv;
01040   nsCAutoString cmd;
01041 
01042   // Check to see what the server said
01043   if (m_responseCode / 100 != 3) {
01044     m_nextState = SMTP_AUTH_LOGIN_RESPONSE;
01045     return 0;
01046   }
01047 
01048   rv = DoGSSAPIStep2(m_responseText, cmd);
01049   if (NS_FAILED(rv))
01050     cmd = "*";
01051   cmd += CRLF;
01052 
01053   m_nextStateAfterResponse = (rv == NS_SUCCESS_AUTH_FINISHED)?SMTP_AUTH_LOGIN_RESPONSE:SMTP_SEND_AUTH_GSSAPI_STEP;
01054   m_nextState = SMTP_RESPONSE;
01055   SetFlag(SMTP_PAUSE_FOR_READ);
01056 
01057   nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
01058   return SendData(url, cmd.get());
01059 }
01060 
01061        
01062 // LOGIN and MSN consist of three steps (MSN not through the mechanism
01063 // but by non-RFC2821 compliant implementation in M$ servers) not two as
01064 // PLAIN or CRAM-MD5, so we've to start here and continue with AuthStep1
01065 // if the server responds with with a 3xx code to "AUTH LOGIN" or "AUTH MSN"
01066 PRInt32 nsSmtpProtocol::AuthLoginStep0()
01067 {
01068     nsCAutoString command(TestFlag(SMTP_AUTH_MSN_ENABLED) ? "AUTH MSN" CRLF :
01069                                                             "AUTH LOGIN" CRLF);
01070     m_nextState = SMTP_RESPONSE;
01071     m_nextStateAfterResponse = SMTP_AUTH_LOGIN_STEP0_RESPONSE;
01072     SetFlag(SMTP_PAUSE_FOR_READ);
01073 
01074     return SendData(m_url, command.get());
01075 }
01076 
01077 PRInt32 nsSmtpProtocol::AuthLoginStep0Response()
01078 {
01079     // need the test to be here instead in AuthLoginResponse() to
01080     // continue with step 1 instead of 2 in case of a code 3xx
01081     m_nextState = (m_responseCode/100 == 3) ?
01082                   SMTP_SEND_AUTH_LOGIN_STEP1 : SMTP_AUTH_LOGIN_RESPONSE;
01083 
01084     return 0;
01085 }
01086 
01087 PRInt32 nsSmtpProtocol::AuthLoginStep1()
01088 {
01089   char buffer[512];
01090   nsresult rv;
01091   PRInt32 status = 0;
01092   nsXPIDLCString username;
01093   char *base64Str = nsnull;
01094   nsXPIDLCString origPassword;
01095   nsCAutoString password;
01096   nsCOMPtr<nsISmtpServer> smtpServer;
01097   rv = m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
01098   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01099   
01100   rv = smtpServer->GetUsername(getter_Copies(username));
01101   
01102   if (username.IsEmpty()) {
01103     rv = GetUsernamePassword(getter_Copies(username), getter_Copies(origPassword));
01104     m_usernamePrompted = PR_TRUE;
01105     password.Assign(origPassword);
01106     if (username.IsEmpty() || password.IsEmpty())
01107       return NS_ERROR_SMTP_PASSWORD_UNDEFINED;
01108   }
01109   else if (!TestFlag(SMTP_USE_LOGIN_REDIRECTION))
01110   {
01111     rv = GetPassword(getter_Copies(origPassword));
01112     password.Assign(origPassword);
01113     if (password.IsEmpty())
01114       return NS_ERROR_SMTP_PASSWORD_UNDEFINED;
01115   }
01116   else
01117     password.Assign(mLogonCookie);
01118   
01119   if (TestFlag(SMTP_AUTH_CRAM_MD5_ENABLED))
01120     PR_snprintf(buffer, sizeof(buffer), "AUTH CRAM-MD5" CRLF);
01121   else
01122   if (TestFlag(SMTP_AUTH_NTLM_ENABLED) || TestFlag(SMTP_AUTH_MSN_ENABLED))
01123   {
01124     nsCAutoString response;
01125     rv = DoNtlmStep1(username.get(), password.get(), response);
01126     PR_snprintf(buffer, sizeof(buffer), TestFlag(SMTP_AUTH_NTLM_ENABLED) ?
01127                                         "AUTH NTLM %.256s" CRLF :
01128                                         "%.256s" CRLF, response.get());
01129   }
01130   else
01131   if (TestFlag(SMTP_AUTH_PLAIN_ENABLED))
01132   {
01133     char plain_string[512];
01134     int len = 1; /* first <NUL> char */
01135     
01136     memset(plain_string, 0, 512);
01137     PR_snprintf(&plain_string[1], 510, "%s", (const char*)username);
01138     len += PL_strlen(username);
01139     len++; /* second <NUL> char */
01140     PR_snprintf(&plain_string[len], 511-len, "%s", password.get());
01141     len += password.Length();
01142     
01143     base64Str = PL_Base64Encode(plain_string, len, nsnull);
01144     PR_snprintf(buffer, sizeof(buffer), "AUTH PLAIN %.256s" CRLF, base64Str);
01145   }
01146   else
01147   if (TestFlag(SMTP_AUTH_LOGIN_ENABLED))
01148   {
01149     base64Str = PL_Base64Encode((const char *)username, 
01150         strlen((const char*)username), nsnull);
01151     PR_snprintf(buffer, sizeof(buffer), "%.256s" CRLF, base64Str);
01152   } 
01153   else
01154     return (NS_ERROR_COMMUNICATIONS_ERROR);
01155     
01156   nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
01157   status = SendData(url, buffer, PR_TRUE);
01158   m_nextState = SMTP_RESPONSE;
01159   m_nextStateAfterResponse = SMTP_AUTH_LOGIN_RESPONSE;
01160   SetFlag(SMTP_PAUSE_FOR_READ);
01161   nsCRT::free(base64Str);
01162     
01163   return (status);
01164 }
01165 
01166 PRInt32 nsSmtpProtocol::AuthLoginStep2()
01167 {
01168   
01169   /* use cached smtp password first
01170   * if not then use cached pop password
01171   * if pop password undefined 
01172   * sync with smtp password
01173   */
01174   PRInt32 status = 0;
01175   nsresult rv;
01176   nsXPIDLCString origPassword;
01177   nsCAutoString password;
01178   
01179   if (!TestFlag(SMTP_USE_LOGIN_REDIRECTION))
01180   {
01181     rv = GetPassword(getter_Copies(origPassword));
01182     PRInt32 passwordLength = strlen((const char *) origPassword);
01183     if (!(const char*) origPassword || passwordLength == 0)
01184       return NS_ERROR_SMTP_PASSWORD_UNDEFINED;
01185     password.Assign((const char*) origPassword);
01186   }
01187   else
01188     password.Assign(mLogonCookie);
01189   
01190   if (!password.IsEmpty()) 
01191   {
01192     char buffer[512];
01193     if (TestFlag(SMTP_AUTH_CRAM_MD5_ENABLED))
01194     {
01195       unsigned char digest[DIGEST_LENGTH];
01196       char * decodedChallenge = PL_Base64Decode(m_responseText.get(), 
01197         m_responseText.Length(), nsnull);
01198       
01199       if (decodedChallenge)
01200         rv = MSGCramMD5(decodedChallenge, strlen(decodedChallenge), password.get(), password.Length(), digest);
01201       else
01202         rv = NS_ERROR_FAILURE;
01203       
01204       PR_Free(decodedChallenge);
01205       if (NS_SUCCEEDED(rv) && digest)
01206       {
01207         nsCAutoString encodedDigest;
01208         char hexVal[8];
01209         
01210         for (PRUint32 j=0; j<16; j++) 
01211         {
01212           PR_snprintf (hexVal,8, "%.2x", 0x0ff & (unsigned short)digest[j]);
01213           encodedDigest.Append(hexVal); 
01214         }
01215         
01216         nsCOMPtr<nsISmtpServer> smtpServer;
01217         rv = m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
01218         if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01219         
01220         nsXPIDLCString userName;
01221         rv = smtpServer->GetUsername(getter_Copies(userName));
01222         
01223         PR_snprintf(buffer, sizeof(buffer), "%s %s", userName.get(), encodedDigest.get());
01224         char *base64Str = PL_Base64Encode(buffer, strlen(buffer), nsnull);
01225         PR_snprintf(buffer, sizeof(buffer), "%s" CRLF, base64Str);
01226         PR_Free(base64Str);
01227       }
01228       if (NS_FAILED(rv))
01229         PR_snprintf(buffer, sizeof(buffer), "*" CRLF);
01230     }
01231     else
01232     if (TestFlag(SMTP_AUTH_NTLM_ENABLED) || TestFlag(SMTP_AUTH_MSN_ENABLED))
01233     {
01234       nsCAutoString response;
01235       rv = DoNtlmStep2(m_responseText, response);
01236       PR_snprintf(buffer, sizeof(buffer), "%.256s" CRLF, response.get());
01237     }
01238     else
01239     {
01240       char *base64Str = PL_Base64Encode(password.get(), password.Length(), nsnull);
01241       PR_snprintf(buffer, sizeof(buffer), "%.256s" CRLF, base64Str);
01242       nsCRT::free(base64Str);
01243     }
01244 
01245     nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
01246     status = SendData(url, buffer, PR_TRUE);
01247     m_nextState = SMTP_RESPONSE;
01248     m_nextStateAfterResponse = SMTP_AUTH_LOGIN_RESPONSE;
01249     SetFlag(SMTP_PAUSE_FOR_READ);   
01250     return (status);
01251   }
01252   
01253   return -1;
01254 }
01255 
01256 PRInt32 nsSmtpProtocol::SendVerifyResponse()
01257 {
01258 #if 0
01259   PRInt32 status = 0;
01260   char buffer[512];
01261   
01262   if(m_responseCode == 250 || m_responseCode == 251)
01263     return(NS_USER_VERIFIED_BY_SMTP);
01264   else
01265     return(NS_USER_NOT_VERIFIED_BY_SMTP);
01266 #else  
01267   PR_ASSERT(0);
01268   return(-1);
01269 #endif
01270 }
01271 
01272 PRInt32 nsSmtpProtocol::SendMailResponse()
01273 {
01274   PRInt32 status = 0;
01275   nsCAutoString buffer;
01276   nsresult rv;
01277 
01278   if(m_responseCode != 250 || CHECK_SIMULATED_ERROR(SIMULATED_SEND_ERROR_11))
01279   {
01280     rv = nsExplainErrorDetails(m_runningURL,
01281      (m_responseCode == 452) ? NS_ERROR_SMTP_TEMP_SIZE_EXCEEDED :
01282     ((m_responseCode == 552) ? NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_2 :
01283                                NS_ERROR_SENDING_FROM_COMMAND),
01284                                m_responseText.get());
01285 
01286     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
01287     
01288     m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
01289     return(NS_ERROR_SENDING_FROM_COMMAND);
01290   }
01291 
01292   /* Send the RCPT TO: command */
01293 #ifdef UNREADY_CODE
01294   if (TestFlag(SMTP_EHLO_DSN_ENABLED) &&
01295     (CE_URL_S->msg_pane && 
01296     MSG_RequestForReturnReceipt(CE_URL_S->msg_pane)))
01297 #else
01298     if (TestFlag(SMTP_EHLO_DSN_ENABLED) && PR_FALSE)
01299 #endif
01300     {
01301       char *encodedAddress = esmtp_value_encode(m_addresses);
01302       
01303       if (encodedAddress) 
01304       {
01305         buffer = "RCPT TO:<";
01306         buffer += m_addresses;
01307         buffer += "> NOTIFY=SUCCESS,FAILURE ORCPT=rfc822;";
01308         buffer += encodedAddress;
01309         buffer += CRLF; 
01310         PR_FREEIF(encodedAddress);
01311       }
01312       else 
01313       {
01314         m_urlErrorState = NS_ERROR_OUT_OF_MEMORY;
01315         return (NS_ERROR_OUT_OF_MEMORY);
01316       }
01317     }
01318     else
01319     {
01320       buffer = "RCPT TO:<";
01321       buffer += m_addresses;
01322       buffer += ">";
01323       buffer += CRLF;
01324     }
01325     /* take the address we sent off the list (move the pointer to just
01326           past the terminating null.) */
01327     m_addresses += PL_strlen (m_addresses) + 1;
01328     m_addressesLeft--;
01329     nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
01330     status = SendData(url, buffer.get());
01331     
01332     m_nextState = SMTP_RESPONSE;
01333     m_nextStateAfterResponse = SMTP_SEND_RCPT_RESPONSE;
01334     SetFlag(SMTP_PAUSE_FOR_READ);
01335     
01336     return(status);
01337 }
01338 
01339 PRInt32 nsSmtpProtocol::SendRecipientResponse()
01340 {
01341   PRInt32 status = 0;
01342   nsCAutoString buffer;
01343   nsresult rv;
01344 
01345   if(m_responseCode != 250 && m_responseCode != 251)
01346   {
01347     rv = nsExplainErrorDetails(m_runningURL,
01348      (m_responseCode == 452) ? NS_ERROR_SMTP_TEMP_SIZE_EXCEEDED :
01349     ((m_responseCode == 552) ? NS_ERROR_SMTP_PERM_SIZE_EXCEEDED_2 :
01350                                NS_ERROR_SENDING_RCPT_COMMAND),
01351                                m_responseText.get());
01352 
01353     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
01354     
01355     m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
01356     return(NS_ERROR_SENDING_RCPT_COMMAND);
01357   }
01358   
01359   if(m_addressesLeft > 0)
01360   {
01361     /* more senders to RCPT to 
01362     */
01363     // fake to 250 because SendMailResponse() can't handle 251
01364     m_responseCode = 250;
01365     m_nextState = SMTP_SEND_MAIL_RESPONSE; 
01366     return(0);
01367   }
01368   
01369   /* else send the DATA command */
01370   buffer = "DATA";
01371   buffer += CRLF;
01372   nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);  
01373   status = SendData(url, buffer.get());
01374   
01375   m_nextState = SMTP_RESPONSE;  
01376   m_nextStateAfterResponse = SMTP_SEND_DATA_RESPONSE; 
01377   SetFlag(SMTP_PAUSE_FOR_READ);   
01378   
01379   return(status);  
01380 }
01381 
01382 
01383 PRInt32 nsSmtpProtocol::SendData(nsIURI *url, const char *dataBuffer, PRBool aSuppressLogging)
01384 {
01385   if (!dataBuffer) return -1;
01386 
01387   if (!aSuppressLogging) {
01388       PR_LOG(SMTPLogModule, PR_LOG_ALWAYS, ("SMTP Send: %s", dataBuffer));
01389   } else {
01390       PR_LOG(SMTPLogModule, PR_LOG_ALWAYS, ("Logging suppressed for this command (it probably contained authentication information)"));
01391   }
01392   return nsMsgAsyncWriteProtocol::SendData(url, dataBuffer);
01393 }
01394 
01395 
01396 PRInt32 nsSmtpProtocol::SendDataResponse()
01397 {
01398   PRInt32 status = 0;
01399   char * command=0;   
01400 
01401   if((m_responseCode != 354) && (m_responseCode != 250)) {
01402     nsresult rv = nsExplainErrorDetails(m_runningURL, NS_ERROR_SENDING_DATA_COMMAND, m_responseText.get());
01403     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
01404 
01405     m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
01406     return(NS_ERROR_SENDING_DATA_COMMAND);
01407        }
01408 
01409        PR_FREEIF(command);
01410 
01411     m_nextState = SMTP_SEND_POST_DATA;
01412     ClearFlag(SMTP_PAUSE_FOR_READ);   /* send data directly */
01413 
01414     UpdateStatus(SMTP_DELIV_MAIL);
01415 
01416          {
01417 //            m_runningURL->GetBodySize(&m_totalMessageSize);
01418          }
01419 
01420 
01421     return(status);  
01422 }
01423 
01424 PRInt32 nsSmtpProtocol::SendMessageInFile()
01425 {
01426        nsCOMPtr<nsIFileSpec> fileSpec;
01427   nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
01428        m_runningURL->GetPostMessageFile(getter_AddRefs(fileSpec));
01429        if (url && fileSpec)
01430         // need to fully qualify to avoid getting overwritten by a #define
01431         // in some windows header file
01432         nsMsgAsyncWriteProtocol::PostMessage(url, fileSpec);
01433 
01434        SetFlag(SMTP_PAUSE_FOR_READ);
01435 
01436        // for now, we are always done at this point..we aren't making multiple calls
01437        // to post data...
01438 
01439   UpdateStatus(SMTP_DELIV_MAIL);
01440   m_nextState = SMTP_RESPONSE;
01441   m_nextStateAfterResponse = SMTP_SEND_MESSAGE_RESPONSE;
01442   return(0);
01443 }
01444 
01445 PRInt32 nsSmtpProtocol::SendPostData()
01446 {
01447        // mscott: as a first pass, I'm writing everything at once and am not
01448        // doing it in chunks...
01449 
01450     PRInt32 status = 0;
01451 
01452        /* returns 0 on done and negative on error
01453         * positive if it needs to continue.
01454         */
01455 
01456        // check to see if url is a file..if it is...call our file handler...
01457        PRBool postMessageInFile = PR_TRUE;
01458        m_runningURL->GetPostMessage(&postMessageInFile);
01459        if (postMessageInFile)
01460        {
01461               return SendMessageInFile();
01462        }
01463 
01464        /* Update the thermo and the status bar.  This is done by hand, rather
01465           than using the FE_GraphProgress* functions, because there seems to be
01466           no way to make FE_GraphProgress shut up and not display anything more
01467           when all the data has arrived.  At the end, we want to show the
01468           "message sent; waiting for reply" status; FE_GraphProgress gets in
01469           the way of that.  See bug #23414. */
01470 
01471     return(status);
01472 }
01473 
01474 
01475 
01476 PRInt32 nsSmtpProtocol::SendMessageResponse()
01477 {
01478 
01479   if(((m_responseCode != 354) && (m_responseCode != 250)) || CHECK_SIMULATED_ERROR(SIMULATED_SEND_ERROR_12)) {
01480     nsresult rv = nsExplainErrorDetails(m_runningURL, NS_ERROR_SENDING_MESSAGE, m_responseText.get());
01481     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to explain SMTP error");
01482 
01483     m_urlErrorState = NS_ERROR_BUT_DONT_SHOW_ALERT;
01484     return(NS_ERROR_SENDING_MESSAGE);
01485        }
01486 
01487   UpdateStatus(SMTP_PROGRESS_MAILSENT);
01488 
01489     /* else */
01490     m_sendDone = PR_TRUE;
01491        nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
01492        SendData(url, "QUIT"CRLF); // send a quit command to close the connection with the server.
01493        m_nextState = SMTP_RESPONSE;
01494        m_nextStateAfterResponse = SMTP_DONE;
01495        return(0);
01496 }
01497 
01498 
01499 nsresult nsSmtpProtocol::LoadUrl(nsIURI * aURL, nsISupports * aConsumer )
01500 {
01501        nsresult rv = NS_OK;
01502 
01503   // if we are currently waiting for login redirection information
01504   // then hold off on loading the url....but be sure to remember 
01505   // aConsumer so we can use it later...
01506   if (TestFlag(SMTP_WAIT_FOR_REDIRECTION))
01507   {
01508     // mark a pending load...
01509     SetFlag(SMTP_LOAD_URL_PENDING);
01510     mPendingConsumer = aConsumer;
01511     return NS_OK;
01512   }
01513   else
01514     ClearFlag(SMTP_LOAD_URL_PENDING); 
01515 
01516   // otherwise begin loading the url
01517 
01518   PRInt32 status = 0; 
01519        m_continuationResponse = -1;  /* init */
01520        if (aURL)
01521        {
01522               m_runningURL = do_QueryInterface(aURL);
01523 
01524     // we had a bug where we failed to bring up an alert if the host
01525     // name was empty....so throw up an alert saying we don't have
01526     // a host name and inform the caller that we are not going to
01527     // run the url...
01528 
01529     nsCAutoString hostName;
01530     aURL->GetHost(hostName);
01531     if (hostName.IsEmpty())
01532     {
01533        nsCOMPtr <nsIMsgMailNewsUrl> aMsgUrl = do_QueryInterface(aURL);
01534        if (aMsgUrl)
01535        {
01536            aMsgUrl->SetUrlState(PR_TRUE, NS_OK);
01537            rv = aMsgUrl->SetUrlState(PR_FALSE /* we aren't running the url */, NS_ERROR_COULD_NOT_LOGIN_TO_SMTP_SERVER); // set the url as a url currently being run...
01538        }
01539         return NS_ERROR_BUT_DONT_SHOW_ALERT;
01540     }
01541        
01542               PRBool postMessage = PR_FALSE;
01543               m_runningURL->GetPostMessage(&postMessage);
01544 
01545               if(postMessage)
01546               {
01547                      char *addrs1 = 0;
01548                      char *addrs2 = 0;
01549             m_nextState = SMTP_RESPONSE;
01550             m_nextStateAfterResponse = SMTP_EXTN_LOGIN_RESPONSE;
01551 
01552                      /* Remove duplicates from the list, to prevent people from getting
01553                             more than one copy (the SMTP host may do this too, or it may not.)
01554                             This causes the address list to be parsed twice; this probably
01555                             doesn't matter.
01556                      */
01557 
01558                      nsXPIDLCString addresses;
01559                      nsCOMPtr<nsIMsgHeaderParser> parser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
01560 
01561                      m_runningURL->GetRecipients(getter_Copies(addresses));
01562 
01563                      if (NS_SUCCEEDED(rv) && parser)
01564                      {
01565                             parser->RemoveDuplicateAddresses(nsnull, addresses, nsnull, PR_FALSE, &addrs1);
01566 
01567                             /* Extract just the mailboxes from the full RFC822 address list.
01568                                This means that people can post to mailto: URLs which contain
01569                                full RFC822 address specs, and we will still send the right
01570                                thing in the SMTP RCPT command.
01571                             */
01572                             if (addrs1 && *addrs1)
01573                             {
01574                                    rv = parser->ParseHeaderAddresses(nsnull, addrs1, nsnull, &addrs2, &m_addressesLeft);
01575                                    PR_FREEIF (addrs1);
01576                             }
01577 
01578                             if (m_addressesLeft == 0 || addrs2 == nsnull || CHECK_SIMULATED_ERROR(SIMULATED_SEND_ERROR_8)) // hmm no addresses to send message to...
01579                             {
01580                                    m_nextState = SMTP_ERROR_DONE;
01581                                    ClearFlag(SMTP_PAUSE_FOR_READ);
01582                                    status = NS_MSG_NO_RECIPIENTS;
01583           m_urlErrorState = NS_MSG_NO_RECIPIENTS;
01584                                    return(status);
01585                             }
01586 
01587                             m_addressCopy = addrs2;
01588                             m_addresses = m_addressCopy;
01589                      } // if parser
01590               } // if post message
01591               
01592               rv = nsMsgProtocol::LoadUrl(aURL, aConsumer);
01593        } // if we received a url!
01594 
01595        return rv;
01596 }
01597        
01598 /*
01599  * returns negative if the transfer is finished or error'd out
01600  *
01601  * returns zero or more if the transfer needs to be continued.
01602  */
01603  nsresult nsSmtpProtocol::ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream, 
01604    PRUint32 sourceOffset, PRUint32 length)
01605  {
01606    PRInt32 status = 0;
01607    ClearFlag(SMTP_PAUSE_FOR_READ); /* already paused; reset */
01608    
01609    while(!TestFlag(SMTP_PAUSE_FOR_READ))
01610    {
01611      PR_LOG(SMTPLogModule, PR_LOG_ALWAYS, ("SMTP entering state: %d",
01612        m_nextState));
01613      switch(m_nextState) 
01614      {
01615      case SMTP_RESPONSE:
01616        if (inputStream == nsnull)
01617          SetFlag(SMTP_PAUSE_FOR_READ);
01618        else
01619          status = SmtpResponse(inputStream, length);
01620        break;
01621        
01622      case SMTP_START_CONNECT:
01623        SetFlag(SMTP_PAUSE_FOR_READ);
01624        m_nextState = SMTP_RESPONSE;
01625        m_nextStateAfterResponse = SMTP_EXTN_LOGIN_RESPONSE;
01626        break;
01627      case SMTP_FINISH_CONNECT:
01628        SetFlag(SMTP_PAUSE_FOR_READ);
01629        break;
01630      case SMTP_TLS_RESPONSE:
01631        if (inputStream == nsnull)
01632          SetFlag(SMTP_PAUSE_FOR_READ);
01633        else
01634          status = SendTLSResponse();
01635        break;
01636      case SMTP_EXTN_LOGIN_RESPONSE:
01637        if (inputStream == nsnull)
01638          SetFlag(SMTP_PAUSE_FOR_READ);
01639        else
01640          status = ExtensionLoginResponse(inputStream, length);
01641        break;
01642        
01643      case SMTP_SEND_HELO_RESPONSE:
01644        if (inputStream == nsnull)
01645          SetFlag(SMTP_PAUSE_FOR_READ);
01646        else
01647          status = SendHeloResponse(inputStream, length);
01648        break;
01649      case SMTP_SEND_EHLO_RESPONSE:
01650        if (inputStream == nsnull)
01651          SetFlag(SMTP_PAUSE_FOR_READ);
01652        else
01653          status = SendEhloResponse(inputStream, length);
01654        break;
01655      case SMTP_AUTH_PROCESS_STATE:
01656        status = ProcessAuth();
01657        break;
01658        
01659       case SMTP_SEND_AUTH_GSSAPI_FIRST:
01660         status = AuthGSSAPIFirst();
01661         break;
01662 
01663       case SMTP_SEND_AUTH_GSSAPI_STEP:
01664         status = AuthGSSAPIStep();
01665         break;
01666       
01667       case SMTP_SEND_AUTH_LOGIN_STEP0:
01668         status = AuthLoginStep0();
01669         break;
01670       
01671       case SMTP_AUTH_LOGIN_STEP0_RESPONSE:
01672         status = AuthLoginStep0Response();
01673         break;
01674       
01675       case SMTP_AUTH_EXTERNAL_RESPONSE:
01676       case SMTP_AUTH_LOGIN_RESPONSE:
01677         if (inputStream == nsnull)
01678           SetFlag(SMTP_PAUSE_FOR_READ);
01679         else
01680           status = AuthLoginResponse(inputStream, length);
01681         break;
01682       
01683       case SMTP_SEND_AUTH_LOGIN_STEP1:
01684         status = AuthLoginStep1();
01685         break;
01686       
01687       case SMTP_SEND_AUTH_LOGIN_STEP2:
01688         status = AuthLoginStep2(); 
01689         break;
01690       
01691       case SMTP_SEND_VRFY_RESPONSE:
01692         if (inputStream == nsnull)
01693           SetFlag(SMTP_PAUSE_FOR_READ);
01694         else
01695           status = SendVerifyResponse();
01696         break;
01697       
01698       case SMTP_SEND_MAIL_RESPONSE:
01699         if (inputStream == nsnull)
01700           SetFlag(SMTP_PAUSE_FOR_READ);
01701         else
01702           status = SendMailResponse();
01703         break;
01704       
01705       case SMTP_SEND_RCPT_RESPONSE:
01706         if (inputStream == nsnull)
01707           SetFlag(SMTP_PAUSE_FOR_READ);
01708         else
01709           status = SendRecipientResponse();
01710         break;
01711       
01712       case SMTP_SEND_DATA_RESPONSE:
01713         if (inputStream == nsnull)
01714           SetFlag(SMTP_PAUSE_FOR_READ);
01715         else
01716           status = SendDataResponse();
01717         break;
01718       
01719       case SMTP_SEND_POST_DATA:
01720         status = SendPostData(); 
01721         break;
01722       
01723       case SMTP_SEND_MESSAGE_RESPONSE:
01724         if (inputStream == nsnull)
01725           SetFlag(SMTP_PAUSE_FOR_READ);
01726         else
01727           status = SendMessageResponse();
01728         break;
01729       case SMTP_DONE:
01730         {
01731           nsCOMPtr <nsIMsgMailNewsUrl> mailNewsUrl = do_QueryInterface(m_runningURL);
01732           mailNewsUrl->SetUrlState(PR_FALSE, NS_OK);
01733           m_nextState = SMTP_FREE;
01734         }
01735         break;
01736       
01737       case SMTP_ERROR_DONE:
01738         {
01739           nsCOMPtr <nsIMsgMailNewsUrl> mailNewsUrl = do_QueryInterface(m_runningURL);
01740           // propagate the right error code
01741           mailNewsUrl->SetUrlState(PR_FALSE, m_urlErrorState);
01742         }
01743       
01744         m_nextState = SMTP_FREE;
01745         break;
01746       
01747       case SMTP_FREE:
01748         // smtp is a one time use connection so kill it if we get here...
01749         nsMsgAsyncWriteProtocol::CloseSocket(); 
01750         return NS_OK; /* final end */
01751       
01752       default: /* should never happen !!! */
01753         m_nextState = SMTP_ERROR_DONE;
01754         break;
01755     }
01756   
01757     /* check for errors during load and call error 
01758     * state if found
01759     */
01760     if(status < 0 && m_nextState != SMTP_FREE)
01761     {
01762       nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
01763       // send a quit command to close the connection with the server.
01764       if (SendData(url, "QUIT"CRLF) == NS_OK)
01765       {
01766         m_nextState = SMTP_RESPONSE;
01767         m_nextStateAfterResponse = SMTP_ERROR_DONE;
01768       }
01769       else
01770       {
01771       m_nextState = SMTP_ERROR_DONE;
01772       /* don't exit! loop around again and do the free case */
01773       ClearFlag(SMTP_PAUSE_FOR_READ);
01774     }
01775     }
01776   } /* while(!SMTP_PAUSE_FOR_READ) */
01777 
01778   return NS_OK;
01779 }
01780 
01781 nsresult
01782 nsSmtpProtocol::GetPassword(char **aPassword)
01783 {
01784     NS_ENSURE_ARG_POINTER(aPassword);
01785 
01786     nsresult rv;
01787     nsCOMPtr<nsISmtpUrl> smtpUrl = do_QueryInterface(m_runningURL, &rv);
01788     NS_ENSURE_SUCCESS(rv,rv); 
01789    
01790     nsCOMPtr<nsISmtpServer> smtpServer;
01791     rv = smtpUrl->GetSmtpServer(getter_AddRefs(smtpServer));
01792     NS_ENSURE_SUCCESS(rv,rv);
01793     
01794     rv = smtpServer->GetPassword(aPassword);
01795     NS_ENSURE_SUCCESS(rv,rv); 
01796 
01797     if (*aPassword && **aPassword)
01798         return rv;
01799     // empty password
01800 
01801     nsCRT::free(*aPassword);
01802     *aPassword = 0;
01803 
01804     nsXPIDLCString redirectorType; 
01805     rv = smtpServer->GetRedirectorType(getter_Copies(redirectorType));
01806     NS_ENSURE_SUCCESS(rv,rv);
01807       
01808     nsCAutoString prefName("smtp.");
01809     prefName.Append(redirectorType);
01810     prefName.Append(".hide_hostname_for_password");
01811     
01812     nsCOMPtr <nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
01813     NS_ENSURE_SUCCESS(rv,rv);
01814     
01815     nsCOMPtr<nsIPrefBranch> prefBranch; 
01816     rv = prefs->GetBranch(nsnull, getter_AddRefs(prefBranch)); 
01817     NS_ENSURE_SUCCESS(rv,rv);
01818 
01819     nsXPIDLCString username;
01820     rv = smtpServer->GetUsername(getter_Copies(username));
01821     NS_ENSURE_SUCCESS(rv, rv);
01822     
01823     NS_ConvertASCIItoUCS2 usernameUTF16(username);
01824     const PRUnichar *formatStrings[] =
01825     {
01826       usernameUTF16.get(),
01827       nsnull  // this will be overwritten in some cases.
01828     };
01829 
01830     PRBool hideHostnameForPassword = PR_FALSE;
01831     rv = prefBranch->GetBoolPref(prefName.get(), &hideHostnameForPassword);
01832     // for certain redirector types, we don't want to show the
01833     // hostname to the user when prompting for password
01834     nsAutoString hostnameUTF16;
01835     if (!hideHostnameForPassword) 
01836     {
01837       nsXPIDLCString hostname;      
01838       rv = smtpServer->GetHostname(getter_Copies(hostname));
01839       NS_ENSURE_SUCCESS(rv, rv);
01840       CopyASCIItoUTF16(hostname, hostnameUTF16);
01841       formatStrings[1] = hostnameUTF16.get();
01842     }
01843     rv = PromptForPassword(smtpServer, smtpUrl, formatStrings, aPassword);
01844     NS_ENSURE_SUCCESS(rv,rv);
01845     return rv;
01846 }
01847 
01848 nsresult
01849 nsSmtpProtocol::PromptForPassword(nsISmtpServer *aSmtpServer, nsISmtpUrl *aSmtpUrl, const PRUnichar **formatStrings, char **aPassword)
01850 {
01851   nsresult rv;
01852   nsCOMPtr<nsIStringBundleService> stringService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
01853   NS_ENSURE_SUCCESS(rv,rv);
01854   
01855   nsCOMPtr<nsIStringBundle> composeStringBundle;
01856   rv = stringService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(composeStringBundle));
01857   NS_ENSURE_SUCCESS(rv,rv);
01858   
01859   nsXPIDLString passwordPromptString;
01860   if(formatStrings[1])
01861     rv = composeStringBundle->FormatStringFromID(NS_SMTP_PASSWORD_PROMPT2,
01862       formatStrings, 2,
01863       getter_Copies(passwordPromptString));
01864   else
01865     rv = composeStringBundle->FormatStringFromID(NS_SMTP_PASSWORD_PROMPT1,
01866       formatStrings, 1,
01867       getter_Copies(passwordPromptString));
01868   NS_ENSURE_SUCCESS(rv, rv);
01869   
01870   nsCOMPtr<nsIAuthPrompt> netPrompt;
01871   rv = aSmtpUrl->GetAuthPrompt(getter_AddRefs(netPrompt));
01872   NS_ENSURE_SUCCESS(rv, rv);
01873   
01874   nsXPIDLString passwordTitle;
01875   rv = composeStringBundle->GetStringFromID(NS_SMTP_PASSWORD_PROMPT_TITLE, getter_Copies(passwordTitle));
01876   NS_ENSURE_SUCCESS(rv,rv);
01877   
01878   rv = aSmtpServer->GetPasswordWithUI(passwordPromptString.get(), passwordTitle,
01879     netPrompt, aPassword);
01880   NS_ENSURE_SUCCESS(rv,rv);
01881   return rv;
01882 }
01883 
01884 nsresult
01885 nsSmtpProtocol::GetUsernamePassword(char **aUsername, char **aPassword)
01886 {
01887     NS_ENSURE_ARG_POINTER(aUsername);
01888     NS_ENSURE_ARG_POINTER(aPassword);
01889 
01890     nsresult rv;
01891     nsCOMPtr<nsISmtpUrl> smtpUrl = do_QueryInterface(m_runningURL, &rv);
01892     NS_ENSURE_SUCCESS(rv,rv);
01893     
01894     nsCOMPtr<nsISmtpServer> smtpServer;
01895     rv = smtpUrl->GetSmtpServer(getter_AddRefs(smtpServer));
01896     NS_ENSURE_SUCCESS(rv,rv);
01897     
01898     rv = smtpServer->GetPassword(aPassword);
01899     NS_ENSURE_SUCCESS(rv,rv);
01900 
01901     if (*aPassword && **aPassword) {
01902         rv = smtpServer->GetUsername(aUsername);
01903         NS_ENSURE_SUCCESS(rv,rv);
01904 
01905         if (*aUsername && **aUsername)
01906             return rv;
01907         
01908         // empty username
01909         nsCRT::free(*aUsername);
01910         *aUsername = 0;
01911     }
01912     // empty password
01913 
01914     nsCRT::free(*aPassword);
01915     *aPassword = 0;
01916 
01917     nsXPIDLCString hostname;      
01918     rv = smtpServer->GetHostname(getter_Copies(hostname));
01919     NS_ENSURE_SUCCESS(rv, rv);
01920 
01921     const PRUnichar *formatStrings[] =
01922     {
01923       NS_ConvertASCIItoUCS2(hostname).get(),
01924       nsnull
01925     };
01926 
01927     rv = PromptForPassword(smtpServer, smtpUrl, formatStrings, aPassword);
01928     NS_ENSURE_SUCCESS(rv,rv);
01929     return rv;
01930 }
01931 
01932 nsresult nsSmtpProtocol::RequestOverrideInfo(nsISmtpServer * aSmtpServer)
01933 {
01934   NS_ENSURE_ARG(aSmtpServer);
01935 
01936        nsresult rv;
01937        nsCAutoString contractID(NS_MSGLOGONREDIRECTORSERVICE_CONTRACTID);
01938 
01939   // go get the redirection type...
01940   nsXPIDLCString redirectionTypeStr; 
01941   aSmtpServer->GetRedirectorType(getter_Copies(redirectionTypeStr));
01942 
01943   const char * redirectionType = (const char *) redirectionTypeStr;
01944 
01945   // if we don't have a redirection type, then get out and proceed normally.
01946   if (!redirectionType || !*redirectionType )
01947     return NS_OK;
01948 
01949        contractID.Append('/');
01950        contractID.Append(redirectionTypeStr);
01951 
01952        m_logonRedirector = do_GetService(contractID.get(), &rv);
01953        if (m_logonRedirector && NS_SUCCEEDED(rv))
01954        {
01955               nsXPIDLCString password;
01956               nsXPIDLCString userName;
01957     PRBool requiresPassword = PR_TRUE;
01958 
01959               aSmtpServer->GetUsername(getter_Copies(userName));
01960     m_logonRedirector->RequiresPassword(userName, redirectionTypeStr.get(), &requiresPassword);
01961     if (requiresPassword)
01962                 GetPassword(getter_Copies(password));
01963 
01964     nsCOMPtr<nsIPrompt> prompter;
01965     m_runningURL->GetPrompt(getter_AddRefs(prompter));
01966               rv = m_logonRedirector->Logon(userName, password, redirectionType, prompter, NS_STATIC_CAST(nsIMsgLogonRedirectionRequester *, this), nsMsgLogonRedirectionServiceIDs::Smtp);
01967        }
01968 
01969   // this protocol instance now needs to wait until
01970   // we receive the login redirection information so set the appropriate state
01971   // flag
01972   SetFlag(SMTP_WAIT_FOR_REDIRECTION);
01973   SetFlag(SMTP_USE_LOGIN_REDIRECTION);
01974   
01975   // even though we haven't started to send the message yet, 
01976   // we are going off and doing an asynch operation to get the redirection
01977   // information. So start the url as being run.
01978   nsCOMPtr <nsIMsgMailNewsUrl> mailNewsUrl = do_QueryInterface(m_runningURL);
01979   // this will cause another dialog to get thrown up....
01980        mailNewsUrl->SetUrlState(PR_TRUE /* start running url */, NS_OK);
01981   UpdateStatus(NS_SMTP_CONNECTING_TO_SERVER);
01982   // and update the status
01983 
01984        return rv;
01985 }
01986 
01987 NS_IMETHODIMP nsSmtpProtocol::OnLogonRedirectionError(const PRUnichar *pErrMsg, PRBool aBadPassword)
01988 {
01989   nsCOMPtr<nsISmtpServer> smtpServer;
01990   m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
01991   
01992   NS_ENSURE_TRUE(smtpServer, NS_ERROR_FAILURE);
01993   NS_ENSURE_TRUE(m_logonRedirector, NS_ERROR_FAILURE);
01994 
01995   m_logonRedirector = nsnull; // we don't care about it anymore
01996        
01997   // step (1) alert the user about the error
01998   nsCOMPtr<nsIPrompt> dialog;
01999   if (m_runningURL && pErrMsg && pErrMsg[0]) 
02000   {
02001     m_runningURL->GetPrompt(getter_AddRefs(dialog));
02002     if (dialog)
02003       dialog->Alert(nsnull, pErrMsg);
02004   }
02005 
02006   // step (2) if they entered a bad password, forget about it!
02007   if (aBadPassword && smtpServer)
02008     smtpServer->ForgetPassword();
02009 
02010   // step (3) we need to let the originator of the send url request know that an
02011   // error occurred and we aren't sending the message...in our case, this will
02012   // force the user back into the compose window and they can try to send it 
02013   // again.
02014   
02015   // this will cause another dialog to get thrown up....
02016   nsCOMPtr <nsIMsgMailNewsUrl> mailNewsUrl = do_QueryInterface(m_runningURL);
02017        mailNewsUrl->SetUrlState(PR_FALSE /* stopped running url */, NS_ERROR_COULD_NOT_LOGIN_TO_SMTP_SERVER);
02018        return NS_OK;
02019 }
02020   
02021   /* Logon Redirection Progress */
02022 NS_IMETHODIMP nsSmtpProtocol::OnLogonRedirectionProgress(nsMsgLogonRedirectionState pState)
02023 {
02024        return NS_OK;
02025 }
02026 
02027   /* reply with logon redirection data. */
02028 NS_IMETHODIMP nsSmtpProtocol::OnLogonRedirectionReply(const PRUnichar * aHost, unsigned short aPort, const char * aCookieData,  unsigned short aCookieSize)
02029 {
02030   NS_ENSURE_ARG(aHost);
02031   
02032   nsresult rv = NS_OK;
02033   nsCOMPtr<nsISmtpServer> smtpServer;
02034   m_runningURL->GetSmtpServer(getter_AddRefs(smtpServer));
02035   NS_ENSURE_TRUE(smtpServer, NS_ERROR_FAILURE);
02036   NS_ENSURE_TRUE(m_logonRedirector, NS_ERROR_FAILURE);
02037  
02038   // we used to logoff from the requestor but we don't want to do
02039   // that anymore in the success case. We want to end up caching the
02040   // external connection for the entire session.
02041   m_logonRedirector = nsnull; // we don't care about it anymore
02042        
02043   // remember the logon cookie
02044   mLogonCookie.Assign(aCookieData, aCookieSize);
02045 
02046   //currently the server isn't returning a valid auth logon capability
02047   // this line is just a HACK to force us to use auth login.
02048   SetFlag(SMTP_AUTH_LOGIN_ENABLED);
02049   // currently the account manager isn't properly setting the authMethod
02050   // preference for servers which require redirectors. This is another 
02051   // HACK ALERT....we'll force it to be set...
02052   m_prefAuthMethod = PREF_AUTH_ANY;
02053 
02054   // now that we have a host and port to connect to, 
02055   // open up the channel...
02056   NS_ConvertUTF16toUTF8 hostUTF8(aHost);
02057   PR_LOG(SMTPLogModule, PR_LOG_ALWAYS, ("SMTP Connecting to: %s on port %d.", hostUTF8.get(), aPort));
02058   nsCOMPtr<nsIInterfaceRequestor> callbacks;
02059   nsCOMPtr<nsISmtpUrl> smtpUrl(do_QueryInterface(m_runningURL));
02060   if (smtpUrl)
02061       smtpUrl->GetNotificationCallbacks(getter_AddRefs(callbacks));
02062 
02063   nsCOMPtr<nsIProxyInfo> proxyInfo;
02064   rv = NS_ExamineForProxy("mailto", hostUTF8.get(), aPort, getter_AddRefs(proxyInfo));
02065   if (NS_FAILED(rv)) proxyInfo = nsnull;
02066 
02067   // pass in "ssl" for connectionType if you want this to be over SSL
02068   rv = OpenNetworkSocketWithInfo(hostUTF8.get(), aPort, nsnull /* connectionType */, proxyInfo, callbacks);
02069 
02070   // we are no longer waiting for a logon redirection reply
02071   ClearFlag(SMTP_WAIT_FOR_REDIRECTION);
02072 
02073   // check to see if we had a pending LoadUrl call...be sure to
02074   // do this after we clear the SMTP_WAIT_FOR_REDIRECTION flag =).
02075   nsCOMPtr<nsIURI> url = do_QueryInterface(m_runningURL);
02076   if (TestFlag(SMTP_LOAD_URL_PENDING))
02077     rv = LoadUrl(url , mPendingConsumer);
02078 
02079   mPendingConsumer = nsnull; // we don't need to remember this anymore...
02080 
02081   // since we are starting this url load out of the normal loading process,
02082   // we probably should stop the url from running and throw up an error dialog
02083   // if for some reason we didn't successfully load the url...
02084 
02085   // we may want to always return NS_OK regardless of an error
02086   return rv;
02087 }
02088