Milán Major

netscape-revival

1 branch
Code

lib/libnet/mksmtp.c

/* -*- Mode: C; tab-width: 4 -*-
 * state machine to speak SMTP
 */

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

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

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


#include "xp_error.h"


/* 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;

#define SMTP_PORT 25

#define OUTPUT_BUFFER_SIZE 512

/* 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

/* 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   *output_buffer;
	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;
} 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 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;

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 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);
        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_HostName());
	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_send_helo_response(ActiveEntry *cur_entry)
{
    SMTPConData * cd = (SMTPConData *)cur_entry->con_data;
	char buffer[512];
	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);
		  }
		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_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[512];

    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 */
    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 buffer[512];
    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)
		{
		  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
	 */
	FE_ClearReadSelect(CE_WINDOW_ID, CE_SOCK);
	FE_SetConnectSelect(CE_WINDOW_ID, CE_SOCK);
#ifdef XP_WIN
	cd->calling_netlib_all_the_time = TRUE;
	net_call_all_the_time_count++;
	FE_SetCallNetlibAllTheTime(CE_WINDOW_ID);
#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));

        FE_ClearConnectSelect(CE_WINDOW_ID, CE_SOCK);
#ifdef XP_WIN
		if(cd->calling_netlib_all_the_time)
		{
			net_call_all_the_time_count--;
			cd->calling_netlib_all_the_time = FALSE;
			if(net_call_all_the_time_count == 0)
				FE_ClearCallNetlibAllTheTime(CE_WINDOW_ID);
		}
#endif
        FE_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 > 0)
	  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);
}


MODULE_PRIVATE int 
NET_MailtoLoad (ActiveEntry * cur_entry)
{
    /* get memory for Connection Data */
    SMTPConData * cd = XP_NEW(SMTPConData);

    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 */
	cd->output_buffer = (char *)XP_ALLOC(OUTPUT_BUFFER_SIZE);

	if(!cd->output_buffer)
	  {
		FREE(cd);
		return(MK_OUT_OF_MEMORY);
	  }
  
    CE_SOCK = SOCKET_INVALID;

	/* make a copy of the address
	 */
	if(CE_URL_S->method == URL_POST_METHOD)
	  {
		int status;
		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.
		 */
		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(MK_SMTP_SERVER_ERROR);
			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 *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 *newshost = 0;					/* internal only */
		XP_Bool encrypt_p = FALSE;
		XP_Bool sign_p = FALSE;				/* internal only */

		char *newspost_url = 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);
					break;
				  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 '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
		  {
			newspost_url = XP_STRDUP ("news:");
		  }

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

		CE_STATUS = MK_NO_DATA;
		return(-1);
	  }
}


/*
 * returns negative if the transfer is finished or error'd out
 *
 * returns zero or more if the transfer needs to be continued.
 */
MODULE_PRIVATE int 
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_LOGIN_RESPONSE;
                FE_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 */
                FE_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;
            if(CE_STATUS == MK_CONNECTED)
              {
                CD_NEXT_STATE = SMTP_RESPONSE;
                CD_NEXT_STATE_AFTER_RESPONSE = SMTP_LOGIN_RESPONSE;
                FE_ClearConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
                CE_CON_SOCK = SOCKET_INVALID;  /* reset con_sock so we don't select on it */
                FE_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)
                  {
                    FE_ClearConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
                    CE_CON_SOCK = CE_SOCK;
                    FE_SetConnectSelect(CE_WINDOW_ID, CE_CON_SOCK);
                  }
              }
            break;

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

	   case SMTP_SEND_HELO_RESPONSE:
            CE_STATUS = net_smtp_send_helo_response(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:
            FE_ClearReadSelect(CE_WINDOW_ID, CE_SOCK);
            NETCLOSE(CE_SOCK);
            CD_NEXT_STATE = SMTP_FREE;
            break;
        
        case SMTP_ERROR_DONE:
            if(CE_SOCK != SOCKET_INVALID)
              {
                FE_ClearReadSelect(CE_WINDOW_ID, CE_SOCK);
                FE_ClearConnectSelect(CE_WINDOW_ID, CE_SOCK);
#ifdef XP_WIN
				if(cd->calling_netlib_all_the_time)
				{
					net_call_all_the_time_count--;
					cd->calling_netlib_all_the_time = FALSE;
					if(net_call_all_the_time_count == 0)
						FE_ClearCallNetlibAllTheTime(CE_WINDOW_ID);
				}
#endif /* XP_WIN */
#if defined(XP_WIN) || defined(XP_UNIX)
                FE_ClearDNSSelect(CE_WINDOW_ID, CE_SOCK);
#endif /* XP_WIN || XP_UNIX */
                NETCLOSE(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);
            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
 */
MODULE_PRIVATE int
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.
 */
MODULE_PRIVATE void
NET_CleanupMailto(void)
{
    /* nothing so far needs freeing */
    return;
}

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