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

/*
 * NNTP protocol implementation. Maintains some state and sends most
 * data to messenger backend.
 */

/* Please leave outside of ifdef for windows precompiled headers */
#define FORCE_PR_LOG /* Allow logging in the release build (sorry this breaks the PCH) */
extern "C" {
#include "mkutils.h"
#include "netutils.h"
}

#ifdef MOZILLA_CLIENT

#include "merrors.h"

#include "mime.h"
#include "shist.h"
#include "glhist.h"

#include "mknews.h"
#include "mktcp.h"
#include "mkparse.h"
#include "mkformat.h"
#include "mkstream.h"
#include "mkaccess.h"
#include "mksort.h"
#include "netcache.h"
#include "libi18n.h"
#include "gui.h"

#include "rosetta.h"
#include HG40855
#include "mknews.h"

#include "msgcom.h"
#include "msgnet.h"
#include "msg_srch.h"
#include "libmime.h"

#include "prtime.h"
#include "prlog.h"
#include "prerror.h"

#include "xp_error.h"
#include HG09893
#include "prefapi.h"	
#include "xplocale.h"

/* start turning on XPCOM interfaces here.
 * when they are all turned on, PLEASE remove dead code */
#define XPCOM_XOVER
#define XPCOM_NEWSPARSE
#define XPCOM_NEWSHOST
#define XPCOM_NEWSGROUP
#define XPCOM_OFFLINE

#ifdef XPCOM_XOVER
#include "nsIMsgXOVERParser.h"
#endif

#ifdef XPCOM_NEWSPARSE
#include "nsIMsgNewsArticleList.h"
#endif

#ifdef XPCOM_NEWSHOST
#include "nsIMsgNewsHost.h"
#endif

#ifdef XPCOM_NEWSGROUP
#include "nsIMsgNewsgroup.h"
#endif

#ifdef XPCOM_OFFLINE
#include "nsIMsgOfflineNewsState.h"
#endif

#include "nsIMsgRFC822Parser.h"
#include "nsMsgRFC822Parser.h"  /* for the factory */

/*#define CACHE_NEWSGRP_PASSWORD*/

/* for XP_GetString() */
#include "xpgetstr.h"
extern int MK_MALFORMED_URL_ERROR;
extern int MK_NEWS_ERROR_FMT;
extern int MK_NNTP_CANCEL_CONFIRM;
extern int MK_NNTP_CANCEL_DISALLOWED;
extern int MK_NNTP_NOT_CANCELLED;
extern int MK_OUT_OF_MEMORY;
extern int XP_CONFIRM_SAVE_NEWSGROUPS;
extern int XP_HTML_ARTICLE_EXPIRED;
extern int XP_HTML_NEWS_ERROR;
extern int XP_PROGRESS_READ_NEWSGROUPINFO;
extern int XP_PROGRESS_RECEIVE_ARTICLE;
extern int XP_PROGRESS_RECEIVE_LISTARTICLES;
extern int XP_PROGRESS_RECEIVE_NEWSGROUP;
extern int XP_PROGRESS_SORT_ARTICLES;
extern int XP_PROGRESS_READ_NEWSGROUP_COUNTS;
extern int XP_THERMO_PERCENT_FORM;
extern int XP_PROMPT_ENTER_USERNAME;
extern int MK_BAD_NNTP_CONNECTION;
extern int MK_NNTP_AUTH_FAILED;
extern int MK_NNTP_ERROR_MESSAGE;
extern int MK_NNTP_NEWSGROUP_SCAN_ERROR;
extern int MK_NNTP_SERVER_ERROR;
extern int MK_NNTP_SERVER_NOT_CONFIGURED;
HG25431
extern int MK_TCP_READ_ERROR;
extern int MK_TCP_WRITE_ERROR;
extern int MK_NNTP_CANCEL_ERROR;
extern int XP_CONNECT_NEWS_HOST_CONTACTED_WAITING_FOR_REPLY;
extern int XP_PLEASE_ENTER_A_PASSWORD_FOR_NEWS_SERVER_ACCESS;
extern int XP_GARBAGE_COLLECTING;
extern int XP_MESSAGE_SENT_WAITING_NEWS_REPLY;
extern int MK_MSG_DELIV_NEWS;
extern int MK_MSG_COLLABRA_DISABLED;
extern int MK_MSG_EXPIRE_NEWS_ARTICLES;
extern int MK_MSG_HTML_IMAP_NO_CACHED_BODY;

/* Logging stuff */

PRLogModuleInfo* NNTP = NULL;
#define out     PR_LOG_ALWAYS

#define NNTP_LOG_READ(buf) \
if (NNTP==NULL) \
    NNTP = PR_NewLogModule("NNTP"); \
PR_LOG(NNTP, out, ("Receiving: %s", buf)) ;

#define NNTP_LOG_WRITE(buf) \
if (NNTP==NULL) \
    NNTP = PR_NewLogModule("NNTP"); \
PR_LOG(NNTP, out, ("Sending: %s", buf)) ;

#define NNTP_LOG_NOTE(buf) \
if (NNTP==NULL) \
    NNTP = PR_NewLogModule("NNTP"); \
PR_LOG(NNTP, out, buf) ;


/* end logging */

/* Forward declarations */
static int net_xpat_send (ActiveEntry*);
static int net_list_pretty_names(ActiveEntry*);

int net_ProcessNews(ActiveEntry *cur_entry);

#ifdef PROFILE
#pragma profile on
#endif

#define LIST_WANTED     0
#define ARTICLE_WANTED  1
#define CANCEL_WANTED   2
#define GROUP_WANTED    3
#define NEWS_POST       4
#define READ_NEWS_RC    5
#define NEW_GROUPS      6
#define SEARCH_WANTED   7
#define PRETTY_NAMES_WANTED 8
#define PROFILE_WANTED	9
#define IDS_WANTED		10

/* the output_buffer_size must be larger than the largest possible line
 * 2000 seems good for news
 *
 * jwz: I increased this to 4k since it must be big enough to hold the
 * entire button-bar HTML, and with the new "mailto" format, that can
 * contain arbitrarily long header fields like "references".
 *
 * fortezza: proxy auth is huge, buffer increased to 8k (sigh).
 */
#define OUTPUT_BUFFER_SIZE (4096*2)

/* the amount of time to subtract from the machine time
 * for the newgroup command sent to the nntp server
 */
#define NEWGROUPS_TIME_OFFSET 60L*60L*12L   /* 12 hours */

/* Allow the user to open at most this many connections to one news host*/
#define kMaxConnectionsPerHost 3

/* Keep this many connections cached. The cache is global, not per host */
#define kMaxCachedConnections 2

/* globals
 */
PRIVATE XP_List * nntp_connection_list=0;

PRIVATE XP_Bool net_news_last_username_probably_valid=FALSE;
PRIVATE int32 net_NewsChunkSize=-1;  /* default */

PRIVATE int32 net_news_timeout = 170; /* seconds that an idle NNTP conn can live */

#define PUTSTRING(s)      (*cd->stream->put_block) \
                    (cd->stream, s, PL_strlen(s))
#define COMPLETE_STREAM   (*cd->stream->complete)  \
                    (cd->stream)
#define ABORT_STREAM(s)   (*cd->stream->abort) \
                    (cd->stream, s)
#define PUT_STREAM(buf, size)   (*cd->stream->put_block) \
						  (cd->stream, buf, size)

/* states of the machine
 */
typedef enum _StatesEnum {
NNTP_RESPONSE,
#ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE,
NNTP_CONNECTIONS_ARE_AVAILABLE,
#endif
NNTP_CONNECT,
NNTP_CONNECT_WAIT,
HG07711
NNTP_LOGIN_RESPONSE,
NNTP_SEND_MODE_READER,
NNTP_SEND_MODE_READER_RESPONSE,
SEND_LIST_EXTENSIONS,
SEND_LIST_EXTENSIONS_RESPONSE,
SEND_LIST_SEARCHES,
SEND_LIST_SEARCHES_RESPONSE,
NNTP_LIST_SEARCH_HEADERS,
NNTP_LIST_SEARCH_HEADERS_RESPONSE,
NNTP_GET_PROPERTIES,
NNTP_GET_PROPERTIES_RESPONSE,
SEND_LIST_SUBSCRIPTIONS,
SEND_LIST_SUBSCRIPTIONS_RESPONSE,
SEND_FIRST_NNTP_COMMAND,
SEND_FIRST_NNTP_COMMAND_RESPONSE,
SETUP_NEWS_STREAM,
NNTP_BEGIN_AUTHORIZE,
NNTP_AUTHORIZE_RESPONSE,
NNTP_PASSWORD_RESPONSE,
NNTP_READ_LIST_BEGIN,
NNTP_READ_LIST,
DISPLAY_NEWSGROUPS,
NNTP_NEWGROUPS_BEGIN,
NNTP_NEWGROUPS,
NNTP_BEGIN_ARTICLE,
NNTP_READ_ARTICLE,
NNTP_XOVER_BEGIN,
NNTP_FIGURE_NEXT_CHUNK,
NNTP_XOVER_SEND,
NNTP_XOVER_RESPONSE,
NNTP_XOVER,
NEWS_PROCESS_XOVER,
NNTP_READ_GROUP,
NNTP_READ_GROUP_RESPONSE,
NNTP_READ_GROUP_BODY,
NNTP_SEND_GROUP_FOR_ARTICLE,
NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE,
NNTP_PROFILE_ADD,
NNTP_PROFILE_ADD_RESPONSE,
NNTP_PROFILE_DELETE,
NNTP_PROFILE_DELETE_RESPONSE,
NNTP_SEND_ARTICLE_NUMBER,
NEWS_PROCESS_BODIES,
NNTP_PRINT_ARTICLE_HEADERS,
NNTP_SEND_POST_DATA,
NNTP_SEND_POST_DATA_RESPONSE,
NNTP_CHECK_FOR_MESSAGE,
NEWS_NEWS_RC_POST,
NEWS_DISPLAY_NEWS_RC,
NEWS_DISPLAY_NEWS_RC_RESPONSE,
NEWS_START_CANCEL,
NEWS_DO_CANCEL,
NNTP_XPAT_SEND,
NNTP_XPAT_RESPONSE,
NNTP_SEARCH,
NNTP_SEARCH_RESPONSE,
NNTP_SEARCH_RESULTS,
NNTP_LIST_PRETTY_NAMES,
NNTP_LIST_PRETTY_NAMES_RESPONSE,
NNTP_LIST_XACTIVE,
NNTP_LIST_XACTIVE_RESPONSE,
NNTP_LIST_GROUP,
NNTP_LIST_GROUP_RESPONSE,
NEWS_DONE,
NEWS_ERROR,
NNTP_ERROR,
NEWS_FREE
} StatesEnum;

#ifdef DEBUG
char *stateLabels[] = {
"NNTP_RESPONSE",
#ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
"NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE",
"NNTP_CONNECTIONS_ARE_AVAILABLE",
#endif
"NNTP_CONNECT",
"NNTP_CONNECT_WAIT",
HG25430
"NNTP_LOGIN_RESPONSE",
"NNTP_SEND_MODE_READER",
"NNTP_SEND_MODE_READER_RESPONSE",
"SEND_LIST_EXTENSIONS",
"SEND_LIST_EXTENSIONS_RESPONSE",
"SEND_LIST_SEARCHES",
"SEND_LIST_SEARCHES_RESPONSE",
"NNTP_LIST_SEARCH_HEADERS",
"NNTP_LIST_SEARCH_HEADERS_RESPONSE",
"NNTP_GET_PROPERTIES",
"NNTP_GET_PROPERTIES_RESPONSE",
"SEND_LIST_SUBSCRIPTIONS",
"SEND_LIST_SUBSCRIPTIONS_RESPONSE",
"SEND_FIRST_NNTP_COMMAND",
"SEND_FIRST_NNTP_COMMAND_RESPONSE",
"SETUP_NEWS_STREAM",
"NNTP_BEGIN_AUTHORIZE",
"NNTP_AUTHORIZE_RESPONSE",
"NNTP_PASSWORD_RESPONSE",
"NNTP_READ_LIST_BEGIN",
"NNTP_READ_LIST",
"DISPLAY_NEWSGROUPS",
"NNTP_NEWGROUPS_BEGIN",
"NNTP_NEWGROUPS",
"NNTP_BEGIN_ARTICLE",
"NNTP_READ_ARTICLE",
"NNTP_XOVER_BEGIN",
"NNTP_FIGURE_NEXT_CHUNK",
"NNTP_XOVER_SEND",
"NNTP_XOVER_RESPONSE",
"NNTP_XOVER",
"NEWS_PROCESS_XOVER",
"NNTP_READ_GROUP",
"NNTP_READ_GROUP_RESPONSE",
"NNTP_READ_GROUP_BODY",
"NNTP_SEND_GROUP_FOR_ARTICLE",
"NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE",
"NNTP_PROFILE_ADD",
"NNTP_PROFILE_ADD_RESPONSE",
"NNTP_PROFILE_DELETE",
"NNTP_PROFILE_DELETE_RESPONSE",
"NNTP_SEND_ARTICLE_NUMBER",
"NEWS_PROCESS_BODIES",
"NNTP_PRINT_ARTICLE_HEADERS",
"NNTP_SEND_POST_DATA",
"NNTP_SEND_POST_DATA_RESPONSE",
"NNTP_CHECK_FOR_MESSAGE",
"NEWS_NEWS_RC_POST",
"NEWS_DISPLAY_NEWS_RC",
"NEWS_DISPLAY_NEWS_RC_RESPONSE",
"NEWS_START_CANCEL",
"NEWS_DO_CANCEL",
"NNTP_XPAT_SEND",
"NNTP_XPAT_RESPONSE",
"NNTP_SEARCH",
"NNTP_SEARCH_RESPONSE",
"NNTP_SEARCH_RESULTS",
"NNTP_LIST_PRETTY_NAMES",
"NNTP_LIST_PRETTY_NAMES_RESPONSE",
"NNTP_LIST_XACTIVE_RESPONSE",
"NNTP_LIST_XACTIVE",
"NNTP_LIST_GROUP",
"NNTP_LIST_GROUP_RESPONSE",
"NEWS_DONE",
"NEWS_ERROR",
"NNTP_ERROR",
"NEWS_FREE"
};

#endif

/* structure to hold data about a tcp connection
 * to a news host
 */
typedef struct _NNTPConnection {
    char   *hostname;       /* hostname string (may contain :port) */
    PRFileDesc *csock;          /* control socket */
    XP_Bool busy;           /* is the connection in use already? */
    XP_Bool prev_cache;     /* did this connection come from the cache? */
    XP_Bool posting_allowed;/* does this server allow posting */
	XP_Bool default_host;
	XP_Bool no_xover;       /* xover command is not supported here */
    HG55785
    char *current_group;	/* last GROUP command sent on this connection */
	PRTime last_used_time;  /* last time this conn was used, for conn aging purposes */
} NNTPConnection;


/* structure to hold all the state and data
 * for the state machine
 */
typedef struct _NewsConData {
	MSG_Pane     *pane;
#ifdef XPCOM_NEWSHOST
    nsIMsgNewsHost *news_host;
#else
	MSG_NewsHost *host;
#endif

    StatesEnum  next_state;
    StatesEnum  next_state_after_response;
    int         type_wanted;     /* Article, List, or Group */
    int         response_code;   /* code returned from NNTP server */
    char       *response_txt;    /* text returned from NNTP server */
    Bool     pause_for_read;  /* should we pause for the next read? */

	char       *proxy_server;    /* @#$ proxy server hostname */
	XP_Bool     proxy_auth_required; /* is auth required */
	XP_Bool     sent_proxy_auth;     /* have we sent a proxy auth? */

	XP_Bool     newsrc_performed;    /* have we done a newsrc update? */
	XP_Bool		mode_reader_performed; /* have we sent any cmds to the server yet? */

#ifdef XP_WIN
	XP_Bool     calling_netlib_all_the_time;
#endif
    NET_StreamClass * stream;

    NNTPConnection * control_con;  /* all the info about a connection */

    char   *data_buf;
    int32   data_buf_size;

    /* for group command */
    char   *path; /* message id */

#ifdef XPCOM_NEWSGROUP
    nsIMsgNewsgroup *newsgroup;
#else
    char   *group_name;
#endif
  
    int32   first_art;
    int32   last_art;
    int32   first_possible_art;
    int32   last_possible_art;

	int32	num_loaded;			/* How many articles we got XOVER lines for. */
	int32	num_wanted;			/* How many articles we wanted to get XOVER
								   lines for. */

    /* for cancellation: the headers to be used */
    char *cancel_from;
    char *cancel_newsgroups;
    char *cancel_distribution;
    char *cancel_id;
    char *cancel_msg_file;
    int cancel_status;

	/* variables for ReadNewsRC */
	int32    newsrc_list_index;
	int32    newsrc_list_count;

    XP_Bool  use_fancy_newsgroup_listing;  /* use LIST XACTIVE or LIST */
    XP_Bool  destroy_graph_progress;       /* do we need to destroy 
											* graph progress? 
											*/    

    char  *output_buffer;                 /* use it to write out lines */

    int32  article_num;   /* current article number */

    char  *message_id;    /* for reading groups */
    char  *author;
    char  *subject;

	int32  original_content_length; /* the content length at the time of
                                     * calling graph progress
                                     */

    /* random pointer for libmsg state */
#ifdef XPCOM_XOVER
    nsIMsgXOVERParser *xover_parser;
#else
    void *xover_parse_state;
#endif

#ifdef XPCOM_NEWSPARSE
    nsIMsgNewsArticleList *article_list;
#else
	void *newsgroup_parse_state;
#endif

	TCP_ConData * tcp_con_data;

	void * write_post_data_data;

	XP_Bool some_protocol_succeeded; /* We know we have done some protocol
										with this newsserver recently, so don't
										respond to an error by closing and
										reopening it.  */
	char *command_specific_data;
	char *current_search;
#ifdef XPCOM_OFFLINE
    nsIMsgOfflineNewsState *offline_state;
#else
	void *offlineState;				/* offline news state machine for article retrieval */
#endif
	XP_Bool articleIsOffline;
	int previous_response_code;

} NewsConData;

static char * last_password = 0;
static char * last_password_hostname = 0;
static char * last_username=0;
static char * last_username_hostname=0;

/* publicly available function to set the news host
 * this will be a useful front end callable routine
 */
PUBLIC void NET_SetNewsHost (const char * value)
{
	/* ### This routine is obsolete and should go away. */
}



/* set the default number of articles retrieved by news at any one time
 */
PUBLIC void 
NET_SetNumberOfNewsArticlesInListing(int32 number)
{
	net_NewsChunkSize = number;
}

/* Whether we cache XOVER data.  NYI. */
PUBLIC void
NET_SetCacheXOVER(XP_Bool value)
{
}



PUBLIC void NET_CleanTempXOVERCache(void)
{
}



PUBLIC void net_graceful_shutdown(PRFileDesc* sock, XP_Bool bVal)
{
	static int32 int_pref = -1;

#ifdef THIS_IS_DEPRICATED
	if (sock == NULL) return;
#endif
    
	if (int_pref == -1)
		PREF_GetIntPref("network.socket_shutdown", &int_pref);
	if (int_pref > 0 && int_pref < 4)
	{
		if (bVal)
								/* ??? */
			NET_BlockingWrite(sock, "", 0);
		else
			PR_Shutdown(sock, (PRShutdownHow)(int_pref-1));
	}
}

static void net_nntp_close (NNTPConnection *con, int status)
{
	if (!con) 
	{
		PR_ASSERT(FALSE);
		return;
	}
	else if (status != MK_INTERRUPTED)
	{
		const char *command = "QUIT" CRLF;
		status = NET_BlockingWrite (con->csock, command, PL_strlen(command));
		NNTP_LOG_WRITE (command);
		if (status >= 0) {
		  HG51966
        }
        PR_Close(con->csock);
	}

	FREEIF(con->current_group);
}

/* user has removed a news host from the UI. 
 * be sure it has also been removed from the
 * connection cache so we renegotiate news host
 * capabilities if we try to use it again
 */
PUBLIC void NET_OnNewsHostDeleted (const char *hostName)
{
	NNTPConnection *conn;
	XP_List *list = nntp_connection_list;

	while ((conn = (NNTPConnection*) XP_ListNextObject(list)) != NULL)
	{
		if (!(PL_strcasecmp(conn->hostname, hostName)))
		{
			net_nntp_close (conn, /* conn->busy ? MK_INTERRUPTED : */ 0);

			list = list->prev ? list->prev : nntp_connection_list;
			XP_ListRemoveObject (nntp_connection_list, conn);
			FREEIF(conn->hostname);
			FREE(conn);
		}
	}
}


/* display HTML error state in the context window
 * returns negative error status
 */
PRIVATE int
net_display_html_error_state (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
	XP_Bool inMsgPane = FALSE;
	int major_opcode = MK_NNTP_RESPONSE_TYPE(cd->response_code);
	/* #### maybe make this a dialog box
	   to do that, just return an error.
	 */

	/* ERROR STATE
     *  Set up the HTML stream
     */
	ce->format_out = CLEAR_CACHE_BIT(ce->format_out);
    StrAllocCopy(ce->URL_s->content_type, TEXT_HTML);

	/* If we're not in a message pane, don't try to spew HTML
	 * This allows error reporting from the folder pane, subscribe UI, etc.
	 */
	if (ce->URL_s->msg_pane)
		inMsgPane = (MSG_MESSAGEPANE == MSG_GetPaneType(ce->URL_s->msg_pane));

	if (ce->format_out == FO_PRESENT && inMsgPane)
	  {
		cd->stream = NET_StreamBuilder(ce->format_out, ce->URL_s,
									  ce->window_id);
		if(!cd->stream)
		  {
			ce->URL_s->error_msg =
			  NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
			return MK_UNABLE_TO_CONVERT;
		  }

		PR_snprintf(cd->output_buffer,
					OUTPUT_BUFFER_SIZE,
					XP_GetString(XP_HTML_NEWS_ERROR),
					cd->response_txt);
		if(ce->status > -1)
		  ce->status = PUTSTRING(cd->output_buffer);

		if(cd->type_wanted == ARTICLE_WANTED ||
		   cd->type_wanted == CANCEL_WANTED)
		  {
			PL_strcpy(cd->output_buffer,
					   XP_GetString(XP_HTML_ARTICLE_EXPIRED));
			if(ce->status > -1)
			  PUTSTRING(cd->output_buffer);

			if (cd->path && *cd->path && ce->status > -1)
			  {
				char *urlScheme;
				HG72589

				PR_snprintf(cd->output_buffer, 
							OUTPUT_BUFFER_SIZE,
							"<P>&lt;%.512s&gt;", cd->path);
				if (cd->article_num > 0)
				{
				  PR_snprintf(cd->output_buffer+PL_strlen(cd->output_buffer),
							 OUTPUT_BUFFER_SIZE-PL_strlen(cd->output_buffer),
							 " (%lu)", (unsigned long) cd->article_num);
				  if (ce->URL_s->msg_pane)
					MSG_MarkMessageKeyRead(ce->URL_s->msg_pane, cd->article_num, "");
				}
				PUTSTRING(cd->output_buffer);

                char *group_name;
                cd->newsgroup->GetName(&group_name);
				PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE,
                            "<P> <A HREF=\"%s//%s/%s?list-ids\">%s</A> </P>\n",
                            urlScheme,
                            cd->control_con->hostname,
                            group_name,
                            XP_GetString(MK_MSG_EXPIRE_NEWS_ARTICLES));
                PR_Free(group_name);
				PUTSTRING(cd->output_buffer);
			  }

			if(ce->status > -1)
			  {
				COMPLETE_STREAM;
				cd->stream = 0;
			  }
		  }
		else
			if (cd->type_wanted == SEARCH_WANTED)
			ce->status = net_xpat_send(ce);

		ce->URL_s->expires = 1;
		/* ce->URL_s->error_msg =
		   NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR); 
		 */
	  }
	else
	  {
		/* FORMAT_OUT is not FO_PRESENT - so instead of emitting an HTML
		   error message, present it in a dialog box. */

		PR_snprintf(cd->output_buffer,  /* #### i18n */
				   OUTPUT_BUFFER_SIZE,
				   XP_GetString(MK_NEWS_ERROR_FMT),
				   cd->response_txt);
		ce->URL_s->error_msg = PL_strdup (cd->output_buffer);
	  }

	/* if the server returned a 400 error then it is an expected
	 * error.  the NEWS_ERROR state will not sever the connection
	 */
	if(major_opcode == MK_NNTP_RESPONSE_TYPE_CANNOT)
	  cd->next_state = NEWS_ERROR;
	else
	  cd->next_state = NNTP_ERROR;

	/* If we ever get an error, let's re-issue the GROUP command
	   before the next ARTICLE command; the overhead isn't very high,
	   and weird stuff seems to be happening... */
	FREEIF(cd->control_con->current_group);

    /* cd->control_con->prev_cache = FALSE;  to keep if from reconnecting */
    return MK_NNTP_SERVER_ERROR;
}


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

    ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf, 
                    &cd->data_buf_size, (Bool*)&cd->pause_for_read);

	NNTP_LOG_READ(cd->data_buf);

    if(ce->status == 0)
      {
        cd->next_state = NNTP_ERROR;
        cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
        return(MK_NNTP_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, 
                                                       PR_GetOSError());

        /* return TCP error
         */
        return MK_TCP_READ_ERROR;
	  }
	else if(!line)
	  {
         return ce->status;
	  }

    cd->pause_for_read = FALSE; /* don't pause if we got a line */

	HG43574
    /* almost correct
     */
    if(ce->status > 1)
	  {
        ce->bytes_received += ce->status;
        FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
	  }

    StrAllocCopy(cd->response_txt, line+4);

	cd->previous_response_code = cd->response_code;

    sscanf(line, "%d", &cd->response_code);

	/* authentication required can come at any time
	 */
#ifdef CACHE_NEWSGRP_PASSWORD
	/*
	 * This is an effort of trying to cache the username/password
	 * per newsgroup. It is extremely hard to make it work along with
	 * nntp voluntary password checking mechanism. We are backing this 
	 * feature out. Instead of touching various of backend msg files
	 * at this late Dogbert 4.0 beta4 game, the infrastructure will
	 * remain in the msg library. We only modify codes within this file.
	 * Maybe one day we will try to do it again. Zzzzz -- jht
	 */
	if(MK_NNTP_RESPONSE_AUTHINFO_REQUIRE == cd->response_code ||
       MK_NTTP_RESPONSE_AUTHINFO_SIMPLE_REQUIRE == cd->response_code || 
	   MK_NNTP_RESPONSE_PERMISSION_DENIED == cd->response_code)
	  {
        cd->next_state = NNTP_BEGIN_AUTHORIZE;
		if (MK_NNTP_RESPONSE_PERMISSION_DENIED == cd->response_code) {
			if (MK_NNTP_RESPONSE_TYPE_OK == MK_NNTP_RESPONSE_TYPE(cd->previous_response_code) {
				if (net_news_last_username_probably_valid) {
				  net_news_last_username_probably_valid = FALSE;
				}
				else {
#ifdef XPCOM_NEWSGROUP
                  cd->newsgroup->SetUsername(NULL);
                  cd->newsgroup->SetPassword(NULL);
#else
				  MSG_SetNewsgroupUsername(cd->pane, NULL);
				  MSG_SetNewsgroupPassword(cd->pane, NULL);
#endif
				}
			}
			else {
			  net_news_last_username_probably_valid = FALSE;
			  if (NNTP_PASSWORD_RESPONSE == cd->next_state_after_response) {
#ifdef XPCOM_NEWSGROUP
                  cd->newsgroup->SetUsername(NULL);
                  cd->newsgroup->SetPassword(NULL);
#else
				MSG_SetNewsgroupUsername(cd->pane, NULL);
				MSG_SetNewsgroupPassword(cd->pane, NULL);
#endif
			  }
			}

		}
	  }
#else
	if (MK_NNTP_RESPONSE_AUTHINFO_REQUIRE == cd->response_code ||
        MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_REQUIRE == cd->response_code) 
	{
		cd->next_state = NNTP_BEGIN_AUTHORIZE;
	}
	else if (MK_NNTP_RESPONSE_PERMISSION_DENIED == cd->response_code)
	{
	    net_news_last_username_probably_valid = FALSE;
		return net_display_html_error_state(ce);
	}
#endif
	else
	  {
    	cd->next_state = cd->next_state_after_response;
	  }

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

HG43072

/* interpret the server response after the connect
 *
 * returns negative if the server responds unexpectedly
 */
PRIVATE int
net_nntp_login_response (ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
	XP_Bool postingAllowed = cd->response_code == MK_NNTP_RESPONSE_POSTING_ALLOWED;

    if(MK_NNTP_RESPONSE_TYPE(cd->response_code)!=MK_NNTP_RESPONSE_TYPE_OK)
      {
		ce->URL_s->error_msg  = NET_ExplainErrorDetails(MK_NNTP_ERROR_MESSAGE, cd->response_txt);

    	cd->next_state = NNTP_ERROR;
        cd->control_con->prev_cache = FALSE; /* to keep if from reconnecting */
        return MK_BAD_NNTP_CONNECTION;
      }

	cd->control_con->posting_allowed = postingAllowed; /* ###phil dead code */
#ifdef XPCOM_NEWSHOST
    cd->news_host->SetPostingAllowed(postingAllowed);
#else
	MSG_SetNewsHostPostingAllowed (cd->host, postingAllowed);
#endif
    
    cd->next_state = NNTP_SEND_MODE_READER;
    return(0);  /* good */
}

PRIVATE int
net_nntp_send_mode_reader (ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;

    PL_strcpy(cd->output_buffer, "MODE READER" CRLF);

    ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
	NNTP_LOG_WRITE(cd->output_buffer);

    cd->next_state = NNTP_RESPONSE;
    cd->next_state_after_response = NNTP_SEND_MODE_READER_RESPONSE;
    cd->pause_for_read = TRUE;

    return(ce->status);

}

PRIVATE int
net_nntp_send_mode_reader_response (ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
	cd->mode_reader_performed = TRUE;

	/* ignore the response code and continue
	 */
#ifdef XPCOM_NEWSHOST
    PRBool pushAuth;
    nsresult rv = cd->news_host->IsPushAuth(&pushAuth);
    if (NS_SUCCEEDED(rv) && pushAuth)
#else
	if (MSG_GetNewsHostPushAuth(cd->host))
#endif
		/* if the news host is set up to require volunteered (pushed) authentication,
		 * do that before we do anything else
		 */
		cd->next_state = NNTP_BEGIN_AUTHORIZE;
	else
		cd->next_state = SEND_LIST_EXTENSIONS;

	return(0);
}

PRIVATE int net_nntp_send_list_extensions (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;
    PL_strcpy(cd->output_buffer, "LIST EXTENSIONS" CRLF);
    ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
	NNTP_LOG_WRITE(cd->output_buffer);

	cd->next_state = NNTP_RESPONSE;
	cd->next_state_after_response = SEND_LIST_EXTENSIONS_RESPONSE;
	cd->pause_for_read = TRUE;
	return ce->status;
}

PRIVATE int net_nntp_send_list_extensions_response (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;

    if (MK_NNTP_RESPONSE_TYPE(cd->response_code) == MK_NNTP_RESPONSE_TYPE_OK)
	{
		char *line = NULL;
#ifdef XPCOM_NEWSHOST
        nsIMsgNewsHost *news_host = cd->news_host;
#else
		MSG_NewsHost *host = cd->host;
#endif

		ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
			&cd->data_buf_size, (Bool*)&cd->pause_for_read);

		if(ce->status == 0)
		{
			cd->next_state = NNTP_ERROR;
			cd->pause_for_read = FALSE;
			ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
			return MK_NNTP_SERVER_ERROR;
		}
		if (!line)
			return ce->status;  /* no line yet */
		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;
		}

		if ('.' != line[0])
#ifdef XPCOM_NEWSHOST
            news_host->AddExtension(line);
#else
			MSG_AddNewsExtension (host, line);
#endif
		else
		{
			/* tell libmsg that it's ok to ask this news host for extensions */
#ifdef XPCOM_NEWSHOST
          cd->news_host->SetSupportsExtensions(TRUE);
#else
			MSG_SupportsNewsExtensions (cd->host, TRUE);
#endif
			/* all extensions received */
			cd->next_state = SEND_LIST_SEARCHES;
			cd->pause_for_read = FALSE;
		}
	}
	else
	{
		/* LIST EXTENSIONS not recognized 
		 * tell libmsg not to ask for any more extensions and move on to
		 * the real NNTP command we were trying to do.
		 */
#ifdef XPCOM_NEWSHOST
      cd->news_host->SetSupportsExtensions(FALSE);
#else
		MSG_SupportsNewsExtensions (cd->host, FALSE);
#endif
		cd->next_state = SEND_FIRST_NNTP_COMMAND;
	}

	return ce->status;
}

PRIVATE int net_nntp_send_list_searches (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;
    
#ifdef XPCOM_NEWSHOST
    nsresult rv;
    PRBool searchable=FALSE;
    
    rv = cd->news_host->QueryExtension("SEARCH",&searchable);
    if (NS_SUCCEEDED(rv) && searchable)
#else
	if (MSG_QueryNewsExtension (cd->host, "SEARCH"))
#endif
	{
		PL_strcpy(cd->output_buffer, "LIST SEARCHES" CRLF);
		ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
		NNTP_LOG_WRITE(cd->output_buffer);

		cd->next_state = NNTP_RESPONSE;
		cd->next_state_after_response = SEND_LIST_SEARCHES_RESPONSE;
		cd->pause_for_read = TRUE;
	}
	else
	{
		/* since SEARCH isn't supported, move on to GET */
		cd->next_state = NNTP_GET_PROPERTIES;
		cd->pause_for_read = FALSE;
	}

	return ce->status;
}

PRIVATE int net_nntp_send_list_searches_response (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;

	char *line = NULL;
	ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
		&cd->data_buf_size, (Bool*)&cd->pause_for_read);

	if(ce->status == 0)
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return MK_NNTP_SERVER_ERROR;
	}
	if (!line)
		return ce->status;  /* no line yet */
	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;
	}

	if ('.' != line[0])
	{
#ifdef XPCOM_NEWSHOST
      cd->news_host->AddSearchableGroup(line);
#else
		MSG_AddSearchableGroup (cd->host, line);
#endif
	}
	else
	{
		/* all searchable groups received */
		/* LIST SRCHFIELDS is legal if the server supports the SEARCH extension, which */
		/* we already know it does */
		cd->next_state = NNTP_LIST_SEARCH_HEADERS;
		cd->pause_for_read = FALSE;
	}

	return ce->status;
}

PRIVATE int net_send_list_search_headers (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;
    PL_strcpy(cd->output_buffer, "LIST SRCHFIELDS" CRLF);
    ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
	NNTP_LOG_WRITE(cd->output_buffer);

	cd->next_state = NNTP_RESPONSE;
	cd->next_state_after_response = NNTP_LIST_SEARCH_HEADERS_RESPONSE;
	cd->pause_for_read = TRUE;

	return ce->status;
}

PRIVATE int net_send_list_search_headers_response (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;
#ifdef XPCOM_NEWSHOST
    nsIMsgNewsHost* news_host = cd->news_host;
#else
	MSG_NewsHost *host = cd->host;
#endif

	char *line = NULL;
	ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
		&cd->data_buf_size, (Bool*)&cd->pause_for_read);

	if(ce->status == 0)
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return MK_NNTP_SERVER_ERROR;
	}
	if (!line)
		return ce->status;  /* no line yet */
	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;
	}

	if ('.' != line[0])
#ifdef XPCOM_NEWSHOST
        news_host->AddSearchableHeader(line);
#else
		MSG_AddSearchableHeader (host, line);
#endif
	else
	{
		cd->next_state = NNTP_GET_PROPERTIES;
		cd->pause_for_read = FALSE;
	}

	return ce->status;
}

PRIVATE int nntp_get_properties (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;

#ifdef XPCOM_NEWSHOST
    nsresult rv;
    PRBool setget=FALSE;
    
    rv = cd->news_host->QueryExtension("SETGET",&setget);
    if (NS_SUCCEEDED(rv) && setget)
#else
	if (MSG_QueryNewsExtension (cd->host, "SETGET"))
#endif
	{
		PL_strcpy(cd->output_buffer, "GET" CRLF);
		ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
		NNTP_LOG_WRITE(cd->output_buffer);

		cd->next_state = NNTP_RESPONSE;
		cd->next_state_after_response = NNTP_GET_PROPERTIES_RESPONSE;
		cd->pause_for_read = TRUE;
	}
	else
	{
		/* since GET isn't supported, move on LIST SUBSCRIPTIONS */
		cd->next_state = SEND_LIST_SUBSCRIPTIONS;
		cd->pause_for_read = FALSE;
	}
	return ce->status;
}

PRIVATE int nntp_get_properties_response (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;

	char *line = NULL;
	ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
		&cd->data_buf_size, (Bool*)&cd->pause_for_read);

	if(ce->status == 0)
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return MK_NNTP_SERVER_ERROR;
	}
	if (!line)
		return ce->status;  /* no line yet */
	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;
	}

	if ('.' != line[0])
	{
		char *propertyName = PL_strdup(line);
		if (propertyName)
		{
			char *space = PL_strchr(propertyName, ' ');
			if (space)
			{
				char *propertyValue = space + 1;
				*space = '\0';
#ifdef XPCOM_NEWSHOST
                cd->news_host->AddPropertyForGet(propertyName, propertyValue);
#else
				MSG_AddPropertyForGet (cd->host,
					propertyName, propertyValue);
#endif
			}
			PR_Free(propertyName);
		}
	}
	else
	{
		/* all GET properties received, move on to LIST SUBSCRIPTIONS */
		cd->next_state = SEND_LIST_SUBSCRIPTIONS;
		cd->pause_for_read = FALSE;
	}

	return ce->status;
}

PRIVATE int net_send_list_subscriptions (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;
    
#if 0
#ifdef XPCOM_NEWSHOST
    nsresult rv;
    PRBool searchable=FALSE;
    rv = cd->news_host->QueryExtension("LISTSUBSCR",&listsubscr);
    if (NS_SUCCEEDED(rv) && listsubscr)
#else
	if (MSG_QueryNewsExtension (cd->host, "LISTSUBSCR"))
#endif
#else
	if (0)
#endif
	{
		PL_strcpy(cd->output_buffer, "LIST SUBSCRIPTIONS" CRLF);
		ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
		NNTP_LOG_WRITE(cd->output_buffer);

		cd->next_state = NNTP_RESPONSE;
		cd->next_state_after_response = SEND_LIST_SUBSCRIPTIONS_RESPONSE;
		cd->pause_for_read = TRUE;
	}
	else
	{
		/* since LIST SUBSCRIPTIONS isn't supported, move on to real work */
		cd->next_state = SEND_FIRST_NNTP_COMMAND;
		cd->pause_for_read = FALSE;
	}

	return ce->status;
}

PRIVATE int net_send_list_subscriptions_response (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;

	char *line = NULL;
	ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
		&cd->data_buf_size, (Bool*)&cd->pause_for_read);

	if(ce->status == 0)
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return MK_NNTP_SERVER_ERROR;
	}
	if (!line)
		return ce->status;  /* no line yet */
	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;
	}

	if ('.' != line[0])
	{
#if 0
		char *urlScheme;
		HG56946
		char *url = PR_smprintf ("%s//%s/%s", urlScheme, cd->control_con->hostname, line);
		if (url)
			MSG_AddSubscribedNewsgroup (cd->pane, url);
#endif
	}
	else
	{
		/* all default subscriptions received */
		cd->next_state = SEND_FIRST_NNTP_COMMAND;
		cd->pause_for_read = FALSE;
	}

	return ce->status;
}

/* figure out what the first command is and send it
 *
 * returns the status from the NETWrite
 */
PRIVATE int
net_send_first_nntp_command (ActiveEntry *ce)
{
    char *command=0;
    NewsConData * cd = (NewsConData *)ce->con_data;

	if (cd->type_wanted == ARTICLE_WANTED)
	  {
#ifdef XPCOM_NEWSGROUP
		const char *group = 0;
#endif
		uint32 number = 0;

#ifdef XPCOM_NEWSGROUP
        nsresult rv;
        nsIMsgNewsgroup *newsgroup;
        rv = cd->news_host->GetNewsGroupAndNumberOfID(cd->path,
                                                      &newsgroup, &number);
#else
		MSG_NewsGroupAndNumberOfID (cd->pane, cd->path,
									&group, &number);
#endif
		if (NS_SUCCEEDED(rv) && newsgroup && number)
		  {
			cd->article_num = number;
#ifdef XPCOM_NEWSGROUP
            cd->newsgroup = newsgroup;
#else
			StrAllocCopy (cd->group_name, group);
#endif

			if (cd->control_con->current_group && !PL_strcmp (cd->control_con->current_group, group))
			  cd->next_state = NNTP_SEND_ARTICLE_NUMBER;
			else
			  cd->next_state = NNTP_SEND_GROUP_FOR_ARTICLE;

			cd->pause_for_read = FALSE;
			return 0;
		  }
	  }

    if(cd->type_wanted == NEWS_POST && !ce->URL_s->post_data)
      {
		PR_ASSERT(0);
        return(-1);
      }
    else if(cd->type_wanted == NEWS_POST)
      {  /* posting to the news group */
        StrAllocCopy(command, "POST");
      }
    else if(cd->type_wanted == READ_NEWS_RC)
      {
		if(ce->URL_s->method == URL_POST_METHOD ||
								PL_strchr(ce->URL_s->address, '?'))
        	cd->next_state = NEWS_NEWS_RC_POST;
		else
        	cd->next_state = NEWS_DISPLAY_NEWS_RC;
		return(0);
      } 
	else if(cd->type_wanted == NEW_GROUPS)
	  {
#ifdef XPCOM_NEWSHOST
        PRTime last_update;
        nsresult rv;

        rv = cd->news_host->GetLastUpdatedTime(&last_update);
#else
		time_t last_update =
			MSG_NewsgroupsLastUpdatedTime(cd->host);
#endif
		char small_buf[64];
        PRExplodedTime  expandedTime;

		if(!last_update)
	 	  {
			
			ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_NEWSGROUP_SCAN_ERROR);
			cd->next_state = NEWS_ERROR;
			return(MK_INTERRUPTED);
		  }
	
		/* subtract some hours just to be sure */
		last_update -= NEWGROUPS_TIME_OFFSET;

        {
           int64  secToUSec, timeInSec, timeInUSec;
           LL_I2L(timeInSec, last_update);
           LL_I2L(secToUSec, PR_USEC_PER_SEC);
           LL_MUL(timeInUSec, timeInSec, secToUSec);
           PR_ExplodeTime(timeInUSec, PR_LocalTimeParameters, &expandedTime);
        }
		PR_FormatTimeUSEnglish(small_buf, sizeof(small_buf), 
                               "NEWGROUPS %y%m%d %H%M%S", &expandedTime);
		
        StrAllocCopy(command, small_buf);

	  }
    else if(cd->type_wanted == LIST_WANTED)
    {
		
		cd->use_fancy_newsgroup_listing = FALSE;
#ifdef XPCOM_NEWSHOST
        PRTime last_update;
        nsresult rv = cd->news_host->GetLastUpdatedTime(&last_update);
        
        if (NS_SUCCEEDED(rv) && last_update!=0)
#else
		if (MSG_NewsgroupsLastUpdatedTime(cd->host))
#endif
		{
			cd->next_state = DISPLAY_NEWSGROUPS;
        	return(0);
	    }
		else
		{
#ifdef BUG_21013
			if(!FE_Confirm(ce->window_id, XP_GetString(XP_CONFIRM_SAVE_NEWSGROUPS)))
	  		  {
				cd->next_state = NEWS_ERROR;
				return(MK_INTERRUPTED);
	  		  }
#endif /* BUG_21013 */

#ifdef XPCOM_NEWSHOST
    nsresult rv;
    PRBool xactive=FALSE;
    rv = cd->news_host->QueryExtension("XACTIVE",&xactive);
    if (NS_SUCCEEDED(rv) && xactive)
#else
			if (MSG_QueryNewsExtension(cd->host, "XACTIVE"))
#endif
			{
				StrAllocCopy(command, "LIST XACTIVE");
				cd->use_fancy_newsgroup_listing = TRUE;
			}
			else
			{
       			StrAllocCopy(command, "LIST");
			}
		}
    } 
    else if(cd->type_wanted == GROUP_WANTED) 
    {
        /* Don't use MKParse because the news: access URL doesn't follow traditional
         * rules. For instance, if the article reference contains a '#',
         * the rest of it is lost.
         */
        char * slash;
#ifdef XPCOM_NEWSGROUP
        char * group_name;
        nsresult rv;
#endif

        StrAllocCopy(command, "GROUP ");
#ifdef XPCOM_NEWSGROUP
        rv = cd->newsgroup->GetName(&group_name);
        slash = PL_strchr(group_name, '/');
#else
        slash = PL_strchr(cd->group_name, '/');  /* look for a slash */
#endif
        cd->first_art = 0;
        cd->last_art = 0;
        if (slash)
          {
            *slash = '\0';
            (void) sscanf(slash+1, "%d-%d", &cd->first_art, &cd->last_art);
          }

#ifdef XPCOM_NEWSGROUP
        StrAllocCopy (cd->control_con->current_group, group_name);
#else
		StrAllocCopy (cd->control_con->current_group, cd->group_name);
#endif
        StrAllocCat(command, cd->control_con->current_group);
      }
	else if (cd->type_wanted == SEARCH_WANTED)
	{
#ifdef XPCOM_NEWSHOST
    nsresult rv;
    PRBool searchable=FALSE;
    rv = cd->news_host->QueryExtension("SEARCH", &searchable);
    if (NS_SUCCEEDED(rv) && searchable)
#else
		if (MSG_QueryNewsExtension(cd->host, "SEARCH"))
#endif
		{
			/* use the SEARCH extension */
			char *slash = PL_strchr (cd->command_specific_data, '/');
			if (slash)
			{
				char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1);
				if (allocatedCommand)
				{
					StrAllocCopy (command, allocatedCommand);
					PR_Free(allocatedCommand);
				}
			}
			cd->next_state = NNTP_RESPONSE;
			cd->next_state_after_response = NNTP_SEARCH_RESPONSE;
		}
		else
		{
            nsresult rv;
            char *group_name;
            
			/* for XPAT, we have to GROUP into the group before searching */
			StrAllocCopy (command, "GROUP ");
#ifdef XPCOM_NEWSGROUP
            rv = cd->newsgroup->GetName(&group_name);
            StrAllocCat (command, group_name);
#else
			StrAllocCat (command, cd->group_name);
#endif
			cd->next_state = NNTP_RESPONSE;
			cd->next_state_after_response = NNTP_XPAT_SEND;
		}
	}
	else if (cd->type_wanted == PRETTY_NAMES_WANTED)
	{
#ifdef XPCOM_NEWSHOST
    nsresult rv;
    PRBool listpretty=FALSE;
    rv = cd->news_host->QueryExtension("LISTPRETTY",&listpretty);
    if (NS_SUCCEEDED(rv) && listpretty)
#else
		if (MSG_QueryNewsExtension(cd->host, "LISTPRETTY"))
#endif
		{
			cd->next_state = NNTP_LIST_PRETTY_NAMES;
			return 0;
		}
		else
		{
			PR_ASSERT(FALSE);
			cd->next_state = NNTP_ERROR;
		}
	}
	else if (cd->type_wanted == PROFILE_WANTED)
	{
		char *slash = PL_strchr (cd->command_specific_data, '/');
		if (slash)
		{
			char *allocatedCommand = MSG_UnEscapeSearchUrl (slash + 1);
			if (allocatedCommand)
			{
				StrAllocCopy (command, allocatedCommand);
				PR_Free(allocatedCommand);
			}
		}
		cd->next_state = NNTP_RESPONSE;
		if (PL_strstr(ce->URL_s->address, "PROFILE NEW"))
			cd->next_state_after_response = NNTP_PROFILE_ADD_RESPONSE;
		else
			cd->next_state_after_response = NNTP_PROFILE_DELETE_RESPONSE;
	}
	else if (cd->type_wanted == IDS_WANTED)
	{
#ifndef XPCOM_NEWSGROUP
        char *slash = PL_strchr(cd->group_name, '/');  /* look for a slash */
        if (slash)
            *slash = '\0';
#endif
		cd->next_state = NNTP_LIST_GROUP;
		return 0;
	}
    else  /* article or cancel */
	{
		if (cd->type_wanted == CANCEL_WANTED)
			StrAllocCopy(command, "HEAD ");
		else
			StrAllocCopy(command, "ARTICLE ");
		if (*cd->path != '<')
			StrAllocCat(command,"<");
		StrAllocCat(command, cd->path);
		if (PL_strchr(command+8, '>')==0) 
			StrAllocCat(command,">");
	}

    StrAllocCat(command, CRLF);
    ce->status = (int) NET_BlockingWrite(ce->socket, command, PL_strlen(command));
	NNTP_LOG_WRITE(command);
    PR_Free(command);

	cd->next_state = NNTP_RESPONSE;
	if (cd->type_wanted != SEARCH_WANTED && cd->type_wanted != PROFILE_WANTED)
		cd->next_state_after_response = SEND_FIRST_NNTP_COMMAND_RESPONSE;
	cd->pause_for_read = TRUE;
    return(ce->status);

} /* sent first command */


/* interprets the server response from the first command sent
 *
 * returns negative if the server responds unexpectedly
 */
PRIVATE int
net_send_first_nntp_command_response (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
	int major_opcode = MK_NNTP_RESPONSE_TYPE(cd->response_code);

	if((major_opcode == MK_NNTP_RESPONSE_TYPE_CONT &&
        cd->type_wanted == NEWS_POST)
	 	|| (major_opcode == MK_NNTP_RESPONSE_TYPE_OK &&
            cd->type_wanted != NEWS_POST) )
      {

        cd->next_state = SETUP_NEWS_STREAM;
		cd->some_protocol_succeeded = TRUE;

        return(0);  /* good */
      }
    else
      {
		if (cd->response_code == MK_NNTP_RESPONSE_GROUP_NO_GROUP &&
            cd->type_wanted == GROUP_WANTED)
#ifdef XPCOM_NEWSHOST
            cd->news_host->GroupNotFound(cd->control_con->current_group,
                                         TRUE /* opening */);
#else
			MSG_GroupNotFound(cd->pane, cd->host,
								cd->control_con->current_group, TRUE /* opening group */);
#endif
		return net_display_html_error_state(ce);
      }

	/* start the graph progress indicator
     */
    FE_GraphProgressInit(ce->window_id, ce->URL_s, ce->URL_s->content_length);
    cd->destroy_graph_progress = TRUE;  /* we will need to destroy it */
    cd->original_content_length = ce->URL_s->content_length;

	return(ce->status);
}


PRIVATE int
net_send_group_for_article (ActiveEntry * ce)
{
  NewsConData * cd = (NewsConData *)ce->con_data;

#ifdef XPCOM_NEWSGROUP
  nsresult rv;
  PR_FREEIF(cd->control_con->current_group);
  rv = cd->newsgroup->GetName(&cd->control_con->current_group);
  PR_ASSERT(NS_SUCCEEEDED(rv));
#else
  StrAllocCopy (cd->control_con->current_group, cd->group_name);
#endif
  
  PR_snprintf(cd->output_buffer, 
			OUTPUT_BUFFER_SIZE, 
			"GROUP %.512s" CRLF, 
			cd->control_con->current_group);

   ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,
									  PL_strlen(cd->output_buffer));
	NNTP_LOG_WRITE(cd->output_buffer);

    cd->next_state = NNTP_RESPONSE;
    cd->next_state_after_response = NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE;
    cd->pause_for_read = TRUE;

    return(ce->status);
}

PRIVATE int
net_send_group_for_article_response (ActiveEntry * ce)
{
  NewsConData * cd = (NewsConData *)ce->con_data;

  /* ignore the response code and continue
   */
  cd->next_state = NNTP_SEND_ARTICLE_NUMBER;

  return(0);
}


PRIVATE int
net_send_article_number (ActiveEntry * ce)
{
  NewsConData * cd = (NewsConData *)ce->con_data;

  PR_snprintf(cd->output_buffer, 
			OUTPUT_BUFFER_SIZE, 
			"ARTICLE %lu" CRLF, 
			cd->article_num);

  ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,
									  PL_strlen(cd->output_buffer));
	NNTP_LOG_WRITE(cd->output_buffer);

    cd->next_state = NNTP_RESPONSE;
    cd->next_state_after_response = SEND_FIRST_NNTP_COMMAND_RESPONSE;
    cd->pause_for_read = TRUE;

    return(ce->status);
}




static char *
news_generate_news_url_fn (const char *dest, void *closure,
						   MimeHeaders *headers)
{
  ActiveEntry *ce = (ActiveEntry *) closure;
  /* NOTE: you can't use NewsConData here, because ce might refer
	 to a file in the disk cache rather than an NNTP connection. */
  char *prefix = NET_ParseURL(ce->URL_s->address,
							  (GET_PROTOCOL_PART | GET_HOST_PART));
  char *new_dest = NET_Escape (dest, URL_XALPHAS);
  char *result = (char *) PR_Malloc (PL_strlen (prefix) +
									(new_dest ? PL_strlen (new_dest) : 0)
									+ 40);
  if (result && prefix)
	{
	  PL_strcpy (result, prefix);
	  if (prefix[PL_strlen(prefix) - 1] != ':')
		/* If there is a host, it must be terminated with a slash. */
		PL_strcat (result, "/");
	  PL_strcat (result, new_dest);
	}
  FREEIF (prefix);
  FREEIF (new_dest);
  return result;
}

static char *
news_generate_reference_url_fn (const char *dest, void *closure,
								MimeHeaders *headers)
{
  return news_generate_news_url_fn (dest, closure, headers);
}



/* Initiates the stream and sets state for the transfer
 *
 * returns negative if unable to setup stream
 */
PRIVATE int
net_setup_news_stream (ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
    
    if (cd->type_wanted == NEWS_POST)
	  {
        NET_ClearReadSelect(ce->window_id, ce->socket);
        NET_SetConnectSelect(ce->window_id, ce->socket);
#ifdef XP_WIN
		cd->calling_netlib_all_the_time = TRUE;
#if 0
        /* This should be handled by NET_SetCallNetlibAllTheTime */
		net_call_all_the_time_count++;
#endif
		NET_SetCallNetlibAllTheTime(ce->window_id,"mknews");
#endif /* XP_WIN */

		ce->con_sock = ce->socket;
        cd->next_state = NNTP_SEND_POST_DATA;

		NET_Progress(ce->window_id, XP_GetString(MK_MSG_DELIV_NEWS));
	  }
    else if(cd->type_wanted == LIST_WANTED)
	{
		if (cd->use_fancy_newsgroup_listing)
			cd->next_state = NNTP_LIST_XACTIVE_RESPONSE;
		else
			cd->next_state = NNTP_READ_LIST_BEGIN;
	}
    else if(cd->type_wanted == GROUP_WANTED)
        cd->next_state = NNTP_XOVER_BEGIN;
	else if(cd->type_wanted == NEW_GROUPS)
		cd->next_state = NNTP_NEWGROUPS_BEGIN;
    else if(cd->type_wanted == ARTICLE_WANTED ||
			cd->type_wanted == CANCEL_WANTED)
	    cd->next_state = NNTP_BEGIN_ARTICLE;
	else if (cd->type_wanted == SEARCH_WANTED)
		cd->next_state = NNTP_XPAT_SEND;
	else if (cd->type_wanted == PRETTY_NAMES_WANTED)
		cd->next_state = NNTP_LIST_PRETTY_NAMES;
	else if (cd->type_wanted == PROFILE_WANTED)
	{
		if (PL_strstr(ce->URL_s->address, "PROFILE NEW"))
			cd->next_state = NNTP_PROFILE_ADD;
		else
			cd->next_state = NNTP_PROFILE_DELETE;
	}
	else
	  {
		PR_ASSERT(0);
		return -1;
	  }

   return(0); /* good */
}

/* This doesn't actually generate HTML - but it is our hook into the 
   message display code, so that we can get some values out of it
   after the headers-to-be-displayed have been parsed.
 */
static char *
news_generate_html_header_fn (const char *dest, void *closure,
							  MimeHeaders *headers)
{
  ActiveEntry *ce = (ActiveEntry *) closure;
  /* NOTE: you can't always use NewsConData here if, because ce might
	 refer to a file in the disk cache rather than an NNTP connection. */
  NewsConData *cd = 0;

  PR_ASSERT(ce->protocol == NEWS_TYPE_URL ||
			ce->protocol == FILE_CACHE_TYPE_URL ||
			ce->protocol == MEMORY_CACHE_TYPE_URL);
  if (ce->protocol == NEWS_TYPE_URL)
	cd = (NewsConData *)ce->con_data;

  if (cd && cd->type_wanted == CANCEL_WANTED)
	{
	  /* Save these for later (used in NEWS_DO_CANCEL state.) */
	  cd->cancel_from =
		MimeHeaders_get(headers, HEADER_FROM, FALSE, FALSE);
	  cd->cancel_newsgroups =
		MimeHeaders_get(headers, HEADER_NEWSGROUPS, FALSE, TRUE);
	  cd->cancel_distribution =
		MimeHeaders_get(headers, HEADER_DISTRIBUTION, FALSE, FALSE);
	  cd->cancel_id =
		MimeHeaders_get(headers, HEADER_MESSAGE_ID, FALSE, FALSE);
	}
  else
	{
	  MSG_Pane *messagepane = ce->URL_s->msg_pane;
	  if (!messagepane)
	  {
		NNTP_LOG_NOTE(("news_generate_html_header_fn: url->msg_pane NULL for URL: %s", ce->URL_s->address));
	    messagepane = MSG_FindPane(ce->window_id, MSG_MESSAGEPANE);
	  }
      PR_ASSERT (!cd || cd->type_wanted == ARTICLE_WANTED);

	  if (messagepane)
		MSG_ActivateReplyOptions (messagepane, headers);
	}
  return 0;
}



XP_Bool NET_IsNewsMessageURL (const char *url);

int
net_InitializeNewsFeData (ActiveEntry * ce)
{
  MimeDisplayOptions *opt;

  if (!NET_IsNewsMessageURL (ce->URL_s->address))
	{
	  PR_ASSERT(0);
	  return -1;
	}

  if (ce->URL_s->fe_data)
	{
	  PR_ASSERT(0);
	  return -1;
	}

  opt = PR_NEW (MimeDisplayOptions);
  if (!opt) return MK_OUT_OF_MEMORY;
  memset (opt, 0, sizeof(*opt));

  opt->generate_reference_url_fn      = news_generate_reference_url_fn;
  opt->generate_news_url_fn           = news_generate_news_url_fn;
  opt->generate_header_html_fn		  = 0;
  opt->generate_post_header_html_fn	  = news_generate_html_header_fn;
  opt->html_closure					  = ce;

  ce->URL_s->fe_data = opt;
  return 0;
}


PRIVATE int
net_begin_article(ActiveEntry * ce)
{
  NewsConData * cd = (NewsConData *)ce->con_data;
  if (cd->type_wanted != ARTICLE_WANTED &&
	  cd->type_wanted != CANCEL_WANTED)
	return 0;

  /*  Set up the HTML stream
   */ 
  FREEIF (ce->URL_s->content_type);
  ce->URL_s->content_type = PL_strdup (MESSAGE_RFC822);

#ifdef NO_ARTICLE_CACHEING
  ce->format_out = CLEAR_CACHE_BIT (ce->format_out);
#endif

  if (cd->type_wanted == CANCEL_WANTED)
	{
	  PR_ASSERT(ce->format_out == FO_PRESENT);
	  ce->format_out = FO_PRESENT;
	}

  /* Only put stuff in the fe_data if this URL is going to get
	 passed to MIME_MessageConverter(), since that's the only
	 thing that knows what to do with this structure. */
  if (CLEAR_CACHE_BIT(ce->format_out) == FO_PRESENT)
	{
	  ce->status = net_InitializeNewsFeData (ce);
	  if (ce->status < 0)
		{
		  /* #### what error message? */
		  return ce->status;
		}
	}

  cd->stream = NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id);
  PR_ASSERT (cd->stream);
  if (!cd->stream) return -1;

  cd->next_state = NNTP_READ_ARTICLE;

  return 0;
}


PRIVATE int
net_news_begin_authorize(ActiveEntry * ce)
{
	char * command = 0;
    NewsConData * cd = (NewsConData *)ce->con_data;
	char * username = 0;
	char * cp;

#ifdef CACHE_NEWSGRP_PASSWORD
	/* reuse cached username from newsgroup folder info*/
	if (cd->pane && 
		(!net_news_last_username_probably_valid ||
		 (last_username_hostname && 
		  PL_strcasecmp(last_username_hostname, cd->control_con->hostname)))) {
#ifdef XPCOM_NEWSGROUP
      cd->newsgroup->GetUsername(&username);
#else
	  username = HG63218 (MSG_GetNewsgroupUsername(cd->pane));
#endif
	  if (username && last_username &&
		  !PL_strcmp (username, last_username) &&
		  (cd->previous_response_code == MK_NNTP_RESPONSE_AUTHINFO_OK || 
		   cd->previous_response_code == MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_OK ||
		   cd->previous_response_code == MK_NNTP_RESPONSE_GROUP_SELECTED)) {
		FREEIF (username);
#ifdef XPCOM_NEWSGROUP
        cd->newsgroup->SetUsername(NULL);
        cd->newsgroup->SetPassword(NULL);
#else
		MSG_SetNewsgroupUsername(cd->pane, NULL);
		MSG_SetNewsgroupPassword(cd->pane, NULL);
#endif
	  }
	}
#endif
	
	if (cd->pane) 
	{
	/* Following a snews://username:password@newhost.domain.com/newsgroup.topic
	 * backend calls MSG_Master::FindNewsHost() to locate the folderInfo and setting 
	 * the username/password to the newsgroup folderInfo
	 */
#ifdef XPCOM_NEWSGROUP
      cd->newsgroup->GetUsername(&username);
#else
	  username = HG63218 (MSG_GetNewsgroupUsername(cd->pane));
#endif
	  if (username && *username)
	  {
		StrAllocCopy(last_username, username);
		StrAllocCopy(last_username_hostname, cd->control_con->hostname);
		/* use it for only once */
#ifdef XPCOM_NEWSGROUP
        cd->newsgroup->SetUsername(NULL);
#else
		MSG_SetNewsgroupUsername(cd->pane, NULL);
#endif
	  }
	  else {
		  /* empty username; free and clear it so it will work with
		   * our logic
		   */
		  FREEIF(username);
	  }
	}

	/* If the URL/cd->control_con->hostname contains @ this must be triggered
	 * from the bookmark. Use the embed username if we could.
	 */
	if ((cp = PL_strchr(cd->control_con->hostname, '@')) != NULL)
	  {
		/* in this case the username and possibly
		 * the password are in the URL
		 */
		char * colon;
		*cp = '\0';

		colon = PL_strchr(cd->control_con->hostname, ':');
		if(colon)
			*colon = '\0';

		StrAllocCopy(username, cd->control_con->hostname);
		StrAllocCopy(last_username, cd->control_con->hostname);
		StrAllocCopy(last_username_hostname, cp+1);

		*cp = '@';

		if(colon)
			*colon = ':';
	  }
	/* reuse global saved username if we think it is
	 * valid
	 */
    if (!username && net_news_last_username_probably_valid)
	  {
		if( last_username_hostname &&
			!PL_strcasecmp(last_username_hostname, cd->control_con->hostname) )
			StrAllocCopy(username, last_username);
		else
			net_news_last_username_probably_valid = FALSE;
	  }


	if (!username) {
#if defined(CookiesAndSignons)
	  username = SI_Prompt(ce->window_id,
			       XP_GetString(XP_PROMPT_ENTER_USERNAME),
                               "",
			       cd->control_con->hostname);

#else
	  username = FE_Prompt(ce->window_id,
						   XP_GetString(XP_PROMPT_ENTER_USERNAME),
						   username ? username : "");
#endif
	  
	  /* reset net_news_last_username_probably_valid to false */
	  net_news_last_username_probably_valid = FALSE;
	  if(!username) {
		ce->URL_s->error_msg = 
		  NET_ExplainErrorDetails( MK_NNTP_AUTH_FAILED, "Aborted by user");
		return(MK_NNTP_AUTH_FAILED);
	  }
	  else {
		StrAllocCopy(last_username, username);
		StrAllocCopy(last_username_hostname, cd->control_con->hostname);
	  }
	}

#ifdef CACHE_NEWSGRP_PASSWORD
#ifdef XPCOM_NEWSGROUP
    if (NS_SUCCEEDED(cd->newsgroup->GetUsername(&username)) {
#else
	if (cd->pane && !MSG_GetNewsgroupUsername(cd->pane)) {
#endif
	  munged_username = HG64643 (username);
#ifdef XPCOM_NEWSGROUP
      cd->newsgroup->SetUsername(munged_username);
#else
	  MSG_SetNewsgroupUsername(cd->pane, munged_username);
#endif
	  PR_FreeIF(munged_username);
	}
#endif

	StrAllocCopy(command, "AUTHINFO user ");
	StrAllocCat(command, username);
	StrAllocCat(command, CRLF);

	ce->status = (int) NET_BlockingWrite(ce->socket, command, PL_strlen(command));
	NNTP_LOG_WRITE(command);

	FREE(command);
	FREE(username);

	cd->next_state = NNTP_RESPONSE;
	cd->next_state_after_response = NNTP_AUTHORIZE_RESPONSE;;

	cd->pause_for_read = TRUE;

	return ce->status;
}

PRIVATE int
net_news_authorize_response(ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;

    if (MK_NNTP_RESPONSE_AUTHINFO_OK == cd->response_code ||
        MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_OK == cd->response_code) 
	  {
		/* successful login */
        nsresult rv;
        PRBool pushAuth;
		/* If we're here because the host demanded authentication before we
		 * even sent a single command, then jump back to the beginning of everything
		 */
        rv = cd->news_host->IsPushAuth(&pushAuth);
        
        if (!cd->mode_reader_performed)
			cd->next_state = NNTP_SEND_MODE_READER;
		/* If we're here because the host needs pushed authentication, then we 
		 * should jump back to SEND_LIST_EXTENSIONS
		 */
#ifdef XPCOM_NEWSHOST
        else if (NS_SUCCEEDED(rv) && pushAuth)
#else
		else if (MSG_GetNewsHostPushAuth(cd->host))
#endif
			cd->next_state = SEND_LIST_EXTENSIONS;
		else
			/* Normal authentication */
			cd->next_state = SEND_FIRST_NNTP_COMMAND;

		net_news_last_username_probably_valid = TRUE;
		return(0); 
	  }
	else if (MK_NNTP_RESPONSE_AUTHINFO_CONT == cd->response_code)
	  {
		/* password required
		 */	
		char * command = 0;
		char * password = 0;
		char * cp;

		if (cd->pane)
		{
#ifdef XPCOM_NEWSGROUP
            cd->newsgroup->GetPassword(&password);
            password = HG63218 (password);
            cd->newsgroup->SetPassword(NULL);
#else
			password = HG63218 (MSG_GetNewsgroupPassword(cd->pane));
			/* use it only once */
			MSG_SetNewsgroupPassword(cd->pane, NULL);
#endif
		}

        if (net_news_last_username_probably_valid 
			&& last_password
			&& last_password_hostname
			&& !PL_strcasecmp(last_password_hostname, cd->control_con->hostname))
          {
#ifdef CACHE_NEWSGRP_PASSWORD
			if (cd->pane)
#ifdef XPCOM_NEWSGROUP
            cd->newsgroup->GetPassword(&password);
            password = HG63218 (password);
#else
				password = HG63218(MSG_GetNewsgroupPassword(cd->pane));
#endif
#else
            StrAllocCopy(password, last_password);
#endif
          }
        else if ((cp = PL_strchr(cd->control_con->hostname, '@')) != NULL)
          {
            /* in this case the username and possibly
             * the password are in the URL
             */
            char * colon;
            *cp = '\0';
    
            colon = PL_strchr(cd->control_con->hostname, ':');
            if(colon)
			  {
                *colon = '\0';
    
            	StrAllocCopy(password, colon+1);
            	StrAllocCopy(last_password, colon+1);
            	StrAllocCopy(last_password_hostname, cp+1);

                *colon = ':';
			  }
    
            *cp = '@';
    
          }
		if (!password) {
		  password = 
#if defined(CookiesAndSignons)
		  password = SI_PromptPassword
		      (ce->window_id,
		      XP_GetString
			  (XP_PLEASE_ENTER_A_PASSWORD_FOR_NEWS_SERVER_ACCESS),
		      cd->control_con->hostname,
		      TRUE, TRUE);
#else
			FE_PromptPassword(ce->window_id, XP_GetString( 
			           XP_PLEASE_ENTER_A_PASSWORD_FOR_NEWS_SERVER_ACCESS ) );
#endif
		  net_news_last_username_probably_valid = FALSE;
		}
		  
		if(!password)	{
		  ce->URL_s->error_msg = 
			NET_ExplainErrorDetails(MK_NNTP_AUTH_FAILED, "Aborted by user");
		  return(MK_NNTP_AUTH_FAILED);
		}
		else {
		  StrAllocCopy(last_password, password);
		  StrAllocCopy(last_password_hostname, cd->control_con->hostname);
		}

#ifdef CACHE_NEWSGRP_PASSWORD
#ifdef XPCOM_NEWSGROUP
        char *garbage_password;
        nsresult rv;
        rv = cd->newsgroup->GetPassword(&garbage_password);
        if (!NS_SUCCEEDED(rv)) {
          PR_Free(garbage_password);
          munged_password = HG64643(password);
          cd->newsgroup->SetPassword(munged_password);
#else
		if (cd->pane && !MSG_GetNewsgroupPassword(cd->pane)) {
		  munged_password = HG64643(password);
		  MSG_SetNewsgroupPassword(cd->pane, munged_password);
#endif
		  PR_FreeIF(munged_password);
		}
#endif

		StrAllocCopy(command, "AUTHINFO pass ");
		StrAllocCat(command, password);
		StrAllocCat(command, CRLF);
	
		ce->status = (int) NET_BlockingWrite(ce->socket, command, PL_strlen(command));
		NNTP_LOG_WRITE(command);

		FREE(command);
		FREE(password);

		cd->next_state = NNTP_RESPONSE;
		cd->next_state_after_response = NNTP_PASSWORD_RESPONSE;
		cd->pause_for_read = TRUE;

		return ce->status;
	  }
	else
	  {
		ce->URL_s->error_msg = NET_ExplainErrorDetails(
									MK_NNTP_AUTH_FAILED,
									cd->response_txt ? cd->response_txt : "");
#ifdef CACHE_NEWSGRP_PASSWORD
		if (cd->pane)
#ifdef XPCOM_NEWSGROUP
          cd->newsgroup->SetUsername(NULL);
#else
		  MSG_SetNewsgroupUsername(cd->pane, NULL);
#endif
#endif
		net_news_last_username_probably_valid = FALSE;

        return(MK_NNTP_AUTH_FAILED);
	  }
		
	PR_ASSERT(0); /* should never get here */
	return(-1);

}

PRIVATE int
net_news_password_response(ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;

    if (MK_NNTP_RESPONSE_AUTHINFO_OK == cd->response_code ||
        MK_NNTP_RESPONSE_AUTHINFO_SIMPLE_OK == cd->response_code) 
	  {
        /* successful login */
        nsresult rv = NS_OK;
        PRBool pushAuth;
		/* If we're here because the host demanded authentication before we
		 * even sent a single command, then jump back to the beginning of everything
		 */
        rv = cd->news_host->IsPushAuth(&pushAuth);
        
		if (!cd->mode_reader_performed)
			cd->next_state = NNTP_SEND_MODE_READER;
		/* If we're here because the host needs pushed authentication, then we 
		 * should jump back to SEND_LIST_EXTENSIONS
		 */
#ifdef XPCOM_NEWSHOST
        else if (NS_SUCCEEDED(rv) && pushAuth)
#else
		else if (MSG_GetNewsHostPushAuth(cd->host))
#endif
			cd->next_state = SEND_LIST_EXTENSIONS;
		else
			/* Normal authentication */
			cd->next_state = SEND_FIRST_NNTP_COMMAND;

		net_news_last_username_probably_valid = TRUE;
#ifdef XPCOM_XOVER
        rv = cd->xover_parser->Reset();
#else
		MSG_ResetXOVER( ce->URL_s->msg_pane, &cd->xover_parse_state );
#endif
        return(0);
	  }
	else
	  {
		ce->URL_s->error_msg = NET_ExplainErrorDetails(
									MK_NNTP_AUTH_FAILED,
									cd->response_txt ? cd->response_txt : "");
#ifdef CACHE_NEWSGRP_PASSWORD
		if (cd->pane)
#ifdef XPCOM_NEWSGROUP
          cd->newsgroup->SetPassword(NULL);
#else
		  MSG_SetNewsgroupPassword(cd->pane, NULL);
#endif
#endif
        return(MK_NNTP_AUTH_FAILED);
	  }
		
	PR_ASSERT(0); /* should never get here */
	return(-1);
}

PRIVATE int
net_display_newsgroups (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;

	cd->next_state = NEWS_DONE;
    cd->pause_for_read = FALSE;

	NNTP_LOG_NOTE(("about to display newsgroups. path: %s",cd->path));

#if 0
	/* #### Now ignoring "news:alt.fan.*"
	   Need to open the root tree of the default news host and keep
	   opening one child at each level until we've exhausted the
	   wildcard...
	 */
	if(rv < 0)
       return(rv);  
	else
#endif
       return(MK_DATA_LOADED);  /* all finished */
}

PRIVATE int
net_newgroups_begin (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;

	cd->next_state = NNTP_NEWGROUPS;
	NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_NEWSGROUP));

	ce->bytes_received = 0;

	return(ce->status);

}

PRIVATE int
net_process_newgroups (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
	char *line, *s, *s1=NULL, *s2=NULL, *flag=NULL;
	int32 oldest, youngest;

    ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
                                        &cd->data_buf_size, (Bool*)&cd->pause_for_read);

    if(ce->status == 0)
      {
        cd->next_state = NNTP_ERROR;
        cd->pause_for_read = FALSE;
        ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
        return(MK_NNTP_SERVER_ERROR);
      }

    if(!line)
        return(ce->status);  /* no line yet */

    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;
      }

    /* End of list? 
	 */
    if (line[0]=='.' && line[1]=='\0')
      {
		cd->pause_for_read = FALSE;
#ifdef XPCOM_NEWSHOST
    nsresult rv;
    PRBool xactive=FALSE;
    rv = cd->news_host->QueryExtension("XACTIVE",&xactive);
    if (NS_SUCCEEDED(rv) && xactive)
#else
		if (MSG_QueryNewsExtension(cd->host, "XACTIVE"))
#endif
		{
#ifdef XPCOM_NEWSHOST
          nsresult rv;
          rv = cd->news_host->GetFirstGroupNeedingExtraInfo(&cd->newsgroup);
#else
			cd->group_name = MSG_GetFirstGroupNeedingExtraInfo(cd->host);
#endif
			if (NS_SUCCEEDED(rv) && cd->newsgroup)
			{
				cd->next_state = NNTP_LIST_XACTIVE;
#ifdef DEBUG_bienvenu1
				PR_LogPrint("listing xactive for %s\n", cd->group_name);
#endif
				return 0;
			}
		}
		cd->next_state = NEWS_DONE;

		if(ce->bytes_received == 0)
		  {
			/* #### no new groups */
		  }

		if(ce->status > 0)
        	return MK_DATA_LOADED;
		else
        	return ce->status;
      }
    else if (line [0] == '.' && line [1] == '.')
      /* The NNTP server quotes all lines beginning with "." by doubling it. */
      line++;

    /* almost correct
     */
    if(ce->status > 1)
      {
        ce->bytes_received += ce->status;
        FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
      }

    /* format is "rec.arts.movies.past-films 7302 7119 y"
	 */
	s = PL_strchr (line, ' ');
	if (s)
	  {
		*s = 0;
		s1 = s+1;
		s = PL_strchr (s1, ' ');
		if (s)
		  {
			*s = 0;
			s2 = s+1;
			s = PL_strchr (s2, ' ');
			if (s)
			{
			  *s = 0;
			  flag = s+1;
			}
		  }
	  }
	youngest = s2 ? atol(s1) : 0;
	oldest   = s1 ? atol(s2) : 0;

	ce->bytes_received++;  /* small numbers of groups never seem
						   * to trigger this
						   */

#ifdef XPCOM_NEWSHOST
    cd->news_host->AddNewNewsgroup(line, oldest, youngest, flag, PR_FALSE);
#else
	MSG_AddNewNewsGroup(ce->URL_s->msg_pane, cd->host,
						line, oldest, youngest, flag, FALSE);
#endif
#ifdef XPCOM_NEWSHOST
    nsresult rv;
    PRBool xactive=FALSE;
    rv = cd->news_host->QueryExtension("XACTIVE",&xactive);
    if (NS_SUCCEEDED(rv) && xactive)
#else
	if (MSG_QueryNewsExtension(cd->host, "XACTIVE"))
#endif
	{
#ifdef XPCOM_NEWSHOST
      cd->news_host->SetGroupNeedsExtraInfo(line, TRUE);
#else
		MSG_SetGroupNeedsExtraInfo(cd->host, line, TRUE);
#endif
	}
    return(ce->status);
}

		
/* Ahhh, this like print's out the headers and stuff
 *
 * always returns 0
 */
PRIVATE int
net_read_news_list_begin (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;

    cd->next_state = NNTP_READ_LIST;

	NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_NEWSGROUP));
	 
    return(ce->status);


}

/* display a list of all or part of the newsgroups list
 * from the news server
 */
PRIVATE int
net_read_news_list (ActiveEntry *ce)
{
    char * line;
    char * description;
    int i=0;
    NewsConData * cd = (NewsConData *)ce->con_data;
   
    ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf, 
                       					&cd->data_buf_size, (Bool*) &cd->pause_for_read);

    if(ce->status == 0)
      {
        cd->next_state = NNTP_ERROR;
        cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
        return(MK_NNTP_SERVER_ERROR);
      }

    if(!line)
        return(ce->status);  /* no line yet */

    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;
	  }

            /* End of list? */
    if (line[0]=='.' && line[1]=='\0')
      {
#ifdef XPCOM_NEWSHOST
    nsresult rv;
    PRBool listpnames=FALSE;
    rv = cd->news_host->QueryExtension("LISTPNAMES",&listpnames);
    if (NS_SUCCEEDED(rv) && listpnames)
#else
		if (MSG_QueryNewsExtension(cd->host, "LISTPNAMES"))
#endif
		{
			cd->next_state = NNTP_LIST_PRETTY_NAMES;
		}
		else
		{
			cd->next_state = DISPLAY_NEWSGROUPS;
		}
        cd->pause_for_read = FALSE;

        return 0;  
      }
	else if (line [0] == '.' && line [1] == '.')
	  /* The NNTP server quotes all lines beginning with "." by doubling it. */
	  line++;

    /* almost correct
     */
    if(ce->status > 1)
      {
    	ce->bytes_received += ce->status;
        FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
	  }

    /* find whitespace seperator if it exits */
    for(i=0; line[i] != '\0' && !NET_IS_SPACE(line[i]); i++)
        ;  /* null body */

    if(line[i] == '\0')
        description = &line[i];
    else
        description = &line[i+1];

    line[i] = 0; /* terminate group name */

	/* store all the group names 
	 */
#ifdef XPCOM_NEWSHOST
    cd->news_host->AddNewNewsgroup(line, 0, 0, "", FALSE);
#else
	MSG_AddNewNewsGroup(ce->URL_s->msg_pane, cd->host,
						line, 0, 0, "", FALSE);
#endif


    return(ce->status);
}




/* start the xover command
 */
PRIVATE int
net_read_xover_begin (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
    int32 count;     /* Response fields */

	/* Make sure we never close and automatically reopen the connection at this
	   point; we'll confuse libmsg too much... */

	cd->some_protocol_succeeded = TRUE;

	/* We have just issued a GROUP command and read the response.
	   Now parse that response to help decide which articles to request
	   xover data for.
	 */
    sscanf(cd->response_txt,
		   "%d %d %d", 
		   &count, 
		   &cd->first_possible_art, 
		   &cd->last_possible_art);

	/* We now know there is a summary line there; make sure it has the
	   right numbers in it. */
#ifdef XPCOM_NEWSHOST
    nsresult rv;
    char *group_name;
    cd->newsgroup->GetName(&group_name);
    
    cd->news_host->DisplaySubscribedGroup(group_name,
                                          cd->first_possible_art,
                                          cd->last_possible_art,
                                          count, TRUE);
    PR_Free(group_name);
#else
	ce->status = MSG_DisplaySubscribedGroup(cd->pane,
											cd->host,
											cd->group_name,
											cd->first_possible_art,
											cd->last_possible_art,
											count, TRUE);
#endif
	if (ce->status < 0) return ce->status;

	cd->num_loaded = 0;
	cd->num_wanted = net_NewsChunkSize > 0 ? net_NewsChunkSize : 1L << 30;

	cd->next_state = NNTP_FIGURE_NEXT_CHUNK;
	cd->pause_for_read = FALSE;
	return 0;
}



PRIVATE int
net_figure_next_chunk(ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
    nsresult rv;
	char *host_and_port = NET_ParseURL (ce->URL_s->address, GET_HOST_PART);

	if (!host_and_port) return MK_OUT_OF_MEMORY;

	if (cd->first_art > 0) {
#ifdef XPCOM_NEWSPARSE
      nsresult rv;
      /* XXX - parse state stored in MSG_Pane cd->pane */
      rv = cd->article_list->AddToKnownArticles(cd->first_art,
                                                cd->last_art);
      
#else
	  ce->status = MSG_AddToKnownArticles(cd->pane, cd->news_host,
										 cd->group_name,
										 cd->first_art, cd->last_art);
#endif
	  if (ce->status < 0) {
		FREEIF (host_and_port);
		return ce->status;
	  }
	}
										 

	if (cd->num_loaded >= cd->num_wanted) {
	  FREEIF (host_and_port);
	  cd->next_state = NEWS_PROCESS_XOVER;
	  cd->pause_for_read = FALSE;
	  return 0;
	}


#ifdef XPCOM_NEWSPARSE
    /* XXX - parse state stored in MSG_Pane cd->pane */
    rv =
      cd->article_list->GetRangeOfArtsToDownload(&ce->status,
                                                 cd->first_possible_art,
                                                 cd->last_possible_art,
                                                 cd->num_wanted -
                                                 cd->num_loaded,
                                                 &(cd->first_art),
                                                 &(cd->last_art));
#else
	ce->status = MSG_GetRangeOfArtsToDownload(cd->pane,
											 (void **)&cd->xover_parser,
											 cd->news_host,
											 cd->group_name,
											 cd->first_possible_art,
											 cd->last_possible_art,
											 cd->num_wanted - cd->num_loaded,
											 &(cd->first_art),
											 &(cd->last_art));
#endif
	if (ce->status < 0) {
	  FREEIF (host_and_port);
	  return ce->status;
	}


	if (cd->first_art <= 0 || cd->first_art > cd->last_art) {
	  /* Nothing more to get. */
	  FREEIF (host_and_port);
	  cd->next_state = NEWS_PROCESS_XOVER;
	  cd->pause_for_read = FALSE;
	  return 0;
	}

    NNTP_LOG_NOTE(("    Chunk will be (%ld-%ld)", cd->first_art, cd->last_art));

	cd->article_num = cd->first_art;
#ifdef XPCOM_XOVER
    rv = NS_NewMsgXOVERParser(&cd->xover_parser,
                                   cd->news_host, cd->newsgroup,
                                   cd->first_art, cd->last_art,
                                   cd->first_possible_art,
                                   cd->last_possible_art);
    /* convert nsresult->status */
    ce->status = !NS_SUCCEEDED(rv);
#else    
	ce->status = MSG_InitXOVER (cd->pane,
							   cd->news_host, cd->group_name,
							   cd->first_art, cd->last_art,
							   cd->first_possible_art, cd->last_possible_art,
							   &cd->xover_parse_state);
#endif
	FREEIF (host_and_port);

	if (ce->status < 0) {
	  return ce->status;
	}

	cd->pause_for_read = FALSE;
	if (cd->control_con->no_xover) cd->next_state = NNTP_READ_GROUP;
	else cd->next_state = NNTP_XOVER_SEND;

	return 0;
}

PRIVATE int
net_xover_send (ActiveEntry *ce)
{		
    NewsConData * cd = (NewsConData *)ce->con_data;

    PR_snprintf(cd->output_buffer, 
				OUTPUT_BUFFER_SIZE,
				"XOVER %ld-%ld" CRLF, 
				cd->first_art, 
				cd->last_art);

	/* printf("XOVER %ld-%ld\n", cd->first_art, cd->last_art); */

	NNTP_LOG_WRITE(cd->output_buffer);

    cd->next_state = NNTP_RESPONSE;
    cd->next_state_after_response = NNTP_XOVER_RESPONSE;
    cd->pause_for_read = TRUE;

	NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_RECEIVE_LISTARTICLES));

    return((int) NET_BlockingWrite(ce->socket, 
								   cd->output_buffer, 
								   PL_strlen(cd->output_buffer)));
	NNTP_LOG_WRITE(cd->output_buffer);

}

/* see if the xover response is going to return us data
 * if the proper code isn't returned then assume xover
 * isn't supported and use
 * normal read_group
 */
PRIVATE int
net_read_xover_response (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;

#ifdef TEST_NO_XOVER_SUPPORT
	cd->response_code = MK_NNTP_RESPONSE_CHECK_ERROR; /* pretend XOVER generated an error */
#endif

    if(cd->response_code != MK_NNTP_RESPONSE_XOVER_OK)
      {
        /* If we didn't get back "224 data follows" from the XOVER request,
		   then that must mean that this server doesn't support XOVER.  Or
		   maybe the server's XOVER support is busted or something.  So,
		   in that case, fall back to the very slow HEAD method.

		   But, while debugging here at HQ, getting into this state means
		   something went very wrong, since our servers do XOVER.  Thus
		   the assert.
         */
		/*PR_ASSERT (0);*/
		cd->next_state = NNTP_READ_GROUP;
		cd->control_con->no_xover = TRUE;
      }
    else
      {
        cd->next_state = NNTP_XOVER;
      }

    return(0);  /* continue */
}

/* process the xover list as it comes from the server
 * and load it into the sort list.  
 */
PRIVATE int
net_read_xover (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
    char *line;
    nsresult rv;

    ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
									 &cd->data_buf_size,
									 (Bool*)&cd->pause_for_read);

    if(ce->status == 0)
      {
		NNTP_LOG_NOTE(("received unexpected TCP EOF!!!!  aborting!"));
        cd->next_state = NNTP_ERROR;
        cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
        return(MK_NNTP_SERVER_ERROR);
      }

    if(!line)
	  {
	    return(ce->status);  /* no line yet or TCP error */
	  }

	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;
	  }

    if(line[0] == '.' && line[1] == '\0')
      {
		cd->next_state = NNTP_FIGURE_NEXT_CHUNK;
		cd->pause_for_read = FALSE;
		return(0);
      }
	else if (line [0] == '.' && line [1] == '.')
	  /* The NNTP server quotes all lines beginning with "." by doubling it. */
	  line++;

    /* almost correct
     */
    if(ce->status > 1)
      {
        ce->bytes_received += ce->status;
        FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status,
						 ce->URL_s->content_length);
	  }

#ifdef XPCOM_XOVER
    rv = cd->xover_parser->Process(line, &ce->status);
    PR_ASSERT(NS_SUCCEEDED(rv));
#else
	ce->status = MSG_ProcessXOVER (cd->pane, line, &cd->xover_parse_state);
#endif

	cd->num_loaded++;

    return ce->status; /* keep going */
}


/* Finished processing all the XOVER data.
 */
PRIVATE int
net_process_xover (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
    nsresult rv;

#ifdef XPCOM_XOVER
    /* xover_parse_state stored in MSG_Pane cd->pane */
    rv = cd->xover_parser->Finish(0,&ce->status);
#else
	/*	if (cd->xover_parse_state) { ### dmb - we need a different check */
	  ce->status = MSG_FinishXOVER (cd->pane, &cd->xover_parse_state, 0);
	  PR_ASSERT (!cd->xover_parse_state);
#endif
	  if (NS_SUCCEEDED(rv) && ce->status < 0) return ce->status;

	cd->next_state = NEWS_DONE;

    return(MK_DATA_LOADED);
}
 

PRIVATE int net_profile_add (ActiveEntry *ce)
{
#if 0
	NewsConData *cd = (NewsConData*) ce->con_data;
	char *slash = PL_strrchr(ce->URL_s->address, '/');
	if (slash)
	{
		PL_strcpy (cd->output_buffer, slash + 1);
		PL_strcat (cd->output_buffer, CRLF);
		ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
		NNTP_LOG_WRITE(cd->output_buffer);

		cd->next_state = NNTP_RESPONSE;
		cd->next_state_after_response = NNTP_PROFILE_ADD_RESPONSE;
		cd->pause_for_read = TRUE;
	}
	else
	{
		cd->next_state = NNTP_ERROR;
		ce->status = -1;
	}

	return ce->status;
#else
	PR_ASSERT(FALSE);
	return -1;
#endif
}

PRIVATE int net_profile_add_response (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;

    if (MK_NNTP_RESPONSE_TYPE(cd->response_code) == MK_NNTP_RESPONSE_TYPE_OK)
	{
#ifdef XPCOM_NEWSHOST
        cd->news_host->AddProfileGroup(cd->response_txt);
#else
		MSG_AddProfileGroup (cd->pane, cd->host, cd->response_txt);
#endif
		cd->next_state = NEWS_DONE;
	}
	else
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return MK_NNTP_SERVER_ERROR;
	}

	return ce->status;
}

PRIVATE int net_profile_delete (ActiveEntry *ce)
{
#if 0
	NewsConData *cd = (NewsConData*) ce->con_data;
	char *slash = PL_strrchr(ce->URL_s->address, '/');
	if (slash)
	{
		PL_strcpy (cd->output_buffer, slash + 1);
		PL_strcat (cd->output_buffer, CRLF);
		ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
		NNTP_LOG_WRITE(cd->output_buffer);

		cd->next_state = NNTP_RESPONSE;
		cd->next_state_after_response = NNTP_PROFILE_DELETE_RESPONSE;
		cd->pause_for_read = TRUE;
	}
	else
	{
		cd->next_state = NNTP_ERROR;
		ce->status = -1;
	}

	return ce->status;
#else
	PR_ASSERT(FALSE);
	return -1;
#endif
}

PRIVATE int net_profile_delete_response (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;

    if (MK_NNTP_RESPONSE_TYPE(cd->response_code) == MK_NNTP_RESPONSE_TYPE_OK)
		cd->next_state = NEWS_DONE;
	else
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return MK_NNTP_SERVER_ERROR;
	}

	return ce->status;
}

PRIVATE
int
net_read_news_group (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;

    if(cd->article_num > cd->last_art)
      {  /* end of groups */

		cd->next_state = NNTP_FIGURE_NEXT_CHUNK;
		cd->pause_for_read = FALSE;
		return(0);
      }
    else
      {
        PR_snprintf(cd->output_buffer, 
				   OUTPUT_BUFFER_SIZE,  
				   "HEAD %ld" CRLF, 
				   cd->article_num++);
        cd->next_state = NNTP_RESPONSE;
		cd->next_state_after_response = NNTP_READ_GROUP_RESPONSE;

        cd->pause_for_read = TRUE;

		NNTP_LOG_WRITE(cd->output_buffer);
        return((int) NET_BlockingWrite(ce->socket, cd->output_buffer, PL_strlen(cd->output_buffer)));
      }
}

/* See if the "HEAD" command was successful
 */
PRIVATE int
net_read_news_group_response (ActiveEntry *ce)
{
  NewsConData * cd = (NewsConData *)ce->con_data;
  nsresult rv;
  
  if (cd->response_code == MK_NNTP_RESPONSE_ARTICLE_HEAD)
	{     /* Head follows - parse it:*/
	  cd->next_state = NNTP_READ_GROUP_BODY;

	  if(cd->message_id)
		*cd->message_id = '\0';

	  /* Give the message number to the header parser. */
#ifdef XPCOM_XOVER
      rv = cd->xover_parser->ProcessNonXOVER(cd->response_txt);
      /* convert nsresult->status */
      return !NS_SUCCEEDED(rv);
#else
	  return MSG_ProcessNonXOVER (cd->pane, cd->response_txt,
								  &cd->xover_parse_state);
#endif
	}
  else
	{
	  NNTP_LOG_NOTE(("Bad group header found!"));
	  cd->next_state = NNTP_READ_GROUP;
	  return(0);
	}
}

/* read the body of the "HEAD" command
 */
PRIVATE int
net_read_news_group_body (ActiveEntry *ce)
{
  NewsConData * cd = (NewsConData *)ce->con_data;
  char *line;
  nsresult rv;

  ce->status = NET_BufferedReadLine(ce->socket, &line, &cd->data_buf,
								   &cd->data_buf_size, (Bool*)&cd->pause_for_read);

  if(ce->status == 0)
	{
	  cd->next_state = NNTP_ERROR;
	  cd->pause_for_read = FALSE;
	  ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
	  return(MK_NNTP_SERVER_ERROR);
	}

  /* if TCP error of if there is not a full line yet return
   */
  if(!line)
	return ce->status;

  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;
	}

	NNTP_LOG_NOTE(("read_group_body: got line: %s|",line));

  /* End of body? */
  if (line[0]=='.' && line[1]=='\0')
	{
	  cd->next_state = NNTP_READ_GROUP;
	  cd->pause_for_read = FALSE;
	}
  else if (line [0] == '.' && line [1] == '.')
	/* The NNTP server quotes all lines beginning with "." by doubling it. */
	line++;

#ifdef XPCOM_XOVER
  rv = cd->xover_parser->ProcessNonXOVER(line);
  /* convert nsresult->status */
  return !NS_SUCCEEDED(rv);
#else
  return MSG_ProcessNonXOVER (cd->pane, line, &cd->xover_parse_state);
#endif
}


PRIVATE int
net_send_news_post_data(ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *) ce->con_data;

    /* 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->socket,
								  &cd->write_post_data_data,
								  TRUE);

    cd->pause_for_read = TRUE;

    if(ce->status == 0)
      {
        /* normal done
         */
        PL_strcpy(cd->output_buffer, CRLF "." CRLF);
        NNTP_LOG_WRITE(cd->output_buffer);
        ce->status = (int) NET_BlockingWrite(ce->socket,
                                            cd->output_buffer,
                                            PL_strlen(cd->output_buffer));
		NNTP_LOG_WRITE(cd->output_buffer);

		NET_Progress(ce->window_id,
					XP_GetString(XP_MESSAGE_SENT_WAITING_NEWS_REPLY));

        NET_ClearConnectSelect(ce->window_id, ce->socket);
#ifdef XP_WIN
		if(cd->calling_netlib_all_the_time)
		{
			cd->calling_netlib_all_the_time = FALSE;
#if 0
          /* this should be handled by NET_ClearCallNetlibAllTheTime */
			net_call_all_the_time_count--;
			if(net_call_all_the_time_count == 0)
#endif
				NET_ClearCallNetlibAllTheTime(ce->window_id,"mknews");
		}
#endif
        NET_SetReadSelect(ce->window_id, ce->socket);
		ce->con_sock = 0;

        cd->next_state = NNTP_RESPONSE;
        cd->next_state_after_response = NNTP_SEND_POST_DATA_RESPONSE;
        return(0);
      }

    return(ce->status);

}


/* interpret the response code from the server
 * after the post is done
 */   
PRIVATE int
net_send_news_post_data_response(ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *) ce->con_data;


	if (cd->response_code != MK_NNTP_RESPONSE_POST_OK) {
	  ce->URL_s->error_msg =
		NET_ExplainErrorDetails(MK_NNTP_ERROR_MESSAGE, 
								cd->response_txt ? cd->response_txt : "");
	  if (cd->response_code == MK_NNTP_RESPONSE_POST_FAILED 
		  && MSG_GetPaneType(cd->pane) == MSG_COMPOSITIONPANE
		  && MSG_IsDuplicatePost(cd->pane) &&
		  MSG_GetCompositionMessageID(cd->pane)) {
		/* The news server won't let us post.  We suspect that we're submitting
		   a duplicate post, and that's why it's failing.  So, let's go see
		   if there really is a message out there with the same message-id.
		   If so, we'll just silently pretend everything went well. */
		PR_snprintf(cd->output_buffer, OUTPUT_BUFFER_SIZE, "STAT %s" CRLF,
					MSG_GetCompositionMessageID(cd->pane));
		cd->next_state = NNTP_RESPONSE;
		cd->next_state_after_response = NNTP_CHECK_FOR_MESSAGE;
		NNTP_LOG_WRITE(cd->output_buffer);
		return (int) NET_BlockingWrite(ce->socket, cd->output_buffer,
									   PL_strlen(cd->output_buffer));
	  }

	  MSG_ClearCompositionMessageID(cd->pane); /* So that if the user tries
													  to just post again, we
													  won't immediately decide
													  that this was a duplicate
													  message and ignore the
													  error. */
	  cd->next_state = NEWS_ERROR;
	  return(MK_NNTP_ERROR_MESSAGE);
	}
    cd->next_state = NEWS_ERROR; /* even though it worked */
	cd->pause_for_read = FALSE;
    return(MK_DATA_LOADED);
}


PRIVATE int
net_check_for_message(ActiveEntry* ce)
{
  NewsConData * cd = (NewsConData *) ce->con_data;

  cd->next_state = NEWS_ERROR;
  if (cd->response_code >= 220 && cd->response_code <= 223) {
	/* Yes, this article is already there, we're all done. */
	return MK_DATA_LOADED;
  } else {
	/* The article isn't there, so the failure we had earlier wasn't due to
	   a duplicate message-id.  Return the error from that previous
	   posting attempt (which is already in ce->URL_s->error_msg). */
	MSG_ClearCompositionMessageID(cd->pane);
	return MK_NNTP_ERROR_MESSAGE;
  }
}

#define NEWS_GROUP_DISPLAY_FREQ		20

PRIVATE int
net_DisplayNewsRC(ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *) ce->con_data;
    nsresult rv;
	if(!cd->newsrc_performed)
	  {
		cd->newsrc_performed = TRUE;
#ifdef XPCOM_NEWSHOST
        rv = cd->news_host->GetNumGroupsNeedingCounts(&cd->newsrc_list_count);
#else
		cd->newsrc_list_count = MSG_GetNewsRCCount(cd->pane, cd->news_host);
#endif
	  }
	

	
	FREEIF(cd->control_con->current_group);
#ifdef XPCOM_NEWSHOST
    rv =
      cd->news_host->GetFirstGroupNeedingCounts(&cd->control_con->current_group);
#else
	cd->control_con->current_group = MSG_GetNewsRCGroup(cd->pane, cd->host);
#endif
	

	if(NS_SUCCEEDED(rv) && cd->control_con->current_group)
      {
		/* send group command to server
		 */
		int32 percent;

		PR_snprintf(NET_Socket_Buffer, OUTPUT_BUFFER_SIZE, "GROUP %.512s" CRLF,
					cd->control_con->current_group);
    	ce->status = (int) NET_BlockingWrite(ce->socket, NET_Socket_Buffer,
											PL_strlen(NET_Socket_Buffer));
		NNTP_LOG_WRITE(NET_Socket_Buffer);

		percent = (cd->newsrc_list_count) ?
					(int32) (100.0 * ( (double)cd->newsrc_list_index / (double)cd->newsrc_list_count )) :
					0;
		FE_SetProgressBarPercent (ce->window_id, percent);
		
		/* only update every 20 groups for speed */
		if ((cd->newsrc_list_count <= NEWS_GROUP_DISPLAY_FREQ) || (cd->newsrc_list_index % NEWS_GROUP_DISPLAY_FREQ) == 0 ||
									(cd->newsrc_list_index == cd->newsrc_list_count))
		{
			char thisGroup[20];
			char totalGroups[20];
			char *statusText;
			
			PR_snprintf (thisGroup, sizeof(thisGroup), "%ld", (long) cd->newsrc_list_index);
			PR_snprintf (totalGroups, sizeof(totalGroups), "%ld", (long) cd->newsrc_list_count);
			statusText = PR_smprintf (XP_GetString(XP_THERMO_PERCENT_FORM), thisGroup, totalGroups);
			if (statusText)
			{
				FE_Progress (ce->window_id, statusText);
				PR_Free(statusText);
			}
		}
		
		cd->newsrc_list_index++;

		cd->pause_for_read = TRUE;
		cd->next_state = NNTP_RESPONSE;
		cd->next_state_after_response = NEWS_DISPLAY_NEWS_RC_RESPONSE;
      }
	else
	  {
		if (cd->newsrc_list_count)
		  {
			FE_SetProgressBarPercent (ce->window_id, -1);
			cd->newsrc_list_count = 0;
		  }
		else if (cd->response_code == MK_NNTP_RESPONSE_LIST_OK)  
		  {
			/*
			 * 5-9-96 jefft 
			 * If for some reason the news server returns an empty 
			 * newsgroups list with a nntp response code MK_NNTP_RESPONSE_LIST_OK -- list of
			 * newsgroups follows. We set ce->status to MK_EMPTY_NEWS_LIST
			 * to end the infinite dialog loop.
			 */
			ce->status = MK_EMPTY_NEWS_LIST;
		  }
		cd->next_state = NEWS_DONE;
	
		if(ce->status > -1)
		  return MK_DATA_LOADED; 
		else
		  return(ce->status);
	  }

	return(ce->status); /* keep going */

}

/* Parses output of GROUP command */
PRIVATE int
net_DisplayNewsRCResponse(ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *) ce->con_data;

    if(cd->response_code == MK_NNTP_RESPONSE_GROUP_SELECTED)
      {
		char *num_arts = 0, *low = 0, *high = 0, *group = 0;
		int32 first_art, last_art;

		/* line looks like:
		 *     211 91 3693 3789 comp.infosystems
		 */

		num_arts = cd->response_txt;
		low = PL_strchr(num_arts, ' ');

		if(low)
		  {
			first_art = atol(low);
			*low++ = '\0';
			high= PL_strchr(low, ' ');
		  }
		if(high)
		  {
			*high++ = '\0';
			group = PL_strchr(high, ' ');
		  }
		if(group)
		  {
			*group++ = '\0';
			/* the group name may be contaminated by "group selected" at
			   the end.  This will be space separated from the group name.
			   If a space is found in the group name terminate at that
			   point. */
			strtok(group, " ");
			last_art = atol(high);
		  }

#ifdef XPCOM_NEWSHOST
        cd->news_host->DisplaySubscribedGroup(group,
                                              low ? atol(low) : 0,
                                              high ? atol(high) : 0,
                                              atol(num_arts), FALSE);
#else
		ce->status = MSG_DisplaySubscribedGroup(cd->pane,
												cd->host,
												group,
												low  ? atol(low)  : 0,
												high ? atol(high) : 0,
												atol(num_arts), FALSE);
#endif
		if (ce->status < 0)
		  return ce->status;
	  }
	  else if (cd->response_code == MK_NNTP_RESPONSE_GROUP_NO_GROUP)
	  {
#ifdef XPCOM_NEWSHOST
          cd->news_host->GroupNotFound(cd->control_con->current_group, FALSE);
#else
		  MSG_GroupNotFound(cd->pane, cd->host, cd->control_con->current_group, FALSE);
#endif
	  }
	  /* it turns out subscribe ui depends on getting this displaysubscribedgroup call,
	     even if there was an error.
	  */
	  if(cd->response_code != MK_NNTP_RESPONSE_GROUP_SELECTED)
	  {
		/* only on news server error or when zero articles
		 */
#ifdef XPCOM_NEWSHOST
        cd->news_host->DisplaySubscribedGroup(cd->control_con->current_group,
                                              0, 0, 0, FALSE);
#else
		ce->status = MSG_DisplaySubscribedGroup(cd->pane,
												cd->host,
												cd->control_con->current_group,
												0, 0, 0, FALSE);
#endif

	  }

	cd->next_state = NEWS_DISPLAY_NEWS_RC;
		
	return 0;
}


PRIVATE int
net_NewsRCProcessPost(ActiveEntry *ce)
{
	return(0);
}


#ifdef USE_LIBMSG
static void
net_cancel_done_cb (MWContext *context, void *data, int status,
					const char *file_name)
{
  ActiveEntry *ce = (ActiveEntry *) data;
  NewsConData *cd = (NewsConData *) ce->con_data;
  cd->cancel_status = status;
  PR_ASSERT(status < 0 || file_name);
  cd->cancel_msg_file = (status < 0 ? 0 : PL_strdup(file_name));
}
#endif 


static int
net_start_cancel (ActiveEntry *ce)
{
  NewsConData *cd = (NewsConData *) ce->con_data;
  char *command = "POST" CRLF;

  ce->status = (int) NET_BlockingWrite(ce->socket, command, PL_strlen(command));
	NNTP_LOG_WRITE(command);

  cd->next_state = NNTP_RESPONSE;
  cd->next_state_after_response = NEWS_DO_CANCEL;
  cd->pause_for_read = TRUE;
  return (ce->status);
}


static int
net_do_cancel (ActiveEntry *ce)
{
  int status = 0;
  NewsConData *cd = (NewsConData *) ce->con_data;
  char *id, *subject, *newsgroups, *distribution, *other_random_headers, *body;
  char *from, *old_from, *news_url;
  int L;
#ifdef USE_LIBMSG
  MSG_CompositionFields *fields = NULL;
#endif 


  /* #### Should we do a more real check than this?  If the POST command
	 didn't respond with "MK_NNTP_RESPONSE_POST_SEND_NOW Ok", then it's not ready for us to throw a
	 message at it...   But the normal posting code doesn't do this check.
	 Why?
   */
  PR_ASSERT (cd->response_code == MK_NNTP_RESPONSE_POST_SEND_NOW);

  /* These shouldn't be set yet, since the headers haven't been "flushed" */
  PR_ASSERT (!cd->cancel_id &&
			 !cd->cancel_from &&
			 !cd->cancel_newsgroups &&
			 !cd->cancel_distribution);

  /* Write out a blank line.  This will tell mimehtml.c that the headers
	 are done, and it will call news_generate_html_header_fn which will
	 notice the fields we're interested in.
   */
  PL_strcpy (cd->output_buffer, CRLF); /* CRLF used to be LINEBREAK. 
  										 LINEBREAK is platform dependent
  										 and is only <CR> on a mac. This
										 CRLF is the protocol delimiter 
										 and not platform dependent  -km */
  ce->status = PUTSTRING(cd->output_buffer);
  if (ce->status < 0) return ce->status;

  /* Now news_generate_html_header_fn should have been called, and these
	 should have values. */
  id = cd->cancel_id;
  old_from = cd->cancel_from;
  newsgroups = cd->cancel_newsgroups;
  distribution = cd->cancel_distribution;

  PR_ASSERT (id && newsgroups);
  if (!id || !newsgroups) return -1; /* "unknown error"... */

  cd->cancel_newsgroups = 0;
  cd->cancel_distribution = 0;
  cd->cancel_from = 0;
  cd->cancel_id = 0;

  L = PL_strlen (id);

  from = MIME_MakeFromField ();
  subject = (char *) PR_Malloc (L + 20);
  other_random_headers = (char *) PR_Malloc (L + 20);
  body = (char *) PR_Malloc (PL_strlen (XP_AppCodeName) + 100);

  /* Make sure that this loser isn't cancelling someone else's posting.
	 Yes, there are occasionally good reasons to do so.  Those people
	 capable of making that decision (news admins) have other tools with
	 which to cancel postings (like telnet.)
	 Don't do this if server tells us it will validate user. DMB 3/19/97
   */
#ifdef XPCOM_NEWSHOST
  nsresult rv;
  PRBool cancelchk=FALSE;
  rv = cd->news_host->QueryExtension("CANCELCHK",&cancelchk);
  if (NS_SUCCEEDED(rv) && cancelchk)
#else
  if (!MSG_QueryNewsExtension(cd->host, "CANCELCHK"))
#endif
  {
    nsIMsgRFC822Parser *parser;
    nsresult rv;
    PRBool ok = FALSE;

    rv = NS_NewRFC822Parser(&parser);
    if (NS_SUCCEEDED(rv)) {
      char *us, *them;
      nsresult rv1 = parser->ExtractRFC822AddressMailboxes(from, &us);
      nsresult rv2 = parser->ExtractRFC822AddressMailboxes(old_from, &them);
      ok = (NS_SUCCEEDED(rv1) &&
            NS_SUCCEEDED(rv2) &&
            !PL_strcasecmp(us, them));
      if (NS_SUCCEEDED(rv1)) PR_Free(us);
      if (NS_SUCCEEDED(rv2)) PR_Free(them);
      NS_RELEASE(parser);
    }
	if (!ok)
	  {
		status = MK_NNTP_CANCEL_DISALLOWED;
		ce->URL_s->error_msg = PL_strdup (XP_GetString(status));
		cd->next_state = NEWS_ERROR; /* even though it worked */
		cd->pause_for_read = FALSE;
		goto FAIL;
	  }
  }

  /* Last chance to cancel the cancel.
   */
  if (!FE_Confirm (ce->window_id, XP_GetString(MK_NNTP_CANCEL_CONFIRM)))
	{
	  status = MK_NNTP_NOT_CANCELLED;
	  goto FAIL;
	}

  news_url = ce->URL_s->address;  /* we can just post here. */

  if (!from || !subject || !other_random_headers || !body)
	{
	  status = MK_OUT_OF_MEMORY;
	  goto FAIL;
	}

  PL_strcpy (subject, "cancel ");
  PL_strcat (subject, id);

  PL_strcpy (other_random_headers, "Control: cancel ");
  PL_strcat (other_random_headers, id);
  PL_strcat (other_random_headers, CRLF);
  if (distribution)
	{
	  PL_strcat (other_random_headers, "Distribution: ");
	  PL_strcat (other_random_headers, distribution);
	  PL_strcat (other_random_headers, CRLF);
	}

  PL_strcpy (body, "This message was cancelled from within ");
  PL_strcat (body, XP_AppCodeName);
  PL_strcat (body, "." CRLF);

#ifdef USE_LIBMSG
  fields = MSG_CreateCompositionFields(from, 0, 0, 0, 0, 0, newsgroups,
									   0, 0, subject, id, other_random_headers,
									   0, 0, news_url);
#endif 
  
/* so that this would compile - will probably change later */
#if 0
									   FALSE,
									   FALSE  
									   );
#endif

#ifdef USE_LIBMSG
  if (!fields)
  {
	  status = MK_OUT_OF_MEMORY;
	  goto FAIL;
  }
  HG38712
#endif 

  cd->cancel_status = 0;

#ifdef USE_LIBMSG
  MSG_StartMessageDelivery (cd->pane, (void *) ce,
							fields,
							FALSE, /* digest_p */
							TRUE,  /* dont_deliver_p */
							TEXT_PLAIN, body, PL_strlen (body),
							0, /* other attachments */
							NULL, /* multipart/related chunk */
							net_cancel_done_cb);

  /* Since there are no attachments, MSG_StartMessageDelivery will run
	 net_cancel_done_cb right away (it will be called before the return.) */

  if (!cd->cancel_msg_file)
	{
	  status = cd->cancel_status;
	  PR_ASSERT (status < 0);
	  if (status >= 0) status = -1;
	  goto FAIL;
	}

  /* Now send the data - do it blocking, who cares; the message is known
	 to be very small.  First suck the whole file into memory.  Then delete
	 the file.  Then do a blocking write of the data.

	 (We could use file-posting, maybe, but I couldn't figure out how.)
   */
  {
	char *data;
	uint32 data_size, data_fp;
	XP_StatStruct st;
	int nread = 0;
	XP_File file = XP_FileOpen (cd->cancel_msg_file,
								xpFileToPost, XP_FILE_READ);
	if (! file) return -1; /* "unknown error"... */
	XP_Stat (cd->cancel_msg_file, &st, xpFileToPost);

	data_fp = 0;
	data_size = st.st_size + 20;
	data = (char *) PR_Malloc (data_size);
	if (! data)
	  {
		status = MK_OUT_OF_MEMORY;
		goto FAIL;
	  }

	while ((nread = XP_FileRead (data + data_fp, data_size - data_fp, file))
		   > 0)
	  data_fp += nread;
	data [data_fp] = 0;
	XP_FileClose (file);
	XP_FileRemove (cd->cancel_msg_file, xpFileToPost);

	PL_strcat (data, CRLF "." CRLF CRLF);
	status = NET_BlockingWrite(ce->socket, data, PL_strlen(data));
	NNTP_LOG_WRITE(data);
	PR_Free (data);
    if (status < 0)
	  {
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR,
													  status);
		goto FAIL;
	  }

    cd->pause_for_read = TRUE;
	cd->next_state = NNTP_RESPONSE;
	cd->next_state_after_response = NNTP_SEND_POST_DATA_RESPONSE;
  }
#else

  {
    /* NET_BlockingWrite() should go away soon? I think. */
    /* The following are what we really need to cancel a posted message */
    char *data;
    data = PR_smprintf("From: %s" CRLF
                       "Newsgroups: %s" CRLF
                       "Subject: %s" CRLF
                       "References: %s" CRLF
                       "%s" CRLF /* other_random_headers */
                       "%s"     /* body */
                       CRLF "." CRLF CRLF, /* trailing SMTP "." */
                       from, newsgroups, subject, id,
                       other_random_headers, body);
    
    status = NET_BlockingWrite(ce->socket, data, PL_strlen(data));
    NNTP_LOG_WRITE(data);
    PR_Free (data);
    if (status < 0)
	  {
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR,
													  status);
		goto FAIL;
	  }

    cd->pause_for_read = TRUE;
	cd->next_state = NNTP_RESPONSE;
	cd->next_state_after_response = NNTP_SEND_POST_DATA_RESPONSE;
  }

#endif 

 FAIL:
  FREEIF (id);
  FREEIF (from);
  FREEIF (old_from);
  FREEIF (subject);
  FREEIF (newsgroups);
  FREEIF (distribution);
  FREEIF (other_random_headers);
  FREEIF (body);
  FREEIF (cd->cancel_msg_file);

#ifdef USE_LIBMSG
  if (fields)
	  MSG_DestroyCompositionFields(fields);
#endif 

  return status;
}


static int net_xpat_send (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData *) ce->con_data;
	int status = 0;
	char *thisTerm = NULL;

	if (cd->current_search &&
		(thisTerm = PL_strchr(cd->current_search, '/')) != NULL)
	{
		/* extract the XPAT encoding for one query term */
/*		char *next_search = NULL; */
		char *command = NULL;
		char *unescapedCommand = NULL;
		char *endOfTerm = NULL;
		StrAllocCopy (command, ++thisTerm);
		endOfTerm = PL_strchr(command, '/');
		if (endOfTerm)
			*endOfTerm = '\0';
		StrAllocCat (command, CRLF);
	
		unescapedCommand = MSG_UnEscapeSearchUrl(command);

		/* send one term off to the server */
		NNTP_LOG_WRITE(command);
		status = NET_BlockingWrite(ce->socket, unescapedCommand, PL_strlen(unescapedCommand));
		NNTP_LOG_WRITE(unescapedCommand);

		cd->next_state = NNTP_RESPONSE;
		cd->next_state_after_response = NNTP_XPAT_RESPONSE;
	    cd->pause_for_read = TRUE;

		PR_Free(command);
		PR_Free(unescapedCommand);
	}
	else
	{
		cd->next_state = NEWS_DONE;
		status = MK_DATA_LOADED;
	}
	return status;
}

static int net_list_pretty_names(ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;

#ifdef XPCOM_NEWSGROUP
    char *group_name;
    nsresult rv = cd->newsgroup->GetName(&group_name);
#endif
	PR_snprintf(cd->output_buffer, 
			OUTPUT_BUFFER_SIZE, 
			"LIST PRETTYNAMES %.512s" CRLF,
#ifdef XPCOM_NEWSGROUP
            NS_SUCCEEDED(rv) ? group_name : "");
#else
		  	cd->group_name ? cd->group_name : "");
#endif
    
    ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
	NNTP_LOG_WRITE(cd->output_buffer);
#ifdef DEBUG_bienvenu1
	PR_LogPrint(cd->output_buffer);
#endif
	cd->next_state = NNTP_RESPONSE;
	cd->next_state_after_response = NNTP_LIST_PRETTY_NAMES_RESPONSE;

	return ce->status;
}

static int net_list_pretty_names_response(ActiveEntry *ce)
{
	char *line;
	char *prettyName;

	NewsConData * cd = (NewsConData *)ce->con_data;

	if (cd->response_code != MK_NNTP_RESPONSE_LIST_OK)
	{
		cd->next_state = DISPLAY_NEWSGROUPS;
/*		cd->next_state = NEWS_DONE; */
		cd->pause_for_read = FALSE;
		return 0;
	}

	ce->status = NET_BufferedReadLine(ce->socket, &line,
									 &cd->data_buf, 
									 &cd->data_buf_size,
									 (Bool*)&cd->pause_for_read);
	NNTP_LOG_READ(line);

	if(ce->status == 0)
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return(MK_NNTP_SERVER_ERROR);
	}

	if (line)
	{
		if (line[0] != '.')
		{
			int i;
			/* find whitespace seperator if it exits */
			for (i=0; line[i] != '\0' && !NET_IS_SPACE(line[i]); i++)
				;  /* null body */

			if(line[i] == '\0')
				prettyName = &line[i];
			else
				prettyName = &line[i+1];

			line[i] = 0; /* terminate group name */
			if (i > 0)
#ifdef XPCOM_NEWSHOST
              cd->news_host->AddPrettyName(line,prettyName);
#else
				MSG_AddPrettyName(cd->host, line, prettyName);
#endif
#ifdef DEBUG_bienvenu1
			PR_LogPrint("adding pretty name %s\n", prettyName);
#endif
		}
		else
		{
			cd->next_state = DISPLAY_NEWSGROUPS;	/* this assumes we were doing a list */
/*			cd->next_state = NEWS_DONE;	 */ /* ### dmb - don't really know */
			cd->pause_for_read = FALSE;
			return 0;
		}
	}
	return 0;
}

static int net_list_xactive(ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;
    
#ifdef XPCOM_NEWSGROUP
    char *group_name;
    nsresult rv = cd->newsgroup->GetName(&group_name);
#endif
    
	PR_snprintf(cd->output_buffer, 
			OUTPUT_BUFFER_SIZE, 
			"LIST XACTIVE %.512s" CRLF,
#ifdef XPCOM_NEWSGROUP
            group_name);
#else
			cd->group_name);
#endif
    ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
	NNTP_LOG_WRITE(cd->output_buffer);

	cd->next_state = NNTP_RESPONSE;
	cd->next_state_after_response = NNTP_LIST_XACTIVE_RESPONSE;

	return ce->status;
}

static int net_list_xactive_response(ActiveEntry *ce)
{
	char *line;

	NewsConData * cd = (NewsConData *)ce->con_data;

	PR_ASSERT(cd->response_code == MK_NNTP_RESPONSE_LIST_OK);
	if (cd->response_code != MK_NNTP_RESPONSE_LIST_OK)
	{
		cd->next_state = DISPLAY_NEWSGROUPS;
/*		cd->next_state = NEWS_DONE; */
		cd->pause_for_read = FALSE;
		return MK_DATA_LOADED;
	}

	ce->status = NET_BufferedReadLine(ce->socket, &line,
									 &cd->data_buf, 
									 &cd->data_buf_size,
									 (Bool*)&cd->pause_for_read);
	NNTP_LOG_READ(line);

	if(ce->status == 0)
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return(MK_NNTP_SERVER_ERROR);
	}

	 /* almost correct
	*/
	if(ce->status > 1)
	{
		ce->bytes_received += ce->status;
		FE_GraphProgress(ce->window_id, ce->URL_s, ce->bytes_received, ce->status, ce->URL_s->content_length);
	}

	if (line)
	{
		if (line[0] != '.')
		{
			char *s = line;
			/* format is "rec.arts.movies.past-films 7302 7119 csp"
			 */
			while (*s && !NET_IS_SPACE(*s))
				s++;
			if (s)
			{
				char flags[32];	/* ought to be big enough */
				*s = 0;
				sscanf(s + 1,
					   "%d %d %31s", 
					   &cd->first_possible_art, 
					   &cd->last_possible_art,
					   flags);
#ifdef XPCOM_NEWSHOST
                cd->news_host->AddNewNewsgroup(line,
                                          cd->first_possible_art,
                                          cd->last_possible_art, flags, TRUE);
#else
				MSG_AddNewNewsGroup(cd->pane, cd->host, line,
									cd->first_possible_art,
									cd->last_possible_art, flags, TRUE /* xactive flags */);
#endif
				/* we're either going to list prettynames first, or list
                   all prettynames every time, so we won't care so much
                   if it gets interrupted. */
#ifdef DEBUG_bienvenu1
				PR_LogPrint("got xactive for %s of %s\n", line, flags);
#endif
				/*	This isn't required, because the extra info is
                    initialized to false for new groups. And it's
                    an expensive call.
				*/
				/* MSG_SetGroupNeedsExtraInfo(cd->host, line, FALSE); */
			}
		}
		else
		{
#ifdef XPCOM_NEWSHOST
          nsresult rv;
          PRBool xactive=FALSE;
          rv = cd->news_host->QueryExtension("XACTIVE",&xactive);
          if (cd->type_wanted == NEW_GROUPS &&
              NS_SUCCEEDED(rv) && xactive)
#else
			if (cd->type_wanted == NEW_GROUPS && MSG_QueryNewsExtension(cd->host, "XACTIVE"))
#endif
			{
#ifdef XPCOM_NEWSGROUP
                nsIMsgNewsgroup* old_newsgroup = cd->newsgroup;
                cd->news_host->GetFirstGroupNeedingExtraInfo(&cd->newsgroup);
                if (old_newsgroup && cd->newsgroup &&
                    old_newsgroup != cd->newsgroup)
                /* make sure we're not stuck on the same group */
                {
                    NS_RELEASE(old_newsgroup);
#else
				char *old_group_name = cd->group_name;
				cd->group_name = MSG_GetFirstGroupNeedingExtraInfo(cd->host);
				if (old_group_name && cd->group_name && PL_strcmp(old_group_name, cd->group_name))
				/* make sure we're not stuck on the same group... */
				{
					PR_Free(old_group_name);
#endif
#ifdef DEBUG_bienvenu1
					PR_LogPrint("listing xactive for %s\n", cd->group_name);
#endif
					cd->next_state = NNTP_LIST_XACTIVE;
			        cd->pause_for_read = FALSE;
					return 0;
				}
				else
				{
#ifdef XPCOM_NEWSGROUP
                    NS_RELEASE(old_newsgroup);
                    cd->newsgroup = NULL;
#else
					FREEIF(old_group_name);
					cd->group_name = NULL;
#endif
				}
			}
#ifdef XPCOM_NEWSHOST
            PRBool listpname;
            rv = cd->news_host->QueryExtension("LISTPNAME",&listpname);
            if (NS_SUCCEEDED(rv) && listpname)
#else
			if (MSG_QueryNewsExtension(cd->host, "LISTPNAMES"))
#endif
				cd->next_state = NNTP_LIST_PRETTY_NAMES;
			else
				cd->next_state = DISPLAY_NEWSGROUPS;	/* this assumes we were doing a list - who knows? */
/*			cd->next_state = NEWS_DONE;	 */ /* ### dmb - don't really know */
			cd->pause_for_read = FALSE;
			return 0;
		}
	}
	return 0;
}

static int net_list_group(ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;
    nsresult rv;
#ifdef XPCOM_NEWSGROUP
    char *group_name;
    rv = cd->newsgroup->GetName(&group_name);
#endif
    
	PR_snprintf(cd->output_buffer, 
			OUTPUT_BUFFER_SIZE, 
			"listgroup %.512s" CRLF,
#ifdef XPCOM_NEWSGROUP
                group_name);
#else
			cd->group_name);
#endif
    
#ifdef XPCOM_NEWSPARSE
    rv = NS_NewMsgNewsArticleList(&cd->article_list,
                                  cd->news_host, cd->newsgroup);
#else
	MSG_InitAddArticleKeyToGroup(cd->pane, cd->host, cd->group_name,
							   &cd->newsgroup_parse_state);
#endif
    ce->status = (int) NET_BlockingWrite(ce->socket,cd->output_buffer,PL_strlen(cd->output_buffer));
	NNTP_LOG_WRITE(cd->output_buffer);

	cd->next_state = NNTP_RESPONSE;
	cd->next_state_after_response = NNTP_LIST_GROUP_RESPONSE;

	return ce->status;
}

static int net_list_group_response(ActiveEntry *ce)
{
	char *line;

	NewsConData * cd = (NewsConData *)ce->con_data;

	PR_ASSERT(cd->response_code == MK_NNTP_RESPONSE_GROUP_SELECTED);
	if (cd->response_code != MK_NNTP_RESPONSE_GROUP_SELECTED)
	{
		cd->next_state = NEWS_DONE; 
		cd->pause_for_read = FALSE;
		return MK_DATA_LOADED;
	}

	ce->status = NET_BufferedReadLine(ce->socket, &line,
									 &cd->data_buf, 
									 &cd->data_buf_size,
									 (Bool*)&cd->pause_for_read);
	NNTP_LOG_READ(line);

	if(ce->status == 0)
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return(MK_NNTP_SERVER_ERROR);
	}

	if (line)
	{
		if (line[0] != '.')
		{
			long found_id = MSG_MESSAGEKEYNONE;
            nsresult rv;
			sscanf(line, "%ld", &found_id);
            
#ifdef XPCOM_NEWSPARSE
            rv = cd->article_list->AddArticleKeyToGroup(found_id);
#else
			MSG_AddArticleKeyToGroup(cd->newsgroup_parse_state, (int32) found_id);
#endif
		}
		else
		{
			cd->next_state = NEWS_DONE;	 /* ### dmb - don't really know */
			cd->pause_for_read = FALSE;
			return 0;
		}
	}
	return 0;
}


static int net_xpat_response (ActiveEntry *ce)
{
	char *line;
	NewsConData * cd = (NewsConData *)ce->con_data;

	if (cd->response_code != MK_NNTP_RESPONSE_XPAT_OK)
	{
		ce->URL_s->error_msg  = NET_ExplainErrorDetails(MK_NNTP_ERROR_MESSAGE, cd->response_txt);
    	cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		return MK_NNTP_SERVER_ERROR;
	}

	ce->status = NET_BufferedReadLine(ce->socket, &line,
									 &cd->data_buf, 
									 &cd->data_buf_size,
									 (Bool*)&cd->pause_for_read);
	NNTP_LOG_READ(line);

	if(ce->status == 0)
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return(MK_NNTP_SERVER_ERROR);
	}

	if (line)
	{
		if (line[0] != '.')
		{
			long articleNumber;
			sscanf(line, "%ld", &articleNumber);
			MSG_AddNewsXpatHit (ce->window_id, (uint32) articleNumber);
		}
		else
		{
			/* set up the next term for next time around */
			char *nextTerm = PL_strchr(cd->current_search, '/');
			if (nextTerm)
				cd->current_search = ++nextTerm;
			else
				cd->current_search = NULL;

			cd->next_state = NNTP_XPAT_SEND;
			cd->pause_for_read = FALSE;
			return 0;
		}
	}
	return 0;
}


PRIVATE int net_nntp_search(ActiveEntry *ce)
{
	PR_ASSERT(FALSE);
	return 0;
}

PRIVATE int net_nntp_search_response(ActiveEntry *ce)
{
	NewsConData * cd = (NewsConData *)ce->con_data;
	
    if (MK_NNTP_RESPONSE_TYPE(cd->response_code) == MK_NNTP_RESPONSE_TYPE_OK)
		cd->next_state = NNTP_SEARCH_RESULTS;
	else
		cd->next_state = NEWS_DONE;
	cd->pause_for_read = FALSE;
	return 0;
}

PRIVATE int net_nntp_search_results (ActiveEntry *ce)
{
	NewsConData *cd = (NewsConData*) ce->con_data;

	char *line = NULL;
	ce->status = NET_BufferedReadLine (ce->socket, &line, &cd->data_buf,
		&cd->data_buf_size, (Bool*)&cd->pause_for_read);

	if(ce->status == 0)
	{
		cd->next_state = NNTP_ERROR;
		cd->pause_for_read = FALSE;
		ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
		return MK_NNTP_SERVER_ERROR;
	}
	if (!line)
		return ce->status;  /* no line yet */
	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;
	}

	if ('.' != line[0])
		MSG_AddNewsSearchHit (ce->window_id, line);
	else
	{
		/* all overview lines received */
		cd->next_state = NEWS_DONE;
		cd->pause_for_read = FALSE;
	}

	return ce->status;
}

/* The main news load init routine, and URL parser.
   The syntax of news URLs is:

	 To list all hosts:
	   news:

	 To list all groups on a host, or to post a new message:
	   news://HOST

	 To list some articles in a group:
	   news:GROUP
	   news:/GROUP
	   news://HOST/GROUP

	 To list a particular range of articles in a group:
	   news:GROUP/N1-N2
	   news:/GROUP/N1-N2
	   news://HOST/GROUP/N1-N2

	 To retrieve an article:
	   news:MESSAGE-ID                (message-id must contain "@")

    To cancel an article:
	   news:MESSAGE-ID?cancel

	 (Note that message IDs may contain / before the @:)

	   news:SOME/ID@HOST?headers=all
	   news:/SOME/ID@HOST?headers=all
	   news:/SOME?ID@HOST?headers=all
        are the same as
	   news://HOST/SOME/ID@HOST?headers=all
	   news://HOST//SOME/ID@HOST?headers=all
	   news://HOST//SOME?ID@HOST?headers=all
        bug: if the ID is <//xxx@host> we will parse this correctly:
          news://non-default-host///xxx@host
        but will mis-parse it if it's on the default host:
          news://xxx@host
        whoever had the idea to leave the <> off the IDs should be flogged.
		So, we'll make sure we quote / in message IDs as %2F.

 */
int
NET_parse_news_url (const char *url,
					char **host_and_portP,
					XP_Bool *bValP,
					char **groupP,
					char **message_idP,
					char **command_specific_dataP)
{
  int status = 0;
  HG43575
  char *host_and_port = 0;
  int32 port = 0;
  char *group = 0;
  char *message_id = 0;
  char *command_specific_data = 0;
  const char *path_part;
  char *s;

  HG66376

  host_and_port = NET_ParseURL (url, GET_HOST_PART);
  if (!host_and_port || !*host_and_port)
	{
	  char* defhost = NULL;
	  int32 defport = 0;
	  FREEIF(host_and_port);
	  PREF_CopyCharPref("network.hosts.nntp_server", &defhost);
	  if (defhost && *defhost == '\0') {
		PR_Free(defhost);
		defhost = NULL;
	  }
	  if (defhost) {
		PREF_GetIntPref("news.server_port", &defport);
		HG05417

		if (defport) {
		  host_and_port = PR_smprintf("%s:%ld", defhost, (long) defport);
		  PR_Free(defhost);
		} else {
		  host_and_port = defhost;
		}
		if (!host_and_port) {
		  status = MK_OUT_OF_MEMORY;
		  goto FAIL;
		}
	  }
	}

  if (!host_and_port)
	{
	  status = MK_NO_NEWS_SERVER;
	  goto FAIL;
	}

  /* If a port was specified, but it was the default port, pretend
	 it wasn't specified.
   */
  s = PL_strchr (host_and_port, ':');
  if (s && sscanf (s+1, " %u ", &port) == 1 &&
	  HG05998)
	*s = 0;

  path_part = PL_strchr (url, ':');
  PR_ASSERT (path_part);
  if (!path_part)
	{
	  status = -1;
	  goto FAIL;
	}
  path_part++;
  if (path_part[0] == '/' && path_part[1] == '/')
	{
	  /* Skip over host name. */
	  path_part = PL_strchr (path_part + 2, '/');
	  if (path_part)
		path_part++;
	}
  if (!path_part)
	path_part = "";

  group = PL_strdup (path_part);
  if (!group)
	{
	  status = MK_OUT_OF_MEMORY;
	  goto FAIL;
	}

  NET_UnEscape (group);

  /* "group" now holds the part after the host name:
	 "message@id?search" or "/group/xxx?search" or "/message?id@xx?search"

	 If there is an @, this is a message ID; else it is a group.
	 Either way, there may be search data at the end.
   */

  s = PL_strchr (group, '@');
  if (s)
	{
	  message_id = group;
	  group = 0;
	}
  else if (!*group)
	{
	  PR_Free (group);
	  group = 0;
	}

  /* At this point, the search data is attached to `message_id' (if there
	 is one) or `group' (if there is one) or `host_and_port' (otherwise.)
	 Seperate the search data from what it is clinging to, being careful
	 to interpret the "?" only if it comes after the "@" in an ID, since
	 the syntax of message IDs is tricky.  (There may be a ? in the
	 random-characters part of the ID (before @), but not in the host part
	 (after @).)
   */
  if (message_id || group || host_and_port)
	{
	  char *start;
	  if (message_id)
		{
		  /* Move past the @. */
		  PR_ASSERT (s && *s == '@');
		  start = s;
		}
	  else
		{
		  start = group ? group : host_and_port;
		}

	  /* Take off the "?" or "#" search data */
	  for (s = start; *s; s++)
		if (*s == '?' || *s == '#')
		  break;
	  if (*s)
		{
		  command_specific_data = PL_strdup (s);
		  *s = 0;
		  if (!command_specific_data)
			{
			  status = MK_OUT_OF_MEMORY;
			  goto FAIL;
			}
		}

	  /* Discard any now-empty strings. */
	  if (message_id && !*message_id)
		{
		  PR_Free (message_id);
		  message_id = 0;
		}
	  else if (group && !*group)
		{
		  PR_Free (group);
		  group = 0;
		}
	}

  FAIL:
  PR_ASSERT (!message_id || message_id != group);
  if (status >= 0)
	{
	  *host_and_portP = host_and_port;
	  HG45873
	  *groupP = group;
	  *message_idP = message_id;
	  *command_specific_dataP = command_specific_data;
	}
  else
	{
	  FREEIF (host_and_port);
	  FREEIF (group);
	  FREEIF (message_id);
	  FREEIF (command_specific_data);
	}
  return status;
}


/* Returns true if this URL is a reference to a news message,
   that is, the kind of news URL entity that can be displayed
   in a regular browser window, and doesn't involve all kinds
   of other magic state and callbacks like listing a group.
 */
XP_Bool
NET_IsNewsMessageURL (const char *url)
{
  char *host_and_port = 0;
  XP_Bool bVal = FALSE;
  char *group = 0;
  char *message_id = 0;
  char *command_specific_data = 0;
  int status = NET_parse_news_url (url, &host_and_port, &bVal,
								   &group, &message_id, &command_specific_data);
  XP_Bool result = FALSE;
  if (status >= 0 && message_id && *message_id)
	result = TRUE;
  FREEIF (host_and_port);
  FREEIF (group);
  FREEIF (message_id);
  FREEIF (command_specific_data);
  return result;
}

XP_Bool 
NET_IsNewsServerURL( const char *url)
{
	  char *host_and_port = 0;
	XP_Bool bVal = FALSE;
	char *group = 0;
	char *message_id = 0;
	char *command_specific_data = 0;
	int status = NET_parse_news_url (url, &host_and_port, &bVal,
								   &group, &message_id, &command_specific_data);
	XP_Bool result = FALSE;
	if (status >= 0 && host_and_port && !group && !message_id)
		result = TRUE;
	FREEIF (host_and_port);
	FREEIF (group);
	FREEIF (message_id);
	FREEIF (command_specific_data);
	return result;
}

static XP_Bool nntp_are_connections_available (NNTPConnection *conn)
{
	int connCount = 0;

	NNTPConnection *tmpConn;
	XP_List *tmpList = nntp_connection_list;

	while ((tmpConn = (NNTPConnection*) XP_ListNextObject(tmpList)) != NULL)
	{
		NNTP_LOG_NOTE(("nntp_are_connections_available: comparing %08lX %s", tmpConn, tmpConn->busy ? "busy" : "available"));

		if (conn != tmpConn && tmpConn->busy && !PL_strcasecmp(tmpConn->hostname, conn->hostname))
			connCount++;
	}

	return connCount < kMaxConnectionsPerHost;
}

static XP_Bool isOnline = TRUE;
static XP_Bool prefInitialized = FALSE;

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif

MODULE_PRIVATE int PR_CALLBACK NET_OnlinePrefChangedFunc(const char *pref, void *data) 
{
	int status=0;
	int32 port=0;
	char * socksHost = NULL;
	char text[MAXHOSTNAMELEN + 8];
	extern void NET_MaybeDownloadAutoAdminCfgFile(void);

	if (!PL_strcasecmp(pref,"network.online")) 
		status = PREF_GetBoolPref("network.online", &isOnline);

	if ( isOnline ) {
		CACHE_CloseAllOpenSARCache();

		NET_MaybeDownloadAutoAdminCfgFile();

		/* If the user wants to use a socks server set it up. */
		if ( (NET_GetProxyStyle() == PROXY_STYLE_MANUAL) ) {
			PREF_CopyCharPref("network.hosts.socks_server",&socksHost);
			PREF_GetIntPref("network.hosts.socks_serverport",&port);
			if (socksHost && *socksHost && port) {
				PR_snprintf(text, sizeof(text), "%s:%d", socksHost, port);  
				NET_SetSocksHost(text);
			}
			else {
				NET_SetSocksHost(socksHost); /* NULL is ok */
			}
		}
	}
	else {
		CACHE_OpenAllSARCache();
	}

	return status;
}

MODULE_PRIVATE int PR_CALLBACK NET_NewsMaxArticlesChangedFunc(const char *pref, void *data) 
{
	int status=0;
	if (!PL_strcasecmp(pref,"news.max_articles")) 
	{
		int32 maxArticles;

		status = PREF_GetIntPref("news.max_articles", &maxArticles);
		NET_SetNumberOfNewsArticlesInListing(maxArticles);
	}
	return status;
}

MODULE_PRIVATE int PR_CALLBACK net_news_timeout_changed (const char *pref, void *data)
{
	return PREF_GetIntPref ("news.timeout", &net_news_timeout);
}

MODULE_PRIVATE XP_Bool
NET_IsOffline()
{
	/*  Cache this value, and register a pref callback to
		find out when it changes. 
	*/
	if (!prefInitialized)
	{
		/*int status =*/ PREF_GetBoolPref("network.online", &isOnline);
		PREF_RegisterCallback("network.online",NET_OnlinePrefChangedFunc, NULL);

		/* because this routine gets called so often, we can register this callback here too. */
		PREF_RegisterCallback("news.max_articles", NET_NewsMaxArticlesChangedFunc, NULL);

		PREF_GetIntPref ("news.timeout", &net_news_timeout);
		PREF_RegisterCallback ("news.timeout", net_news_timeout_changed, NULL);

		prefInitialized = TRUE;
	}
	return !isOnline;
}

MODULE_PRIVATE int
NET_NewsLoad (ActiveEntry *ce, char *proxy_server)
{
  int status = 0;
  NewsConData *cd = PR_NEW(NewsConData);
  XP_Bool bVal = FALSE;
  XP_Bool default_host = FALSE;
  char *url = ce->URL_s->address;
  char *host_and_port = 0;
  int32 port = 0;
  char *group = 0;
  char *message_id = 0;
  char *command_specific_data = 0;
  XP_Bool cancel_p = FALSE;
  char* colon;
  MSG_NewsHost* defhost = NULL;
  nsresult rv;
  
  if(!cd)
	{
	  status = MK_OUT_OF_MEMORY;
	  goto FAIL;
	}

  memset(cd, 0, sizeof(NewsConData));
  ce->URL_s->content_length = 0;
  StrAllocCopy(cd->proxy_server, proxy_server);
  
#ifdef USE_PANES
  cd->pane = ce->URL_s->msg_pane;
  if (!cd->pane)
  {
    NNTP_LOG_NOTE(("NET_NewsLoad: url->msg_pane NULL for URL: %s\n", ce->URL_s->address));
	if (NET_IsNewsMessageURL(ce->URL_s->address))
		cd->pane = MSG_FindPane(ce->window_id, MSG_MESSAGEPANE);
	else
		cd->pane = MSG_FindPane(ce->window_id, MSG_ANYPANE);
  }
  PR_ASSERT(cd->pane && MSG_GetContext(cd->pane) == ce->window_id);
  if (!cd->pane)
  {
	PR_ASSERT(FALSE);
	status = -1;				/* ### */
	goto FAIL;
  }
#endif
  
#ifdef DEBUG
  {
	char urlDate[64];
	PRTime now = PR_Now();
	XP_StrfTime(ce->window_id, urlDate, sizeof(urlDate), XP_DATE_TIME_FORMAT, localtime(&now));
	NNTP_LOG_NOTE (("******** Begin loading news URL [ %s ] at %s", ce->URL_s->address, urlDate));
  }
#endif

  cd->output_buffer = (char *) PR_Malloc(OUTPUT_BUFFER_SIZE);
  if(!cd->output_buffer)
	{
	  status = MK_OUT_OF_MEMORY;
	  goto FAIL;
	}

  ce->con_data = cd;
  ce->socket = NULL;
  cd->article_num = -1;

  PR_ASSERT (url);
  if (!url)
	{
	  status = -1;
	  goto FAIL;
	}


  status = NET_parse_news_url (url, &host_and_port, &bVal,
							   &group, &message_id, &command_specific_data);
  if (status < 0)
	goto FAIL;

  colon = PL_strchr (host_and_port, ':');
  if (colon) {
	*colon = '\0';
	sscanf (colon+1, " %u ", &port);
  }

#ifdef XPCOM_NEWSHOST
  rv = NS_NewMsgNewsHost(&cd->news_host, host_and_port, bVal, port);
#else
  cd->host = MSG_CreateNewsHost(MSG_GetMaster(cd->pane), host_and_port,
								bVal, port);
#endif
  if (colon) *colon = ':';

#ifdef XPCOM_NEWSHOST
  PR_ASSERT(NS_SUCCEEDED(rv));
  if (!NS_SUCCEEDED(rv)) {
#else
  PR_ASSERT(cd->host);
  if (!cd->host) {
#endif
	status = -1;
	goto FAIL;
  }

  if (message_id && command_specific_data && !PL_strcmp (command_specific_data, "?cancel"))
	cancel_p = TRUE;

  StrAllocCopy(cd->path, message_id);
#ifdef XPCOM_NEWSGROUP
  cd->newsgroup->SetName(group);
#else
  StrAllocCopy(cd->group_name, group);
#endif

  /* make sure the user has a news host configured */
  
#ifndef XPCOM_NEWSHOST
  defhost = MSG_GetDefaultNewsHost(MSG_GetMaster(cd->pane));

  if (defhost == NULL)
    {
	  char* alert = NET_ExplainErrorDetails(MK_NNTP_SERVER_NOT_CONFIGURED);
	  FE_Alert(ce->window_id, alert);
#ifdef XP_MAC
	  /* AFTER the alert, not before! */
	  FE_EditPreference(PREF_NewsHost);
#endif
      status = -1;
      goto FAIL;
    }

  default_host = (cd->host == defhost);
#endif
  
  if (cancel_p && !ce->URL_s->internal_url)
	{
	  /* Don't allow manual "news:ID?cancel" URLs, only internal ones. */
	  status = -1;
	  goto FAIL;
	}
  else if (ce->URL_s->method == URL_POST_METHOD)
	{
	  /* news://HOST done with a POST instead of a GET;
		 this means a new message is being posted.
		 Don't allow this unless it's an internal URL.
	   */
	  if (!ce->URL_s->internal_url)
		{
		  status = -1;
		  goto FAIL;
		}
	  PR_ASSERT (!group && !message_id && !command_specific_data);
	  cd->type_wanted = NEWS_POST;
	  StrAllocCopy(cd->path, "");
	}
  else if (message_id)
	{
	  /* news:MESSAGE_ID
		 news:/GROUP/MESSAGE_ID (useless)
		 news://HOST/MESSAGE_ID
		 news://HOST/GROUP/MESSAGE_ID (useless)
	   */
	  if (cancel_p)
		cd->type_wanted = CANCEL_WANTED;
	  else
		cd->type_wanted = ARTICLE_WANTED;
	}
  else if (command_specific_data)
	{
	  if (PL_strstr (command_specific_data, "?newgroups"))
	    /* news://HOST/?newsgroups
	        news:/GROUP/?newsgroups (useless)
	        news:?newsgroups (default host)
	     */
	    cd->type_wanted = NEW_GROUPS;
      else
	  {
		if (PL_strstr(command_specific_data, "?list-pretty"))
		{
			cd->type_wanted = PRETTY_NAMES_WANTED;
			cd->command_specific_data = PL_strdup(command_specific_data);
		}
		else if (PL_strstr(command_specific_data, "?profile"))
		{
			cd->type_wanted = PROFILE_WANTED;
			cd->command_specific_data = PL_strdup(command_specific_data);
		}
		else if (PL_strstr(command_specific_data, "?list-ids"))
		{
			cd->type_wanted = IDS_WANTED;
			cd->command_specific_data = PL_strdup(command_specific_data);
		}
		else
		{
			cd->type_wanted = SEARCH_WANTED;
			cd->command_specific_data = PL_strdup(command_specific_data);
			cd->current_search = cd->command_specific_data;
		}
	  }
	}
  else if (group)
	{
	  /* news:GROUP
		 news:/GROUP
		 news://HOST/GROUP
	   */
	  if (ce->window_id->type != MWContextNews && ce->window_id->type != MWContextNewsMsg 
		  && ce->window_id->type != MWContextMailMsg
		  && ce->window_id->type != MWContextMail && ce->window_id->type != MWContextMailNewsProgress)
		{
		  status = -1;
		  goto FAIL;
		}
	  if (PL_strchr (group, '*'))
		cd->type_wanted = LIST_WANTED;
	  else
		cd->type_wanted = GROUP_WANTED;
	}
  else
	{
	  /* news:
	     news://HOST
	   */
	  cd->type_wanted = READ_NEWS_RC;
	}


  /* let's look for the user name and password in the url */
  {
		char * unamePwd = NET_ParseURL(url, GET_USERNAME_PART | GET_PASSWORD_PART);
		char *userName,*colon,*password=NULL;

		/* get the username & password out of the combo string */
		if( (colon = PL_strchr(unamePwd, ':')) != NULL ) 
		{
			*colon = '\0';
			userName = PL_strdup(unamePwd);
			password = PL_strdup(colon+1);
			*colon = ':';
			PR_Free(unamePwd);
		}
		else 
		{
			userName = unamePwd;
		}
		if (userName)
		{
		    char *mungedUsername = HG64643(userName);
#ifdef XPCOM_NEWSGROUP
            cd->newsgroup->SetUsername(mungedUsername);
#else
			MSG_SetNewsgroupUsername(cd->pane, mungedUsername);
#endif
			PR_FREEIF(mungedUsername);
			PR_Free(userName);
		}
		if (password)
		{
		    char *mungedPassword = HG64643(password);
#ifdef XPCOM_NEWSGROUP
            cd->newsgroup->SetPassword(mungedPassword);
#else
			MSG_SetNewsgroupPassword(cd->pane, mungedPassword);
#endif
			PR_FREEIF(mungedPassword);
			PR_Free(password);
		}

    }
  /* At this point, we're all done parsing the URL, and know exactly
	 what we want to do with it.
   */

#ifndef NO_ARTICLE_CACHEING
  /* Turn off caching on all news entities, except articles. */
  /* It's very important that this be turned off for CANCEL_WANTED;
	 for the others, I don't know what cacheing would cause, but
	 it could only do harm, not good. */
  if(cd->type_wanted != ARTICLE_WANTED)
#endif
  	ce->format_out = CLEAR_CACHE_BIT (ce->format_out);

  if (cd->type_wanted == ARTICLE_WANTED)
  {
		uint32 number = 0;
#ifdef XPCOM_NEWSGROUP
        nsresult rv;
        PRBool articleIsOffline;
        rv =cd->newsgroup->IsOfflineArticle(number,&articleIsOffline);
#else
		const char *group = 0;
		XP_Bool articleIsOffline = MSG_IsOfflineArticle (cd->pane, cd->path, &group, &number);
#endif
		if (NET_IsOffline() || (NS_SUCCEEDED(rv) && articleIsOffline))
		{
			ce->local_file = TRUE;
			cd->articleIsOffline = articleIsOffline;
			ce->socket = NULL;

#if 0
            /* this should be handled by NET_SetCallNetlibAllTheTime */
			net_call_all_the_time_count++;
#endif
			if (!articleIsOffline)
				ce->format_out = CLEAR_CACHE_BIT(ce->format_out);
			NET_SetCallNetlibAllTheTime(ce->window_id,"mknews");
#ifdef XPCOM_OFFLINE
            rv = NS_NewOfflineNewState(&cd->offline_state,
                                       cd->newsgroup, number);
#else
			MSG_StartOfflineRetrieval(cd->pane, group, number, &cd->offlineState);
#endif
		}
  }
#ifdef XPCOM_OFFLINE
  if (cd->offline_state)
#else
  if (cd->offlineState)
#endif
	  goto FAIL;	/* we don't need to do any of this connection stuff */

  /* check for established connection and use it if available
   */
  if(nntp_connection_list)
	{
	  NNTPConnection * tmp_con;
	  XP_List * list_entry = nntp_connection_list;

	  while((tmp_con = (NNTPConnection *)XP_ListNextObject(list_entry))
			!= NULL)
		{
		  /* if the hostnames match up exactly and the connection
		   * is not busy at the moment then reuse this connection.
		   */
		  if(!PL_strcmp(tmp_con->hostname, host_and_port)
			 && HG78293
			 &&!tmp_con->busy)
			{
			  /* check to see if this connection can be reused, or if it should be aged away */
			  int32 con_age = XP_TIME() - tmp_con->last_used_time;
			  if (con_age <= net_news_timeout)
			  {
			    cd->control_con = tmp_con;

			    NNTP_LOG_NOTE(("Reusing control_con %08lX (age %ld secs) for %s", tmp_con, con_age, url));

			    /* set select on the control socket */
			    ce->socket = cd->control_con->csock;
			    NET_SetReadSelect(ce->window_id, cd->control_con->csock);
			    cd->control_con->prev_cache = TRUE;  /* this was from the cache */
			    break;
			  }
			  else
			  {
			    NNTP_LOG_NOTE(("Aging away control_con %08lX (age %ld secs)", tmp_con, con_age));

				/* kill idle conn which has lived too long */
				list_entry = list_entry->prev ? list_entry->prev : nntp_connection_list;
                XP_ListRemoveObject(nntp_connection_list, tmp_con);
                net_nntp_close(tmp_con, ce->status);
                FREEIF(tmp_con->hostname);
                PR_Free(tmp_con);
			  }
			}
		}
	}
  else
	{
	  /* initialize the connection list
	   */
	  nntp_connection_list = XP_ListNew();
	}


  if(cd->control_con)
	{
	  cd->next_state = SEND_FIRST_NNTP_COMMAND;
	  /* set the connection busy
	   */
	  cd->control_con->busy = TRUE;
	  NET_TotalNumberOfOpenConnections++;
	}
  else
	{
	  /* build a control connection structure so we
	   * can store the data as we go along
	   */
	  cd->control_con = PR_NEW(NNTPConnection);
	  if(!cd->control_con)
		{
		  status = MK_OUT_OF_MEMORY;
		  goto FAIL;
		}
	  NNTP_LOG_NOTE(("Created control_con %08lX for %s", cd->control_con, ce->URL_s->address));
	  memset(cd->control_con, 0, sizeof(NNTPConnection));

#ifndef XPCOM_NEWSHOST
	  cd->control_con->default_host = default_host;
#endif
	  StrAllocCopy(cd->control_con->hostname, host_and_port);
	  NNTP_LOG_NOTE(("Set host string: %s", cd->control_con->hostname));

	  HG01869

	  cd->control_con->prev_cache = FALSE;  /* this wasn't from the cache */

	  cd->control_con->csock = NULL;

	  cd->control_con->last_used_time = XP_TIME();

	  /* define this to test support for older NNTP servers
	   * that don't support the XOVER command
	   */
#ifdef TEST_NO_XOVER_SUPPORT
	  cd->control_con->no_xover = TRUE;
#endif /* TEST_NO_XOVER_SUPPORT */

	  /* add this structure to the connection list even
	   * though it's not really valid yet.
	   * we will fill it in as we go and if
	   * an error occurs will will remove it from the
	   * list.  No one else will be able to use it since
	   * we will mark it busy.
	   */
	  XP_ListAddObject(nntp_connection_list, cd->control_con);

	  /* set the connection busy
	   */
	  cd->control_con->busy = TRUE;

	  /* gate the maximum number of cached connections
	   * but don't delete busy ones.
	   */
	  if(XP_ListCount(nntp_connection_list) > kMaxCachedConnections)
		{
		  XP_List * list_ptr = nntp_connection_list->next;
		  NNTPConnection * con;

		  while(list_ptr)
			{
			  con = (NNTPConnection *) list_ptr->object;
			  list_ptr = list_ptr->next;
			  if(!con->busy)
				{
				  XP_ListRemoveObject(nntp_connection_list, con);
				  net_nntp_close (con, status);
				  FREEIF(con->hostname);
				  PR_Free(con);
				  break;
				}
			}
		}

	  cd->next_state = NNTP_CONNECT;
	}

 FAIL:

  FREEIF (host_and_port);
  FREEIF (group);
  FREEIF (message_id);
  FREEIF (command_specific_data);

  if (status < 0)
  {
	  FREEIF (cd->output_buffer);
	  FREEIF (cd);
	  ce->URL_s->error_msg = NET_ExplainErrorDetails(status);
	  return status;
  }
  else 
  {
	  return (net_ProcessNews (ce));
  }
}
/* process the offline news state machine, such as it is. */

PRIVATE int
NET_ProcessOfflineNews(ActiveEntry *ce, NewsConData *cd)
{
	int32 read_size = 0;
	int status;
    nsresult rv;
    
    cd->pause_for_read  = TRUE;

	if (cd->stream)
		read_size = (*cd->stream->is_write_ready)
								(cd->stream);
	else
	{
		StrAllocCopy(ce->URL_s->content_type, cd->articleIsOffline ? MESSAGE_RFC822 : TEXT_HTML);
		if (CLEAR_CACHE_BIT(ce->format_out) == FO_PRESENT)
		{
			ce->status = net_InitializeNewsFeData (ce);
			if (ce->status < 0)
			{
			  /* #### what error message? */
				return ce->status;
			}
		}
		cd->stream = NET_StreamBuilder(ce->format_out, ce->URL_s, ce->window_id); 
        if (!cd->stream)
        	ce->status = -1;      
	}
	if(!read_size)
		return(0);  /* wait until we are ready to write */
	else
		read_size = MIN(read_size, NET_Socket_Buffer_Size);
#ifdef XPCOM_OFFLINE
    rv = cd->offline_state->Process(&NET_Socket_Buffer, read_size, &status);
    if (NS_SUCCEEDED(rv) && status > 0)
#else
	status = MSG_ProcessOfflineNews(cd->offlineState, NET_Socket_Buffer, read_size);
	if(status > 0)
#endif
	{
		ce->bytes_received += status;
		FE_GraphProgress(ce->window_id, ce->URL_s,
						 ce->bytes_received, status,
						 ce->URL_s->content_length);
		PUT_STREAM(NET_Socket_Buffer, status);	/* blow off error? ### dmb */
	}

	if (status == 0)
	{
 	  if (cd->stream)
		COMPLETE_STREAM;
#if 0
      /* this should be handled by NET_ClearCallNetlibAllTheTime()*/
       net_call_all_the_time_count--;
        if(net_call_all_the_time_count < 1)
#endif
           	NET_ClearCallNetlibAllTheTime(ce->window_id,"mknews");

		if(cd->destroy_graph_progress)
		  FE_GraphProgressDestroy(ce->window_id, 
								  ce->URL_s, 
								  cd->original_content_length,
								  ce->bytes_received);
		status = -1;

	}
	return status;
}

/* process the state machine
 *
 * returns negative when completely done
 */
MODULE_PRIVATE int
net_ProcessNews (ActiveEntry *ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;

#ifdef XPCOM_OFFLINE
    if (cd->offline_state != NULL)
#else
	if (cd->offlineState != NULL)
#endif
	{
		return NET_ProcessOfflineNews(ce, cd);
	}
    cd->pause_for_read = FALSE;

    while(!cd->pause_for_read)
      {

#if DEBUG
        NNTP_LOG_NOTE(("Next state: %s",stateLabels[cd->next_state])); 
#endif

        switch(cd->next_state)
          {
            case NNTP_RESPONSE:
                ce->status = net_news_response(ce);
                break;

#ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
			case NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE:
				if (nntp_are_connections_available(cd->control_con))
				{
					/* haven't reached connection ceiling on this host; go to connect */
					cd->next_state = NNTP_CONNECTIONS_ARE_AVAILABLE;
					cd->pause_for_read = FALSE;
				}
				else
				{
					NET_SetReadSelect(ce->window_id, ce->socket);
					cd->pause_for_read = TRUE;
				}
				break;
			case NNTP_CONNECTIONS_ARE_AVAILABLE:
				/* release our hacky little lock and move on to connection */
#if 0
      /* this should be handled by NET_ClearCallNetlibAllTheTime()*/
				net_call_all_the_time_count--;
				if (net_call_all_the_time_count == 0)
#endif
					NET_ClearCallNetlibAllTheTime(ce->window_id,"mknews");
				
				cd->next_state = NNTP_CONNECT;
				cd->pause_for_read = FALSE;
				break;
#endif

            case NNTP_CONNECT:
				if (nntp_are_connections_available(cd->control_con))
				{
					ce->status = NET_BeginConnect(
						HG51154, 
						NULL,
						"NNTP", 
						HG28465,
						&ce->socket, 
						HG42817,
						&cd->tcp_con_data, 
						ce->window_id,
						&ce->URL_s->error_msg,
						ce->socks_host,
						ce->socks_port,
						ce->URL_s->localIP);

					if(ce->socket != NULL)
						NET_TotalNumberOfOpenConnections++;

					cd->pause_for_read = TRUE;
					if(ce->status == MK_CONNECTED)
					{
					    HG51514
						{
							cd->next_state = NNTP_RESPONSE;
							cd->next_state_after_response = NNTP_LOGIN_RESPONSE;
						}
						NET_SetReadSelect(ce->window_id, ce->socket);
						cd->control_con->csock = ce->socket;
					}
					else if(ce->status > -1)
					{
						ce->con_sock = ce->socket;  /* set con_sock so we can select on it */
						cd->control_con->csock = ce->socket;
						NET_SetConnectSelect(ce->window_id, ce->con_sock);
						cd->next_state = NNTP_CONNECT_WAIT;
					}
				}
				else
				{
#ifdef BLOCK_UNTIL_AVAILABLE_CONNECTION
					/* ###phil this doesn't work yet, but the idea is that we've
					 * maxed out connections on this host; stay in this state 
					 */
					ce->con_sock = ce->socket;  /* set con_sock so we can select on it */
					cd->control_con->csock = ce->socket;
					NET_SetConnectSelect(ce->window_id, ce->con_sock);
					cd->next_state = NNTP_BLOCK_UNTIL_CONNECTIONS_ARE_AVAILABLE;
#else
					ce->status = -1;
					cd->next_state = NNTP_ERROR;
					ce->socket = cd->control_con->csock = NULL;
#endif
				}
				break;

            case NNTP_CONNECT_WAIT:
                ce->status = NET_FinishConnect(
							  HG65321,
							  "NNTP", 
							  HG28465, 
							  &ce->socket, 
							  &cd->tcp_con_data, 
							  ce->window_id,
							  &ce->URL_s->error_msg,
							  ce->URL_s->localIP);
  
                cd->pause_for_read = TRUE;
                if(ce->status == MK_CONNECTED)
                  {
				    cd->control_con->csock = ce->socket;
					HG28477
                      {
                        cd->next_state = NNTP_RESPONSE;
                        cd->next_state_after_response = NNTP_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->socket);
                  }
				else if(ce->status < 0)
                  {
                    	NET_ClearConnectSelect(ce->window_id, ce->con_sock);
                  }
				else
                  {
					/* the not yet connected state */

        			/* unregister the old ce->socket from the select list
         			 * and register the new value in the case that it changes
         			 */
        			if(ce->con_sock != ce->socket)
          			  {
            			NET_ClearConnectSelect(ce->window_id, ce->con_sock);
            			ce->con_sock = ce->socket;
            			NET_SetConnectSelect(ce->window_id, ce->con_sock);
          			  }
                  }
                break;

			HG42871

            case NNTP_LOGIN_RESPONSE:
                ce->status = net_nntp_login_response(ce);
                break;

			case NNTP_SEND_MODE_READER:
                ce->status = net_nntp_send_mode_reader(ce);
                break;

			case NNTP_SEND_MODE_READER_RESPONSE:
                ce->status = net_nntp_send_mode_reader_response(ce);
                break;

			case SEND_LIST_EXTENSIONS:
				ce->status = net_nntp_send_list_extensions(ce);
				break;
			case SEND_LIST_EXTENSIONS_RESPONSE:
				ce->status = net_nntp_send_list_extensions_response(ce);
				break;
			case SEND_LIST_SEARCHES:
				ce->status = net_nntp_send_list_searches(ce);
				break;
			case SEND_LIST_SEARCHES_RESPONSE:
				ce->status = net_nntp_send_list_searches_response(ce);
				break;
			case NNTP_LIST_SEARCH_HEADERS:
				ce->status = net_send_list_search_headers(ce);
				break;
			case NNTP_LIST_SEARCH_HEADERS_RESPONSE:
				ce->status = net_send_list_search_headers_response(ce);
				break;
			case NNTP_GET_PROPERTIES:
				ce->status = nntp_get_properties(ce);
				break;
			case NNTP_GET_PROPERTIES_RESPONSE:
				ce->status = nntp_get_properties_response(ce);
				break;
				
			case SEND_LIST_SUBSCRIPTIONS:
				ce->status = net_send_list_subscriptions(ce);
				break;
			case SEND_LIST_SUBSCRIPTIONS_RESPONSE:
				ce->status = net_send_list_subscriptions_response(ce);
				break;

            case SEND_FIRST_NNTP_COMMAND:
                ce->status = net_send_first_nntp_command(ce);
                break;

            case SEND_FIRST_NNTP_COMMAND_RESPONSE:
                ce->status = net_send_first_nntp_command_response(ce);
                break;

            case NNTP_SEND_GROUP_FOR_ARTICLE:
                ce->status = net_send_group_for_article(ce);
                break;
            case NNTP_SEND_GROUP_FOR_ARTICLE_RESPONSE:
                ce->status = net_send_group_for_article_response(ce);
                break;
            case NNTP_SEND_ARTICLE_NUMBER:
                ce->status = net_send_article_number(ce);
                break;

            case SETUP_NEWS_STREAM:
                ce->status = net_setup_news_stream(ce);
                break;

			case NNTP_BEGIN_AUTHORIZE:
				ce->status = net_news_begin_authorize(ce);
                break;

			case NNTP_AUTHORIZE_RESPONSE:
				ce->status = net_news_authorize_response(ce);
                break;

			case NNTP_PASSWORD_RESPONSE:
				ce->status = net_news_password_response(ce);
                break;
    
            case NNTP_READ_LIST_BEGIN:
                ce->status = net_read_news_list_begin(ce);
                break;

            case NNTP_READ_LIST:
                ce->status = net_read_news_list(ce);
                break;

			case DISPLAY_NEWSGROUPS:
				ce->status = net_display_newsgroups(ce);
                break;

			case NNTP_NEWGROUPS_BEGIN:
				ce->status = net_newgroups_begin(ce);
                break;
    
			case NNTP_NEWGROUPS:
				ce->status = net_process_newgroups(ce);
                break;
    
            case NNTP_BEGIN_ARTICLE:
				ce->status = net_begin_article(ce);
                break;

            case NNTP_READ_ARTICLE:
			  {
				char *line;
				NewsConData * cd =
				  (NewsConData *)ce->con_data;

				ce->status = NET_BufferedReadLine(ce->socket, &line,
												 &cd->data_buf, 
												 &cd->data_buf_size,
												 (Bool*)&cd->pause_for_read);

				if(ce->status == 0)
				  {
					cd->next_state = NNTP_ERROR;
					cd->pause_for_read = FALSE;
					ce->URL_s->error_msg =
					  NET_ExplainErrorDetails(MK_NNTP_SERVER_ERROR);
					return(MK_NNTP_SERVER_ERROR);
				  }

				if(ce->status > 1)
				  {
					ce->bytes_received += ce->status;
					FE_GraphProgress(ce->window_id, ce->URL_s,
									 ce->bytes_received, ce->status,
									 ce->URL_s->content_length);
				  }

				if(!line)
				  return(ce->status);  /* no line yet or error */

				if (cd->type_wanted == CANCEL_WANTED &&
					cd->response_code != MK_NNTP_RESPONSE_ARTICLE_HEAD)
				  {
					/* HEAD command failed. */
					return MK_NNTP_CANCEL_ERROR;
				  }

				if (line[0] == '.' && line[1] == 0)
				  {
					if (cd->type_wanted == CANCEL_WANTED)
					  cd->next_state = NEWS_START_CANCEL;
					else
					  cd->next_state = NEWS_DONE;

					cd->pause_for_read = FALSE;
				  }
				else
				  {
					if (line[0] == '.')
					  PL_strcpy (cd->output_buffer, line + 1);
					else
					  PL_strcpy (cd->output_buffer, line);

					/* When we're sending this line to a converter (ie,
					   it's a message/rfc822) use the local line termination
					   convention, not CRLF.  This makes text articles get
					   saved with the local line terminators.  Since SMTP
					   and NNTP mandate the use of CRLF, it is expected that
					   the local system will convert that to the local line
					   terminator as it is read.
					 */
					PL_strcat (cd->output_buffer, LINEBREAK);
					/* Don't send content-type to mime parser if we're doing a cancel
					  because it confuses mime parser into not parsing.
					  */
					if (cd->type_wanted != CANCEL_WANTED || XP_STRNCMP(cd->output_buffer, "Content-Type:", 13))
						ce->status = PUTSTRING(cd->output_buffer);
				  }
				break;
			  }

            case NNTP_XOVER_BEGIN:
			    NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_READ_NEWSGROUPINFO));
			    ce->status = net_read_xover_begin(ce);
                break;

		  case NNTP_FIGURE_NEXT_CHUNK:
				ce->status = net_figure_next_chunk(ce);
				break;

			case NNTP_XOVER_SEND:
				ce->status = net_xover_send(ce);
				break;
    
            case NNTP_XOVER:
                ce->status = net_read_xover(ce); 
                break;

            case NNTP_XOVER_RESPONSE:
                ce->status = net_read_xover_response(ce); 
                break;

            case NEWS_PROCESS_XOVER:
		    case NEWS_PROCESS_BODIES:
                NET_Progress(ce->window_id, XP_GetString(XP_PROGRESS_SORT_ARTICLES));
				  ce->status = net_process_xover(ce);
                break;

			case NNTP_PROFILE_ADD:
				ce->status = net_profile_add (ce);
				break;
			case NNTP_PROFILE_ADD_RESPONSE:
				ce->status = net_profile_add_response(ce);
				break;
			case NNTP_PROFILE_DELETE:
				ce->status = net_profile_delete (ce);
				break;
			case NNTP_PROFILE_DELETE_RESPONSE:
				ce->status = net_profile_delete_response(ce);
				break;

            case NNTP_READ_GROUP:
                ce->status = net_read_news_group(ce);
                break;
    
            case NNTP_READ_GROUP_RESPONSE:
                ce->status = net_read_news_group_response(ce);
                break;

            case NNTP_READ_GROUP_BODY:
                ce->status = net_read_news_group_body(ce);
                break;

	        case NNTP_SEND_POST_DATA:
	            ce->status = net_send_news_post_data(ce);
	            break;

	        case NNTP_SEND_POST_DATA_RESPONSE:
	            ce->status = net_send_news_post_data_response(ce);
	            break;

			case NNTP_CHECK_FOR_MESSAGE:
				ce->status = net_check_for_message(ce);
				break;

			case NEWS_NEWS_RC_POST:
		        ce->status = net_NewsRCProcessPost(ce);
		        break;

            case NEWS_DISPLAY_NEWS_RC:
		        ce->status = net_DisplayNewsRC(ce);
		        break;

            case NEWS_DISPLAY_NEWS_RC_RESPONSE:
		        ce->status = net_DisplayNewsRCResponse(ce);
		        break;

            case NEWS_START_CANCEL:
		        ce->status = net_start_cancel(ce);
		        break;

            case NEWS_DO_CANCEL:
		        ce->status = net_do_cancel(ce);
		        break;

			case NNTP_XPAT_SEND:
				ce->status = net_xpat_send(ce);
				break;
			case NNTP_XPAT_RESPONSE:
				ce->status = net_xpat_response(ce);
				break;
			case NNTP_SEARCH:
				ce->status = net_nntp_search(ce);
				break;
			case NNTP_SEARCH_RESPONSE:
				ce->status = net_nntp_search_response(ce);
				break;
			case NNTP_SEARCH_RESULTS:
				ce->status = net_nntp_search_results(ce);
				break;
			case NNTP_LIST_PRETTY_NAMES:
				ce->status = net_list_pretty_names(ce);
				break;
			case NNTP_LIST_PRETTY_NAMES_RESPONSE:
				ce->status = net_list_pretty_names_response(ce);
				break;
			case NNTP_LIST_XACTIVE:
				ce->status = net_list_xactive(ce);
				break;
			case NNTP_LIST_XACTIVE_RESPONSE:
				ce->status = net_list_xactive_response(ce);
				break;
			case NNTP_LIST_GROUP:
				ce->status = net_list_group(ce);
				break;
			case NNTP_LIST_GROUP_RESPONSE:
				ce->status = net_list_group_response(ce);
				break;
	        case NEWS_DONE:
			  /* call into libmsg and see if the article counts
			   * are up to date.  If they are not then we
			   * want to do a "news://host/group" URL so that we
			   * can finish up the article counts.
			   */
			  if (cd->stream)
				COMPLETE_STREAM;

	            cd->next_state = NEWS_FREE;
                /* set the connection unbusy
     	         */
    		    cd->control_con->busy = FALSE;
                NET_TotalNumberOfOpenConnections--;

				NET_ClearReadSelect(ce->window_id, cd->control_con->csock);
				NET_RefreshCacheFileExpiration(ce->URL_s);
	            break;

	        case NEWS_ERROR:
	            if(cd->stream)
		             ABORT_STREAM(ce->status);
	            cd->next_state = NEWS_FREE;
    	        /* set the connection unbusy
     	         */
    		    cd->control_con->busy = FALSE;
                NET_TotalNumberOfOpenConnections--;

				if(cd->control_con->csock != NULL)
				  {
					NET_ClearReadSelect(ce->window_id, cd->control_con->csock);
				  }
	            break;

	        case NNTP_ERROR:
	            if(cd->stream)
				  {
		            ABORT_STREAM(ce->status);
					cd->stream=0;
				  }
    
				if(cd->control_con && cd->control_con->csock != NULL)
				  {
					NNTP_LOG_NOTE(("Clearing read and connect select on socket %d",
															cd->control_con->csock));
					NET_ClearConnectSelect(ce->window_id, cd->control_con->csock);
					NET_ClearReadSelect(ce->window_id, cd->control_con->csock);
#if 0
                    /* this is probably depricated/merged into
                       NET_ClearReadSelect */
					NET_ClearFileReadSelect(ce->window_id,
										   cd->control_con->csock);
#endif
#ifdef XP_WIN
					if(cd->calling_netlib_all_the_time)
					{
						cd->calling_netlib_all_the_time = FALSE;
#if 0
      /* this should be handled by NET_ClearCallNetlibAllTheTime()*/
						net_call_all_the_time_count--;
						if(net_call_all_the_time_count == 0)
#endif
							NET_ClearCallNetlibAllTheTime(ce->window_id,"mknews");
					}
#endif /* XP_WIN */
#if defined(XP_WIN) || (defined(XP_UNIX)&&defined(UNIX_ASYNC_DNS))
                    NET_ClearDNSSelect(ce->window_id, cd->control_con->csock);
#endif /* XP_WIN || XP_UNIX */
				    net_nntp_close (cd->control_con, ce->status);  /* close the
																  socket */ 
					NET_TotalNumberOfOpenConnections--;
					ce->socket = NULL;
				  }

                /* check if this connection came from the cache or if it was
                 * a new connection.  If it was not new lets start it over
				 * again.  But only if we didn't have any successful protocol
				 * dialog at all.
                 */
                if(cd->control_con && cd->control_con->prev_cache &&
				   !cd->some_protocol_succeeded)
                  {
                     cd->control_con->prev_cache = FALSE;
                     cd->next_state = NNTP_CONNECT;
                     ce->status = 0;  /* keep going */
                  }
                else
                  {
                    cd->next_state = NEWS_FREE;
            
                    /* remove the connection from the cache list
                     * and free the data
                     */
                    XP_ListRemoveObject(nntp_connection_list, cd->control_con);
                    if(cd->control_con)
                      {
                        FREEIF(cd->control_con->hostname);
                        FREE(cd->control_con);
						cd->control_con = NULL;
                      }
                  }
                break;
    
            case NEWS_FREE:

				  /* do we need to know if we're parsing xover to call finish xover? */
				/* yes, I think we do! Why did I think we should??? */
					/* If we've gotten to NEWS_FREE and there is still XOVER
					   data, there was an error or we were interrupted or
					   something.  So, tell libmsg there was an abnormal
					   exit so that it can free its data. */
#ifdef XPCOM_XOVER
              if (cd->xover_parser != NULL)
#else
				if (cd->xover_parse_state != NULL)
#endif
				{
					int status;
                    nsresult rv;
/*					PR_ASSERT (ce->status < 0);*/
#ifdef XPCOM_XOVER
                    /* XXX - how/when to Release() this? */
                    rv = cd->xover_parser->Finish(ce->status,&status);
                    PR_ASSERT(NS_SUCCEEDED(rv));

                    if (NS_SUCCEEDED(rv))
                      NS_RELEASE(cd->xover_parser);

#else
					status = MSG_FinishXOVER (cd->pane,
											  &cd->xover_parse_state,
											  ce->status);
					PR_ASSERT (!cd->xover_parse_state);
#endif
					if (NS_SUCCEEDED(rv) &&
                        ce->status >= 0 && status < 0)
					  ce->status = status;
				}
				else
				{
#ifdef XPCOM_NEWSPARSE
                  /* XXX - state is stored in the MSG_Pane cd->pane */
                  NS_RELEASE(cd->article_list);
#else
					MSG_ClearListNewsgroupState(cd->pane, cd->host, cd->group_name, ce->status);
#endif
				}

				if (cd->control_con)
					cd->control_con->last_used_time = XP_TIME();

                FREEIF(cd->path);
                FREEIF(cd->response_txt);
                FREEIF(cd->data_buf);

				FREEIF(cd->output_buffer);
				FREEIF(cd->proxy_server);
                FREEIF(cd->stream);  /* don't forget the stream */
            	if(cd->tcp_con_data)
                	NET_FreeTCPConData(cd->tcp_con_data);

#ifdef XPCOM_NEWSGROUP
                NS_RELEASE(cd->newsgroup);
#else
                FREEIF(cd->group_name);
#endif

				FREEIF (cd->cancel_id);
				FREEIF (cd->cancel_from);
				FREEIF (cd->cancel_newsgroups);
				FREEIF (cd->cancel_distribution);

                if(cd->destroy_graph_progress)
                    FE_GraphProgressDestroy(ce->window_id, 
                                            ce->URL_s, 
                                            cd->original_content_length,
											ce->bytes_received);
                PR_Free(cd);

        		/* gate the maximum number of cached connections
				 * but don't delete busy ones.
                 */
                if(XP_ListCount(nntp_connection_list) > kMaxCachedConnections)
                  {
                    XP_List * list_ptr = nntp_connection_list->next;
                    NNTPConnection * con;

					NNTP_LOG_NOTE(("killing cached news connection"));

                    while(list_ptr)
                      {
                        con = (NNTPConnection *) list_ptr->object;
                        list_ptr = list_ptr->next;
                        if(!con->busy)
                          {
                            XP_ListRemoveObject(nntp_connection_list, con);
                            net_nntp_close(con, ce->status);
                            FREEIF(con->hostname);
                            PR_Free(con);
                            break;
                          }
                      }
				  }
                return(-1); /* all done */

            default:
                /* big error */
                return(-1);
          }

        if(ce->status < 0 && cd->next_state != NEWS_ERROR &&
                    cd->next_state != NNTP_ERROR && cd->next_state != NEWS_FREE)
          {
            cd->next_state = NNTP_ERROR;
            cd->pause_for_read = FALSE;
          }
      } /* end big while */

      return(0); /* keep going */
}

/* abort the connection in progress
 */
MODULE_PRIVATE int
NET_InterruptNews(ActiveEntry * ce)
{
    NewsConData * cd = (NewsConData *)ce->con_data;
    nsresult rv;
#ifdef XPCOM_OFFLINE
    if (cd->offline_state != NULL) {
      int status;
      rv = cd->offline_state->Interrupt(&status);
      if (NS_SUCCEEDED(rv))
        return status;
      else
        return -1;              /* ??? */
    }
#else
	if (cd->offlineState != NULL)
		return MSG_InterruptOfflineNews(cd->offlineState);
#endif

    cd->next_state = NNTP_ERROR;
    ce->status = MK_INTERRUPTED;

	if (cd->control_con)
		cd->control_con->prev_cache = FALSE;   /* to keep if from reconnecting */
 
    return(net_ProcessNews(ce));
}

/* Free any memory used up
 * close cached connections and
 * free newsgroup listings
 */
MODULE_PRIVATE void
NET_CleanupNews(void)
{
	NNTP_LOG_NOTE(("NET_CleanupNews called"));

	/* this is a small leak but since I don't have another function to
	 * clear my connections I need to call this one alot and I don't
	 * want to free the newshost
	 *
     * FREE_AND_CLEAR(NET_NewsHost);
	 */

    if(nntp_connection_list)
      {
        NNTPConnection * tmp_con;

        while((tmp_con = (NNTPConnection *)XP_ListRemoveTopObject(nntp_connection_list)) != NULL)
          {
			if(!tmp_con->busy)
			  {
			    FREE(tmp_con->hostname);
			    FREEIF(tmp_con->current_group);
			    if(tmp_con->csock != NULL)
			      {
			        net_nntp_close(tmp_con, 0 /* status? */);
			      }
			    FREE(tmp_con);
			  }
          }

		if(XP_ListIsEmpty(nntp_connection_list))
		  {
			XP_ListDestroy(nntp_connection_list);
			nntp_connection_list = 0;
		  }
      }

    return;
}



#ifdef PROFILE
#pragma profile off
#endif

#endif /* MOZILLA_CLIENT */
