/* -*- 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.
 */
/*
 * mkgeturl.c
 *  This file implements the main calling api for netlib.
 *    Includes NET_InitNetlib, NET_Shutdown, NET_GetURL, and NET_ProcessNet
 *
 * Designed and originally implemented by Lou Montulli '94
 * Additions/Changes by Judson Valeski, Gagan Saksena 1997.
 */
#include "mkutils.h"
#include "mkgeturl.h"
#include "shist.h"
#include "mkparse.h"
#include "mkformat.h"
#include "mkstream.h"
#include "mkpadpac.h"
 
#include "mkhttp.h"
#include "mkftp.h"
#include "mkgopher.h"
#include "mkremote.h"
#include "mknews.h"
#include "mkpop3.h"
#include "mkdaturl.h"
#include "mkfile.h"
#include "mksmtp.h"
#include "mkcache.h"
#include "mkmemcac.h"
#include "glhist.h"
#include "mkaccess.h"
#include "mkmailbx.h"
#include "mkautocf.h"
#include "mkmarimb.h"
#include "mkabook.h"
#include "mkhelp.h"
#include "ssl.h"
#include "mkcertld.h"

#ifdef MOZ_LDAP
#incclude "mkldap.h"
#endif

#ifdef MOZ_MAIL_NEWS
#include "mkimap4.h"
#endif

#include "mktcp.h"  /* for NET_InGetHostByName semaphore */
#include "cert.h"
#ifdef MOZILLA_CLIENT
#include "secnav.h"
#ifdef MOCHA
#include "libmocha.h"
#include "libevent.h"
#include "jscompat.h"
#include "jspubtd.h"
#endif
#endif

#include "libimg.h"             /* Image Lib public API. */ 
#include "il_strm.h"             /* Image Lib public API. */

#include "libi18n.h"
#include "xp_sec.h"
#include "mkmocha.h"
#include "htmldlgs.h"

#include "np.h"
#include "prefapi.h"
#ifdef NSPR20
#ifdef XP_MAC
#include "prpriv.h"             /* for NewNamedMonitor */
#else
#include "private/prpriv.h"
#endif
#endif /* NSPR20 */

#include "sslerr.h"
#include "xp_error.h"
#include "merrors.h"
#include "prefetch.h"

#ifdef XP_OS2
#include "os2sock.h"
#endif
			 
#ifdef EDITOR
#include "edt.h"
#endif

#ifdef MOCHA
#include "libmocha.h"
#endif

#ifdef XP_UNIX
#include "prnetdb.h"
#else
#define PRHostEnt struct hostent
#endif

#include "xplocale.h"

/* This is only until all platforms have libfont/ checked in */
#define WEBFONTS

#ifdef WEBFONTS
#include "nf.h"
#endif /* WEBFONTS */

/* plugins:  */
#include "np.h"

/* for XP_GetString() */
#include "xpgetstr.h"
extern int MK_CONNECTION_REFUSED;
extern int MK_CONNECTION_TIMED_OUT;
extern int MK_MALFORMED_URL_ERROR;
extern int MK_OUT_OF_MEMORY;
extern int MK_RELATIVE_TIMEBOMB_MESSAGE;
extern int MK_RELATIVE_TIMEBOMB_WARNING_MESSAGE;
extern int MK_TIMEBOMB_MESSAGE;
extern int MK_TIMEBOMB_URL_PROHIBIT;
extern int MK_TIMEBOMB_WARNING_MESSAGE;
extern int MK_UNABLE_TO_CONNECT;
extern int MK_UNABLE_TO_CREATE_SOCKET;
extern int MK_UNABLE_TO_LOCATE_HOST;
extern int MK_UNABLE_TO_LOCATE_PROXY;
extern int XP_ALERT_CONNECTION_LESSZERO;
extern int XP_ALERT_INTERRUPT_WINDOW;
extern int XP_ALERT_URLS_LESSZERO;
extern int XP_CONFIRM_REPOST_FORMDATA;
extern int XP_CONSULT_SYS_ADMIN;
extern int XP_HTML_MISSING_REPLYDATA;
extern int XP_SOCKS_NS_ENV_VAR;
extern int XP_SOME_HOSTS_UNREACHABLE;
extern int XP_THIS_IS_DNS_VERSION;
extern int XP_THIS_IS_YP_VERSION;
extern int XP_UNKNOWN_HOST;
extern int XP_UNKNOWN_HOSTS;
extern int XP_UNKNOWN_HTTP_PROXY;
extern int XP_UNKNOWN_SOCKS_HOST;
extern int XP_URLS_WAITING_FOR_AN_OPEN_SOCKET;
extern int XP_URLS_WAITING_FOR_FEWER_ACTIVE_URLS;
extern int XP_CONNECTIONS_OPEN;
extern int XP_ACTIVE_URLS;
extern int XP_USING_PREVIOUSLY_CACHED_COPY_INSTEAD;
extern int XP_BAD_AUTOCONFIG_NO_FAILOVER;

extern int XP_SOCK_CON_SOCK_PROTOCOL;                   
extern int XP_URL_NOT_FOUND_IN_CACHE;                   
extern int XP_PARTIAL_CACHE_ENTRY;
extern int XP_CHECKING_SERVER__FORCE_RELOAD;
extern int XP_OBJECT_HAS_EXPIRED;
extern int XP_CHECKING_SERVER_CACHE_ENTRY;
extern int XP_CHECKING_SERVER__LASTMOD_MISS;
extern int XP_NETSITE_  ;
extern int XP_LOCATION_ ;
extern int XP_FILE_MIME_TYPE_   ;
extern int XP_CURRENTLY_UNKNOWN;
extern int XP_SOURCE_ ;
extern int XP_CURRENTLY_IN_DISK_CACHE   ;
extern int XP_CURRENTLY_IN_MEM_CACHE; 
extern int XP_CURRENTLY_NO_CACHE        ;
extern int XP_LOCAL_CACHE_FILE_ ;
extern int XP_NONE      ;
extern int XP_LOCAL_TIME_FMT ;
extern int XP_LAST_MODIFIED ;
extern int XP_GMT_TIME_FMT      ;
extern int XP_CONTENT_LENGTH_   ;
extern int XP_NO_DATE_GIVEN;
extern int XP_EXPIRES_  ;
extern int XP_MAC_TYPE_ ;
extern int XP_MAC_CREATOR_      ;
extern int XP_CHARSET_  ;
extern int XP_STATUS_UNKNOWN ;
extern int XP_SECURITY_ ;
extern int XP_CERTIFICATE_      ;
extern int XP_UNTITLED_DOCUMENT ;
extern int XP_HAS_THE_FOLLOWING_STRUCT  ;
extern int XP_DOCUMENT_INFO ;
extern int XP_THE_WINDOW_IS_NOW_INACTIVE ;
extern int XP_MSG_UNKNOWN ;
extern int XP_EDIT_NEW_DOC_URL;
extern int XP_EDIT_NEW_DOC_NAME;
extern int XP_CONFIRM_MAILTO_POST_1;
extern int XP_CONFIRM_MAILTO_POST_2;

extern int XP_ALERT_OFFLINE_MODE_SELECTED;


#if defined(JAVA)
#include "nslocks.h"
#include "prlog.h"
#include <stdarg.h>

PRMonitor* libnet_asyncIO;

#if defined(SOLARIS)
extern int gethostname(char *, int);
#endif /* SOLARIS */

#endif /* JAVA */

#define LIBNET_UNLOCK_AND_RETURN(value) \
	do { int rv = (value); LIBNET_UNLOCK(); return rv; } while (0)

/*
** Don't ever forget about this!!!
**
** This is ALL superseeded by the setting in /ns/modules/libpref/src/init/all.js
** lock for timebomb.use_timebomb
 
** Define TIMEBOMB_ON for beta builds.
** Undef TIMEBOMB_ON for release builds.
*/
#define TIMEBOMB_ON
/* #undef TIMEBOMB_ON  */
/*
** After this date all hell breaks loose
*/
#define TIME_BOMB_TIME          888739203       /* 3/01/98 + 3 secs */
#ifdef XP_OS2
#define TIME_BOMB_WARNING_TIME  869976003       /* 7/21/97 + 3 secs */
#else
#define TIME_BOMB_WARNING_TIME  857203203       /* 3/01/97 + 3 secs */
#endif

#ifndef XP_UNIX
#ifdef NADA_VERSION
static char *https_security = "SECURITY_VERSION: -https";
#else
static char *https_security = "SECURITY_VERSION: +https";
#endif
#endif

#ifdef EDITOR
/* 
 * Strings used for new, empty document creation
 * Kluge we use to load a new document is resource with one space.
 * These strings are loaded from XP_MSG.I on first GetURL call
 * (this allows changing "Untitled" for other languages)
*/
PUBLIC char *XP_NEW_DOC_URL = NULL; /* "about:editfilenew" */
/* 
 * This is the name we want to appear as the URL address
 * You can load a new doc using this - it will be changed 
 *  to XP_NEW_DOC_URL. 
 * When done, XP_NEW_DOC_URL will be changed to XP_NEW_DOC_NAME
*/
PUBLIC char *XP_NEW_DOC_NAME = NULL; /* "Untitled" */
#endif

#ifdef PROFILE
#pragma profile on
#endif

/* forward decl's */
PRIVATE void add_slash_to_URL (URL_Struct *URL_s);
PRIVATE Bool override_proxy (CONST char * URL);
PRIVATE int net_output_about_url(ActiveEntry * this_entry);
PRIVATE int net_output_security_url(ActiveEntry * this_entry, MWContext *cx);
PRIVATE Bool net_about_kludge(URL_Struct *URL_s);
PRIVATE void net_FreeURLAllHeaders(URL_Struct * URL_s);
PRIVATE void NET_InitAboutProtocol(void);
PRIVATE void NET_InitSecurityProtocol(void);

PRIVATE NET_TimeBombActive = FALSE;

typedef struct _WaitingURLStruct {
	int                 type;
	URL_Struct         *URL_s;
	FO_Present_Types    format_out;
    MWContext          *window_id;
    Net_GetUrlExitFunc *exit_routine;
} WaitingURLStruct;

/* Pointers to proxy servers
 * They are pointers to a host if
 * a proxy environment variable
 * was found or they are zero if not.
 */
PRIVATE char * MKftp_proxy=0;
PRIVATE char * MKgopher_proxy=0;
PRIVATE char * MKhttp_proxy=0;
PRIVATE char * MKhttps_proxy=0;
PRIVATE char * MKwais_proxy=0;
PRIVATE char * MKnews_proxy=0;
PRIVATE char * MKno_proxy=0;
PRIVATE char * MKproxy_ac_url=0;
PRIVATE NET_ProxyStyle MKproxy_style = PROXY_STYLE_UNSET;

PRIVATE char * MKglobal_config_url = 0;

PRIVATE XP_List * net_EntryList=0;

MODULE_PRIVATE CacheUseEnum NET_CacheUseMethod=CU_CHECK_PER_SESSION;

#ifdef XP_WIN16
#define MAX_NUMBER_OF_PROCESSING_URLS 8
#else
#define MAX_NUMBER_OF_PROCESSING_URLS 15
#endif

#define INITIAL_MAX_ALL_HEADERS 10
#define MULT_ALL_HEADER_COUNT   2
#define MAX_ALL_HEADER_COUNT    16000

#ifndef XP_MAC
	/* I don't want these annoying asserts in the Mac build but in case some other platform does... */
	#define DO_ANNOYING_ASSERTS_IN_STUBS	1
#endif

MODULE_PRIVATE time_t NET_StartupTime=0;

MODULE_PRIVATE XP_Bool NET_ProxyAcLoaded = FALSE;
MODULE_PRIVATE XP_Bool NET_GlobalAcLoaded = FALSE;
MODULE_PRIVATE int NET_TotalNumberOfOpenConnections=0;
MODULE_PRIVATE int NET_MaxNumberOfOpenConnections=100;
MODULE_PRIVATE int NET_MaxNumberOfOpenConnectionsPerContext=4;
MODULE_PRIVATE int NET_TotalNumberOfProcessingURLs=0;
PRIVATE XP_List  * net_waiting_for_actives_url_list=0;
PRIVATE XP_List  * net_waiting_for_connection_url_list=0;

typedef struct NETExitCallbackNode {
	struct NETExitCallbackNode *next;
	void *arg;
	void (* func)(void *arg);
} NETExitCallbackNode;

PRIVATE NETExitCallbackNode *NETExitCallbackHead = NULL;

/* forward decl */
PRIVATE void net_cleanup_reg_protocol_impls(void);
PRIVATE void NET_InitTotallyRandomStuffPeopleAddedProtocols(void);



/* fix Mac warning for missing prototype */
PUBLIC void
NET_AddExitCallback( void (* func)(void *arg), void *arg);

PUBLIC void
NET_AddExitCallback( void (* func)(void *arg), void *arg)
{
	NETExitCallbackNode *node;
	
	/* alloc node */
	node = XP_ALLOC(sizeof(NETExitCallbackNode));
	if ( node != NULL ) {
		/* fill it in */
		node->func = func;
		node->arg = arg;
		/* link it into the list */
		node->next = NETExitCallbackHead;
		NETExitCallbackHead = node;
	}
	
	return;
}

PRIVATE void
NET_ProcessExitCallbacks(void)
{
	NETExitCallbackNode *node, *freenode;

	node = NETExitCallbackHead;
	
	while ( node != NULL ) {
		(* node->func)(node->arg);
		freenode = node;
		node = node->next;
		XP_FREE(freenode);
	}
	
	NETExitCallbackHead = NULL;
	
	return;
}

/*  Gather manual proxy information from the prefapi. Called from 
   SetupPrefs and SelectProxyStyle */
PRIVATE void
NET_UpdateManualProxyInfo(const char * prefChanged) {

	XP_Bool bSetupAll=FALSE;
	char * proxy = NULL;
	int32 iPort=0;
	char text[MAXHOSTNAMELEN + 8];

	if (!prefChanged)
		bSetupAll = TRUE;

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.proxy.ftp") || 
		!XP_STRCMP(prefChanged, "network.proxy.ftp_port")) {
		PREF_CopyCharPref("network.proxy.ftp",&proxy);
		if(proxy && *proxy) {
			PREF_GetIntPref("network.proxy.ftp_port",&iPort);
			sprintf(text,"%s:%d", proxy, iPort);
		    StrAllocCopy(MKftp_proxy, text);
			iPort=0;
		}
		else 
			FREE_AND_CLEAR(MKftp_proxy);
	}
	if (proxy) FREE_AND_CLEAR(proxy);

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.proxy.gopher") || 
		!XP_STRCMP(prefChanged, "network.proxy.gopher_port")) {
		PREF_CopyCharPref("network.proxy.gopher",&proxy);
		if(proxy && *proxy) {
			PREF_GetIntPref("network.proxy.gopher_port",&iPort);
			sprintf(text,"%s:%d", proxy, iPort);  
		    StrAllocCopy(MKgopher_proxy, text);
			iPort=0;
		}
		else 
			FREE_AND_CLEAR(MKgopher_proxy);
	}
	if (proxy) FREE_AND_CLEAR(proxy);

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.proxy.http") || 
		!XP_STRCMP(prefChanged, "network.proxy.http_port")) {
		PREF_CopyCharPref("network.proxy.http",&proxy);
		if(proxy && *proxy) {
			PREF_GetIntPref("network.proxy.http_port",&iPort);
			sprintf(text,"%s:%d", proxy, iPort);  
		    StrAllocCopy(MKhttp_proxy, text);
			iPort=0;
		}
		else 
			FREE_AND_CLEAR(MKhttp_proxy);
	}
	if (proxy) FREE_AND_CLEAR(proxy);

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.proxy.ssl") || 
		!XP_STRCMP(prefChanged, "network.proxy.ssl_port")) {
		PREF_CopyCharPref("network.proxy.ssl",&proxy);
		if(proxy && *proxy) {
			PREF_GetIntPref("network.proxy.ssl_port",&iPort);
			sprintf(text,"%s:%d", proxy, iPort);  
		    StrAllocCopy(MKhttps_proxy, text);
			iPort=0;
		}
		else 
			FREE_AND_CLEAR(MKhttps_proxy);
	}
	if (proxy) FREE_AND_CLEAR(proxy);

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.proxy.news") || 
		!XP_STRCMP(prefChanged, "network.proxy.news_port")) {
		PREF_CopyCharPref("network.proxy.news",&proxy);
		if(proxy && *proxy) {
			PREF_GetIntPref("network.proxy.news_port",&iPort);
			sprintf(text,"%s:%d", proxy, iPort);  
		    StrAllocCopy(MKnews_proxy, text);
			iPort=0;
		}
		else 
			FREE_AND_CLEAR(MKnews_proxy);
	}
	if (proxy) FREE_AND_CLEAR(proxy);

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.proxy.wais") || 
		!XP_STRCMP(prefChanged, "network.proxy.wais_port")) {
		PREF_CopyCharPref("network.proxy.wais",&proxy);
		if(proxy && *proxy) {
			PREF_GetIntPref("network.proxy.wais_port",&iPort);
			sprintf(text,"%s:%d", proxy, iPort);  
		    StrAllocCopy(MKwais_proxy, text);
			iPort=0;
		}
		else 
			FREE_AND_CLEAR(MKwais_proxy);
	}
	if (proxy) FREE_AND_CLEAR(proxy);

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.hosts.socks_server") || 
		!XP_STRCMP(prefChanged, "network.hosts.socks_serverport")) {
		PREF_CopyCharPref("network.hosts.socks_server",&proxy);
		if (proxy && *proxy) {
			PREF_GetIntPref("network.hosts.socks_serverport",&iPort);
			PR_snprintf(text, sizeof(text), "%s:%d", proxy, iPort);  
			NET_SetSocksHost(text);
		}
		else {
			NET_SetSocksHost(proxy); /* NULL is ok */
		}
		iPort=0;
	}
	if (proxy) FREE_AND_CLEAR(proxy);

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.proxy.no_proxies_on")) {
		PREF_CopyCharPref("network.proxy.no_proxies_on",&proxy);
		if(proxy && *proxy) {
		    StrAllocCopy(MKno_proxy, proxy);
		}
		else 
			FREE_AND_CLEAR(MKno_proxy);
	}
	if (proxy) FREE_AND_CLEAR(proxy);
	return;
}

PUBLIC NET_ProxyStyle
NET_GetProxyStyle(void)
{
	return MKproxy_style;
}

/* These are used in netlib only for exposing the MKproxy_ac_url 
 * to proxy autodiscovery stuff in mkpadpac.c */
MODULE_PRIVATE const char *
net_GetPACUrl(void) {
	return MKproxy_ac_url;
}

MODULE_PRIVATE void
net_SetPACUrl(char *u) {
	/* Do a straight assignment, no checking. */
	MKproxy_ac_url=u;
}

PUBLIC void
NET_SelectProxyStyle(NET_ProxyStyle style)
{
 /* The NET_ProxyStyle enum is defined in net.h.
	PROXY_STYLE_UNSET               = 0             -- ?
	PROXY_STYLE_MANUAL              = 1             -- old style proxies
	PROXY_STYLE_AUTOMATIC   = 2             -- new style proxies
	PROXY_STYLE_NONE                = 3             -- no proxies, direct connection
 */     
	char * proxy=0;

	if (MKproxy_style != style) {
		NET_ProxyAcLoaded = FALSE;
	}

	MKproxy_style = style;

	if (MKproxy_style == PROXY_STYLE_MANUAL) {
		/* Get the manual info */
		NET_UpdateManualProxyInfo(NULL);
	} 
	
	if (MKproxy_style == PROXY_STYLE_AUTOMATIC) {
		/* Get the autoconfig url */
		PREF_CopyCharPref("network.proxy.autoconfig_url",&proxy);
		if (proxy && *proxy) {
			StrAllocCopy(MKproxy_ac_url, proxy);
			NET_ProxyAcLoaded = FALSE;
		}
		if (proxy) FREE_AND_CLEAR(proxy);
	}

	/* If we're not in automatic or manual style, clear un-needed internal info */
	if (MKproxy_style != PROXY_STYLE_AUTOMATIC) {
		FREE_AND_CLEAR(MKproxy_ac_url);
		NET_ProxyAcLoaded = FALSE;
	}

	/* If we're not in manual (proxies) clear the un-needed internal info. */
	if (MKproxy_style != PROXY_STYLE_MANUAL) {
		FREE_AND_CLEAR(MKftp_proxy);
		FREE_AND_CLEAR(MKgopher_proxy);
		FREE_AND_CLEAR(MKhttp_proxy);
		FREE_AND_CLEAR(MKhttps_proxy);
		FREE_AND_CLEAR(MKwais_proxy);
		FREE_AND_CLEAR(MKnews_proxy);
		FREE_AND_CLEAR(MKno_proxy);

		NET_SetSocksHost(NULL);
	} 
}

/* this function loads the proxy values from the various preferences and
sets up the internal cached values...It is called during Init and whenever
a proxy pref is changed...If no specific pref is passed, it sets them all
up */

PUBLIC void
NET_SetupPrefs(const char * prefChanged)
{
	XP_Bool bSetupAll=FALSE;
	char * proxy = NULL;

	/* if prefChanged is null the following XP_STRCMP's are never executed because the
	   || statement never gets that far because when prefChanged is null bSetupAll is
	   set to true and is always checked first in the if statement. Don't set prefChanged
	   to "" if it is null because the UpdateManualProxyInfo below needs null not "" if
	   prefChanged was originally null. */
	if (!prefChanged)
		bSetupAll = TRUE;

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.sendRefererHeader")) {
		Bool b;
		PREF_GetBoolPref("network.sendRefererHeader", &b);
		NET_SetSendRefererHeader(b);
	}

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.dnsCacheExpiration")) {
		int32 n;
		PREF_GetIntPref("network.dnsCacheExpiration",&n);
		NET_SetDNSExpirationPref(n);
	}
	
	if (bSetupAll || !XP_STRCMP(prefChanged,"browser.prefetch")) {
		XP_Bool enabled;
		PREF_GetBoolPref("browser.prefetch",&enabled);
    	PRE_Enable(enabled);
	}
		
	if (bSetupAll || !XP_STRCMP(prefChanged,"browser.cache.memory_cache_size")) {
		int32 nMemCache;
		PREF_GetIntPref("browser.cache.memory_cache_size",&nMemCache);
		NET_SetMemoryCacheSize(nMemCache * 1024);
	}

	if (bSetupAll || !XP_STRCMP(prefChanged,"browser.cache.disk_cache_size")) {
		int32 nDiskCache;
		PREF_GetIntPref("browser.cache.disk_cache_size",&nDiskCache);
	    NET_SetDiskCacheSize(nDiskCache * 1024);
	}

	if (bSetupAll || !XP_STRCMP(prefChanged, "browser.cache.check_doc_frequency")) {
		int32 nDocReqFreq;
		PREF_GetIntPref("browser.cache.check_doc_frequency" ,&nDocReqFreq);
		NET_SetCacheUseMethod((CacheUseEnum)nDocReqFreq);
	}
	
	if (bSetupAll || !XP_STRCMP(prefChanged,"browser.cache.disk_cache_ssl")) {
		XP_Bool prefBool;
		PREF_GetBoolPref("browser.cache.disk_cache_ssl",&prefBool);
		NET_DontDiskCacheSSL(!prefBool);
	}
#ifdef MOZ_MAIL_NEWS
	if (bSetupAll || !XP_STRCMP(prefChanged,"mail.allow_at_sign_in_user_name")) {
		XP_Bool prefBool;
		PREF_GetBoolPref("mail.allow_at_sign_in_user_name",&prefBool);
		NET_SetAllowAtSignInMailUserName (prefBool);
	}
#endif /* MOZ_MAIL_NEWS */
	
	if (bSetupAll || !XP_STRCMP(prefChanged,"network.proxy.autoconfig_url")) {
		PREF_CopyCharPref("network.proxy.autoconfig_url",&proxy);
		if (proxy && *proxy) {
			StrAllocCopy(MKproxy_ac_url, proxy);
			NET_ProxyAcLoaded = FALSE;
		}
		else 
			FREE_AND_CLEAR(MKproxy_ac_url);
	}
	if (proxy) FREE_AND_CLEAR(proxy);
	
	NET_UpdateManualProxyInfo(prefChanged);

	if (bSetupAll || !XP_STRCMP(prefChanged, "network.proxy.type")) {
		int32 iType;
		PREF_GetIntPref("network.proxy.type",&iType);
	NET_SelectProxyStyle((NET_ProxyStyle)iType);
	}
}

/* register preference callbacks for netlib prefs
*/

MODULE_PRIVATE int PR_CALLBACK NET_PrefChangedFunc(const char *pref, void *data) {
	NET_SetupPrefs(pref);
	return TRUE;
} 

/* initialize the netlibrary and set the socket buffer size
 */
PUBLIC int
NET_InitNetLib(int socket_buffer_size, int max_number_of_connections)
{
    int status;

#if defined(NSPR20) && defined(DEBUG)
        if (NETLIB==NULL)
           NETLIB = PR_NewLogModule("NETLIB");
#endif

	TRACEMSG(("Initializing Network library"));

	NET_StartupTime = time(NULL);

	/* seed the random generator
	 */
	XP_SRANDOM((unsigned int) time(NULL));

	if(max_number_of_connections < 1)
		max_number_of_connections = 1;

#ifdef XP_UNIX
	signal(SIGPIPE, SIG_IGN);
#endif
	

    status = NET_ChangeSocketBufferSize(socket_buffer_size);

	NET_ChangeMaxNumberOfConnections(max_number_of_connections);
	net_waiting_for_actives_url_list = XP_ListNew();
	net_waiting_for_connection_url_list = XP_ListNew();

    net_EntryList = XP_ListNew();

#ifdef MOZILLA_CLIENT
    NET_ReadCacheFAT("", TRUE);

	NET_ReadCookies("");

	
#endif /* MOZILLA_CLIENT */

	NET_TotalNumberOfProcessingURLs=0; /* reset */

#ifdef XP_UNIX
	{
		char *ac_url = getenv("AUTOCONF_URL");
		if (ac_url) {
			NET_SetProxyServer(PROXY_AUTOCONF_URL, ac_url);
			NET_SelectProxyStyle(PROXY_STYLE_AUTOMATIC);
		}
	}
#endif

#ifdef JAVA
    libnet_asyncIO = PR_NewNamedMonitor("libnet");
#endif
	NET_SetupPrefs(NULL);  /* setup initial proxy, socks, dnsExpiration and cache preference info */
	PREF_RegisterCallback("network.proxy",NET_PrefChangedFunc,NULL);
	PREF_RegisterCallback("browser.cache",NET_PrefChangedFunc,NULL);
	PREF_RegisterCallback("network.hosts.socks_server",NET_PrefChangedFunc,NULL);
	PREF_RegisterCallback("network.hosts.socks_serverport",NET_PrefChangedFunc,NULL);
	PREF_RegisterCallback("network.dnsCacheExpiration",NET_DNSExpirationPrefChanged,NULL);
	NET_RegisterCookiePrefCallbacks(); /* simply inits cookie info in mkaccess.c */
									   /* and registers the callbacks */
	/* inits the proxy autodiscovery vars and registers their callbacks. */
	NET_RegisterPadPrefCallbacks();
	NET_RegisterEnableUrlMatchCallback();
	PREF_RegisterCallback("mail.allow_at_sign_in_user_name", NET_PrefChangedFunc, NULL);

	/* initialize protocol modules 
	 *
	 * eventually this should be done dynamically,
	 * but first we need to make URL types be dynamically
	 * registered, and make URL parsing dynamic as well.
	 *
	 * Currently, registering them in the most used order is 
	 * very slightly more efficient
	 */
	NET_InitFileProtocol();
	NET_InitHTTPProtocol();
	NET_InitMemCacProtocol();
	NET_InitFTPProtocol();
	NET_InitAboutProtocol();
	NET_InitGopherProtocol();
	NET_InitMochaProtocol();
	NET_InitNFSProtocol();
	NET_InitURNProtocol();
	NET_InitWAISProtocol();
	NET_InitRemoteProtocol();
	NET_InitDataURLProtocol();
#ifdef JAVA
	NET_InitMarimbaProtocol();
#endif
	NET_InitTotallyRandomStuffPeopleAddedProtocols();

	NET_InitMailtoProtocol();  /* has a stub for MOZ_MAIL_NEWS */
#ifdef MOZ_MAIL_NEWS    
	NET_InitNewsProtocol();
	NET_InitMailboxProtocol();
	NET_InitMsgSearchProtocol();
	NET_InitPop3Protocol();
	NET_InitLDAPProtocol();
	NET_InitCertLdapProtocol();
	NET_InitAddressBookProtocol();
#endif /* MOZ_MAIL_NEWS */

    return(status);
}

/* set the maximum allowable number of open connections at any one
 * time regardless of the context
 */
PUBLIC void
NET_ChangeMaxNumberOfConnections(int max_number_of_connections)
{

	/* if already equal return */
	if(NET_MaxNumberOfOpenConnections == max_number_of_connections)
		return;

	if(max_number_of_connections < 1)
	    max_number_of_connections = 1;

	/* make sure that # conns per context is less than or equal
	 * to this Max
	 */
	if(NET_MaxNumberOfOpenConnectionsPerContext > max_number_of_connections)
	    NET_MaxNumberOfOpenConnectionsPerContext = max_number_of_connections;

	NET_MaxNumberOfOpenConnections = max_number_of_connections;

	/* close any open connections to prevent deadlock
	 */
	net_cleanup_reg_protocol_impls();
}

/* set the maximum allowable number of open connections at any one
 * time Per context
 */
PUBLIC void
NET_ChangeMaxNumberOfConnectionsPerContext(int max_number_of_connections)
{

	/* if already equal return */
	if(NET_MaxNumberOfOpenConnectionsPerContext == max_number_of_connections)
		return;

	/* gate the number of max connections to be within 1 and 4
	 */
	if(max_number_of_connections < 1)
		max_number_of_connections = 1;

	if(max_number_of_connections > 6)
		max_number_of_connections = 6;

	NET_MaxNumberOfOpenConnectionsPerContext = max_number_of_connections;

	/* close any open connections to prevent deadlock
	 */
/* @@@@	NET_CleanupFTP(); */
#ifdef MOZILLA_CLIENT
#ifdef MOZ_MAIL_NEWS    
/* @@@@	NET_CleanupNews(); */
#endif /* MOZ_MAIL_NEWS */   
#endif /* MOZILLA_CLIENT */

}

/* check the date and set off the timebomb if necessary
 *
 * Calls FE_Alert with a message and then disables
 * all network calls to hosts not in our domains,
 * except for FTP connects.
 */
PUBLIC Bool
NET_CheckForTimeBomb(MWContext *context)
{
	static Bool done = FALSE;
	time_t cur_time;
	time_t timebomb_time = 0;
	time_t timebomb_warning_time = 0;
	
	int32 relative_timebomb_days = 0;
	int32 relative_timebomb_warning_days = 0;
	time_t relative_timebomb_start_date = 0;
	char *relative_timebomb_prefs_name;
	time_t relative_timebomb_time = 0;
	time_t relative_timebomb_warning_time = 0;
	Bool have_relative_timebomb = FALSE;
	Bool just_started_relative_timebomb = FALSE;

	if(done)
		return(NET_TimeBombActive);

	/*
	 * Check if any timebomb is enabled
	 */
	PREF_GetConfigInt("timebomb.expiration_time",(int32 *)&timebomb_time);
	PREF_GetConfigInt("timebomb.relative_timebomb_days",
								(int32 *)&relative_timebomb_days);

	if (relative_timebomb_days >= 1)
		have_relative_timebomb = TRUE;

	done = TRUE;

	if ((timebomb_time < 1) && (have_relative_timebomb == FALSE))
		return FALSE;

	cur_time  = XP_TIME();

	/*
	 * check the absolute timebomb
	 */
	TRACEMSG(("current time is: %ld %s", cur_time, ctime(&cur_time)));
	TRACEMSG(("Timebomb time is: %ld %s", timebomb_time,ctime(&timebomb_time)));
	if ((timebomb_time >= 1) && (cur_time > timebomb_time))
	  {
		char * alert = NET_ExplainErrorDetails(MK_TIMEBOMB_MESSAGE);

		FE_Alert(context, alert);
		FREE(alert);

		NET_TimeBombActive = TRUE;

		return(TRUE);
	  }

	/*
	 * check the relative timebomb
	 * (note: we store the start date in prefs using a "secret" name )
	 */
	if (have_relative_timebomb) {
		TRACEMSG(("Relative timebomb days = %ld", relative_timebomb_days));
		relative_timebomb_prefs_name = NULL;
		PREF_CopyConfigString("timebomb.relative_timebomb_secret_name",
								&relative_timebomb_prefs_name);
		if (relative_timebomb_prefs_name == NULL)
			relative_timebomb_prefs_name = 
								XP_STRDUP("general.bproxy_cert_digest");
		relative_timebomb_start_date = 0;
		PREF_GetIntPref(relative_timebomb_prefs_name,
								(int32 *)&relative_timebomb_start_date);

		if (relative_timebomb_start_date < 1)
		{
			/* if no value must be first time so set start date */
			just_started_relative_timebomb = TRUE;
			relative_timebomb_start_date = cur_time;
			PREF_SetIntPref(relative_timebomb_prefs_name,
							(int32)relative_timebomb_start_date);
		}
		XP_FREE(relative_timebomb_prefs_name);
		relative_timebomb_time = relative_timebomb_start_date +
								(relative_timebomb_days * 24 * 60 * 60);
		if (cur_time > relative_timebomb_time)
		{
			char *alert = NET_ExplainErrorDetails(MK_RELATIVE_TIMEBOMB_MESSAGE);

			FE_Alert(context, alert);
			FREE(alert);

			NET_TimeBombActive = TRUE;

			return(TRUE);
		}
	}

	/*
	 * check the absolute timebomb warning
	 */
	PREF_GetConfigInt("timebomb.warning_time",(int32 *)&timebomb_warning_time);
	TRACEMSG(("Timebomb warning time is: %ld %s", timebomb_warning_time,
								ctime(&timebomb_warning_time)));
	if ((timebomb_warning_time >= 1) && (cur_time > timebomb_warning_time))
	  {
		char * alert = NET_ExplainErrorDetails( MK_TIMEBOMB_WARNING_MESSAGE,
										INTL_ctime(context, &timebomb_time));
		FE_Alert(context, alert);
		FREE(alert);

		NET_TimeBombActive = FALSE;

		return(FALSE);
	  }

	/*
	 * check the relative timebomb warning
	 * also: on first use warn user this is timebombed 
	 */
	if (have_relative_timebomb) {
		relative_timebomb_warning_days = 0;
		PREF_GetConfigInt("timebomb.relative_timebomb_warning_days",
								(int32 *)&relative_timebomb_warning_days);
		TRACEMSG(("Relative timebomb warning days = %ld", 
									relative_timebomb_warning_days));
		if (relative_timebomb_warning_days >= 1)
			relative_timebomb_warning_time = relative_timebomb_start_date +
								(relative_timebomb_warning_days * 24 * 60 * 60);
		else
			relative_timebomb_warning_time = cur_time;
		if (just_started_relative_timebomb ||
				(cur_time > relative_timebomb_warning_time))
		{
			char * alert = NET_ExplainErrorDetails(
						MK_RELATIVE_TIMEBOMB_WARNING_MESSAGE,
						INTL_ctime(context, &relative_timebomb_time));
			FE_Alert(context, alert);
			FREE(alert);

			NET_TimeBombActive = FALSE;

			return(FALSE);
		}
	}

	return(FALSE);
}

/* set the way the cache should be used
 */
PUBLIC void
NET_SetCacheUseMethod(CacheUseEnum method)
{
    NET_CacheUseMethod = method;
}

PRIVATE void
net_CallExitRoutine(Net_GetUrlExitFunc *exit_routine,
					URL_Struct                 *URL_s,
					int                 status,
					FO_Present_Types        format_out,
					MWContext          *window_id)
{
#ifdef EDITOR
    /* History_entry * his; */
    /* Change all references to "about:editfilenew" into "file:///Untitled" */
    /* Do this for both Browser and Editor windows */
    if( /* EDT_IS_EDITOR(window_id) && */
	URL_s && URL_s->address &&
	0 == XP_STRCMP(URL_s->address, XP_NEW_DOC_URL) )
    {
	XP_FREE(URL_s->address);
	URL_s->address = XP_STRDUP(XP_NEW_DOC_NAME);
       
	/* Not sure if this is the best place to do this,
	 * but it needs to go someplace!        
	*/
	LO_SetBaseURL(window_id, XP_NEW_DOC_NAME);

	/* Setting context title to NULL will trigger
	 * Windows front end to use the URL address and set 
	 * doc and window title to "Untitled"
	 * Do all front ends do this?
	 * If not, they will have to use EDT_IS_NEW_DOCUMENT
	 *  to test for new doc and update title (in exit_routine)
	*/
	if ( window_id->title ) {
	    XP_FREE(window_id->title);
	    window_id->title = NULL;
	}
	/* Note: We replace "about:editfilenew" with "file:///Untitled"
	 * in SHIST_AddDocument(), so we don't need to mess with
	 * History data here.
	*/

	/* Set  window_id->is_new_document flag appropriately 
	 * This flag allows quicker response for often-called 
	 * status queries at front end
	*/
	if ( EDT_IS_EDITOR(window_id) ) {
	    EDT_NEW_DOCUMENT(window_id, TRUE);
	}
    }
#endif /* EDITOR */
#if defined(XP_WIN) || defined (XP_MAC) || defined (XP_OS2)
	FE_URLEcho(URL_s, status, window_id);
#endif /* XP_WIN/MAC/OS2 */

	if (!URL_s->load_background)
	  FE_EnableClicking(window_id);

#ifdef MOZILLA_CLIENT
	if(URL_s->refresh_url && status != MK_INTERRUPTED)
		FE_SetRefreshURLTimer(window_id, URL_s->refresh, URL_s->refresh_url);
#endif /* MOZILLA_CLIENT */

    if (URL_s->pre_exit_fn)
	  {
		Net_GetUrlExitFunc *per = URL_s->pre_exit_fn;
		URL_s->pre_exit_fn = 0;
		(*per) (URL_s, status, window_id);
	  }
    /* byrd:  plugin specific stuff, where we've added our own exit routine:  */
	if ((URL_s->owner_data != NULL) && 
		(URL_s->owner_id == 0x0000BAC0)){
			NPL_URLExit(URL_s,status,window_id);
	}

	if ( exit_routine != NULL ) {
		(*exit_routine) (URL_s, status, window_id);
	}
}

PRIVATE XP_Bool
net_does_url_require_socket_limit(int urltype)
{
	if( urltype == HTTP_TYPE_URL
	|| urltype == SECURE_HTTP_TYPE_URL
	|| urltype == FTP_TYPE_URL
	|| urltype == NEWS_TYPE_URL
	|| urltype == GOPHER_TYPE_URL)
		return TRUE;

	return(FALSE);
}


PRIVATE XP_Bool
net_is_one_url_allowed_to_run(MWContext *context, int url_type)
{
    /* put a limit on the total number of open connections
     */
    if(NET_TotalNumberOfOpenConnections < NET_MaxNumberOfOpenConnectionsPerContext
	|| !net_does_url_require_socket_limit(url_type))
      {
		return(TRUE);
	  }
	else if(NET_TotalNumberOfOpenConnections >= NET_MaxNumberOfOpenConnections)
	  {
		return(FALSE);
	  }
	else
	  {
	XP_List * list_ptr;
	ActiveEntry * tmpEntry;
	int32 cur_win_id = FE_GetContextID(context);
	int real_number_of_connections = 0;


		/* run through the list of running URLs and check
		 * for open connections only for this context.
		 * This will allow for the Total number of open
		 * connections to only apply to each window not
		 * to the whole program
		 */
	list_ptr = net_EntryList;
	while((tmpEntry = (ActiveEntry *) XP_ListNextObject(list_ptr)) != NULL)
	  {
		if(cur_win_id == FE_GetContextID(tmpEntry->window_id)
				&& net_does_url_require_socket_limit(url_type))
			  {
				real_number_of_connections++;
		  }
	  } /* end while */

		/* pause it if the number of connections per context
		 * is higher than the per context limit or
		 */
	if(real_number_of_connections >= NET_MaxNumberOfOpenConnectionsPerContext)

		  {
			return(FALSE);
		  }
      }

	return(TRUE);
}
 
static int
net_push_url_on_wait_queue(int                 url_type,
						   URL_Struct         *URL_s,
						   FO_Present_Types    format_out,
						   MWContext          *context,
						   Net_GetUrlExitFunc  exit_routine)
{

	WaitingURLStruct * wus = XP_NEW(WaitingURLStruct);

	TRACEMSG(("Pushing URL on wait queue with %d open connections",
					 NET_TotalNumberOfOpenConnections ));
	if(!wus)
	  {
		net_CallExitRoutine(exit_routine,
							URL_s,
							MK_OUT_OF_MEMORY,
							format_out,
							context);
		return(MK_OUT_OF_MEMORY);
	  }

	wus->type         = url_type;
	wus->URL_s        = URL_s;
	wus->format_out   = format_out;
	wus->window_id    = context;
	wus->exit_routine = exit_routine;

	/* add "text/ *" to beginning of list so it gets processed first */
	if (CLEAR_CACHE_BIT(format_out) == FO_INTERNAL_IMAGE)
		/* low priority */
		XP_ListAddObjectToEnd(net_waiting_for_connection_url_list, wus);
	else
		/* higher priority */
		XP_ListAddObject(net_waiting_for_connection_url_list, wus);

	return(0);
}

/* returns a malloc'd string that has a bunch of netlib
 * status info in it.
 *
 * please free the pointer when done.
 */
PUBLIC char *
NET_PrintNetlibStatus()
{
	char small_buf[128];
    XP_List * list_ptr;
	ActiveEntry *tmpEntry;
	char *rv=0;

	LIBNET_LOCK();
	list_ptr = net_EntryList;

	sprintf(small_buf, XP_GetString( XP_URLS_WAITING_FOR_AN_OPEN_SOCKET ),
						XP_ListCount(net_waiting_for_connection_url_list),
						NET_MaxNumberOfOpenConnectionsPerContext);
    StrAllocCat(rv, small_buf);

	sprintf(small_buf, XP_GetString( XP_URLS_WAITING_FOR_FEWER_ACTIVE_URLS ),
					XP_ListCount(net_waiting_for_actives_url_list));
    StrAllocCat(rv, small_buf);

	sprintf(small_buf, XP_GetString( XP_CONNECTIONS_OPEN ),
										NET_TotalNumberOfOpenConnections);
    StrAllocCat(rv, small_buf);

	sprintf(small_buf, XP_GetString( XP_ACTIVE_URLS ),
	NET_TotalNumberOfProcessingURLs);
    StrAllocCat(rv, small_buf);

    while((tmpEntry = (ActiveEntry *)XP_ListNextObject(list_ptr)) != 0)
	 {
	   sprintf(small_buf, "------------------------------------\nURL:");
       StrAllocCat(rv, small_buf);
       StrAllocCat(rv, tmpEntry->URL_s->address);
       StrAllocCat(rv, "\n");

	   sprintf(small_buf, XP_GetString(XP_SOCK_CON_SOCK_PROTOCOL),
					tmpEntry->socket, tmpEntry->con_sock, tmpEntry->protocol);
       StrAllocCat(rv, small_buf);
	 }

#if defined(DEBUG) && defined(JAVA)
	{
		static int loggingOn = 0;
#ifndef NSPR20
		NETLIBLog.level = (loggingOn ? PRLogLevel_none : PRLogLevel_debug);
		NETLIBLog.depth = 0;    /* keep it from auto-initializing */
#else
		NETLIB->level = (loggingOn ? PR_LOG_NONE : PR_LOG_DEBUG);
#endif /* NSPR20 */
		loggingOn = !loggingOn;
	}
#endif /*  defined(DEBUG) && defined(JAVA) */

	LIBNET_UNLOCK();
	return(rv);
}

#define RELEASE_PREFERRED_URLS TRUE
#define RELEASE_PREFERRED_OR_NON_PREFERRED FALSE
#define RELEASE_PREFETCH_URLS TRUE
#define DONT_RELEASE_PREFETCH_URLS FALSE
PRIVATE void
net_release_urls_for_processing(XP_Bool release_prefered, XP_Bool release_prefetch)
{
	XP_List * list_ptr = net_waiting_for_connection_url_list;
    WaitingURLStruct * wus;

	while((wus = (WaitingURLStruct *)
						XP_ListNextObject(list_ptr)) != NULL)
	  {

		if(!release_prefered
		   || ( (wus->format_out != FO_INTERNAL_IMAGE
			    && wus->format_out != FO_CACHE_AND_INTERNAL_IMAGE)
#ifdef MOZILLA_CLIENT
			    || IL_PreferredStream(wus->URL_s) ) )
#else
		  ) )
#endif /* MOZILLA_CLIENT */
		  {
			/* if the type passed in NOT Prefetch_priority then only
			 * release URL's that are NOT Prefetch_priority
			 */
			if(release_prefetch || wus->URL_s->priority != Prefetch_priority)
			{
				XP_ListRemoveObject(net_waiting_for_connection_url_list, wus);

				/* change prefetch to active prefetch to allow it to load */
				if(wus->URL_s->priority == Prefetch_priority)
					wus->URL_s->priority = CurrentlyPrefetching_priority;

				NET_GetURL(wus->URL_s,
						   wus->format_out,
						   wus->window_id,
						   wus->exit_routine);
				FREE(wus);
				break;
			}
		  }
	  }
}

PRIVATE void
net_release_prefetch_urls_for_processing(void)
{
	/* for now release just one url at a time to give the best possibility
	 * of individual URL's completeing before the user interrupts things
	 */
	net_release_urls_for_processing(RELEASE_PREFERRED_OR_NON_PREFERRED, 
									RELEASE_PREFETCH_URLS);

}

/*
 * Must be called under the cover of the LIBNET_LOCK!
 * 
 * The was_background parameter is used to determine when to call
 * FE_AllConnectionsComplete. It should only be called if we've just
 * completed processing a URL that's *not* a background loading URL.
 * A background URL is one that loads without UI activity e.g. no
 * status bar and thermo activity.
 * This ensures that looping animations don't call 
 * FE_AllConnectionsComplete on every loop cycle.
*/
PRIVATE void
net_CheckForWaitingURL(MWContext * window_id, int protocol, Bool was_background)
{
#ifdef NSPR
	XP_ASSERT(LIBNET_IS_LOCKED());
#endif

	/* decrement here since this function is called
	 * after every exit_routine
	 */
	NET_TotalNumberOfProcessingURLs--;

    if(NET_TotalNumberOfProcessingURLs < 0)
      {
	FE_Alert(window_id, XP_GetString(XP_ALERT_URLS_LESSZERO));
	NET_TotalNumberOfProcessingURLs = 0;
      }

	if(NET_TotalNumberOfOpenConnections < 0)
	  {
		FE_Alert(window_id, XP_GetString(XP_ALERT_CONNECTION_LESSZERO));
	    NET_TotalNumberOfOpenConnections = 0;
	  }

	TRACEMSG(("In: net_CheckForWaitingURL with %d connection waiting URL's, %d active waiting URL's and %d open connections",
	      XP_ListCount(net_waiting_for_connection_url_list),
	      XP_ListCount(net_waiting_for_actives_url_list),
	      NET_TotalNumberOfOpenConnections));

	/* release preferred streams first */
	net_release_urls_for_processing(RELEASE_PREFERRED_URLS,
									DONT_RELEASE_PREFETCH_URLS);

	/* release non-prefered streams */
	net_release_urls_for_processing(RELEASE_PREFERRED_OR_NON_PREFERRED, 
									DONT_RELEASE_PREFETCH_URLS);


    if(!NET_AreThereActiveConnectionsForWindow(window_id))
	  {
		/* if there are prefetch URL's in the queue then we
		 * arn't quite done yet
		 */
		net_release_prefetch_urls_for_processing();

		if(!was_background)
		{
			ET_SendLoadEvent(window_id, EVENT_XFER_DONE, 
					 NULL, NULL, LO_DOCUMENT_LAYER_ID,
					 FALSE);
			FE_AllConnectionsComplete(window_id);
		}
	  }

}

PRIVATE int
net_AbortWaitingURL(MWContext * window_id, Bool all, XP_List *list)
{
	XP_List * list_ptr;                     /* Can't initialize here as list might be null */
	XP_List * prev_list_ptr = list;
	XP_List * tmp_list_ptr;
	WaitingURLStruct * wus;
    int32 cur_win_id=0;
	int number_killed = 0;

	/* make sure we actually got a list of things to Abort */
	if (!list)
		return(number_killed);
	
	/* Now initialize the list pointer */
	list_ptr = list->next;
	
	if(!all && window_id)
		cur_win_id = FE_GetContextID(window_id);

	while(list_ptr)
	  {
		wus = (WaitingURLStruct *)list_ptr->object;

		if(all || (window_id && cur_win_id == FE_GetContextID(wus->window_id)))
		  {
			TRACEMSG(("killing waiting URL"));

			/* call exit routine since we are done */
			net_CallExitRoutine(wus->exit_routine,
								wus->URL_s,
								MK_INTERRUPTED,
								wus->format_out,
								wus->window_id);

			number_killed += 1;

			tmp_list_ptr = list_ptr;
			list_ptr = list_ptr->next;

			/* remove the element from the list
			 *
			 * we have to use the function since it does
			 * funky doubly linked list stuff.
			 */
			XP_ListRemoveObject(list, wus);

			FREE(wus);
		  }
		else
		  {
			prev_list_ptr = list_ptr;
			list_ptr = list_ptr->next;
		  }
	  }

	return(number_killed);
}


/* Helper function for NET_SanityCheckDNS() */
PRIVATE Bool
net_IsHostResolvable (CONST char *hostname, MWContext *context)
{
#ifdef XP_UNIX
	int rv;
	PRHostEnt hpbuf;
	char dbbuf[PR_NETDB_BUF_SIZE];
#ifndef NSPR20
	rv = (PR_gethostbyname(hostname, &hpbuf, dbbuf, sizeof(dbbuf), 0)
		  ? TRUE : FALSE);
#else
        if (PR_GetHostByName(hostname, dbbuf, sizeof(dbbuf), &hpbuf) == PR_FAILURE)
	    rv = FALSE;
	else
	    rv = TRUE;
#endif
	return rv;
#else
	return(FALSE);
#endif
}


#if defined(__sun) && !defined(__svr4__)
/* From xfe/dns-stub.o or xfe/nis-stub.o, depending.  Gag. */
extern int fe_HaveDNS;
#endif


PUBLIC void
NET_SanityCheckDNS (MWContext *context)
{
#ifdef XP_UNIX

  char *proxy = MKhttp_proxy;
  char *socks = NET_SocksHostName;
  char *test_host_1 = "home.netscape.com";
  char *test_host_2 = "home6.netscape.com";
  char *test_host_3 = "internic.net";
  char *message;
#if defined(__sun) && !defined(__svr4__)
  char temp[1000];
#endif

  static Bool done;
  if (done) return;
  done = TRUE;

  message = (char *) XP_ALLOC (3000);
  if (! message)
	return;
  *message = 0;

  if (proxy)
	proxy = XP_STRDUP (proxy);
  if (socks)
	socks = XP_STRDUP (socks);

  /* Strip off everything after last colon. */
  {
    char *s;
    if (proxy && (s = XP_STRRCHR (proxy, ':')))
	  *s = 0;
    if (socks && (s = XP_STRRCHR (socks, ':')))
	  *s = 0;
  }

  if (proxy)
	proxy = XP_StripLine (proxy); /* in case it was "hostname:  80" */
  if (socks)
	socks = XP_StripLine (socks);

  /* If the hosts are specified as IP numbers, don't try and resolve them.
     (Yes, hostnames can't begin with digits.) */
  if (proxy && proxy[0] >= '0' && proxy[0] <= '9')
    XP_FREE (proxy), proxy = 0;
  if (socks && socks[0] >= '0' && socks[0] <= '9')
    XP_FREE (socks), socks = 0;

  if (proxy && *proxy)
    {
      /* If there is an HTTP proxy, then we shouldn't try to resolve any
	 other hosts at all, because they might legitimately be unresolvable.
	 The HTTP proxy will do all lookups.
       */
      if (!net_IsHostResolvable (proxy, context))
	{
		  sprintf(message, XP_GetString(XP_UNKNOWN_HTTP_PROXY), proxy);
	}
    }
  else
    {
      /* There is not an HTTP proxy specified.  So check the other hosts.
       */

      if (socks && *socks && !net_IsHostResolvable (socks, context))
	{
		  sprintf(message, XP_GetString(XP_UNKNOWN_SOCKS_HOST), socks);
#ifdef XP_UNIX
			XP_STRCAT (message, XP_GetString(XP_SOCKS_NS_ENV_VAR));
		  /* Only Unix has the $SOCKS_NS environment variable. */
#endif /* XP_UNIX */
			XP_STRCAT (message, XP_GetString(XP_CONSULT_SYS_ADMIN));
	}
      else
	{
	  /* At this point, we're not using a proxy, and either we're not
	     using a SOCKS host or we're using a resolvable SOCKS host.
	     So that means that all the usual host names should be resolvable,
	     and the world is broken if they're not.
	   */
	  char *losers [10];
	  int loser_count = 0;

#ifdef XP_UNIX
	  char local [255], *local2;
	  PRHostEnt *hent, hpbuf;
		  char dbbuf[PR_NETDB_BUF_SIZE];

	  if (gethostname (local, sizeof (local) - 1))
	    local [0] = 0;

	  /* gethostname() and gethostbyname() often return different data -
	     on many systems, the former is basename, the latter is FQDN. */
#ifndef NSPR20
	  if (local &&
	      (hent = PR_gethostbyname (local, &hpbuf, dbbuf, sizeof(dbbuf), 0)) &&
	      XP_STRCMP (local, hent->h_name))
	    local2 = XP_STRDUP (hent->h_name);
	  else
	    local2 = 0;
#else
          local2 = 0;
	  if (local &&
	      (PR_GetHostByName (local, dbbuf, sizeof(dbbuf), &hpbuf) == PR_SUCCESS)) { 
	  hent = &hpbuf;
	  if (XP_STRCMP (local, hent->h_name))
	    local2 = XP_STRDUP (hent->h_name);
      }
#endif
	  if (local && *local && !net_IsHostResolvable (local, context))
	    losers [loser_count++] = local;
	  if (local2 && *local2 && !net_IsHostResolvable (local2, context))
	    losers [loser_count++] = local2;
#endif /* XP_UNIX */

	  if (!net_IsHostResolvable (test_host_1, context))
	    losers [loser_count++] = test_host_1;
	  if (!net_IsHostResolvable (test_host_2, context))
	    losers [loser_count++] = test_host_2;
	  if (!net_IsHostResolvable (test_host_3, context))
	    losers [loser_count++] = test_host_3;

	  if (loser_count > 0)
	    {
	      if (loser_count > 1)
		{
		  int i;
		  sprintf(message, XP_GetString(XP_UNKNOWN_HOSTS));
		  for (i = 0; i < loser_count; i++)
		    {
		      XP_STRCAT (message, "                    ");
		      XP_STRCAT (message, losers [i]);
		      XP_STRCAT (message, "\n");
		    }
		}
	      else
		{
				  sprintf(message, XP_GetString(XP_UNKNOWN_HOST), losers[0]);
		}
	      XP_STRCAT (message, XP_GetString(XP_SOME_HOSTS_UNREACHABLE));
#ifdef XP_UNIX
# if defined(__sun) && !defined(__svr4__)       /* compiled on SunOS 4.1.3 */
			  if (fe_HaveDNS)
				/* Don't talk about $SOCKS_NS in the YP/NIS version. */
# endif
				XP_STRCAT (message, XP_GetString(XP_SOCKS_NS_ENV_VAR));

#if defined(__sun) && !defined(__svr4__)        /* compiled on SunOS 4.1.3 */
			  assert (XP_AppName);
			  if (fe_HaveDNS)
				{
					sprintf(temp, XP_GetString(XP_THIS_IS_DNS_VERSION),
						XP_AppName);
				}
			  else
				{
					sprintf(temp, XP_GetString(XP_THIS_IS_YP_VERSION),
						XP_AppName);
				}
			  XP_STRCAT(message, temp);
# endif /*  SunOS 4.* */
#endif /* XP_UNIX */
	      XP_STRCAT (message, XP_GetString(XP_CONSULT_SYS_ADMIN));
	    }

#ifdef XP_UNIX
	  if (local2) XP_FREE (local2);
#endif /* XP_UNIX */
	}
    }

  if (proxy) XP_FREE (proxy);
  if (socks) XP_FREE (socks);

  if (*message)
    FE_Alert (context, message);
  XP_FREE (message);
#endif /* XP_UNIX full function wrap */
}


/* shutdown the netlibrary, cancel all
 * conections and free all
 * memory
 */
PUBLIC void
NET_ShutdownNetLib(void)
{
    ActiveEntry *tmpEntry;

/* #ifdef XP_WIN */
#ifdef JAVA
	if (libnet_asyncIO) LIBNET_LOCK();
#else
    LIBNET_LOCK();
#endif

    if(net_waiting_for_actives_url_list) {
	    net_AbortWaitingURL(0, TRUE, net_waiting_for_actives_url_list);
	XP_ListDestroy(net_waiting_for_actives_url_list);
	net_waiting_for_actives_url_list = 0;
    }

    if(net_waiting_for_connection_url_list) {
	net_AbortWaitingURL(0, TRUE, net_waiting_for_connection_url_list);
	XP_ListDestroy(net_waiting_for_connection_url_list);
	net_waiting_for_connection_url_list = 0;
    }

    /* run through list of
     * connections
     */
    while((tmpEntry = (ActiveEntry *)XP_ListRemoveTopObject(net_EntryList)) != 0)
      {

		if(tmpEntry->proto_impl)
		{
			(*tmpEntry->proto_impl->interrupt)(tmpEntry);
		}
		else
		{
			XP_ASSERT(0);
		}

     	/* XP_OS2_FIX IBM-MAS: limit length of output to keep from blowing trace buffer! */
    	TRACEMSG(("End of transfer, entry (soc=%d, con=%d) being removed from list with %d status: %-.1900s",
               tmpEntry->socket, tmpEntry->con_sock, tmpEntry->status, tmpEntry->URL_s->address));

	 	/* call exit routine since we know we are done */
	 	net_CallExitRoutine(tmpEntry->exit_routine,
							 tmpEntry->URL_s,
							 tmpEntry->status,
							 tmpEntry->format_out,
							 tmpEntry->window_id);
	 	XP_FREE(tmpEntry);  /* free the no longer active entry */
      }

	XP_ListDestroy(net_EntryList);
	net_EntryList = 0;

    /* free any memory in the protocol modules
     */
	net_cleanup_reg_protocol_impls();

#ifdef MOZILLA_CLIENT
	NET_SaveCookies("");
	GH_SaveGlobalHistory();
#endif /* MOZILLA_CLIENT */

	/* purge existing cache files */

    /* free memory in the tcp routines
     */
    NET_CleanupTCP();

#ifdef MOZILLA_CLIENT
    NET_CleanupCache("");
	NET_SetMemoryCacheSize(0); /* free memory cache */

#endif /* MOZILLA_CLIENT */

#ifdef XP_UNIX
	NET_CleanupFileFormat(NULL);
#else
	NET_CleanupFileFormat();
#endif

/* #ifdef XP_WIN */
#ifdef JAVA
    if (libnet_asyncIO) LIBNET_UNLOCK();
#else
    LIBNET_UNLOCK();
#endif
}

static PRBool warn_on_mailto_post = PR_TRUE;

extern void
NET_WarnOnMailtoPost(PRBool warn)
{
	warn_on_mailto_post = warn;
	return;
}

/* use a small static array for speed of traversal.
 * We rely on the fact that we dont expect ton's of 
 * protocol implementations to be dynamically added
 * so a static size is managable for now.
 *
 * URL types should really be dynamic so that we dont have
 * to define types in net.h
 */
#define MAX_NUMBER_OF_PROTOCOL_IMPLS LAST_URL_TYPE

typedef struct {
	NET_ProtoImpl *impl;
	int            url_type;
} net_ProtoImplAndTypeAssoc;

PRIVATE net_ProtoImplAndTypeAssoc net_proto_impls[MAX_NUMBER_OF_PROTOCOL_IMPLS];
PRIVATE net_number_of_proto_impls = 0;

/* registers a protocol impelementation for a particular url_type
 * see NET_URL_Type() for types
 */
void NET_RegisterProtocolImplementation(NET_ProtoImpl *impl, int for_url_type)
{

	if(!impl || for_url_type < 0 || for_url_type > LAST_URL_TYPE)
	{
		XP_ASSERT(0);
		return;
	}

	net_proto_impls[net_number_of_proto_impls].impl = impl;
	net_proto_impls[net_number_of_proto_impls].url_type = for_url_type;
	
	net_number_of_proto_impls++;
}

/* get a handle to a protocol implemenation
 */
NET_ProtoImpl *
net_get_protocol_impl(int for_url_type)
{
	int count=0;

	/* if we ever get around to doing dynamic protocol loading
	 * this would be a good place to plug it in.
	 * just load a DLL with the implementation and
	 * return the handle.
	 * The integer URL_TYPE would need to be replaced with
	 * strings or some other identifier so that it can all be
	 * handled dynamically
	 */

	for(; count < net_number_of_proto_impls; count++)
	{
		if(net_proto_impls[count].url_type == for_url_type)
			return net_proto_impls[count].impl;
	}

	XP_ASSERT(0);  /* should always find one */
	return NULL; 
}

void
net_cleanup_reg_protocol_impls(void)
{
	int count=0;

	for(; count < net_number_of_proto_impls; count++)
	{
        (*net_proto_impls[count].impl->cleanup)();
	}

}

/* register and begin a transfer.
 *
 * returns negative if the transfer errored or finished during the call
 * otherwise returns 0 or greater.
 *
 * A URL, an output format, a window context pointer, and a callback routine
 * should be passed in.
 */

PUBLIC int
NET_GetURL (URL_Struct *URL_s,
	    FO_Present_Types output_format,
	    MWContext *window_id,
	    Net_GetUrlExitFunc* exit_routine)
{
    int          status=MK_MALFORMED_URL_ERROR;
    int          pacf_status=TRUE;
    int          type;
    int          cache_method=0;
    ActiveEntry *this_entry=0;  /* a new entry */
	char        *new_address;
	int processcallbacks = 0;
	Bool confirm;
	Bool load_background;
	char *confirmstring;
	TRACEMSG(("Entering NET_GetURL"));
	LIBNET_LOCK();

#if !defined(NSPR20_DISABLED) && defined(XP_UNIX)
	/* temporarily use busy poll to ease transition */
	NET_SetCallNetlibAllTheTime(window_id, "mkgeturl");
#endif
	
#ifdef XP_WIN
	/* this runs a timer to periodically call the netlib
	 * so that we still get events even when OnIdle is never
	 * called
	 */
	NET_SetNetlibSlowKickTimer(TRUE);
#endif

	XP_ASSERT (URL_s && URL_s->address);
#ifdef MOZILLA_CLIENT
	if ( URL_s->mailto_post && warn_on_mailto_post) {
		confirmstring = NULL;
		confirm = FALSE;
		
		StrAllocCopy(confirmstring, XP_GetString(XP_CONFIRM_MAILTO_POST_1));
		StrAllocCat(confirmstring, XP_GetString(XP_CONFIRM_MAILTO_POST_2));
		
		if ( confirmstring ) {
			confirm = FE_Confirm(window_id, confirmstring);
			XP_FREE(confirmstring);
		}
		
		if ( !confirm ) {
			/* abort url
			 */
			net_CallExitRoutine(exit_routine,
								URL_s,
								MK_INTERRUPTED,
								output_format,
								window_id);
			LIBNET_UNLOCK_AND_RETURN(MK_INTERRUPTED);
		}
	}
#endif /* MOZILLA_CLIENT */     
#ifdef EDITOR
    /* First time here - get our strings out of XP_MSG */
    if (XP_NEW_DOC_URL == NULL) {
		StrAllocCopy(XP_NEW_DOC_URL, XP_GetString(XP_EDIT_NEW_DOC_URL));
    }
    if (XP_NEW_DOC_NAME == NULL) {
		StrAllocCopy(XP_NEW_DOC_NAME, XP_GetString(XP_EDIT_NEW_DOC_NAME));
    }

    /* Test for "Untitled" URL */
    if( 0 == XP_STRCMP(URL_s->address, XP_NEW_DOC_NAME) ) {
    	/* Change request to load "file:///Untitled" into "about:editfilenew"  */
	    XP_FREE(URL_s->address);
	    URL_s->address = XP_STRDUP(XP_NEW_DOC_URL);
	    /* Set flag so FE can quickly detect new doc */
	    window_id->is_new_document = TRUE;
    }
    else if( 0 == XP_STRCMP(URL_s->address, XP_NEW_DOC_URL) ) {
		window_id->is_new_document = TRUE;
    }
    else {
        /* Be sure this is FALSE if not a new document */
        window_id->is_new_document = FALSE;
    }
    /* Hack to allow special URL for new Editor doc 
     *   and still allow filtering of non-editable doc types
     * All editor GetUrl calls should use FO_CACHE_AND_EDIT
     *   except in the case of the new document URL
    */
    if( EDT_IS_EDITOR(window_id) && window_id->is_new_document ) {
        output_format = FO_CACHE_AND_PRESENT;
    }
#endif
#ifdef MOZILLA_CLIENT

		/* Hack. Proxy AutoDiscovery. If we want to use proxy autodiscovery
	     * simply point the MKproxy_ac_url to the MK_padpacURL and use all 
	     * all the same logic. */
		if(!MKproxy_ac_url && NET_UsingPadPac()) {
			NET_SetNoProxyFailover();
			MKproxy_ac_url=MK_padPacURL;
		}

	/* Initialize global/auto proxy codepath. This gets called for every 
	 * NET_GetURL() call. */
	if (
#ifdef MOZ_OFFLINE
		!NET_IsOffline() &&
#endif /* MOZ_OFFLINE */
		/* Do we even have auto config urls set? If not, don't bother
		 * continuing. See mkautocf.c for more info. */
		(MKglobal_config_url || MKproxy_ac_url)
		&& (CLEAR_CACHE_BIT(output_format) == FO_PRESENT)
		&& (!(type = NET_URL_Type(URL_s->address))
		 || type == HTTP_TYPE_URL
		 || type == SECURE_HTTP_TYPE_URL
		 || type == GOPHER_TYPE_URL
		 || type == FTP_TYPE_URL
		 || type == WAIS_TYPE_URL
		 || type == URN_TYPE_URL
		 || type == NFS_TYPE_URL
		 || type == POP3_TYPE_URL
		 || (type == NEWS_TYPE_URL && !strncasecomp(URL_s->address, "snews:", 6)))
		 ) {
		int status=-1;
		/* Figure out which auto config we're dealing (global or pac file
		 * with and load it.
		 * If the status is other than -1, the load started and the 
		 * originally requested url will be loaded by the pre_exit_fn()
		 * that is in the proxy auto config URL_Struct. */
		if(MKglobal_config_url && !NET_GlobalAcLoaded) {
			NET_GlobalAcLoaded=TRUE;
			status=NET_LoadProxyConfig(MKglobal_config_url,
										URL_s,
										output_format,
										window_id,
										exit_routine);
		} 
		/* else we know we're dealing with a proxyACL (the other 
		 * possibility in the above if statement) so see if it's 
		 * loaded or not. Also make sure we want to use it (i.e. style is
		 * automatic or if we want to use a proxy autodiscovery url). */
		else if(!NET_ProxyAcLoaded
				&& (   (MKproxy_style == PROXY_STYLE_AUTOMATIC) 
					|| !MKproxy_style
					|| NET_UsingPadPac() ) ) {
			NET_ProxyAcLoaded=TRUE;
			status=NET_LoadProxyConfig(MKproxy_ac_url,
										URL_s,
										output_format,
										window_id,
										exit_routine);
		}
		if(status != -1)
			LIBNET_UNLOCK_AND_RETURN(status);
	} /* End big proxy if */

#endif  /* MOZILLA_CLIENT */

	load_background = URL_s->load_background;

	/* kill leading and trailing spaces in the URL address
	 */
	new_address = XP_StripLine(URL_s->address);
	if(new_address != URL_s->address)
	  {
		XP_MEMMOVE(URL_s->address, new_address, XP_STRLEN(new_address)+1);
	  }

	/* get the protocol type
	 */
    type = NET_URL_Type(URL_s->address);

	if (URL_s->method == URL_HEAD_METHOD &&
		type != HTTP_TYPE_URL && type != SECURE_HTTP_TYPE_URL && type != FILE_TYPE_URL) {
	  /* We can only do HEAD on http connections. */
	  net_CallExitRoutine(exit_routine,
						  URL_s,
						  MK_MALFORMED_URL_ERROR, /* Is this right? ### */
						  output_format,
						  window_id);
	  /* increment since it will get decremented */
	  NET_TotalNumberOfProcessingURLs++;
	  net_CheckForWaitingURL(window_id, 0, load_background);

	  LIBNET_UNLOCK_AND_RETURN(MK_MALFORMED_URL_ERROR);
	}

    /* XP_OS2_FIX IBM-MAS:limit length of output to keep from blowing trace buffer! */
   TRACEMSG(("Called NET_GetURL with FO: %d URL %-.1900s --", output_format, URL_s->address));
   TRACEMSG(("with method: %d, and post headers: %s", URL_s->method,
            URL_s->post_headers ? URL_s->post_headers : "none"));

	/* if this URL is for prefetching, put it on the wait queue until
	 * everything else is done
	 */
	if(URL_s->priority == Prefetch_priority)
	{
		LIBNET_UNLOCK_AND_RETURN(net_push_url_on_wait_queue(type,
															URL_s,
															output_format,
															window_id,
															exit_routine));
	}

	/* put a limit on the total number of active urls
	 */
    if((NET_TotalNumberOfProcessingURLs >= MAX_NUMBER_OF_PROCESSING_URLS) && 
       ((output_format & FO_ONLY_FROM_CACHE) == 0))
	  {
	LIBNET_UNLOCK_AND_RETURN(net_push_url_on_wait_queue(type,
															URL_s,
															output_format,
															window_id,
															exit_routine));
	  }

	/* if we get this far then we should add the URL
	 * to the number of processing URL's
	 */
	NET_TotalNumberOfProcessingURLs++;

	if(type == VIEW_SOURCE_TYPE_URL)
	  {
		/* this is a view-source: URL
		 * strip off the front stuff 
		 */
		char *new_address=0;
		/* the colon is guarenteed to be there */
		StrAllocCopy(new_address, XP_STRCHR(URL_s->address, ':')+1);
		FREE(URL_s->address);
		URL_s->address = new_address;

		type = NET_URL_Type(URL_s->address);

		/* remap the format out for the fo_present type
		 */
		if(CLEAR_CACHE_BIT(output_format) == FO_PRESENT)
			output_format = FO_VIEW_SOURCE;
	  }

	if(type == MARIMBA_TYPE_URL)
	  {
		/* If this a castanet URL, and Netcaster is not installed, try http://
		 * Otherwise, we let the castanet protocol handler do its thing.
		 */
		if( ! FE_IsNetcasterInstalled() )
		{
			char *new_address;
			/* Replace castanet:// with http:// */
			/* Allocate space for 'http' + the rest of the string */
			new_address = XP_ALLOC(XP_STRLEN(XP_STRCHR(URL_s->address, ':'))+5);

			XP_ASSERT(new_address);
			*new_address=0;
			XP_STRCAT(new_address,"http");
			XP_STRCAT(new_address,XP_STRCHR(URL_s->address, ':'));

			FREE(URL_s->address);
			URL_s->address = new_address;

			type = NET_URL_Type(URL_s->address);
		}
	  }

	if(type == NETHELP_TYPE_URL)
	  {
		/* this is a nethelp: URL
		 * separate into its component parts, the mapping file in URL_s->address,
		 * and the topic in URL_s->fe_data
		 */

		if (NET_ParseNetHelpURL(URL_s) == MK_OUT_OF_MEMORY) {
			LIBNET_UNLOCK_AND_RETURN(MK_OUT_OF_MEMORY);
		}
		
		type = NET_URL_Type(URL_s->address);

		output_format = FO_LOAD_HTML_HELP_MAP_FILE;
	  }

#ifdef MOZILLA_CLIENT
	if(
#if defined(XP_WIN) || defined(XP_MAC) || defined(XP_OS2)
	   FE_UseExternalProtocolModule(window_id, output_format, URL_s, exit_routine) ||
#endif
	   NPL_HandleURL(window_id, output_format, URL_s, exit_routine))
      {
		/* don't call the exit routine since the
		 * External protocol module will call it
		 */
	net_CheckForWaitingURL(window_id, 0, load_background);
 
	LIBNET_UNLOCK_AND_RETURN(-1); /* all done */
      }

	if(NET_TimeBombActive)
	  {
		/* Timebomb has gone off!!
		 *
		 * limit URL's to FTP and anything in our domains
		 */
		if(type != FTP_TYPE_URL
			&& type != ABOUT_TYPE_URL
			 && type != FILE_TYPE_URL
			  && type != MAILBOX_TYPE_URL
			   && type != IMAP_TYPE_URL
			   && type != POP3_TYPE_URL
			    && type != MOCHA_TYPE_URL
			     && type != DATA_TYPE_URL
			  && type != HTML_DIALOG_HANDLER_TYPE_URL
				   && type != HTML_PANEL_HANDLER_TYPE_URL
					&& type != INTERNAL_SECLIB_TYPE_URL

			  && !strcasestr(URL_s->address, "mcom.com")
			       &&  !strcasestr(URL_s->address, "netscape.com"))
		  {
			char * alert = NET_ExplainErrorDetails(MK_TIMEBOMB_URL_PROHIBIT);

			FE_Alert(window_id, alert);
			FREE(alert);

		/* we need at least an address
		 * if we don't have it exit this routine
	     */
		net_CallExitRoutine(exit_routine,
			    URL_s,
			    MK_TIMEBOMB_URL_PROHIBIT,
							output_format,
			    window_id);

			net_CheckForWaitingURL(window_id, 0, load_background);

		LIBNET_UNLOCK_AND_RETURN(MK_TIMEBOMB_URL_PROHIBIT);
		  }
	  }

	/* put up security dialog boxes to tell the user
	 * about transitions
	 *
	 * only do this for FO_PRESENT objects and objects
	 * not coming out of history
	 * and also make sure that the object is not being asked
	 * for twice via the "304 use local copy" method
	 */
	if(output_format == FO_CACHE_AND_PRESENT
		&& !URL_s->history_num
		 && !URL_s->use_local_copy)
	  {
		Bool continue_loading_url = TRUE;
		History_entry * h = SHIST_GetCurrent(&window_id->hist);

		/* this is some protection against the "mail document"
		 * feature popping up a dialog warning about an insecure
		 * post.
		 * If the type is MAILTO and the first post header
		 * begins with "Content-type" then it is a post from
		 * inside a form, otherwise it is just a normal MAILTO
		 *
		 * And, of course, anything not a MAILTO link will fall
		 * into the if as well.
		 */
		if(type != MAILTO_TYPE_URL
			|| !URL_s->post_headers
			 || !XP_STRNCMP("Content-type", URL_s->post_headers, 12))
		  {
		    if(h && h->security_on)
		      {
				/* if this is not a secure transaction */
			    if(type != SECURE_HTTP_TYPE_URL
					&& !(type == NEWS_TYPE_URL &&
						 toupper(*URL_s->address) == 'S') )
			      {
				    if(URL_s->method == URL_POST_METHOD)
				      {
					    continue_loading_url = (Bool)SECNAV_SecurityDialog(window_id,
										   SD_INSECURE_POST_FROM_SECURE_DOC);
				      }
				    else if(!URL_s->redirecting_url
							 && type != MAILTO_TYPE_URL
								&& type != ABOUT_TYPE_URL
							       && type != MOCHA_TYPE_URL)
				      {
					    /* don't put up in case of redirect or mocha
					     */
					    continue_loading_url = (Bool)SECNAV_SecurityDialog(window_id,
										   SD_LEAVING_SECURE_SPACE);
				      }
			      }
		      }
		    else
		      {
				/* put up a dialog for all insecure posts
				 * except news and mail posts
				 */
			    if(URL_s->method == URL_POST_METHOD
					&& type != SECURE_HTTP_TYPE_URL
					 && type != INTERNAL_NEWS_TYPE_URL
					 && type != NEWS_TYPE_URL
					  && type != HTML_DIALOG_HANDLER_TYPE_URL
					   && type != HTML_PANEL_HANDLER_TYPE_URL
					   && type != INTERNAL_SECLIB_TYPE_URL
					    && type != MAILTO_TYPE_URL)
		  {
				    continue_loading_url = (Bool)SECNAV_SecurityDialog(window_id,
					   SD_INSECURE_POST_FROM_INSECURE_DOC);
		  }
		      }

		    if(!continue_loading_url)
		      {
			    /* abort url
		 */
		net_CallExitRoutine(exit_routine,
									URL_s,
									MK_INTERRUPTED,
									output_format,
									window_id);
		net_CheckForWaitingURL(window_id, 0, load_background);

		LIBNET_UNLOCK_AND_RETURN(MK_INTERRUPTED);
		      }
		  }
      }
#endif /* MOZILLA_CLIENT */

	/*
	 * if the string is not a valid URL we are just going
     * to punt, so we may as well try it as http
	 */
	if(type==0)
	{
		char *munged;

#define INT_SEARCH_URL "http://cgi.netscape.com/cgi-bin/url_search.cgi?search="
#define INT_SEARCH_URL_TYPE HTTP_TYPE_URL

		if(!(munged = (char*) XP_ALLOC(XP_STRLEN(URL_s->address)+20)))
		{
		    net_CallExitRoutine(exit_routine,
								URL_s,
								MK_OUT_OF_MEMORY,
								output_format,
								window_id);
			
			LIBNET_UNLOCK_AND_RETURN(MK_OUT_OF_MEMORY);
		}       

#ifdef EDITOR
	/* Some platforms resize a window while creating it. An example is
	 * Windows in Communicator 4.0. On these platforms,
	 * the Composer View Document Source window can end up here with a
	 * bizarre (and potentially localized) URL of "View Document Source".
	 * If we allow this URL to be resolved, it will cause a search engine
	 * to be triggered.
	 * Fortunately, edit_view_source_hack is TRUE iff the context is
	 * a Composer View Document Source window.
	 */
	if ( window_id->edit_view_source_hack ){
	    net_CallExitRoutine(exit_routine,
							    URL_s,
							    MK_INTERRUPTED,
							    output_format,
							    window_id);
			LIBNET_UNLOCK_AND_RETURN(MK_INTERRUPTED);
	}
#endif

		/* if it starts with a question mark or has a space it's
		 * a search URL
		 */
		if(*URL_s->address == '?' || XP_STRCHR(URL_s->address, ' '))
		  {
			/* URL contains spaces.  Treat it as search text. */
			char *escaped;

			if(*URL_s->address == '?')
				escaped = NET_Escape(URL_s->address+1, URL_XPALPHAS);
			else
				escaped = NET_Escape(URL_s->address, URL_XPALPHAS);

			if(escaped)
			  {
				char *pUrl;
				PREF_CopyCharPref("network.search.url",&pUrl);
				FREE(munged);
				if (pUrl) {
					munged = PR_smprintf("%s%s", pUrl, escaped);
					FREE(escaped);
					XP_FREE(pUrl);
				}
			    type = INT_SEARCH_URL_TYPE;
			  }
		  }
		else
		  {
			if(*URL_s->address == '/')
			  {
			    XP_STRCPY(munged, "file:");
			    type = FILE_TYPE_URL;
		      }
		    else if(!strncasecomp(URL_s->address, "ftp", 3))
		      {
			    XP_STRCPY(munged, "ftp://");
			    type = FTP_TYPE_URL;
		      }
		    else if(!strncasecomp(URL_s->address, "gopher", 6))
		      {
			    XP_STRCPY(munged, "gopher://");
			    type = GOPHER_TYPE_URL;
		      }
		    else if(!strncasecomp(URL_s->address, "news", 4)
				    || !strncasecomp(URL_s->address, "nntp", 4))
		      {
			    XP_STRCPY(munged, "news://");
			    type = NEWS_TYPE_URL;
		      }
			else
		      {
			    XP_STRCPY(munged, "http://");
			    type = HTTP_TYPE_URL;
		      }
    
		    XP_STRCAT(munged, URL_s->address);
		  }

		XP_FREE(URL_s->address);
		URL_s->address = munged;

#ifdef MOZILLA_CLIENT
		/*      Try the external protocol handlers again, since with the
		 *              newly appended protocol, they might work this time.
		 */
		if(
	#if defined(XP_WIN) || defined(XP_MAC)
		   FE_UseExternalProtocolModule(window_id, output_format, URL_s, exit_routine) ||
	#endif
		   NPL_HandleURL(window_id, output_format, URL_s, exit_routine))
	      {
			/* don't call the exit routine since the
			 * External protocol module will call it
			 */
		net_CheckForWaitingURL(window_id, 0, load_background);
 
		LIBNET_UNLOCK_AND_RETURN(-1); /* all done */
	      }
#endif /* MOZILLA_CLIENT */
	}

    if(type == HTTP_TYPE_URL || type == FILE_TYPE_URL ||
		type == SECURE_HTTP_TYPE_URL || type == GOPHER_TYPE_URL
#ifdef JAVA
	/* Castanet URLs don't work when there's no Java */
		|| type == MARIMBA_TYPE_URL
#endif
		)
	add_slash_to_URL(URL_s);

#ifdef MOZILLA_CLIENT
	/*
	 * If this is a resize-reload OR a view-source URL, try to use the
	 * URL's wysiwyg: counterpart.
	 */
	URL_s->resize_reload = (URL_s->force_reload == NET_RESIZE_RELOAD);
	if(URL_s->wysiwyg_url &&
	   (URL_s->resize_reload == TRUE ||
		CLEAR_CACHE_BIT(output_format) == FO_VIEW_SOURCE))
	  {
		StrAllocCopy(URL_s->address, URL_s->wysiwyg_url);
		type = WYSIWYG_TYPE_URL;
	  }

	/* the FE's were screwing up the use of force_reload
     * to get around a bug in the scroll to named anchor
	 * code.  So I gave them an enum NET_RESIZE_RELOAD
	 * that they could use instead.  NET_RESIZE_RELOAD
	 * should be treated just like NET_DONT_RELOAD within
	 * the netlib
	 */
	if((URL_s->force_reload == NET_RESIZE_RELOAD) ||
       (URL_s->force_reload == NET_CACHE_ONLY_RELOAD))
		URL_s->force_reload = NET_DONT_RELOAD;

    /* check for the url in the cache
     */
    cache_method = NET_FindURLInCache(URL_s, window_id);

	if (!cache_method)
	  {
		/* cache testing stuff */
		if(NET_CacheTraceOn)
		  {
			char *buf = 0;
			StrAllocCopy(buf, XP_GetString(XP_URL_NOT_FOUND_IN_CACHE));
			StrAllocCat(buf, URL_s->address);
			FE_Alert(window_id, buf);
			FREE(buf);
		  }

		/* if wysiwyg, there must be a cache entry or we retry the real url */
		if(type == WYSIWYG_TYPE_URL)
		  {
			const char *real_url = LM_SkipWysiwygURLPrefix(URL_s->address);

			/* XXX can't use StrAllocCopy because it frees dest first */
			if (real_url && (new_address = XP_STRDUP(real_url)) != NULL)
			  {
				/* cache miss: we must clear this flag so scripts rerun */
				URL_s->resize_reload = FALSE;

				XP_FREE(URL_s->address);
				URL_s->address = new_address;
				FREE_AND_CLEAR(URL_s->wysiwyg_url);
				/* 
		 		* Since the call to NET_GetURL will increment the
		 		* following counter again, we decrement it so as not
		 		* to overcount.
		 		*/
				NET_TotalNumberOfProcessingURLs--;
				LIBNET_UNLOCK_AND_RETURN(
							NET_GetURL(URL_s, output_format, window_id, exit_routine)
						);
			  }
		  }
	  }

    /* if cache_method is non zero then we have this URL cached.  Use
     * the bogus cache url type
	 *
	 * This nasty bit of logic figures out if we really
	 * need to reload it or if we can use the cached copy
     */
    else
      {
		if(URL_s->use_local_copy || output_format & FO_ONLY_FROM_CACHE)
		  {
	    /* the cached file is valid so use it unilaterally
	     */
	    type = cache_method;
		  }
		else if(URL_s->real_content_length > URL_s->content_length)
		  {
			/* cache testing stuff */
			if(NET_CacheTraceOn)
			  {
				char *buf = 0;
				StrAllocCopy(buf, XP_GetString(XP_PARTIAL_CACHE_ENTRY));
				StrAllocCat(buf, URL_s->address);
				FE_Alert(window_id, buf);
				FREE(buf);
			  }

	    URL_s->memory_copy = 0;
	    cache_method = 0;
	    TRACEMSG(("Getting the rest of a partial cache file"));
		  }
	else if (URL_s->force_reload != NET_DONT_RELOAD)
	  {
	    TRACEMSG(("Force reload flag set.  Checking server to see if modified"));
	    /* cache testing stuff */
	    if(NET_CacheTraceOn)
	      {
		char *buf = 0;
		StrAllocCopy(buf, XP_GetString(XP_CHECKING_SERVER__FORCE_RELOAD));
		StrAllocCat(buf, URL_s->address);
		FE_Alert(window_id, buf);
		FREE(buf);
	      }

	    /* strip the cache file and
	     * memory pointer
	     */
	    if (type == WYSIWYG_TYPE_URL)
		type = cache_method;
	    else
	      {
		FREE_AND_CLEAR(URL_s->cache_file);
		URL_s->memory_copy = 0;
		cache_method = 0;
	      }
	  }
	else if(URL_s->expires)         
	  {
	    time_t cur_time = time(NULL);

	    if(cur_time > URL_s->expires)
	      {
		FREE_AND_CLEAR(URL_s->cache_file);
		URL_s->memory_copy = 0;
		URL_s->expires = 0;  /* remove cache reference */
		cache_method = 0;

		/* cache testing stuff */
		if(NET_CacheTraceOn)
		  {
		    char *buf = 0;
		    StrAllocCopy(buf, XP_GetString(XP_OBJECT_HAS_EXPIRED));
		    StrAllocCat(buf, URL_s->address);
		    FE_Alert(window_id, buf);
		    FREE(buf);
		  }
	      }
			else
			  {
			/* the cached file is valid so use it unilaterally
			 */
		type = cache_method;
			  }
		  }
		else if((NET_CacheUseMethod == CU_NEVER_CHECK || URL_s->history_num)
				  && !URL_s->expires)
		  {
	    type = cache_method;
		  }
		else if(NET_CacheUseMethod == CU_CHECK_ALL_THE_TIME
			    && CLEAR_CACHE_BIT(output_format) == FO_PRESENT
				  && (type == HTTP_TYPE_URL || type == SECURE_HTTP_TYPE_URL)
					  && !URL_s->expires)
		  {
			/* cache testing stuff */
			if(NET_CacheTraceOn)
			  {
				char *buf = 0;
				StrAllocCopy(buf, XP_GetString(XP_CHECKING_SERVER_CACHE_ENTRY));
				StrAllocCat(buf, URL_s->address);
				FE_Alert(window_id, buf);
				FREE(buf);
			  }

			 /* if it's an HTTP URL and its not an Internal image
			  * and it's not being asked for with ONLY FROM CACHE
			  * and it's not coming out of the history
			  * and it doesn't have an expiration date...
			  * FORCE Reload it
			  */
	    TRACEMSG(("Non history http file found. Force reloading it "));
	    /* strip the cache file and
	     * memory pointer
	     */
	    FREE_AND_CLEAR(URL_s->cache_file);
	    URL_s->memory_copy = 0;
	    cache_method = 0;

		  }
		else if(!URL_s->last_modified
				&& CLEAR_CACHE_BIT(output_format) == FO_PRESENT
				&& (type == HTTP_TYPE_URL || type == SECURE_HTTP_TYPE_URL) 
#ifdef MOZ_OFFLINE            
	    && !NET_IsOffline() 
#endif /* MOZ_OFFLINE */
	    )  /* *X* check for is offline */
		  {
			TRACEMSG(("Non history cgi script found (probably)."
					  " Force reloading it "));
			/* cache testing stuff */
			if(NET_CacheTraceOn)
			  {
				char *buf = 0;
				StrAllocCopy(buf, XP_GetString(XP_CHECKING_SERVER__LASTMOD_MISS));
				StrAllocCat(buf, URL_s->address);
				FE_Alert(window_id, buf);
				FREE(buf);
			  }

	    /* strip the cache file and
	     * memory pointer
	     */
	    FREE_AND_CLEAR(URL_s->cache_file);
	    URL_s->memory_copy = 0;
		URL_s->expires = 0;  /* remove cache reference */
	    cache_method = 0;
		  }
		else
		  {
		    /* the cached file is valid so use it unilaterally
		     */
	    type = cache_method;
		  }
      }
#endif /* MOZILLA_CLIENT */

	/*
	 * If the object should only come out of the cache
	 * we should abort now if there is not a cache object
	 */
    if(output_format & FO_ONLY_FROM_CACHE)
      {
		TRACEMSG(("object called with ONLY_FROM_CACHE format out"));

		if(!cache_method)
		  {
			net_CallExitRoutine(exit_routine,
								URL_s,
								MK_OBJECT_NOT_IN_CACHE,
								output_format,
								window_id);
			net_CheckForWaitingURL(window_id, type, load_background);

			LIBNET_UNLOCK_AND_RETURN(MK_OBJECT_NOT_IN_CACHE);
		  }
		else
		  {
			/* remove the ONLY_FROM_CACHE designation and replace
			 * it with CACHE_AND.. so that we go through the cacheing
			 * module again so that we can memory cache disk objects
			 * and do the right thing in general
			 */
			output_format = CLEAR_PRESENT_BIT(output_format,FO_ONLY_FROM_CACHE);
			output_format = SET_PRESENT_BIT(output_format,FO_CACHE_ONLY);
		  }
      }

    /* put a limit on the total number of open connections
     */
	if(!net_is_one_url_allowed_to_run(window_id, type))
	  {
		NET_TotalNumberOfProcessingURLs--; /* waiting not processing */
		LIBNET_UNLOCK_AND_RETURN(net_push_url_on_wait_queue(type,
															URL_s,
															output_format,
															window_id,
															exit_routine));
	  }

    /* start a new entry */
    this_entry = XP_NEW(ActiveEntry);
    if(!this_entry)
	  {
	net_CallExitRoutine(exit_routine,
							URL_s,
							MK_OUT_OF_MEMORY,
							output_format,
							window_id);

		LIBNET_UNLOCK_AND_RETURN(MK_OUT_OF_MEMORY);
	  }

    /* init new entry */
    XP_MEMSET(this_entry, 0, sizeof(ActiveEntry));
    this_entry->URL_s        = URL_s;
    this_entry->socket       = NULL;
    this_entry->con_sock     = NULL;
    this_entry->exit_routine = exit_routine;
    this_entry->window_id    = window_id;
	this_entry->protocol     = type;

    /* set the format out for the entry
     */
    this_entry->format_out = output_format;

	/* set it busy */
	this_entry->busy = TRUE;

	/* add it to the processing list now
	 */
    XP_ListAddObjectToEnd(net_EntryList, this_entry);

	/* this will protect against multiple posts unknown to the
	 * user
	 *
	 * check if the method is post and it came from the
	 * history and it isn't coming from the cache
	 */
	if(URL_s->method == URL_POST_METHOD
		&& !URL_s->expires
		 && !cache_method
		  && URL_s->history_num)
	  {
		if(URL_s->force_reload != NET_DONT_RELOAD)
		  {
			if(!FE_Confirm(window_id, XP_GetString(XP_CONFIRM_REPOST_FORMDATA)))
			  {
				XP_ListRemoveObject(net_EntryList, this_entry);
				net_CallExitRoutine(exit_routine,
									URL_s,
									MK_INTERRUPTED,
									output_format,
									window_id);
				/* will get decremented */
				net_CheckForWaitingURL(window_id, this_entry->protocol, 
									   load_background);

				XP_FREE(this_entry);  /* not needed any more */

				LIBNET_UNLOCK_AND_RETURN(MK_INTERRUPTED);
			  }
			/* otherwise fall through and repost
			 */
		  }
		else
		  {
			NET_StreamClass * stream;
			char buffer[1000];

			StrAllocCopy(URL_s->content_type, TEXT_HTML);
			stream =  NET_StreamBuilder(CLEAR_CACHE_BIT(output_format),
										URL_s,
										window_id);
			if(stream)
			  {
				XP_STRCPY(buffer, XP_GetString(XP_HTML_MISSING_REPLYDATA));
				(*stream->put_block)(stream,
									 buffer,
									 XP_STRLEN(buffer));
				(*stream->complete)(stream);
			  }

			XP_ListRemoveObject(net_EntryList, this_entry);

			net_CallExitRoutine(exit_routine,
								URL_s,
								MK_INTERRUPTED,
								output_format,
								window_id);
			net_CheckForWaitingURL(window_id, this_entry->protocol,
								   load_background);

			XP_FREE(this_entry);  /* not needed any more */

			LIBNET_UNLOCK_AND_RETURN(MK_INTERRUPTED);
		  }
	  }

redo_load_switch:   /* come here on file/ftp retry */

	/* This code path handles the case when a pac/global file has been loaded
	 * and we now want to use it in for a request. If we find a proxy/socks
	 * server to use, we call NET_HTTPLoad here, skipping the below switch.
	 * See mkautocf.c for more info. */
	pacf_status = TRUE;
	if ((NET_ProxyAcLoaded || NET_GlobalAcLoaded) 
		&& (this_entry->protocol == HTTP_TYPE_URL
			|| this_entry->protocol == SECURE_HTTP_TYPE_URL
			|| this_entry->protocol == GOPHER_TYPE_URL
			|| this_entry->protocol == FTP_TYPE_URL
			|| this_entry->protocol == WAIS_TYPE_URL
			|| this_entry->protocol == URN_TYPE_URL
			|| this_entry->protocol == NFS_TYPE_URL
			|| (this_entry->protocol == NEWS_TYPE_URL 
				&& !strncasecomp(URL_s->address, "snews:", 6)
				)
			)
		&& (this_entry->proxy_conf =
		     pacf_find_proxies_for_url(window_id, URL_s)) != NULL
		&& (pacf_status = pacf_get_proxy_addr(window_id,
							 this_entry->proxy_conf,
							 &this_entry->proxy_addr,
							 &this_entry->socks_host,
							 &this_entry->socks_port))
		&& this_entry->proxy_addr
		)
	  {
		TRACEMSG(("PAC returned \"%s\" for \"%s\".", this_entry->proxy_conf, URL_s->address));
		/* Secure protocols need to be kept in their own protocol
		 * to make SSL tunneling happen. */
		if (this_entry->protocol != SECURE_HTTP_TYPE_URL 
			&& this_entry->protocol != NEWS_TYPE_URL) {
				this_entry->protocol = HTTP_TYPE_URL;
		}

		/* Everything else except SNEWS gets loaded by HTTP loader,
		 * including HTTPS. */
		if (this_entry->protocol != NEWS_TYPE_URL) {
			this_entry->proto_impl = net_get_protocol_impl(HTTP_TYPE_URL);
		} else {
			this_entry->proto_impl = net_get_protocol_impl(NEWS_TYPE_URL);
		}

		if(this_entry->proto_impl)
			status = (*this_entry->proto_impl->init)(this_entry);
	  }
	else if ( pacf_status == FALSE && NET_GetNoProxyFailover() == TRUE ) 
	  {
		status = MK_UNABLE_TO_LOCATE_PROXY;
		FE_Alert(window_id, XP_GetString(XP_BAD_AUTOCONFIG_NO_FAILOVER));
      } 
	else
	  {
		  char *proxy_address = NET_FindProxyHostForUrl(type, this_entry->URL_s->address);
		
  		  if(proxy_address)
			{
				this_entry->protocol = HTTP_TYPE_URL;
				this_entry->proxy_addr = proxy_address;
			}
			
		  this_entry->proto_impl = net_get_protocol_impl(this_entry->protocol);

		  if(this_entry->proto_impl)
		  {
			status = (*this_entry->proto_impl->init)(this_entry);
		  }
		  else
		  {
				/* bad url */
				URL_s->error_msg =
					NET_ExplainErrorDetails(MK_MALFORMED_URL_ERROR,
											this_entry->URL_s->address);
				status = MK_MALFORMED_URL_ERROR;
				this_entry->status = MK_MALFORMED_URL_ERROR;
		  }
	}

	/* the entry is no longer busy
	 */
	this_entry->busy = FALSE;

#ifdef MOZILLA_CLIENT
    /* OH NASTY A GOTO!
     * If we went into load file and what we really wanted to do
     * was use FTP, LoadFile will return MK_USE_FTP_INSTEAD so
     * we will go back up to the beginning of the switch and
     * use ftp instead
     */
    if(status == MK_USE_FTP_INSTEAD)
      {
		type = FTP_TYPE_URL;
	    this_entry->protocol = type; /* change protocol designator */
		this_entry->status = 0; /* reset */
		goto redo_load_switch;
      }
	else 
#endif /* MOZILLA_CLIENT */
	if(this_entry->status == MK_USE_COPY_FROM_CACHE)
	  {
	    TRACEMSG(("304 Not modified recieved using cached entry\n"));

#ifdef MOZILLA_CLIENT
	    NET_RefreshCacheFileExpiration(this_entry->URL_s);
#endif /* MOZILLA_CLIENT */

		/* turn off force reload by telling it to use local copy
		 */
		this_entry->URL_s->use_local_copy = TRUE;

	    /* restart the transfer
	     */
		XP_ListRemoveObject(net_EntryList, this_entry);
	    status = NET_GetURL(this_entry->URL_s,
				   this_entry->format_out,
				   this_entry->window_id,
				   this_entry->exit_routine);

		net_CheckForWaitingURL(this_entry->window_id,
							   this_entry->protocol,
							   this_entry->URL_s->load_background);

		XP_FREE(this_entry);
		LIBNET_UNLOCK_AND_RETURN(0);
	  }
	else if(this_entry->status == MK_DO_REDIRECT)
	  {
		/* this redirect should just call GetURL again
		 */
		int status;
		XP_ListRemoveObject(net_EntryList, this_entry);
		status =NET_GetURL(this_entry->URL_s, 
						   this_entry->format_out,
						   this_entry->window_id,
						   this_entry->exit_routine);
		net_CheckForWaitingURL(this_entry->window_id, 
							   this_entry->protocol,
							   load_background);
		XP_FREE(this_entry);  /* not needed any more */
		LIBNET_UNLOCK_AND_RETURN(0);
	  }
    else if(this_entry->status == MK_TOO_MANY_OPEN_FILES)
      {
		/* Queue this URL so it gets tried again
		 */
		XP_ListRemoveObject(net_EntryList, this_entry);
		status = net_push_url_on_wait_queue(
						NET_URL_Type(this_entry->URL_s->address),
						this_entry->URL_s,
						this_entry->format_out,
						this_entry->window_id,
						this_entry->exit_routine);
		NET_TotalNumberOfProcessingURLs--;
		XP_FREE(this_entry);
		LIBNET_UNLOCK_AND_RETURN(status);
      }
    else if(this_entry->status == MK_OFFLINE)
      {
		/*  Stop the stars and put up a nice message
		 */
		XP_ListRemoveObject(net_EntryList, this_entry);
		{
			if (window_id->type != MWContextMail && window_id->type != MWContextMailMsg &&
				(CLEAR_CACHE_BIT(output_format) != FO_INTERNAL_IMAGE) &&
				(CLEAR_CACHE_BIT(output_format) != FO_EMBED) &&
				(CLEAR_CACHE_BIT(output_format) != FO_PLUGIN) &&
				(CLEAR_CACHE_BIT(output_format) != FO_DO_JAVA))
			{
				/* We only display the message for top-level items (i.e., not for
				   inline images, plugins, or java classes */
				   
				char * alert = XP_STRDUP(XP_GetString(XP_ALERT_OFFLINE_MODE_SELECTED));
				FE_Alert(window_id, alert);
				FREE(alert);
			}
		}
		
		net_CallExitRoutine(this_entry->exit_routine,
							this_entry->URL_s,
							this_entry->status,
							this_entry->format_out,
							this_entry->window_id);


		/* Check for WaitingURL decrements TotalNumberofProcessingURLS */
		net_CheckForWaitingURL(this_entry->window_id, 
							   this_entry->protocol,
							   load_background);

		XP_FREE(this_entry);
		LIBNET_UNLOCK_AND_RETURN(MK_OFFLINE);
      }

    if(status < 0)  /* check for finished state */
      {

        /* XP_OS2_FIX IBM-MAS: limit length of output string to keep from blowing trace buffer */
      TRACEMSG(("End of transfer, entry (soc=%d, con=%d) being removed from list with %d status: %-.1900s",
             this_entry->socket, this_entry->con_sock, this_entry->status,
             this_entry->URL_s->address));

		XP_ListRemoveObject(net_EntryList, this_entry);

		net_CallExitRoutine(this_entry->exit_routine,
							this_entry->URL_s,
							this_entry->status,
							this_entry->format_out,
							this_entry->window_id);
		net_CheckForWaitingURL(this_entry->window_id, 
							   this_entry->protocol,
							   load_background);
		XP_FREE(this_entry);  /* not needed any more */
      }

    TRACEMSG(("Leaving GetURL with %d items in list",
			  XP_ListCount(net_EntryList)));

	/* XXX - hack for chromeless windows - jsw 10/27/95 */
	LIBNET_UNLOCK();
	if ( processcallbacks ) {
		NET_ProcessExitCallbacks();
	}
	
	return status;

#ifdef NOTDEF /* this is the real code here */
    LIBNET_UNLOCK_AND_RETURN(status);
#endif
}

/* process_net is called from the client's main event loop and
 * causes connections to be read and processed.  Multiple
 * connections can be processed simultaneously.
 */
PUBLIC int NET_ProcessNet (PRFileDesc *ready_fd,  int fd_type)
{
    ActiveEntry * tmpEntry;
    XP_List * list_item;
    int rv= -1;
	Bool load_background;

#ifdef XP_OS2_FIX
   /* assume no local files in net_EntryList.
      see "Thrash Optomization", below */
   int localfiles = 0;
#endif

	TRACEMSG(("Entering ProcessNet!  ready_fd: %d", ready_fd));
	LIBNET_LOCK();

	if(XP_ListIsEmpty(net_EntryList))
	  {
		TRACEMSG(("Invalid call to NET_ProcessNet with fd: %d - No active entries\n", ready_fd));

#ifdef MOZILLA_CLIENT
		if (fd_type == NET_EVERYTIME_TYPE)
		  {
			MWContext *c = XP_FindContextOfType(0, MWContextBrowser);
			if (!c) c = XP_FindContextOfType(0, MWContextMail);
			if (!c) c = XP_FindContextOfType(0, MWContextNews);
			if (!c) c = XP_FindContextOfType(0, MWContextMessageComposition);
			if (!c) c = XP_FindContextOfType(0, MWContextMailMsg);
			if (!c) c = XP_FindContextOfType(0, MWContextPane);

			if (c)
			{
			  if(NET_IsCallNetlibAllTheTimeSet(c, NULL))
			  {	
				NET_ClearCallNetlibAllTheTime(c, "mkgeturl");
			  }
			  NET_SetNetlibSlowKickTimer(FALSE);
			}
		  }
#endif /* MOZILLA_CLIENT */

	LIBNET_UNLOCK_AND_RETURN(0); /* no entries to process */
	  }

	if(NET_InGetHostByName)
	  {
		TRACEMSG(("call to processnet while doing gethostbyname call"));
		XP_ASSERT(0);
		LIBNET_UNLOCK_AND_RETURN(1);
	  }

    /*
     * if -1 is passed into ProcessNet use select to
     * figure out if a socket is ready
     */
    if(ready_fd == NULL)
	  {
		/* try and find a socket ready for reading
		 */
#define MAX_SIMULTANIOUS_SOCKETS 100
		/* should never have more than MAX sockets */
		PRPollDesc poll_desc_array[MAX_SIMULTANIOUS_SOCKETS];
		unsigned int fd_set_size=0;

		/* reorder the list so that we get a round robin effect */
		XP_ListMoveTopToBottom(net_EntryList);

		fd_type = NET_SOCKET_FD;

		/* process one socket ready for reading */
		list_item = net_EntryList;
		while((tmpEntry = (ActiveEntry *) XP_ListNextObject(list_item)) != 0)
		  {

			if(tmpEntry->busy)
				continue;

			if(!tmpEntry->local_file && !tmpEntry->memory_file)
			  {
				if (tmpEntry->socket != NULL)
				  {

					if(tmpEntry->con_sock)
					{
						poll_desc_array[fd_set_size].fd = tmpEntry->con_sock;
						poll_desc_array[fd_set_size].in_flags = PR_POLL_READ | PR_POLL_EXCEPT | PR_POLL_WRITE; 
					}
					else
					{
						poll_desc_array[fd_set_size].fd = tmpEntry->socket;
						poll_desc_array[fd_set_size].in_flags = PR_POLL_READ | PR_POLL_EXCEPT; 
					}

					fd_set_size++;
					XP_ASSERT(fd_set_size < MAX_SIMULTANIOUS_SOCKETS);
				  }
			  }
			else if(tmpEntry == 
					(ActiveEntry *) XP_ListGetObjectNum(net_EntryList, 1))
			  {
				/* if this is the very first object in the list
				 * and it's a local file or a memory cache copy
				 * then use this one entry and skip the select call
				 * we won't deadlock because we reorder the list
				 * with every call to net process net.
				 */
				fd_type = NET_LOCAL_FILE_FD;
				ready_fd = tmpEntry->socket;  /* it's actually going to be NULL in this case */

				/* call yeild to let other thread do something */
				PR_Sleep(PR_INTERVAL_NO_WAIT);

				break;  /* exit while */
			  }
		  }

		if(fd_type == NET_SOCKET_FD) /* in case we set it for the local file type above */
		  {
			int count=0;
			int ret;
			
#ifndef NSPR20_DISABLED
			PR_Sleep(PR_INTERVAL_NO_WAIT); /* thread yeild */
#endif

			ret = PR_Poll(poll_desc_array,
							 fd_set_size,
							 0);

		    if(ret < 1)
			  {

				TRACEMSG(("Select returned no active sockets! "
						  "WASTED CALL TO PROCESS NET"));
				LIBNET_UNLOCK_AND_RETURN(XP_ListIsEmpty(net_EntryList) ? 0 : 1);
			  }

			/* process one socket ready for reading,
			 * find the first one ready
			 */
			list_item = net_EntryList;
			while((tmpEntry=(ActiveEntry *) XP_ListNextObject(list_item)) != 0)
			  {

				if(tmpEntry->busy)
					continue;

				if(!tmpEntry->local_file && !tmpEntry->memory_file)
				  {
					/* count should line up to the appropriate socket since
					 * it was added in the same order
					 */
					XP_ASSERT(poll_desc_array[count].fd == tmpEntry->socket
							  || poll_desc_array[count].fd == tmpEntry->con_sock);
					if(poll_desc_array[count].out_flags & (PR_POLL_READ | PR_POLL_WRITE | PR_POLL_EXCEPT))
					  {
						ready_fd = tmpEntry->socket;
						break;
					  }
					count++;
				  }
			  }

			if(!ready_fd)
			{
				/* couldn't find the active socket.  Shouldn't ever happen */
				XP_ASSERT(0);
				LIBNET_UNLOCK_AND_RETURN(XP_ListIsEmpty(net_EntryList) ? 0 : 1);
			}

		  }
      }

    list_item = net_EntryList;

    /* process one socket ready for reading
     *
	 * find the ready socket in the active entry list
     */
    while((tmpEntry = (ActiveEntry *) XP_ListNextObject(list_item)) != 0)
      {
		TRACEMSG(("Found item in Active Entry List. sock #%d  con_sock #%d",
			    tmpEntry->socket, tmpEntry->con_sock));

		if(tmpEntry->busy)
		{
			/* I guess this is alright since one of the streams
			 * (Play audio for instance) may put up a modal dialog
			 * box which causes the main event loop to get called
			 * from within the stream
			 *
			 * FE_Alert(tmpEntry->window_id, "reentrant call to Process Net");
			 */
		}
			/* this will activate local and memory files as well since the ready_fd
			 * will be zero and will match the NULL socket ID.
			 *
			 * We won't have starvation because the list is reordered every time
			 */
		else if(ready_fd == tmpEntry->socket
				|| ready_fd == tmpEntry->con_sock)
	    {

			tmpEntry->busy = TRUE;

		 	TRACEMSG(("Item has data ready for read"));

			rv = (*tmpEntry->proto_impl->process)(tmpEntry);

			tmpEntry->busy = FALSE;
		
	    	/* check for done status on transfer and call
	     	* exit routine if done.
	     	*/
	    	if(rv < 0)
	      	{

				XP_ListRemoveObject(net_EntryList, tmpEntry);

				if(tmpEntry->status == MK_USE_COPY_FROM_CACHE)
		  		{
		    		TRACEMSG(("304 Not modified recieved using cached entry\n"));
	#ifdef MOZILLA_CLIENT
		    		NET_RefreshCacheFileExpiration(tmpEntry->URL_s);
	#endif /* MOZILLA_CLIENT */

					/* turn off force reload by telling it to use local copy
					 */
					tmpEntry->URL_s->use_local_copy = TRUE;

		    		/* restart the transfer
		     		*/
		    		NET_GetURL(tmpEntry->URL_s,
								   tmpEntry->format_out,
								   tmpEntry->window_id,
								   tmpEntry->exit_routine);

					net_CheckForWaitingURL(tmpEntry->window_id,
											   tmpEntry->protocol,
											   tmpEntry->URL_s->load_background);

		  		}
				else if(tmpEntry->status == MK_DO_REDIRECT)
		  		{
		    		TRACEMSG(("Doing redirect part in ProcessNet"));

		    		/* restart the whole transfer */
		    		NET_GetURL(tmpEntry->URL_s,
				   		tmpEntry->format_out,
				   		tmpEntry->window_id,
				   		tmpEntry->exit_routine);

					net_CheckForWaitingURL(tmpEntry->window_id,
											   tmpEntry->protocol,
											   tmpEntry->URL_s->load_background);                  
				}
				else if(tmpEntry->status == MK_TOO_MANY_OPEN_FILES)
				{
					/* Queue this URL so it gets tried again
					 */
					LIBNET_UNLOCK_AND_RETURN(net_push_url_on_wait_queue(
						NET_URL_Type(tmpEntry->URL_s->address),
						tmpEntry->URL_s,
						tmpEntry->format_out,
						tmpEntry->window_id,
						tmpEntry->exit_routine));
				}
				else if(tmpEntry->status < 0
							&& !tmpEntry->URL_s->use_local_copy
							&& XP_GetError() != SSL_ERROR_BAD_CERTIFICATE
						&& (tmpEntry->status == MK_CONNECTION_REFUSED
			    			|| tmpEntry->status == MK_CONNECTION_TIMED_OUT
			    			|| tmpEntry->status == MK_UNABLE_TO_CREATE_SOCKET
			    			|| tmpEntry->status == MK_UNABLE_TO_LOCATE_HOST
			    			|| tmpEntry->status == MK_UNABLE_TO_CONNECT)
						&& (NET_IsURLInDiskCache(tmpEntry->URL_s)
							|| NET_IsURLInMemCache(tmpEntry->URL_s)))
				{
					/* if the status is negative something went wrong
					 * with the load.  If last_modified is set
					 * then we probably have a cache file,
					 * but it might be a broken image so
					 * make sure "use_local_copy" is not
					 * set.
					 *
					 * Only do this when we can't connect to the
					 * server.
					 */

					/* if we had a cache file and got a load
		     		 * error, go ahead and use it anyways
		     		 */

		    		/* turn off force reload by telling it to use local copy
		     		 */
		    		tmpEntry->URL_s->use_local_copy = TRUE;

					if(CLEAR_CACHE_BIT(tmpEntry->format_out) == FO_PRESENT)
					  {
						StrAllocCat(tmpEntry->URL_s->error_msg,
						XP_GetString( XP_USING_PREVIOUSLY_CACHED_COPY_INSTEAD ) );

						FE_Alert(tmpEntry->window_id,
								 tmpEntry->URL_s->error_msg);
					  }

					FREE_AND_CLEAR(tmpEntry->URL_s->error_msg);

		    		/* restart the transfer
		     		 */
		    		NET_GetURL(tmpEntry->URL_s,
			       			tmpEntry->format_out,
			       			tmpEntry->window_id,
			       			tmpEntry->exit_routine);

		    		net_CheckForWaitingURL(tmpEntry->window_id,
					   						tmpEntry->protocol,
											tmpEntry->URL_s->load_background);
				}
				else
				{
					/* XP_OS2_FIX IBM-MAS: limit size of URL string to 100 to keep from blowing trace message buffer! */
					TRACEMSG(("End of transfer, entry (soc=%d, con=%d) being removed from list with %d status: %-.1900s",
							tmpEntry->socket, tmpEntry->con_sock, tmpEntry->status,
							(tmpEntry->URL_s->address ? tmpEntry->URL_s->address : "")));

					/* catch out of memory errors at the lowest
					 * level since we don't do it at all the out
					 * of memory condition spots
					 */
		    		if(tmpEntry->status == MK_OUT_OF_MEMORY
							&& !tmpEntry->URL_s->error_msg)
		      		{
						tmpEntry->URL_s->error_msg =
							NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
		      		}

					load_background = tmpEntry->URL_s->load_background;
					/* run the exit routine
					 */
		    		net_CallExitRoutine(tmpEntry->exit_routine,
											tmpEntry->URL_s,
											tmpEntry->status,
											tmpEntry->format_out,
											tmpEntry->window_id);

					net_CheckForWaitingURL(tmpEntry->window_id,
										   tmpEntry->protocol,
										   load_background);

	#ifdef MILAN
					/* if the error was caused by a NETWORK DOWN
					 * then interrupt the window.  This
					 * could still be a problem since another
					 * window may be active, but it should get
					 * the same error
					 */
					if(SOCKET_ERRNO == XP_ERRNO_ENETDOWN)
					{
						NET_SilentInterruptWindow(tmpEntry->window_id);
					}
	#endif /* MILAN */
		  		}

				XP_FREE(tmpEntry);  /* free the now non active entry */

			} /* end if  rv < 0 */

			TRACEMSG(("Leaving process net with %d items in list",
						  XP_ListCount(net_EntryList)));

			LIBNET_UNLOCK_AND_RETURN(XP_ListIsEmpty(net_EntryList) ? 0 : 1); /* all done */

		  } /* end if */

      } /* end while */


	/* the active socket wasn't found in the list :(
     */
	TRACEMSG(("Invalid call to NET_ProcessNet: Active item with passed in fd: %d not found\n", ready_fd));

    LIBNET_UNLOCK_AND_RETURN(XP_ListIsEmpty(net_EntryList) ? 0 : 1);
}

/*
 *      net_InterruptActiveStream
 *      Kills a single stream given its ActiveEntry*
 *      *       FrontEndCancel -> NetInterruptWindow -> net_InterruptActiveStream
 *      *       LayoutNewDoc -> KillOtherLayouts -> net_InterruptActiveStream
 *
 *      Also removes the entry from the active list and frees it.
 *      Also allows waiting urls into the active list.
 */
PRIVATE int
net_InterruptActiveStream (ActiveEntry *entry)
{
	TRACEMSG(("Terminating transfer on port #%d, proto: %d",
								entry->socket, entry->protocol));

	/* remove it from the active list first to prevent
	 * reentrant problem
	 */
	XP_ListRemoveObject(net_EntryList, entry);

	if(entry->proto_impl)
	{
		(*entry->proto_impl->interrupt)(entry);
	}
	else
	{
		XP_ASSERT(0);
	}

    /* XP_OS2_FIX IBM-MAS: limit length of output string to keep from blowing trace buffer */
   TRACEMSG(("End of transfer, entry (soc=%d, con=%d) being removed from list with %d status: %-.1900s",
           entry->socket, entry->con_sock, entry->status, entry->URL_s->address));

	/* call exit routine since we know we are done */
	net_CallExitRoutine(entry->exit_routine,
						entry->URL_s,
						entry->status,
						entry->format_out,
						entry->window_id);

	/* decrement here since we don't call checkForWaitingURL's
	 */
	NET_TotalNumberOfProcessingURLs--;

	if(!NET_AreThereActiveConnectionsForWindow(entry->window_id))
		FE_AllConnectionsComplete(entry->window_id);

	/* free the no longer active entry */
	XP_FREE(entry);

	return 0;
}

/* use a URL struct to find an active entry
 * The netlib lock must be held prior to calling this
 * returns NULL if not found
 */
PRIVATE ActiveEntry *
net_find_ac_from_url(URL_Struct *nurl)
{
    XP_List * iter = net_EntryList;
	ActiveEntry *tmpEntry = NULL, *rv = NULL;

    while ((tmpEntry = (ActiveEntry *) XP_ListNextObject(iter)) != NULL) 
	  {
		if (tmpEntry->URL_s == nurl) 
		{
			rv = tmpEntry;
			break;
		}
      }

	return(rv);
}

PUBLIC Bool
NET_SetActiveEntryBusyStatus(URL_Struct *nurl, Bool set_busy)
{
	ActiveEntry *entry = NULL;

	entry = net_find_ac_from_url(nurl);

	if(entry)
		entry->busy = set_busy;

	return (entry != 0);
}

/*
 *      NET_InterruptStream kills just stream associated with an URL.
 */
PUBLIC int
NET_InterruptStream (URL_Struct *nurl)
{
	/* Find the ActiveEntry structure for this URL */
	ActiveEntry *entryToKill = NULL;
	int status;
	
	TRACEMSG(("Entering NET_InterruptStream"));
	LIBNET_LOCK();

	if(NET_InGetHostByName)
	  {
		TRACEMSG(("call to InterrruptStream while doing gethostbyname call"));
		LIBNET_UNLOCK_AND_RETURN(1);
	  }

	entryToKill = net_find_ac_from_url(nurl);

/*    assert (entryToKill);*/
    /* Kill it & free it */
	if (entryToKill)
	    status = net_InterruptActiveStream (entryToKill);
	else
	    status = -1;

	LIBNET_UNLOCK_AND_RETURN(status);
}


/* interrupt a LoadURL action by using a socket number as
 * an index.  NOTE: that this cannot interrupt non-socket
 * based operations such as file or memory cache loads
 *
 * returns -1 on error.
 */
PUBLIC int
NET_InterruptSocket (PRFileDesc *socket)
{
    /* Find the ActiveEntry structure for this URL */
    ActiveEntry *tmpEntry = NULL, *entryToKill = NULL;
    XP_List * iter;
    int status;

    TRACEMSG(("Entering NET_InterruptSocket"));
    LIBNET_LOCK();
    iter = net_EntryList;

    if(NET_InGetHostByName)
      {
	TRACEMSG(("call to InterrruptStream while doing gethostbyname call"));
	LIBNET_UNLOCK_AND_RETURN(1);
      }

    while ((tmpEntry = (ActiveEntry *) XP_ListNextObject(iter)) != NULL) {
	if (tmpEntry->con_sock == socket || tmpEntry->socket == socket) {
	    entryToKill = tmpEntry;
	    break;
	}
    }

	if (entryToKill)
	    status = net_InterruptActiveStream (entryToKill);
	else
	    status = -1;

	LIBNET_UNLOCK_AND_RETURN(status);
}

/*
 *  NET_SetNewContext changes the context associated with a URL Struct.
 *      can also change the url exit routine, which causes the old one to be
 *      called with a status of MK_CHANGING_CONTEXT
 */
PUBLIC int
NET_SetNewContext(URL_Struct *URL_s, MWContext * new_context, Net_GetUrlExitFunc *exit_routine)
{
    /* Find the ActiveEntry structure for this URL */
    ActiveEntry *tmpEntry = NULL;
    XP_List * iter;
	MWContext *old_window_id;

	LIBNET_LOCK();
	iter = net_EntryList;

    while ((tmpEntry = (ActiveEntry *) XP_ListNextObject(iter)) != NULL)
	  {
	if (tmpEntry->URL_s == URL_s)
		  {
			/* call the old exit routine now with the old context */
			/* call with MK_CHANGING_CONTEXT, FE shouldn't free URL_s */
			net_CallExitRoutine(tmpEntry->exit_routine,
								URL_s,
								MK_CHANGING_CONTEXT,
								0,
								tmpEntry->window_id);

			/*      see if we're changing exit routines */
			if(exit_routine != NULL)        {
				/*      okay to change now. */
				tmpEntry->exit_routine = exit_routine;
			}
			old_window_id = tmpEntry->window_id;
	    tmpEntry->window_id = new_context;

#ifdef XP_WIN
			/*
			 *      Windows needs a way to know when a URL switches contexts,
			 *              so that it can keep the NCAPI progress messages
			 *              specific to a URL loading and not specific to the
			 *              context attempting to load.
			 *      So sue me.  GAB 10-16-95
			 */
			FE_UrlChangedContext(URL_s, old_window_id, new_context);
#endif /* XP_WIN */

			/*      if there are no more connections in the old context, we
			 *      need to call AllConnectionsComplete
			 */
		if(!NET_AreThereActiveConnectionsForWindow(old_window_id))
				FE_AllConnectionsComplete(old_window_id);

			LIBNET_UNLOCK();
	    return(0);
	  }
      }

	/* couldn't find it :( */
    XP_ASSERT (0);

	LIBNET_UNLOCK();
    return(-1);
}

/* returns true if the stream is safe for setting up a new
 * context and using a separate window.
 *
 * This will return FALSE in multipart/mixed cases where
 * it is impossible to use separate windows for the
 * same stream.
 */
PUBLIC Bool
NET_IsSafeForNewContext(URL_Struct *URL_s)
{
	if(!URL_s || URL_s->is_active)
		return(FALSE);

	return(TRUE);
}


/* NET_InterruptTransfer
 *
 * Interrupts all transfers in progress that have the same window id as
 * the one passed in.
 */
PRIVATE int
net_InternalInterruptWindow(MWContext * window_id, Bool show_warning)
{
    int starting_list_count;
    ActiveEntry * tmpEntry;
    ActiveEntry * tmpEntry2;
    XP_List * list_item;
    int32 cur_win_id = FE_GetContextID(window_id);
	int number_killed=0;
	XP_Bool call_all_connections_complete = TRUE;

	TRACEMSG(("-------Interrupt Transfer called!"));

	LIBNET_LOCK();
	list_item = net_EntryList;
	starting_list_count = XP_ListCount(net_EntryList);

#ifdef DEBUG
	if(NET_InGetHostByName)
	  {
		TRACEMSG(("call to InterrruptWindow while doing gethostbyname call"));
		LIBNET_UNLOCK_AND_RETURN(1);
	  }
#endif /* DEBUG */

	number_killed = net_AbortWaitingURL(window_id,
										FALSE,
										net_waiting_for_actives_url_list);
	number_killed += net_AbortWaitingURL(window_id,
										FALSE,
										net_waiting_for_connection_url_list);

    /* run through the whole list of connections and
     * interrupt any of them that have a matching window
     * id.
     */
	tmpEntry = (ActiveEntry *) XP_ListNextObject(list_item);

    while(tmpEntry)
      {
	/* advance to the next item NOW in case we free this one */
	tmpEntry2 = (ActiveEntry *) XP_ListNextObject(list_item);

	if(FE_GetContextID(tmpEntry->window_id) == cur_win_id)
	  {
		    if(tmpEntry->busy)
		      {
				if(show_warning)
				  {
				TRACEMSG(("AAAACK, reentrant protection kicking in.!!!"));
#ifdef DEBUG
				FE_Alert(tmpEntry->window_id, XP_GetString(XP_ALERT_INTERRUPT_WINDOW));
#endif /* DEBUG */
				  }
		      }
			else
		      {
				FE_EnableClicking(window_id);
				net_InterruptActiveStream (tmpEntry);
				number_killed += 1;

				/* unset call_all_connections_complete here
				 * since InterruptActiveStream will already
				 * call all_connections_complete when it detects
				 * that there are no more active connections
				 * going on for the window.
				 */
				call_all_connections_complete = FALSE;
		      }
	  } /* end if cur_win_id */

		tmpEntry = tmpEntry2;
      } /* end while */

	/* If all_connection_complete was called, FE might have destroyed
	 * the context. So dont call FE_EnableClicking() if
	 * call_all_connections_complete was already called.
	 */
	if (call_all_connections_complete)
		FE_EnableClicking(window_id);

	/* we are sure that all connections are complete
	 * but only call it if we killed one.
	 */
	if(number_killed && call_all_connections_complete)
	FE_AllConnectionsComplete(window_id);


    TRACEMSG(("Leaving Interrupt transfer with %d items in list",
				XP_ListCount(net_EntryList)));

    LIBNET_UNLOCK_AND_RETURN(starting_list_count - XP_ListCount(net_EntryList));

}

/*
 * Interrupts all transfers in progress that have the same window id as
 * the one passed in.
 */
PUBLIC int
NET_InterruptWindow(MWContext * window_id)
{
/* call interruptwindow twice so that things we interrupt that call NET_GetURL
 * will be interrupted as well
 */
	
    int rv = net_InternalInterruptWindow(window_id, TRUE);
    #ifdef XP_MAC
    /* pchen - Fix bug #72831. On Mac, continuously call
     * net_InternalInterruptWindow while
     * NET_AreThereNonBusyActiveConnectionsForWindow returns true
     */
    while(NET_AreThereNonBusyActiveConnectionsForWindow(window_id))
    #endif /* XP_MAC */
    rv += net_InternalInterruptWindow(window_id, TRUE);
    return(rv);
}

/*
 * Silently Interrupts all transfers in progress that have the same
 * window id as the one passed in.
 */
MODULE_PRIVATE int
NET_SilentInterruptWindow(MWContext * window_id)
{
    return(net_InternalInterruptWindow(window_id, FALSE));
}

/* check for any active URL transfers in progress for the given
 * window Id
 *
 * It is possible that there exist URL's on the wait queue that
 * will not be returned by this function.  It is recommended
 * that you call NET_InterruptWindow(MWContext * window_id)
 * when FALSE is returned to make sure that the wait queue is
 * cleared as well.
 * 
 * It is also possible that some URLs will be explicitly marked as
 * "background" URLs that are not identified as active, even though
 * they are.
 */
PUBLIC Bool
NET_AreThereActiveConnectionsForWindow(MWContext * window_id)
{
    ActiveEntry * tmpEntry;
    int32 cur_win_id = FE_GetContextID(window_id);
    XP_List * list_ptr;
    WaitingURLStruct * wus;

	LIBNET_LOCK();

    TRACEMSG(("-------NET_AreThereActiveConnectionsForWindow called!"));

	/* check for connections in the wait queue
	 */
    list_ptr = net_waiting_for_actives_url_list;
    while((wus = (WaitingURLStruct*) XP_ListNextObject(list_ptr)) != NULL)
      {
	if(cur_win_id == FE_GetContextID(wus->window_id) &&
	   !wus->URL_s->load_background)
		  {
			LIBNET_UNLOCK();
			return(TRUE);
		  }
      }

    /* check for connections in the connections wait queue
     */
	list_ptr = net_waiting_for_connection_url_list;
    while((wus = (WaitingURLStruct*) XP_ListNextObject(list_ptr)) != NULL)
      {
	if(cur_win_id == FE_GetContextID(wus->window_id) &&
	   !wus->URL_s->load_background)
	  {
	    LIBNET_UNLOCK();
	    return(TRUE);
	  }
      }

    /* run through the whole list of active connections and
     * return true if any of them have the passed in window id
     */
	list_ptr = net_EntryList;
    while((tmpEntry = (ActiveEntry *) XP_ListNextObject(list_ptr)) != NULL)
      {
	if(cur_win_id == FE_GetContextID(tmpEntry->window_id) &&
	   !tmpEntry->URL_s->load_background)
		  {
			LIBNET_UNLOCK();
	    return(TRUE);
		  }

      } /* end while */

	LIBNET_UNLOCK();
    return(FALSE);
}

#ifdef XP_MAC
/* pchen - Fix bug #72831. Same as NET_AreThereActiveConnectionsForWindow
 * except that it will return false if there is an active connection that
 * is busy.
 */
PUBLIC Bool
NET_AreThereNonBusyActiveConnectionsForWindow(MWContext * window_id)
{
    ActiveEntry * tmpEntry;
    int32 cur_win_id = FE_GetContextID(window_id);
    XP_List * list_ptr;
    WaitingURLStruct * wus;

	LIBNET_LOCK();

    TRACEMSG(("-------NET_AreThereActiveConnectionsForWindow called!"));

	/* check for connections in the wait queue
	 */
    list_ptr = net_waiting_for_actives_url_list;
    while((wus = (WaitingURLStruct*) XP_ListNextObject(list_ptr)) != NULL)
      {
	if(cur_win_id == FE_GetContextID(wus->window_id) &&
	   !wus->URL_s->load_background)
		  {
			LIBNET_UNLOCK();
			return(TRUE);
		  }
      }

    /* check for connections in the connections wait queue
     */
	list_ptr = net_waiting_for_connection_url_list;
    while((wus = (WaitingURLStruct*) XP_ListNextObject(list_ptr)) != NULL)
      {
	if(cur_win_id == FE_GetContextID(wus->window_id) &&
	   !wus->URL_s->load_background)
	  {
	    LIBNET_UNLOCK();
	    return(TRUE);
	  }
      }

    /* run through the whole list of active connections and
     * return true if any of them have the passed in window id
     */
	list_ptr = net_EntryList;
    while((tmpEntry = (ActiveEntry *) XP_ListNextObject(list_ptr)) != NULL)
      {
	if(cur_win_id == FE_GetContextID(tmpEntry->window_id) &&
	   !tmpEntry->URL_s->load_background &&
	   !tmpEntry->busy)
		  {
			LIBNET_UNLOCK();
	    return(TRUE);
		  }

      } /* end while */

	LIBNET_UNLOCK();
    return(FALSE);
}
#endif /* XP_MAC */

/* KM - Are there other ActiveEntry structures being processed with the same
	context? */
/* fix Mac warning for missing prototype */
PUBLIC Bool
NET_AreThereActiveConnectionsForWindowWithOtherActiveEntry(ActiveEntry *thisEntry);

PUBLIC Bool
NET_AreThereActiveConnectionsForWindowWithOtherActiveEntry(ActiveEntry *thisEntry)
{
    ActiveEntry * tmpEntry;
    int32 cur_win_id = FE_GetContextID(thisEntry->window_id);
    XP_List * list_ptr;

	LIBNET_LOCK();
	
    /* run through the whole list of active connections and
     * return true if any of them have the passed in window id
     * with a different entry
     */
	list_ptr = net_EntryList;
    while((tmpEntry = (ActiveEntry *) XP_ListNextObject(list_ptr)) != NULL)
      {
	if((cur_win_id == FE_GetContextID(tmpEntry->window_id)) &&
	   (tmpEntry != thisEntry))
		  {
			LIBNET_UNLOCK();
	    return(TRUE);
		  }

      } /* end while */

	LIBNET_UNLOCK();
    return(FALSE);
}


/*
 *  GAB 04-23-96
 *  We're interested in knowing if there are any operations which
 *      will cause activity of the said type, in the said context.
 *  This is in the interests of PE, where they desire a world of
 *      dial on demand (hanging up the phone when we're not doing anything
 *      on the network).
 *
 *  Args:
 *      window_id   The said context to check.
 *                  If NULL, check them all (disregard).
 *      waiting     Include waiting queues in check if TRUE.
 *                  Note that waiting queues do not know thier type
 *                      yet (local, cache, or net), thus type can not
 *                      be correctly checked.  I can only assume that
 *                      we have the technology to mostly decide this
 *                      before the fact, but I see no facility available.
 *      background  Include background activity in the check if TRUE.
 *  Returns:
 *      Bool        TRUE, there is chance of activity of said type.
 *                  FALSE, that particular type of activity is not present.
 *
 */
PUBLIC Bool
NET_HasNetworkActivity(MWContext * window_id, Bool waiting, Bool background)
{
    ActiveEntry * tmpEntry;
    XP_List * list_ptr;
    WaitingURLStruct * wus;

	LIBNET_LOCK();

    TRACEMSG(("-------NET_HasNetworkActivity called!"));

	/* check for connections in the wait queue
	 */
    if(waiting)
      {
	list_ptr = net_waiting_for_actives_url_list;
	while((wus = (WaitingURLStruct*) XP_ListNextObject(list_ptr)) != NULL)
	  {
	    if((window_id == NULL ||
		FE_GetContextID(window_id) ==
		FE_GetContextID(wus->window_id)) &&
		(background || !wus->URL_s->load_background))
		      {
			    LIBNET_UNLOCK();
			    return(TRUE);
		      }
	  }

	/* check for connections in the connections wait queue
	 */
	    list_ptr = net_waiting_for_connection_url_list;
	while((wus = (WaitingURLStruct*) XP_ListNextObject(list_ptr)) != NULL)
	  {
	    if((window_id == NULL ||
		FE_GetContextID(window_id) ==
		FE_GetContextID(wus->window_id)) &&
		(background || !wus->URL_s->load_background))
	      {
		LIBNET_UNLOCK();
		return(TRUE);
	      }
	  }
      }

    /* run through the whole list of active connections
     */
	list_ptr = net_EntryList;
    while((tmpEntry = (ActiveEntry *) XP_ListNextObject(list_ptr)) != NULL)
      {
	if((window_id == NULL ||
	    FE_GetContextID(window_id) ==
	    FE_GetContextID(tmpEntry->window_id)) &&
	    (background || !tmpEntry->URL_s->load_background) &&
	    !tmpEntry->memory_file &&
	    !tmpEntry->local_file)
		  {
			LIBNET_UNLOCK();
	    return(TRUE);
		  }

      } /* end while */

	LIBNET_UNLOCK();
    return(FALSE);
}


/* malloc, init, and set the URL structure used
 * for the network library
 */
PUBLIC URL_Struct * 
NET_CreateURLStruct (CONST char *url, NET_ReloadMethod force_reload)
{
    uint32 all_headers_size;
    URL_Struct * URL_s  = XP_NEW(URL_Struct);

    if(!URL_s)
	  {
		return NULL;
	  }

    /* zap the whole structure */
    XP_MEMSET (URL_s, 0, sizeof (URL_Struct));

    URL_s->SARCache = NULL;

    /* we haven't modified the address at all (since we are just creating it)*/
    URL_s->address_modified=NO;

	URL_s->method = URL_GET_METHOD;

    URL_s->force_reload = force_reload;

	URL_s->load_background = FALSE;

    URL_s->ref_count = 1;

    /* Allocate space for pointers to hold message (http, news etc) headers */
    all_headers_size = 
	INITIAL_MAX_ALL_HEADERS * sizeof (URL_s->all_headers.key[0]);
    URL_s->all_headers.key = (char **) XP_ALLOC(all_headers_size);
    if(!URL_s->all_headers.key)
      {
	NET_FreeURLStruct(URL_s);
	return NULL;
      }
    XP_MEMSET (URL_s->all_headers.key, 0, all_headers_size);

    all_headers_size = 
	INITIAL_MAX_ALL_HEADERS * sizeof (URL_s->all_headers.value[0]);
    URL_s->all_headers.value = (char **) XP_ALLOC(all_headers_size);
    if(!URL_s->all_headers.value)
      {
	NET_FreeURLStruct(URL_s);
	return NULL;
      }
    XP_MEMSET (URL_s->all_headers.value, 0, all_headers_size);

    URL_s->all_headers.max_index = INITIAL_MAX_ALL_HEADERS;

	URL_s->owner_data = NULL;
	URL_s->owner_id   = 0;

    /* set the passed in value */
    StrAllocCopy(URL_s->address, url);
	if (!URL_s->address) {
		/* out of memory, clean up previously allocated objects */
		NET_FreeURLStruct(URL_s);
		URL_s = NULL;
	}

    return(URL_s);
}

/* Increase the size of AllHeaders in URL_struct
 */
PRIVATE Bool 
net_EnlargeURLAllHeaders (URL_Struct * URL_s)
{
    uint32 number_of_entries = 
	URL_s->all_headers.empty_index * MULT_ALL_HEADER_COUNT;
    uint32 realloc_size = 
	number_of_entries * sizeof (URL_s->all_headers.key[0]);
    char **temp;

    if (number_of_entries < MAX_ALL_HEADER_COUNT) {

	temp = (char **) XP_REALLOC(URL_s->all_headers.key, realloc_size);
	if (temp) {
	    URL_s->all_headers.key = temp;

	    temp = (char **) XP_REALLOC(URL_s->all_headers.value, realloc_size);
	    if (temp) {
		URL_s->all_headers.value = temp;

		URL_s->all_headers.max_index = number_of_entries;
		return(TRUE);
	    }
	}
    }
    net_FreeURLAllHeaders(URL_s);
    return(FALSE);
}


/* Append Key, Value pair into AllHeaders list of URL_struct
 */
PUBLIC Bool 
NET_AddToAllHeaders(URL_Struct * URL_s, char *name, char *value)
{
    char *key_ptr;
    char *value_ptr;

    XP_ASSERT(URL_s);
    XP_ASSERT(name);
    XP_ASSERT(value);

    if ((URL_s->all_headers.empty_index >= URL_s->all_headers.max_index) &&
	(!net_EnlargeURLAllHeaders(URL_s))) {
	return(FALSE);
    }

    key_ptr = URL_s->all_headers.key[URL_s->all_headers.empty_index] =
	XP_STRDUP(name);
    if (!key_ptr) {
	net_FreeURLAllHeaders(URL_s);
	return(FALSE);
	}

    value_ptr = URL_s->all_headers.value[URL_s->all_headers.empty_index] = 
	XP_STRDUP(value);
    if (!value_ptr) {
	XP_FREE(key_ptr);
	URL_s->all_headers.key[URL_s->all_headers.empty_index] = NULL;
	net_FreeURLAllHeaders(URL_s);
	return(FALSE);
	}
    URL_s->all_headers.empty_index++;
    return(TRUE);
}

/* Set the option IPAddressString field
 * This field overrides the hostname when doing the connect, 
 * and allows Java to specify a spelling of an IP number.
 * Out of memory condition will cause the URL_struct to be destroyed.
 */
PUBLIC int
NET_SetURLIPAddressString (URL_Struct * URL_s, CONST char *ip_string)
{
    XP_ASSERT(URL_s);

    FREEIF(URL_s->IPAddressString);
    URL_s->IPAddressString = NULL;

    /* set the passed in value */
    StrAllocCopy(URL_s->IPAddressString, ip_string);
    return NULL == URL_s->IPAddressString;
}

PUBLIC URL_Struct *
NET_HoldURLStruct (URL_Struct * URL_s)
{
    if(URL_s) {
	XP_ASSERT(URL_s->ref_count > 0);
	URL_s->ref_count++;
    }
    return URL_s;
}


/* free the contents and the URL structure when finished
 */
PUBLIC void
NET_FreeURLStruct (URL_Struct * URL_s)
{
    if(!URL_s)
	return;
    
    /* if someone is holding onto a pointer to us don't die */
    XP_ASSERT(URL_s->ref_count > 0);
    URL_s->ref_count--;
    if(URL_s->ref_count > 0)
	return;

    FREEIF(URL_s->address);
    FREEIF(URL_s->username);
    FREEIF(URL_s->password);
    FREEIF(URL_s->IPAddressString);
    FREEIF(URL_s->referer);
    FREEIF(URL_s->post_data);
    FREEIF(URL_s->post_headers);
    FREEIF(URL_s->content_type);
    FREEIF(URL_s->content_encoding);
    FREEIF(URL_s->x_mac_type);
    FREEIF(URL_s->x_mac_creator);
	FREEIF(URL_s->charset);
	FREEIF(URL_s->boundary);
    FREEIF(URL_s->redirecting_url);
	FREEIF(URL_s->authenticate);
	FREEIF(URL_s->protection_template);
    FREEIF(URL_s->http_headers);
	FREEIF(URL_s->cache_file);
    FREEIF(URL_s->window_target);
	FREEIF(URL_s->window_chrome);
	FREEIF(URL_s->refresh_url);
	FREEIF(URL_s->wysiwyg_url);
	FREEIF(URL_s->error_msg);

    FREEIF(URL_s->key_cipher);
    FREEIF(URL_s->key_issuer);
    FREEIF(URL_s->key_subject);
	if ( URL_s->certificate ) {
		CERT_DestroyCertificate(URL_s->certificate);
	}
	
	if ( URL_s->redirecting_cert ) {
		CERT_DestroyCertificate(URL_s->redirecting_cert);
	}

    /* Free all memory associated with header information */
    net_FreeURLAllHeaders(URL_s);

	if(URL_s->files_to_post)
	  {
		/* free a null terminated array of filenames
		 */
		int i=0;
		for(i=0; URL_s->files_to_post[i] != NULL; i++)
		  {
			FREE(URL_s->files_to_post[i]); /* free the filenames */
		  }
		FREE(URL_s->files_to_post); /* free the array */
	  }

    /* Like files_to_post. */
	if(URL_s->post_to)
	  {
		/* free a null terminated array of filenames
		 */
		int i=0;
		for(i=0; URL_s->post_to[i] != NULL; i++)
		  {
			FREE(URL_s->post_to[i]); /* free the URLs */
		  }
		FREE(URL_s->post_to); /* free the array */
	  }


  /* free the array */
  XP_FREEIF(URL_s->add_crlf);

	 XP_FREEIF(URL_s->page_services_url);

    XP_FREE(URL_s);

}

/* free the contents of AllHeader structure that is part of URL_Struct
 */
PRIVATE void
net_FreeURLAllHeaders (URL_Struct * URL_s)
{
    uint32 i=0;

    /* Free all memory associated with header information */
    for (i = 0; i < URL_s->all_headers.empty_index; i++) {
	if (URL_s->all_headers.key) {
	    FREEIF(URL_s->all_headers.key[i]);
	}
	if (URL_s->all_headers.value) {
	    FREEIF(URL_s->all_headers.value[i]);
	}
    }
    URL_s->all_headers.empty_index = URL_s->all_headers.max_index = 0;

    FREEIF(URL_s->all_headers.key);
    FREEIF(URL_s->all_headers.value);
    URL_s->all_headers.key = URL_s->all_headers.value = NULL;
}


/* if there is no slash in a URL besides the two that specify a host
 * then add one to the end.
 */
PRIVATE void add_slash_to_URL (URL_Struct *URL_s)
{
	char *colon=XP_STRCHR(URL_s->address,':');  /* must find colon */
	char *slash;

	/* make sure there is a hostname
	 */
	if(*(colon+1) == '/' && *(colon+2) == '/')
	    slash = XP_STRCHR(colon+3,'/');
	else
		return;

	if(slash==NULL)
      {
      TRACEMSG(("GetURL: URL %-.1900s changed to ",URL_s->address));

#ifdef MOZILLA_CLIENT
		/* add it now to the Global history since we can't get this
		 * name back :(  this is a bug because we are not sure
		 * that the link will actually work.
		 */
	    GH_UpdateGlobalHistory(URL_s);
#endif /* MOZILLA_CLIENT */

	StrAllocCat(URL_s->address, "/");
	URL_s->address_modified = YES;

	TRACEMSG(("%-.1900s",URL_s->address));
      }
}
 
#ifdef MOZILLA_CLIENT
PRIVATE void
net_OutputURLDocInfo(MWContext *ctxt, char *which, char **data, int32 *length)
{
	char *output=0;
	URL_Struct *URL_s = NET_CreateURLStruct(which, NET_DONT_RELOAD);
	struct tm *tm_struct_p;
	char buf[64];
	char *tmp=0;
	char *sec_msg, *il_msg;

	NET_FindURLInCache(URL_s, ctxt);

#define CELL_TOP                                                        \
	StrAllocCat(output,                                     \
				"<TR><TD VALIGN=BASELINE ALIGN=RIGHT><B>");     
#define CELL_TITLE(title)                                       \
	StrAllocCat(output, title);
#define CELL_MIDDLE                                                     \
	StrAllocCat(output,                                     \
				"</B></TD>"                                     \
				"<TD>");
#define CELL_BODY(body)                                         \
	StrAllocCat(output, body);
#define CELL_END                                                        \
	StrAllocCat(output,                                     \
				"</TD></TR>");
#define ADD_CELL(c_title, c_body)                       \
	CELL_TOP;                                                               \
	CELL_TITLE(c_title);                                    \
	CELL_MIDDLE;                                                    \
	CELL_BODY(c_body);                                              \
	CELL_END;

	StrAllocCopy(output, "<TABLE>");

	StrAllocCopy(tmp, "<A HREF=\"");
	StrAllocCat(tmp, URL_s->address);
	StrAllocCat(tmp, "\">");
	StrAllocCat(tmp, URL_s->address);
	StrAllocCat(tmp, "</a>");
	if(URL_s->is_netsite)
	  {
		ADD_CELL(XP_GetString(XP_NETSITE_), tmp);
	  }
	else
	  {
		ADD_CELL(XP_GetString(XP_LOCATION_), tmp);
	  }
	FREE(tmp);

	ADD_CELL(XP_GetString(XP_FILE_MIME_TYPE_), 
			 URL_s->content_type ? URL_s->content_type : XP_GetString(XP_CURRENTLY_UNKNOWN)); 
	ADD_CELL(XP_GetString(XP_SOURCE_), URL_s->cache_file
							? XP_GetString(XP_CURRENTLY_IN_DISK_CACHE)
							  : URL_s->memory_copy ? 
								XP_GetString(XP_CURRENTLY_IN_MEM_CACHE) 
								: XP_GetString(XP_CURRENTLY_NO_CACHE));

	ADD_CELL(XP_GetString(XP_LOCAL_CACHE_FILE_), URL_s->cache_file 
								 ? URL_s->cache_file
									:  XP_GetString(XP_NONE));

	tm_struct_p = localtime(&URL_s->last_modified);

	if(tm_struct_p)
	  {
#ifdef XP_UNIX
		strftime(buf, 64, XP_GetString(XP_LOCAL_TIME_FMT),
									   tm_struct_p);
#else
       char tmpbuf[64];
       XP_StrfTime(ctxt,tmpbuf,64,XP_LONG_DATE_TIME_FORMAT,
		   tm_struct_p);
       /* NOTE: XP_LOCAL_TIME_FMT is different depend on platform */
       PR_snprintf(buf, 64, XP_GetString(XP_LOCAL_TIME_FMT), tmpbuf);
#endif
      }
	else
	  {
		buf[0] = 0;
	  }
	ADD_CELL(XP_GetString(XP_LAST_MODIFIED), URL_s->last_modified ? buf :  XP_GetString(XP_MSG_UNKNOWN));

	tm_struct_p = gmtime(&URL_s->last_modified);

	if(tm_struct_p)
	  {
#ifdef XP_UNIX
		strftime(buf, 64, XP_GetString(XP_GMT_TIME_FMT),
									   tm_struct_p);
#else
       char tmpbuf[64];
       XP_StrfTime(ctxt,tmpbuf,64,XP_LONG_DATE_TIME_FORMAT,
				   tm_struct_p);
       /* NOTE: XP_GMT_TIME_FMT is different depend on platform */
       PR_snprintf(buf, 64, XP_GetString(XP_GMT_TIME_FMT), tmpbuf);
#endif
	  }
	else
	  {
		buf[0] = 0;
	  }

	ADD_CELL(XP_GetString(XP_LAST_MODIFIED), URL_s->last_modified ? buf :  XP_GetString(XP_MSG_UNKNOWN));

#ifdef DEBUG
	PR_snprintf(buf, 64, "%lu seconds since 1-1-70 GMT", URL_s->last_modified);
	ADD_CELL("Last Modified:", URL_s->last_modified ? buf :  "Unknown");
#endif

	XP_SPRINTF(buf, "%lu", URL_s->content_length);
	ADD_CELL(XP_GetString(XP_CONTENT_LENGTH_), URL_s->content_length 
								 ? buf
									:  XP_GetString(XP_MSG_UNKNOWN));
	ADD_CELL(XP_GetString(XP_EXPIRES_), URL_s->expires > 0
								 ? INTL_ctime(ctxt, &URL_s->expires)
									:  XP_GetString(XP_NO_DATE_GIVEN));

	if (URL_s->x_mac_type && *URL_s->x_mac_type)
	  {
		ADD_CELL(XP_GetString(XP_MAC_TYPE_), URL_s->x_mac_type);
	  }
	if (URL_s->x_mac_creator && *URL_s->x_mac_creator)
	  {
		ADD_CELL(XP_GetString(XP_MAC_CREATOR_), URL_s->x_mac_creator);
	  }

	if (URL_s->charset)
	  {
		ADD_CELL(XP_GetString(XP_CHARSET_), URL_s->charset);
	  }
	else 
	  {
		ADD_CELL(XP_GetString(XP_CHARSET_), XP_GetString(XP_MSG_UNKNOWN));
	  }

	if(URL_s->cache_file || URL_s->memory_copy)
		sec_msg = XP_PrettySecurityStatus(URL_s->security_on, 
										  URL_s->key_cipher, 
										  URL_s->key_size, 
										  URL_s->key_secret_size);
	else
		sec_msg = XP_STRDUP(XP_GetString(XP_STATUS_UNKNOWN));

	if(sec_msg)
	  {
		ADD_CELL(XP_GetString(XP_SECURITY_), sec_msg);
		FREE(sec_msg);
	  }

	if(URL_s->certificate)
		sec_msg = CERT_HTMLCertInfo(URL_s->certificate, PR_TRUE,
					    PR_TRUE);
	else
		sec_msg = 0;

	if(sec_msg)
	  {
		char *extstring;
		extstring = SECNAV_MakeCertButtonString(URL_s->certificate);
		if ( extstring ) {
			StrAllocCat(sec_msg, extstring);
			XP_FREE(extstring);
		}
		ADD_CELL(XP_GetString(XP_CERTIFICATE_), sec_msg);
		FREE(sec_msg);
	  }
	StrAllocCat(output, "</TABLE>");

	if(URL_s->content_type 
		&& !strncasecomp(URL_s->content_type, "image", 5))
	  {
	/* Have a seat. Lie down.  Tell me about yourself. */
	il_msg = IL_HTMLImageInfo(URL_s->address);
	if (il_msg) {
	  StrAllocCat(output, "<HR>\n");
	  StrAllocCat(output, il_msg);
	  XP_FREE(il_msg);
	}
	  }
		
	*data = output;
	*length = XP_STRLEN(output);

	NET_FreeURLStruct(URL_s);
	return;
}

/* prints information about a context usint HTML.
 *
 * The context passed in as arg2 is the context that
 * we are printing info about and the context that
 * is in the ActiveEntry is the window we are displaying
 * within
 */
PRIVATE
int
net_PrintContextInfo(ActiveEntry *cur_entry, MWContext *context)
{
	History_entry *his;
	NET_StreamClass *stream;
	char buf[64];
	char *tmp;

    /* get the current history entry and
     * extract the URL from it.  That has
     * the top level document URL and the title
     */
    his = SHIST_GetCurrent (&context->hist);
   
	cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);
	StrAllocCopy(cur_entry->URL_s->content_type, TEXT_HTML);
    stream = NET_StreamBuilder(cur_entry->format_out,
				   cur_entry->URL_s, cur_entry->window_id);

    if(!stream)
	return(MK_UNABLE_TO_CONVERT);

    XP_STRCPY(buf, "<FONT SIZE=+2><b>");
	(*stream->put_block)(stream, buf, XP_STRLEN(buf));

    if(his && his->title)
		tmp = XP_STRDUP(his->title);
	else
		tmp = XP_STRDUP(XP_GetString(XP_UNTITLED_DOCUMENT));

	(*stream->put_block)(stream, tmp, XP_STRLEN(tmp));

    XP_STRCPY(buf, XP_GetString(XP_HAS_THE_FOLLOWING_STRUCT));
	(*stream->put_block)(stream, buf, XP_STRLEN(buf));

	LO_DocumentInfo(context, stream);

	(*stream->complete)(stream);
	cur_entry->status = MK_DATA_LOADED;
	cur_entry->status = MK_DATA_LOADED;

	return(-1);
}
#endif /* MOZILLA_CLIENT */

#define PICS_SUPPORT

PRIVATE char * 
net_gen_pics_document(ActiveEntry *cur_entry)
{
    char *rv = NULL;
#ifdef PICS_SUPPORT
    char *help_dir = FE_GetNetHelpDir();

    if(help_dir)
    {
		if (help_dir[XP_STRLEN(help_dir)-1] != '/')
			StrAllocCat(help_dir, "/");
		
		if (!help_dir)
			return NULL;
			
#define PICS_HTML " \
<TITLE>XXXXXXXXXXXXXXX</TITLE> \
<FRAMESET border=no cols=1,100%%> \
<FRAME src=about:blank> \
<FRAME src='%spicsfail.htm'> \
</FRAMESET>"

        rv = PR_smprintf(PICS_HTML, help_dir);

        FREE(help_dir);
    }

#endif /* PICS_SUPPORT */

    return rv;
}

/* print out silly about type URL
 */
PRIVATE int net_output_about_url(ActiveEntry * cur_entry)
{
    NET_StreamClass * stream;
	char * data=0;
	char * content_type=0;
	int32 length;
	char * which = cur_entry->URL_s->address;
	char * colon = strchr (which, ':');
	void * fe_data = NULL;
	Bool   uses_fe_data=FALSE;

	
	if (colon)
	  which = colon + 1;

	if(0)
	  {
		/* placeholder to make ifdef's easier */
	  }
#ifdef MOZILLA_CLIENT
	else if(NET_URL_Type(which))
	  {
	    /* this url is asking for document info about this
		 * URL.
		 * Call a NET function to generate the HTML block
		 */
		net_OutputURLDocInfo(cur_entry->window_id, which, &data, &length);
		StrAllocCopy(content_type, TEXT_HTML);
	  }
	else if(!strncasecomp(which, "pics", 4))
	  {
		data = net_gen_pics_document(cur_entry);
   		if (data) {
			length = XP_STRLEN(data);
			content_type = XP_STRDUP(TEXT_HTML);
            uses_fe_data = FALSE;
        } 
	  }
	else if(!strcasecomp(which, "cookies"))
	{
		NET_DisplayCookieInfoAsHTML(cur_entry);
		return(-1);
	}
	else if(!strncasecomp(which, "cache", 5))
	  {
		NET_DisplayCacheInfoAsHTML(cur_entry);
		return(-1);
	  }
	else if(!strcasecomp(which, "memory-cache"))
	  {
		NET_DisplayMemCacheInfoAsHTML(cur_entry);
		return(-1);
	  }
	else if(!strcasecomp(which, "logout"))
	  {
		NET_RemoveAllAuthorizations();
		return(-1);
	  }
	else if(!strcasecomp(which, "image-cache"))
	  {
	IL_DisplayMemCacheInfoAsHTML(cur_entry->format_out, cur_entry->URL_s,
				     cur_entry->window_id);
		return(-1);
	  }
#ifdef DEBUG
    else if(!strcasecomp(which, "streams"))
      {
	NET_DisplayStreamInfoAsHTML(cur_entry);
	return(-1);
      }
#endif /* DEBUG */
	else if(!strcasecomp(which, "document"))
	  {
		char buf[64];
		History_entry *his;
		MWContext *context;

		/* output a Grid here with the first grid
		 * cell containing an about: url with the
		 * context pointer as the argument.
		 * 
		 * also put the context pointer in as the post data
		 * if it's not there already so that history will
		 * do the right thing.
		 *
		 * also output blank string for all FO's not view source
		 * and save
		 */

		/* if there is post data then a context ID is hiding
		 * in there.  Get it and verify it
		 */
		if(cur_entry->URL_s->post_data)
		  {
#if defined(__sun) && !defined(SVR4) /* sun 4.1.3 */
			sscanf(cur_entry->URL_s->post_data, "%lu", &context);
#else
			sscanf(cur_entry->URL_s->post_data, "%p", &context);
#endif
			if(!XP_IsContextInList(context))
				context = cur_entry->window_id;
		  }
		else
		  {
			context = cur_entry->window_id;
		  }
			
		/* get the current history entry and
		 * extract the URL from it.  That has
		 * the top level document URL 
		 */
		his = SHIST_GetCurrent (&context->hist);

		StrAllocCat(data, "<TITLE>");
		StrAllocCat(data, XP_GetString(XP_DOCUMENT_INFO));
		StrAllocCat(data,       "</TITLE>"
							"<frameset rows=\"40%,60%\">"
							"<frame src=about:");

		StrAllocCopy(cur_entry->URL_s->window_target, "%DocInfoWindow");
		/* add a Chrome Struct so that we can control the window */
		cur_entry->URL_s->window_chrome = XP_NEW(Chrome);

		if(cur_entry->URL_s->window_chrome)
		  {
			XP_MEMSET(cur_entry->URL_s->window_chrome, 0, sizeof(Chrome));
			/* it should be MWContextDialog
			 * but X isn't ready for it yet...
			 */
/* X is ready for MWContextDialog now...
 *#ifdef XP_UNIX
 *                      cur_entry->URL_s->window_chrome->type = MWContextBrowser;
 *#else
 */
			cur_entry->URL_s->window_chrome->type = MWContextDialog;
/*
 *#endif
 */
			cur_entry->URL_s->window_chrome->show_scrollbar = TRUE;
			cur_entry->URL_s->window_chrome->allow_resize = TRUE;
			cur_entry->URL_s->window_chrome->allow_close = TRUE;
		  }

		/* don't let people really view the source of this... */
		if(FO_PRESENT != CLEAR_CACHE_BIT(cur_entry->format_out))        
		  {
			StrAllocCat(data, "blank");
		  }
		else
		  {
			StrAllocCat(data, "FeCoNtExT=");

		    if(!cur_entry->URL_s->post_data)
		      {
#if defined(__sun) && !defined(SVR4) /* sun 4.1.3 */
			    XP_SPRINTF(buf, "%lu", context);
#else
			    XP_SPRINTF(buf, "%p", context);
#endif
			    StrAllocCat(data, buf);
			    StrAllocCopy(cur_entry->URL_s->post_data, buf);
		      }
		    else
		      {
			    StrAllocCat(data, cur_entry->URL_s->post_data);
		      }
		  }

		StrAllocCat(data, "><frame name=Internal_URL_Info src=about:");
		
		if(his && his->address)
			StrAllocCat(data, his->address);
		else
			StrAllocCat(data, "blank");

		StrAllocCat(data, "></frameset>");

		length = XP_STRLEN(data);
		StrAllocCopy(content_type, TEXT_HTML);
	  }
	else if(!strncasecomp(which, "Global", 6))
	  {
		cur_entry->status = NET_DisplayGlobalHistoryInfoAsHTML(
													cur_entry->window_id,
									cur_entry->URL_s,
									cur_entry->format_out);

		return(-1);

	  }
	else if(!XP_STRNCMP(which, "FeCoNtExT=", 10))
	  {
		MWContext *old_ctxt;
		
#if defined(__sun) && !defined(SVR4) /* sun 4.1.3 */
		sscanf(which+10, "%lu", &old_ctxt);
#else
		sscanf(which+10, "%p", &old_ctxt);
#endif

		/* verify that we have the right context 
		 *
		 */
		if(!XP_IsContextInList(old_ctxt))
		  {
			StrAllocCopy(data, XP_GetString(XP_THE_WINDOW_IS_NOW_INACTIVE));
		  }
		else    
		  {
			return(net_PrintContextInfo(cur_entry, old_ctxt));
		  }             
    
	    length = XP_STRLEN(data);
	    StrAllocCopy(content_type, TEXT_HTML);

      }
#endif /* MOZILLA_CLIENT */
	else if(!strncasecomp(which, "authors", 7))
	  {
		static const char *d =
		  ("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html\" "
		   "CHARSET=\"iso-8859-1\"><TITLE>Mozilla &Uuml;ber Alles</TITLE>"
		   "<BODY BGCOLOR=\"#C0C0C0\" TEXT=\"#000000\">"
		   "<TABLE width=\"100%\" height=\"100%\"><TR><TD ALIGN=CENTER>"
		   "<FONT SIZE=5><FONT COLOR=\"#0000EE\">about:authors</FONT> "
		   "removed.</FONT></TD></TR></TABLE>");
	    data = XP_STRDUP(d);
		length = (d ? XP_STRLEN(d) : 0);
	    content_type = XP_STRDUP(TEXT_HTML);
	    uses_fe_data = FALSE;
	  }
	/* Admin Kit/xp prefs diagnostic support */
	else if (!strncasecomp(which, "config", 6))
	{
		data = PREF_AboutConfig();
		if (data) {
			length = XP_STRLEN(data);
			content_type = XP_STRDUP(TEXT_HTML);
		}
	}
#ifdef XP_UNIX
	else if (!strncasecomp(which, "minibuffer", 10))
	{
		extern void FE_ShowMinibuffer(MWContext *);

		FE_ShowMinibuffer(cur_entry->window_id);
	}
#endif
#ifdef WEBFONTS
	else if (!strncasecomp(which, "fonts", 5))
	{
		NF_AboutFonts(cur_entry->window_id, which);
		return (-1);
	}
#endif /* WEBFONTS */
    else
      {
	    fe_data = FE_AboutData(which, &data, &length, &content_type);
	    uses_fe_data=TRUE;
      }

	if (!data || !content_type)
	  {
		cur_entry->status = MK_MALFORMED_URL_ERROR;
	  }
	else
	  {
		int status;
		StrAllocCopy(cur_entry->URL_s->content_type, content_type);

		cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);

		stream = NET_StreamBuilder(cur_entry->format_out,
								   cur_entry->URL_s, cur_entry->window_id);

		if(!stream)
		  {
			cur_entry->status = MK_UNABLE_TO_CONVERT;
			return(MK_UNABLE_TO_CONVERT);
		  }

		status = (*stream->put_block)(stream, data, length);

		if (status >= 0) {
			/* Append optional Admin Kit-specified about page text */
			char* custom_text = NULL;
			if (XP_STRCMP(which, "") == 0 &&
				PREF_CopyConfigString("about_text", &custom_text) == PREF_NOERROR) {
				(*stream->put_block)(stream, custom_text, XP_STRLEN(custom_text));
			}
		
			(*stream->complete) (stream);
			
			FREEIF(custom_text);
		}
		else
		  (*stream->abort) (stream, status);

		cur_entry->status = status;

		FREE(stream);
	  }

	if(uses_fe_data)
	  {
#if defined(XP_MAC) || defined(XP_UNIX) || defined (XP_WIN)
		FE_FreeAboutData (fe_data, which);
#endif /* XP_MAC || XP_UNIX */
	  }
	else
	  {
		FREE(data);
		FREE(content_type);
	  }


    return(-1);
}

#ifdef MOZILLA_CLIENT
/* print out security URL
 */
PRIVATE int net_output_security_url(ActiveEntry * cur_entry, MWContext *cx)
{
    NET_StreamClass * stream;
	char * content_type;
	char * which = cur_entry->URL_s->address;
	char * colon = XP_STRCHR (which, ':');

	if (colon)
	  {
		/* found the first colon; now find the question mark
		   (as in "about:security?certs"). */
		which = colon + 1;
		colon = XP_STRCHR (which, '?');
		if (colon)
		  which = colon + 1;
		else
		  which = which + XP_STRLEN (which); /* give it "" */
	  }

	content_type = SECNAV_SecURLContentType(which);
	if (!content_type) {
		cur_entry->status = MK_MALFORMED_URL_ERROR;

	} else if (!strcasecomp(content_type, "advisor")) {
	    cur_entry->status = SECNAV_SecHandleSecurityAdvisorURL(cx, which);

	} else {
		int status;

		StrAllocCopy(cur_entry->URL_s->content_type, content_type);

		cur_entry->format_out = CLEAR_CACHE_BIT(cur_entry->format_out);

		stream = NET_StreamBuilder(cur_entry->format_out,
								   cur_entry->URL_s, cur_entry->window_id);

		if (!stream)
			return(MK_UNABLE_TO_CONVERT);

		status = SECNAV_SecURLData(which, stream, cx);

		if (status >= 0) {
			(*stream->complete) (stream);
		} else {
			(*stream->abort) (stream, status);
		}

		cur_entry->status = status;

		FREE(stream);
	}

    return(-1);
}

PRIVATE int32
net_SecurityURLLoad(ActiveEntry *ce)
{
	if(ce->URL_s)
		StrAllocCopy(ce->URL_s->charset, INTL_ResourceCharSet());
	return net_output_security_url(ce, ce->window_id);
}

PRIVATE int32
net_SeclibURLLoad(ActiveEntry *ce)
{
	if(ce->URL_s)
		StrAllocCopy(ce->URL_s->charset, INTL_ResourceCharSet());
	SECNAV_HandleInternalSecURL(ce->URL_s, ce->window_id);
	return -1;
}

PRIVATE int32
net_HTMLPanelLoad(ActiveEntry *ce)
{
	if(ce->URL_s)
		StrAllocCopy(ce->URL_s->charset, INTL_ResourceCharSet());
	XP_HandleHTMLPanel(ce->URL_s);
	return -1;
}

PRIVATE int32
net_HTMLDialogLoad(ActiveEntry *ce)
{
	if(ce->URL_s)
		StrAllocCopy(ce->URL_s->charset, INTL_ResourceCharSet());
	XP_HandleHTMLDialog(ce->URL_s);
	return -1;
}

PRIVATE int32
net_WysiwygLoad(ActiveEntry *ce)
{
	const char *real_url = LM_SkipWysiwygURLPrefix(ce->URL_s->address);
	char *new_address;

	/* XXX can't use StrAllocCopy because it frees dest first */
	if (real_url && (new_address = XP_STRDUP(real_url)) != NULL)
	{
		XP_FREE(ce->URL_s->address);
		ce->URL_s->address = new_address;
		FREE_AND_CLEAR(ce->URL_s->wysiwyg_url);
	}

	/* no need to free real_url */

	ce->status = MK_DO_REDIRECT;
	return MK_DO_REDIRECT;
}

PRIVATE int32
net_ProtoMainStub(ActiveEntry *ce)
{
#ifdef DO_ANNOYING_ASSERTS_IN_STUBS
	XP_ASSERT(0);
#endif
	return -1;
}

PRIVATE void
net_ProtoCleanupStub(void)
{
}

PRIVATE void
net_reg_random_protocol(NET_ProtoInitFunc *LoadRoutine, int type)
{
    NET_ProtoImpl *random_proto_impl;

	random_proto_impl = XP_NEW(NET_ProtoImpl);

	if(!random_proto_impl)
		return;
	
    random_proto_impl->init = LoadRoutine;
    random_proto_impl->process = net_ProtoMainStub;
    random_proto_impl->interrupt = net_ProtoMainStub;
    random_proto_impl->cleanup = net_ProtoCleanupStub;

    NET_RegisterProtocolImplementation(random_proto_impl, type);
}

/* don't you just hate it when people come along and hack this
 * kind of stuff into your code.
 *
 * @@@ clean this up some time
 */
PRIVATE void
NET_InitTotallyRandomStuffPeopleAddedProtocols(void)
{
	net_reg_random_protocol(net_SecurityURLLoad, SECURITY_TYPE_URL);
	net_reg_random_protocol(net_SeclibURLLoad, INTERNAL_SECLIB_TYPE_URL);
	net_reg_random_protocol(net_HTMLPanelLoad, HTML_PANEL_HANDLER_TYPE_URL);
	net_reg_random_protocol(net_HTMLDialogLoad, HTML_DIALOG_HANDLER_TYPE_URL);
	net_reg_random_protocol(net_WysiwygLoad, WYSIWYG_TYPE_URL);
}

#endif /* MOZILLA_CLIENT */

PRIVATE Bool net_about_kludge(URL_Struct *URL_s)
{
  unsigned char *url = (unsigned char *) URL_s->address;
  unsigned char *user = (unsigned char *) XP_STRDUP ((char*)(url + 6));
  unsigned char *tmp;
 
  if (user == NULL)
	return FALSE;

  for (tmp = user; *tmp; tmp++) *tmp += 23;

  if (	 !XP_STRCMP((char*)user, "\170\202\202\170\205\170") ||	/* akkana */
	 !XP_STRCMP((char*)user, "\170\211\200") ||				/* ari */
	 !XP_STRCMP((char*)user, "\170\211\212\177\170\173") ||			/* arshad */
	 !XP_STRCMP((char*)user, "\170\213\206\213\200\172") ||			/* atotic */
	 !XP_STRCMP((char*)user, "\171\176\170\220") ||				/* bgay */
	 !XP_STRCMP((char*)user, "\171\201\206\205\174\212") ||			/* bjones */
	 !XP_STRCMP((char*)user, "\171\203\220\213\177\174") ||			/* blythe */
	 !XP_STRCMP((char*)user, "\171\205\174\203\212\206\205") ||		/* bnelson */
	 !XP_STRCMP((char*)user, "\171\206\171") ||				/* bob */
	 !XP_STRCMP((char*)user, "\171\206\171\201") ||				/* bobj */
	 !XP_STRCMP((char*)user, "\171\211\170\173\174") ||			/* brade */
	 !XP_STRCMP((char*)user, "\171\211\200\170\205\206") ||			/* briano */
	 !XP_STRCMP((char*)user, "\171\211\200\173\176\174") ||			/* bridge */
	 !XP_STRCMP((char*)user, "\172\170\213\177\203\174\174\204") ||		/* cathleen */
	 !XP_STRCMP((char*)user, "\172\177\206\214\172\202") ||			/* chouck */
	 !XP_STRCMP((char*)user, "\172\177\211\200\212\175") ||			/* chrisf */
	 !XP_STRCMP((char*)user, "\172\202\211\200\213\221\174\211") ||		/* ckritzer */
	 !XP_STRCMP((char*)user, "\172\204\170\205\212\202\174") ||		/* cmanske */
	 !XP_STRCMP((char*)user, "\172\206\205\215\174\211\212\174") ||		/* converse */
	 !XP_STRCMP((char*)user, "\172\206\211\171\170\205") ||			/* corban */
	 !XP_STRCMP((char*)user, "\172\217\214\174") ||				/* cxue */
	 !XP_STRCMP((char*)user, "\172\220\174\177") ||				/* cyeh */
	 !XP_STRCMP((char*)user, "\173\170\205\173\170") ||             /* danda */
	 !XP_STRCMP((char*)user, "\173\170\205\204") ||				/* danm */
	 !XP_STRCMP((char*)user, "\173\170\215\200\173\204") ||			/* davidm */
	 !XP_STRCMP((char*)user, "\173\174\172\170\212\213\211\200") || /* decastri */
	 !XP_STRCMP((char*)user, "\173\201\216") ||				/* djw */
	 !XP_STRCMP((char*)user, "\173\202\170\211\203\213\206\205") ||		/* dkarlton */
	 !XP_STRCMP((char*)user, "\173\204\206\212\174") ||			/* dmose */
	 !XP_STRCMP((char*)user, "\173\206\205") ||				/* don */
	 !XP_STRCMP((char*)user, "\173\206\211\170") ||				/* dora */
	 !XP_STRCMP((char*)user, "\173\206\214\176") ||				/* doug */
	 !XP_STRCMP((char*)user, "\173\207") ||					/* dp */
	 !XP_STRCMP((char*)user, "\174\171\200\205\170") ||			/* ebina */
	 !XP_STRCMP((char*)user, "\174\204\170\173\174\211") ||			/* emader */
	 !XP_STRCMP((char*)user, "\174\211\200\202") ||				/* erik */
	 !XP_STRCMP((char*)user, "\175\211\200\174\173\204\170\205") ||		/* friedman */
	 !XP_STRCMP((char*)user, "\175\213\170\205\176") ||			/* ftang */
	 !XP_STRCMP((char*)user, "\175\214\171\200\205\200") ||			/* fubini */
	 !XP_STRCMP((char*)user, "\175\214\211") ||				/* fur */
	 !XP_STRCMP((char*)user, "\176\170\176\170\205") ||			/* gagan */
	 !XP_STRCMP((char*)user, "\176\203\220\205\205") ||			/* glynn */\
	 !XP_STRCMP((char*)user, "\176\211\206\216\203\213\200\176\174\211") ||	/* growltiger */\
	 !XP_STRCMP((char*)user, "\177\170\176\170\205") ||			/* hagan */
	 !XP_STRCMP((char*)user, "\177\170\211\173\213\212") ||			/* hardts */
 	 !XP_STRCMP((char*)user, "\177\212\177\170\216") ||			/* hshaw */
 	 !XP_STRCMP((char*)user, "\177\220\170\213\213") ||			/* hyatt */
	 !XP_STRCMP((char*)user, "\200\211\174\205\174") ||			/* irene */
	 !XP_STRCMP((char*)user, "\201\170\211") ||				/* jar */
	 !XP_STRCMP((char*)user, "\201\174\175\175") ||				/* jeff */
	 !XP_STRCMP((char*)user, "\201\174\215\174\211\200\205\176") ||		/* jevering */  
	 !XP_STRCMP((char*)user, "\201\176") ||					/* jg */
	 !XP_STRCMP((char*)user, "\201\176\174\203\203\204\170\205") ||		/* jgellman */
	 !XP_STRCMP((char*)user, "\201\201") ||					/* jj */
	 !XP_STRCMP((char*)user, "\201\206\174\211\214\175\175") ||		/* joeruff */
	 !XP_STRCMP((char*)user, "\201\206\202\200") ||				/* joki */
	 !XP_STRCMP((char*)user, "\201\206\205\170\212") ||			/* jonas */
	 !XP_STRCMP((char*)user, "\201\207\204") ||				/* jpm */
	 !XP_STRCMP((char*)user, "\201\212\216") ||				/* jsw */
	 !XP_STRCMP((char*)user, "\201\216\221") ||				/* jwz */
	 !XP_STRCMP((char*)user, "\202\170\177\174\211\205") ||			/* kahern */
	 !XP_STRCMP((char*)user, "\202\170\211\203\213\206\205") ||		/* karlton */
	 !XP_STRCMP((char*)user, "\202\200\207\207") ||				/* kipp */
	 !XP_STRCMP((char*)user, "\202\204\172\174\205\213\174\174") ||		/* kmcentee */
	 !XP_STRCMP((char*)user, "\202\206\174\177\204") ||			/* koehm */
	 !XP_STRCMP((char*)user, "\202\211\200\212\213\200\205") ||		/* kristin */
	 !XP_STRCMP((char*)user, "\203\170\211\214\171\171\200\206") ||		/* larubbio */
    !XP_STRCMP((char*)user, "\203\170\216") ||                     /* law */
	 !XP_STRCMP((char*)user, "\203\174\205\221") ||				/* lenz */
	 !XP_STRCMP((char*)user, "\203\206\211\200\202") ||			/* lorik */
	 !XP_STRCMP((char*)user, "\203\213\170\171\171") ||			/* ltabb */
	 !XP_STRCMP((char*)user, "\204\170\203\204\174\211") ||			/* malmer */
	 !XP_STRCMP((char*)user, "\204\170\211\172\170") ||			/* marca */
	 !XP_STRCMP((char*)user, "\204\170\213\213") ||				/* matt */ 
	 !XP_STRCMP((char*)user, "\204\172\170\175\174\174") ||			/* mcafee */
	 !XP_STRCMP((char*)user, "\204\172\204\214\203\203\174\205") ||		/* mcmullen */
	 !XP_STRCMP((char*)user, "\204\174\173\200\213\170\213\200\206\205") ||	/* meditation */
	 !XP_STRCMP((char*)user, "\204\203\204") ||				/* mlm */
	 !XP_STRCMP((char*)user, "\204\206\205\200\210\214\174") ||		/* monique */
	 !XP_STRCMP((char*)user, "\204\206\205\213\214\203\203\200")||		/* montulli */
	 !XP_STRCMP((char*)user, "\204\206\211\212\174") ||			/* morse */
	 !XP_STRCMP((char*)user, "\204\206\214\211\174\220")||			/* mourey */
	 !XP_STRCMP((char*)user, "\204\213\206\220") ||				/* mtoy */
	 !XP_STRCMP((char*)user, "\204\216\174\203\172\177") ||			/* mwelch */
	 !XP_STRCMP((char*)user, "\205\174\203\212\206\205\171") ||		/* nelsonb */
	 !XP_STRCMP((char*)user, "\205\200\212\177\174\174\213\177") ||		/* nisheeth */
	 !XP_STRCMP((char*)user, "\206\200\205\202") ||				/* oink */
	 !XP_STRCMP((char*)user, "\207\170\203\174\215\200\172\177") ||		/* palevich */
	 !XP_STRCMP((char*)user, "\207\170\210\214\200\205") ||			/* paquin */
	 !XP_STRCMP((char*)user, "\207\170\214\203\173") ||			/* pauld */
	 !XP_STRCMP((char*)user, "\207\172\177\174\205") ||			/* pchen */
	 !XP_STRCMP((char*)user, "\207\200\174\211\211\174") ||			/* pierre */
	 !XP_STRCMP((char*)user, "\207\200\176\203\174\213") ||			/* piglet */
	 !XP_STRCMP((char*)user, "\207\200\205\202\174\211\213\206\205") ||	/* pinkerton */
	 !XP_STRCMP((char*)user, "\211\170\204\170\205") ||			/* raman */
	 !XP_STRCMP((char*)user, "\211\170\204\200\211\206") ||			/* ramiro */
	 !XP_STRCMP((char*)user, "\211\174\172\214\211\212\200\206\205") ||	/* recursion */
	 !XP_STRCMP((char*)user, "\211\174\207\202\170") ||			/* repka */
	 !XP_STRCMP((char*)user, "\211\200\172\170\211\173\206\171") ||		/* ricardob */
	 !XP_STRCMP((char*)user, "\211\201\172") ||				/* rjc */
	 !XP_STRCMP((char*)user, "\211\204\216") ||				/* rmw */
	 !XP_STRCMP((char*)user, "\211\170\204\211\170\201") ||		/* ramraj */
	 !XP_STRCMP((char*)user, "\211\206\171\204") ||				/* robm */
	 !XP_STRCMP((char*)user, "\211\206\174\171\174\211") ||			/* roeber */
	 !XP_STRCMP((char*)user, "\211\207\206\213\213\212") ||			/* rpotts */
	 !XP_STRCMP((char*)user, "\211\214\212\203\170\205") ||			/* ruslan */
	 !XP_STRCMP((char*)user, "\212\172\172") ||				/* scc */
	 !XP_STRCMP((char*)user, "\212\173\170\176\203\174\220") ||		/* sdagley */
	 !XP_STRCMP((char*)user, "\212\177\170\211\206\205\200") ||		/* sharoni */
	 !XP_STRCMP((char*)user, "\212\177\214\170\205\176") ||			/* shuang */
	 !XP_STRCMP((char*)user, "\212\202") ||					/* sk */
	 !XP_STRCMP((char*)user, "\212\202\220\174") ||				/* skye */
	 !XP_STRCMP((char*)user, "\212\203\170\204\204") ||			/* slamm */
	 !XP_STRCMP((char*)user, "\212\204\212\200\203\215\174\211") ||		/* smsilver */
	 !XP_STRCMP((char*)user, "\212\207\174\205\172\174") ||			/* spence */
	 !XP_STRCMP((char*)user, "\212\207\200\173\174\211") ||			/* spider */
	 !XP_STRCMP((char*)user, "\212\212\207\200\213\221\174\211") ||		/* sspitzer */
	 !XP_STRCMP((char*)user, "\212\213\174\211\205") ||			/* stern */
	 !XP_STRCMP((char*)user, "\213\170\211\170\177") ||			/* tarah */
	 !XP_STRCMP((char*)user, "\213\174\211\211\220") ||			/* terry */
    !XP_STRCMP((char*)user, "\213\175\203\200\205\213") ||         /* tflint */
	 !XP_STRCMP((char*)user, "\213\200\204\204") ||				/* timm */
	 !XP_STRCMP((char*)user, "\213\206\212\177\206\202") ||			/* toshok */
	 !XP_STRCMP((char*)user, "\213\211\170\172\220") ||			/* tracy */
	 !XP_STRCMP((char*)user, "\213\211\200\212\213\170\205") ||		/* tristan */
	 !XP_STRCMP((char*)user, "\213\211\214\173\174\203\203\174") ||		/* trudelle */
	 !XP_STRCMP((char*)user, "\215\170\203\174\212\202\200") ||		/* valeski */
	 !XP_STRCMP((char*)user, "\216\170\211\211\174\205") ||			/* warren */
	 !XP_STRCMP((char*)user, "\216\201\212") )				/* wjs */
  {
	  /* "http://people.netscape.com/" */
	  char *head = ("\177\213\213\207\121\106\106\207\174\206\207\203\174\105"
			"\205\174\213\212\172\170\207\174\105\172\206\204\106");
	  unsigned char *location = (unsigned char *)
		XP_ALLOC (XP_STRLEN ((char *) head) + XP_STRLEN ((char *) user) + 2);
	  if (! location)
		{
		  XP_FREE (user);
		  return FALSE;
		}
	  XP_STRCPY ((char *) location, (char *) head);
	  XP_STRCAT ((char *) location, (char *) user);
	  for (tmp = location; *tmp; tmp++) *tmp -= 23;
	  XP_STRCAT ((char *) location, "/");
	  XP_FREE (user);
	  XP_FREE (URL_s->address);
	  URL_s->address = (char *) location;
	  URL_s->address_modified = TRUE;
	  return TRUE;
	}
  else
	{
	  XP_FREE (user);
	  return FALSE;
	}
}

PRIVATE int32
net_AboutLoad(ActiveEntry *ce)
{
	if(net_about_kludge (ce->URL_s))
	{
		ce->status = MK_DO_REDIRECT;
		return MK_DO_REDIRECT;
	}

	return net_output_about_url(ce);
}

PRIVATE int32
net_AboutStub(ActiveEntry *ce)
{
#ifdef DO_ANNOYING_ASSERTS_IN_STUBS
	XP_ASSERT(0);
#endif
	return -1;
}

PRIVATE void
net_CleanupAbout(void)
{
}

PRIVATE void
NET_InitAboutProtocol(void)
{
        static NET_ProtoImpl about_proto_impl;

        about_proto_impl.init = net_AboutLoad;
        about_proto_impl.process = net_AboutStub;
        about_proto_impl.interrupt = net_AboutStub;
        about_proto_impl.cleanup = net_CleanupAbout;

        NET_RegisterProtocolImplementation(&about_proto_impl, ABOUT_TYPE_URL);
}

/*
 *  Check the no_proxy environment variable to get the list
 *  of hosts for which proxy server is not consulted.
 *
 *  no_proxy is a comma- or space-separated list of machine
 *  or domain names, with optional :port part.  If no :port
 *  part is present, it applies to all ports on that domain.
 *
 *  Example: "netscape.com,some.domain:8001"
 *
 */
PRIVATE Bool override_proxy (CONST char * URL)
{
    char * p = NULL;
    char * host = NULL;
    int port = 0;
    int h_len = 0;
    char *no_proxy = MKno_proxy;

    if(no_proxy==NULL)
	return(FALSE);

    if (!(host = NET_ParseURL(URL, GET_HOST_PART)))
	return NO;

    if (!*host)
      {
	XP_FREE(host);
	return NO;
      }

    p = XP_STRCHR(host, ':');
    if (p)     /* Port specified */
      {
	*p++ = 0;                       /* Chop off port */
	port = atoi(p);
      }
    else
      {                          /* Use default port */
	char * access = NET_ParseURL(URL, GET_PROTOCOL_PART);
	if (access) {
	    if      (!XP_STRCMP(access,"http"))    port = 80;
	    else if (!XP_STRCMP(access,"gopher"))  port = 70;
	    else if (!XP_STRCMP(access,"ftp"))     port = 21;
		XP_FREE(access);
	}
      }
    if (!port) port = 80;           /* Default */
    h_len = XP_STRLEN(host);

    while (*no_proxy) {
	char * end;
	char * colon = NULL;
	int templ_port = 0;
	int t_len;

	while (*no_proxy && (isspace(*no_proxy) || *no_proxy==','))
	no_proxy++;                 /* Skip whitespace and separators */

	end = no_proxy;
	while (*end && !isspace(*end) && *end != ',')   /* Find separator */
		  {
	    if (*end==':') colon = end;                 /* Port number given */
	    end++;
	  }

	if (colon)
		  {
	    templ_port = atoi(colon+1);
	    t_len = colon - no_proxy;
	  }
		else
		  {
	    t_len = end - no_proxy;
	  }

	/* don't worry about case when comparing the requested host to the proxies in the
	   no proxy list, i.e. use XP_STRNCASECMP */
	if ((!templ_port || templ_port == port)  && (t_len > 0  &&  t_len <= h_len  &&
		!XP_STRNCASECMP(host + h_len - t_len, no_proxy, t_len))) 
		  {
	    XP_FREE(host);
	    return YES;
	  }
	if (*end)
	    no_proxy = end+1;
	else
	    break;
    }

    XP_FREE(host);
    return NO;
}

PUBLIC void
NET_SetProxyServer(NET_ProxyType type, const char * org_host_port)
{
	char *host_port = 0;

	if(org_host_port && *org_host_port) {
		host_port = XP_STRDUP(org_host_port);

		if(!host_port)
			return;

		/* limit the size of host_port to within MAXHOSTNAMELEN */
		if(XP_STRLEN(host_port) > MAXHOSTNAMELEN)
			host_port[MAXHOSTNAMELEN] = '\0';
	}

	TRACEMSG(("NET_SetProxyServer called with host: %-.1900s",
			  host_port ? host_port : "(null)"));

    switch(type)
	  {
		case PROXY_AUTOCONF_URL:
			if (host_port) {
				if (!MKproxy_ac_url || XP_STRCMP(MKproxy_ac_url, org_host_port)) {
					StrAllocCopy(MKproxy_ac_url, org_host_port);
					NET_ProxyAcLoaded = FALSE;
				}
			}
			else {
				if (MKproxy_ac_url) {
					FREE_AND_CLEAR(MKproxy_ac_url);
					NET_ProxyAcLoaded = FALSE;
				}
			}
			break;

		case FTP_PROXY:
			if(host_port)
			StrAllocCopy(MKftp_proxy, host_port);
			else
				FREE_AND_CLEAR(MKftp_proxy);
			break;
		case GOPHER_PROXY:
			if(host_port)
			StrAllocCopy(MKgopher_proxy, host_port);
			else
				FREE_AND_CLEAR(MKgopher_proxy);
			break;
		case HTTP_PROXY:
			if(host_port)
			StrAllocCopy(MKhttp_proxy, host_port);
			else
				FREE_AND_CLEAR(MKhttp_proxy);
			break;
		case HTTPS_PROXY:
			if(host_port)
			StrAllocCopy(MKhttps_proxy, host_port);
			else
				FREE_AND_CLEAR(MKhttps_proxy);
			break;
		case NEWS_PROXY:
			if(host_port)
			StrAllocCopy(MKnews_proxy, host_port);
			else
				FREE_AND_CLEAR(MKnews_proxy);
			break;
		case WAIS_PROXY:
			if(host_port)
			StrAllocCopy(MKwais_proxy, host_port);
			else
				FREE_AND_CLEAR(MKno_proxy);
			break;

		case NO_PROXY:
			if(host_port)
			StrAllocCopy(MKno_proxy, org_host_port);
			break;

		default:
			/* ignore */
			break;
	  }

	FREEIF(host_port);
}

/* return a proxy server host and port to the caller or NULL
 *
 * all this proxy bussiness should be rewritten to be generalized
 * and not switch() based [LJM]
 */
MODULE_PRIVATE char *
NET_FindProxyHostForUrl(int url_type, char *url_address)
{

	if(override_proxy(url_address))
		return NULL;

    switch(url_type)
	  {
		case FTP_TYPE_URL:
			return MKftp_proxy;
			break;

		case GOPHER_TYPE_URL:
			return(MKgopher_proxy);
			break;

		case HTTP_TYPE_URL:
		case URN_TYPE_URL:
		case NFS_TYPE_URL:
			return(MKhttp_proxy);
			break;

		case SECURE_HTTP_TYPE_URL:
			return(MKhttps_proxy);
			break;

		case NEWS_TYPE_URL:
		case INTERNAL_NEWS_TYPE_URL:
			return(MKnews_proxy);
			break;

		case WAIS_TYPE_URL:
			return(MKwais_proxy);
			break;

		default:
			/* ignore */
			break;
	  }

	return NULL;
}

/* Force it to reload automatic proxy config.
 * This function simply sets a flag so the config will be 
 * reloaded during the next geturl. It doesn't actually
 * do the reloading here. */
#ifdef MOZILLA_CLIENT
PUBLIC void
NET_ReloadProxyConfig(MWContext *window_id)
{
	/* if we have a global config load it */
	if (MKglobal_config_url)
		  NET_GlobalAcLoaded = FALSE;
	/* else if we have an old style proxy config and we are set on automatic...load it 
	 * , or if we're using the old style proxy config that is actually a proxy autodiscovery
	 * url, reload it also */
    else if( (MKproxy_ac_url &&	(!MKproxy_style || MKproxy_style == PROXY_STYLE_AUTOMATIC))
			|| (NET_UsingPadPac()) )
		  NET_ProxyAcLoaded = FALSE;
}
#endif  /* MOZILLA_CLIENT */


/* Debugging routine prints an URL (and string "header")
 */
#ifdef DEBUG
void TraceURL (URL_Struct *url, char *header)
{
    TRACEMSG(("URL %-.1900s", header));
    if (url->address)           TRACEMSG(("    address          %s", url->address));
    if (url->content_length)    TRACEMSG(("    content_length   %i", url->content_length));
    if (url->content_type)      TRACEMSG(("    content_type     %s", url->content_type));
    if (url->content_encoding)  TRACEMSG(("    content_encoding %s", url->content_encoding));
}
#endif

/* This function's existance was prompted by the front end desiring the ability to put the
   client in kiosk mode. The function removes any potentially sensitive information from the
   background: passwords, caches, histories, cookies. */
/* fix Mac warning of missing prototype */
PUBLIC void
NET_DestroyEvidence();

PUBLIC void
NET_DestroyEvidence()
{
	int32 oldSize = 0;

	/* Handle authorizations */
	NET_RemoveAllAuthorizations();
	
	/* Handle global history */
	GH_ClearGlobalHistory();

	/* Handle Session History */

	/* Handle cookies */
	NET_RemoveAllCookies();

	/* Handle disk cache */
	oldSize = NET_GetDiskCacheSize();
	NET_SetDiskCacheSize(0); /* zero it out */
	NET_SetDiskCacheSize(oldSize); /* set it back up */

	/* Handle memory cache */
	oldSize = NET_GetMemoryCacheSize();
	NET_SetMemoryCacheSize(0); /* zero it out */
	NET_SetMemoryCacheSize(oldSize); /* set it back up */
}

#ifndef MOZ_MAIL_NEWS

/* this whole mess should get moved to the mksmtp.c file
 * where it can share the InitMailtoProtocol function
 * since that is what it is trying to replace
 *
 * for now InitMailto will be duplicated inside the ifdef.
 */

#define CE_URL_S            cur_entry->URL_s
#define CE_STATUS           cur_entry->status

extern void FE_AlternateCompose(
    char * from, char * reply_to, char * to, char * cc, char * bcc,
    char * fcc, char * newsgroups, char * followup_to,
    char * organization, char * subject, char * references,
    char * other_random_headers, char * priority,
    char * attachment, char * newspost_url, char * body);

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

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

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

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

#if defined(XP_WIN) /* other FE's add here when Front end code added */
        FE_AlternateCompose(
            from, reply_to, to, cc, bcc, fcc,
			newsgroups, followup_to, organization,
			subject, references, other_random_headers,
			priority, attachment, newspost_url, body );
#endif /* XP_WIN */

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

		CE_STATUS = MK_NO_DATA;
		return(-1);
}

PRIVATE int32
net_MailtoStub(ActiveEntry *ce)
{
#ifdef DO_ANNOYING_ASSERTS_IN_STUBS
	XP_ASSERT(0);
#endif
	return(0);		/* Well the function definition says it returns SOMETHING! */
}
PRIVATE void
net_CleanupMailtoStub(void)
{
#ifdef DO_ANNOYING_ASSERTS_IN_STUBS
	XP_ASSERT(0);
#endif
}

MODULE_PRIVATE void
NET_InitMailtoProtocol(void)
{
        static NET_ProtoImpl mailto_proto_impl;

        mailto_proto_impl.init = net_MailtoLoad;
        mailto_proto_impl.process = net_MailtoStub;
        mailto_proto_impl.interrupt = net_MailtoStub;
        mailto_proto_impl.cleanup = net_CleanupMailtoStub;

        NET_RegisterProtocolImplementation(&mailto_proto_impl, MAILTO_TYPE_URL);
}

#endif /* MOZ_MAIL_NEWS */

#ifdef PROFILE
#pragma profile off
#endif

