/* -*- 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.
 */
/*
 *
 * Designed and Implemented by Lou Montulli '94
 * Heavily modified by Judson Valeski '97
 * Yada yada yada... Gagan Saksena '98
 *
 * This file implements HTTP access authorization
 * and HTTP cookies
 */

#if defined(CookieManagement)
/* #define TRUST_LABELS */
#endif

#define alphabetize 1
#include "rosetta.h"
#include "xp.h"
#include "mkprefs.h"
#include "netutils.h"
#include "mkselect.h"
#include "mktcp.h"
#include "mkgeturl.h"
#include "mkparse.h"
#include "mkaccess.h"
#include "net_xp_file.h"
#include "cookies.h"
#include "httpauth.h"
#include "prefapi.h"
#include "shist.h"
#include "jscookie.h"
#include "prmon.h"
#include "edt.h" /* for EDT_SavePublishUsernameAndPassword */

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

#include "secnav.h"
#include "sechash.h"
#include "libevent.h"
#include "pwcacapi.h"

/* for XP_GetString() */
#include "xpgetstr.h"

extern int XP_CONFIRM_AUTHORIZATION_FAIL;
extern int XP_ACCESS_ENTER_USERNAME;
extern int XP_CONFIRM_PROXYAUTHOR_FAIL;
extern int XP_CONNECT_PLEASE_ENTER_PASSWORD_FOR_HOST;
extern int XP_PROXY_REQUIRES_UNSUPPORTED_AUTH_SCHEME;
extern int XP_LOOPING_OLD_NONCES;
extern int XP_UNIDENTIFIED_PROXY_SERVER;
extern int XP_PROXY_AUTH_REQUIRED_FOR;
extern int XP_CONNECT_PLEASE_ENTER_PASSWORD_FOR_PROXY;
extern int XP_FORTEZZA_PROXY_AUTH;
extern int MK_ACCESS_COOKIES_THE_SERVER;
extern int MK_ACCESS_COOKIES_WISHES; 
#if defined(CookieManagement)
extern int MK_ACCESS_COOKIES_WISHES0;
extern int MK_ACCESS_COOKIES_WISHES1;
extern int MK_ACCESS_COOKIES_WISHESN;
extern int MK_ACCESS_COOKIES_WISHES_MODIFY;
extern int MK_ACCESS_COOKIES_REMEMBER;
extern int MK_ACCESS_COOKIES_ACCEPTED;
extern int MK_ACCESS_SITE_COOKIES_ACCEPTED;
extern int MK_ACCESS_COOKIES_PERMISSION;
#else
extern int MK_ACCESS_COOKIES_TOANYSERV; 
extern int MK_ACCESS_COOKIES_TOSELF;
extern int MK_ACCESS_COOKIES_NAME_AND_VAL;
extern int MK_ACCESS_COOKIES_COOKIE_WILL_PERSIST;
#endif
extern int MK_ACCESS_COOKIES_SET_IT;
extern int MK_ACCESS_YOUR_COOKIES;
extern int MK_ACCESS_NAME;
extern int MK_ACCESS_VALUE;
extern int MK_ACCESS_HOST;
extern int MK_ACCESS_DOMAIN;
extern int MK_ACCESS_PATH;
extern int MK_ACCESS_YES;
extern int MK_ACCESS_NO;
extern int MK_ACCESS_SECURE;
extern int MK_ACCESS_EXPIRES;
extern int MK_ACCESS_END_OF_SESSION;
extern int MK_ACCESS_VIEW_COOKIES;
extern int MK_ACCESS_VIEW_SITES;

#ifdef TRUST_LABELS
extern int MK_ACCESS_TL_PUR1; 
extern int MK_ACCESS_TL_PUR2; 
extern int MK_ACCESS_TL_PUR3; 
extern int MK_ACCESS_TL_PUR4; 
extern int MK_ACCESS_TL_PUR5; 
extern int MK_ACCESS_TL_PUR6; 
extern int MK_ACCESS_TL_PPH0; 
extern int MK_ACCESS_TL_PPH1; 
extern int MK_ACCESS_TL_PPH2; 
extern int MK_ACCESS_TL_PPH3; 
extern int MK_ACCESS_TL_PPH4; 
extern int MK_ACCESS_TL_PPH5; 
extern int MK_ACCESS_TL_ID1;  
extern int MK_ACCESS_TL_ID0;  
extern int MK_ACCESS_TL_BY;   
extern int MK_ACCESS_TL_RECP1;
extern int MK_ACCESS_TL_RECP2;
extern int MK_ACCESS_TL_RECP3;
extern int MK_ACCESS_TL_RPH0; 
extern int MK_ACCESS_TL_RPH1; 
extern int MK_ACCESS_TL_RPH2; 
extern int MK_ACCESS_TL_RPH3; 

#endif


#define MAX_NUMBER_OF_COOKIES  300
#define MAX_COOKIES_PER_SERVER 20
#define MAX_BYTES_PER_COOKIE   4096  /* must be at least 1 */

/*
 * Authentication information for servers and proxies is kept
 * on separate lists, but both lists consist of net_AuthStruct's.
 */

HG73943

PRIVATE XP_List * net_auth_list = NULL;
PRIVATE XP_List * net_proxy_auth_list = NULL;

PRIVATE Bool cookies_changed = FALSE;
#if defined(CookieManagement)
PRIVATE Bool cookie_permissions_changed = FALSE;
PRIVATE Bool cookie_remember_checked = FALSE;
#endif

PRIVATE NET_CookieBehaviorEnum net_CookieBehavior = NET_Accept;
PRIVATE Bool net_WarnAboutCookies = FALSE;
PRIVATE char *net_scriptName = (char *)0;

/*
 * Different schemes supported by the client.
 * Order means the order of preference between authentication schemes.
 *
 */
typedef enum _net_AuthType {
    AUTH_INVALID   = 0,
    AUTH_BASIC     = 1
#ifdef SIMPLE_MD5
	HG72621
#endif /* SIMPLE_MD5 */
    ,AUTH_FORTEZZA  = 3
} net_AuthType;


/*
 * This struct describes both Basic  authentication stuff,
 * for both HTTP servers and proxies.
 *
 */
HG10828


typedef struct _net_AuthStruct {
    net_AuthType	auth_type;
    char *			path;			/* For normal authentication only */
	char *			proxy_addr;		/* For proxy authentication only */
	char *			username;		/* Obvious */
	char *			password;		/* Not too cryptic either */
	char *			auth_string;	/* Storage for latest Authorization str */
	char *			realm;			/* For all auth schemes */
#ifdef SIMPLE_MD5
    HG82727
#endif
    char *                      challenge;
    HG26250
    char *                      signature;
    char *                      clientRan;
    PRBool                     oldChallenge;
    int                         oldChallenge_retries;
} net_AuthStruct;



/*----------------- Normal client-server authentication ------------------ */

PRIVATE net_AuthStruct *
net_CheckForAuthorization(char * address, Bool exact_match)
{

    char *atSign, *fSlash, *afterProto, *newAddress=NULL;
    char tmp;
    XP_List * list_ptr = net_auth_list;
    net_AuthStruct * auth_s;

	TRACEMSG(("net_CheckForAuthorization: checking for auth on: %s", address));

    /* Auth struct->path doesn't contain username/password info (such as
    * http://uname:pwd@host.com, so make sure we don't compare an address
    * passed in with one with an auth struct->path until we remove/reduce the 
    * address passed in. */
    if( (afterProto=PL_strstr(address, "://")) ) {
    afterProto=afterProto+3;
    tmp=*afterProto;
    *afterProto='\0';
    StrAllocCopy(newAddress, address);
    *afterProto=tmp;

    /* temporarily truncate after first slash, if any. */
    if( (fSlash=PL_strchr(afterProto, '/')) )
      *fSlash='\0';
    atSign=PL_strchr(afterProto, '@');
    if(fSlash)
      *fSlash='/';
    if(atSign)
      StrAllocCat(newAddress, atSign+1);
    else
      StrAllocCat(newAddress, afterProto);
    }

    while((auth_s = (net_AuthStruct *) XP_ListNextObject(list_ptr))!=0)
      {
        XP_ASSERT(newAddress);
        if(!newAddress)
            return NULL;

		if(exact_match)
		  {
            if(!PL_strcmp(address, auth_s->path)) {
                XP_FREE(newAddress);       
                return(auth_s);
            }
		  }
		else
		  {
			/* shorter strings always come last so there can be no
			 * ambiquity
			 */
            if(!PL_strncasecmp(address, auth_s->path, PL_strlen(auth_s->path))) {
                XP_FREE(newAddress);       
                return(auth_s);
            }
		  }
      }
    XP_FREE(newAddress);
	HG25262
   
    return(NULL);
}

/* returns TRUE if authorization is required
 */
PUBLIC Bool
NET_AuthorizationRequired(char * address)
{
    net_AuthStruct * rv;
	char * last_slash = PL_strrchr(address, '/');

	if(last_slash)
		*last_slash = '\0';

	rv = net_CheckForAuthorization(address, FALSE);

	HG17993
	if(last_slash)
		*last_slash = '/';

	if(!rv)
		return(FALSE);
	else
		return(TRUE);
}

/* returns a authorization string if one is required, otherwise
 * returns NULL
 */
PUBLIC char *
NET_BuildAuthString(MWContext * context, URL_Struct *URL_s)
{
	char * address = URL_s->address;
	net_AuthStruct * auth_s = net_CheckForAuthorization(address, FALSE);

	if(!auth_s)
#if defined(XP_WIN) && defined(MOZILLA_CLIENT)
		return(WFE_BuildCompuserveAuthString(URL_s));
#else
		return(NULL);
#endif
	else
	  {
	  	static char * auth_header = 0;
		
		if(auth_header)
			PR_Free(auth_header);
		auth_header = PR_smprintf("Authorization: Basic %s"CRLF, auth_s->auth_string);
		return(auth_header);
	  }
}

PRIVATE net_AuthStruct *
net_ScanForHostnameRealmMatch(char * address, char * realm)
{
	char * proto_host = NET_ParseURL(address, GET_HOST_PART | GET_PROTOCOL_PART);
    XP_List * list_ptr = net_auth_list;
    net_AuthStruct * auth_s;

    while((auth_s = (net_AuthStruct *) XP_ListNextObject(list_ptr))!=0)
      {
        /* shorter strings always come last so there can be no
         * ambiquity
         */
        if(!PL_strncasecmp(proto_host, auth_s->path, PL_strlen(proto_host))
			&& !PL_strcasecmp(realm, auth_s->realm))
		{
			PR_Free(proto_host);
            return(auth_s);
		}
      }
	PR_Free(proto_host);
    return(NULL);
}

PRIVATE void
net_free_auth_struct(net_AuthStruct *auth)
{
    PR_Free(auth->path);
    PR_Free(auth->proxy_addr);
    PR_Free(auth->username);
    PR_Free(auth->password);
    PR_Free(auth->auth_string);
    PR_Free(auth->realm);
    /*FORTEZZA related stuff   */
    PR_FREEIF(auth->challenge);
    HG26763
    PR_FREEIF(auth->signature);
    PR_FREEIF(auth->clientRan);
    /*End FORTEZZA related stuff*/
    PR_Free(auth);
}

/* blows away all authorization structs currently in the list, and the list itself.
 * frees and nulls the list pointer itself (net_auth_list)
 */
PUBLIC void
NET_RemoveAllAuthorizations()
{
	net_AuthStruct * victim;
	if(XP_ListIsEmpty(net_auth_list)) /* XP_ListIsEmpty handles null list */
		return;

    while((victim = (net_AuthStruct *) XP_ListNextObject(net_auth_list)) != 0)
		net_free_auth_struct(victim);
	XP_ListDestroy(net_auth_list);
	net_auth_list = NULL;
}

PRIVATE void
net_remove_exact_auth_match_on_cancel(net_AuthStruct *prev_auth, char *cur_path)
{
	if(!prev_auth || !cur_path)
		return;

	if(!PL_strcmp(prev_auth->path, cur_path))
      {
        /* if the paths are exact and the user cancels
         * remove the mapping
         */
        XP_ListRemoveObject(net_auth_list, prev_auth);
		net_free_auth_struct(prev_auth);
	  }
}

#define HTTP_PW_MODULE_NAME  "http_pw"
#define HTTP_PW_NAME_TOKEN            "name"
#define HTTP_PW_PASSWORD_TOKEN        "pass"

PRIVATE char *
gen_http_key(char *address, char *realm)
{
	char *rv=NULL;

	StrAllocCopy(rv, address);
	StrAllocCat(rv, "\t");
	StrAllocCat(rv, realm);

	return rv;
}

PRIVATE void
separate_http_key(char *key, char **address, char **realm)
{
	char *tab;

	HG72294
		
	*address = NULL;
	*realm = NULL;

	if(!key)
		return;

	tab = PL_strchr(key, '\t');

	if(!tab)
		return;

	*address = key;
	*realm = tab+1;
}

PRIVATE void
net_store_http_password(char *address, char *realm, char *username, char *password)
{
	char *key;
	PCNameValueArray *array = PC_NewNameValueArray();

	if(!array)
		return;

	PC_AddToNameValueArray(array, HTTP_PW_NAME_TOKEN, username);	
	PC_AddToNameValueArray(array, HTTP_PW_PASSWORD_TOKEN, password);	

	key = gen_http_key(address, realm);

	if(!key)
		return;

	PC_StorePasswordNameValueArray(HTTP_PW_MODULE_NAME, key, array);

	PR_Free(key);

}

PRIVATE void
net_remove_stored_http_password(char *url)
{
	PC_DeleteStoredPassword(HTTP_PW_MODULE_NAME, url);
}

MODULE_PRIVATE void
net_http_password_data_interp(
        char *module,
        char *key,
        char *data, int32 data_len,
        char *type_buffer, int32 type_buffer_size,
        char *url_buffer, int32 url_buffer_size,
        char *username_buffer, int32 username_buffer_size,
        char *password_buffer, int32 password_buffer_size)
{
	PCNameValueArray * array;
	char *username, *password;
	char *address, *realm;

	array = PC_CharToNameValueArray(data, data_len);

	if(!array)
		return;

	username = PC_FindInNameValueArray(array, HTTP_PW_NAME_TOKEN);
	password = PC_FindInNameValueArray(array, HTTP_PW_PASSWORD_TOKEN);
	
	PL_strncpyz(type_buffer, "HTTP basic authorization", type_buffer_size);

	separate_http_key(key, &address, &realm);

	if(address)
	{
		PL_strncpyz(url_buffer, address, url_buffer_size);
	}

	if(username)
	{
		PL_strncpyz(username_buffer, username, username_buffer_size);
		PR_Free(username);
	}
	
	if(password)
	{
		PL_strncpyz(password_buffer, password, password_buffer_size);
		PR_Free(password);
	}
}

PRIVATE void
net_initialize_http_access(void)
{
	/* register PW cache interp function */
	
	PC_RegisterDataInterpretFunc(HTTP_PW_MODULE_NAME, 	
				     net_http_password_data_interp);
	HG21522
}

/* returns false if the user wishes to cancel authorization
 * and TRUE if the user wants to continue with a new authorization
 * string
 */
/* HARDTS: I took a whack at fixing up some of the strings leaked in this 
 * function.  All the PR_FREEIF()s are new. 
 */
PUBLIC Bool 
NET_AskForAuthString(MWContext *context,
					 URL_Struct * URL_s, 
					 char * authenticate, 
					 char * prot_template,
					 Bool   already_sent_auth)
{
	static PRBool first_time=TRUE;
	net_AuthStruct *prev_auth;
	char *address=URL_s->address;
	char *host=NET_ParseURL(address, GET_HOST_PART);
	char *new_address=NULL;
	char *username=NULL,*colon=NULL,*password=NULL,*unamePwd=NULL;
	char *u_pass_string=NULL;
	char *auth_string=NULL;
	char *realm;
	char *slash;
	char *authenticate_header_value;
	char *buf=NULL;
	int32 len=0;
	int status;
	PRBool re_authorize=FALSE;

	TRACEMSG(("Entering NET_AskForAuthString"));

	if(first_time)
	{
		net_initialize_http_access();
		first_time = FALSE;
	}

	if(authenticate)
	  {
		/* check for the compuserve Remote-Passphrase type of
	 	 * authentication
	 	 */
		authenticate_header_value = XP_StripLine(authenticate);
		HG03937

#define COMPUSERVE_HEADER_NAME "Remote-Passphrase"

		if(!PL_strncasecmp(authenticate_header_value, 
					 COMPUSERVE_HEADER_NAME, 
					 sizeof(COMPUSERVE_HEADER_NAME) - 1))
	  	{
	  		/* This is a compuserv style header 
	  	 	*/

		PR_FREEIF(host);
#if defined(XP_WIN) && defined(MOZILLA_CLIENT)
		return(WFE_DoCompuserveAuthenticate(context, URL_s, authenticate_header_value));
#else
		return(NET_AUTH_FAILED_DISPLAY_DOCUMENT);		
#endif	
	  	}			 
#define HTTP_BASIC_AUTH_TOKEN "BASIC"
		else if(PL_strncasecmp(authenticate_header_value, 
					 HTTP_BASIC_AUTH_TOKEN, 
					 sizeof(HTTP_BASIC_AUTH_TOKEN) - 1))
		{
			HG21632
			/* unsupported auth type */
			return(NET_AUTH_FAILED_DISPLAY_DOCUMENT);		
		}
	  }

	new_address = NET_ParseURL(address,	GET_PROTOCOL_PART | GET_HOST_PART | GET_PATH_PART);
	if(!new_address) {
		PR_FREEIF(host);
		return NET_AUTH_FAILED_DISPLAY_DOCUMENT;
	}

    /* get the username and password from the URL struct */
    if( URL_s->password && *URL_s->password ) {
        password = PL_strdup(URL_s->password);
    }

    if( URL_s->username && *URL_s->username ) {
        username = PL_strdup(URL_s->username);
    } else {
	    /* See if username and password are still in the address */
        unamePwd=NET_ParseURL(address, GET_USERNAME_PART | GET_PASSWORD_PART);
	    /* get the username & password out of the combo string */
	    if( (colon = PL_strchr(unamePwd, ':')) != NULL ) {
		    *colon='\0';
		    username=PL_strdup(unamePwd);
            /* Use password we found above? */
            if( password == NULL ) {
    		    password=PL_strdup(colon+1);
            }

		    *colon=':';
		    PR_Free(unamePwd);
	    } else {
		    username=unamePwd;
	    }
    }


	/*if last char is not a slash then */
	if (new_address[PL_strlen(new_address)-1] != '/')
	{
		/* remove everything after the last slash */
		slash = PL_strrchr(new_address, '/');
		if(++slash)
			*slash = '\0';
	}

	if(!authenticate)
	  {
		realm = "unknown";
	  }
	else
	  {
		realm = PL_strchr(authenticate, '"');
	
		if(realm)
		  {
			realm++;

			/* terminate at next quote */
			strtok(realm, "\"");

#define MAX_REALM_SIZE 128
			if(PL_strlen(realm) > MAX_REALM_SIZE)
				realm[MAX_REALM_SIZE] = '\0';
	
      }
		else
	      {
		    realm = "unknown";
	 	  }
		
    }

	/* no hostname/realm match search for exact match */
    prev_auth = net_CheckForAuthorization(new_address, FALSE);

    if(prev_auth && !already_sent_auth)
      {
		/* somehow the mapping changed since the time we sent
		 * the authorization.
		 * This happens sometimes because of the parrallel
		 * nature of the requests.
		 * In this case we want to just retry the connection
		 * since it will probably succede now.
		 */
		HG22220
		PR_FREEIF(host);
		PR_FREEIF(new_address);
		PR_FREEIF(username);
		PR_FREEIF(password);
		return(NET_RETRY_WITH_AUTH);
      }
    else if(prev_auth)
      {
		/* we sent the authorization string and it was wrong
		 */
        if(!FE_Confirm(context, XP_GetString(XP_CONFIRM_AUTHORIZATION_FAIL)))
		  {
        	TRACEMSG(("User canceled login!!!"));

			if(!PL_strcmp(prev_auth->path, new_address))
			  {
				/* if the paths are exact and the user cancels
				 * remove the mapping
				 */
				net_remove_exact_auth_match_on_cancel(prev_auth, new_address);
				PR_FREEIF(host);
				PR_FREEIF(new_address);
				PR_FREEIF(username);
				PR_FREEIF(password);
            	return(NET_AUTH_FAILED_DISPLAY_DOCUMENT);
			  }
		  }
		
		if (!username)
			username = PL_strdup(prev_auth->username);
		if (!password)
			password = PL_strdup(prev_auth->password);
		re_authorize = TRUE;
      }
	else
	  {
		char *ptr1, *ptr2;

        /* scan all the authorization strings to see if we
         * can find a hostname and realm match.  If we find
         * a match then reduce the authorization path to include
         * the current path as well.
         */
        prev_auth = net_ScanForHostnameRealmMatch(address, realm);
    
        if(prev_auth)
          {
		char *tmp;
		int skipProtoHost;

		net_remove_stored_http_password(prev_auth->path);

		tmp = NET_ParseURL(prev_auth->path, GET_HOST_PART | GET_PROTOCOL_PART);
		skipProtoHost = PL_strlen(tmp);
		PR_ASSERT(!PL_strncasecmp(
			tmp, 
			NET_ParseURL(new_address, GET_HOST_PART | GET_PROTOCOL_PART), 
			skipProtoHost));
		PR_Free(tmp);

            /* compare the two url paths until they deviate
             * once they deviate truncate.
             * skip ptr1, ptr2 past the host names 
             * which we have already compared
             */
            for(ptr1 = prev_auth->path + skipProtoHost, ptr2 = new_address + skipProtoHost; *ptr1 && *ptr2; ptr1++, ptr2++)
			  {
                if(*ptr1 != *ptr2)
                  {
					break;        /* end for */
                  }
			  }
			/* truncate at *ptr1 now since the new address may
			 * be just a subpath of the original address and
			 * the compare above will not handle the subpath correctly
			 */
			*ptr1 = '\0'; /* truncate */

			if(*(ptr1-1) == '/')
				*(ptr1-1) = '\0'; /* strip a trailing slash */

			/* make sure a path always has at least a slash
			 * at the end.
			 * If the slash isn't there then
			 * the password will be sent to ports on the
			 * same host since we use a first part match
			 */
			tmp = NET_ParseURL(prev_auth->path, GET_PATH_PART);
			if(!*tmp)
				StrAllocCat(prev_auth->path, "/");
			PR_Free(tmp);				

			TRACEMSG(("Truncated new auth path to be: %s", prev_auth->path));

			net_store_http_password(prev_auth->path, prev_auth->realm, prev_auth->username, prev_auth->password);

			PR_Free(host);
			PR_Free(new_address);
			return(NET_RETRY_WITH_AUTH);
          }
	  }
					 
	/* Use username and/or password specified in URL_struct if exists. */
	if (!username && URL_s->username && *URL_s->username) {
		username = PL_strdup(URL_s->username);
	}
	if (!password && URL_s->password && *URL_s->password) {
		password = PL_strdup(URL_s->password);
	}

	if(!username && !password)
	{
		/* look for a previously stored password in the pw cache */
		PCNameValueArray *array;

		array = PC_CheckForStoredPasswordArray(HTTP_PW_MODULE_NAME, URL_s->address);

		if(array)
		{
			username = PC_FindInNameValueArray(array, HTTP_PW_NAME_TOKEN);
			password = PC_FindInNameValueArray(array, HTTP_PW_PASSWORD_TOKEN);

			if(!username)
			{
				PR_FREEIF(password);
				password = NULL;
			}
		}
	}

	/* if the password is filled in then the username must
	 * be filled in already.  
	 */
	if(!password || re_authorize)
	  {
		XP_Bool remember_password = FALSE;
        char *loginString=XP_GetString(XP_ACCESS_ENTER_USERNAME);
	   	host = NET_ParseURL(address, GET_HOST_PART);

		/* malloc memory here to prevent buffer overflow */
		len = XP_STRLEN(loginString) + XP_STRLEN(realm) + XP_STRLEN(host) + 10;
		buf = (char *)PR_Malloc(len*sizeof(char));
		
		if(buf) {
            PR_snprintf( buf, len*sizeof(char), loginString, realm, host);


			NET_Progress(context, XP_GetString( XP_CONNECT_PLEASE_ENTER_PASSWORD_FOR_HOST) );
#if defined(SingleSignon)
			/* prefill prompt with previous username/passwords if any 
             * this returns 1 if user pressed OK, or 0 if they Canceled
            */
			status = SI_PromptUsernameAndPassword
			    (context, buf, &username, &password, URL_s->address);

            /* Tell the Editor the correct username */
            if( status > 0 ) {
                EDT_SavePublishUsername(context, address, username);
            }
#else
			status = PC_PromptUsernameAndPassword
			    (context, buf, &username, &password,
			    &remember_password, NET_IsURLSecure(URL_s->address));
#endif
	
			PR_Free(buf);
        } else {
	        status = 0;
		}

		PR_Free(host);

		if(!status) {
			TRACEMSG(("User canceled login!!!"));

			/* if the paths are exact and the user cancels
			 * remove the mapping
			 */
			net_remove_exact_auth_match_on_cancel(prev_auth, new_address);

			PR_FREEIF(username);
			PR_FREEIF(password);
			PR_FREEIF(new_address);
			return(NET_AUTH_FAILED_DISPLAY_DOCUMENT);
		  }
		else if(!username || !password)
		  {
			PR_FREEIF(username);
			PR_FREEIF(password);
			PR_FREEIF(new_address);
			return(NET_AUTH_FAILED_DISPLAY_DOCUMENT);
		  }
		else if(remember_password)
		{
			net_store_http_password(URL_s->address, realm, username, password);
		}
	  }

	StrAllocCopy(u_pass_string, username);
	StrAllocCat(u_pass_string, ":");
	StrAllocCat(u_pass_string, password);

	len = PL_strlen(u_pass_string);
	auth_string = (char*) PR_Malloc((((len+1)*4)/3)+10);

	if(!auth_string)
	  {
		PR_FREEIF(username);
		PR_FREEIF(password);
		PR_FREEIF(u_pass_string);
		PR_Free(new_address);
		return(NET_RETRY_WITH_AUTH);
	  }

	HG38932
	NET_UUEncode((unsigned char *)u_pass_string, (unsigned char*) auth_string, len);

	PR_Free(u_pass_string);

	if(prev_auth)
	  {
	    PR_FREEIF(prev_auth->auth_string);
        prev_auth->auth_string = auth_string;
		PR_FREEIF(prev_auth->username);
		prev_auth->username = username;
        PR_FREEIF(prev_auth->password);
        prev_auth->password = password;
	  }
	else
	  {
        char *atSign, *host, *fSlash;
		XP_List * list_ptr = net_auth_list;
		net_AuthStruct * tmp_auth_ptr;
		size_t new_len;

		/* construct a new auth_struct
		 */
		prev_auth = PR_NEWZAP(net_AuthStruct);
	    if(!prev_auth)
		  {
			PR_FREEIF(auth_string);
			PR_FREEIF(username);
			PR_FREEIF(password);
		    PR_Free(new_address);
		    return(NET_RETRY_WITH_AUTH);
		  }
		
        prev_auth->auth_string = auth_string;
		prev_auth->username = username;
        prev_auth->password = password;
		prev_auth->path = 0;
       /* Don't save username/password info in the auth struct path. */
        if( (atSign=PL_strchr(new_address, '@')) ) {
          if( (host=PL_strstr(new_address, "://")) ) {
            char tmp;
            host+=3;
            tmp=*host;
            *host='\0';
            StrAllocCopy(prev_auth->path, new_address);
            *host=tmp;
        
            fSlash=PL_strchr(host, '/');
            if(fSlash)
              *fSlash='\0';
            /* Do the atSign check again so we're sure to get one between the 
             * protocol part and the first slash. */
            atSign=PL_strchr(host, '@');
            if(fSlash)
              *fSlash='/';

            if(atSign) {
              StrAllocCat(prev_auth->path, atSign+1);
            } else {
              StrAllocCat(prev_auth->path, host);
            }
          }
        } else {
          StrAllocCopy(prev_auth->path, new_address);
        }

		prev_auth->realm = 0;
		StrAllocCopy(prev_auth->realm, realm);

		if(!net_auth_list)
		  {
			net_auth_list = XP_ListNew();
		    if(!net_auth_list)
			  {
          /* Maybe should free prev_auth here. */
		   		PR_Free(new_address);
				return(NET_RETRY_WITH_AUTH);
			  }
		  }		

		/* add it to the list so that it is before any strings of
		 * smaller length
		 */
		new_len = PL_strlen(prev_auth->path);
		while((tmp_auth_ptr = (net_AuthStruct *) XP_ListNextObject(list_ptr))!=0)
		  { 
			if(new_len > PL_strlen(tmp_auth_ptr->path))
			  {
				XP_ListInsertObject(net_auth_list, tmp_auth_ptr, prev_auth);
		   		PR_Free(new_address);
				return(NET_RETRY_WITH_AUTH);
			  }
		  }
		/* no shorter strings found in list */	
		XP_ListAddObjectToEnd(net_auth_list, prev_auth);
	  }

	PR_Free(new_address);
	return(NET_RETRY_WITH_AUTH);
}

/*--------------------------------------------------
 * The following routines support the 
 * Set-Cookie: / Cookie: headers
 */

PRIVATE XP_List * net_cookie_list=0;
#if defined(CookieManagement)
PRIVATE XP_List * net_cookie_permission_list=0;
PRIVATE XP_List * net_defer_cookie_list=0;
#endif

typedef struct _net_CookieStruct {
    char * path;
	char * host;
	char * name;
    char * cookie;
	time_t expires;
	time_t last_accessed;
	HG26237
	Bool   is_domain;   /* is it a domain instead of an absolute host? */
} net_CookieStruct;

#if defined(CookieManagement)
typedef struct _net_CookiePermissionStruct {
    char * host;
    Bool permission;
#ifdef TRUST_LABELS
    XP_List * TrustList; /* a list of trust label entries */
#endif
} net_CookiePermissionStruct;

typedef struct _net_DeferCookieStruct {
    MWContext * context;
    char * cur_url;
    char * set_cookie_header;
    time_t timeToExpire;
} net_DeferCookieStruct;

#ifdef TRUST_LABELS
void ParseTrustLabelInfo
	(char *Buf, net_CookiePermissionStruct * cookie_permission);
void SaveTrustLabelData
	( net_CookiePermissionStruct * cookie_permission, XP_File fp );
void DeleteCookieFromPermissions( char *CookieHost, char *CookieName );
void AddTrustLabelInfo( char *CookieName, TrustLabel *trustlabel );

typedef struct {
    char * CookieName;
    int purpose; /* the purpose rating has a series of bits set */
    int recipient; /* the recipient value */
    Bool bIdentifiable; /* true if the related cookie puts to identifiable information */
    char * by; /* the by field from the trust label */
    char * CookieSet; /* when the cookie was set into cookies.txt */
} TrustEntry;
#endif

#endif

/* Routines and data to protect the cookie list so it
**   can be accessed by mulitple threads
*/

#include "prthread.h"
#include "prmon.h"

static PRMonitor * cookie_lock_monitor = NULL;
static PRThread  * cookie_lock_owner = NULL;
static int cookie_lock_count = 0;

PRIVATE void
net_lock_cookie_list(void)
{
    if(!cookie_lock_monitor)
	cookie_lock_monitor = PR_NewNamedMonitor("cookie-lock");

    PR_EnterMonitor(cookie_lock_monitor);

    while(TRUE) {

	/* no current owner or owned by this thread */
	PRThread * t = PR_CurrentThread();
	if(cookie_lock_owner == NULL || cookie_lock_owner == t) {
	    cookie_lock_owner = t;
	    cookie_lock_count++;

	    PR_ExitMonitor(cookie_lock_monitor);
	    return;
	}

	/* owned by someone else -- wait till we can get it */
	PR_Wait(cookie_lock_monitor, PR_INTERVAL_NO_TIMEOUT);

    }
}

PRIVATE void
net_unlock_cookie_list(void)
{
   PR_EnterMonitor(cookie_lock_monitor);

#ifdef DEBUG
    /* make sure someone doesn't try to free a lock they don't own */
    PR_ASSERT(cookie_lock_owner == PR_CurrentThread());
#endif

    cookie_lock_count--;

    if(cookie_lock_count == 0) {
	cookie_lock_owner = NULL;
	PR_Notify(cookie_lock_monitor);
    }
    PR_ExitMonitor(cookie_lock_monitor);

}

#if defined(CookieManagement)
static PRMonitor * defer_cookie_lock_monitor = NULL;
static PRThread  * defer_cookie_lock_owner = NULL;
static int defer_cookie_lock_count = 0;

PRIVATE void
net_lock_defer_cookie_list(void)
{
    if(!defer_cookie_lock_monitor)
	defer_cookie_lock_monitor =
            PR_NewNamedMonitor("defer_cookie-lock");

    PR_EnterMonitor(defer_cookie_lock_monitor);

    while(TRUE) {

	/* no current owner or owned by this thread */
	PRThread * t = PR_CurrentThread();
	if(defer_cookie_lock_owner == NULL || defer_cookie_lock_owner == t) {
	    defer_cookie_lock_owner = t;
	    defer_cookie_lock_count++;

	    PR_ExitMonitor(defer_cookie_lock_monitor);
	    return;
	}

	/* owned by someone else -- wait till we can get it */
	PR_Wait(defer_cookie_lock_monitor, PR_INTERVAL_NO_TIMEOUT);

    }
}

PRIVATE void
net_unlock_defer_cookie_list(void)
{
   PR_EnterMonitor(defer_cookie_lock_monitor);

#ifdef DEBUG
    /* make sure someone doesn't try to free a lock they don't own */
    PR_ASSERT(defer_cookie_lock_owner == PR_CurrentThread());
#endif

    defer_cookie_lock_count--;

    if(defer_cookie_lock_count == 0) {
	defer_cookie_lock_owner = NULL;
	PR_Notify(defer_cookie_lock_monitor);
    }
    PR_ExitMonitor(defer_cookie_lock_monitor);

}

static PRMonitor * cookie_permission_lock_monitor = NULL;
static PRThread  * cookie_permission_lock_owner = NULL;
static int cookie_permission_lock_count = 0;

PRIVATE void
net_lock_cookie_permission_list(void)
{
    if(!cookie_permission_lock_monitor)
	cookie_permission_lock_monitor =
            PR_NewNamedMonitor("cookie_permission-lock");

    PR_EnterMonitor(cookie_permission_lock_monitor);

    while(TRUE) {

	/* no current owner or owned by this thread */
	PRThread * t = PR_CurrentThread();
	if(cookie_permission_lock_owner == NULL || cookie_permission_lock_owner == t) {
	    cookie_permission_lock_owner = t;
	    cookie_permission_lock_count++;

	    PR_ExitMonitor(cookie_permission_lock_monitor);
	    return;
	}

	/* owned by someone else -- wait till we can get it */
	PR_Wait(cookie_permission_lock_monitor, PR_INTERVAL_NO_TIMEOUT);

    }
}

PRIVATE void
net_unlock_cookie_permission_list(void)
{
   PR_EnterMonitor(cookie_permission_lock_monitor);

#ifdef DEBUG
    /* make sure someone doesn't try to free a lock they don't own */
    PR_ASSERT(cookie_permission_lock_owner == PR_CurrentThread());
#endif

    cookie_permission_lock_count--;

    if(cookie_permission_lock_count == 0) {
	cookie_permission_lock_owner = NULL;
	PR_Notify(cookie_permission_lock_monitor);
    }
    PR_ExitMonitor(cookie_permission_lock_monitor);

}

PRIVATE int
net_SaveCookiePermissions(char * filename);

PRIVATE void
net_FreeCookiePermission
    (net_CookiePermissionStruct * cookie_permission, Bool save)
{

    /*
     * This routine should only be called while holding the
     * cookie permission list lock
     */

    if(!cookie_permission) {
	return;
    }
    XP_ListRemoveObject(net_cookie_permission_list, cookie_permission);
    PR_FREEIF(cookie_permission->host);

#ifdef TRUST_LABELS
    /* free the entries on the trust list */
    if ( cookie_permission->TrustList ) {
        TrustEntry * TEntry;
        XP_List *Tptr = cookie_permission->TrustList;
        while( (TEntry = (TrustEntry *)XP_ListNextObject( Tptr )) ) {
            PR_FREEIF(TEntry->CookieName);
            PR_FREEIF(TEntry->by);
            PR_FREEIF(TEntry->CookieSet);
            /* before removing the current entry get the ptr to the next one */
            Tptr = Tptr->prev;
            XP_ListRemoveObject(cookie_permission->TrustList, TEntry);
        }
        XP_ListDestroy(cookie_permission->TrustList);
    }
#endif

    PR_Free(cookie_permission);
    if (save) {
	cookie_permissions_changed = TRUE;
	net_SaveCookiePermissions(NULL);
    }
}

/* blows away all cookie permissions currently in the list */
PRIVATE void
net_RemoveAllCookiePermissions()
{
    net_CookiePermissionStruct * victim;
    XP_List * cookiePermissionList;

    /* check for NULL or empty list */
    net_lock_cookie_permission_list();
    cookiePermissionList = net_cookie_permission_list;

    if(XP_ListIsEmpty(cookiePermissionList)) {
	net_unlock_cookie_permission_list();
	return;
    }
    while((victim =
	    (net_CookiePermissionStruct *)
	    XP_ListNextObject(cookiePermissionList)) != 0) {
	net_FreeCookiePermission(victim, FALSE);
	cookiePermissionList = net_cookie_permission_list;
    }
    XP_ListDestroy(net_cookie_permission_list);
    net_cookie_permission_list = NULL;
    net_unlock_cookie_permission_list();
}
#endif


/* This should only get called while holding the cookie-lock
**
*/
PRIVATE void
net_FreeCookie(net_CookieStruct * cookie)
{

	if(!cookie)
		return;
#ifdef TRUST_LABELS
        /*
         * delete the reference to the cookie in the cookie permission list
         * which contains the trust label for the cookie
         */
        DeleteCookieFromPermissions(cookie->host, cookie->name );
#endif
	XP_ListRemoveObject(net_cookie_list, cookie);

	PR_FREEIF(cookie->path);
	PR_FREEIF(cookie->host);
	PR_FREEIF(cookie->name);
	PR_FREEIF(cookie->cookie);

	PR_Free(cookie);

	cookies_changed = TRUE;
}


PUBLIC void
NET_DeleteCookie(char* cookieURL)
{
    net_CookieStruct * cookie;
    XP_List * list_ptr;
    char * cookieURL2 = 0;

    net_lock_cookie_list();
    list_ptr = net_cookie_list;
    while((cookie = (net_CookieStruct *) XP_ListNextObject(list_ptr))!=0) {
        StrAllocCopy(cookieURL2, "cookie:");
	StrAllocCat(cookieURL2, cookie->host);
        StrAllocCat(cookieURL2, "!");
	StrAllocCat(cookieURL2, cookie->path);
        StrAllocCat(cookieURL2, "!");
	StrAllocCat(cookieURL2, cookie->name);
	if (PL_strcmp(cookieURL, cookieURL2)==0) {
	    net_FreeCookie(cookie);
	    break;
	}
    }
    PR_FREEIF(cookieURL2);
    net_unlock_cookie_list();
}


/* blows away all cookies currently in the list, then blows away the list itself
 * nulling it after it's free'd
 */
PRIVATE void
net_RemoveAllCookies()
{
	net_CookieStruct * victim;
	XP_List * cookieList;

	/* check for NULL or empty list */
	net_lock_cookie_list();
	cookieList = net_cookie_list;
	if(XP_ListIsEmpty(cookieList)) {
		net_unlock_cookie_list();
		return;
	}

    while((victim = (net_CookieStruct *) XP_ListNextObject(cookieList)) != 0) {
		net_FreeCookie(victim);
		cookieList=net_cookie_list;
	}
	XP_ListDestroy(net_cookie_list);
	net_cookie_list = NULL;
	net_unlock_cookie_list();
}

PUBLIC void
NET_RemoveAllCookies()
{

#if defined(CookieManagement)
	net_RemoveAllCookiePermissions();
#endif
	net_RemoveAllCookies();
}

PRIVATE void
net_remove_oldest_cookie(void)
{
    XP_List * list_ptr;
    net_CookieStruct * cookie_s;
    net_CookieStruct * oldest_cookie;

	net_lock_cookie_list();
	list_ptr = net_cookie_list;

	if(XP_ListIsEmpty(list_ptr)) {
		net_unlock_cookie_list();
		return;
	}

	oldest_cookie = (net_CookieStruct*) list_ptr->next->object;
	
    while((cookie_s = (net_CookieStruct *) XP_ListNextObject(list_ptr))!=0)
      {
		if(cookie_s->last_accessed < oldest_cookie->last_accessed)
			oldest_cookie = cookie_s;
	  }

	if(oldest_cookie)
	  {
		TRACEMSG(("Freeing cookie because global max cookies has been exceeded"));
	    net_FreeCookie(oldest_cookie);
	  }
	net_unlock_cookie_list();
}

/* Remove any expired cookies from memory 
** This routine should only be called while holding the cookie list lock
*/
PRIVATE void
net_remove_expired_cookies(void)
{
    XP_List * list_ptr = net_cookie_list;
    net_CookieStruct * cookie_s;
	time_t cur_time = time(NULL);

	if(XP_ListIsEmpty(list_ptr))
		return;

    while((cookie_s = (net_CookieStruct *) XP_ListNextObject(list_ptr))!=0)
      {
		/* Don't get rid of expire time 0 because these need to last for 
		 * the entire session. They'll get cleared on exit.
		 */
		if( cookie_s->expires && (cookie_s->expires < cur_time) ) {
			net_FreeCookie(cookie_s);
			/* Reset the list_ptr to the beginning of the list.
			 * Do this because list_ptr's object was just freed
			 * by the call to net_FreeCookie struct, even
			 * though it's inefficient.
			 */
			list_ptr = net_cookie_list;
		}
	  }
}

PRIVATE XP_List * net_dormant_cookie_list=0;
Bool net_anonymous = FALSE;

PUBLIC void
NET_AnonymizeCookies()
{
    if (!net_anonymous) {
	net_lock_cookie_list();
	net_dormant_cookie_list = net_cookie_list;
	net_cookie_list = XP_ListNew();
	net_unlock_cookie_list();
	net_anonymous = TRUE;
    }
}

PUBLIC void
NET_UnanonymizeCookies()
{
    if (net_anonymous) {
	net_RemoveAllCookies();
	net_lock_cookie_list();
	net_cookie_list = net_dormant_cookie_list;
	net_unlock_cookie_list();
	net_dormant_cookie_list = 0;
	net_anonymous = FALSE;
    }
}

/*
 * Determine whether or not to suppress the referer field for anonymity.
 * The first time this function is called on behalf of a given window
 * after a mode change (into or out of anonymous mode), the returned value is
 * true.
 */
PUBLIC Bool
NET_SupressRefererForAnonymity(MWContext *window_id) {
   MWContext *outermost_window = window_id;
   Bool result;
   if (!window_id) {
      return FALSE;
   }
   while (outermost_window->grid_parent) {
      outermost_window = outermost_window->grid_parent;
   }
   result = (window_id->anonymous != net_anonymous);
   window_id->anonymous = net_anonymous;
   return result;
}

/* checks to see if the maximum number of cookies per host
 * is being exceeded and deletes the oldest one in that
 * case
 * This routine should only be called while holding the cookie lock
 */
PRIVATE void
net_CheckForMaxCookiesFromHost(const char * cur_host)
{
    XP_List * list_ptr = net_cookie_list;
    net_CookieStruct * cookie_s;
    net_CookieStruct * oldest_cookie = 0;
	int cookie_count = 0;

    if(XP_ListIsEmpty(list_ptr))
        return;

    while((cookie_s = (net_CookieStruct *) XP_ListNextObject(list_ptr))!=0)
      {
	    if(!PL_strcasecmp(cookie_s->host, cur_host))
		  {
			cookie_count++;
			if(!oldest_cookie 
				|| oldest_cookie->last_accessed > cookie_s->last_accessed)
                oldest_cookie = cookie_s;
		  }
      }

	if(cookie_count >= MAX_COOKIES_PER_SERVER && oldest_cookie)
	  {
		TRACEMSG(("Freeing cookie because max cookies per server has been exceeded"));
        net_FreeCookie(oldest_cookie);
	  }
}


/* search for previous exact match
** This routine should only be called while holding the cookie lock
*/
PRIVATE net_CookieStruct *
net_CheckForPrevCookie(char * path,
                   char * hostname,
                   char * name)
{

    XP_List * list_ptr = net_cookie_list;
    net_CookieStruct * cookie_s;

    while((cookie_s = (net_CookieStruct *) XP_ListNextObject(list_ptr))!=0) {
        if(path && hostname && cookie_s->path && cookie_s->host && cookie_s->name
                && !PL_strcmp(name, cookie_s->name)
                && !PL_strcmp(path, cookie_s->path)
                && !PL_strcasecmp(hostname, cookie_s->host))
            return(cookie_s);
    }
    return(NULL);
}

/* cookie utility functions */
PRIVATE void
NET_SetCookieBehaviorPref(NET_CookieBehaviorEnum x)
{
	net_CookieBehavior = x;

	HG83330
	if(net_CookieBehavior == NET_DontUse) {
		NET_XP_FileRemove("", xpHTTPCookie);
#if defined(CookieManagement)
		NET_XP_FileRemove("", xpHTTPCookiePermission);
#endif
	}
}

PRIVATE void
NET_SetCookieWarningPref(Bool x)
{
	net_WarnAboutCookies = x;
}

PRIVATE void
NET_SetCookieScriptPref(const char *name)
{
    PR_FREEIF(net_scriptName);
	if( name && *name )
		net_scriptName=PL_strdup(name);
	else
		net_scriptName=NULL;
}

PRIVATE NET_CookieBehaviorEnum
NET_GetCookieBehaviorPref(void)
{
	return net_CookieBehavior;
}

PRIVATE Bool
NET_GetCookieWarningPref(void)
{
	return net_WarnAboutCookies;
}

PRIVATE const char *
NET_GetCookieScriptPref(void)
{
    return (const char *)net_scriptName;
}

MODULE_PRIVATE int PR_CALLBACK
NET_CookieBehaviorPrefChanged(const char * newpref, void * data)
{
	int32	n;
    if ( (PREF_OK != PREF_GetIntPref(pref_cookieBehavior, &n)) ) {
        n = DEF_COOKIE_BEHAVIOR;
    }
	NET_SetCookieBehaviorPref((NET_CookieBehaviorEnum)n);
	return PREF_NOERROR;
}

MODULE_PRIVATE int PR_CALLBACK
NET_CookieWarningPrefChanged(const char * newpref, void * data)
{
	Bool	x;
    if ( (PREF_OK != PREF_GetBoolPref(pref_warnAboutCookies, &x)) ) {
        x = FALSE;
    }
	NET_SetCookieWarningPref(x);
	return PREF_NOERROR;
}

MODULE_PRIVATE int PR_CALLBACK
NET_CookieScriptPrefChanged(const char * newpref, void * data)
{
    char    s[64];
    int len = sizeof(s);
	*s='\0';
    PREF_GetCharPref(pref_scriptName, s, &len);
    NET_SetCookieScriptPref(s);
    return PREF_NOERROR;
}
 
#if defined(CookieManagement)

/*
 * search if permission already exists
 */
PRIVATE net_CookiePermissionStruct *
net_CheckForCookiePermission(char * hostname) {
    XP_List * list_ptr;
    net_CookiePermissionStruct * cookie_s;

    /* ignore leading period in host name */
    while (hostname && (*hostname == '.')) {
	hostname++;
    }

    net_lock_cookie_permission_list();
    list_ptr = net_cookie_permission_list;
    while((cookie_s = (net_CookiePermissionStruct *) XP_ListNextObject(list_ptr))!=0) {
	if(hostname && cookie_s->host
		&& !PL_strcasecmp(hostname, cookie_s->host)) {
	    net_unlock_cookie_permission_list();
	    return(cookie_s);
        }
    }
    net_unlock_cookie_permission_list();
    return(NULL);
}

/*
 * return:
 *  +1 if cookie permission exists for host and indicates host can set cookies
 *   0 if cookie permission does not exist for host
 *  -1 if cookie permission exists for host and indicates host can't set cookies
 */
PUBLIC int
NET_CookiePermission(MWContext * context) {
    net_CookiePermissionStruct * cookiePermission;
    char * host;
    char * colon;

    if (!context || !(context->hist.cur_doc_ptr) ||
	    !(context->hist.cur_doc_ptr->address)) {
	return 0;
    }

    /* remove protocol from URL name */
    host = NET_ParseURL(context->hist.cur_doc_ptr->address, GET_HOST_PART);

    /* remove port number from URL name */
    colon = PL_strchr(host, ':');
    if(colon) {
        *colon = '\0';
    }
    cookiePermission = net_CheckForCookiePermission(host);
    if(colon) {
        *colon = ':';
    }
    PR_Free(host);
    if (cookiePermission == NULL) {
	return 0;
    }
    if (cookiePermission->permission) {
	return 1;
    }
    return -1;
}

#endif

/* called from mkgeturl.c, NET_InitNetLib(). This sets the module local cookie pref variables
   and registers the callbacks */
PUBLIC void
NET_RegisterCookiePrefCallbacks(void)
{
	int32	n;
	Bool	x;
    char    s[64];
    int len = sizeof(s);
    *s = '\0';

    if ( (PREF_OK != PREF_GetIntPref(pref_cookieBehavior, &n)) ) {
        n = DEF_COOKIE_BEHAVIOR;
    }
	NET_SetCookieBehaviorPref((NET_CookieBehaviorEnum)n);
	PREF_RegisterCallback(pref_cookieBehavior, NET_CookieBehaviorPrefChanged, NULL);

    if ( (PREF_OK != PREF_GetBoolPref(pref_warnAboutCookies, &x)) ) {
        x = FALSE;
    }
	NET_SetCookieWarningPref(x);
	PREF_RegisterCallback(pref_warnAboutCookies, NET_CookieWarningPrefChanged, NULL);

	*s='\0';
	PREF_GetCharPref(pref_scriptName, s, &len);
	NET_SetCookieScriptPref(s);
	PREF_RegisterCallback(pref_scriptName, NET_CookieScriptPrefChanged, NULL);
}

/* returns TRUE if authorization is required
** 
**
** IMPORTANT:  Now that this routine is mutli-threaded it is up
**             to the caller to free any returned string
*/
PUBLIC char *
NET_GetCookie(MWContext * context, char * address)
{
	char *name=0;
	char *host = NET_ParseURL(address, GET_HOST_PART);
	char *path = NET_ParseURL(address, GET_PATH_PART);
    XP_List * list_ptr;
    net_CookieStruct * cookie_s;
	Bool first=TRUE;
	HG26748
	time_t cur_time = time(NULL);
	int host_length;
	int domain_length;

	/* return string to build */
	char * rv=0;

	/* disable cookie's if the user's prefs say so
	 */
	if(NET_GetCookieBehaviorPref() == NET_DontUse)
		return NULL;

	HG98476

	/* search for all cookies
	 */
	net_lock_cookie_list();
	list_ptr = net_cookie_list;
    while((cookie_s = (net_CookieStruct *) XP_ListNextObject(list_ptr))!=0)
      {
		if(!cookie_s->host)
			continue;

		/* check the host or domain first
		 */
		if(cookie_s->is_domain)
		  {
			char *cp;
			domain_length = PL_strlen(cookie_s->host);

			/* calculate the host length by looking at all characters up to
			 * a colon or '\0'.  That way we won't include port numbers
			 * in domains
			 */
			for(cp=host; *cp != '\0' && *cp != ':'; cp++)
				; /* null body */ 

			host_length = cp - host;
			if(domain_length > host_length 
				|| PL_strncasecmp(cookie_s->host, 
								&host[host_length - domain_length], 
								domain_length))
			  {
				HG20476
				/* no match. FAIL 
				 */
				continue;
			  }
			
		  }
		else if(PL_strcasecmp(host, cookie_s->host))
		  {
			/* hostname matchup failed. FAIL
			 */
			continue;
		  }

        /* shorter strings always come last so there can be no
         * ambiquity
         */
        if(cookie_s->path && !PL_strncmp(path,
                                         cookie_s->path,
                                         PL_strlen(cookie_s->path)))
          {

			/* if the cookie is secure and the path isn't
			 * dont send it
			 */
			HG83764
			
			/* check for expired cookies
			 */
			if( cookie_s->expires && (cookie_s->expires < cur_time) )
			  {
				/* expire and remove the cookie 
				 */
		   		net_FreeCookie(cookie_s);

				/* start the list parsing over :(
				 * we must also start the string over
				 */
				PR_FREEIF(rv);
				rv = NULL;
				list_ptr = net_cookie_list;
				first = TRUE; /* reset first */
				continue;
			  }

			if(first)
				first = FALSE;
			else
				StrAllocCat(rv, "; ");
			
			if(cookie_s->name && *cookie_s->name != '\0')
			  {
				cookie_s->last_accessed = cur_time;
            	StrAllocCopy(name, cookie_s->name);
            	StrAllocCat(name, "=");

#ifdef PREVENT_DUPLICATE_NAMES
				/* make sure we don't have a previous
				 * name mapping already in the string
				 */
				if(!rv || !PL_strstr(rv, name))
			      {	
            	    StrAllocCat(rv, name);
            	    StrAllocCat(rv, cookie_s->cookie);
				  }
#else
            	StrAllocCat(rv, name);
                StrAllocCat(rv, cookie_s->cookie);
#endif /* PREVENT_DUPLICATE_NAMES */
			  }
			else
			  {
            	StrAllocCat(rv, cookie_s->cookie);
			  }
          }
	  }

	  net_unlock_cookie_list();
	PR_FREEIF(name);
	PR_Free(path);
	PR_Free(host);

	/* may be NULL */
	return(rv);
}

#if defined(CookieManagement)
void
net_AddCookiePermission
	(net_CookiePermissionStruct * cookie_permission, Bool save ) {

    /*
     * This routine should only be called while holding the
     * cookie permission list lock
     */
    if (cookie_permission) {
	XP_List * list_ptr = net_cookie_permission_list;
	if(!net_cookie_permission_list) {
	    net_cookie_permission_list = XP_ListNew();
	    if(!net_cookie_permission_list) {
		PR_Free(cookie_permission->host);
		PR_Free(cookie_permission);
		return;
	    }
	    list_ptr = net_cookie_permission_list;
	}

#ifdef alphabetize
	/* add it to the list in alphabetical order */
	{
	    net_CookiePermissionStruct * tmp_cookie_permission;
	    Bool permissionAdded = FALSE;
	    while((tmp_cookie_permission = (net_CookiePermissionStruct *)
					   XP_ListNextObject(list_ptr))!=0) {
		if (PL_strcasecmp
			(cookie_permission->host,tmp_cookie_permission->host)<0) {
		    XP_ListInsertObject
			(net_cookie_permission_list,
			tmp_cookie_permission,
			cookie_permission);
		    permissionAdded = TRUE;
		    break;
		}
	    }
	    if (!permissionAdded) {
		XP_ListAddObjectToEnd
		    (net_cookie_permission_list, cookie_permission);
	    }
	}
#else
	/* add it to the end of the list */
	XP_ListAddObjectToEnd (net_cookie_permission_list, cookie_permission);
#endif

	if (save) {
	    cookie_permissions_changed = TRUE;
	    net_SaveCookiePermissions(NULL);
	}
    }
}

MODULE_PRIVATE PRBool
net_CookieIsFromHost(net_CookieStruct *cookie_s, char *host) {
    if (!cookie_s || !(cookie_s->host)) {
        return FALSE;
    }
    if (cookie_s->is_domain) {
        char *cp;
        int domain_length, host_length;

        domain_length = PL_strlen(cookie_s->host);

        /* calculate the host length by looking at all characters up to
         * a colon or '\0'.  That way we won't include port numbers
         * in domains
         */
        for(cp=host; *cp != '\0' && *cp != ':'; cp++) {
            ; /* null body */
        }
        host_length = cp - host;

        /* compare the tail end of host to cook_s->host */
        return (domain_length <= host_length &&
                PL_strncasecmp(cookie_s->host,
                    &host[host_length - domain_length],
                    domain_length) == 0);
    } else {
        return PL_strcasecmp(host, cookie_s->host) == 0;
    }
}

/* find out how many cookies this host has already set */
PRIVATE int
net_CookieCount(char * host) {
    int count = 0;
    XP_List * list_ptr;
    net_CookieStruct * cookie;

    net_lock_cookie_list();
    list_ptr = net_cookie_list;
    while((cookie = (net_CookieStruct *) XP_ListNextObject(list_ptr))!=0) {
	if (host && net_CookieIsFromHost(cookie, host)) {
	    count++;
	}
    }
    net_unlock_cookie_list();

    return count;
}

PUBLIC int
NET_CookieCount(char * URLName) {
    char * host;
    char * colon;
    int count = 0;

    /* remove protocol from URL name */
    host = NET_ParseURL(URLName, GET_HOST_PART);

    /* remove port number from URL name */
    colon = PL_strchr(host, ':');
    if(colon) {
        *colon = '\0';
    }

    /* get count */
    count = net_CookieCount(host);

    /* free up allocated string and return */
    if(colon) {
        *colon = ':';
    }
    PR_Free(host);
    return count;
}

PRBool net_IntSetCookieStringInUse = FALSE;

PRIVATE void
net_DeferCookie(
		MWContext * context,
		char * cur_url,
		char * set_cookie_header,
		time_t timeToExpire) {
	net_DeferCookieStruct * defer_cookie = PR_NEW(net_DeferCookieStruct);
	defer_cookie->context = context;
	defer_cookie->cur_url = NULL;
	StrAllocCopy(defer_cookie->cur_url, cur_url);
	defer_cookie->set_cookie_header = NULL;
	StrAllocCopy(defer_cookie->set_cookie_header, set_cookie_header);
	defer_cookie->timeToExpire = timeToExpire;
	net_lock_defer_cookie_list();
	if (!net_defer_cookie_list) {
		net_defer_cookie_list = XP_ListNew();
		if (!net_cookie_list) {
			PR_FREEIF(defer_cookie->cur_url);
			PR_FREEIF(defer_cookie->set_cookie_header);
			PR_Free(defer_cookie);
		}
	}
	XP_ListAddObject(net_defer_cookie_list, defer_cookie);
	net_unlock_defer_cookie_list();
}

PRIVATE void
net_IntSetCookieString(MWContext * context,
					char * cur_url,
					char * set_cookie_header,
					time_t timeToExpire );

PRIVATE void
net_UndeferCookies() {
	net_DeferCookieStruct * defer_cookie;
	net_lock_defer_cookie_list();
	if(XP_ListIsEmpty(net_defer_cookie_list)) {
		net_unlock_defer_cookie_list();
		return;
	}
	defer_cookie = XP_ListRemoveEndObject(net_defer_cookie_list);
	net_unlock_defer_cookie_list();
	net_IntSetCookieString (
		defer_cookie->context,
		defer_cookie->cur_url,
		defer_cookie->set_cookie_header,
		defer_cookie->timeToExpire);
	PR_FREEIF(defer_cookie->cur_url);
	PR_FREEIF(defer_cookie->set_cookie_header);
	PR_Free(defer_cookie);
}
#endif

/* Java script is calling NET_SetCookieString, netlib is calling 
** this via NET_SetCookieStringFromHttp.
*/

PRIVATE void
net_IntSetCookieString(MWContext * context,
					char * cur_url,
					char * set_cookie_header,
					time_t timeToExpire )
{
	net_CookieStruct * prev_cookie;
	char *path_from_header=NULL, *host_from_header=NULL;
	char *host_from_header2=NULL;
	char *name_from_header=NULL, *cookie_from_header=NULL;
	time_t expires=0;
	char *cur_path = NET_ParseURL(cur_url, GET_PATH_PART);
	char *cur_host = NET_ParseURL(cur_url, GET_HOST_PART);
	char *semi_colon, *ptr, *equal;
	const char *script_name;
	PRBool HG83744 is_domain=FALSE, ask=FALSE, accept=FALSE;
	MWContextType type;
	Bool bCookieAdded;

	if(!context) {
		PR_Free(cur_path);
		PR_Free(cur_host);
		return;
	}

	/* Only allow cookies to be set in the listed contexts. We
	 * don't want cookies being set in html mail. 
	 */
	type = context->type;
	if(!( (type == MWContextBrowser)
		|| (type == MWContextHTMLHelp)
		|| (type == MWContextPane) )) {
		PR_Free(cur_path);
		PR_Free(cur_host);
		return;
	}
	
	if(NET_GetCookieBehaviorPref() == NET_DontUse) {
		PR_Free(cur_path);
		PR_Free(cur_host);
		return;
	}

#if defined(CookieManagement)
	/* Don't enter this routine if it is already in use by another
	   thread.  Otherwise the "remember this decision" result of the
	   other cookie (which came first) won't get applied to this cookie.
	 */
	if (net_IntSetCookieStringInUse) {
		PR_Free(cur_path);
		PR_Free(cur_host);
		net_DeferCookie(context, cur_url, set_cookie_header, timeToExpire);
		return;
	}
	net_IntSetCookieStringInUse = TRUE;
#endif

	HG87358
	/* terminate at any carriage return or linefeed */
	for(ptr=set_cookie_header; *ptr; ptr++)
		if(*ptr == LF || *ptr == CR) {
			*ptr = '\0';
			break;
		}

	/* parse path and expires attributes from header if
 	 * present
	 */
	semi_colon = PL_strchr(set_cookie_header, ';');

	if(semi_colon)
	  {
		/* truncate at semi-colon and advance 
		 */
		*semi_colon++ = '\0';

		/* there must be some attributes. (hopefully)
		 */
		HG83476

		/* look for the path attribute
		 */
		ptr = PL_strcasestr(semi_colon, "path=");

		if(ptr) {
			/* allocate more than we need */
			StrAllocCopy(path_from_header, XP_StripLine(ptr+5));
			/* terminate at first space or semi-colon
			 */
			for(ptr=path_from_header; *ptr != '\0'; ptr++)
				if(NET_IS_SPACE(*ptr) || *ptr == ';' || *ptr == ',') {
					*ptr = '\0';
					break;
				  }
		  }

		/* look for the URI or URL attribute
		 *
		 * This might be a security hole so I'm removing
		 * it for now.
		 */

		/* look for a domain */
        ptr = PL_strcasestr(semi_colon, "domain=");

        if(ptr) {
			char *domain_from_header=NULL;
			char *dot, *colon;
			int domain_length, cur_host_length;

            /* allocate more than we need */
			StrAllocCopy(domain_from_header, XP_StripLine(ptr+7));

            /* terminate at first space or semi-colon
             */
            for(ptr=domain_from_header; *ptr != '\0'; ptr++)
                if(NET_IS_SPACE(*ptr) || *ptr == ';' || *ptr == ',') {
                    *ptr = '\0';
                    break;
                  }

            /* verify that this host has the authority to set for
             * this domain.   We do this by making sure that the
             * host is in the domain
             * We also require that a domain have at least two
             * periods to prevent domains of the form ".com"
             * and ".edu"
			 *
			 * Also make sure that there is more stuff after
			 * the second dot to prevent ".com."
             */
            dot = PL_strchr(domain_from_header, '.');
			if(dot)
                dot = PL_strchr(dot+1, '.');

			if(!dot || *(dot+1) == '\0') {
				/* did not pass two dot test. FAIL
				 */
				PR_Free(domain_from_header);
				PR_Free(cur_path);
				PR_Free(cur_host);
				TRACEMSG(("DOMAIN failed two dot test"));
#if defined(CookieManagement)
				net_IntSetCookieStringInUse = FALSE;
				net_UndeferCookies();
#endif
				return;
			  }

			/* strip port numbers from the current host
			 * for the domain test
			 */
			colon = PL_strchr(cur_host, ':');
			if(colon)
			   *colon = '\0';

			domain_length   = PL_strlen(domain_from_header);
			cur_host_length = PL_strlen(cur_host);

			/* check to see if the host is in the domain
			 */
			if(domain_length > cur_host_length
				|| PL_strcasecmp(domain_from_header, 
							   &cur_host[cur_host_length-domain_length]))
			  {
				TRACEMSG(("DOMAIN failed host within domain test."
					  " Domain: %s, Host: %s", domain_from_header, cur_host));
				PR_Free(domain_from_header);
				PR_Free(cur_path);
				PR_Free(cur_host);
#if defined(CookieManagement)
				net_IntSetCookieStringInUse = FALSE;
				net_UndeferCookies();
#endif
				return;
              }

			/* all tests passed, copy in domain to hostname field
			 */
			StrAllocCopy(host_from_header, domain_from_header);
			is_domain = TRUE;

			TRACEMSG(("Accepted domain: %s", host_from_header));

			PR_Free(domain_from_header);
          }

		/* now search for the expires header 
		 * NOTE: that this part of the parsing
		 * destroys the original part of the string
		 */
		ptr = PL_strcasestr(semi_colon, "expires=");

		if(ptr) {
			char *date =  ptr+8;
			/* terminate the string at the next semi-colon
			 */
			for(ptr=date; *ptr != '\0'; ptr++)
				if(*ptr == ';') {
					*ptr = '\0';
					break;
				  }
			if(timeToExpire)
				expires = timeToExpire;
			else
				expires = NET_ParseDate(date);

			TRACEMSG(("Have expires date: %ld", expires));
		  }
	  }

	if(!path_from_header) {
        /* strip down everything after the last slash
         * to get the path.
         */
        char * slash = PL_strrchr(cur_path, '/');
        if(slash)
            *slash = '\0';

		path_from_header = cur_path;
	  } else {
		PR_Free(cur_path);
	  }

	if(!host_from_header)
		host_from_header = cur_host;
	else
		PR_Free(cur_host);

	/* keep cookies under the max bytes limit */
	if(PL_strlen(set_cookie_header) > MAX_BYTES_PER_COOKIE)
		set_cookie_header[MAX_BYTES_PER_COOKIE-1] = '\0';

	/* separate the name from the cookie */
	equal = PL_strchr(set_cookie_header, '=');

	if(equal) {
		*equal = '\0';
		StrAllocCopy(name_from_header, XP_StripLine(set_cookie_header));
		StrAllocCopy(cookie_from_header, XP_StripLine(equal+1));
	  } else {
		TRACEMSG(("Warning: no name found for cookie"));
		StrAllocCopy(cookie_from_header, XP_StripLine(set_cookie_header));
		StrAllocCopy(name_from_header, "");
	  }

    /* If there's a script, call it now */
    script_name = NET_GetCookieScriptPref();
    if( (const char *)0 != script_name ) {
        JSCFCookieData *cd;
        Bool changed = FALSE;
        JSCFResult result;

        cd = PR_NEWZAP(JSCFCookieData);
        if( (JSCFCookieData *)0 == cd ) {
            PR_FREEIF(path_from_header);
            PR_FREEIF(host_from_header);
            PR_FREEIF(name_from_header);
            PR_FREEIF(cookie_from_header);
            /* FREEIF(cur_path); */
            /* FREEIF(cur_host); */
#if defined(CookieManagement)
            net_IntSetCookieStringInUse = FALSE;
            net_UndeferCookies();
#endif
            return;
        }

        cd->path_from_header = path_from_header;
        cd->host_from_header = host_from_header;
        cd->name_from_header = name_from_header;
        cd->cookie_from_header = cookie_from_header;
        cd->expires = expires;
        cd->url = cur_url;
        HG84777
        cd->domain = is_domain;
        cd->prompt = NET_GetCookieWarningPref();
        cd->preference = NET_GetCookieBehaviorPref();

	/*
	 * This is only safe to do from the mozilla thread
	 *   since it might do file I/O and uses the preferences
	 *   global context + objects.
	 */
#if !defined(MODULAR_NETLIB)
        result= (JSCFResult)ET_JSCFExecute(context, script_name, cd, &changed);
#else
        /* XXX:  This is probably not correct, but it will fix the build for now... */
        result= JSCF_Execute(context, script_name, cd, &changed);
#endif /* MODULAR_NETLIB */
		if( result != JSCF_error) {
			if( changed ) {
				if( cd->path_from_header != path_from_header ) {
					PR_FREEIF(path_from_header);
					path_from_header = PL_strdup(cd->path_from_header);
				}
				if( cd->host_from_header != host_from_header ) {
					PR_FREEIF(host_from_header);
					host_from_header = PL_strdup(cd->host_from_header);
				}
				if( cd->name_from_header != name_from_header ) {
					PR_FREEIF(name_from_header);
					name_from_header = PL_strdup(cd->name_from_header);
				}
				if( cd->cookie_from_header != cookie_from_header ) {
					PR_FREEIF(cookie_from_header);
				  cookie_from_header = PL_strdup(cd->cookie_from_header);
			  }
			  if( cd->expires != expires )
				  expires = cd->expires;
			  if( cd->domain != is_domain )
				  /* lets hope the luser remembered to change the domain field */
				  is_domain = cd->domain;
			  HG27398
			}
			switch( result ) {
				case JSCF_reject:
					PR_FREEIF(path_from_header);
					PR_FREEIF(host_from_header);
					PR_FREEIF(name_from_header);
					PR_FREEIF(cookie_from_header);
					PR_Free(cd);
					/* FREEIF(cur_path); */
					/* FREEIF(cur_host); */
#if defined(CookieManagement)
					net_IntSetCookieStringInUse = FALSE;
					net_UndeferCookies();
#endif
					return;
				case JSCF_accept:
					accept=TRUE;
				case JSCF_error:
				case JSCF_ask:
					ask=TRUE;
				case JSCF_whatever:
				break;
			}
		}
		PR_Free(cd);
#if defined(CookieManagement)
    } else {
	net_CookiePermissionStruct * cookie_permission;
	cookie_permission = net_CheckForCookiePermission(host_from_header);
	if (cookie_permission != NULL) {
	    if (cookie_permission->permission == FALSE) {
		PR_FREEIF(path_from_header);
		PR_FREEIF(host_from_header);
		PR_FREEIF(name_from_header);
		PR_FREEIF(cookie_from_header);
		net_IntSetCookieStringInUse = FALSE;
		net_UndeferCookies();
		return;
	    } else {
		accept = TRUE;
            }
	}
#endif
    }
 

	if( (NET_GetCookieWarningPref() || ask) && !accept ) {
		/* the user wants to know about cookies so let them
		 * know about every one that is set and give them
		 * the choice to accept it or not
		 */
		char * new_string=0;
#if defined(CookieManagement)
		int count;
		char * remember_string = 0;
		StrAllocCopy
		    (remember_string, XP_GetString(MK_ACCESS_COOKIES_REMEMBER));

		/* find out how many cookies this host has already set */
		count = net_CookieCount(host_from_header);
		net_lock_cookie_list();
		prev_cookie = net_CheckForPrevCookie
			(path_from_header, host_from_header, name_from_header);
		net_unlock_cookie_list();
		if (prev_cookie) {
		    new_string = PR_smprintf(
			XP_GetString(MK_ACCESS_COOKIES_WISHES_MODIFY),
			host_from_header ? host_from_header : "");
		} else if (count>1) {
		    new_string = PR_smprintf(
			XP_GetString(MK_ACCESS_COOKIES_WISHESN),
			host_from_header ? host_from_header : "",
			count);
		} else if (count==1){
		    new_string = PR_smprintf(
			XP_GetString(MK_ACCESS_COOKIES_WISHES1),
			host_from_header ? host_from_header : "");
		} else {
		    new_string = PR_smprintf(
			XP_GetString(MK_ACCESS_COOKIES_WISHES0),
			host_from_header ? host_from_header : "");
		}
#else
		char * tmp_host = NET_ParseURL(cur_url, GET_HOST_PART);
		StrAllocCopy(new_string, XP_GetString(MK_ACCESS_COOKIES_THE_SERVER));
		StrAllocCat(new_string, tmp_host ? tmp_host : "");
		StrAllocCat(new_string, XP_GetString(MK_ACCESS_COOKIES_WISHES));
		PR_Free(tmp_host);

		if(is_domain) {
			StrAllocCat(new_string, XP_GetString(MK_ACCESS_COOKIES_TOANYSERV));
			StrAllocCat(new_string, host_from_header);
		  } else {
			StrAllocCat(new_string, XP_GetString(MK_ACCESS_COOKIES_TOSELF));
		  }

		StrAllocCat(new_string, XP_GetString(MK_ACCESS_COOKIES_NAME_AND_VAL));

		StrAllocCat(new_string, name_from_header);
		StrAllocCat(new_string, "=");
		StrAllocCat(new_string, cookie_from_header);
		StrAllocCat(new_string, "\n");

		if(expires) {
			StrAllocCat(new_string, XP_GetString(MK_ACCESS_COOKIES_COOKIE_WILL_PERSIST));
			StrAllocCat(new_string, ctime(&expires));
		  }

		StrAllocCat(new_string, XP_GetString(MK_ACCESS_COOKIES_SET_IT));
#endif

		/* 
		 * Who knows what thread we are on.  Only the mozilla thread
		 *   is allowed to post dialogs so, if need be, go over there
		 */
#if defined(CookieManagement)

	    {
		Bool old_cookie_remember_checked = cookie_remember_checked;
		XP_Bool userHasAccepted = ET_PostCheckConfirmBox
		    (context,
		     new_string,
		     remember_string,
		     0,0,
		     &cookie_remember_checked);
		PR_FREEIF(new_string);
		PR_FREEIF(remember_string);
		if (cookie_remember_checked) {
                    net_CookiePermissionStruct * cookie_permission;
                    cookie_permission = PR_NEW(net_CookiePermissionStruct);
                    if (cookie_permission) {
                        net_lock_cookie_permission_list();
                        StrAllocCopy(host_from_header2, host_from_header);
                        /* ignore leading periods in host name */
                        while (host_from_header2 && (*host_from_header2 == '.')) {
                            host_from_header2++;
                        }
                        cookie_permission->host = host_from_header2; /* set host string */
                        cookie_permission->permission = userHasAccepted;
#ifdef TRUST_LABELS
                        cookie_permission->TrustList = NULL;
#endif
                        net_AddCookiePermission(cookie_permission, TRUE);
                        net_unlock_cookie_permission_list();
                    }
                }

		if (old_cookie_remember_checked != cookie_remember_checked) {
		    cookie_permissions_changed = TRUE;
		    net_SaveCookiePermissions(NULL);
		}


		if (!userHasAccepted) {
#if defined(CookieManagement)
			net_IntSetCookieStringInUse = FALSE;
			net_UndeferCookies();
#endif
			return;
		}
	    }
#else
		if(!ET_PostMessageBox(context, new_string, TRUE)) {
			PR_FREEIF(new_string);
			return;
		}
		PR_FREEIF(new_string);
#endif
	  }

	TRACEMSG(("mkaccess.c: Setting cookie: %s for host: %s for path: %s",
					cookie_from_header, host_from_header, path_from_header));

	/* 
	 * We have decided we are going to insert a cookie into the list
	 *   Get the cookie lock so that we can munge on the list
	 */
	net_lock_cookie_list();

	/* limit the number of cookies from a specific host or domain */
	net_CheckForMaxCookiesFromHost(host_from_header);

	if(XP_ListCount(net_cookie_list) > MAX_NUMBER_OF_COOKIES-1)
		net_remove_oldest_cookie();


    prev_cookie = net_CheckForPrevCookie
        (path_from_header, host_from_header, name_from_header);

    if(prev_cookie) {
        prev_cookie->expires = expires;
        PR_FREEIF(prev_cookie->cookie);
        PR_FREEIF(prev_cookie->path);
        PR_FREEIF(prev_cookie->host);
        PR_FREEIF(prev_cookie->name);
        prev_cookie->cookie = cookie_from_header;
        prev_cookie->path = path_from_header;
        prev_cookie->host = host_from_header;
        prev_cookie->name = name_from_header;
        HG83263
        prev_cookie->is_domain = is_domain;
		prev_cookie->last_accessed = time(NULL);
      }	else {
		XP_List * list_ptr = net_cookie_list;
		net_CookieStruct * tmp_cookie_ptr;
		size_t new_len;

        /* construct a new cookie_struct
         */
        prev_cookie = PR_NEW(net_CookieStruct);
        if(!prev_cookie) {
			PR_FREEIF(path_from_header);
			PR_FREEIF(host_from_header);
			PR_FREEIF(name_from_header);
			PR_FREEIF(cookie_from_header);
			net_unlock_cookie_list();
#if defined(CookieManagement)
			net_IntSetCookieStringInUse = FALSE;
			net_UndeferCookies();
#endif
			return;
          }
    
        /* copy
         */
        prev_cookie->cookie  = cookie_from_header;
        prev_cookie->name    = name_from_header;
        prev_cookie->path    = path_from_header;
        prev_cookie->host    = host_from_header;
        prev_cookie->expires = expires;
        HG22730
        prev_cookie->is_domain = is_domain;
		prev_cookie->last_accessed = time(NULL);

		if(!net_cookie_list) {
			net_cookie_list = XP_ListNew();
		    if(!net_cookie_list) {
				PR_FREEIF(path_from_header);
				PR_FREEIF(name_from_header);
				PR_FREEIF(host_from_header);
				PR_FREEIF(cookie_from_header);
				PR_Free(prev_cookie);
				net_unlock_cookie_list();
#if defined(CookieManagement)
				net_IntSetCookieStringInUse = FALSE;
				net_UndeferCookies();
#endif
				return;
			  }
		  }		

		/* add it to the list so that it is before any strings of
		 * smaller length
		 */
		bCookieAdded = FALSE;
		new_len = PL_strlen(prev_cookie->path);
		while((tmp_cookie_ptr = (net_CookieStruct *) XP_ListNextObject(list_ptr))!=0) { 
			if(new_len > PL_strlen(tmp_cookie_ptr->path)) {
				XP_ListInsertObject(net_cookie_list, tmp_cookie_ptr, prev_cookie);
				bCookieAdded = TRUE;
				break;
			  }
		  }
		if ( !bCookieAdded ) {
			/* no shorter strings found in list */
			XP_ListAddObjectToEnd(net_cookie_list, prev_cookie);
		}
	  }

	/* At this point we know a cookie has changed. Write the cookies to file. */
	cookies_changed = TRUE;
	NET_SaveCookies(NULL);
	net_unlock_cookie_list();
#ifdef TRUST_LABELS
    {
        TrustLabel *trustlabel;
        /*
         * At this point the cookie was added to
         * the cookie list.  So see if there is a trust label that
         * matches the cookie and if so add the trust label to the
         * cookie permission list.  Existing trust label entries in 
		 * the permission list replaced with this latest entry.
         */
            if ( MatchCookieToLabel2(
                    cur_url, prev_cookie->name,
                    prev_cookie->path, prev_cookie->host, &trustlabel ) ) {
                /*
                 * found a match - add the trust info to the proper
                 * entry in the cookie permission list
                 */
                AddTrustLabelInfo( prev_cookie->name, trustlabel );
                /* update the disk file */
                cookie_permissions_changed = TRUE;
                net_SaveCookiePermissions(NULL);
            }
        }
#endif
#if defined(CookieManagement)
	net_IntSetCookieStringInUse = FALSE;
	net_UndeferCookies();
#endif
	return;
}

PUBLIC void
NET_SetCookieString(MWContext * context, 
					char * cur_url,
					char * set_cookie_header) {
    net_IntSetCookieString(context, cur_url, set_cookie_header, 0);
}

/* Determines whether the inlineHost is in the same domain as the currentHost. For use with rfc 2109
 * compliance/non-compliance. */
#ifdef TRUST_LABELS
PUBLIC int
#else
PRIVATE int
#endif
NET_SameDomain(char * currentHost, char * inlineHost)
{
	char * dot = 0;
	char * currentDomain = 0;
	char * inlineDomain = 0;

	if(!currentHost || !inlineHost)
		return 0;

	/* case insensitive compare */
	if(PL_strcasecmp(currentHost, inlineHost) == 0)
		return 1;

	currentDomain = PL_strchr(currentHost, '.');
	inlineDomain = PL_strchr(inlineHost, '.');

	if(!currentDomain || !inlineDomain)
		return 0;
	
	/* check for at least two dots before continuing, if there are
	   not two dots we don't have enough information to determine
	   whether or not the inlineDomain is within the currentDomain */
	dot = PL_strchr(inlineDomain, '.');
	if(dot)
		dot = PL_strchr(dot+1, '.');
	else
		return 0;

	/* handle .com. case */
	if(!dot || (*(dot+1) == '\0') )
		return 0;

	if(!PL_strcasecmp(inlineDomain, currentDomain))
		return 1;
	return 0;
}

/* This function wrapper wraps NET_SetCookieString for the purposes of 
** determining whether or not a cookie is inline (we need the URL struct, 
** and outputFormat to do so).  this is called from NET_ParseMimeHeaders 
** in mkhttp.c
** This routine does not need to worry about the cookie lock since all of
**   the work is handled by sub-routines
*/
PUBLIC void
NET_SetCookieStringFromHttp(FO_Present_Types outputFormat,
							URL_Struct * URL_s,
							MWContext * context, 
							char * cur_url,
							char * set_cookie_header)
{
	/* If the outputFormat is not PRESENT (the url is not going to the screen), and not
	*  SAVE AS (shift-click) then 
	*  the cookie being set is defined as inline so we need to do what the user wants us
	*  to based on his preference to deal with foreign cookies. If it's not inline, just set
	*  the cookie. */
	char *ptr=NULL, *date=NULL;
	time_t gmtCookieExpires=0, expires=0;

	if(CLEAR_CACHE_BIT(outputFormat) != FO_PRESENT && CLEAR_CACHE_BIT(outputFormat) != FO_SAVE_AS)
	{
		if (NET_GetCookieBehaviorPref() == NET_DontAcceptForeign)
		{
			/* the user doesn't want foreign cookies, check to see if its foreign */
			char * curSessionHistHost = 0;
			char * theColon = 0;
			char * curHost = NET_ParseURL(cur_url, GET_HOST_PART);
			History_entry * shistEntry = SHIST_GetCurrent(&context->hist);
			if (shistEntry) {
			curSessionHistHost = NET_ParseURL(shistEntry->address, GET_HOST_PART);
			}
			if(!curHost || !curSessionHistHost)
			{
				PR_FREEIF(curHost);
				PR_FREEIF(curSessionHistHost);
				return;
			}

			/* strip ports */
			theColon = PL_strchr(curHost, ':');
			if(theColon)
			   *theColon = '\0';
			theColon = PL_strchr(curSessionHistHost, ':');
			if(theColon)
				*theColon = '\0';

			/* if it's foreign, get out of here after a little clean up */
			if(!NET_SameDomain(curHost, curSessionHistHost))
			{
				PR_FREEIF(curHost);	
				PR_FREEIF(curSessionHistHost);
				return;
			}
			PR_FREEIF(curHost);	
			PR_FREEIF(curSessionHistHost);
		}
	}

	/* Determine when the cookie should expire. This is done by taking the difference between 
	   the server time and the time the server wants the cookie to expire, and adding that 
	   difference to the client time. This localizes the client time regardless of whether or
	   not the TZ environment variable was set on the client. */

	/* Get the time the cookie is supposed to expire according to the attribute*/
	ptr = PL_strcasestr(set_cookie_header, "expires=");
	if(ptr)
	{
		char *date =  ptr+8;
		char origLast = '\0';
		for(ptr=date; *ptr != '\0'; ptr++)
			if(*ptr == ';')
			  {
				origLast = ';';
				*ptr = '\0';
				break;
			  }
		expires = NET_ParseDate(date);
		*ptr=origLast;
	}
	if( URL_s->server_date && expires )
	{
		if( expires < URL_s->server_date )
		{
			gmtCookieExpires=1;
		}
		else
		{
			gmtCookieExpires = expires - URL_s->server_date + time(NULL);
			/* if overflow */
			if( gmtCookieExpires < time(NULL) )
				gmtCookieExpires = (((unsigned) (~0) << 1) >> 1); /* max int */
		}
	}

	net_IntSetCookieString(context, cur_url, set_cookie_header, gmtCookieExpires);
}

#if defined(CookieManagement)
/* saves the HTTP cookies permissions to disk
 * the parameter passed in on entry is ignored
 * returns 0 on success -1 on failure.
 */
PRIVATE int
net_SaveCookiePermissions(char * filename)
{
    XP_List * list_ptr;
    net_CookiePermissionStruct * cookie_permission_s;
    XP_File fp;
    int32 len = 0;

    if(NET_GetCookieBehaviorPref() == NET_DontUse) {
	return(-1);
    }

    if(!cookie_permissions_changed) {
	return(-1);
    }

    net_lock_cookie_permission_list();
    list_ptr = net_cookie_permission_list;

    if(!(net_cookie_permission_list)) {
	net_unlock_cookie_permission_list();
	return(-1);
    }

    if(!(fp = NET_XP_FileOpen(filename, xpHTTPCookiePermission, XP_FILE_WRITE))) {
	net_unlock_cookie_permission_list();
	return(-1);
    }
    len = NET_XP_FileWrite("# Netscape HTTP Cookie Permission File" LINEBREAK
                 "# http://www.netscape.com/newsref/std/cookie_spec.html"
                  LINEBREAK "# This is a generated file!  Do not edit."
		  LINEBREAK LINEBREAK, -1, fp);

    if (len < 0) {
	NET_XP_FileClose(fp);
	net_unlock_cookie_permission_list();
	return -1;
    }

    /* format shall be:
     * host \t permission <optional trust label information >
     */
    while((cookie_permission_s = (net_CookiePermissionStruct *)
	    XP_ListNextObject(list_ptr)) != NULL) {
	len = NET_XP_FileWrite(cookie_permission_s->host, -1, fp);
	if (len < 0) {
	    NET_XP_FileClose(fp);
	    net_unlock_cookie_permission_list();
	    return -1;
	}

        NET_XP_FileWrite("\t", 1, fp);

	if(cookie_permission_s->permission) {
            NET_XP_FileWrite("TRUE", -1, fp);
	} else {
            NET_XP_FileWrite("FALSE", -1, fp);
	}

#ifdef TRUST_LABELS
        /* save the trust label information */
        SaveTrustLabelData( cookie_permission_s, fp );
#endif
	len = NET_XP_FileWrite(LINEBREAK, -1, fp);
	if (len < 0) {
	    NET_XP_FileClose(fp);
	    net_unlock_cookie_permission_list();
	    return -1;
	}
    }

    /* save current state of cookie nag-box's checkmark */
    NET_XP_FileWrite("@@@@", -1, fp);
    NET_XP_FileWrite("\t", 1, fp);
    if (cookie_remember_checked) {
        NET_XP_FileWrite("TRUE", -1, fp);
    } else {
        NET_XP_FileWrite("FALSE", -1, fp);
    }
    NET_XP_FileWrite(LINEBREAK, -1, fp);

    cookie_permissions_changed = FALSE;
    NET_XP_FileClose(fp);
    net_unlock_cookie_permission_list();
    return(0);
}

/* reads the HTTP cookies permission from disk
 * the parameter passed in on entry is ignored
 * returns 0 on success -1 on failure.
 *
 */
#define PERMISSION_LINE_BUFFER_SIZE 4096

PRIVATE int
net_ReadCookiePermissions(char * filename)
{
    XP_File fp;
    char buffer[PERMISSION_LINE_BUFFER_SIZE];
    char *host, *p;
    Bool permission_value;
    net_CookiePermissionStruct * cookie_permission;
    char *host_from_header2;

    if(!(fp = NET_XP_FileOpen(filename, xpHTTPCookiePermission, XP_FILE_READ)))
	return(-1);

    /* format is:
     * host \t permission <optional trust label information>
     * if this format isn't respected we move onto the next line in the file.
     */

    net_lock_cookie_permission_list();
    while(NET_XP_FileReadLine(buffer, PERMISSION_LINE_BUFFER_SIZE, fp)) {
        if (*buffer == '#' || *buffer == CR || *buffer == LF || *buffer == 0) {
	    continue;
	}

        XP_StripLine(buffer); /* remove '\n' from end of the line */

        host = buffer;
        /* isolate the host field which is the first field on the line */
        if( !(p = PL_strchr(host, '\t')) ) {
            continue; /* no permission field */
        }
        *p++ = '\0';
        if(*p == CR || *p == LF || *p == 0) {
            continue; /* no permission field */
	}

        /* ignore leading periods in host name */
        while (host && (*host == '.')) {
            host++;
	}

	/*
         * the first part of the permission file is valid -
         * allocate a new permission struct and fill it in
         */
        cookie_permission = PR_NEW(net_CookiePermissionStruct);
        if (cookie_permission) {
            host_from_header2 = PL_strdup(host);
            cookie_permission->host = host_from_header2;

            /*
             *  Now handle the permission field.
             * a host value of "@@@@" is a special code designating the
             * state of the cookie nag-box's checkmark
	     */
            permission_value = (!PL_strncmp(p, "TRUE", sizeof("TRUE")-1));
            if (!PL_strcmp(host, "@@@@")) {
                cookie_remember_checked = permission_value;
            } else {
                cookie_permission->permission = permission_value;
			
#ifdef TRUST_LABELS
                /*
                 * pick up the trust label information from the line
                 * and add it to the permission struct
                 */
                cookie_permission->TrustList = NULL;
                ParseTrustLabelInfo(p, cookie_permission);
#endif

                /* add the permission entry */
                net_AddCookiePermission( cookie_permission, FALSE );
            }
        } /* end if (cookie_permission) */
    } /* while(NET_XP_FileReadLine( */

    net_unlock_cookie_permission_list();
    NET_XP_FileClose(fp);
    cookie_permissions_changed = FALSE;
    return(0);
}

#endif

/* saves out the HTTP cookies to disk
 *
 * on entry pass in the name of the file to save
 *
 * returns 0 on success -1 on failure.
 *
 */
PUBLIC int
NET_SaveCookies(char * filename)
{
    XP_List * list_ptr;
    net_CookieStruct * cookie_s;
	time_t cur_date = time(NULL);
	XP_File fp;
	int32 len = 0;
	char date_string[36];

	if(NET_GetCookieBehaviorPref() == NET_DontUse)
	  return(-1);

	if(!cookies_changed)
	  return(-1);

	if(net_anonymous) {
	    return(-1);
	}

	net_lock_cookie_list();
	list_ptr = net_cookie_list;
	if(!(list_ptr)) {
		net_unlock_cookie_list();
		return(-1);
	}

	if(!(fp = NET_XP_FileOpen(filename, xpHTTPCookie, XP_FILE_WRITE))) {
		net_unlock_cookie_list();
		return(-1);
	}

	len = NET_XP_FileWrite("# Netscape HTTP Cookie File" LINEBREAK
				 "# http://www.netscape.com/newsref/std/cookie_spec.html"
				 LINEBREAK "# This is a generated file!  Do not edit."
				 LINEBREAK LINEBREAK,
				 -1, fp);
	if (len < 0)
	{
		NET_XP_FileClose(fp);
		net_unlock_cookie_list();
		return -1;
	}

	/* format shall be:
 	 *
	 * host \t is_domain \t path \t secure \t expires \t name \t cookie
	 *
	 * is_domain is TRUE or FALSE
	 * secure is TRUE or FALSE  
	 * expires is a time_t integer
	 * cookie can have tabs
	 */
    while((cookie_s = (net_CookieStruct *) XP_ListNextObject(list_ptr)) != NULL)
      {
		if(cookie_s->expires < cur_date)
			continue;  /* don't write entry if cookie has expired 
						* or has no expiration date
						*/

		len = NET_XP_FileWrite(cookie_s->host, -1, fp);
		if (len < 0)
		{
			NET_XP_FileClose(fp);
			net_unlock_cookie_list();
			return -1;
		}
		NET_XP_FileWrite("\t", 1, fp);
		
		if(cookie_s->is_domain)
			NET_XP_FileWrite("TRUE", -1, fp);
		else
			NET_XP_FileWrite("FALSE", -1, fp);
		NET_XP_FileWrite("\t", 1, fp);

		NET_XP_FileWrite(cookie_s->path, -1, fp);
		NET_XP_FileWrite("\t", 1, fp);

		HG74640
		    NET_XP_FileWrite("FALSE", -1, fp);
		NET_XP_FileWrite("\t", 1, fp);

		PR_snprintf(date_string, sizeof(date_string), "%lu", cookie_s->expires);
		NET_XP_FileWrite(date_string, -1, fp);
		NET_XP_FileWrite("\t", 1, fp);

		NET_XP_FileWrite(cookie_s->name, -1, fp);
		NET_XP_FileWrite("\t", 1, fp);

		NET_XP_FileWrite(cookie_s->cookie, -1, fp);
 		len = NET_XP_FileWrite(LINEBREAK, -1, fp);
		if (len < 0)
		{
			NET_XP_FileClose(fp);
			net_unlock_cookie_list();
			return -1;
		}
      }

	cookies_changed = FALSE;

	NET_XP_FileClose(fp);
	net_unlock_cookie_list();
    return(0);
}

/* This isn't needed any more */
PUBLIC void
NET_InitRDFCookieResources (void)
{
}

/* reads HTTP cookies from disk
 *
 * on entry pass in the name of the file to read
 *
 * returns 0 on success -1 on failure.
 *
 *
 */
#define LINE_BUFFER_SIZE 4096

PUBLIC int
NET_ReadCookies(char * filename)
{
    XP_List * list_ptr;
    net_CookieStruct *new_cookie, *tmp_cookie_ptr;
	size_t new_len;
    XP_File fp;
	char buffer[LINE_BUFFER_SIZE];
	char *host, *is_domain, *path, *xxx, *expires, *name, *cookie;
	Bool added_to_list;

#if defined(CookieManagement)
    net_ReadCookiePermissions(NULL);
#endif

    if(!(fp = NET_XP_FileOpen(filename, xpHTTPCookie, XP_FILE_READ)))
        return(-1);

	net_lock_cookie_list();
	list_ptr = net_cookie_list;

    /* format is:
     *
     * host \t is_domain \t path \t xxx \t expires \t name \t cookie
     *
     * if this format isn't respected we move onto the next line in the file.
     * is_domain is TRUE or FALSE	-- defaulting to FALSE
     * xxx is TRUE or FALSE   -- should default to TRUE
     * expires is a time_t integer
     * cookie can have tabs
     */
    while(NET_XP_FileReadLine(buffer, LINE_BUFFER_SIZE, fp))
      {
		added_to_list = FALSE;

		if (*buffer == '#' || *buffer == CR || *buffer == LF || *buffer == 0)
		  continue;

		host = buffer;
		
		if( !(is_domain = PL_strchr(host, '\t')) )
			continue;
		*is_domain++ = '\0';
		if(*is_domain == CR || *is_domain == LF || *is_domain == 0)
			continue;
		
		if( !(path = PL_strchr(is_domain, '\t')) )
			continue;
		*path++ = '\0';
		if(*path == CR || *path == LF || *path == 0)
			continue;

		if( !(xxx = PL_strchr(path, '\t')) )
			continue;
		*xxx++ = '\0';
		if(*xxx == CR || *xxx == LF || *xxx == 0)
			continue;

		if( !(expires = PL_strchr(xxx, '\t')) )
			continue;
		*expires++ = '\0';
		if(*expires == CR || *expires == LF || *expires == 0)
			continue;

        if( !(name = PL_strchr(expires, '\t')) )
			continue;
		*name++ = '\0';
		if(*name == CR || *name == LF || *name == 0)
			continue;

        if( !(cookie = PL_strchr(name, '\t')) )
			continue;
		*cookie++ = '\0';
		if(*cookie == CR || *cookie == LF || *cookie == 0)
			continue;

		/* remove the '\n' from the end of the cookie */
		XP_StripLine(cookie);

        /* construct a new cookie_struct
         */
        new_cookie = PR_NEW(net_CookieStruct);
        if(!new_cookie)
          {
			net_unlock_cookie_list();
            return(-1);
          }

		memset(new_cookie, 0, sizeof(net_CookieStruct));
    
        /* copy
         */
        StrAllocCopy(new_cookie->cookie, cookie);
        StrAllocCopy(new_cookie->name, name);
        StrAllocCopy(new_cookie->path, path);
        StrAllocCopy(new_cookie->host, host);
        new_cookie->expires = atol(expires);

		HG87365

        if(!PL_strcmp(is_domain, "TRUE"))
        	new_cookie->is_domain = TRUE;
        else
        	new_cookie->is_domain = FALSE;

		if(!net_cookie_list)
		  {
			net_cookie_list = XP_ListNew();
		    if(!net_cookie_list)
			  {
				net_unlock_cookie_list();
				return(-1);
			  }
		  }		

		/* add it to the list so that it is before any strings of
		 * smaller length
		 */
		new_len = PL_strlen(new_cookie->path);
		while((tmp_cookie_ptr = (net_CookieStruct *) XP_ListNextObject(list_ptr)) != NULL)
		  { 
			if(new_len > PL_strlen(tmp_cookie_ptr->path))
			  {
				XP_ListInsertObject(net_cookie_list, tmp_cookie_ptr, new_cookie);
				added_to_list = TRUE;
				break;
			  }
		  }

		/* no shorter strings found in list */	
		if(!added_to_list)
		    XP_ListAddObjectToEnd(net_cookie_list, new_cookie);
	  }

    NET_XP_FileClose(fp);
	net_unlock_cookie_list();

	cookies_changed = FALSE;

    return(0);
}


/* --- New stuff: General auth utils (currently used only by proxy auth) --- */

/*
 * Figure out the authentication scheme used; currently supported:
 *
 *		* Basic
 *
 */
 HG73632

PRIVATE net_AuthType
net_auth_type(char *name)
{
	if (name) {
		while (*name && NET_IS_SPACE(*name))
			name++;
		if (!PL_strncasecmp(name, "basic", 5))
			return AUTH_BASIC;
#ifdef SIMPLE_MD5
		HG29383
#endif
		/*FORTEZZA checks*/
		else if (!PL_strncasecmp(name, "fortezzaproxy", 13))
			return AUTH_FORTEZZA;
	}
	return AUTH_INVALID;
}


/*
 * Figure out better of two {WWW,Proxy}-Authenticate headers;
 * SimpleMD5 is better than Basic.  Uses the order of AuthType
 * enum values.
 *
 */
MODULE_PRIVATE PRBool
net_IsBetterAuth(char *new_auth, char *old_auth)
{
	if (!old_auth || net_auth_type(new_auth) >= net_auth_type(old_auth))
		return PR_TRUE;
	else
		return PR_FALSE;
}


/*
 * Turns binary data of given lenght into a newly-allocated HEX string.
 *
 *
 */
PRIVATE char *
bin2hex(unsigned char *data, int len)
{
    char *buf = (char *)PR_Malloc(2 * len + 1);
    char *p = buf;

	if(!buf)
		return NULL;

    while (len-- > 0) {
		sprintf(p, "%02x", *data);
		p += 2;
		data++;
    }
    *p = '\0';
    return buf;
}


/*
 * Parse {WWW,Proxy}-Authenticate header parameters into a net_AuthStruct
 * structure.
 *
 */
#define SKIP_WS(p) while((*(p)) && NET_IS_SPACE(*(p))) p++

PRIVATE PRBool
next_params(char **pp, char **name, char **value)
{
    char *q, *p = *pp;

    SKIP_WS(p);
    if (!p || !(*p) || !(q = strchr(p, '=')))
		return FALSE;
    *name = p;
    *q++ = '\0';
    if (*q == '"') {
		*value = q + 1;
		q = strchr(*value, '"');
		if (q)
		  *q++ = '\0';
    }
    else {
		*value = q;
		while (*q && !NET_IS_SPACE(*q)) q++;
		if (*q)
		  *q++ = '\0';
    }

    *pp = q;
    return TRUE;
}

PRIVATE net_AuthStruct *
net_parse_authenticate_line(char *auth, net_AuthStruct *ret)
{
    char *name, *value, *p = auth;

	if (!auth || !*auth)
		return NULL;

	if (!ret)
		ret = PR_NEWZAP(net_AuthStruct);

	if(!ret)
		return NULL;

    SKIP_WS(p);
	ret->auth_type = net_auth_type(p);
    while (*p && !NET_IS_SPACE(*p)) p++;

    while (next_params(&p, &name, &value)) {
		if (!PL_strcasecmp(name, "realm"))
		  {
			  StrAllocCopy(ret->realm, value);
		  }
#ifdef SIMPLE_MD5
		else if (!PL_strcasecmp(name, "domain"))
		  {
			  StrAllocCopy(ret->domain, value);
		  }
		else if (!PL_strcasecmp(name, "nonce"))
		  {
			  StrAllocCopy(ret->nonce, value);
		  }
		else if (!PL_strcasecmp(name, "opaque"))
		  {
			  StrAllocCopy(ret->opaque, value);
		  }
		else if (!PL_strcasecmp(name, "oldnonce"))
		  {
			  ret->oldNonce = (!PL_strcasecmp(value, "TRUE")) ? TRUE : FALSE;
		  }
#endif /* SIMPLE_MD5 */
		/* Some FORTEZZA checks */
		else if (!PL_strcasecmp(name, "challenge"))
		  {
			  StrAllocCopy(ret->challenge, value);
		  }
		else if (!PL_strcasecmp(name, "oldchallenge"))
		  {
			  ret->oldChallenge = (!PL_strcasecmp(value, "TRUE")) ? TRUE : FALSE;
		  }
		/* End FORTEZZA checks  */
    }

#ifdef SIMPLE_MD5
	if (!ret->oldNonce)
		ret->oldNonce_retries = 0;
#endif /* SIMPLE_MD5 */
	/*Another FORTEZZA addition*/
	if (!ret->oldChallenge)
		ret->oldChallenge_retries = 0;
	/*End FORTEZZA  addition   */
    return ret;
}


#ifdef SIMPLE_MD5
/* ---------- New stuff: SimpleMD5 Authentication for proxies -------------- */

PRIVATE void do_md5(unsigned char *stuff, unsigned char digest[16])
{
    SECStatus rv;

    rv = HASH_HashBuf(HASH_AlgMD5, digest, stuff, strlen((char *)stuff));

    return;
}


/*
 * Generate a response for a SimpleMD5 challenge.
 *
 *	HEX( MD5("challenge password method url"))
 *
 */
char *net_generate_md5_challenge_response(char *challenge,
										  char *password,
										  int   method,
										  char *url)
{
    unsigned char digest[16];
    unsigned char *cookie =
	  (unsigned char *)PR_Malloc(strlen(challenge) + strlen(password) +
								strlen(url) + 10);

	if(!cookie)
		return NULL;

    sprintf((char *)cookie, "%s %s %s %s", challenge, password,
			(method==URL_POST_METHOD ? "POST" :
			 method==URL_HEAD_METHOD ? "HEAD" : "GET"),
			url);
    do_md5(cookie, digest);
    return bin2hex(digest, 16);
}


#define SIMPLEMD5_AUTHORIZATION_FMT "SimpleMD5\
 username=\"%s\",\
 realm=\"%s\",\
 nonce=\"%s\",\
 response=\"%s\",\
 opaque=\"%s\""

#endif /* SIMPLE_MD5 */

PRIVATE
char *net_generate_auth_string(URL_Struct *url_s,
							   net_AuthStruct *auth_s)
{
	if (!auth_s)
		return NULL;

	switch (auth_s->auth_type) {

	  case AUTH_INVALID:
		break;

	  case AUTH_BASIC:
		if (!auth_s->auth_string) {
			int len;
			char *u_pass_string = NULL;

			StrAllocCopy(u_pass_string, auth_s->username);
			StrAllocCat (u_pass_string, ":");
			StrAllocCat (u_pass_string, auth_s->password);

			len = PL_strlen(u_pass_string);
			if (!(auth_s->auth_string = (char*) PR_Malloc((((len+1)*4)/3)+20)))
			  {
				  return NULL;
			  }

			PL_strcpy(auth_s->auth_string, "Basic ");
			NET_UUEncode((unsigned char *)u_pass_string,
						 (unsigned char *)&auth_s->auth_string[6],
						 len);

			PR_Free(u_pass_string);
		}
		break;

#ifdef SIMPLE_MD5
	  case AUTH_SIMPLEMD5:
	    if (auth_s->username && auth_s->password &&
			auth_s->nonce    && auth_s->opaque   &&
			url_s            && url_s->address)
		  {
			  char *resp;

			  PR_FREEIF(auth_s->auth_string);
			  auth_s->auth_string = NULL;

			  if ((resp = net_generate_md5_challenge_response(auth_s->nonce,
															  auth_s->password,
															  url_s->method,
															  url_s->address)))
				{
					if ((auth_s->auth_string =
						 (char *)PR_Malloc(PL_strlen(auth_s->username) +
										  PL_strlen(auth_s->realm)    +
										  PL_strlen(auth_s->nonce)    +
										  PL_strlen(resp)             +
										  PL_strlen(auth_s->opaque)   +
										  100)))
					  {
						  sprintf(auth_s->auth_string,
								  SIMPLEMD5_AUTHORIZATION_FMT,
								  auth_s->username,
								  auth_s->realm,
								  auth_s->nonce,
								  resp,
								  auth_s->opaque);
					  }
					PR_Free(resp);
				}
		  }
		break;
#endif /* SIMPLE_MD5 */
		/* Handle the FORTEZZA case        */
	        case AUTH_FORTEZZA:
	       		HG26251
		  break;
		/* Done Handling the FORTEZZA case */
    }

	return auth_s->auth_string;
}


/* --------------- New stuff: client-proxy authentication --------------- */

PRIVATE net_AuthStruct *
net_CheckForProxyAuth(char * proxy_addr)
{
	XP_List * lp = net_proxy_auth_list;
	net_AuthStruct * s;

	while ((s = (net_AuthStruct *)XP_ListNextObject(lp)) != NULL)
      {
		  if (!PL_strcasecmp(s->proxy_addr, proxy_addr))
			  return s;
	  }

    return NULL;
}


/*
 * returns a proxy authorization string if one is required, otherwise
 * returns NULL
 */
PUBLIC char *
NET_BuildProxyAuthString(MWContext * context,
						 URL_Struct * url_s,
						 char * proxy_addr)
{
	net_AuthStruct * auth_s = net_CheckForProxyAuth(proxy_addr);

	return auth_s ? net_generate_auth_string(url_s, auth_s) : NULL;
}


/*
 * Returns FALSE if the user wishes to cancel proxy authorization
 * and TRUE if the user wants to continue with a new authorization
 * string.
 */
#define INVALID_AUTH_HEADER XP_GetString( XP_PROXY_REQUIRES_UNSUPPORTED_AUTH_SCHEME )

#define LOOPING_OLD_NONCES XP_GetString( XP_LOOPING_OLD_NONCES )

PUBLIC PRBool
NET_AskForProxyAuth(MWContext * context,
					char *   proxy_addr,
					char *   pauth_params,
					PRBool  already_sent_auth)
{
	net_AuthStruct * prev;
	PRBool new_entry = FALSE;
	char * username = NULL;
	char * password = NULL;
	char * buf;
	int32  len=0;

	TRACEMSG(("Entering NET_AskForProxyAuth"));

	if (!proxy_addr || !*proxy_addr || !pauth_params || !*pauth_params)
		return FALSE;

	prev = net_CheckForProxyAuth(proxy_addr);
    if (prev) {
		new_entry = FALSE;
		net_parse_authenticate_line(pauth_params, prev);
	}
	else {
		new_entry = TRUE;
		if (!(prev = net_parse_authenticate_line(pauth_params, NULL)))
		  {
			  FE_Alert(context, INVALID_AUTH_HEADER);
			  return FALSE;
		  }
		StrAllocCopy(prev->proxy_addr, proxy_addr);
	}

	if (!prev->realm || !*prev->realm)
		StrAllocCopy(prev->realm, XP_GetString( XP_UNIDENTIFIED_PROXY_SERVER ) );

    if (!new_entry) {
		if (!already_sent_auth)
		  {
			  /* somehow the mapping changed since the time we sent
			   * the authorization.
			   * This happens sometimes because of the parrallel
			   * nature of the requests.
			   * In this case we want to just retry the connection
			   * since it will probably succeed now.
			   */
			  return TRUE;
		  }
#ifdef SIMPLE_MD5
		else if (prev->oldNonce && prev->oldNonce_retries++ < 3)
		  {
			  /*
			   * We already sent the authorization string and the
			   * nonce was expired -- auto-retry.
			   */
			  if (!FE_Confirm(context, LOOPING_OLD_NONCES))
				  return FALSE;
		  }
#endif /* SIMPLE_MD5 */
		/* Do the good old FORTEZZA  stuff */
		else if (prev->oldChallenge && (prev->oldChallenge_retries++ > 3)) 
		  {
			  /*
			   * We already sent the authorization string and the
			   * nonce was expired -- auto-retry.
			   */
			  if (!FE_Confirm(context, XP_GetString(XP_CONFIRM_PROXYAUTHOR_FAIL)))
				  return FALSE;
		  }
		else if (prev->auth_type != AUTH_FORTEZZA)
		  {
			  /*
			   * We already sent the authorization string and it failed.
			   */
			  if (!FE_Confirm(context, XP_GetString(XP_CONFIRM_PROXYAUTHOR_FAIL)))
				  return FALSE;
		  }
	}

    
    if (prev->auth_type == AUTH_FORTEZZA) {
		SECStatus rv;
		rv = HG26252
		if ( rv != SECSuccess ) {
			return(FALSE);
		}
    } else
    {
	username = prev->username;
	password = prev->password;

	len = PL_strlen(prev->realm) + PL_strlen(proxy_addr) + 50;
	buf = (char*)PR_Malloc(len*sizeof(char));
	
	if(buf)
	  {
		PR_snprintf(buf, len*sizeof(char), XP_GetString( XP_PROXY_AUTH_REQUIRED_FOR ), prev->realm, proxy_addr);

		NET_Progress(context, XP_GetString( XP_CONNECT_PLEASE_ENTER_PASSWORD_FOR_PROXY ) );
#if defined(SingleSignon)
		/* prefill prompt with previous username/passwords if any */
		len = SI_PromptUsernameAndPassword
		    (context, buf, &username, &password, proxy_addr);
#else
		len = FE_PromptUsernameAndPassword
		    (context, buf, &username, &password);
#endif
		PR_Free(buf);
	  }
	else
	  {
		len = 0;
	  }

	if (!len)
	  {
		  TRACEMSG(("User canceled login!!!"));
		  return FALSE;
	  }
	else if (!username || !password)
	  {
		  return FALSE;
	  }

	PR_FREEIF(prev->auth_string);
	prev->auth_string = NULL;		/* Generate a new one */
	PR_FREEIF(prev->username);
	prev->username = username;
	PR_FREEIF(prev->password);
	prev->password = password;
    }

	if (new_entry)
	  {
		  if (!net_proxy_auth_list)
			{
				net_proxy_auth_list = XP_ListNew();
				if (!net_proxy_auth_list)
				  {
					  return TRUE;
				  }
			}
		  XP_ListAddObjectToEnd(net_proxy_auth_list, prev);
	  }

	return TRUE;
}


#if defined(CookieManagement)

#ifdef HTMLDialogs
#include "htmldlgs.h"
#else
#define XP_DIALOG_CANCEL_BUTTON		(1<<0)
#define XP_DIALOG_OK_BUTTON             (1<<2)
#define XP_STRINGS_CHUNKSIZE 512
typedef struct _XPDialogState XPDialogState;
typedef struct _XPDialogInfo XPDialogInfo;
typedef struct _XPDialogStrings XPDialogStrings;
typedef PRBool (* XP_HTMLDialogHandler)(XPDialogState *state, char **argv,
                                        int argc, unsigned int button);
struct _XPDialogState {
    PRArenaPool *arena;
    void *window;
    void *proto_win;
    XPDialogInfo *dialogInfo;
    void *arg;
    void (* deleteCallback)(void *arg);
    void *cbarg;
    PRBool deleted;
};
struct _XPDialogInfo {
    unsigned int buttonFlags;
    XP_HTMLDialogHandler handler;
    int width;
    int height;
};
struct _XPDialogStrings
{
    PRArenaPool *arena;
    int basestringnum;
    int nargs;
    char **args;
    char *contents;
};

#define COOKIE_FILE "C:/PROGRA~1/NETSCAPE/USERS/morse/cookies.txt"
time_t CookieTime() {
    struct stat stats;
    stat(COOKIE_FILE, &stats);
    return stats.st_mtime;
}

XPDialogState *
XP_MakeRawHTMLDialog(void *proto_win, XPDialogInfo *dialogInfo,
		     int titlenum, XPDialogStrings *strings,
		     int handlestring, void *arg)
{
    int i;
    time_t oldCookieTime;

    /* write out html dialog to /htmldlgs.htm */
    XP_File f = fopen("/htmldlgs.htm","w");
    for (i=0; i<strings->nargs; i++) {
        if (strings->args[i]) {
            fprintf(f, "%s", (char *)(strings->args[i]));
        }
    }
    fclose(f);
    fflush(f);

    /* write out dialog info to /index.htm */
    f = fopen("/index.htm","w");
    fprintf(f, "<HTML> \n");
    fprintf(f, "  <BODY \n");
    fprintf(f, "    onload=\"msgWindow=window.open(\n");
    fprintf(f, "      'htmldlgs.htm', \n");
    fprintf(f, "      'window2', \n");
    fprintf(f, "      'resizable=no, titlebar=no, width=%d, height=%d')\">\n",
        dialogInfo->width, dialogInfo->height);
    fprintf(f, "  </BODY> \n");
    fprintf(f, "</HTML> \n");
    fclose(f);
    fflush(f);

    /* obtain timestamp of cookies file */
    oldCookieTime = CookieTime();

    /* bring up a browser on /index.htm */
    WinExec("C:\\PROGRA~1\\NETSCAPE\\COMM32\\PROGRAM\\netscape.exe y:\\index.htm",SW_SHOW);

    /* wait for timestamp on cookies file to change */
    while (oldCookieTime == CookieTime()) {
    }

    /* call the htmldlgs done routine */
    {
        struct stat stats;
        long fileLength;
        XP_File f;
        char* readBuf;
        char* argv[] = {NULL, NULL, NULL, NULL};
        char* button = NULL;
        char* cookie;
        char* separator;
        int i, j;

        /* read in the cookies file */
        stat(COOKIE_FILE, &stats);
        fileLength = stats.st_size;
        readBuf = (char *) malloc(fileLength * sizeof(char));
        f = fopen(COOKIE_FILE, "r");
        fread(readBuf, sizeof(char), fileLength, f);

        /* find the htmldlgs cookie (it is preceded by "htmldlgs" tab verical-bar ) */
        cookie = PL_strstr(readBuf, "htmldlgs\t|"); /* get to htmldlgs tab vert-bar */
        cookie = cookie + PL_strlen("htmldlgs\t|"); /* get passed htmldlgs tab vert-bar */

        /* button name is first item in cookie (up to next verical bar) */
        separator = strchr(cookie, '|');
        *separator = '\0';
        StrAllocCopy(button, cookie);
        cookie = separator+1;
        *separator = '|';

        /* remainder of cookie string are the args, separated by vertical bars */
        for (i=0; (*cookie != '\n') && (*cookie != '\r'); i++) {
            separator = strchr(cookie, '|');
            *separator = '\0';
            StrAllocCopy(argv[i], cookie);
            cookie = separator+1;
            *separator = '|';
        }

        /* invoke the dialog callback routine */
        if (!PORT_Strcmp(button,"OK")) {
          (dialogInfo->handler)(NULL, argv, 4, XP_DIALOG_OK_BUTTON);
        } else {
          (dialogInfo->handler)(NULL, argv, 4, XP_DIALOG_CANCEL_BUTTON);
        }

        /* free up the allocated strings */
        XP_FREE(button);
        for (j=0; j<1; j++) {
          XP_FREE(argv[j]);
        }
        XP_FREE(readBuf);
    }

    return NULL;
}

XPDialogStrings *
XP_GetDialogStrings(int stringnum)
{
    XPDialogStrings *header = NULL;
    PRArenaPool *arena = NULL;
    char *dst, *src;
    int n, size, len, done = 0;
    
    /* get a new arena */
    arena = PORT_NewArena(XP_STRINGS_CHUNKSIZE);
    if ( arena == NULL ) {
	return(NULL);
    }

    /* allocate the header structure */
    header = (XPDialogStrings *)PORT_ArenaAlloc(arena, sizeof(XPDialogStrings));
    if ( header == NULL ) {
	goto loser;
    }

    /* init the header */
    header->arena = arena;
    header->basestringnum = stringnum;

    src = XP_GetString(stringnum);
    len = PORT_Strlen(src);
    size = len + 1;
    dst = header->contents =
	(char *)PORT_ArenaAlloc(arena, sizeof(char) * size);
    if (dst == NULL)
	goto loser;
    
    while (!done) {		/* Concatenate pieces to form message */
	PORT_Memcpy(dst, src, len+1);
	done = 1;
	if (XP_STRSTR(src, "%-cont-%")) { /* Continuation */
	    src = XP_GetString(++stringnum);
	    len = PORT_Strlen(src);
	    header->contents =
		(char *)PORT_ArenaGrow(arena,
				     header->contents, size, size + len);
	    if (header->contents == NULL)
		goto loser;
	    dst = header->contents + size - 1;
	    size += len;
	    done = 0;
	}
    }

    /* At this point we should have the complete message in
       header->contents, including like %-cont-%, which will be
       ignored later. */

    /* Count the arguments in the message */
    header->nargs = -1;		/* Support %0% as lowest token */
    src = header->contents;
    while ((src = PORT_Strchr(src, '%'))) {
	src++;
	n = (int)XP_STRTOUL(src, &dst, 10);
	if (dst == src)	{	/* Integer not found... */
	    src = PORT_Strchr(src, '%') + 1; /* so skip this %..% */
	    PORT_Assert(NULL != src-1); /* Unclosed %..% ? */
	    continue;
	}
	
	if (header->nargs < n)
	    header->nargs = n;
	src = dst + 1;
    }

    if (++(header->nargs) > 0)  /* Allocate space for arguments */
	header->args = (char **)PORT_ArenaZAlloc(arena, sizeof(char *) *
					      header->nargs);

    return(header);
    
loser:
    PORT_FreeArena(arena, PR_FALSE);
    return(NULL);
}



void
XP_SetDialogString(XPDialogStrings *strings, int argNum, char *string)
{
    /* make sure we are doing it right */
    PORT_Assert(argNum < strings->nargs);
    PORT_Assert(argNum >= 0);
    PORT_Assert(strings->args[argNum] == NULL);
    
    /* set the string */
    strings->args[argNum] = string;
    
    return;
}

char *
XP_FindValueInArgs(const char *name, char **av, int ac)
{
    for( ;ac > 0; ac -= 2, av += 2 ) {
	if ( PORT_Strcmp(name, av[0]) == 0 ) {
	    return(av[1]);
	}
    }
    return(0);
}

#endif

extern int XP_CERT_PAGE_STRINGS;
extern int SA_REMOVE_BUTTON_LABEL;

#define BUFLEN 5000

#define FLUSH_BUFFER			\
    if (buffer) {			\
	StrAllocCat(buffer2, buffer);	\
	g = 0;				\
        buffer[0] = '\0';               \
    }

/* return TRUE if "number" is in sequence of comma-separated numbers */
Bool net_InSequence(char* sequence, int number) {
    char* ptr;
    char* endptr;
    char* undo = NULL;
    Bool retval = FALSE;
    int i;

    /* not necessary -- routine will work even with null sequence */
    if (!*sequence) {
	return FALSE;
    }

    for (ptr = sequence ; ptr ; ptr = endptr) {

	/* get to next comma */
        endptr = PL_strchr(ptr, ',');

        /* if comma found, process it */
        if (endptr) {

            /* restore last comma-to-null back to comma */
            if (undo) {
                *undo = ',';
            }

            /* set the comma to a null */
            undo = endptr;
            *endptr++ = '\0';

            /* compare the number before the comma with "number" */
            if (*ptr) {
                i = atoi(ptr);
                if (i == number) {

                    /* "number" was in the sequence so return TRUE */
                    retval = TRUE;
                    break;
                }
            }
        }
    }

    /* restore last comma-to-null back to comma */
    if (undo) {
        *undo = ',';
    }
    return retval;
}

PR_STATIC_CALLBACK(PRBool)
net_AboutCookiesDialogDone(XPDialogState* state, char** argv, int argc,
						unsigned int button)
{
    XP_List *list;
    net_CookieStruct *cookie;
    net_CookiePermissionStruct *cookiePermission;
    char *buttonName;
    int cookieNumber;
    net_CookieStruct *cookieToDelete = 0;
    net_CookiePermissionStruct *cookiePermissionToDelete = 0;

    char* gone;

    buttonName = XP_FindValueInArgs("button", argv, argc);
    if (button != XP_DIALOG_OK_BUTTON) {
	/* OK button not pressed (must be cancel button that was pressed) */
	return PR_FALSE;
    }

    /* OK was pressed, do the deletions */

    /* get the comma-separated sequence of cookies to be deleted */
    gone = XP_FindValueInArgs("goneC", argv, argc);
    PR_ASSERT(gone);
    if (!gone) {
	return PR_FALSE;
    }

    /*
     * walk through the cookie list, deleting the designated cookies
     * Note: we can't delete cookie while "list" is pointing to it because
     * that would destroy "list".  So we do a lazy deletion
     */
    net_lock_cookie_list();
    list = net_cookie_list;
    cookieNumber = 0;
    while ( (cookie=(net_CookieStruct *) XP_ListNextObject(list)) ) {
	if (net_InSequence(gone, cookieNumber)) {
	    if (cookieToDelete) {
		net_FreeCookie(cookieToDelete);
	    }
	    cookieToDelete = cookie;
	}
	cookieNumber++;
    }

    if (cookieToDelete) {
	net_FreeCookie(cookieToDelete);
	cookies_changed = TRUE;
	NET_SaveCookies(NULL);
    }
    net_unlock_cookie_list();

    /* get the comma-separated sequence of permissions to be deleted */
    gone = XP_FindValueInArgs("goneP", argv, argc);
    PR_ASSERT(gone);
    if (!gone) {
	return PR_FALSE;
    }

    /*
     * walk through the cookie permission list, deleting the designated permissions
     * Note: we can't delete permissions while "list" is pointing to it because
     * that would destroy "list".  So we do a lazy deletion
     */
    net_lock_cookie_permission_list();
    list = net_cookie_permission_list;
    cookieNumber = 0;
    while ( (cookiePermission=(net_CookiePermissionStruct *) XP_ListNextObject(list)) ) {
	if (net_InSequence(gone, cookieNumber)) {
	    if (cookiePermissionToDelete) {
		net_FreeCookiePermission(cookiePermissionToDelete, TRUE);
	    }
	    cookiePermissionToDelete = cookiePermission;
	}
	cookieNumber++;
    }

    if (cookiePermissionToDelete) {
	net_FreeCookiePermission(cookiePermissionToDelete, TRUE);
	cookie_permissions_changed = TRUE;
	net_SaveCookiePermissions(NULL);
    }

    net_unlock_cookie_permission_list();
    return PR_FALSE;
}

PRIVATE Bool
CookieCompare (net_CookieStruct * cookie1, net_CookieStruct * cookie2) {
    char * host1 = cookie1->host;
    char * host2 = cookie2->host;

    /* get rid of leading period on host name, if any */
    if (*host1 == '.') {
	host1++;
    }
    if (*host2 == '.') {
	host2++;
    }

    /* make decision based on host name if they are unequal */
    if (PL_strcmp (host1, host2) < 0) {
	return -1;
    }
    if (PL_strcmp (host1, host2) > 0) {
	return 1;
    }

    /* if host names are equal, make decision based on cookie name */
    return (PL_strcmp (cookie1->name, cookie2->name));
}

/*
 * find the next cookie that is alphabetically after the specified cookie
 *    ordering is based on hostname and the cookie name
 *    if specified cookie is NULL, find the first cookie alphabetically
 */
PRIVATE net_CookieStruct *
NextCookieAfter(net_CookieStruct * cookie, int * cookieNum) {
    XP_List *cookie_list=net_cookie_list;
    net_CookieStruct *cookie_ptr;
    net_CookieStruct *lowestCookie = NULL;
    int localCookieNum = 0;
    int lowestCookieNum;

    while ( (cookie_ptr=(net_CookieStruct *) XP_ListNextObject(cookie_list)) ) {
	if (!cookie || (CookieCompare(cookie_ptr, cookie) > 0)) {
	    if (!lowestCookie ||
		    (CookieCompare(cookie_ptr, lowestCookie) < 0)) {
		lowestCookie = cookie_ptr;
		lowestCookieNum = localCookieNum;
	    }
	}
	localCookieNum++;
    }

    *cookieNum = lowestCookieNum;
    return lowestCookie;
}

typedef struct _CookieViewerDialog CookieViewerDialog;

struct _CookieViewerDialog {
    void *window;
    void *parent_window;
    PRBool dialogUp;
    XPDialogState *state;
};

#ifdef TRUST_LABELS
/* Purpose: given a cookie name look in the list containing the cookperm.txt
 * file and locate a matching trust label.  If a matching trust label
 * is found then format a simple text string that describes the trust label
 * information in a user friendly manner.
 *
 * If a matching label is not found return a string with a single space.
 * The caller must free the string.
 *
 * History:
 *  9/9/98 Paul Chek - switch recipient from a range to a single value 
 */
char *GetTrustLabelString(char *CookieName)
{
    XP_List * list_ptr, *Tptr;
    TrustEntry *TEntry;
    net_CookiePermissionStruct *cookperm;
    char *szTemp = NULL;
    char *szPurpose = NULL;
    char *szRecipient = NULL;
    char *szBy = NULL;
    int i, j;

/*
 * here's how we would like to write the code but it doesn't compile on the mac 
 *
 *   int PurposeIds[] = {MK_ACCESS_TL_PPH0, MK_ACCESS_TL_PPH1,
 *                       MK_ACCESS_TL_PPH2, MK_ACCESS_TL_PPH3,
 *                       MK_ACCESS_TL_PPH4, MK_ACCESS_TL_PPH5};
 *   int RecpIds[] = { MK_ACCESS_TL_RPH0, MK_ACCESS_TL_RPH1, 
 *                     MK_ACCESS_TL_RPH2, MK_ACCESS_TL_RPH3};
*/

#define NumPurposeBits 6 /* number of bits in the recipient value */
    /* array of msg Ids for the purpose values */
    int PurposeIds[NumPurposeBits];

#define NumRecpBits 4 /* number of bits in the recipient value */
    /* the list of msg Ids for the recipient value. */
    int RecpIds[NumRecpBits];

#if NumPurposeBits > NumRecpBits
#define NumStrs  NumPurposeBits
#else
#define NumStrs  NumRecpBits
#endif
    /* this array holds strings used in formatting the message */
    char *szTempStrs[ NumStrs ];

    PurposeIds[0] = MK_ACCESS_TL_PPH0;
    PurposeIds[1] = MK_ACCESS_TL_PPH1;
    PurposeIds[2] = MK_ACCESS_TL_PPH2;
    PurposeIds[3] = MK_ACCESS_TL_PPH3;
    PurposeIds[4] = MK_ACCESS_TL_PPH4;
    PurposeIds[5] = MK_ACCESS_TL_PPH5;

    RecpIds[0] = MK_ACCESS_TL_RPH0;
    RecpIds[1] = MK_ACCESS_TL_RPH1;
    RecpIds[2] = MK_ACCESS_TL_RPH2;
    RecpIds[3] = MK_ACCESS_TL_RPH3;



    if( CookieName ) {
        /* look thru the cookie permission list for a matching trust label */
        net_lock_cookie_permission_list();
        list_ptr = net_cookie_permission_list;
        while( szTemp == NULL &&
                (cookperm = (net_CookiePermissionStruct *) XP_ListNextObject(list_ptr))!=0) {
            /* look thru the trust list for this cookie */
            if (cookperm->TrustList ) {
                Tptr = cookperm->TrustList;
                while( (TEntry = (TrustEntry *)XP_ListNextObject(Tptr))) {
                    if( TEntry->CookieName
                            && !PL_strcmp(CookieName, TEntry->CookieName)) {
                        /* this permission entry matches the given cookie */
                        szTemp = (char*)PR_Malloc(BUFLEN);
                        break;
                    }
                }
            }
        }
        net_unlock_cookie_permission_list();

        /* was a match found?? */
        if (szTemp) {
            /* yes convert the ratings info into user strings */

            for( j=0; j<NumStrs; j++) {
                szTempStrs[j] = NULL;
            }

            /*
             * count the number of purpose bits set AND
             *  make a list of the phrases to use
             */
            for (j=0,i=0; i<NumPurposeBits; i++) {
               if ((TEntry->purpose>>i) & 1) {
                    /* this bit is set get the associated phrase */
                    szTempStrs[j++] = PL_strdup( XP_GetString( PurposeIds[i] ) );
                }
            }
            /*
             * build the purpose string 
             * For translation purposes I am using strings 
             * of the form "This information is used for %1$s, %2$s and %3$s."
             * and inserting the additional phrases in the formating.*/
            switch ( j ) {
                case 1:
                    PR_snprintf(szTemp, BUFLEN,
                        XP_GetString( MK_ACCESS_TL_PUR1 ),
                        szTempStrs[0]
                    );
                    break;
                case 2:
                    PR_snprintf(szTemp, BUFLEN,
                        XP_GetString( MK_ACCESS_TL_PUR2 ),
                        szTempStrs[0],
                        szTempStrs[1]
                    );
                    break;

                case 3:
                    PR_snprintf(szTemp, BUFLEN,
                        XP_GetString( MK_ACCESS_TL_PUR3 ),
                        szTempStrs[0],
                        szTempStrs[1],
                        szTempStrs[2]
                    );
                    break;

                case 4:
                    PR_snprintf(szTemp, BUFLEN,
                        XP_GetString( MK_ACCESS_TL_PUR4 ),
                        szTempStrs[0],
                        szTempStrs[1],
                        szTempStrs[2],
                        szTempStrs[3]
                    );
                    break;

                case 5:
                    PR_snprintf(szTemp, BUFLEN,
                        XP_GetString( MK_ACCESS_TL_PUR5 ),
                        szTempStrs[0],
                        szTempStrs[1],
                        szTempStrs[2],
                        szTempStrs[3],
                        szTempStrs[4]
                    );
                    break;

                case 6:
                    PR_snprintf(szTemp, BUFLEN,
                        XP_GetString( MK_ACCESS_TL_PUR6 ),
                        szTempStrs[0],
                        szTempStrs[1],
                        szTempStrs[2],
                        szTempStrs[3],
                        szTempStrs[4],
                        szTempStrs[5]
                    );
                    break;

            } /* switch ( j ) */
            /* I did things this way to avoid buffer overflow problems */
            szPurpose = PL_strdup( szTemp );
            /* free the temporary strings */
            for( j=0; j<NumStrs; j++) {
                PR_FREEIF(szTempStrs[j]);
                szTempStrs[j] = NULL;
            }

            /* second, build the recipient phrase list.  The recipients value as
			 * ossolated between allowing a range of values (0:2) and a single
			 * value, thats the reason for the ifdef'd code. */
#ifndef RECIPIENT_RANGE
			szTempStrs[0] = PL_strdup( XP_GetString( RecpIds[ TEntry->recipient] ) );
			PR_snprintf(szTemp, BUFLEN,
				XP_GetString( MK_ACCESS_TL_RECP1 ),
				szTempStrs[0]
			);
			PR_FREEIF(szTempStrs[0]);
            szRecipient = PL_strdup( szTemp );
#else
			/* is just a single bit set and is that bit 0 ?*/
			/* if  RECIPIENT_RANGE is defined you will need to 
			 * include these lines in allxpstr.h
			 *	ResDef(MK_ACCESS_TL_RECP2, (TRUST_LABEL_BASE + 17),
			 *	"It is %1$s and %2$s." )
			 *	ResDef(MK_ACCESS_TL_RECP3, (TRUST_LABEL_BASE + 18),
			 *	"It is %1$s, %2$s and %3$s." )
			 */
            if ( TEntry->recipient == 1 ) {
                szTempStrs[0] = PL_strdup( XP_GetString( MK_ACCESS_TL_RPH0 ) );
                PR_snprintf(szTemp, BUFLEN,
                    XP_GetString( MK_ACCESS_TL_RECP1 ),
                    szTempStrs[0]
                );
            } else {
                /*
                 * if bits other than bit 0 are set then dont 
                 * output the "used only by this site" text
                 *
                 * count the number of purpose bits set AND
                 *  make a list of the phrases to use
                 */
                for (j=0,i=1; i<NumRecpBits; i++) {
                    if ((TEntry->recipient>>i) & 1) {
                        /* this bit is set get the associated phrase */
                        szTempStrs[j++] = PL_strdup( XP_GetString( RecpIds[i] ) );
                    }
                }
                /*
                 * format the recipient string according to the 
                 * number of bits set
                 */
                switch ( j ) {
                    case 1:
                        PR_snprintf(szTemp, BUFLEN,
                            XP_GetString( MK_ACCESS_TL_RECP1 ),
                            szTempStrs[0]
                        );
                        break;

                    case 2:
                        PR_snprintf(szTemp, BUFLEN,
                            XP_GetString( MK_ACCESS_TL_RECP2 ),
                            szTempStrs[0],
                            szTempStrs[1]
                        );
                        break;

                    case 3:
                        PR_snprintf(szTemp, BUFLEN,
                            XP_GetString( MK_ACCESS_TL_RECP3 ),
                            szTempStrs[0],
                            szTempStrs[1],
                            szTempStrs[2]
                        );
                        break;

                } /* switch ( j ) */
            }
            /* I did things this way to avoid buffer overflow problems */
            szRecipient = PL_strdup( szTemp );
            /* free the temporary strings */
            for( j=0; j<NumStrs; j++) {
                PR_FREEIF(szTempStrs[j]);
                szTempStrs[j] = NULL;
            }
#endif

            /* construct the by string */
            if (TEntry->by) {
                PR_snprintf(szTemp, BUFLEN, XP_GetString(MK_ACCESS_TL_BY), TEntry->by);
                szBy = PL_strdup( szTemp );
            } else {
                szBy = PL_strdup( " " );
            }

            /*
             * construct the entire trust label statement.
             * WATCHOUT: you can only execute four XP_GetString calls
             * in this format string.
             */
            PR_snprintf(szTemp, BUFLEN,
                "%s " /* identifiable text */
                "%s " /* purpose string */
                "%s " /* receipent */
                "%s",  /* by string */
                (TEntry->bIdentifiable
                    ? XP_GetString(MK_ACCESS_TL_ID1)
                    : XP_GetString(MK_ACCESS_TL_ID0)),
                szPurpose, szRecipient, szBy);
        }
    }
    PR_FREEIF(szPurpose);
    PR_FREEIF(szRecipient);
    PR_FREEIF(szBy);

    return szTemp;
}


#define STR_TRUST_LABEL "(TrustLabel"
/* these structures describe the layout of the trust label information in 
 *  the cookperm.txt.  The structures are used for both reading and writing the 
 *  file.
 */
enum {TYPE_QUOTED_STRING, TYPE_INT};
typedef struct {
    char *szMatchStr; /* the string to look for coming in */
    int ValueType; /* what kind of thing the value is */
    int DataOffset; /* where in the TrustEntry struct to store the value */                    
} ParseItem;

static ParseItem ParseData[] = {
    {"cookie", TYPE_QUOTED_STRING, offsetof(TrustEntry, CookieName)},
    {"purpose", TYPE_INT, offsetof(TrustEntry, purpose)},
    {"id", TYPE_INT, offsetof(TrustEntry, bIdentifiable)},
    {"recipient", TYPE_INT, offsetof(TrustEntry, recipient)},
    {"by", TYPE_QUOTED_STRING, offsetof(TrustEntry, by)},
    /* the timestamp is the date and time when the cookie was set.   
     * It is in the form "14 Apr 89 03:20 GMT".   Use PR_ParseTimeString
     * to parse it into a PR_Exploded time.
     */
    {"timestamp", TYPE_QUOTED_STRING, offsetof(TrustEntry, CookieSet)}
};
#define NUM_ITEMS  sizeof(ParseData) / sizeof( ParseItem)

/******************************************************
 * Purpose:  parse all the trust label string from a line in
 * cookperm.txt and store the result in a net_CookiePermissionStruct.
 *
 * This is a simple table driven parser.  
 ******************************************************/
PRIVATE
void ParseTrustLabelInfo
    (char *Buf, net_CookiePermissionStruct * cookie_permission)
{
    char *pToken, *p, *pEnd, *pSub;
    int i, k, len;
    Bool Status = FALSE;
    char *pChar, *szTemp;
    char *pCopy = NULL;
    TrustEntry *TEntry;

    /*
     * Look for the trust label information which is in the form
     *   "(TrustLabel" - must be the first token
     *     "cookie:"<cookie name>
     *     "purpose:"<purpose value>
     *     "id:"<"0"|"1">
     *     "recipient:"<recipient value>
     *     "by:"<by value>
     *     "timestamp:"<timestamp>
     *    ")"								- must be the last token
     * purpose value == a hex value corresponding to the purpose value to set
     * recipient value == a hex value corresponding to the recipient value to set
     * by value == a text string enclosed in double quotes
     * timestamp == time when the cookie was set: format is quoted string in any of the 
     * formats that PR_Time will accept.
     * cookie name == the name of the cookie enclosed in quotes
     *
     * The middle tokens can be in any order.
     */

    /*
     * Do I have a well formed trust label string ??
     * - can I find a beginning and an end??
     */
    p = Buf;
    while ((p=strcasestr(p, (char *)STR_TRUST_LABEL)) && (pEnd=strchr(p,')'))) {
        p += PL_strlen(STR_TRUST_LABEL);

        /* Allocate a new trust Entry */
        TEntry = PR_NEW(TrustEntry);
        if (TEntry) {
            /* init the elements */
            TEntry->CookieName = NULL;
            TEntry->purpose = 0;
            TEntry->recipient = 0;
            TEntry->bIdentifiable = FALSE;
            TEntry->by = NULL;
            TEntry->CookieSet = NULL;

            /*
             * isolate this trust label string for parsing, I must make a copy
             * of the incoming string because the tokenizer will add nulls to it.
             * Also allocate one more byte than I really need so that the tokenizer
             * can write an extra Null at the end.
             */
            len = pEnd-p+1;
            pCopy = XP_ALLOC(len + 1);
            if (!pCopy) return;
            XP_MEMCPY(pCopy, p, len-1);
            pCopy[len] = '\0';

            /* get the first token from the copy */
            pToken = XP_STRTOK_R(pCopy, " :", &pSub);
            while (pToken) {
                if (pToken != ")") {
                    for ( i=0; i<NUM_ITEMS; i++ ) {
                        /* Is this token one that we are interested in ?? */
                        if (PL_strcasecmp(pToken, ParseData[i].szMatchStr) == 0) {
                            /* yes - extract its associated value and store it */
                            switch(ParseData[i].ValueType) {
                                case TYPE_QUOTED_STRING:
                                    /*
                                     * since I allow white space INSIDE the
                                     * quoted string, AND I want the next token
                                     * to contain all the stuff inside the
                                     * quoted string; I need to eat all the
                                     * whitespace between the : and the
                                     * next "
                                     */
                                    while(*pSub == ' ' || *pSub == '\t') {
                                        pSub++;
                                    }
                                    /* get next token which should be " */
                                    pToken = XP_STRTOK_R(nil, "\"", &pSub );
                                    if (pToken) {
                                        pChar = (char *)((void *)(TEntry))
                                            + ParseData[i].DataOffset;
                                        szTemp = PL_strdup(pToken);
                                        XP_MEMCPY((void *)pChar,
                                            (void *)&szTemp,
                                            sizeof(char *) );
                                    }
                                    break;
                                case TYPE_INT:
                                    /*
                                     * get the next token which should be some
                                     * number
                                     */
                                    pToken = XP_STRTOK_R(nil, " ", &pSub );
                                    if (pToken) {
                                        pChar = (char *)((void *)(TEntry))
                                            + ParseData[i].DataOffset;
                                        k = XP_ATOI( pToken );
                                        XP_MEMCPY((void *)pChar,
                                            (void *)&k, sizeof(int));
                                    }
                                    break;
                            }
                            break;           /* break out of the for loop */
                        }
                    }
                } else {
                    /* all done */
                    break;
                }
                pToken = XP_STRTOK_R(nil, " :", &pSub );     /* get the next token */
            } /* end while */
            PR_FREEIF( pCopy );

            /* Does the trust list exist in this entry, if not create it */
            if(!cookie_permission->TrustList) {
                cookie_permission->TrustList = XP_ListNew();
                if(!cookie_permission->TrustList) {
                    return;
                }
            }

            /* add the entry to the list in the permission entry */
            XP_ListAddObjectToEnd( cookie_permission->TrustList, TEntry );

        } /* end if (TEntry) */
        /* set up for the next entry */
        p = ++pEnd;
    } /* end while ((p=strcasestr( */
    return;
}

/****************************************************************
* Purpose: Write the trust label information from the permission
*		   list to cookperm.txt
*
* History:
*  Paul Chek - initial creation
****************************************************************/
PRIVATE
void SaveTrustLabelData
	( net_CookiePermissionStruct * cookie_permission, XP_File fp )
{
    XP_List *Tptr;
    TrustEntry *TEntry;
    char *szTemp;
    char *szTemp2;
    char **pChar;
    int *pInt;
    int T2Size = 50;
    int len, i;

    /*
     * do an initial allocation for szTemp2.  Whenever a quoted string 
     * is processed the size of szTemp2 is tested to make sure it is big enough,
     * if it is not it is resized.
     */
    szTemp2 = XP_ALLOC( T2Size );
    if ( fp && cookie_permission && cookie_permission->TrustList && szTemp2 ) {
        /* there is trust label information, write it */
        Tptr = cookie_permission->TrustList;
        while( (TEntry = (TrustEntry *)XP_ListNextObject(Tptr))) {
            /* build the string up one piece at a time, its slower but safer */
            szTemp = NULL;
            StrAllocCopy( szTemp, "  "STR_TRUST_LABEL);
            /* march thru the Parsing table and output the TrustLabel information */
            for ( i=0; i<NUM_ITEMS; i++ ) {
                switch(ParseData[i].ValueType) {
                    case TYPE_QUOTED_STRING:
                        pChar = (char **)(((char *)TEntry)+ ParseData[i].DataOffset);
                        if ( PL_strlen( *pChar ) ) {
                            /* make sure szTemp2 is big enough. */
                            len = PL_strlen( ParseData[i].szMatchStr ) + PL_strlen(*pChar) + 3;
                            if ( T2Size <= len ) {
                                T2Size = len +50;
                                szTemp2 = XP_REALLOC( szTemp2, T2Size );
                            }
                            if ( szTemp2 ) {
                                PR_snprintf( szTemp2, T2Size, " %s:\"%s\"",
                                ParseData[i].szMatchStr, *pChar );
                                StrAllocCat( szTemp, szTemp2 ); 
                            } else {
                               /* error reallocating szTemp2, blast out */
                               PR_FREEIF( szTemp );
                               return;
                            }
                        }
                        break;

                    case TYPE_INT:
                        pInt = (int *)(((char *)TEntry)+ ParseData[i].DataOffset);
                        PR_snprintf( szTemp2, T2Size, " %s:%d",
                            ParseData[i].szMatchStr, *pInt );
                        StrAllocCat( szTemp, szTemp2 ); 
                        break;
                }
            } /* end for */
            StrAllocCat( szTemp, " )" ); /* add the termination paren */

            /* write a single trust label string */
            NET_XP_FileWrite(szTemp, -1, fp);
            PR_FREEIF( szTemp );
        } /* end while( (TEntry =  */ 
        PR_FREEIF( szTemp2 );
    }
}

/****************************************************************
* Purpose:  search for a TrustEntry in the trust list of a cookie 
* permission entry.
*
* History:
*  Paul Chek - initial creation
****************************************************************/
PRIVATE
Bool FindTrustEntry( char *CookieName, XP_List *TList, TrustEntry **TheEntry )
{
    TrustEntry *TEntry;
    XP_List *TPtr;
    Bool Status = FALSE;
    if ( TList && !XP_ListIsEmpty( TList ) ) {
        TPtr = TList;
        while( (TEntry = (TrustEntry *)XP_ListNextObject(TPtr))) {
            if (PL_strcasecmp(CookieName, TEntry->CookieName) == 0) {
                /* the entry was found */
                *TheEntry = TEntry;
                Status = TRUE;
                break;
            }
        }
    }
    return Status;
}

/****************************************************************
* Purpose: given a TrustLabel struct sat up the corresponding TrustEntry
*
* History:
*  Paul Chek - initial creation
****************************************************************/
PRIVATE
void CopyTrustEntry( char *CookieName, TrustEntry *TEntry, TrustLabel *trustlabel )
{
    PRExplodedTime now;
    char line[100];

    if ( TEntry && trustlabel ) {
        PR_FREEIF(TEntry->CookieName);
        PR_FREEIF(TEntry->by);
        PR_FREEIF(TEntry->CookieSet);
        TEntry->CookieName  = PL_strdup( CookieName );
        TEntry->purpose = trustlabel->purpose;
        TEntry->recipient = trustlabel->recipients;
        TEntry->bIdentifiable = trustlabel->ID;
        TEntry->by = PL_strdup( trustlabel->szBy );

        /*
         * format an ASCII timestamp for when the cookie was set.  Use a 
         *format that PR_ParseTimeString supports. "22-AUG-1993 10:59 PM"
         */ 
        PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
        PR_FormatTimeUSEnglish(line, 400, "%d-%b-%Y %I:%M %p", &now);
        TEntry->CookieSet  = PL_strdup( line );
	}
}

/****************************************************************
* Purpose: look in the permissions list to see if there is 
* trust label information for the cookie.  If so delete it.
*
* History:
*  Paul Chek - initial creation
****************************************************************/
PRIVATE
void DeleteCookieFromPermissions( char *CookieHost, char *CookieName )
{
    XP_List *TmpList;
    TrustEntry *TEntry;
    net_CookiePermissionStruct * cookie_permission;

    /* lock the permission list */
    TmpList=net_cookie_permission_list;
    net_lock_cookie_permission_list();
    while ( (cookie_permission=(net_CookiePermissionStruct *)XP_ListNextObject(TmpList)) ) {
        /* is this the corect host for the cookie */
        if (PL_strcasecmp(CookieHost, cookie_permission->host) == 0) {
            /* yes - see if there is a trust label entry for the cookie */
            if ( FindTrustEntry( CookieName, cookie_permission->TrustList, &TEntry ) ) {
                /* yes there is - delete it */
                XP_ListRemoveObject( cookie_permission->TrustList, TEntry);
                /* free the strings in the entry */
                PR_FREEIF(TEntry->CookieName);
                PR_FREEIF(TEntry->by);
                PR_FREEIF(TEntry->CookieSet);
            }
            /* dont need to look in the list anymore cause there is only one entry per host */
            break;			
        }
    }
    net_unlock_cookie_permission_list();

}

/****************************************************************
* Purpose: given a TrustLabel struct containing the trust label
*			info that matches a particular cookie add the trust
*			label info to the cookie permission list
*
* History:
*  Paul Chek - initial creation
****************************************************************/
PRIVATE
void AddTrustLabelInfo( char *CookieName, TrustLabel *trustlabel )
{
    XP_List *TmpList;
    TrustEntry *TEntry;
	net_CookiePermissionStruct * cookie_permission;
	/* lock the permission list */
    TmpList=net_cookie_permission_list;
    net_lock_cookie_permission_list();
    while ( (cookie_permission=(net_CookiePermissionStruct *)XP_ListNextObject(TmpList)) ) {
		/* is this the corect host for the cookie */
		if (PL_strcasecmp(trustlabel->domainName, cookie_permission->host) == 0) {
			/* OK - we have found the entry for this host.  Look thru the trust list 
			   to see if we have an existing entry for this cookie.  If so replace it.
			   If not add a new entry
			*/
			if ( FindTrustEntry( CookieName, cookie_permission->TrustList, &TEntry ) ) {
				/* the entry exists modify it */
				CopyTrustEntry( CookieName, TEntry, trustlabel );
			} else {
				/* the entry does NOT exist - add a new one */
				TEntry = PR_NEWZAP( TrustEntry ); 
				if ( TEntry ) {
					/* yes - build the entry for the cookie */
					CopyTrustEntry( CookieName, TEntry, trustlabel );
					/* Does the trust list exist in this entry, if not create it */
					if(!cookie_permission->TrustList) {
						cookie_permission->TrustList = XP_ListNew();
						if(!cookie_permission->TrustList) {
							return;
						}
					}

					/* add it to the list */
					XP_ListAddObjectToEnd(cookie_permission->TrustList, TEntry);
					break;		  /* all done */	
				}
			}
		}
	}
    net_unlock_cookie_permission_list();
}

#endif

/*
 * return a string that has each " of the argument sting
 * replaced with \" so it can be used inside a quoted string
 */
PRIVATE char*
net_FixQuoted(char* s) {
    char * result;
    int count = PL_strlen(s);
    char *quote = s;
    unsigned int i, j;
    while (quote = PL_strchr(quote, '"')) {
        count = count++;
        quote++;
    }
    result = XP_ALLOC(count + 1);
    for (i=0, j=0; i<PL_strlen(s); i++) {
        if (s[i] == '"') {
            result[i+(j++)] = '\\';
        }
        result[i+j] = s[i];
    }
    result[i+j] = '\0';
    return result;
}

#ifdef XP_MAC
/* pinkerton - if we don't do this, it won't compile because it runs out of registers */
#pragma global_optimizer on
#endif

PRIVATE void
net_DisplayCookieInfoAsHTML(MWContext *context, char* host)
{
    char *buffer = (char*)PR_Malloc(BUFLEN);
    char *buffer2 = 0;
    int g = 0, count, cookieNum;
    XP_List *cookie_list;
    XP_List *cookie_permission_list;
    net_CookieStruct *cookie;
    net_CookiePermissionStruct *cookperm;
    CookieViewerDialog *dlg;
    int i;
    char * view_sites = NULL;
    char * view_cookies = NULL;
    char * heading = NULL;
#ifdef TRUST_LABELS
    char * szTrustLabel = NULL;
#endif

    static XPDialogInfo dialogInfo = {
	0,
	net_AboutCookiesDialogDone,
	600,
	400
    };

    XPDialogStrings* strings;
    strings = XP_GetDialogStrings(XP_CERT_PAGE_STRINGS);
    if (!strings) {
	return;
    }
    StrAllocCopy(buffer2, "");
    StrAllocCopy (view_cookies, XP_GetString(MK_ACCESS_VIEW_COOKIES));
    StrAllocCopy (view_sites, XP_GetString(MK_ACCESS_VIEW_SITES));

    /* generate initial section of html file */
    g += PR_snprintf(buffer+g, BUFLEN-g,
"<HTML>\n"
"<HEAD>\n"
"  <TITLE>Cookies</TITLE>\n"
"  <SCRIPT>\n"
"    index_frame = 0;\n"
"    title_frame = 1;\n"
"    spacer1_frame = 2;\n"
"    list_frame = 3;\n"
"    spacer2_frame = 4;\n"
"    prop_frame = 5;\n"
"    spacer3_frame = 6;\n"
"    button_frame = 7;\n"
"\n"
"    var cookie_mode;\n"
"    var goneC;\n"
"    var goneP;\n"
"    deleted_cookies = new Array (0"
	);
    FLUSH_BUFFER


    /* fill in initial 0's for deleted_cookies and deleted_sites arrays */
    count = XP_ListCount(net_cookie_list);
    for (i=1; i<count; i++) {
	g += PR_snprintf(buffer+g, BUFLEN-g,
",0"
	    );
	if ((i%50) == 0) {
	    g += PR_snprintf(buffer+g, BUFLEN-g,
"\n      "
	    );
	}
    }
    g += PR_snprintf(buffer+g, BUFLEN-g,
");\n"
"    deleted_sites = new Array (0"
	);
    count = XP_ListCount(net_cookie_permission_list);
    for (i=1; i<count; i++) {
	g += PR_snprintf(buffer+g, BUFLEN-g,
",0"
	    );
	if ((i%50) == 0) {
	    g += PR_snprintf(buffer+g, BUFLEN-g,
"\n      "
	    );
	}
    }
    FLUSH_BUFFER

    g += PR_snprintf(buffer+g, BUFLEN-g,
");\n"
"\n"
"    function DeleteItemSelected() {\n"
"      if (cookie_mode) {\n"
"        DeleteCookieSelected();\n"
"      } else {\n"
"        DeleteSiteSelected();\n"
"      }\n"
"    }\n"
"\n"
"    function DeleteCookieSelected() {\n"
"      selname = top.frames[list_frame].document.fSelectCookie.selname;\n"
"      goneC = top.frames[button_frame].document.buttons.goneC;\n"
"      var p;\n"
"      var i;\n"
"      for (i=selname.options.length; i>0; i--) {\n"
"        if (selname.options[i-1].selected) {\n"
"          selname.options[i-1].selected = 0;\n"
"          goneC.value = goneC.value + selname.options[i-1].value + \",\";\n"
"          deleted_cookies[selname.options[i-1].value] = 1;\n"
"          for (j=i ; j<selname.options.length ; j++) {\n"
"            selname.options[j-1] = selname.options[j];\n"
"          }\n"
"          selname.options[selname.options.length-1] = null;\n"
"        }\n"
"      }\n"
"      top.frames[prop_frame].document.open();\n"
"      top.frames[prop_frame].document.close();\n"
"    }\n"
"\n"
"    function DeleteSiteSelected() {\n"
"      selname = top.frames[list_frame].document.fSelectSite.selname;\n"
"      goneP = top.frames[button_frame].document.buttons.goneP;\n"
"      var p;\n"
"      var i;\n"
"      for (i=selname.options.length; i>0; i--) {\n"
"        if (selname.options[i-1].selected) {\n"
"          selname.options[i-1].selected = 0;\n"
"          goneP.value = goneP.value + selname.options[i-1].value + \",\";\n"
"          deleted_sites[selname.options[i-1].value] = 1;\n"
"          for (j=i; j < selname.options.length; j++) {\n"
"            selname.options[j-1] = selname.options[j];\n"
"          }\n"
"          selname.options[selname.options.length-1] = null;\n"
"        }\n"
"      }\n"
"      top.frames[prop_frame].document.open();\n"
"      top.frames[prop_frame].document.close();\n"
"    }\n"
"\n"
"    function ViewCookieSelected(selCookie) {\n"
"      selname = top.frames[list_frame].document.fSelectCookie.selname;\n"
"      top.frames[prop_frame].document.open();\n"
"      switch (selname.options[selCookie].value) {\n"
	);
    FLUSH_BUFFER

    /* Get rid of any expired cookies now so user doesn't
     * think/see that we're keeping cookies in memory.
     */
    net_lock_cookie_list();
    net_remove_expired_cookies();

    /* Generate the html for the cookie properties (alphabetical order) */
    cookie_list=net_cookie_list;
    cookie = NULL;
    while (cookie = NextCookieAfter(cookie, &cookieNum)) {
	char * name = NULL;
	char * value = NULL;
	char * domain_or_host = NULL;
	char * path = NULL;
	char * secure = NULL;
	char * yes_or_no = NULL;
	char * expires = NULL;
	char * expireDate = NULL;

	char *fixed_name = net_FixQuoted(cookie->name);
	char *fixed_value = net_FixQuoted(cookie->cookie);
	char *fixed_domain_or_host = net_FixQuoted(cookie->host);
	char *fixed_path = net_FixQuoted(cookie->path);

	StrAllocCopy (name, XP_GetString(MK_ACCESS_NAME));
	StrAllocCopy (value, XP_GetString(MK_ACCESS_VALUE));
	StrAllocCopy (domain_or_host,
	    cookie->is_domain
		? XP_GetString(MK_ACCESS_DOMAIN)
		: XP_GetString(MK_ACCESS_HOST));
	StrAllocCopy (path, XP_GetString(MK_ACCESS_PATH));
	StrAllocCopy (secure, XP_GetString(MK_ACCESS_SECURE));
	StrAllocCopy (yes_or_no,
	    HG78111
		? XP_GetString(MK_ACCESS_YES)
		: XP_GetString(MK_ACCESS_NO));
	StrAllocCopy (expires, XP_GetString(MK_ACCESS_EXPIRES));
	StrAllocCopy (expireDate,
	    cookie->expires
		? ctime(&(cookie->expires))
		: XP_GetString(MK_ACCESS_END_OF_SESSION));

	if (!expireDate) {
            StrAllocCopy (expireDate, "");
        } else if (expireDate[PL_strlen(expireDate)-1] == '\n') {
            expireDate[PL_strlen(expireDate)-1] = ' ';
	}

	g += PR_snprintf(buffer+g, BUFLEN-g,
"       case %d:\n"
"         top.frames[prop_frame].document.write(\n"
"            \"<NOBR><b>%s</b> %s</NOBR><BR>\" +\n"
"            \"<NOBR><b>%s</b> %s</NOBR><BR>\" +\n"
"            \"<NOBR><b>%s</b> %s</NOBR><BR>\" +\n"
"            \"<NOBR><b>%s</b> %s</NOBR><BR>\" +\n"
"            \"<NOBR><b>%s</b> %s</NOBR><BR>\" +\n"
"            \"<NOBR><b>%s</b> %s</NOBR><BR>\" ",
	    cookieNum,
	    name, fixed_name,
	    value, fixed_value,
	    domain_or_host, fixed_domain_or_host,
	    path, fixed_path,
	    secure, yes_or_no,
	    expires, expireDate
	    );
	FLUSH_BUFFER
	PR_FREEIF(fixed_name);
	PR_FREEIF(fixed_value);
	PR_FREEIF(fixed_domain_or_host);
	PR_FREEIF(fixed_path);
	PR_FREEIF(name);
	PR_FREEIF(value);
	PR_FREEIF(domain_or_host);
	PR_FREEIF(path);
	PR_FREEIF(secure);
	PR_FREEIF(yes_or_no);
	PR_FREEIF(expires);
	PR_FREEIF(expireDate);
	/* caveat: It would have been simpler to not allocate and free the
	 * above strings but rather just use the XP_GetString(...) parameters
	 * in the call to PR_snprintf.	However there are only four buffers
	 * that XP_GetString uses and it keeps recycling them.	Since the
	 * above would require more than four parameters which are calls to
	 * XP_GetString, some of the returned buffers would be overwritten
         * before they could be used.  Hence the simpler approach wouldn't work
	 */

#ifdef TRUST_LABELS
        /* add an entry that contains the trust label information */
        szTrustLabel = GetTrustLabelString( cookie->name );
        if ( szTrustLabel ) {
            g += PR_snprintf(buffer+g, BUFLEN-g,
"+\n"
"            \"<HR WIDTH=100%> <FONT SIZE=2> %s </FONT>\"",
                szTrustLabel);
            PR_FREEIF(szTrustLabel);
        }
#endif
        g += PR_snprintf(buffer+g, BUFLEN-g,
"\n"
"         );\n"
"         break;\n"
            );
        FLUSH_BUFFER
    }

    /* generate next section of html file */
    g += PR_snprintf(buffer+g, BUFLEN-g,
"      }\n"
"      top.frames[prop_frame].document.close();\n"
"    }\n"
"\n"
"    function loadCookies(){\n"
"      cookie_mode = 1;\n"
	);
    FLUSH_BUFFER

    /* generate index frame only if full viewer */

    if (!host) {
	g += PR_snprintf(buffer+g, BUFLEN-g,
"      top.frames[index_frame].document.open();\n"
"      top.frames[index_frame].document.write(\n"
"        \"<BODY BGCOLOR=#C0C0C0>\" +\n"
"          \"<TABLE BORDER=0 WIDTH=100%>\" +\n"
"            \"<TR>\" +\n"
"              \"<TD> <SPACER TYPE=HORIZONTAL SIZE=10> </TD>\" +\n"
"              \"<TD ALIGN=CENTER VALIGN=MIDDLE BGCOLOR=#FFFFFF>\" +\n"
"                \"<FONT SIZE=2 COLOR=#666666>\" +\n"
"                  \"<B>%s</B>\" +\n"
"                \"</FONT>\" +\n"
"              \"</TD>\" +\n"
"              \"<TD>&nbsp;&nbsp;&nbsp;</TD>\" +\n"
"              \"<TD ALIGN=CENTER VALIGN=MIDDLE BGCOLOR=#C0C0C0>\" +\n"
"                \"<A HREF=javascript:top.loadSites();>\" +\n"
"                  \"<FONT SIZE=2>%s</FONT>\" +\n"
"                \"</A>\" +\n"
"              \"</TD>\" +\n"
"              \"<TD>&nbsp;&nbsp;&nbsp;</TD>\" +\n"
"            \"</TR>\" +\n"
"          \"</TABLE>\" +\n"
"        \"</BODY>\"\n"
"      );\n"
"      top.frames[index_frame].document.close();\n"
"\n",
	    view_cookies, view_sites);
	FLUSH_BUFFER
    }

    /* generate next section of html file */
    if (!host) {
	StrAllocCopy (heading, XP_GetString(MK_ACCESS_COOKIES_ACCEPTED));
    } else {
	StrAllocCopy (heading, XP_GetString(MK_ACCESS_SITE_COOKIES_ACCEPTED));
    }
    g += PR_snprintf(buffer+g, BUFLEN-g,
"      top.frames[title_frame].document.open();\n"
"      top.frames[title_frame].document.write\n"
"        (\"&nbsp;%s\");\n"
"      top.frames[title_frame].document.close();\n"
"\n"
"      top.frames[list_frame].document.open();\n"
"      top.frames[list_frame].document.write(\n"
"        \"<FORM name=fSelectCookie>\" +\n"
"          \"<P>\" +\n"
"          \"<TABLE BORDER=0 WIDTH=100% HEIGHT=95%>\" +\n"
"            \"<TR>\" +\n"
"              \"<TD WIDTH=100% VALIGN=TOP>\" +\n"
"                \"<CENTER>\" +\n"
"                  \"<P>\" +\n"
"                  \"<SELECT NAME=selname \" +\n"
"                          \"SIZE=15 \" +\n"
"                          \"MULTIPLE \" +\n"
"                          \"onchange=top.ViewCookieSelected(selname.selectedIndex);>\"\n"
"      );\n",
	heading);
    FLUSH_BUFFER
    PR_FREEIF(heading);

    /* generate the html for the list of cookies in alphabetical order */
    cookie_list=net_cookie_list;
    cookie = NULL;
    while (cookie = NextCookieAfter(cookie, &cookieNum)) {
	if (!host || net_CookieIsFromHost(cookie, host)) {
            char *fixed_name = net_FixQuoted(cookie->name);
	    g += PR_snprintf(buffer+g, BUFLEN-g,
"      if (!deleted_cookies[%d]) {\n"
"        top.frames[list_frame].document.write(\n"
"                    \"<OPTION value=%d>\" +\n"
"                      \"%s:%s\" +\n"
"                    \"</OPTION>\"\n"
"        );\n"
"      }\n",
		cookieNum, cookieNum,
		(*(cookie->host)=='.') ? (cookie->host)+1: cookie->host,
		fixed_name
		);
	    FLUSH_BUFFER
            PR_FREEIF(fixed_name);
	}
    }
    net_unlock_cookie_list();

    /* generate next section of html file */
    StrAllocCopy (heading, XP_GetString(MK_ACCESS_COOKIES_PERMISSION));
    g += PR_snprintf(buffer+g, BUFLEN-g,
"      top.frames[list_frame].document.write(\n"
"                  \"</SELECT>\" +\n"
"                \"</CENTER>\" +\n"
"              \"</TD>\" +\n"
"            \"</TR>\" +\n"
"          \"</TABLE>\" +\n"
"        \"</FORM>\"\n"
"      );\n"
"      top.frames[list_frame].document.close();\n"
"\n"
"      top.frames[prop_frame].document.open();\n"
"      top.frames[prop_frame].document.close();\n"
"    }\n"
"\n"
"    function loadSites(){\n"
"      cookie_mode = 0;\n"
"      top.frames[index_frame].document.open();\n"
"      top.frames[index_frame].document.write(\n"
"        \"<BODY BGCOLOR=#C0C0C0>\" +\n"
"          \"<TABLE BORDER=0 WIDTH=100%>\" +\n"
"            \"<TR>\" +\n"
"              \"<TD> <SPACER TYPE=HORIZONTAL SIZE=10> </TD>\" +\n"
"              \"<TD ALIGN=CENTER VALIGN=MIDDLE BGCOLOR=#C0C0C0>\" +\n"
"                \"<A HREF=javascript:top.loadCookies();>\" +\n"
"                  \"<FONT SIZE=2>%s</FONT>\" +\n"
"                \"</A>\" +\n"
"              \"</TD>\" +\n"
"              \"<TD>&nbsp;&nbsp;&nbsp;</TD>\" +\n"
"              \"<TD ALIGN=CENTER VALIGN=MIDDLE BGCOLOR=#FFFFFF>\" +\n"
"                \"<FONT SIZE=2 COLOR=#666666>\" +\n"
"                  \"<B>%s</B>\" +\n"
"                \"</FONT>\" +\n"
"              \"</TD>\" +\n"
"              \"<TD>&nbsp;&nbsp;&nbsp;</TD>\" +\n"
"            \"</TR>\" +\n"
"          \"</TABLE>\" +\n"
"        \"</BODY>\"\n"
"      );\n"
"      top.frames[index_frame].document.close();\n"
"\n"
"      top.frames[title_frame].document.open();\n"
"      top.frames[title_frame].document.write\n"
"        (\"&nbsp;%s\");\n"
"      top.frames[title_frame].document.close();\n"
"\n"
"      top.frames[list_frame].document.open();\n"
"      top.frames[list_frame].document.write(\n"
"        \"<FORM name=fSelectSite>\" +\n"
"          \"<P>\" +\n"
"          \"<TABLE BORDER=0 WIDTH=100% HEIGHT=95%>\" +\n"
"            \"<TR>\" +\n"
"              \"<TD WIDTH=100% VALIGN=TOP>\" +\n"
"                \"<CENTER>\" +\n"
"                  \"<P>\" +\n"
"                  \"<SELECT NAME=selname SIZE=15 MULTIPLE> \"\n"
"      );\n",
	view_cookies, view_sites, heading);
    FLUSH_BUFFER
    PR_FREEIF(heading);
    FLUSH_BUFFER

    /* generate the html for the list of cookie permissions */
    cookieNum = 0;
    cookie_permission_list=net_cookie_permission_list;
    net_lock_cookie_permission_list();
    while ( (cookperm=(net_CookiePermissionStruct *)
		      XP_ListNextObject(cookie_permission_list)) ) {
	g += PR_snprintf(buffer+g, BUFLEN-g,
"      if (!deleted_sites[%d]) {\n"
"        top.frames[list_frame].document.write(\n"
"                    \"<OPTION value=\" +\n"
"                      \"%d>\" +\n"
"                      \"%s %s\" +\n"
"                    \"</OPTION>\"\n"
"        );\n"
"      }\n",
	    cookieNum, cookieNum,
            cookperm->permission ? "+" : "-",
	    cookperm->host);
	FLUSH_BUFFER
	cookieNum++;
    }
    net_unlock_cookie_permission_list();

    /* generate next section of html file */
    g += PR_snprintf(buffer+g, BUFLEN-g,
"      top.frames[list_frame].document.write(\n"
"                  \"</SELECT>\" +\n"
"                \"</CENTER>\" +\n"
"              \"</TD>\" +\n"
"            \"</TR>\" +\n"
"          \"</TABLE>\" +\n"
"        \"</FORM>\"\n"
"      );\n"
"      top.frames[list_frame].document.close();\n"
"\n"
"      top.frames[prop_frame].document.open();\n"
"      top.frames[prop_frame].document.close();\n"
"    }\n"
"\n"
"    function loadButtons(){\n"
"      top.frames[button_frame].document.open();\n"
"      top.frames[button_frame].document.write(\n"
"        \"<FORM name=buttons action=internal-dialog-handler method=post>\" +\n"
"          \"<BR>\" +\n"
"          \"&nbsp;\" +\n"
"          \"<INPUT type=BUTTON \" +\n"
"                 \"value=Remove \" +\n"
"                 \"onclick=top.DeleteItemSelected();>\" +\n"
"          \"<DIV align=right>\" +\n"
"            \"<INPUT type=BUTTON value=OK width=80 onclick=parent.clicker(this,window.parent)>\" +\n"
"            \" &nbsp;&nbsp;\" +\n"
"            \"<INPUT type=BUTTON value=Cancel width=80 onclick=parent.clicker(this,window.parent)>\" +\n"
"          \"</DIV>\" +\n"
"          \"<INPUT type=HIDDEN name=xxxbuttonxxx>\" +\n"
"          \"<INPUT type=HIDDEN name=handle value="
	);
    FLUSH_BUFFER

    /* transfer what we have so far into strings->args[0] */
    if (buffer2) {
	XP_SetDialogString(strings, 0, buffer2);
	buffer2 = NULL;
    }

    /* Note: strings->args[1] will later be filled in with value of handle */

    /* generate remainder of html, it will go into strings->arg[2] */
    g += PR_snprintf(buffer+g, BUFLEN-g,
">\" +\n"
"          \"<INPUT TYPE=HIDDEN NAME=goneC SIZE=-1>\" +\n"
"          \"<INPUT TYPE=HIDDEN NAME=goneP SIZE=-1>\" +\n"
"        \"</FORM>\"\n"
"      );\n"
"      top.frames[button_frame].document.close();\n"
"    }\n"
"\n"
"    function loadFrames(){\n"
"      loadCookies();\n"
"      loadButtons();\n"
"    }\n"
"\n"
"    function clicker(but,win){\n"
#ifndef HTMLDialogs 
"      var goneC = top.frames[button_frame].document.buttons.goneC;\n"
"      var goneP = top.frames[button_frame].document.buttons.goneP;\n"
"      var expires = new Date();\n"
"      expires.setTime(expires.getTime() + 1000*60*60*24*365);\n"
"      document.cookie = \"htmldlgs=|\" + but.value +\n"
"        \"|goneC|\" + goneC.value + \"|goneP|\" + goneP.value + \"|\" +\n"
"        \"; expires=\" + expires.toGMTString();\n"
#endif
"      top.frames[button_frame].document.buttons.xxxbuttonxxx.value = but.value;\n"
"      top.frames[button_frame].document.buttons.xxxbuttonxxx.name = 'button';\n"
"      top.frames[button_frame].document.buttons.submit();\n"
"    }\n"
"\n"
"  </SCRIPT>\n"
"</HEAD>\n"
"<FRAMESET ROWS = 25,25,*,75\n"
"         BORDER=0\n"
"         FRAMESPACING=0\n"
"         onLoad=loadFrames()>\n"
"  <FRAME SRC=about:blank\n"
"        NAME=index_frame\n"
"        SCROLLING=NO\n"
"        MARGINWIDTH=1\n"
"        MARGINHEIGHT=1\n"
"        NORESIZE>\n"
"  <FRAME SRC=about:blank\n"
"        NAME=title_frame\n"
"        SCROLLING=NO\n"
"        MARGINWIDTH=1\n"
"        MARGINHEIGHT=1\n"
"        NORESIZE>\n"
"  <FRAMESET COLS=5,*,10,*,5\n"
"           BORDER=0\n"
"           FRAMESPACING=0>\n"
"    <FRAME SRC=about:blank\n"
"          NAME=spacer1_frame\n"
"          SCROLLING=AUTO\n"
"          MARGINWIDTH=0\n"
"          MARGINHEIGHT=0\n"
"          NORESIZE>\n"
"    <FRAME SRC=about:blank\n"
"          NAME=list_frame\n"
"          SCROLLING=AUTO\n"
"          MARGINWIDTH=0\n"
"          MARGINHEIGHT=0\n"
"          NORESIZE>\n"
"    <FRAME SRC=about:blank\n"
"          NAME=spacer2_frame\n"
"          SCROLLING=AUTO\n"
"          MARGINWIDTH=0\n"
"          MARGINHEIGHT=0\n"
"          NORESIZE>\n"
"    <FRAME SRC=about:blank\n"
"          NAME=prop_frame\n"
"          SCROLLING=AUTO\n"
"          MARGINWIDTH=0\n"
"          MARGINHEIGHT=0\n"
"          NORESIZE>\n"
"    <FRAME SRC=about:blank\n"
"          NAME=spacer3_frame\n"
"          SCROLLING=AUTO\n"
"          MARGINWIDTH=0\n"
"          MARGINHEIGHT=0\n"
"          NORESIZE>\n"
"  </FRAMESET>\n"
"  <FRAME SRC=about:blank\n"
"        NAME=button_frame\n"
"        SCROLLING=NO\n"
"        MARGINWIDTH=1\n"
"        MARGINHEIGHT=1\n"
"        NORESIZE>\n"
"</FRAMESET>\n"
"\n"
"<NOFRAMES>\n"
"  <BODY> <P> </BODY>\n"
"</NOFRAMES>\n"
"</HTML>\n"
	);
    FLUSH_BUFFER

    /* free some strings that are no longer needed */
    PR_FREEIF(view_cookies);
    PR_FREEIF(view_sites);
    PR_FREEIF(buffer);

    /* put html just generated into strings->arg[2] and invoke HTML dialog */
    if (buffer2) {
      XP_SetDialogString(strings, 2, buffer2);
      buffer2 = NULL;
    }
    dlg = PORT_ZAlloc(sizeof(CookieViewerDialog));
    if ( dlg == NULL ) {
	return;
    }
    dlg->parent_window = (void *)context;
    dlg->dialogUp = PR_TRUE;
    dlg->state =XP_MakeRawHTMLDialog(context, &dialogInfo, MK_ACCESS_YOUR_COOKIES,
		strings, 1, (void *)dlg);

    return;
}

PUBLIC void
NET_DisplayCookieInfoOfSiteAsHTML(MWContext * context, char * URLName)
{
    char * host;
    char * colon;

    /* remove protocol from URL name */
    host = NET_ParseURL(URLName, GET_HOST_PART);

    /* remove port number from URL name */
    colon = PL_strchr(host, ':');
    if(colon) {
        *colon = '\0';
    }

    net_DisplayCookieInfoAsHTML(context, host);

    /* free up allocated string and return */
    if(colon) {
        *colon = ':';
    }
    PR_Free(host);
}

PUBLIC void
NET_DisplayCookieInfoAsHTML(MWContext * context)
{
    net_DisplayCookieInfoAsHTML(context, NULL);
}

#ifdef XP_MAC
/* pinkerton - reset optimization state (see above) */
#pragma global_optimizer reset
#endif

#else
PUBLIC void
NET_DisplayCookieInfoAsHTML(ActiveEntry * cur_entry)
{
}
#endif
