/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
/*
 * state machine to speak SMTP
 */

/* Please leave outside of ifdef for windows precompiled headers */
#include "rosetta.h"
#include "mkutils.h"

#if defined(MOZILLA_CLIENT) || defined(LIBNET_SMTP)
#if defined(MOZ_MAIL_NEWS) || defined(MOZ_MAIL_COMPOSE)

#include "mkgeturl.h"
#include "mksmtp.h"
#include "mime.h"
#include "glhist.h"
#include "mktcp.h"
#include "mkparse.h"
#include "msgcom.h"
#include "msgnet.h"
#include "xp_time.h"
#include "xp_thrmo.h"
#include "merrors.h"
#include "ssl.h"
#include "imap.h"

#include "xp_error.h"
#include "prefapi.h"

#ifdef AUTH_SKEY_DEFINED
extern int btoa8(char *out, char*in);
#endif

extern void NET_SetPopPassword2(const char *password);

/* for XP_GetString() */
#include "xpgetstr.h"
extern int XP_PROGRESS_MAILSENT;
extern int MK_COULD_NOT_GET_USERS_MAIL_ADDRESS;
extern int MK_COULD_NOT_LOGIN_TO_SMTP_SERVER;
extern int MK_ERROR_SENDING_DATA_COMMAND;
extern int MK_ERROR_SENDING_FROM_COMMAND;
extern int MK_ERROR_SENDING_MESSAGE;
extern int MK_ERROR_SENDING_RCPT_COMMAND;
extern int MK_OUT_OF_MEMORY;
extern int MK_SMTP_SERVER_ERROR;
extern int MK_TCP_READ_ERROR;
extern int XP_MESSAGE_SENT_WAITING_MAIL_REPLY;
extern int MK_MSG_DELIV_MAIL;
extern int MK_MSG_NO_SMTP_HOST;
extern int MK_MIME_NO_RECIPIENTS;

extern int MK_POP3_USERNAME_UNDEFINED;
extern int MK_POP3_PASSWORD_UNDEFINED;
extern int XP_PASSWORD_FOR_POP3_USER;
extern int XP_RETURN_RECEIPT_NOT_SUPPORT;
extern int XP_SENDMAIL_BAD_TLS;

#define SMTP_PORT 25

/* definitions of state for the state machine design
 */
#define SMTP_RESPONSE               0
#define SMTP_START_CONNECT          1
#define SMTP_FINISH_CONNECT         2
#define SMTP_LOGIN_RESPONSE         3
#define SMTP_SEND_HELO_RESPONSE     4
#define SMTP_SEND_VRFY_RESPONSE     5
#define SMTP_SEND_MAIL_RESPONSE     6
#define SMTP_SEND_RCPT_RESPONSE     7
#define SMTP_SEND_DATA_RESPONSE	    8
#define SMTP_SEND_POST_DATA	    	9
#define SMTP_SEND_MESSAGE_RESPONSE  10
#define SMTP_DONE                   11
#define SMTP_ERROR_DONE             12
#define SMTP_FREE                   13
#define SMTP_EXTN_LOGIN_RESPONSE	14
#define SMTP_SEND_EHLO_RESPONSE		15

#define SMTP_SEND_AUTH_LOGIN_USERNAME 16
#define SMTP_SEND_AUTH_LOGIN_PASSWORD 17
#define SMTP_AUTH_LOGIN_RESPONSE      18
#define SMTP_AUTH_RESPONSE			19


HG08747

/* structure to hold data pertaining to the active state of
 * a transfer in progress.
 *
 */
typedef struct _SMTPConData {
    int     next_state;       			/* the next state or action to be taken */
	int     next_state_after_response;  /* the state after the response one */
    Bool    pause_for_read;   			/* Pause now for next read? */
#ifdef XP_WIN
	Bool    calling_netlib_all_the_time;
#endif
	char   *response_text;
	int     response_code;
    char   *data_buffer;
    int32  data_buffer_size;
	char   *address_copy;
	char   *mail_to_address_ptr;
    int     mail_to_addresses_left;
	TCP_ConData * tcp_con_data;
	int     continuation_response;
	char   *hostname;
	char   *verify_address;
	void   *write_post_data_data;      /* a data object for the 
										* WritePostData function
									 	*/
	int32   total_amt_written;
	uint32  total_message_size;
	unsigned long last_time;
	XP_Bool	ehlo_dsn_enabled;
    XP_Bool auth_login_enabled;
HG60917
} SMTPConData;

/* macro's to simplify variable names */
#define CD_NEXT_STATE        	      cd->next_state
#define CD_NEXT_STATE_AFTER_RESPONSE  cd->next_state_after_response
#define CD_PAUSE_FOR_READ    	      cd->pause_for_read
#define CD_RESPONSE_TXT			      cd->response_text
#define CD_RESPONSE_CODE              cd->response_code
#define CD_DATA_BUFFER				  cd->data_buffer
#define CD_DATA_BUFFER_SIZE			  cd->data_buffer_size
#define CD_ADDRESS_COPY			      cd->address_copy
#define CD_MAIL_TO_ADDRESS_PTR        cd->mail_to_address_ptr
#define CD_MAIL_TO_ADDRESSES_LEFT     cd->mail_to_addresses_left
#define CD_TCP_CON_DATA				  cd->tcp_con_data
#define CD_CONTINUATION_RESPONSE	  cd->continuation_response
#define CD_HOSTNAME	  				  cd->hostname
#define CD_VERIFY_ADDRESS			  cd->verify_address
#define CD_TOTAL_AMT_WRITTEN          cd->total_amt_written
#define CD_TOTAL_MESSAGE_SIZE         cd->total_message_size
#define CD_EHLO_DSN_ENABLED			  cd->ehlo_dsn_enabled

#define CD_AUTH_LOGIN_ENABLED         cd->auth_login_enabled
HG82493

#define CE_URL_S            cur_entry->URL_s
#define CE_SOCK             cur_entry->socket
#define CE_CON_SOCK         cur_entry->con_sock
#define CE_STATUS           cur_entry->status
#define CE_WINDOW_ID        cur_entry->window_id
#define CE_BYTES_RECEIVED   cur_entry->bytes_received
#define CE_FORMAT_OUT       cur_entry->format_out

PRIVATE char *net_smtp_relay_host=0;
PRIVATE char *net_smtp_password=0;

/* forward decl */
PRIVATE int32 net_ProcessMailto (ActiveEntry *cur_entry);

/* fix Mac warning of missing prototype */
MODULE_PRIVATE char *
NET_MailRelayHost(MWContext *context);

MODULE_PRIVATE char *
NET_MailRelayHost(MWContext *context)
{
    if(net_smtp_relay_host)
		return(net_smtp_relay_host);
	else
		return("");	/* caller now checks for empty string and returns MK_MSG_NO_SMTP_HOST */
}

PUBLIC void
NET_SetMailRelayHost(char * host)
{
	char * at = NULL;

	/*
	** If we are called with data like "fred@bedrock.com", then we will
	** help the user by ignoring the stuff before the "@".  People with
	** @ signs in their host names will be hosed.  They also can't possibly
	** be current happy internet users.
	*/
	if (host) at = XP_STRCHR(host, '@');
	if (at != NULL) host = at + 1;
	StrAllocCopy(net_smtp_relay_host, host);
}

/*
 * gets user domian name out from FE_UsersMailAddress()
 */
PRIVATE const char *
net_smtp_get_user_domain_name()
{
	const char *mail_addr, *at_sign = NULL;
	mail_addr = FE_UsersMailAddress();
	at_sign = XP_STRCHR(mail_addr, '@');
	return (at_sign ? at_sign+1 : mail_addr);
}

/* RFC 1891 -- extended smtp value encoding scheme

  5. Additional parameters for RCPT and MAIL commands

     The extended RCPT and MAIL commands are issued by a client when it wishes to request a DSN from the
     server, under certain conditions, for a particular recipient. The extended RCPT and MAIL commands are
     identical to the RCPT and MAIL commands defined in [1], except that one or more of the following parameters
     appear after the sender or recipient address, respectively. The general syntax for extended SMTP commands is
     defined in [4]. 

     NOTE: Although RFC 822 ABNF is used to describe the syntax of these parameters, they are not, in the
     language of that document, "structured field bodies". Therefore, while parentheses MAY appear within an
     emstp-value, they are not recognized as comment delimiters. 

     The syntax for "esmtp-value" in [4] does not allow SP, "=", control characters, or characters outside the
     traditional ASCII range of 1- 127 decimal to be transmitted in an esmtp-value. Because the ENVID and
     ORCPT parameters may need to convey values outside this range, the esmtp-values for these parameters are
     encoded as "xtext". "xtext" is formally defined as follows: 

     xtext = *( xchar / hexchar ) 

     xchar = any ASCII CHAR between "!" (33) and "~" (126) inclusive, except for "+" and "=". 

	; "hexchar"s are intended to encode octets that cannot appear
	; as ASCII characters within an esmtp-value.

		 hexchar = ASCII "+" immediately followed by two upper case hexadecimal digits 

	When encoding an octet sequence as xtext:

	+ Any ASCII CHAR between "!" and "~" inclusive, except for "+" and "=",
		 MAY be encoded as itself. (A CHAR in this range MAY instead be encoded as a "hexchar", at the
		 implementor's discretion.) 

	+ ASCII CHARs that fall outside the range above must be encoded as
		 "hexchar". 

 */
/* caller must free the return buffer */
PRIVATE char *
esmtp_value_encode(char *addr)
{
	char *buffer = XP_ALLOC(512); /* esmpt ORCPT allow up to 500 chars encoded addresses */
	char *bp = buffer, *bpEnd = buffer+500;
	int len, i;

	if (!buffer) return NULL;

	*bp=0;
	if (! addr || *addr == 0) /* this will never happen */
		return buffer;

	for (i=0, len=XP_STRLEN(addr); i < len && bp < bpEnd; i++)
	{
		if (*addr >= 0x21 && 
			*addr <= 0x7E &&
			*addr != '+' &&
			*addr != '=')
		{
			*bp++ = *addr++;
		}
		else
		{
			PR_snprintf(bp, bpEnd-bp, "+%.2X", ((int)*addr++));
			bp += XP_STRLEN(bp);
		}
	}
	*bp=0;
	return buffer;
}

/*
 * gets the response code from the nntp server and the
 * response line
 *
 * returns the TCP return code from the read
 */
PRIVATE int
net_smtp_response (ActiveEntry * cur_entry)
{
    char * line;
	char cont_char;
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;

    CE_STATUS = NET_BufferedReadLine(CE_SOCK, &line, &CD_DATA_BUFFER,
                    						&CD_DATA_BUFFER_SIZE, &CD_PAUSE_FOR_READ);

    if(CE_STATUS == 0)
      {
        CD_NEXT_STATE = SMTP_ERROR_DONE;
        CD_PAUSE_FOR_READ = FALSE;
		CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_SMTP_SERVER_ERROR,
													  CD_RESPONSE_TXT);
		CE_STATUS = MK_SMTP_SERVER_ERROR;
        return(MK_SMTP_SERVER_ERROR);
      }

    /* if TCP error of if there is not a full line yet return
     */
    if(CE_STATUS < 0)
	  {
		CE_URL_S->error_msg =
		  NET_ExplainErrorDetails(MK_TCP_READ_ERROR, SOCKET_ERRNO);

        /* return TCP error
         */
        return MK_TCP_READ_ERROR;
	  }
	else if(!line)
	  {
         return CE_STATUS;
	  }

    TRACEMSG(("SMTP Rx: %s\n", line));

	cont_char = ' '; /* default */
    sscanf(line, "%d%c", &CD_RESPONSE_CODE, &cont_char);

     if(CD_CONTINUATION_RESPONSE == -1)
       {
         if (cont_char == '-')  /* begin continuation */
             CD_CONTINUATION_RESPONSE = CD_RESPONSE_CODE;

         if(XP_STRLEN(line) > 3)
         	StrAllocCopy(CD_RESPONSE_TXT, line+4);
       }
     else
       {    /* have to continue */
         if (CD_CONTINUATION_RESPONSE == CD_RESPONSE_CODE && cont_char == ' ')
             CD_CONTINUATION_RESPONSE = -1;    /* ended */

         StrAllocCat(CD_RESPONSE_TXT, "\n");
         if(XP_STRLEN(line) > 3)
             StrAllocCat(CD_RESPONSE_TXT, line+4);
       }

     if(CD_CONTINUATION_RESPONSE == -1)  /* all done with this response? */
       {
         CD_NEXT_STATE = CD_NEXT_STATE_AFTER_RESPONSE;
         CD_PAUSE_FOR_READ = FALSE; /* don't pause */
       }

    return(0);  /* everything ok */
}

PRIVATE int
net_smtp_login_response(ActiveEntry *cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
	char buffer[356];

    if(CD_RESPONSE_CODE != 220)
	  {
		CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_COULD_NOT_LOGIN_TO_SMTP_SERVER);
		return(MK_COULD_NOT_LOGIN_TO_SMTP_SERVER);
	  }


	PR_snprintf(buffer, sizeof(buffer), "HELO %.256s" CRLF, 
				net_smtp_get_user_domain_name());
	 
	TRACEMSG(("Tx: %s", buffer));

    CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer));

    CD_NEXT_STATE = SMTP_RESPONSE;
    CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_HELO_RESPONSE;
    CD_PAUSE_FOR_READ = TRUE;

    return(CE_STATUS);
}
    

PRIVATE int
net_smtp_extension_login_response(ActiveEntry *cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
	char buffer[356];

    if(CD_RESPONSE_CODE != 220)
	  {
		CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_COULD_NOT_LOGIN_TO_SMTP_SERVER);
		return(MK_COULD_NOT_LOGIN_TO_SMTP_SERVER);
	  }

	PR_snprintf(buffer, sizeof(buffer), "EHLO %.256s" CRLF, 
				net_smtp_get_user_domain_name());

	TRACEMSG(("Tx: %s", buffer));

    CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer));

    CD_NEXT_STATE = SMTP_RESPONSE;
    CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_EHLO_RESPONSE;
    CD_PAUSE_FOR_READ = TRUE;

    return(CE_STATUS);
}
    

PRIVATE int
net_smtp_send_helo_response(ActiveEntry *cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
	char buffer[620];
	const char *mail_add = FE_UsersMailAddress();

	/* don't check for a HELO response because it can be bogus and
	 * we don't care
	 */

	if(!mail_add)
	  {
		CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_COULD_NOT_GET_USERS_MAIL_ADDRESS);
		return(MK_COULD_NOT_GET_USERS_MAIL_ADDRESS);
	  }

	if(CD_VERIFY_ADDRESS)
	  {
		PR_snprintf(buffer, sizeof(buffer), "VRFY %.256s" CRLF, CD_VERIFY_ADDRESS);
	  }
	else
	  {
		/* else send the MAIL FROM: command */
		char *s = MSG_MakeFullAddress (NULL, mail_add);
		if (!s)
		  {
			CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
			return(MK_OUT_OF_MEMORY);
		  }
		if (CE_URL_S->msg_pane) {
			if (MSG_RequestForReturnReceipt(CE_URL_S->msg_pane)) {
				if (CD_EHLO_DSN_ENABLED) {
					PR_snprintf(buffer, sizeof(buffer), 
								"MAIL FROM:<%.256s> RET=FULL ENVID=NS40112696JT" CRLF,
								s);
				}
				else {
					FE_Alert (CE_WINDOW_ID, XP_GetString(XP_RETURN_RECEIPT_NOT_SUPPORT));
					PR_snprintf(buffer, sizeof(buffer), "MAIL FROM:<%.256s>" CRLF, s);
				}
			}
			else if (MSG_SendingMDNInProgress(CE_URL_S->msg_pane)) {
				PR_snprintf(buffer, sizeof(buffer), "MAIL FROM:<%.256s>" CRLF, "");
			}
			else {
				PR_snprintf(buffer, sizeof(buffer), "MAIL FROM:<%.256s>" CRLF, s);
			}
		}
		else {
			PR_snprintf(buffer, sizeof(buffer), "MAIL FROM:<%.256s>" CRLF, s);
		}
		XP_FREE (s);
	  }

	TRACEMSG(("Tx: %s", buffer));
    CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer));

    CD_NEXT_STATE = SMTP_RESPONSE;

	if(CD_VERIFY_ADDRESS)
    	CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_VRFY_RESPONSE;
	else
    	CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_MAIL_RESPONSE;
    CD_PAUSE_FOR_READ = TRUE;

    return(CE_STATUS);
}


PRIVATE int
net_smtp_send_ehlo_response(ActiveEntry *cur_entry)
{
  SMTPConData * cd = (SMTPConData *)cur_entry->con_data;

  if (CD_RESPONSE_CODE != 250) {
	/* EHLO not implemented */
	char buffer[384];

HG85890

	PR_snprintf(buffer, sizeof(buffer), "HELO %.256s" CRLF, 
				net_smtp_get_user_domain_name());

	TRACEMSG(("Tx: %s", buffer));

    CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer));

    CD_NEXT_STATE = SMTP_RESPONSE;
    CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_HELO_RESPONSE;
    CD_PAUSE_FOR_READ = TRUE;
	return (CE_STATUS);
  }
  else {
	char *ptr = NULL;
HG09714

	ptr = strcasestr(CD_RESPONSE_TXT, "DSN");
	CD_EHLO_DSN_ENABLED = (ptr && XP_TO_UPPER(*(ptr-1)) != 'X');
	/* should we use auth login */
	PREF_GetBoolPref("mail.auth_login", &(CD_AUTH_LOGIN_ENABLED));
	if (CD_AUTH_LOGIN_ENABLED) {
	  /* okay user has set to use skey
		 let's see does the server have the capability */
	  CD_AUTH_LOGIN_ENABLED = (NULL != strcasestr(CD_RESPONSE_TXT, "AUTH=LOGIN"));
	}


HG90967
	CD_NEXT_STATE = CD_NEXT_STATE_AFTER_RESPONSE = SMTP_AUTH_RESPONSE;

HG59852
	return (CE_STATUS);
  }
}


PRIVATE int
net_smtp_auth_login_response(ActiveEntry *cur_entry)
{
  SMTPConData * cd = (SMTPConData *)cur_entry->con_data;

  switch (CD_RESPONSE_CODE/100) {
  case 2:
	  {
		  char *pop_password = (char *)NET_GetPopPassword();
		  CD_NEXT_STATE = SMTP_SEND_HELO_RESPONSE;
		  if (pop_password == NULL)
			NET_SetPopPassword2(net_smtp_password);
#ifdef MOZ_MAIL_NEWS
		  if ( IMAP_GetPassword() == NULL )
			  IMAP_SetPassword(net_smtp_password);
#endif /* MOZ_MAIL_NEWS */
		  XP_FREEIF(pop_password);
	  }
	break;
  case 3:
	CD_NEXT_STATE = SMTP_SEND_AUTH_LOGIN_PASSWORD;
	break;
  case 5:
  default:
	  {
		char* pop_username = (char *) NET_GetPopUsername();
		/* NET_GetPopUsername () returns pointer to the cached
		 * username. It did *NOT* alloc a new string
		 */
		XP_FREEIF(net_smtp_password);
        if (FE_PromptUsernameAndPassword(cur_entry->window_id,
                        NULL, &pop_username, &net_smtp_password)) {
            CD_NEXT_STATE = SMTP_SEND_AUTH_LOGIN_USERNAME;
			/* FE_PromptUsernameAndPassword() always alloc a new string
			 * for pop_username. The caller has to free it.
			 */
			XP_FREEIF(pop_username);
        }
        else {
			/* User hit cancel, but since the client and server both say 
			 * they want auth login we're just going to return an error 
			 * and not let the msg be sent to the server
			 */
			CE_STATUS = MK_POP3_PASSWORD_UNDEFINED;
        }
	  }
	break;
  }
  
  return (CE_STATUS);
}

PRIVATE int
net_smtp_auth_login_username(ActiveEntry *cur_entry)
{
  SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
  char buffer[512];
  char *pop_username = (char *) NET_GetPopUsername();
  char *base64Str = NULL;

  if (!pop_username || !*pop_username)
	return (MK_POP3_USERNAME_UNDEFINED);

#ifdef MOZ_MAIL_NEWS
  base64Str = NET_Base64Encode(pop_username,
							   XP_STRLEN(pop_username));
  if (base64Str) {
	PR_snprintf(buffer, sizeof(buffer), "AUTH LOGIN %.256s" CRLF, base64Str);
	TRACEMSG(("Tx: %s", buffer));

	CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer));
	CD_NEXT_STATE = SMTP_RESPONSE;
	CD_NEXT_STATE_AFTER_RESPONSE = SMTP_AUTH_LOGIN_RESPONSE;
	CD_PAUSE_FOR_READ = TRUE;
	XP_FREEIF(base64Str);
	
	return (CE_STATUS);
  }
#endif /* MOZ_MAIL_NEWS */

  return -1;
}

PRIVATE int
net_smtp_auth_login_password(ActiveEntry *cur_entry)
{
  SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
  char buffer[1024];

  /* use cached smtp password first
   * if not then use cached pop password
   * if pop password undefined 
   * sync with smtp password
   */
  
  if (!net_smtp_password || !*net_smtp_password) {
	  XP_FREEIF(net_smtp_password); /* in case its an empty string */
	  net_smtp_password = (char *) NET_GetPopPassword();
  }

  if (!net_smtp_password || !*net_smtp_password) {
	char *fmt = XP_GetString (XP_PASSWORD_FOR_POP3_USER);
	char host[256];
	int len = 256;
	
	XP_MEMSET(host, 0, 256);
	PREF_GetCharPref("network.hosts.smtp_server", host, &len);
	
	PR_snprintf(buffer, sizeof (buffer), 
				fmt, NET_GetPopUsername(), host);
	XP_FREEIF(net_smtp_password);
	net_smtp_password = FE_PromptPassword(cur_entry->window_id, buffer);
	if (!net_smtp_password)
	  return MK_POP3_PASSWORD_UNDEFINED;
  }

  XP_ASSERT(net_smtp_password);
  
  if (net_smtp_password) {
	char *base64Str = NULL;
	
#ifdef MOZ_MAIL_NEWS
	base64Str = NET_Base64Encode(net_smtp_password, XP_STRLEN(net_smtp_password));
#endif /* MOZ_MAIL_NEWS */

	if (base64Str) {
	  PR_snprintf(buffer, sizeof(buffer), "%.256s" CRLF, base64Str);
	  TRACEMSG(("Tx: %s", buffer));

	  CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer));
	  CD_NEXT_STATE = SMTP_RESPONSE;
	  CD_NEXT_STATE_AFTER_RESPONSE = SMTP_AUTH_LOGIN_RESPONSE;
	  CD_PAUSE_FOR_READ = TRUE;
	  XP_FREEIF(base64Str);

	  return (CE_STATUS);
	}
  }

  return -1;
}

PRIVATE int
net_smtp_send_vrfy_response(ActiveEntry *cur_entry)
{
#if 0
	SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
    char buffer[512];

    if(CD_RESPONSE_CODE == 250 || CD_RESPONSE_CODE == 251)
		return(MK_USER_VERIFIED_BY_SMTP);
	else
		return(MK_USER_NOT_VERIFIED_BY_SMTP);
#else	
	XP_ASSERT(0);
	return(-1);
#endif
}

PRIVATE int
net_smtp_send_mail_response(ActiveEntry *cur_entry)
{
	SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
    char buffer[1024];

    if(CD_RESPONSE_CODE != 250)
	  {
		CE_URL_S->error_msg = 
		  NET_ExplainErrorDetails(MK_ERROR_SENDING_FROM_COMMAND,
								  CD_RESPONSE_TXT);
		return(MK_ERROR_SENDING_FROM_COMMAND);
	  }

    /* Send the RCPT TO: command */
	if (CD_EHLO_DSN_ENABLED &&
		(CE_URL_S->msg_pane && 
		 MSG_RequestForReturnReceipt(CE_URL_S->msg_pane)))
	{
		char *encodedAddress = esmtp_value_encode(CD_MAIL_TO_ADDRESS_PTR);

		if (encodedAddress) {
			PR_snprintf(buffer, sizeof(buffer), 
			"RCPT TO:<%.256s> NOTIFY=SUCCESS,FAILURE ORCPT=rfc822;%.500s" CRLF, 
			CD_MAIL_TO_ADDRESS_PTR, encodedAddress);
			XP_FREEIF(encodedAddress);
		}
		else {
			CE_STATUS = MK_OUT_OF_MEMORY;
			return (CE_STATUS);
		}
	}
	else
	{
	    PR_snprintf(buffer, sizeof(buffer), "RCPT TO:<%.256s>" CRLF, CD_MAIL_TO_ADDRESS_PTR);
	}
	/* take the address we sent off the list (move the pointer to just
	   past the terminating null.) */
	CD_MAIL_TO_ADDRESS_PTR += XP_STRLEN (CD_MAIL_TO_ADDRESS_PTR) + 1;
	CD_MAIL_TO_ADDRESSES_LEFT--;

	TRACEMSG(("Tx: %s", buffer));
    
    CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer));

    CD_NEXT_STATE = SMTP_RESPONSE;
    CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_RCPT_RESPONSE;
    CD_PAUSE_FOR_READ = TRUE;

    return(CE_STATUS);
}

PRIVATE int
net_smtp_send_rcpt_response(ActiveEntry *cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
    char buffer[16];

	if(CD_RESPONSE_CODE != 250 && CD_RESPONSE_CODE != 251)
	  {
		CE_URL_S->error_msg =
		  NET_ExplainErrorDetails(MK_ERROR_SENDING_RCPT_COMMAND,
								  CD_RESPONSE_TXT);
        return(MK_ERROR_SENDING_RCPT_COMMAND);
	  }

	if(CD_MAIL_TO_ADDRESSES_LEFT > 0)
	  {
		/* more senders to RCPT to 
		 */
        CD_NEXT_STATE = SMTP_SEND_MAIL_RESPONSE; 
		return(0);
	  }

    /* else send the RCPT TO: command */
    XP_STRCPY(buffer, "DATA" CRLF);

	TRACEMSG(("Tx: %s", buffer));
        
    CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, buffer, XP_STRLEN(buffer));   

    CD_NEXT_STATE = SMTP_RESPONSE;  
    CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_DATA_RESPONSE; 
    CD_PAUSE_FOR_READ = TRUE;   

    return(CE_STATUS);  
}

PRIVATE int
net_smtp_send_data_response(ActiveEntry *cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
    char * command=0;   

    if(CD_RESPONSE_CODE != 354)
	  {
		CE_URL_S->error_msg = NET_ExplainErrorDetails(
									MK_ERROR_SENDING_DATA_COMMAND,
                        			CD_RESPONSE_TXT ? CD_RESPONSE_TXT : "");
        return(MK_ERROR_SENDING_DATA_COMMAND);
	  }

#ifdef XP_UNIX
	{
	  const char * FE_UsersRealMailAddress(void); /* definition */
	  const char *real_name = FE_UsersRealMailAddress();
	  char *s = (real_name ? MSG_MakeFullAddress (NULL, real_name) : 0);
	  if (real_name && !s)
		{
		  CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
		  return(MK_OUT_OF_MEMORY);
		}
	  if(real_name)
		{
          char buffer[512];
		  PR_snprintf(buffer, sizeof(buffer), "Sender: %.256s" CRLF, real_name);
		  StrAllocCat(command, buffer);
	      if(!command)
	        {
		      CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
		      return(MK_OUT_OF_MEMORY);
	        }
		}
	}

    TRACEMSG(("sending extra unix header: %s", command));

    CE_STATUS = (int) NET_BlockingWrite(CE_SOCK, command, XP_STRLEN(command));   
	if(CE_STATUS < 0)
	{
		TRACEMSG(("Error sending message"));
	}
#endif /* XP_UNIX */

	/* set connect select since we need to select on
	 * writes
	 */
	NET_ClearReadSelect(CE_WINDOW_ID, CE_SOCK);
	NET_SetConnectSelect(CE_WINDOW_ID, CE_SOCK);
#ifdef XP_WIN
	cd->calling_netlib_all_the_time = TRUE;
	NET_SetCallNetlibAllTheTime(CE_WINDOW_ID, "mksmtp");
#endif
    CE_CON_SOCK = CE_SOCK;

	FREE(command);

    CD_NEXT_STATE = SMTP_SEND_POST_DATA;
    CD_PAUSE_FOR_READ = FALSE;   /* send data directly */

	NET_Progress(CE_WINDOW_ID, XP_GetString(MK_MSG_DELIV_MAIL));

	/* get the size of the message */
	if(CE_URL_S->post_data_is_file)
	  {
		XP_StatStruct stat_entry;

		if(-1 != XP_Stat(CE_URL_S->post_data,
                         &stat_entry,
                         xpFileToPost))
			CD_TOTAL_MESSAGE_SIZE = stat_entry.st_size;
	  }
	else
	  {
			CD_TOTAL_MESSAGE_SIZE = CE_URL_S->post_data_size;
	  }


    return(CE_STATUS);  
}

PRIVATE int
net_smtp_send_post_data(ActiveEntry *cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
	unsigned long curtime;

	/* returns 0 on done and negative on error
	 * positive if it needs to continue.
	 */
	CE_STATUS = NET_WritePostData(CE_WINDOW_ID, CE_URL_S,
								  CE_SOCK,
								  &cd->write_post_data_data,
								  TRUE);
								  
	cd->pause_for_read = TRUE;

	if(CE_STATUS == 0)
	  {
		/* normal done
		 */
        XP_STRCPY(cd->data_buffer, CRLF "." CRLF);
        TRACEMSG(("sending %s", cd->data_buffer));
        CE_STATUS = (int) NET_BlockingWrite(CE_SOCK,
                                            cd->data_buffer,
                                            XP_STRLEN(cd->data_buffer));

		NET_Progress(CE_WINDOW_ID,
					XP_GetString(XP_MESSAGE_SENT_WAITING_MAIL_REPLY));

        NET_ClearConnectSelect(CE_WINDOW_ID, CE_SOCK);
#ifdef XP_WIN
		if(cd->calling_netlib_all_the_time)
		{
			cd->calling_netlib_all_the_time = FALSE;
			NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mksmtp");
		}
#endif
        NET_SetReadSelect(CE_WINDOW_ID, CE_SOCK);
		CE_CON_SOCK = 0;

        CD_NEXT_STATE = SMTP_RESPONSE;
        CD_NEXT_STATE_AFTER_RESPONSE = SMTP_SEND_MESSAGE_RESPONSE;
        return(0);
	  }

	CD_TOTAL_AMT_WRITTEN += CE_STATUS;

	/* Update the thermo and the status bar.  This is done by hand, rather
	   than using the FE_GraphProgress* functions, because there seems to be
	   no way to make FE_GraphProgress shut up and not display anything more
	   when all the data has arrived.  At the end, we want to show the
	   "message sent; waiting for reply" status; FE_GraphProgress gets in
	   the way of that.  See bug #23414. */

	curtime = XP_TIME();
	if (curtime != cd->last_time) {
		FE_Progress(CE_WINDOW_ID, XP_ProgressText(CD_TOTAL_MESSAGE_SIZE,
												  CD_TOTAL_AMT_WRITTEN,
												  0, 0));
		cd->last_time = curtime;
	}

	if(CD_TOTAL_MESSAGE_SIZE)
		FE_SetProgressBarPercent(CE_WINDOW_ID,
						  	 CD_TOTAL_AMT_WRITTEN*100/CD_TOTAL_MESSAGE_SIZE);

    return(CE_STATUS);
}



PRIVATE int
net_smtp_send_message_response(ActiveEntry *cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;

    if(CD_RESPONSE_CODE != 250)
	  {
		CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_ERROR_SENDING_MESSAGE,
													  CD_RESPONSE_TXT);
        return(MK_ERROR_SENDING_MESSAGE);
	  }

	NET_Progress(CE_WINDOW_ID, XP_GetString(XP_PROGRESS_MAILSENT));

	/* else */
	CD_NEXT_STATE = SMTP_DONE;
	return(MK_NO_DATA);
}


PRIVATE int32
net_MailtoLoad (ActiveEntry * cur_entry)
{
    /* get memory for Connection Data */
    SMTPConData * cd = XP_NEW(SMTPConData);
	int32 pref = 0;

    cur_entry->con_data = cd;
    if(!cur_entry->con_data)
      {
		CE_URL_S->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
        CE_STATUS = MK_OUT_OF_MEMORY;
        return (CE_STATUS);
      }

/*	GH_UpdateGlobalHistory(cur_entry->URL_s); */

    /* init */
    XP_MEMSET(cd, 0, sizeof(SMTPConData));

	CD_CONTINUATION_RESPONSE = -1;  /* init */
  
    CE_SOCK = NULL;
HG61365

	/* make a copy of the address
	 */
	if(CE_URL_S->method == URL_POST_METHOD)
	  {
		int status=0;
		char *addrs1 = 0;
		char *addrs2 = 0;
    	CD_NEXT_STATE = SMTP_START_CONNECT;

		/* Remove duplicates from the list, to prevent people from getting
		   more than one copy (the SMTP host may do this too, or it may not.)
		   This causes the address list to be parsed twice; this probably
		   doesn't matter.
		 */
		addrs1 = MSG_RemoveDuplicateAddresses (CE_URL_S->address+7, 0);

		/* Extract just the mailboxes from the full RFC822 address list.
		   This means that people can post to mailto: URLs which contain
		   full RFC822 address specs, and we will still send the right
		   thing in the SMTP RCPT command.
		 */
		if (addrs1 && *addrs1)
		{
			status = MSG_ParseRFC822Addresses (addrs1, 0, &addrs2);
			FREEIF (addrs1);
		}

		if (status < 0) return status;

		if (status == 0 || addrs2 == 0)
		  {
			CD_NEXT_STATE = SMTP_ERROR_DONE;
			CD_PAUSE_FOR_READ = FALSE;
			CE_STATUS = MK_MIME_NO_RECIPIENTS;
			CE_URL_S->error_msg = NET_ExplainErrorDetails(CE_STATUS);
			return CE_STATUS;
		  }

		CD_ADDRESS_COPY = addrs2;
		CD_MAIL_TO_ADDRESS_PTR = CD_ADDRESS_COPY;
		CD_MAIL_TO_ADDRESSES_LEFT = status;
        return(net_ProcessMailto(cur_entry));
	  }
	else
	  {
		/* parse special headers and stuff from the search data in the
		   URL address.  This data is of the form

		    mailto:TO_FIELD?FIELD1=VALUE1&FIELD2=VALUE2

		   where TO_FIELD may be empty, VALUEn may (for now) only be
		   one of "cc", "bcc", "subject", "newsgroups", "references",
		   and "attachment".

		   "to" is allowed as a field/value pair as well, for consistency.

		   One additional parameter is allowed, which does not correspond
		   to a visible field: "newshost".  This is the NNTP host (and port)
		   to connect to if newsgroups are specified.  If the value of this
		   field ends in "/secure", then SSL will be used.

		   Each parameter may appear only once, but the order doesn't
		   matter.  All values must be URL-encoded.
		 */
		char *parms = NET_ParseURL (CE_URL_S->address, GET_SEARCH_PART);
		char *rest = parms;
		char *from = 0;						/* internal only */
		char *reply_to = 0;					/* internal only */
		char *to = 0;
		char *cc = 0;
		char *bcc = 0;
		char *fcc = 0;						/* internal only */
		char *newsgroups = 0;
		char *followup_to = 0;
		char *html_part = 0;				/* internal only */
		char *organization = 0;				/* internal only */
		char *subject = 0;
		char *references = 0;
		char *attachment = 0;				/* internal only */
		char *body = 0;
		char *other_random_headers = 0;		/* unused (for now) */
		char *priority = 0;
		char *newshost = 0;					/* internal only */
		XP_Bool encrypt_p = FALSE;
		XP_Bool sign_p = FALSE;				/* internal only */

		char *newspost_url = 0;
		XP_Bool force_plain_text = FALSE;
		MSG_Pane *cpane = 0;

		to = NET_ParseURL (CE_URL_S->address, GET_PATH_PART);

		if (rest && *rest == '?')
		  {
 			/* start past the '?' */
			rest++;
			rest = XP_STRTOK (rest, "&");
			while (rest && *rest)
			  {
				char *token = rest;
				char *value = 0;
				char *eq = XP_STRCHR (token, '=');
				if (eq)
				  {
					value = eq+1;
					*eq = 0;
				  }
				switch (*token)
				  {
				  case 'A': case 'a':
					if (!strcasecomp (token, "attachment") &&
						CE_URL_S->internal_url)
					  StrAllocCopy (attachment, value);
					break;
				  case 'B': case 'b':
					if (!strcasecomp (token, "bcc"))
					  {
						if (bcc && *bcc)
						  {
							StrAllocCat (bcc, ", ");
							StrAllocCat (bcc, value);
						  }
						else
						  {
							StrAllocCopy (bcc, value);
						  }
					  }
					else if (!strcasecomp (token, "body"))
					  {
						if (body && *body)
						  {
							StrAllocCat (body, "\n");
							StrAllocCat (body, value);
						  }
						else
						  {
							StrAllocCopy (body, value);
						  }
					  }
					break;
				  case 'C': case 'c':
					if (!strcasecomp (token, "cc"))
					  {
						if (cc && *cc)
						  {
							StrAllocCat (cc, ", ");
							StrAllocCat (cc, value);
						  }
						else
						  {
							StrAllocCopy (cc, value);
						  }
					  }
					break;
				  case 'E': case 'e':
					if (!strcasecomp (token, "encrypt") ||
						!strcasecomp (token, "encrypted"))
					  encrypt_p = (!strcasecomp(value, "true") ||
								   !strcasecomp(value, "yes"));
					break;
				  case 'F': case 'f':
					if (!strcasecomp (token, "followup-to"))
					  StrAllocCopy (followup_to, value);
					else if (!strcasecomp (token, "from") &&
							 CE_URL_S->internal_url)
					  StrAllocCopy (from, value);
					else if (!strcasecomp (token, "force-plain-text") &&
							 CE_URL_S->internal_url)
						force_plain_text = TRUE;
					break;
				  case 'H': case 'h':
					  if (!strcasecomp(token, "html-part") &&
						  CE_URL_S->internal_url) {
						StrAllocCopy(html_part, value);
					  }
				  case 'N': case 'n':
					if (!strcasecomp (token, "newsgroups"))
					  StrAllocCopy (newsgroups, value);
					else if (!strcasecomp (token, "newshost") &&
							 CE_URL_S->internal_url)
					  StrAllocCopy (newshost, value);
					break;
				  case 'O': case 'o':
					if (!strcasecomp (token, "organization") &&
						CE_URL_S->internal_url)
					  StrAllocCopy (organization, value);
					break;
				  case 'R': case 'r':
					if (!strcasecomp (token, "references"))
					  StrAllocCopy (references, value);
					else if (!strcasecomp (token, "reply-to") &&
							 CE_URL_S->internal_url)
					  StrAllocCopy (reply_to, value);
					break;
				  case 'S': case 's':
					if(!strcasecomp (token, "subject"))
					  StrAllocCopy (subject, value);
					else if ((!strcasecomp (token, "sign") ||
							  !strcasecomp (token, "signed")) &&
							 CE_URL_S->internal_url)
					  sign_p = (!strcasecomp(value, "true") ||
								!strcasecomp(value, "yes"));
					break;
				  case 'P': case 'p':
					if (!strcasecomp (token, "priority"))
					  StrAllocCopy (priority, value);
					break;
				  case 'T': case 't':
					if (!strcasecomp (token, "to"))
					  {
						if (to && *to)
						  {
							StrAllocCat (to, ", ");
							StrAllocCat (to, value);
						  }
						else
						  {
							StrAllocCopy (to, value);
						  }
					  }
					break;
				  }
				if (eq)
				  *eq = '='; /* put it back */
				rest = XP_STRTOK (0, "&");
			  }
		  }

		FREEIF (parms);
		if (to)
		  NET_UnEscape (to);
		if (cc)
		  NET_UnEscape (cc);
		if (subject)
		  NET_UnEscape (subject);
		if (newsgroups)
		  NET_UnEscape (newsgroups);
		if (references)
		  NET_UnEscape (references);
		if (attachment)
		  NET_UnEscape (attachment);
		if (body)
		  NET_UnEscape (body);
		if (newshost)
		  NET_UnEscape (newshost);

		if(newshost)
		  {
			char *prefix = "news://";
			char *slash = XP_STRRCHR (newshost, '/');
			if (slash && !strcasecomp (slash, "/secure"))
			  {
				*slash = 0;
				prefix = "snews://";
			  }
			newspost_url = (char *) XP_ALLOC (XP_STRLEN (prefix) +
											  XP_STRLEN (newshost) + 10);
			if (newspost_url)
			  {
				XP_STRCPY (newspost_url, prefix);
				XP_STRCAT (newspost_url, newshost);
				XP_STRCAT (newspost_url, "/");
			  }
		  }
		else
		  {
			XP_Bool newsServerIsSecure = FALSE;
			PREF_GetBoolPref("news.server_is_secure", &newsServerIsSecure);

			if (newsServerIsSecure)
				newspost_url = XP_STRDUP("snews:");
			else
				newspost_url = XP_STRDUP ("news:");
		  }

		/* Tell the message library and front end to pop up an edit window.
		 */
		cpane = MSG_ComposeMessage (CE_WINDOW_ID,
									from, reply_to, to, cc, bcc, fcc,
									newsgroups, followup_to, organization,
									subject, references, other_random_headers,
									priority, attachment, newspost_url, body,
									encrypt_p, sign_p, force_plain_text,
									html_part);

		if (cpane && CE_URL_S->fe_data) {
			/* Tell libmsg what to do after deliver the message */
			MSG_SetPostDeliveryActionInfo (cpane, CE_URL_S->fe_data);
		}

		FREEIF(from);
		FREEIF(reply_to);
		FREEIF(to);
		FREEIF(cc);
		FREEIF(bcc);
		FREEIF(fcc);
		FREEIF(newsgroups);
		FREEIF(followup_to);
		FREEIF(html_part);
		FREEIF(organization);
		FREEIF(subject);
		FREEIF(references);
		FREEIF(attachment);
		FREEIF(body);
		FREEIF(other_random_headers);
		FREEIF(newshost);
		FREEIF(priority);
		FREEIF(newspost_url);

		CE_STATUS = MK_NO_DATA;
		XP_FREE(cd);	/* no one else is gonna do it! */
		return(-1);
	  }
}




/*
	We have connected to the mail relay and the type of authorization/login required
	has been established. Before we actually send our name and password check
	and see if we should do anything else for the selected auth mode.
*/

PRIVATE int
NET_CheckAuthResponse (ActiveEntry *cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
	int err = 0;

HG54978

	if (CD_AUTH_LOGIN_ENABLED)
	{
		CD_NEXT_STATE_AFTER_RESPONSE = SMTP_AUTH_LOGIN_RESPONSE;
		CD_NEXT_STATE = SMTP_SEND_AUTH_LOGIN_USERNAME;
		return (CE_STATUS);
	}
	CD_NEXT_STATE = SMTP_SEND_HELO_RESPONSE;
	return (CE_STATUS);
}




/*
 * returns negative if the transfer is finished or error'd out
 *
 * returns zero or more if the transfer needs to be continued.
 */
PRIVATE int32
net_ProcessMailto (ActiveEntry *cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
	char	*mail_relay_host;

    TRACEMSG(("Entering NET_ProcessSMTP"));

    CD_PAUSE_FOR_READ = FALSE; /* already paused; reset */

    while(!CD_PAUSE_FOR_READ)
      {

		TRACEMSG(("In NET_ProcessSMTP with state: %d", CD_NEXT_STATE));

        switch(CD_NEXT_STATE) {

		case SMTP_RESPONSE:
			net_smtp_response (cur_entry);
			break;

        case SMTP_START_CONNECT:
			mail_relay_host = NET_MailRelayHost(CE_WINDOW_ID);
			if (XP_STRLEN(mail_relay_host) == 0)
			{
				CE_STATUS = MK_MSG_NO_SMTP_HOST;
				break;
			}
            CE_STATUS = NET_BeginConnect(NET_MailRelayHost(CE_WINDOW_ID), 
										NULL,
										"SMTP",
										SMTP_PORT, 
										&CE_SOCK, 
										FALSE, 
										&CD_TCP_CON_DATA, 
										CE_WINDOW_ID,
										&CE_URL_S->error_msg,
										 cur_entry->socks_host,
										 cur_entry->socks_port);
            CD_PAUSE_FOR_READ = TRUE;
            if(CE_STATUS == MK_CONNECTED)
              {
                CD_NEXT_STATE = SMTP_RESPONSE;
                CD_NEXT_STATE_AFTER_RESPONSE = SMTP_EXTN_LOGIN_RESPONSE;
                NET_SetReadSelect(CE_WINDOW_ID, CE_SOCK);
              }
            else if(CE_STATUS > -1)
              {
                CE_CON_SOCK = CE_SOCK;  /* set con_sock so we can select on it */
                NET_SetConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
                CD_NEXT_STATE = SMTP_FINISH_CONNECT;
              }
            break;

		case SMTP_FINISH_CONNECT:
            CE_STATUS = NET_FinishConnect(NET_MailRelayHost(CE_WINDOW_ID), 
										  "SMTP", 
										  SMTP_PORT, 
										  &CE_SOCK, 
										  &CD_TCP_CON_DATA, 
										  CE_WINDOW_ID,
										  &CE_URL_S->error_msg);

            CD_PAUSE_FOR_READ = TRUE;
HG18931
            if(CE_STATUS == MK_CONNECTED)
              {
                CD_NEXT_STATE = SMTP_RESPONSE;
                CD_NEXT_STATE_AFTER_RESPONSE = SMTP_EXTN_LOGIN_RESPONSE;
                NET_ClearConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
                CE_CON_SOCK = NULL;  /* reset con_sock so we don't select on it */
                NET_SetReadSelect(CE_WINDOW_ID, CE_SOCK);
              }
			else
              {
                /* unregister the old CE_SOCK from the select list
                 * and register the new value in the case that it changes
                 */
                if(CE_CON_SOCK != CE_SOCK)
                  {
                    NET_ClearConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
                    CE_CON_SOCK = CE_SOCK;
                    NET_SetConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
                  }
              }
            break;

       case SMTP_AUTH_RESPONSE:
			CE_STATUS = NET_CheckAuthResponse(cur_entry);
			break;

       case SMTP_LOGIN_RESPONSE:
            CE_STATUS = net_smtp_login_response(cur_entry);
            break;

       case SMTP_EXTN_LOGIN_RESPONSE:
            CE_STATUS = net_smtp_extension_login_response(cur_entry);
            break;

	   case SMTP_SEND_HELO_RESPONSE:
		    CE_STATUS = net_smtp_send_helo_response(cur_entry);
            break;

	   case SMTP_SEND_EHLO_RESPONSE:
		    CE_STATUS = net_smtp_send_ehlo_response(cur_entry);
			break;

	   case SMTP_AUTH_LOGIN_RESPONSE:
		    CE_STATUS = net_smtp_auth_login_response(cur_entry);
			break;
			
	   case SMTP_SEND_AUTH_LOGIN_USERNAME:
		    CE_STATUS = net_smtp_auth_login_username(cur_entry);
			break;

	   case SMTP_SEND_AUTH_LOGIN_PASSWORD:
		    CE_STATUS = net_smtp_auth_login_password(cur_entry);
			break;
			
	   case SMTP_SEND_VRFY_RESPONSE:
			CE_STATUS = net_smtp_send_vrfy_response(cur_entry);
            break;
			
	   case SMTP_SEND_MAIL_RESPONSE:
            CE_STATUS = net_smtp_send_mail_response(cur_entry);
            break;
			
	   case SMTP_SEND_RCPT_RESPONSE:
            CE_STATUS = net_smtp_send_rcpt_response(cur_entry);
            break;
			
	   case SMTP_SEND_DATA_RESPONSE:
            CE_STATUS = net_smtp_send_data_response(cur_entry);
            break;
			
	   case SMTP_SEND_POST_DATA:
            CE_STATUS = net_smtp_send_post_data(cur_entry);
            break;
			
	   case SMTP_SEND_MESSAGE_RESPONSE:
            CE_STATUS = net_smtp_send_message_response(cur_entry);
            break;
        
        case SMTP_DONE:
			NET_BlockingWrite(CE_SOCK, "QUIT", 4);
            NET_ClearReadSelect(CE_WINDOW_ID, CE_SOCK);
            PR_Close(CE_SOCK);
            CD_NEXT_STATE = SMTP_FREE;
            break;
        
        case SMTP_ERROR_DONE:
            if(CE_SOCK != NULL)
              {
				/* we only send out quit if user interrupt 
				 * else must be server error which may not be
				 * able to blocking write command
				 */
				if (CE_STATUS == MK_INTERRUPTED)
					NET_BlockingWrite(CE_SOCK, "QUIT", 4);
                NET_ClearReadSelect(CE_WINDOW_ID, CE_SOCK);
                NET_ClearConnectSelect(CE_WINDOW_ID, CE_SOCK);
#ifdef XP_WIN
				if(cd->calling_netlib_all_the_time)
				{
					cd->calling_netlib_all_the_time = FALSE;
					NET_ClearCallNetlibAllTheTime(CE_WINDOW_ID, "mksmtp");
				}
#endif /* XP_WIN */
                NET_ClearDNSSelect(CE_WINDOW_ID, CE_SOCK);
                PR_Close(CE_SOCK);
              }
            CD_NEXT_STATE = SMTP_FREE;
            break;
        
        case SMTP_FREE:
            FREEIF(CD_DATA_BUFFER);
            FREEIF(CD_ADDRESS_COPY);
			FREEIF(CD_RESPONSE_TXT);
            if(CD_TCP_CON_DATA)
                NET_FreeTCPConData(CD_TCP_CON_DATA);
			if (cd->write_post_data_data)
				NET_free_write_post_data_object((struct WritePostDataData *) 
												cd->write_post_data_data);
            FREE(cd);

            return(-1); /* final end */
        
        default: /* should never happen !!! */
            TRACEMSG(("SMTP: BAD STATE!"));
            CD_NEXT_STATE = SMTP_ERROR_DONE;
            break;
        }

        /* check for errors during load and call error 
         * state if found
         */
        if(CE_STATUS < 0 && CD_NEXT_STATE != SMTP_FREE)
          {
            CD_NEXT_STATE = SMTP_ERROR_DONE;
            /* don't exit! loop around again and do the free case */
            CD_PAUSE_FOR_READ = FALSE;
          }
      } /* while(!CD_PAUSE_FOR_READ) */
    
    return(CE_STATUS);
}

/* abort the connection in progress
 */
PRIVATE int32
net_InterruptMailto(ActiveEntry * cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;

    CD_NEXT_STATE = SMTP_ERROR_DONE;
	CE_STATUS = MK_INTERRUPTED;
 
    return(net_ProcessMailto(cur_entry));
}

/* Free any memory that might be used in caching etc.
 */
PRIVATE void
net_CleanupMailto(void)
{
    /* nothing so far needs freeing */
    return;
}

MODULE_PRIVATE void
NET_InitMailtoProtocol(void)
{
        static NET_ProtoImpl mailto_proto_impl;

        mailto_proto_impl.init = net_MailtoLoad;
        mailto_proto_impl.process = net_ProcessMailto;
        mailto_proto_impl.interrupt = net_InterruptMailto;
        mailto_proto_impl.cleanup = net_CleanupMailto;

        NET_RegisterProtocolImplementation(&mailto_proto_impl, MAILTO_TYPE_URL);
}

#endif /* defined(MOZ_MAIL_NEWS) || defined(MOZ_MAIL_COMPOSE) */
#endif /* defined(MOZILLA_CLIENT) || defined(LIBNET_SMTP) */
