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

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

#include "rosetta.h"
#include "xp.h"
#include "netutils.h"
#include "mkselect.h"
#include "mktcp.h"
#include "mkpadpac.h"
#include "prerror.h"
#include "net_xp_file.h"
#include "mkprefs.h"

#include "merrors.h"

#include "mkgeturl.h"
#include "httpurl.h"
#include "shist.h"
#include "glhist.h"
#include "mkparse.h"
#include "netstream.h"
#include "mkformat.h"
#include "mkaccess.h"
#include "cookies.h"
#include "netcache.h"
#include "mkautocf.h"
#include "secnav.h"
#include "ssl.h"
#include "jscookie.h"
#include "prefapi.h"
#include "libi18n.h"
#include "prtime.h"
#include "prmem.h"
#include "plstr.h"
#include "timing.h"

#include "xp_error.h"

#if defined(SMOOTH_PROGRESS)
#include "progress.h"
#endif

/* for XP_GetString() */
#include "xpgetstr.h"
extern int MK_REDIRECT_ATTEMPT_NOT_ALLOWED;
extern int MK_HTTP_TYPE_CONFLICT;
extern int MK_OUT_OF_MEMORY;
extern int MK_TCP_READ_ERROR;
extern int MK_TCP_WRITE_ERROR;
extern int MK_CONNECTION_REFUSED;
extern int MK_CONNECTION_TIMED_OUT;
extern int MK_UNABLE_TO_CONNECT;
extern int MK_UNABLE_TO_CONNECT_TO_PROXY;
extern int MK_UNABLE_TO_LOCATE_HOST;
extern int MK_UNABLE_TO_LOCATE_FILE;
extern int MK_UNABLE_TO_OPEN_FILE;
extern int MK_ZERO_LENGTH_FILE;
extern int MK_COMPUSERVE_AUTH_FAILED;
extern int XP_ALERT_UNKNOWN_STATUS;
extern int XP_ERRNO_EWOULDBLOCK;
extern int XP_PROGRESS_TRANSFER_DATA;
extern int XP_PROGRESS_TRYAGAIN;
extern int XP_PROGRESS_WAIT_REPLY;
extern int XP_HR_TRANSFER_INTERRUPTED;
extern int XP_TRANSFER_INTERRUPTED;
extern int XP_ERRNO_EIO;

#ifdef TRUST_LABELS
extern void ProcessCookiesAndTrustLabels( ActiveEntry *ce );
#endif

#ifdef PROFILE
#pragma profile on
#endif

#define MAX_CACHED_HTTP_CONNECTIONS 4

#define INTERNET_KEYWORD_METATAG "Content-keywords"

/* the maximum size of a HTTP servers first line of response
 */
#define MAX_FIRST_LINE_SIZE 250

/* temporary
 */
#if defined(XP_WIN) || defined(XP_OS2)                      /* IBM-SAH */
PUBLIC char *XP_AppName = 0;
PUBLIC char *XP_AppCodeName = 0;
PUBLIC char *XP_AppVersion = 0;
PUBLIC char *XP_AppLanguage = 0;
PUBLIC char *XP_AppPlatform = 0;
#else
PUBLIC CONST char *XP_AppName = 0;
PUBLIC CONST char *XP_AppCodeName = 0;
PUBLIC CONST char *XP_AppVersion = 0;
PUBLIC CONST char *XP_AppLanguage = 0;
PUBLIC CONST char *XP_AppPlatform = 0;
#endif

PUBLIC char * FE_UsersFromField=0;    /* User's name/email address not used yet */

PRIVATE XP_List * http_connection_list=0;
PRIVATE IdentifyMeEnum http_identification_method = DoNotIdentifyMe;
PRIVATE Bool sendRefererHeader=TRUE;

PUBLIC const HTTP_Version DEFAULT_VERSION = ONE_POINT_ONE;
PRIVATE const char *VERSION_STRING_ONE_ONE  = "HTTP/1.1";
PRIVATE const char *VERSION_STRING_ONE_ZERO = "HTTP/1.0";
#define VERSION_STRING_LEN 8

/* definitions of state for the state machine design
 */
typedef enum {
    HTTP_START_CONNECT,
    HTTP_FINISH_CONNECT,
    HTTP_WAIT_FOR_AUTH,
    HTTP_RESUME_WITH_AUTH,
    HTTP_SEND_PROXY_TUNNEL_REQUEST,
    HTTP_BEGIN_UPLOAD_FILE,
    HTTP_SEND_REQUEST,
    HTTP_SEND_POST_DATA,
    HTTP_PARSE_FIRST_LINE,
    HTTP_PARSE_MIME_HEADERS,
    HTTP_SETUP_STREAM,
    HTTP_BEGIN_PUSH_PARTIAL_CACHE_FILE,
    HTTP_PUSH_PARTIAL_CACHE_FILE,
    HTTP_PULL_DATA,
    HTTP_DONE,
    HTTP_ERROR_DONE,
    HG93634
    HTTP_FREE
} StatesEnum;

/* structure to hold data about a tcp connection
 * to a news host
 */
typedef struct _HTTPConnection {
    char   *hostname;       /* hostname string (may contain :port) */
    PRFileDesc *sock;       /* socket */
    XP_Bool busy;           /* is the connection in use already? */
    XP_Bool prev_cache;     /* did this connection come from the cache? */
    XP_Bool secure;         /* is it a secure connection? */
    XP_Bool doNotSendConHdr;
} HTTPConnection;

/* structure to hold data pertaining to the active state of
 * a transfer in progress.
 *
 */
typedef struct _HTTPConData {
    StatesEnum  next_state;       /* the next state or action to be taken */
    char *      proxy_server;     /* name of proxy server if any */
    char *      proxy_conf;       /* proxy config ptr from proxy autoconfig */
    char *      line_buffer;      /* temporary string storage */
    char *      server_headers;   /* headers from the server for 
                                   * the proxy client */
    char *      orig_host;        /* if the host gets modified
                                   * for my "netscape" -> "www.netscape.com"
                                   * hack, the original host gets put here */
    XP_File     partial_cache_fp;
    int32       partial_needed;   /* the part missing from the cache */

    int32       total_size_of_files_to_post;
    int32       total_amt_written;

    int32       line_buffer_size; /* current size of the line buffer */
  
    HTTPConnection *connection;   /* struct to hold info about connection */

    NET_StreamClass * stream; /* The output stream */
    Bool     pause_for_read;   /* Pause now for next read? */
    Bool     send_http1;       /* should we send http/1.1? */
    Bool     acting_as_proxy;  /* are we acting as a proxy? */
    Bool     server_busy_retry; /* should we retry the get? */
    Bool     posting;          /* are we posting? */
    Bool     doing_redirect;   /* are we redirecting? */
    Bool     save_redirect_method;   /* don't change METHOD when redirecting */
    Bool     sent_authorization;     /* did we send auth with the request? */
    Bool     sent_proxy_auth;    /* did we send proxy auth with the req? */
    Bool     authorization_required; /* did we get a 401 auth return code? */
    Bool     proxy_auth_required;    /* did we get a 407 auth return code? */
    Bool     destroy_graph_progress; /* destroy graph progress? */
    Bool     destroy_file_upload_progress_dialog;  
    HTTP_Version  protocol_version;       /* .9 1.0 or 1.1 */ 
    int32    original_content_length; /* the content length at the time of
                                       * calling graph progress */
    TCP_ConData *tcp_con_data;  /* Data pointer for tcp connect state machine */
    Bool         use_proxy_tunnel;          /* should we use a proxy tunnel? */
    Bool         proxy_tunnel_setup_done;   /* is setup done */
    Bool         use_copy_from_cache;    /* did we get a 304? */
    Bool         displayed_some_data;    /* have we displayed any data? */
    Bool         save_connection;        /* save this connection for reuse? */
    Bool         partial_cache_file;
    Bool         reuse_stream;
    Bool         connection_is_valid;
#ifdef XP_WIN
    Bool         calling_netlib_all_the_time;  /* is SetCallNetlibAllTheTime set? */
#endif
    void        *write_post_data_data;   /* a data object 
                                          * for the WritePostData function */
} HTTPConData;


#if 0
/* macro's to simplify variable names */
#define cd->proxy_conf             cd->proxy_conf
#define cd->line_buffer          cd->line_buffer
#define cd->server_headers       cd->server_headers
#define cd->line_buffer_size     cd->line_buffer_size
#define cd->stream             cd->stream
#define cd->send_http1           cd->send_http1
#define cd->acting_as_proxy      cd->acting_as_proxy
#define cd->server_busy_retry      cd->server_busy_retry
#define cd->posting            cd->posting
#define cd->doing_redirect       cd->doing_redirect
#define cd->sent_authorization     cd->sent_authorization
#define cd->sent_proxy_auth        cd->sent_proxy_auth
#define cd->authorization_required        cd->authorization_required
#define cd->proxy_auth_required    cd->proxy_auth_required
#define cd->destroy_graph_progress   cd->destroy_graph_progress
#define cd->original_content_length  cd->original_content_length
#define cd->tcp_con_data       cd->tcp_con_data
#define cd->use_copy_from_cache    cd->use_copy_from_cache
#define cd->displayed_some_data    cd->displayed_some_data
#define ce->socket             ce->socket
#define ce->con_sock         ce->con_sock
#define ce->bytes_received   ce->bytes_received

#endif

#define CD_USE_PROXY_TUNNEL          cd->use_proxy_tunnel
#define CD_PROXY_TUNNEL_SETUP_DONE   cd->proxy_tunnel_setup_done
#define CD_PAUSE_FOR_READ      cd->pause_for_read
#define CD_NEXT_STATE          cd->next_state
#define CD_PROXY_SERVER         cd->proxy_server
#define CE_URL_S         ce->URL_s
#define CE_STATUS           ce->status
#define CE_WINDOW_ID        ce->window_id
#define CE_FORMAT_OUT       ce->format_out


/* forward decl */
PRIVATE int32 net_ProcessHTTP (ActiveEntry *ce);

/*
 * replace
 *    return(ce->status)
 * with
 *    RETURN_ce->status
 */
PRIVATE
int ReturnErrorStatus (int status)
{
  if (status < 0)
    status |= status; /* set a breakpoint HERE to find errors */
  return status;
}

#define STATUS(Status)      ReturnErrorStatus (Status)

#define PUTBLOCK(b, l)  (*cd->stream->put_block) \
                                    (cd->stream, b, l)
#define PUTSTRING(s)    (*cd->stream->put_block) \
                                    (cd->stream, s, PL_strlen(s))
#define COMPLETE_STREAM (*cd->stream->complete) \
                                    (cd->stream)
#define ABORT_STREAM(s) (*cd->stream->abort) \
                                    (cd->stream, s)


PUBLIC void
NET_SetSendRefererHeader(Bool b) {
    sendRefererHeader=b;
}

/* set the method that the user wishes to identify themselves
 * with.
 * Default is DoNotIdentify unless this is called at startup
 */
PUBLIC void 
NET_SetIdentifyMeType(IdentifyMeEnum method) {
    http_identification_method = method;
}

/* Build a string containing the internet keyword extracted from a URL struct.
   inURL is the raw data containing the data
   outKeyword is where the return value will be built.  It will be a
      null-terminated string (of 0 length of there is no keyword(s)).
   inMaxLength is the size of outKeyword.
*/
PUBLIC void
NET_getInternetKeyword(const URL_Struct *inURL, char *outKeyword, int16 inMaxLength) {

    uint        ctr,
                endIndex = inURL->all_headers.empty_index;
    Bool  doCopy;
    char  nextChar,
    *key,
    *header,
    *keyEnd = outKeyword+(inMaxLength-1);

    outKeyword[0] = 0;
    if (inMaxLength <= 0)
        return;

    key = outKeyword;
    for (ctr = 0; ctr < inURL->all_headers.empty_index; ctr++)
        if (PL_strcasecmp(inURL->all_headers.key[ctr], INTERNET_KEYWORD_METATAG) == 0) {
            /* add a separator, if appropriate */
            if (key > outKeyword)
                if (key < keyEnd-1) {
                    *key++ = ';';
                    *key++ = ' ';
                }

            /* append value, stripping modifiers */
            header = inURL->all_headers.value[ctr];
            doCopy = TRUE;
            while (key < keyEnd && (nextChar = *header++) != 0) {
                if (nextChar == ',') /* modifiers follow */
                    doCopy = FALSE;
                else if (nextChar == ';') /* additional keywords follow */
                    doCopy = TRUE;
                if (doCopy)
                    *key++ = nextChar;
            }
        }
    *key = 0;
}

/* look for names like "ford" and "netscape"
 * and turn them into "www.ford.com" and "www.netscape.com"
 * If goBrowsing is enabled, this feature is turned off and the browser
 * asks the search provider to resolve the word into a url. This is 
 * is prevent a kid typing in bambi (looking for the deer) from being
 * taken to a porn site.
 * returns 0 if found and replaced
 */

PRIVATE int
net_check_for_company_hostname(ActiveEntry *ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    Bool add_www = FALSE;
    Bool add_com = FALSE;
    PRBool goBrowsing = FALSE;
   
    if (PREF_GetBoolPref(pref_goBrowsingEnabled, &goBrowsing) != PREF_OK) goBrowsing = 0;

    if(!cd->orig_host) {
        char *dot=NULL;
  
        /* if the hostname doesn't have any
        * dots in it, then assume it's of
        * the form "netscape" or "ford".
        * If we add www. and .com to the front
        * and end we will get www.netscape.com :)
        */
        char * host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
  
        if(host && *host && !(dot = PL_strchr(host, '.'))) {
            add_www = add_com = TRUE;

        } else if(dot && !PL_strchr(dot+1, '.')) {
            /* there is only one dot in the host name
             * so it's probably of the form of "netscape.com"
             * or "ukans.edu".   Add a "www." on the front
             * and try again.
             */
            add_www = TRUE;
        }

        PR_FREEIF(host);
        host = NULL;

        if(add_www) {
            /* no dots in hostname */
            if (goBrowsing && !PL_strchr(ce->URL_s->address, '/')) {     
                char *pUrl;
                if ( (PREF_OK == PREF_CopyCharPref(pref_searchUrl,&pUrl))
                    && pUrl) {
                    char *tmp = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
                    char* new_address = PR_smprintf("%sgo+%s", pUrl, tmp);
                    PR_Free(pUrl);
                    PR_Free(ce->URL_s->address);
                    PR_FREEIF(tmp);  
                    ce->URL_s->address = new_address;
  
                    if(cd->connection->sock != NULL)
                        NET_TotalNumberOfOpenConnections--;
                    return (0);
                } else {
                    return (-1);
                }
            } else {

                char *new_address = NET_ParseURL(ce->URL_s->address, 
                          GET_PROTOCOL_PART);
                char *tmp = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);

                /* save the host for error messages */
                cd->orig_host = tmp;

                StrAllocCat(new_address, "//www.");
                StrAllocCat(new_address, tmp);

                if(add_com)
                    StrAllocCat(new_address, ".com");

                tmp = NET_ParseURL(ce->URL_s->address, 
                        GET_PATH_PART | GET_SEARCH_PART | GET_HASH_PART);
          
                StrAllocCat(new_address, tmp);
                PR_FREEIF(tmp);

                PR_Free(ce->URL_s->address);
                ce->URL_s->address = new_address;

                if(cd->connection->sock != NULL)
                    NET_TotalNumberOfOpenConnections--;

                return(0);  /* will repeat this step */
            }
        }   
    } else {
        /* the host mapping trick has already been applied and failed
         * redo the error message and fail
         */
        ce->URL_s->error_msg = NET_ExplainErrorDetails(
                        MK_UNABLE_TO_LOCATE_HOST, 
                        cd->orig_host);
        /* fall through for failure case */
    }

  return(-1);
}

/* begins the connect process
 *
 * returns the TCP status code
 */
PRIVATE int
net_start_http_connect(ActiveEntry * ce) {
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    HG29898
    int def_port;

    def_port = DEF_HTTP_PORT;

    HG22201

    /* if proxy_server is non NULL then use the string as a host:port
     * when a proxy server is used a connection is made to the proxy
     * host and port and the entire URL is sent instead of just
     * the path part.
     */ 
    if(cd->proxy_server) {
        /* MKConnect can take a URL or a host name as it's first
         * argument
         */
        ce->status = NET_BeginConnect (cd->proxy_server,  
                    NULL,
                    "HTTP", 
                    def_port, 
                    &cd->connection->sock, 
                    HG38738 
                    &cd->tcp_con_data, 
                    ce->window_id, 
                    &ce->URL_s->error_msg,
                    ce->socks_host,
                    ce->socks_port,
                    ce->URL_s->localIP);
    } else {
        ce->status = NET_BeginConnect (ce->URL_s->address,
                    ce->URL_s->IPAddressString,
                    "HTTP", 
                    def_port, 
                        &cd->connection->sock, 
                    HG02873  
                    &cd->tcp_con_data, 
                    ce->window_id, 
                    &ce->URL_s->error_msg,
                    ce->socks_host,
                    ce->socks_port,
                    ce->URL_s->localIP);
    }

    /* set this so mkgeturl can select on it */
    ce->socket = cd->connection->sock;

    if(cd->connection->sock != NULL)
        NET_TotalNumberOfOpenConnections++;

    if (ce->status < 0) {
        if(ce->status == MK_UNABLE_TO_LOCATE_HOST 
            && 0 == net_check_for_company_hostname(ce))
        {
            /* no need to set the state since this
             * is the state we want again
             */
            if(cd->connection->sock != NULL) {
                PR_Close(cd->connection->sock);
                cd->connection->sock = NULL;
                ce->socket = NULL;
            }
            return(0);
        }

        TRACEMSG(("HTTP: Unable to connect to host for `%s' (errno = %d).", 
                          ce->URL_s->address, PR_GetOSError()));
        /*
         * Proxy failover
         */
        if (cd->proxy_conf && cd->proxy_server &&
            (ce->status == MK_UNABLE_TO_CONNECT    ||
             ce->status == MK_CONNECTION_TIMED_OUT ||
             ce->status == MK_CONNECTION_REFUSED   ||
             ce->status == MK_UNABLE_TO_LOCATE_HOST)
        ){
#ifdef MOZILLA_CLIENT
            if (pacf_get_proxy_addr(ce->window_id, cd->proxy_conf,
                        &ce->proxy_addr,
                        &ce->socks_host,
                        &ce->socks_port))
            {
                cd->proxy_server = ce->proxy_addr;
                return net_start_http_connect(ce);
            }
#endif  /* MOZILLA_CLIENT */
        }

        if(ce->status == MK_UNABLE_TO_CONNECT && cd->proxy_server)
        {
            StrAllocCopy (ce->URL_s->error_msg,
              XP_GetString(MK_UNABLE_TO_CONNECT_TO_PROXY));
            ce->status = MK_UNABLE_TO_CONNECT_TO_PROXY;
        }

        cd->next_state = HTTP_ERROR_DONE;
        return STATUS(ce->status);
    } /* end status < 0 */

    if(ce->status == MK_CONNECTED) {
        if(cd->use_proxy_tunnel)
            cd->next_state = HTTP_SEND_PROXY_TUNNEL_REQUEST;
        else if(ce->URL_s->files_to_post)
            cd->next_state = HTTP_BEGIN_UPLOAD_FILE;
        else {
            HG56817
            cd->next_state = HTTP_SEND_REQUEST;
        }
        TRACEMSG(("Connected to HTTP server on sock #%d",cd->connection->sock));
        NET_SetReadSelect(ce->window_id, cd->connection->sock);
    
    } else {
        cd->next_state = HTTP_FINISH_CONNECT;
        cd->pause_for_read = TRUE;
        ce->con_sock = cd->connection->sock;  /* set the socket for select */
        NET_SetConnectSelect(ce->window_id, ce->con_sock);
    }

    return STATUS(ce->status);
}

/*  begins the connect process
 *
 *  returns the TCP status code
 */
PRIVATE int
net_finish_http_connect(ActiveEntry * ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    int def_port;

    def_port = DEF_HTTP_PORT;
    HG92892

    /* if proxy_server is non NULL then use the string as a host:port
     * when a proxy server is used a connection is made to the proxy
     * host and port and the entire URL is sent instead of just
     * the path part.
     */
    if(cd->proxy_server) {
        /* MKConnect can take a URL or a host name as it's first
         * argument
         */
        ce->status = NET_FinishConnect(cd->proxy_server,  
                    "HTTP",
                    def_port, 
                    &cd->connection->sock, 
                    &cd->tcp_con_data, 
                    ce->window_id,
                    &ce->URL_s->error_msg,
                    ce->URL_s->localIP);
    } else {
        ce->status = NET_FinishConnect(ce->URL_s->address,
                    "HTTP",
                    def_port,
                    &cd->connection->sock,  
                    &cd->tcp_con_data, 
                    ce->window_id,
                    &ce->URL_s->error_msg,
                    ce->URL_s->localIP);
    }

    if (ce->status < 0) {
        if(ce->status == MK_UNABLE_TO_LOCATE_HOST 
            && 0 == net_check_for_company_hostname(ce))
        {
            if(cd->connection->sock != NULL) {
                NET_ClearConnectSelect(ce->window_id, cd->connection->sock);
                PR_Close(cd->connection->sock);
                cd->connection->sock = NULL;
                ce->socket = NULL;
                ce->con_sock = NULL;
            }
            cd->next_state = HTTP_START_CONNECT;
            return(0);
        }

        NET_ClearConnectSelect(ce->window_id, ce->con_sock);
        TRACEMSG(("HTTP: Unable to connect to host for `%s' (errno = %d).",
                                              ce->URL_s->address, PR_GetOSError()));

        /*
         * Proxy failover
         */
        if (cd->proxy_conf && cd->proxy_server &&
          (ce->status == MK_UNABLE_TO_CONNECT    ||
           ce->status == MK_CONNECTION_TIMED_OUT ||
           ce->status == MK_CONNECTION_REFUSED   ||
           ce->status == MK_UNABLE_TO_LOCATE_HOST))
        {
#ifdef MOZILLA_CLIENT
            if (pacf_get_proxy_addr(ce->window_id, cd->proxy_conf,
                        &ce->proxy_addr,
                        &ce->socks_host,
                        &ce->socks_port))
            {
                cd->proxy_server = ce->proxy_addr;
                return net_start_http_connect(ce);
            }
#endif  /* MOZILLA_CLIENT */
        }

        /* proxy autodiscovery failover needs to be silent. If we were trying to
         * connect to a pad server and failed, don't report errors, just go on
         * about your business. */
        if(NET_LoadingPac() && NET_UsingPadPac() ) {
            /* Don't try to use the pad pac again. */
            net_UsePadPac(FALSE);
            cd->next_state=HTTP_DONE;
            ce->status=0;
            return STATUS(ce->status);
        }

        if(ce->status == MK_UNABLE_TO_CONNECT && cd->proxy_server)
        {
            StrAllocCopy (ce->URL_s->error_msg,
              XP_GetString(MK_UNABLE_TO_CONNECT_TO_PROXY));
            ce->status = MK_UNABLE_TO_CONNECT_TO_PROXY;
        }

        cd->next_state = HTTP_ERROR_DONE;
        return STATUS(ce->status);
    } /* end status < 0 */

    if(ce->status == MK_CONNECTED) {
        NET_ClearConnectSelect(ce->window_id, ce->con_sock);
        ce->con_sock = NULL;  /* reset the socket so we don't select on it  */
        /* set this so mkgeturl can select on it */
        ce->socket = cd->connection->sock;
        if(cd->use_proxy_tunnel)
            cd->next_state = HTTP_SEND_PROXY_TUNNEL_REQUEST;
        else if(ce->URL_s->files_to_post)
            cd->next_state = HTTP_BEGIN_UPLOAD_FILE;
        else {
            HG43241
            cd->next_state = HTTP_SEND_REQUEST;
        }
        NET_SetReadSelect(ce->window_id, cd->connection->sock);
        TRACEMSG(("Connected to HTTP server on sock #%d",cd->connection->sock));
    } else {
        /* unregister the old ce->socket from the select list
         * and register the new value in the case that it changes
         */
        if(ce->con_sock != cd->connection->sock) {
            NET_ClearConnectSelect(ce->window_id, ce->con_sock);
            ce->con_sock = cd->connection->sock; 
            NET_SetConnectSelect(ce->window_id, ce->con_sock);
        }
  
        cd->pause_for_read = TRUE;
    } 

    return STATUS(ce->status);
}

PRIVATE int
net_send_proxy_tunnel_request (ActiveEntry *ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;
  char *host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
  char *command=0;
  char *auth = NULL;
  const char *ver = VERSION_STRING_ONE_ONE;

    StrAllocCopy(command, "CONNECT ");
  StrAllocCat(command, host);

  if(!PL_strchr(host, ':'))
      {
      char small_buf[20];
    sprintf(small_buf, ":%d", DEF_HTTPS_PORT);
    StrAllocCat(command, small_buf);
    }

  if (ONE_POINT_ONE != DEFAULT_VERSION)
      ver = VERSION_STRING_ONE_ZERO;

  sprintf(command, "%c%s%c", ' ', ver, '\n');
    
  /*
   * Check if proxy is requiring authorization.
   * If NULL, not necessary, or the proxy will return 407 to
   * require authorization.
   *
   */
  if (NULL != (auth=NET_BuildProxyAuthString(ce->window_id,
                         ce->URL_s,
                         cd->proxy_server)))
    {
      char *line = (char *)PR_Malloc(strlen(auth) + 30);
      if (line) {
        sprintf(line, "Proxy-authorization: %s%c%c", auth, CR, LF);
        StrAllocCat(command, line);
        PR_Free(line);
      }
      cd->sent_proxy_auth = TRUE;
      TRACEMSG(("HTTP: Sending proxy-authorization: %s", auth));
    }
  else
    {
      TRACEMSG(("HTTP: Not sending proxy authorization (yet)"));
    }

  {
    char line[200];
    sprintf(line, "User-Agent: %.100s/%.90s" CRLF CRLF,
           XP_AppCodeName, XP_AppVersion);
    StrAllocCat(command, line);
  }

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

  SSL_SetSockPeerID(cd->connection->sock, command);
    ce->status = NET_HTTPNetWrite(cd->connection->sock, command, PL_strlen(command));

  PR_Free(command);

  cd->pause_for_read = TRUE;

  cd->next_state = HTTP_PARSE_FIRST_LINE;

  return(ce->status);
}

#define POST_DATA_BUFFER_SIZE 2048

PRIVATE int32
net_get_size_with_crlf( char *filename, XP_FileType file_type, XP_Bool add_crlf )
{
  XP_File xpfileptr;
  uint32 return_value = 0;
  int line_length;
  char *line;
  char *buffer;
  
  if (!add_crlf)
  {
    /* Don't need to make any cr/lf adjustment, just stat the file and
       return the size. */
    
    XP_StatStruct stat_entry;
    if(-1 == NET_XP_Stat(filename, &stat_entry, file_type))
      return -1;
    else
      return stat_entry.st_size;
  }
  
  xpfileptr = NET_XP_FileOpen( filename, file_type, XP_FILE_READ );
  if (!xpfileptr)
    return -1;
  
  buffer = (char *) PR_Malloc(POST_DATA_BUFFER_SIZE);
  if (!buffer)
    return -1;
  
  do {
    line = NET_XP_FileReadLine(buffer, POST_DATA_BUFFER_SIZE-5, xpfileptr);
    if (!line)
    break;

    line_length = PL_strlen(line);

    if (line_length > 1 && line[line_length-2] == CR && line[line_length-1] == LF)
    {
      /* already ok */
    }
    else if(line_length > 0 && (line[line_length-1] == LF || line[line_length-1] == CR))
    {
        /* increment to account for missing CR or missing LF */
        line_length++;
    }

    return_value += line_length;
  } while (line);

  PR_Free( buffer );
  
  return return_value;
}

/* begin sending a file
 */
PRIVATE int
net_begin_upload_file (ActiveEntry *ce)
{
  HTTPConData * cd = (HTTPConData *) ce->con_data;
    char * filename;
    char * file_to_post;
  char * header=0;
  char * last_slash;
  char * status_msg;
  int i;
  int32 adjusted_file_size = -1;

  if(ce->URL_s->server_status == 401)
    {
    /* retrying with auth, don't change the filename */
    cd->next_state = HTTP_SEND_REQUEST;
    return(0);
    }

  PR_ASSERT(ce->URL_s->files_to_post && ce->URL_s->files_to_post[0]);
  if(!ce->URL_s->files_to_post || !ce->URL_s->files_to_post[0])
    return MK_UNABLE_TO_LOCATE_FILE;

  /* get a filename from the files_to_post array
   */
  for(i=0; ce->URL_s->files_to_post[i]; i++)
    ; /* find the end */

  PR_ASSERT(i>0);

  file_to_post = ce->URL_s->files_to_post[i-1];

  /* zero the file now so that we don't upload it again. */
  ce->URL_s->files_to_post[i-1] = NULL;

#ifdef XP_MAC
    filename = PL_strrchr(file_to_post, '/');
#elif defined(XP_WIN)
    filename = PL_strrchr(file_to_post, '\\');
#else
    filename = PL_strrchr(file_to_post, '/');
#endif

    if(!filename)
        filename = file_to_post;
    else
        filename++; /* go past delimiter */

  /* get the size of the file adjusting for crlf conversion */
  adjusted_file_size = net_get_size_with_crlf(file_to_post, xpFileToPost, ce->URL_s->add_crlf ? ce->URL_s->add_crlf[i-1] : FALSE);
  if (-1 == adjusted_file_size)
    {                                              
    ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_LOCATE_FILE, file_to_post);
    PR_Free(file_to_post);
    return MK_UNABLE_TO_LOCATE_FILE;
    }
    
  status_msg = PR_smprintf("Uploading file %s", filename);
  if(status_msg)
    {
#if defined(SMOOTH_PROGRESS)
    PM_Status(ce->window_id, ce->URL_s, status_msg);
#else
    NET_Progress(ce->window_id, status_msg);
#endif
    PR_Free(status_msg);
    }   

/*#ifdef EDITOR
   FE_SaveDialogSetFilename(ce->window_id, filename);
#endif */  /* EDITOR */

  header = PR_smprintf("Content-Length: %ld" CRLF CRLF, adjusted_file_size);


  PR_FREEIF(ce->URL_s->post_headers);
  /* if header is null this won't crash */
  ce->URL_s->post_headers = header;

    /* If the destination URL is explicitly specified in the post_to array, use it, otherwise
     * generate destination from URL_s->address and filename.  hardts */
    if (ce->URL_s->post_to && ce->URL_s->post_to[i - 1]) {
        PR_Free(ce->URL_s->address); /* We don't use it at all. */
        ce->URL_s->address = ce->URL_s->post_to[i - 1];
        ce->URL_s->post_to[i - 1] = NULL;  /* zero the element */
    }
    else {
       /* strip the filename from the last slash and append the
       * new filename to the URL
       */
      last_slash = PL_strrchr(ce->URL_s->address, '/');
      if(last_slash)
        *last_slash = '\0';
      StrAllocCat(ce->URL_s->address, "/");
      StrAllocCat(ce->URL_s->address, filename);
    }

#ifdef EDITOR
    {
         /* Strip out username and password. from address */
        char *pLocation = NULL;
        if (!NET_ParseUploadURL( ce->URL_s->address, &pLocation, NULL,NULL )) {
            PR_ASSERT(0);
        }        
        if (CLEAR_CACHE_BIT(ce->format_out) != FO_LOCATION_INDEPENDENCE)
            FE_SaveDialogSetFilename(ce->window_id, pLocation);
        PR_Free(pLocation);
   }
#endif /* EDITOR */

  /* make the method PUT */
  ce->URL_s->method = URL_PUT_METHOD;

  /* specify the file to be uploaded */
  StrAllocCopy(ce->URL_s->post_data, file_to_post);

  /* specify that the post data is a file */
  ce->URL_s->post_data_is_file = TRUE;

  /* do the request */
  cd->next_state = HTTP_SEND_REQUEST;
  
  PR_Free(file_to_post);

  return(0);
}

/* build the HTTP request.
 *
 * mallocs a big string that must be free'd
 * returns the string
 */
PRIVATE unsigned int
net_build_http_request (URL_Struct * URL_s,
            FO_Present_Types format_out,
            HTTPConData *cd,
            MWContext *window_id,
            char          ** command)
{

    Bool http1=cd->send_http1;
    char *proxy_server=(cd->use_proxy_tunnel ? NULL : cd->proxy_server);
    Bool posting=cd->posting;

    char line_buffer[4096];  /* buffer */
    char *auth;         /* authorization header string */
    size_t csize;        /* size of command */
    const char *tempURL=NULL;
    char *proxyServer=NULL;
    int tmpSize = 0;
        
    /* begin the request */
    switch(URL_s->method) {
        case URL_MKDIR_METHOD:
#define MKDIR_WORD "MKDIR "
            BlockAllocCopy(*command, MKDIR_WORD, 5);
            csize = sizeof(MKDIR_WORD)-1;
            break;

        case URL_DELETE_METHOD:
#define DELETE_WORD "DELETE "
#define RMDIR_WORD "RMDIR "
            if(URL_s->is_directory) {
                BlockAllocCopy(*command, RMDIR_WORD, 6);
                csize = sizeof(RMDIR_WORD)-1;
            } else {
                BlockAllocCopy(*command, DELETE_WORD, 7);
                csize = sizeof(DELETE_WORD)-1;
            }
            break;

        case URL_POST_METHOD:
#define POST_WORD "POST "
            BlockAllocCopy(*command, POST_WORD, 5);
            csize = sizeof(POST_WORD)-1;
            break;

        case URL_PUT_METHOD:
#define PUT_WORD "PUT "
            BlockAllocCopy(*command, PUT_WORD, 4);
            csize = sizeof(PUT_WORD)-1;
            break;

        case URL_HEAD_METHOD:
#define HEAD_WORD "HEAD "
            BlockAllocCopy(*command, HEAD_WORD, 5);
            csize = sizeof(HEAD_WORD)-1;
            break;
      
        case URL_INDEX_METHOD:
#define INDEX_WORD "INDEX "
            BlockAllocCopy(*command, INDEX_WORD, 6);
            csize = sizeof(INDEX_WORD)-1;
            break;

        case URL_MOVE_METHOD:
#define MOVE_WORD "MOVE "
            BlockAllocCopy(*command, MOVE_WORD, 5);
            csize = sizeof(MOVE_WORD)-1;
            break;

        case URL_COPY_METHOD:
#define COPY_WORD "COPY "
            BlockAllocCopy(*command, COPY_WORD, 5);
            csize = sizeof(COPY_WORD)-1;
            break;

        default:
            PR_ASSERT(0);
            /* fall through to GET */

        case URL_GET_METHOD:
#define GET_WORD "GET "
            BlockAllocCopy(*command, GET_WORD, 4);
            csize = sizeof(GET_WORD)-1;
            break;

        case URL_GETPROPERTIES_METHOD:
#define GETPROPERTIES_WORD "GETPROPERTIES "
            BlockAllocCopy(*command, GETPROPERTIES_WORD, 14);
            csize = sizeof(GETPROPERTIES_WORD)-1;
            break;
    } /* End method switch */


    /* if we are using a proxy gateway copy in the entire URL
     * minus the hash mark */
    if(proxy_server) {

        char *url_minus_hash = NET_ParseURL(URL_s->address, 
              GET_PROTOCOL_PART 
            | GET_USERNAME_PART
            | GET_PASSWORD_PART
            | GET_HOST_PART 
            | GET_PATH_PART 
            | GET_SEARCH_PART);

        if(url_minus_hash) {
            tmpSize = PL_strlen(url_minus_hash);
            BlockAllocCat(*command, (size_t) csize, url_minus_hash, tmpSize);
            csize += tmpSize;
            PR_Free(url_minus_hash);
        }

    } else {
        /* else use just the path and search stuff */
        char *path = NET_ParseURL(URL_s->address, GET_PATH_PART | GET_SEARCH_PART);
        tmpSize = PL_strlen(path);
        BlockAllocCat(*command, csize, path, tmpSize);
        csize += tmpSize;
        PR_Free(path);
    }

    if(http1) {
        const char *ver = VERSION_STRING_ONE_ONE;
        if (ONE_POINT_ONE != DEFAULT_VERSION)
            ver = VERSION_STRING_ONE_ZERO;
        BlockAllocCat(*command, csize, " ", 1);
        csize += 1;
        PR_ASSERT(VERSION_STRING_LEN == PL_strlen(ver));
        BlockAllocCat(*command, csize, ver, VERSION_STRING_LEN);
        csize += VERSION_STRING_LEN;
        /* finish the line */
        BlockAllocCat(*command, csize, CRLF, 2); /* CR LF, as in rfc 977 */
        csize += 2;

        if ((URL_s->etag) && (URL_s->force_reload != NET_SUPER_RELOAD))
        {
            /* add the If-None-Match header */
            PL_strcpy(line_buffer, "If-None-Match: \"");
            tmpSize = PL_strlen(line_buffer);
            BlockAllocCat(*command, csize,
                line_buffer, tmpSize);
            csize += tmpSize;
            tmpSize = PL_strlen(URL_s->etag);
            BlockAllocCat(*command, csize, 
                URL_s->etag,
                tmpSize);
            csize += tmpSize;
            /* Closing " */
            BlockAllocCat(*command, csize,
                "\"",
                1);
            csize += 1;
            BlockAllocCat(*command, csize, CRLF, 2);
            csize += 2;
        }

        if(URL_s->last_modified && URL_s->force_reload != NET_SUPER_RELOAD)
        {
            /* add if modified since
             *
             * right now this is being added even if the server sent pragma: no-cache
             * I'm not sure if this is the right thing to do but it seems
             * very efficient since we can still use the cache.
             */

            /* add tmp character storage for strings in order for assert
             * to work properly in debug compile.
             * IBM compiler does not put "\" to make " work properly.
             */
#ifndef AIX
            char *tmp_str = "http";
#endif
            {
                /* A conversion from time_t to NSPR's int64 is needed in order to
                 * use the NSPR time functions.  URL_Struct should really be changed
                 * to use the NSPR time type instead of time_t.
                 */

                PRTime   timeInSec;
                PRTime timeInUSec;
                PRTime   secToUSec;
                PRExplodedTime expandedTime;

                LL_I2L(timeInSec, URL_s->last_modified);
                LL_I2L(secToUSec, PR_USEC_PER_SEC);
                LL_MUL(timeInUSec, timeInSec, secToUSec);

                PR_ExplodeTime(timeInUSec, PR_GMTParameters, &expandedTime);

                PR_FormatTimeUSEnglish(line_buffer, 400,
                                      "If-Modified-Since: %a, %d %b %Y %H:%M:%S GMT",
                                      &expandedTime);
            }

            /* must not be zero for http URL's 
             * or else I screwed up the cache logic 
             */
#ifndef AIX
            PR_ASSERT(PL_strncasecmp(URL_s->address, tmp_str, 4)
                || URL_s->real_content_length > 0);
#endif

            if(URL_s->real_content_length)
                sprintf(&line_buffer[PL_strlen(line_buffer)], 
                    "; length=%ld" CRLF,
                    URL_s->real_content_length);
            tmpSize = PL_strlen(line_buffer);
            BlockAllocCat(*command, csize, line_buffer, tmpSize);
            csize += tmpSize;

            /* reset the expires since we will want to
             * either get a new one from the server or
             * set it to zero
             */
            URL_s->expires = 0;
        } /* end if last_modified */

        if(URL_s->http_headers) {  /* use headers that were passed in */
            tmpSize = PL_strlen(URL_s->http_headers);
            BlockAllocCat(*command, csize, URL_s->http_headers, tmpSize);
            csize += tmpSize;

        } else {

            /* For MOVE method. This contains the destination uri name */
            int meth=URL_s->method;
            if(URL_s->destination 
                    && (*(URL_s->destination) != '\0')
                    && ( (meth == URL_MOVE_METHOD)
                        || (meth == URL_COPY_METHOD) )
            ){
                if(meth == URL_MOVE_METHOD) {
                    sprintf(line_buffer, "New-uri: %s", URL_s->destination);
                    tmpSize = PL_strlen(line_buffer);
                    BlockAllocCat(*command, csize, line_buffer, tmpSize);
                    csize+=tmpSize;
                } else if(meth == URL_COPY_METHOD) {
                    ;/* some http copy syntax */
                }

                BlockAllocCat(*command, csize, CRLF, 2);
                csize += 2;
            }
            /* sendRefererHeader is set in NET_SetSendRefererHeaderPref in mkhttp.c. 
             This condition is set via a javascript pref and was implemented to 
             settle some privacy/security issue */
            /* NET_SupressRefererForAnonymity is true on the first call after entering
             or leaving anonymous mode and false all other times.  Note that this
             function has a side effect of changing its own state (so that future
             calls to it return false until another mode change occurs).  For this
             reason it must appear first in the if-statement below to make sure it
             gets called even if sendReferHeader is false */
            if(!NET_SupressRefererForAnonymity(window_id) && sendRefererHeader)
            {
                int url_type = NET_URL_Type(URL_s->referer);
                char *newReferer=NULL;
                /* Don't allow these url types to send the referer field. */
                if(URL_s->referer 
                    && url_type != MAILBOX_TYPE_URL
                    && url_type != IMAP_TYPE_URL
                    && url_type != FILE_TYPE_URL)
                {
                    char *colon=NULL, *refToSend=NULL;
                    TRACEMSG(("Sending referer field"));
                    PL_strcpy(line_buffer, "Referer: ");
                    tmpSize = PL_strlen(line_buffer);
                    BlockAllocCat(*command, csize, line_buffer, tmpSize);
                    csize += tmpSize;
          
                    /* The URL_s->referer can contain a username and password as it draws
                     * this data from the session history. The session history can contain 
                     * the username and password if the user types them into the location
                     * field, like so: http://username:password@www.here.com. We strip out
                     * the username and password here so this info isn't revealed in the 
                     * referer header. */

                    colon=PL_strchr(URL_s->referer, ':');
                    if(colon
                        && *(colon+1) == '/'
                        && *(colon+2) == '/'
                        && *(colon+3) != '\0')
                    {
                        char *atSign=NULL;            
                        char *path=NULL;

                        if( (path=PL_strchr(colon+3, '/')) != 0)
                            *path='\0';
                        if( (atSign=PL_strchr(colon, '@')) != 0) {
                            /* We found a username and/or a password, don't let it through */
                            char temp;
                            if(path)
                                *path='/';
                            /* Get the protocol part. */
                            temp=*(colon+3);
                            *(colon+3)='\0';
                            StrAllocCopy(newReferer, URL_s->referer);
                            *(colon+3)=temp;

                            /* Get the host part. */
                            *atSign='\0';
                            StrAllocCat(newReferer, atSign+1);
                            *atSign='@';
                        }
                        if(path)
                            *path='/';
                    } /* End colon */

                    refToSend= newReferer ? newReferer : URL_s->referer;
                    tmpSize = PL_strlen(refToSend);
                    BlockAllocCat(*command,  csize, refToSend, tmpSize);
                    csize += tmpSize;
                    BlockAllocCat(*command, csize, CRLF, 2);
                    csize += 2;
                    PR_FREEIF(newReferer);
                } /* end URL_s->refrerer */
            } /* end supress */

            assert (XP_AppCodeName);
            assert (XP_AppVersion);

            if(!(cd->connection->doNotSendConHdr)) {
                if(proxy_server)
                    PL_strcpy(line_buffer, "Proxy-Connection: Keep-Alive" CRLF);
                else
                    PL_strcpy(line_buffer, "Connection: Keep-Alive" CRLF);
            }

            sprintf (&line_buffer[PL_strlen(line_buffer)], 
                "User-Agent: %.100s/%.90s" CRLF,
                XP_AppCodeName, XP_AppVersion);
            tmpSize = PL_strlen(line_buffer);
            BlockAllocCat(*command, csize, line_buffer, tmpSize);
            csize += tmpSize;

            if(URL_s->force_reload) {
                BlockAllocCat(*command, csize, "Pragma: no-cache" CRLF, 18);
                csize += 18;
            }

            if(URL_s->range_header) {
#ifndef AIX
                char *tmp_str = CRLF;
                PR_ASSERT(!PL_strstr(URL_s->range_header, tmp_str));
#endif
#define REQUEST_RANGE_HEADER "Range: "
                tmpSize = PL_strlen(REQUEST_RANGE_HEADER);
                BlockAllocCat(*command, csize, 
                    REQUEST_RANGE_HEADER,
                    tmpSize);
                csize += tmpSize;
                tmpSize = PL_strlen(URL_s->range_header);
                BlockAllocCat(*command, csize, 
                    URL_s->range_header,
                    tmpSize);
                csize += tmpSize;
                BlockAllocCat(*command, csize, 
                    CRLF, 2);
                csize += 2;
            } /* end url_s->range_header */

#define OLD_RANGE_SUPPORT
#ifdef OLD_RANGE_SUPPORT
            /* this is here for backwards compatibility with
             * the old range spec
             * It should be removed before final beta 2
             */
            if(URL_s->range_header) {
#ifndef AIX
                char *tmp_str = CRLF;
                PR_ASSERT(!PL_strstr(URL_s->range_header, tmp_str));
#endif
#undef REQUEST_RANGE_HEADER
#define REQUEST_RANGE_HEADER "Request-Range: "
                tmpSize = PL_strlen(REQUEST_RANGE_HEADER);
                BlockAllocCat(*command, csize,
                    REQUEST_RANGE_HEADER,
                    tmpSize);
                csize += tmpSize;
                tmpSize = PL_strlen(URL_s->range_header);
                BlockAllocCat(*command, csize,
                    URL_s->range_header,
                    tmpSize);
                csize += tmpSize;
                BlockAllocCat(*command, csize,
                    CRLF, 2);
                csize += 2;
            } /* end url_s->range_header */
#endif

            if(1) {
                char * host = NET_ParseURL(URL_s->address, GET_HOST_PART);

                if(host) {

#define HOST_HEADER "Host: "
                    BlockAllocCat(*command, 
                        csize,  
                        HOST_HEADER,  
                        sizeof(HOST_HEADER)-1);
                    csize += sizeof(HOST_HEADER)-1;
                    tmpSize = PL_strlen(host);
                    BlockAllocCat(*command, csize, host, tmpSize);

                    csize += tmpSize;

                    PR_Free(host);

                    BlockAllocCat(*command, csize, CRLF, 2);
                    csize += 2;
                }
            }

#ifdef SEND_FROM_FIELD
            sprintf(line_buffer, "From: %.256s%c%c", 
                FE_UsersMailAddress() ? FE_UsersMailAddress() : "unregistered", CR,LF);
            tmpSize = PL_strlen(line_buffer);
            BlockAllocCat(*command, csize, line_buffer, tmpSize);
            csize += tmpSize;
#endif /* SEND_FROM_FIELD */

            if(CLEAR_CACHE_BIT(format_out) != FO_INTERNAL_IMAGE)
            {
                /* send Accept: *(slash)* as well as the others */
                sprintf(line_buffer, "Accept: %s, %s, %s, %s, %s, %s, %s, */*" CRLF, 
                    TEXT_HTML, TEXT_XML, IMAGE_PNG, IMAGE_GIF, IMAGE_XBM, IMAGE_JPG, IMAGE_PJPG);
            } else {
                sprintf(line_buffer, "Accept: %s, %s, %s, %s, %s" CRLF, 
                    IMAGE_PNG, IMAGE_GIF, IMAGE_XBM, IMAGE_JPG, IMAGE_PJPG);
            }

            tmpSize = PL_strlen(line_buffer);
            BlockAllocCat(*command, csize, line_buffer, tmpSize);
            csize += tmpSize;

            /* add Accept-Encoding Header */
            sprintf(line_buffer, "Accept-Encoding: %s" CRLF, ENCODING_GZIP2);
            tmpSize = PL_strlen(line_buffer);
            BlockAllocCat(*command, csize, line_buffer, tmpSize);
            csize += tmpSize;

#ifdef MOZILLA_CLIENT
#define SEND_ACCEPT_LANGUAGE 1
#ifdef SEND_ACCEPT_LANGUAGE /* Added by ftang */
            {
                char *acceptlang = INTL_GetAcceptLanguage();
                if((acceptlang != NULL) && ( *acceptlang != '\0') )
                {
                    sprintf(line_buffer, "Accept-Language: %s" CRLF, 
                        acceptlang );
                    tmpSize = PL_strlen(line_buffer);
                    BlockAllocCat(*command, csize, line_buffer, tmpSize);
                    csize += tmpSize;
                }
            }
#endif /* SEND_ACCEPT_LANGUAGE */

#define SEND_ACCEPT_CHARSET 1
#ifdef SEND_ACCEPT_CHARSET  /* Added by bstell */
            {
                char *acceptCharset = INTL_GetAcceptCharset();
                if((acceptCharset != NULL) && ( *acceptCharset != '\0') )
                {
                    sprintf(line_buffer, "Accept-Charset: %s" CRLF, 
                        acceptCharset );
                    tmpSize = PL_strlen(line_buffer);
                    BlockAllocCat(*command, csize, line_buffer, tmpSize);
                    csize += tmpSize;
                }
            }
#endif /* SEND_ACCEPT_CHARSET */
#endif /* MOZILLA_CLIENT */

            /*
             * Check if proxy is requiring authorization.
             * If NULL, not necessary, or the proxy will return 407 to
             * require authorization.
             *
             */
            /* This was hacked in because proxy auth can be required when the Auto-config url
             * itself requires authorization. We used to ask for proxy auth only when the proxy was 
             * input directly into the prefs. Now we check to see if there's a pacurl (if there
             * is, there can't be a proxy url simultaneously) use it, otherwise we're using a
             * proxy from the prefs.
             */

            /* Figure out which kind of proxy we're using: PAC or straight proxy. 
             * DON'T FREE tempURL!!!
             */
            if ( (tempURL = net_GetPACUrl()) != NULL && (*tempURL) )
                proxyServer = NET_ParseURL(tempURL, 
                                  GET_HOST_PART 
                                | GET_PATH_PART
                                | GET_USERNAME_PART
                                | GET_PASSWORD_PART);
            else 
                proxyServer = proxy_server; 

            if (proxyServer &&
                    NULL != (auth=NET_BuildProxyAuthString(window_id,
                             URL_s,
                             proxyServer))
            ){
                if (tempURL)
                    PR_Free(proxyServer);
                cd->sent_proxy_auth = TRUE;
                sprintf(line_buffer, "Proxy-authorization: %.3840s%c%c", auth, CR, LF);
                tmpSize = PL_strlen(line_buffer);
                BlockAllocCat(*command, csize, line_buffer, tmpSize);
                csize += tmpSize;
                TRACEMSG(("HTTP: Sending proxy-authorization: %s", auth));
            } else {
                TRACEMSG(("HTTP: Not sending proxy authorization (yet)"));
            }

            /* NET_BuildAuthString will return non-NULL if authorization is
             * known to be required for this document.
             *
             * If NULL is returned authorization is not required or is
             * not known to be required yet so we will look for a 401
             * return code later on to see if authorization will be
             * neccessary
             */
            if (NULL!=(auth=NET_BuildAuthString(window_id, URL_s))) 
            {
                cd->sent_authorization = TRUE;
                tmpSize = PL_strlen(auth);
                BlockAllocCat(*command, csize, auth, tmpSize);
                csize += tmpSize;
                TRACEMSG(("HTTP: Sending authorization: %s", auth));
            } else {
                TRACEMSG(("HTTP: Not sending authorization (yet)"));
            }

            if (NULL!=(auth=NET_GetCookie(window_id, URL_s->address))) 
            {
                PL_strcpy(line_buffer, "Cookie: ");
                tmpSize = PL_strlen(line_buffer);
                BlockAllocCat(*command, csize, 
                    line_buffer, tmpSize);
        
                csize += tmpSize;
                tmpSize = PL_strlen(auth);
                BlockAllocCat(*command, csize, auth, tmpSize);
                csize += tmpSize;
                BlockAllocCat(*command, csize, CRLF, 2);
                csize += 2;
                TRACEMSG(("HTTP: Sending Cookie: %s", auth));
                PR_Free(auth);
            } else {
                TRACEMSG(("HTTP: Not sending cookie"));
            }

        }  /* end of if passed in headers */
    }  /* end of if(http1) */

    /* end of Get/Post request */

    /* Blank line means "end" of headers
     * If we are posting WritePostData will
     * add the extra line feed for us
     */
    if(!posting) {
        BlockAllocCat(*command, csize, CRLF, 2); 
        csize += 2;
    }

    return((unsigned int) csize);
}

HG12515

/* send_http_request  makes a connection to the http server
 * and sends a request (ethier http1 or http/.9) to the server
 *
 * returns the TCP status code
 */
#define SOCK_CHUNK_SIZE 1024
PRIVATE int
net_send_http_request (ActiveEntry *ce)
{
    /* assign it so that we have a typed structure pointer */
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    char * command=0;
    unsigned int  command_size;

    /* we make the assumption that we are always acting as a proxy
     * server if we get http_headers passed in the URL structure.
     *
     * when acting as a proxy_server the clients headers are sent
     * to the server instead of being generated here and the headers
     * from the server are unparsed and sent on the the client.
     */
    if(ce->format_out == FO_OUT_TO_PROXY_CLIENT
        || ce->format_out == FO_CACHE_AND_OUT_TO_PROXY_CLIENT)
        cd->acting_as_proxy=YES;

    TRACEMSG(("Entered \"send_http_request\""));

    /* we make the assumption that we will always use the POST
     * method if there is post data in the URL structure
     * Is that a bad assumption?
     */
    if(ce->URL_s->method == URL_POST_METHOD
        || ce->URL_s->method == URL_PUT_METHOD) {
        cd->posting = YES;
#ifdef MOZILLA_CLIENT
        SECNAV_Posting(cd->connection->sock);
#endif /* MOZILLA_CLIENT */
    } else if (ce->URL_s->method == URL_HEAD_METHOD) {
#ifdef MOZILLA_CLIENT
        SECNAV_HTTPHead(cd->connection->sock);
#endif /* MOZILLA_CLIENT */
    }
  
    /* zero out associated mime fields in URL structure
     * all except content_length since that may be passed in */
    ce->URL_s->protection_template = 0;
    PR_FREEIF(ce->URL_s->redirecting_url);
    ce->URL_s->redirecting_url = NULL;
    PR_FREEIF(ce->URL_s->authenticate);
    ce->URL_s->authenticate = NULL;
    PR_FREEIF(ce->URL_s->proxy_authenticate);
    ce->URL_s->proxy_authenticate = NULL;

    /* Build the request command.  (It must be free'd!!!) 
     *
     * build_http_request will assemble a string containing
     * the main request line, HTTP/1.0 MIME headers and
     * the post data (if it exits)
     */

    command_size = net_build_http_request(ce->URL_s,
                  ce->format_out,
                  cd, 
                  ce->window_id,
                  &command);

    TRACEMSG(("Sending HTTP Request:\n---------------------------------"));

    TIMING_STARTCLOCK_NAME("http:xfer", ce->URL_s->address);
    TIMING_STARTCLOCK_NAME("http:req", ce->URL_s->address);
    ce->status = (int) NET_BlockingWrite(cd->connection->sock, command, command_size);

#if defined(JAVA)
#if defined(DEBUG)
    if(MKLib_trace_flag) {
        NET_NTrace("Tx: ", 4);
        NET_NTrace(command, command_size);
    }
#endif /* DEBUG */
#endif /* JAVA */
    PR_Free (command);  /* freeing the request */

    if (ce->status < 0) {
        int err = PR_GetError();

        if(err == PR_WOULD_BLOCK_ERROR)
            return(1); /* continue */

        ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, err);

        cd->next_state= HTTP_ERROR_DONE;

        return(MK_TCP_WRITE_ERROR);
    }


    /* make sure these are empty
     */
    PR_FREEIF(cd->line_buffer); /* reset */
    cd->line_buffer = NULL;
    cd->line_buffer_size=0;          /* reset */

    if(cd->posting && ce->URL_s->post_data) {
        NET_ClearReadSelect(ce->window_id, cd->connection->sock);
        NET_SetConnectSelect(ce->window_id, cd->connection->sock);
#ifdef XP_WIN
        cd->calling_netlib_all_the_time = TRUE;
        NET_SetCallNetlibAllTheTime(ce->window_id, "mkhttp");
#endif /* XP_WIN */
        ce->con_sock = cd->connection->sock;
        cd->next_state = HTTP_SEND_POST_DATA;
        return(0);
    }
  
    cd->next_state = HTTP_PARSE_FIRST_LINE;

    /* Don't pause for read any more because we need to do
     * at least a single read right away to detect if the
     * connection is bad.  Apparently windows will queue a
     * write and not return a failure, but in fact the
     * connection has already been closed by the server.
     * Windows select will not even detect the exception
     * so we end up deadlocked.  By doing one read we
     * can detect errors immediately
     *
     * cd->pause_for_read = TRUE;
     */

    {
        char * nonProxyHost = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
        if (nonProxyHost) {
            char* msg = PR_smprintf(XP_GetString(XP_PROGRESS_WAIT_REPLY),
                            nonProxyHost);
            if (msg) {
#if defined(SMOOTH_PROGRESS)
                PM_Status(ce->window_id, ce->URL_s, msg);
#else
                NET_Progress(ce->window_id, msg);
#endif
                PR_Free(msg);
            }
            PR_Free(nonProxyHost);
        }
    }

    return STATUS(ce->status);
} /* end of net_send_http_request */

PRIVATE int
net_http_send_post_data (ActiveEntry *ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    /* Default to not add_crlf_to_line_endings for HTTP. */
    XP_Bool add_crlf = FALSE;
  

  /* make sure the line buffer is large enough
   * for us to use as a buffer
   */
  if(cd->line_buffer_size < 200)
    {
    PR_FREEIF(cd->line_buffer);
    cd->line_buffer = (char*)PR_Malloc(256);
    cd->line_buffer_size = 256;
    }

    /* If the add_crlf field is set in the URL struct, find the
       entry corresponding to the current file being posted. */
    if (ce->URL_s->files_to_post && ce->URL_s->add_crlf) {
      int n = 0;
      while (ce->URL_s->files_to_post[n]) {
        n++;
      }
      /* n should now point after the last entry in files_to_post,
         which is the current file being uploaded. */      
      add_crlf = ce->URL_s->add_crlf[n];
    }

    TIMING_STARTCLOCK_NAME("http:post", ce->URL_s->address);

    /* returns 0 on done and negative on error
     * positive if it needs to continue.
     */
    ce->status = NET_WritePostData(ce->window_id, ce->URL_s,
                                  cd->connection->sock,
                  &cd->write_post_data_data,
                  add_crlf);

    cd->pause_for_read = TRUE;

    if(ce->status == 0)
      {
        /* normal done
         */
        TRACEMSG(("End of post data data"));
        TIMING_STOPCLOCK_NAME("http:post", ce->URL_s->address, ce->window_id, "ok");

    /* make sure these are empty
     */
    PR_FREEIF(cd->line_buffer); /* reset */
    cd->line_buffer = NULL;
    cd->line_buffer_size=0;          /* reset */

        NET_ClearConnectSelect(ce->window_id, cd->connection->sock);
        NET_SetReadSelect(ce->window_id, cd->connection->sock);
    ce->con_sock = NULL;

    {
      char * nonProxyHost = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
      if (nonProxyHost) {
        char* msg = PR_smprintf(XP_GetString(XP_PROGRESS_WAIT_REPLY),
                    nonProxyHost);
        if (msg) {
#if defined(SMOOTH_PROGRESS)
          PM_Status(ce->window_id, ce->URL_s, msg);
#else
          NET_Progress(ce->window_id, msg);
#endif
          PR_Free(msg);
        }
        PR_Free(nonProxyHost);
      }
    }

      cd->next_state = HTTP_PARSE_FIRST_LINE;
        return(0);
      }
  else if(cd->total_size_of_files_to_post && ce->status > 0)
    {
      cd->total_amt_written += ce->status;
#if defined(SMOOTH_PROGRESS)
      PM_Progress(ce->window_id,
                  ce->URL_s,
                  cd->total_amt_written,
                  cd->total_size_of_files_to_post);
#else
        FE_GraphProgress(ce->window_id, 
             ce->URL_s, 
             cd->total_amt_written, 
             ce->status, 
             cd->total_size_of_files_to_post);
    FE_SetProgressBarPercent(ce->window_id, 
        cd->total_amt_written*100/cd->total_size_of_files_to_post);
#endif
    }



    return(ce->status);
}

/* parse_http_mime_headers
 *
 * parses the mime headers as they are received from the
 * HTTP server.
 *
 * Returns the TCP status code
 * 
 */

PRIVATE int 
net_parse_http_mime_headers (ActiveEntry *ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    char *line;
    char *value;

    ce->status = NET_BufferedReadLine(cd->connection->sock, &line, &cd->line_buffer, 
                              &cd->line_buffer_size, &cd->pause_for_read);

    if(ce->status < 0) {
        NET_ExplainErrorDetails(MK_TCP_READ_ERROR, PR_GetOSError());
        /* return TCP error */
        return MK_TCP_READ_ERROR;
    }

    if(ce->status == 0) {
        /* if this is set just return so  we can use
         * the cached copythat
         */
        if(cd->use_copy_from_cache) {
            /* clear the URL content fields so that
             * the 304 object doesn't effect the actual
             * cache file
             */
            if(!ce->URL_s->preset_content_type) {
                PR_FREEIF(ce->URL_s->content_type);
                ce->URL_s->content_type = NULL;
            }
            ce->URL_s->content_length = 0;
            ce->URL_s->real_content_length = 0;
            ce->URL_s->last_modified = 0;
            PR_FREEIF(ce->URL_s->content_encoding);
            ce->URL_s->content_encoding = NULL;
            PR_FREEIF(ce->URL_s->transfer_encoding);
            ce->URL_s->transfer_encoding=NULL;
            PR_FREEIF(ce->URL_s->content_name);
            ce->URL_s->content_name = NULL;
            cd->next_state = HTTP_DONE;
            cd->pause_for_read = FALSE;
            return(MK_USE_COPY_FROM_CACHE);
        }

        /* no mo data */
        if(cd->use_proxy_tunnel && !cd->proxy_tunnel_setup_done)
        {
            /* we have now successfully initiated a proxy tunnel
             * connection to a remote host
             *
             * now we need to give the file descriptor
             * to the xxx library and let it initiate
             * a secure connection
             * after that we need to send a normal
             * http request
             *
             * ADD STUFF HERE KIPP!!!!
             *
             * fd is cd->connection->sock
             */
            if(ce->URL_s->files_to_post)
                cd->next_state = HTTP_BEGIN_UPLOAD_FILE;
            else
                cd->next_state = HTTP_SEND_REQUEST;
            cd->proxy_tunnel_setup_done = TRUE;

        } else if(ce->URL_s->files_to_post) {

            if(ce->URL_s->files_to_post[0]
                && ce->URL_s->server_status/100 == 2)
            {
                if(ce->URL_s->can_reuse_connection) {
                    cd->next_state = HTTP_BEGIN_UPLOAD_FILE;
                } else {
                    NET_ClearReadSelect(ce->window_id, cd->connection->sock);
                    NET_TotalNumberOfOpenConnections--;

                    /* close the old connection
                     */
                    PR_Close(cd->connection->sock);
                    cd->connection->sock = NULL;

                    cd->next_state = HTTP_START_CONNECT;
                }
            } else {
                cd->next_state = HTTP_DONE;
            }
        } else {
            cd->next_state = HTTP_SETUP_STREAM;
        }
        cd->pause_for_read = FALSE;
        return(0);
    } /* end status == 0 */

    if(!line)
        return(0);  /* wait for next line */

    TRACEMSG(("parse_http_mime_headers: Parsing headers, got line: %s",line));

    if(cd->acting_as_proxy) {
        /* save all the headers so we can pass them to the client
         * when neccessary
         */
        StrAllocCat(cd->server_headers, line);
        StrAllocCat(cd->server_headers, "\r\n");  /* add the \n back on */
    }

    /* check for end of MIME headers */
    if(*line == '\0' || *line == '\r') {
        TRACEMSG(("Finished parsing MIME headers"));

        cd->pause_for_read = FALSE;
        HG07606
        if(ce->URL_s->files_to_post
            && ce->URL_s->server_status/100 == 2)
        {
            if(ce->URL_s->files_to_post[0]) {
                if(ce->URL_s->can_reuse_connection) {
                    cd->next_state = HTTP_BEGIN_UPLOAD_FILE;
                } else {
                    NET_ClearReadSelect(ce->window_id, cd->connection->sock);
                    NET_TotalNumberOfOpenConnections--;
        
                    /* close the old connection */
                    PR_Close(cd->connection->sock);
                    cd->connection->sock = NULL;

                    cd->next_state = HTTP_START_CONNECT;
                }
            } else {
                cd->next_state = HTTP_DONE;
            }
        } else {
            cd->next_state = HTTP_SETUP_STREAM;
        }

        return(0);
    }

    value = PL_strchr(line, ':');
    if(value)
        value++;
    NET_ParseMimeHeader(ce->format_out, ce->window_id, ce->URL_s, line, value, TRUE);

    /* If the headers returned from the server indicate that this is a persistant
     * connection, we don't need to send the "Connection: keep-alive" header
     * on subsequent requests to this server over this connection. So, set the 
     * HTTPConnection data variable accordingly. This only works when the server is
     * speaking 1.1. */
    if( (cd->protocol_version == ONE_POINT_ONE) && ce->URL_s->can_reuse_connection)
        cd->connection->doNotSendConHdr=TRUE;
    return(1);
}

/* setup HTTP/1.1 specific protocol defaults */
PRIVATE void
net_setup_http11_defaults(ActiveEntry *ce) {
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    ce->URL_s->can_reuse_connection = TRUE;
    return;
}

/* parses the first line of the http server's response
 * and determines if it is an http1 or http/.9 server
 *
 * returns the tcp status code
 */
PRIVATE int 
net_parse_first_http_line (ActiveEntry *ce) {
    char *line_feed=0;
    char *ptr;
    int line_size;
    int num_fields;
    char server_version[36];
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    char small_buf[MAX_FIRST_LINE_SIZE+16];
    Bool do_redirect = TRUE;

    TRACEMSG(("Entered: net_parse_first_http_line"));

    ce->status = PR_Read(cd->connection->sock, small_buf, MAX_FIRST_LINE_SIZE+10);

    if(ce->status == 0) {
        /* the server dropped the connection? */
        ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_ZERO_LENGTH_FILE);
        cd->next_state = HTTP_ERROR_DONE;
        cd->pause_for_read = FALSE;
        return(MK_ZERO_LENGTH_FILE);

    } else if(ce->status < 0) { 

        int s_error = PR_GetError();
        if (s_error == PR_WOULD_BLOCK_ERROR) {
            cd->pause_for_read = TRUE;
            return(0);
        } else {
            ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, s_error);
            /* return TCP error */
            return MK_TCP_READ_ERROR;
        }
    }

    HG21899

#ifdef MOZILLA_CLIENT
    HG19088
#endif /* MOZILLA_CLIENT */
  
    /* ce->status greater than 0 */

    TIMING_STOPCLOCK_NAME("http:req", ce->URL_s->address,
                          ce->window_id, "response received");
    BlockAllocCat(cd->line_buffer, cd->line_buffer_size, small_buf, ce->status);
    cd->line_buffer_size += ce->status;

    for(ptr = cd->line_buffer, line_size=0; line_size < cd->line_buffer_size; ptr++, line_size++)
        if(*ptr == LF) {
            line_feed = ptr;
            break;
        }

    /* assume HTTP/.9 until we know better */
    cd->protocol_version = POINT_NINE;

    if(line_feed) {
        *server_version = 0;

        *line_feed = '\0';
    
        num_fields = sscanf(cd->line_buffer, "%20s %d", server_version, &ce->URL_s->server_status);
    
        TRACEMSG(("HTTP: Scanned %d fields from first_line: %s", num_fields, cd->line_buffer));
    
        /* Try and make sure this is an HTTP/1.0 reply */
        if (num_fields == 2 || !PL_strncmp("HTTP/", server_version, 5))
        {
            double ver = atof(server_version+5);

            if(ver > 1.0) {
                /* HTTP1.1 */
                cd->protocol_version = ONE_POINT_ONE;

                net_setup_http11_defaults(ce);

                PR_ASSERT(ver == 1.1);

            } else {
                /* HTTP1 */
                cd->protocol_version = ONE_POINT_O;

                PR_ASSERT(ver == 1.0  || ver == 0.0); /* allow 0 bug */
            }
        }

        /* put the line back the way it should be 
         */
        *line_feed = LF;

    } else if(cd->line_buffer_size < MAX_FIRST_LINE_SIZE) {
        return(0); /* not ready to process */
    }

    if(cd->connection->prev_cache && cd->protocol_version == POINT_NINE)
    {
        /* got a non HTTP/1.0 or above response from
         * server that must support HTTP/1.0 since it
         * supports keep-alive.  The connection
         * must be in a bad state now so
         * restart the whole thang by going to the ERROR state
         */
        cd->next_state = HTTP_ERROR_DONE;
    }

    /* if we are getting a successful read here
     * then the connection is valid
     */
    cd->connection_is_valid = TRUE;

    if(cd->protocol_version == POINT_NINE)
    {  /* HTTP/0.9 */
        NET_cinfo * ctype;

        TRACEMSG(("Receiving HTTP/0.9"));
        
        ce->URL_s->content_length = 0;
        ce->URL_s->real_content_length = 0;
        PR_FREEIF(ce->URL_s->content_encoding);
        ce->URL_s->content_encoding = NULL;
        PR_FREEIF(ce->URL_s->content_name);
        ce->URL_s->content_name = NULL;

        if(!ce->URL_s->preset_content_type) {
            PR_FREEIF(ce->URL_s->content_type);
            ce->URL_s->content_type = NULL;

            /* fake the content_type since we can't get it */
            ctype = NET_cinfo_find_type(ce->URL_s->address);

            /* treat unknown types as HTML */
            if(ctype->is_default)
                StrAllocCopy(ce->URL_s->content_type, TEXT_HTML);
            else
                StrAllocCopy(ce->URL_s->content_type, ctype->type);

            /* fake the content_encoding since we can't get it */
            StrAllocCopy(ce->URL_s->content_encoding, 
                (NET_cinfo_find_enc(ce->URL_s->address))->encoding);
        }

    cd->next_state = HTTP_SETUP_STREAM;

    } else {
        /* Decode full HTTP/1.0 or 1.1 response */
    
        TRACEMSG(("Receiving HTTP1 reply, status: %d", ce->URL_s->server_status));

        if(ce->URL_s->server_status != 304
            && (!cd->use_proxy_tunnel || cd->proxy_tunnel_setup_done))
        {
            /* if we don't have a 304, zero all
             * the content headers so that they
             * don't interfere with the
             * incoming object
             *
             * don't zero these in the case of a proxy tunnel
             * since this isn't the real request this
             * is just the connection open request response
             */
            if(!ce->URL_s->preset_content_type) {
                PR_FREEIF(ce->URL_s->content_type);
                ce->URL_s->content_type = NULL;
            }
            ce->URL_s->content_length = 0;
            ce->URL_s->real_content_length = 0;
            PR_FREEIF(ce->URL_s->content_encoding);
            ce->URL_s->content_encoding = NULL;
            PR_FREEIF(ce->URL_s->transfer_encoding);
            ce->URL_s->transfer_encoding=NULL;
            PR_FREEIF(ce->URL_s->content_name);
            ce->URL_s->content_name = NULL;
        }
 
        switch (ce->URL_s->server_status / 100) {
            case 1:
                if(ce->URL_s->server_status == 100) {
                    char * endOfResp;
                    char tmp;
                    int curRespSize=0;

                    /* We just got a 100 Continue response from the server.
                     * Skip it. We don't do any special handling. We're not
                     * dealing with the status line or any headers sent with
                     * the response. */

                    /* Get the length of the response as a string. The end
                     * of the response is a double CRLF sequence. */

                    endOfResp=PL_strstr(cd->line_buffer, CRLF CRLF);

                    /* If we can't find the end of the response, either we
                     * haven't received it all yet, or it's a malformed
                     * resposne. */
                    if(endOfResp) {
                        endOfResp+=4;
                        tmp=*endOfResp;
                        *endOfResp='\0';
                        curRespSize=PL_strlen(cd->line_buffer);
                        *endOfResp=tmp;

                        cd->line_buffer_size -= curRespSize;
                        PR_ASSERT(cd->line_buffer_size >= 0);
                        if(cd->line_buffer_size > 0)
                            memmove(cd->line_buffer, endOfResp, cd->line_buffer_size);
                    }

                    /* by not setting cd->next_state to something different
                     * we will come back to this function and look for another
                     * first line.
                     */
                    return(0);
                }
                break;

            case 2:   /* Succesful reply */

                /* Since we
                 * are getting a new copy, delete the old one
                 * from the cache
                 */
#ifdef MOZILLA_CLIENT
/*                NET_RemoveURLFromCache(ce->URL_s); */
#endif


                if((ce->URL_s->server_status == 204
                    || ce->URL_s->server_status == 201)
                    && !cd->acting_as_proxy)
                {
                    if(ce->URL_s->files_to_post && ce->URL_s->files_to_post[0])
                    {
                        /* fall through since we need to get
                         * the headers to complete the
                         * file transfer
                         */
                    } else {
                        ce->status = MK_NO_ACTION;
                        cd->next_state = HTTP_ERROR_DONE;
                        return STATUS(ce->status);
                    }
                } else if(cd->partial_cache_file
                            && ce->URL_s->range_header
                            && ce->URL_s->server_status != 206)
                {
                    /* we asked for a range and got back
                     * the whole document.  Something
                     * went wrong, error out.
                     */
                    ce->status = MK_TCP_READ_ERROR;
                    ce->URL_s->error_msg = NET_ExplainErrorDetails(
                              MK_TCP_READ_ERROR, 
                              XP_ERRNO_EIO);
                    cd->next_state = HTTP_ERROR_DONE;
                    return(ce->status);
                }

                break;
                
            case 3:   /* redirection and other such stuff */

                if(!cd->acting_as_proxy 
                    && (ce->URL_s->server_status == 301 
                        || ce->URL_s->server_status == 302))
                {
                    /* Redirect with GET only (change POST to get)
                     *
                     * Supported within the HTTP module.  
                     * Will retry after mime parsing 
                     */
                    TRACEMSG(("Got Redirect code"));
                    cd->doing_redirect = TRUE;
                }
                if(!cd->acting_as_proxy && ce->URL_s->server_status == 307)
                {
                    /* Redirect without changing METHOD
                     * 
                     * Supported within the HTTP module.  
                     * Will retry after mime parsing 
                     */
                    TRACEMSG(("Got Redirect code"));
                    cd->doing_redirect = TRUE;
                    cd->save_redirect_method = TRUE;

                } else if(ce->URL_s->server_status == 304)
                {
                    /* use the copy from the cache since it wasn't modified 
                     *
                     *  note: this will work with proxy clients too
                     */
                    if(ce->URL_s->last_modified)
                    {
#ifdef MOZILLA_CLIENT
                        /* check to see if we just now entered a secure space
                         *
                         * don't do if this is coming from history
                         * don't do this if about to redirect
                         */
                        if(HG82773
                            && (ce->format_out == FO_CACHE_AND_PRESENT 
                                || ce->format_out == FO_PRESENT)
                            && !ce->URL_s->history_num)
                        {
                            History_entry * h = SHIST_GetCurrent(&ce->window_id->hist);
                            HG03903
                        }
#endif /* MOZILLA_CLIENT */

                        cd->use_copy_from_cache = TRUE;
                        /* no longer return since we need to parse
                         * headers from the server
                         *
                         * return(MK_USE_COPY_FROM_CACHE);  
                         */
                    } /* end url_s->last_modified */

                    /* else continue since the server messed up
                     * and sent us a 304 even without us having sent
                     * it an if-modified-since header
                     */
                }
                break;
                
            case 4:    /* client error */

                ce->URL_s->preset_content_type = FALSE;

                if(ce->URL_s->server_status == 401 && !cd->acting_as_proxy)
                {
                    /* never do this if acting as a proxy 
                     * If we are a proxy then just pass on 
                     * the headers and the document.
                     *
                     * if authorization_required is set we will check
                     * below after parsing the MIME headers to see
                     * if we should redo the request with an authorization
                     * string 
                     */
                    cd->authorization_required = TRUE;

                    /* Since we
                     * are getting a new copy, delete the old one
                     * from the cache
                     */
#ifdef MOZILLA_CLIENT
                    NET_RemoveURLFromCache(ce->URL_s);
#endif

                    /* we probably want to cache this unless
                     * the user chooses not to authorize himself
                     */
                } else if (ce->URL_s->server_status == 407 && !cd->acting_as_proxy)
                {
                    /* This happens only if acting as a client */
                    cd->proxy_auth_required = TRUE;

                    /* we probably want to cache this unless
                     * the user chooses not to authorize himself
                     */
                } else {
                    /* don't cache unless we have a succesful reply */
                    TRACEMSG(("Server did not return success: NOT CACHEING!!"));
                    ce->URL_s->dont_cache = TRUE;

                    /* all other codes should be displayed */
                }
                break;
    
            case 5:    /* server error code */
                TRACEMSG(("Server did not return success: NOT CACHEING!!!"));
                ce->URL_s->dont_cache = TRUE;
                ce->URL_s->preset_content_type = FALSE;
#ifdef DO_503
                if(ce->URL_s->server_status == 503 && !cd->acting_as_proxy)
                {
                    cd->server_busy_retry = TRUE;
                }
#endif /* DO_503 */
        
                /* display the error results */
                break;
                
            default:  /* unexpected reply code */
                {
                    char message_buffer[256];
                    sprintf(message_buffer,
                    XP_GetString(XP_ALERT_UNKNOWN_STATUS),
                        ce->URL_s->server_status);
                    FE_Alert(ce->window_id, message_buffer);

                    /* don't cache unless we have a succesful reply */
                    TRACEMSG(("Server did not return 200: NOT CACHEING!!!"));
                    ce->URL_s->dont_cache = TRUE;
                }
                break;
        } /* Switch on server_status/100 */

        cd->next_state = HTTP_PARSE_MIME_HEADERS;

      } /* Full HTTP reply */

    return(0); /* good */
}

/* if we were posting a file and received an error put the
 * current file we were posting back on the end of the list
 * so that our state is correctly reset
 */
PRIVATE void
net_revert_post_data(ActiveEntry * ce)
{
  if(ce->URL_s->files_to_post && ce->URL_s->post_data)
  {
    /* find the end of the files to post array */
    int index=0;

    for(; ce->URL_s->files_to_post[index]; index++)
      ; /* null body */

    /* this will not explode even if the malloc fails */
    ce->URL_s->files_to_post[index] = PL_strdup(ce->URL_s->post_data);          

    ce->URL_s->files_to_post[index+1] = NULL;

    if(ce->URL_s->post_to)
    {
      ce->URL_s->post_to[index] = PL_strdup(ce->URL_s->address);

      ce->URL_s->post_to[index+1] = NULL;
    }
  }
}
   
/* forward declaration */
PRIVATE int net_finish_setup_http_stream(ActiveEntry * ce);

/* sets up the stream and performs special actions like redirect and
 * retry on authorization
 *
 * returns the tcp status code
 */
PRIVATE int
net_setup_http_stream(ActiveEntry * ce) {
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    NET_AuthClosure * auth_closure;
    XP_Bool need_to_do_again = FALSE;

    TRACEMSG(("NET_ProcessHTTP: setting up stream: %s", ce->URL_s->address));

    /* save this since it can be changed in lots
     * of places.  This will be used for graph progress
     * and to terminate the tranfer
     */
    if(!ce->URL_s->high_range)
        cd->original_content_length = ce->URL_s->content_length;
    else
        cd->original_content_length = ce->URL_s->high_range 
            - ce->URL_s->low_range 
            + 1;

    if ((ce->URL_s->method == URL_HEAD_METHOD) && 
        ( cd->authorization_required == FALSE))
    {
        /* We only wanted the head, so we should stop doing anything else. */
        cd->next_state = HTTP_DONE;
        return 0;
    }

    /* if this is set just return so that we can use
     * the cached copy
     */
    if(cd->use_copy_from_cache) {
        /* if this is a partial cache file situation
         * then we will keep going and switch later
         * on in this file.
         *
         * If it's not a partial cache file then
         * leave the HTTP module and go to the
         * file module to display the file
         */
        if(!cd->partial_cache_file) {
            /* clear the URL content fields so that
             * the 304 object doesn't effect the actual
             * cache file
             */
            if(!ce->URL_s->preset_content_type) {
                PR_FREEIF(ce->URL_s->content_type);
                ce->URL_s->content_type = NULL;
            }
            ce->URL_s->content_length = 0;
            ce->URL_s->real_content_length = 0;
            PR_FREEIF(ce->URL_s->content_encoding);
            ce->URL_s->content_encoding = NULL;
            PR_FREEIF(ce->URL_s->transfer_encoding);
            ce->URL_s->transfer_encoding=NULL;
            PR_FREEIF(ce->URL_s->content_name);
            ce->URL_s->content_name = NULL;
            cd->next_state = HTTP_DONE;
            cd->pause_for_read = FALSE;
            return(MK_USE_COPY_FROM_CACHE);  
        } else {
            /* set the correct content length so that
             * the cache gets it right
             */
            ce->URL_s->content_length = ce->URL_s->real_content_length;
        }
    } /* end cd->use_copy_from_cache */
     
    /* do we need to start the tranfer over with authorization? 
     */
    if(cd->authorization_required) {
        /* clear to prevent tight loop */
        int status;

        NET_ClearReadSelect(ce->window_id, cd->connection->sock);

#if defined(SMOOTH_PROGRESS)
        PM_Suspend(ce->window_id, ce->URL_s);
#endif

        /* tuck the ce away for the auth callback */
        /* set up the auth closure struct for the password dialog */
		auth_closure = PR_NEWZAP(NET_AuthClosure);
        if (!auth_closure) {
          return(MK_INTERRUPTED);
        }
        
        auth_closure->_private = (void *) ce;
        auth_closure->msg = NULL;
        auth_closure->user = NULL;
        auth_closure->pass = NULL;

        status = NET_AskForAuthString(ce->window_id, 
                    ce->URL_s, 
                    ce->URL_s->authenticate,
                    ce->URL_s->protection_template,
                    cd->sent_authorization,
                    (void *) auth_closure);

#if defined(SMOOTH_PROGRESS)
        PM_Resume(ce->window_id, ce->URL_s);
#endif

        if (status == NET_RETRY_WITH_AUTH) {
            need_to_do_again = TRUE;
        } else if (status == NET_WAIT_FOR_AUTH) {
          if (!ce->URL_s->password)
            cd->next_state = HTTP_WAIT_FOR_AUTH;
          return(0);
        }
        else
            ce->URL_s->dont_cache = TRUE;

        NET_SetReadSelect(ce->window_id, cd->connection->sock);
    }
#if defined(XP_WIN) && defined(MOZILLA_CLIENT)

#define COMPUSERVE_HEADER_NAME "Remote-Passphrase"

    else if(ce->URL_s->authenticate && !PL_strncasecmp(ce->URL_s->authenticate, 
                          COMPUSERVE_HEADER_NAME, 
                          sizeof(COMPUSERVE_HEADER_NAME) - 1))
    {
        /* compuserve auth requires us to send all authenticate
         * headers into their code to verify the authentication
         */
        int status = WFE_DoCompuserveAuthenticate(ce->window_id, 
                        ce->URL_s, 
                        ce->URL_s->authenticate);

        if(status == NET_AUTH_FAILED_DONT_DISPLAY) {                        
            ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_COMPUSERVE_AUTH_FAILED);
            return(MK_COMPUSERVE_AUTH_FAILED);      
        }
    }
#endif /* XP_WIN and MOZILLA_CLIENT */
          
    if(cd->proxy_auth_required) {
        /* This was hacked in because proxy auth can be required when the Auto-config url
         * itself requires authorization. We used to ask for proxy auth only when the proxy was 
         * input directly into the prefs. Now we check to see if there's a pacurl (if there
         * is, there can't be a proxy url simultaneously) use it, otherwise we're using a
         * proxy from the prefs.
         */
        const char *tempURL=NULL;
        char *proxyServer=NULL;

        /* Figure out which kind of proxy we're using: PAC or straight proxy. 
         * DON'T FREE tempURL!!!
         */
        if ( (tempURL = net_GetPACUrl()) != NULL && (*tempURL) )
            proxyServer = NET_ParseURL(tempURL, GET_HOST_PART | GET_PATH_PART | GET_USERNAME_PART | GET_PASSWORD_PART);
        else
            proxyServer = cd->proxy_server;

#if defined(SMOOTH_PROGRESS)
        PM_Suspend(ce->window_id, ce->URL_s);
#endif

        /* tuck the ce away for the auth callback */
        /* set up the auth closure struct for the password dialog */
		auth_closure = PR_NEWZAP(NET_AuthClosure);
        if (!auth_closure) {
          return(MK_INTERRUPTED);
        }
        
        auth_closure->_private = (void *) ce;
        auth_closure->msg = NULL;
        auth_closure->user = NULL;
        auth_closure->pass = NULL;

        if(NET_AskForProxyAuth(ce->window_id, 
                               proxyServer,
                               ce->URL_s->proxy_authenticate,
                               cd->sent_proxy_auth,
                               (void *) auth_closure)) {
            need_to_do_again = TRUE;
            TRACEMSG(("NET_AskForProxyAuth(): need_to_do_again: %s", ce->URL_s->address));
        } else
            ce->URL_s->dont_cache = TRUE;

#if defined(SMOOTH_PROGRESS)
        PM_Resume(ce->window_id, ce->URL_s);
#endif

        /* Only free the our temp proxy server if it's not pointing to cd->proxy_server. 
         * We don't want to be freeing someone elses memory, we were just temporarily 
         * pointing to it. 
         */
        if (tempURL)
            PR_FREEIF(proxyServer);
    } /* end if cd->proxy_auth_required */

    if (need_to_do_again) {
        NET_ClearReadSelect(ce->window_id, cd->connection->sock);
        PR_Close(cd->connection->sock);
        NET_TotalNumberOfOpenConnections--;
        cd->connection->sock = NULL;
        cd->send_http1 = TRUE;
        cd->authorization_required = FALSE;
        cd->proxy_auth_required = FALSE;
        cd->next_state = HTTP_START_CONNECT;
        ce->URL_s->last_modified = 0;  /* clear this so we don't get 304 loopage */

        /* we don't know if the connection is valid anymore */
        cd->connection_is_valid = FALSE;

        /* clear the buffer */ 
        PR_FREEIF(cd->line_buffer);
        cd->line_buffer = NULL;
        cd->line_buffer_size = 0;

        /* if necessary */
        PR_FREEIF(cd->server_headers);
        cd->server_headers = NULL;

        return(0); /* continue */

    }

    /* Now finish stream setup. Setup needs to be broken in half
       'cuz in the case of modal dialogs, we need to wait for username
       and password, then return to finish */
    return (ce->status = net_finish_setup_http_stream(ce));
}

PUBLIC void
net_ResumeHTTP(ActiveEntry * ce, NET_AuthClosure *auth_closure, PRBool resume)
{
    HTTPConData * cd = (HTTPConData *) ce->con_data;

    TRACEMSG (("NET_ResumeHTTP: %s", ce->URL_s->address));

    if (resume) {

      /* now update the user/pass */
      ce->URL_s->username = PL_strdup (auth_closure->user);
      ce->URL_s->password = PL_strdup (auth_closure->pass);
      cd->next_state = HTTP_SETUP_STREAM;

    } else {
      cd->next_state = HTTP_DONE;
    }
      
    return;
}

PRIVATE int
net_finish_setup_http_stream(ActiveEntry * ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    MWContext * stream_context;

    if (cd->doing_redirect && ce->URL_s->redirecting_url &&
               /* try and prevent a circular loop. wont work for dual doc loop */
               PL_strcmp(ce->URL_s->redirecting_url, ce->URL_s->address)) 
    {
        Bool do_redirect=TRUE;
        char *curURLHost, *redirectURLHost;
        char *curPort, *redirectPort;

#ifdef MOZILLA_CLIENT
        /* update the global history since we wont have the same
         * url later */
        GH_UpdateGlobalHistory(ce->URL_s);
#endif /* MOZILLA_CLIENT */

        /* Inserted for security reasons. Ie. java applets being told to redirect to places they shouldn't be.*/
        if(ce->URL_s->dontAllowDiffHostRedirect) {

            if( !(curURLHost = NET_ParseURL(ce->URL_s->address, GET_HOST_PART)) )
                return MK_INTERRUPTED;
            if( !(redirectURLHost = NET_ParseURL(ce->URL_s->redirecting_url, GET_HOST_PART)) )
            {
                PR_Free(curURLHost);
                return MK_INTERRUPTED;
            }

            if ( (curPort = PL_strchr(curURLHost, ':')) != NULL)
                *curPort='\0';
            if ( (redirectPort = PL_strchr(redirectURLHost, ':')) != NULL)
                *redirectPort='\0';

            if(PL_strcasecmp(curURLHost, redirectURLHost)) {
                PR_Free(curURLHost);
                PR_Free(redirectURLHost);
                PR_FREEIF(ce->URL_s->redirecting_url);
                ce->URL_s->redirecting_url = NULL;
                ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_REDIRECT_ATTEMPT_NOT_ALLOWED);
                return MK_REDIRECT_ATTEMPT_NOT_ALLOWED;
            }

            PR_Free(curURLHost);
            PR_Free(redirectURLHost);   

        } /* End URL_s->dontAllowDiffHostRedirect */

        HG92871

        /* OK, now we've got the redirection URL stored
         * in redirecting_url.
         * Now change the URL in the URL structure and do the whole
         * thing again */
        StrAllocCopy(ce->URL_s->address, ce->URL_s->redirecting_url);

        /* if we were posting a file and received an error put the
         * current file we were posting back on the end of the list
         * so that our state is correctly reset*/
        net_revert_post_data(ce);  

        if(!cd->save_redirect_method) {
            PR_FREEIF(ce->URL_s->post_data);
            ce->URL_s->post_data = NULL;
            PR_FREEIF(ce->URL_s->post_headers);
            ce->URL_s->post_headers = NULL;
            ce->URL_s->post_data_size = 0;
            ce->URL_s->method = URL_GET_METHOD;
        }

        /* clear these */
        if(!ce->URL_s->preset_content_type) {
            PR_FREEIF(ce->URL_s->content_type);
            ce->URL_s->content_type = NULL;
        }
        PR_FREEIF(ce->URL_s->content_encoding);
        ce->URL_s->content_encoding = NULL;
        PR_FREEIF(ce->URL_s->transfer_encoding);
        ce->URL_s->transfer_encoding=NULL;
        ce->URL_s->content_length = 0;       /* reset */
        ce->URL_s->real_content_length = 0;  /* reset */
        ce->URL_s->last_modified = 0;        /* reset */
        cd->posting = FALSE;
        ce->URL_s->address_modified = TRUE;

        if(do_redirect)
            return(MK_DO_REDIRECT); /*fall out of HTTP and load the redirecting url */
        else
            return(MK_INTERRUPTED);

    } else if(cd->server_busy_retry) {

        /* the redirecting mechanism works well for the retry
         * since it is really the same thing except the url stays
         * the same */
        return(MK_DO_REDIRECT); /* fall out of HTTP and reload the url */
    }

#ifdef MOZILLA_CLIENT
    /* check to see if we just now entered a secure space */ 
    /* don't do if this is coming from history */ 
    /* don't do this if about to redirect */ 
    if( HG22087 && 
        (ce->format_out == FO_CACHE_AND_PRESENT || ce->format_out == FO_PRESENT) 
        && !ce->URL_s->history_num) 
    { 
        History_entry * h = SHIST_GetCurrent(&ce->window_id->hist);
        XP_Bool warn = FALSE;
  
        if (h == NULL) {
            /* Deal with frames.  If the window doesn't have history, */
            /* then it is a new window or a new frame cell. */
            if ( ce->window_id->grid_parent != NULL ) {
                h = SHIST_GetCurrent(&ce->window_id->grid_parent->hist);
                HG22088
            } else {
                /* no parent frame - this is a top level window */
                warn = TRUE;
            }
        } else if (HG22089) {
            warn = TRUE;
        }
        if ( warn ) {
            SECNAV_SecurityDialog(ce->window_id, SD_ENTERING_SECURE_SPACE);
        }
    }
#endif /* MOZILLA_CLIENT */

    /* set a default content type if one wasn't given 
     */
    if(!ce->URL_s->content_type || !*ce->URL_s->content_type)
        StrAllocCopy(ce->URL_s->content_type, TEXT_HTML);

    /* If a stream previously exists from a partial cache
     * situation, reuse it
     */
    if(!cd->stream) {
        /* clear to prevent tight loop */
        NET_ClearReadSelect(ce->window_id, cd->connection->sock);

#ifdef MOZILLA_CLIENT
        /* if the context can't handle HTML then we
         * need to generate an HTML dialog to handle
         * the message
         */
        if(ce->URL_s->files_to_post && EDT_IS_EDITOR(ce->window_id))
        {
            Chrome chrome_struct;

            memset(&chrome_struct, 0, sizeof(Chrome));


            chrome_struct.is_modal = TRUE;
            chrome_struct.allow_close = TRUE;
            chrome_struct.allow_resize = TRUE;
            chrome_struct.show_scrollbar = TRUE;
            chrome_struct.w_hint = 400;
            chrome_struct.h_hint = 300;
#ifdef XP_MAC
            /* on Mac, topmost windows are floating windows not dialogs */
            chrome_struct.topmost = FALSE;
            /* disable commands to change to minimal menu bar; */
            /* avoids confusion about which commands are present */
            chrome_struct.disable_commands = TRUE;
#else
            chrome_struct.topmost = TRUE;
#endif
       

            stream_context = FE_MakeNewWindow(ce->window_id, 
                                  NULL, 
                                  NULL, 
                                  &chrome_struct);
            if(!stream_context)
                return (MK_OUT_OF_MEMORY);

            /* zero out the post_data field so that it doesn't get
             * pushed onto the history stack.  Otherwise it can
             * get deleted when the history gets cleared
             */
            PR_FREEIF(ce->URL_s->post_data);
            ce->URL_s->post_data = NULL;
            ce->URL_s->post_data_is_file = FALSE;
        } else
#endif /* MOZILLA_CLIENT */
        {
            stream_context = ce->window_id;
        }

        /* we can get here on server or proxy errors
         * if we proceed to build the stream with post_data
         * set then the file could get deleted by history
         * cleanup code.  Make sure we zero the field
         */
        if(ce->URL_s->files_to_post && ce->URL_s->post_data)
        {
            /* we shoved the file to post into the post data.
             * remove it so the history doesn't get confused
             * and try and delete the file.
             */
            PR_FREEIF(ce->URL_s->post_data);
            ce->URL_s->post_data = NULL;
            ce->URL_s->post_data_is_file = FALSE;
        }

        /* Set up the stream stack to handle the body of the message */
        cd->stream = NET_StreamBuilder(ce->format_out, 
                        ce->URL_s, 
                        stream_context);

        if (!cd->stream) {
            ce->status = MK_UNABLE_TO_CONVERT;
            ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_CONVERT);
            return STATUS(ce->status);
        }

        NET_SetReadSelect(ce->window_id, cd->connection->sock);

        if(ce->URL_s->files_to_post) {
            char * tmp_string = PL_strdup("<h2>Error uploading files</h2><b>The server responded:<b><hr><p>\n");

            if(tmp_string)
                PUTSTRING(tmp_string);
        } 
    } else {
        /* check to see if it's a multipart respose.
         * if it is then we need to do some magic to
         * strip the multipart
         */
        if(!PL_strncasecmp(ce->URL_s->content_type, "multipart", 9))
        {
            /* reset the state to parse_mime_headers to strip
             * the multipart headers off
             */
            cd->next_state = HTTP_PARSE_MIME_HEADERS;
            return STATUS(ce->status);
        }
    }

    if(cd->use_copy_from_cache) {
        /* we can only get here if it's a partial cache file */
        cd->next_state = HTTP_BEGIN_PUSH_PARTIAL_CACHE_FILE;
        cd->use_copy_from_cache = FALSE;

    } else {
#if defined(SMOOTH_PROGRESS)
        PM_Progress(ce->window_id,
                    ce->URL_s,
                    0,
                    cd->original_content_length);
#else
        /* start the graph progress indicator */
        FE_GraphProgressInit(ce->window_id, 
            ce->URL_s, 
            cd->original_content_length);
        cd->destroy_graph_progress = TRUE;  /* we will need to destroy it */
#endif

        cd->next_state = HTTP_PULL_DATA;

        if(cd->acting_as_proxy && cd->server_headers)
        {
            ce->status = PUTBLOCK(cd->server_headers, 
                PL_strlen(cd->server_headers));
            cd->displayed_some_data = TRUE;
        }

        { /* open brace1 */
        char * nonProxyHost = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
        if (nonProxyHost) {
            char* msg = PR_smprintf(XP_GetString(XP_PROGRESS_TRANSFER_DATA),
                        nonProxyHost);
            if (msg) {
#if defined(SMOOTH_PROGRESS)
                PM_Status(ce->window_id, ce->URL_s, msg);
#else
                NET_Progress(ce->window_id, msg);
#endif
                PR_Free(msg);
            }
            PR_Free(nonProxyHost);
        }
        } /* close brace1 */

        /* Push though buffered data */
        if(cd->line_buffer_size)
        {
            /* @@@ bug, check return status and only send
             * up to the return value
             */
            (*cd->stream->is_write_ready)(cd->stream);
            ce->status = PUTBLOCK(cd->line_buffer, cd->line_buffer_size);
            ce->bytes_received = cd->line_buffer_size;
#if defined(SMOOTH_PROGRESS)
            PM_Progress(ce->window_id,
                        ce->URL_s,
                        ce->bytes_received, 
                        cd->original_content_length);
#else
            FE_GraphProgress(ce->window_id, 
                  ce->URL_s, 
                  ce->bytes_received, 
                  cd->line_buffer_size, 
                  cd->original_content_length);
#endif
            cd->displayed_some_data = TRUE;
        }
    }

    PR_FREEIF(cd->line_buffer);
    cd->line_buffer = NULL;
    cd->line_buffer_size=0;

    /* check to see if we have read the whole object,
     * and finish the transfer if so.
     */
    if(ce->status > -1
        && cd->original_content_length
        && ce->bytes_received >= cd->original_content_length)
    {
        /* normal end of transfer */
        ce->status = MK_DATA_LOADED;
        cd->next_state = HTTP_DONE;
        cd->pause_for_read = FALSE;
    }

    return STATUS(ce->status);
}

/* begin pushing a partial cache file down the stream
 */
PRIVATE int
net_http_push_partial_cache_file(ActiveEntry *ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;
  int32 write_ready, status;
  
  write_ready = (*cd->stream->is_write_ready)(cd->stream);

  write_ready = MIN(write_ready, NET_Socket_Buffer_Size);

  status = NET_XP_FileRead(NET_Socket_Buffer, write_ready, cd->partial_cache_fp);

  if(status < 0)
    {
    /* @@@ This is the wrong error code
     */
    ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, 
                             PR_GetOSError());
    return(MK_TCP_READ_ERROR);
    }
  else if(status == 0)  
    {
    /* all done with reading this file
     */
    NET_ClearFileReadSelect(ce->window_id, XP_Fileno(cd->partial_cache_fp));
    NET_XP_FileClose(cd->partial_cache_fp);
    cd->partial_cache_fp = NULL;

    /* set these back in preperation for
     * using the http connection again
     */
    ce->socket = cd->connection->sock;
    ce->local_file = FALSE;

    /* add a request-range header
     */
    PR_ASSERT(!ce->URL_s->range_header);
    ce->URL_s->range_header = PR_smprintf("bytes=%ld-", 
                        cd->partial_needed);

    /* the byterange part has not been gotten yet */
    ce->URL_s->last_modified = 0;

    /* we don't know if the connection is valid anymore 
     * because we are going to try it again
     */
    cd->connection_is_valid = FALSE;

    if(ce->URL_s->can_reuse_connection)
      {
      NET_SetReadSelect(ce->window_id, cd->connection->sock);
      cd->next_state = HTTP_SEND_REQUEST;

      /* set the connection to be from the connection cache
       * since we have used it once
       */
      cd->connection->prev_cache = TRUE;
      }
    else
      {
            NET_ClearReadSelect(ce->window_id, cd->connection->sock);
            NET_ClearConnectSelect(ce->window_id, cd->connection->sock);
            PR_Close(cd->connection->sock);
      NET_TotalNumberOfOpenConnections--;
      cd->connection->sock = NULL;
      cd->next_state = HTTP_START_CONNECT;
      }

    cd->reuse_stream = TRUE;

    return(0);
    }

  /* else, push the data read up the stream 
   */ 
  status = (*cd->stream->put_block)(cd->stream, 
                    NET_Socket_Buffer, 
                    status);

  cd->pause_for_read = TRUE;

  return(status);
}

/* begin pushing a partial cache file down the stream
 */
PRIVATE int
net_http_begin_push_partial_cache_file(ActiveEntry *ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;
  char *cache_file = ce->URL_s->cache_file;
  XP_File fp;

  if(!cache_file 
     || NULL == (fp = NET_XP_FileOpen(cache_file, xpCache, XP_FILE_READ_BIN)))
    {
    ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_UNABLE_TO_OPEN_FILE, 
                             cache_file);
    return(MK_UNABLE_TO_OPEN_FILE);
    }

  /* set up read select on the file instead of the socket
   */
  NET_ClearReadSelect(ce->window_id, cd->connection->sock);
  NET_SetFileReadSelect(ce->window_id, XP_Fileno(fp));
  ce->socket = NULL;
  ce->local_file = TRUE;

  cd->next_state = HTTP_PUSH_PARTIAL_CACHE_FILE;

  cd->partial_cache_fp = fp;
    
  return(net_http_push_partial_cache_file(ce));
}

/* pulls down all the data
 *
 * returns the tcp status code
 */
PRIVATE int
net_pull_http_data(ActiveEntry * ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    unsigned int write_ready, read_size;

    TRACEMSG(("NET_ProcessHTTP: pulling data"));

    /* check to see if the stream is ready for writing
   */
    write_ready = (*cd->stream->is_write_ready)(cd->stream);

	TRACEMSG(("NET_ProcessHTTP: write ready returned %d", write_ready));

  if(!write_ready)
    {
    cd->pause_for_read = TRUE;
    return(0);  /* wait until we are ready to write */
    }
  else if(write_ready < (unsigned int) NET_Socket_Buffer_Size)
    {
    read_size = write_ready;
    }
  else
    {
    read_size = NET_Socket_Buffer_Size;
    }

    ce->status = PR_Read(cd->connection->sock, NET_Socket_Buffer, read_size);

	TRACEMSG(("NET_ProcessHTTP: just read %d bytes.", ce->status));
    cd->pause_for_read = TRUE;  /* pause for the next read */

    if(ce->status > 0)
      {
        ce->bytes_received += ce->status;
#if defined(SMOOTH_PROGRESS)
        PM_Progress(ce->window_id,
                    ce->URL_s,
                    ce->bytes_received,
                    cd->original_content_length);
#else
        FE_GraphProgress(ce->window_id, 
             ce->URL_s, 
             ce->bytes_received, 
             ce->status, 
             cd->original_content_length);
#endif

        ce->status = PUTBLOCK(NET_Socket_Buffer, ce->status); /* ALEKS */
    cd->displayed_some_data = TRUE;

    if(cd->original_content_length 
      && ce->bytes_received >= cd->original_content_length)
      {
      /* normal end of transfer */
      ce->status = MK_DATA_LOADED;
          cd->next_state = HTTP_DONE;
          cd->pause_for_read = FALSE;
      }

      }
    else if(ce->status == 0)
      {
    /* transfer finished
         */
      TRACEMSG(("MKHTTP.c: Caught TCP EOF ending stream"));

    if(!cd->displayed_some_data)
      {
      ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_ZERO_LENGTH_FILE);
          cd->next_state = HTTP_ERROR_DONE;
      cd->pause_for_read = FALSE;
      return(MK_ZERO_LENGTH_FILE);
       }

     /* return the server status instead of data loaded
      */
         ce->status = MK_DATA_LOADED;
         cd->next_state = HTTP_DONE;
         cd->pause_for_read = FALSE;
      }
  else /* error */
    {
    int err = PR_GetError();

    TRACEMSG(("TCP Error: %d", err));

    if (err == PR_WOULD_BLOCK_ERROR)
      {
      cd->pause_for_read = TRUE;
      return (0);
      }

        ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_READ_ERROR, err);

        /* return TCP error
         */
        return MK_TCP_READ_ERROR;
    }

     return STATUS(ce->status);

}

/* begin loading an object from an http host
 *
 * proxy_server  If not empty, the name of a host and/or port that will
 *               act as a proxy server for the request.  If proxy_server
 *               is NULL then no proxy server is used
 *
 */
PRIVATE int32
net_HTTPLoad (ActiveEntry * ce)
{
    /* get memory for Connection Data */
    HTTPConData * cd = PR_NEW(HTTPConData);
  HG21092
  char *use_host;

    ce->con_data = cd;
    if(!ce->con_data)
      {
        ce->status = MK_OUT_OF_MEMORY;
    ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
        return STATUS(ce->status);
      }

    /* kill any returns in the URL */
    strtok(ce->URL_s->address, "\r");
    strtok(ce->URL_s->address, "\n");

    /* init */
    memset(cd, 0, sizeof(HTTPConData));
    cd->proxy_server     = ce->proxy_addr;
  cd->proxy_conf       = ce->proxy_conf;
    cd->send_http1       = TRUE;

  /* set partial_cache_file if the whole file is not 
   * cached
   */
  if(ce->URL_s->content_length < ce->URL_s->real_content_length)
    {
    cd->partial_cache_file = TRUE;
    cd->partial_needed = ce->URL_s->content_length;

#ifdef MOZILLA_CLIENT
    /* if this isn't true then partial cacheing is screwed */
    PR_ASSERT(NET_IsPartialCacheFile(ce->URL_s));
#else
    PR_ASSERT(0);
#endif /* MOZILLA_CLIENT */
    }
  
  if(cd->proxy_server)
    {
    use_host = PL_strdup(cd->proxy_server);
    }
  else
    {
    use_host = NET_ParseURL(ce->URL_s->address, GET_HOST_PART);
    }

  HG09309

  if(!use_host)
    {
      PR_Free(ce->con_data);
        ce->status = MK_OUT_OF_MEMORY;
    ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
        return STATUS(ce->status);
    }

  /* if we are useing the files_to_post method
   * make sure that the directory specified in the
   * URL contains a slash at the end, otherwise
   * it wont work
   */
  if(ce->URL_s->files_to_post)
    {
    int32 end = PL_strlen(ce->URL_s->address)-1;
    XP_StatStruct stat_entry;
    int i;

    if(ce->URL_s->address[end] != '/')
      StrAllocCat(ce->URL_s->address, "/");

    /* run through the list of files and
     * gather the total size
     */
    for(i=0; ce->URL_s->files_to_post[i]; i++)
      if(-1 != NET_XP_Stat(ce->URL_s->files_to_post[i], 
               &stat_entry,
               xpFileToPost))
        cd->total_size_of_files_to_post += stat_entry.st_size;

#if defined(SMOOTH_PROGRESS)
      PM_Progress(ce->window_id,
                  ce->URL_s,
                  0,
                  cd->total_size_of_files_to_post);
#else
    /* start the graph progress indicator
     */
      FE_GraphProgressInit(ce->window_id, 
               ce->URL_s, 
               cd->total_size_of_files_to_post);
                                                    
    cd->destroy_graph_progress = TRUE;  /* we will need to destroy it */
#endif
                                                                           
#ifdef EDITOR
    /* Don't show the dialog if the data is being delivered to a plug-in */
    if ( (CLEAR_CACHE_BIT(ce->format_out) != FO_PLUGIN)
      && (CLEAR_CACHE_BIT(ce->format_out) != FO_LOCATION_INDEPENDENCE))
    {
          FE_SaveDialogCreate(ce->window_id, i, ED_SAVE_DLG_PUBLISH);
          cd->destroy_file_upload_progress_dialog = TRUE;
      }
#endif /* EDITOR */
    }

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

        /* If the url is secure and we are using a proxy server 
     * then never use a cached connection
     */
    if(!cd->use_proxy_tunnel)
          {

            while((tmp_con = (HTTPConnection *)XP_ListNextObject(list_entry))
                  != NULL)
              {
                /* if the hostnames match up exactly 
           * and security matches up.
           * and the connection
                 * is not busy at the moment then reuse this connection.
                 */
                if(HG29802 
                   && !tmp_con->busy)
                  {
                    cd->connection = tmp_con;
                    cd->connection->sock = cd->connection->sock;
                    NET_SetReadSelect(ce->window_id, cd->connection->sock);
            ce->socket = cd->connection->sock;
                    cd->connection->prev_cache = TRUE;  /* this was from the cache */
            TRACEMSG(("Found cached HTTP connection: %d", cd->connection->sock));

            /* reorder the connection in the list to keep most recently
             * used connections at the end
             */
            XP_ListRemoveObject(http_connection_list, tmp_con);
            XP_ListAddObjectToEnd(http_connection_list, tmp_con);

                    break;
                  }
              }
          }
      }
    else
      {
        /* initialize the connection list
         */
        http_connection_list = XP_ListNew();
      }

    if(cd->connection)
      {
    if(cd->use_proxy_tunnel)
            cd->next_state = HTTP_SEND_PROXY_TUNNEL_REQUEST;
    else if(ce->URL_s->files_to_post)
      cd->next_state = HTTP_BEGIN_UPLOAD_FILE;
    else
      cd->next_state = HTTP_SEND_REQUEST;

        /* set the connection busy
         */
        cd->connection->busy = TRUE;
        NET_TotalNumberOfOpenConnections++;
      }
    else
      {
        /* build a control connection structure so we
         * can store the data as we go along
         */
        cd->connection = PR_NEW(HTTPConnection);
        if(!cd->connection)
          {
            ce->status = MK_OUT_OF_MEMORY;
      ce->URL_s->error_msg = NET_ExplainErrorDetails(MK_OUT_OF_MEMORY);
      PR_Free(use_host);
      PR_Free(ce->con_data);
      return(-1);
          }
        memset(cd->connection, 0, sizeof(HTTPConnection));
  
        StrAllocCopy(cd->connection->hostname, use_host);
  
        HG93882
  
        cd->connection->prev_cache = FALSE;  /* this wasn't from the cache */
  
        cd->connection->sock = NULL;
  
        /* add this structure to the connection list even
         * though it's not really valid yet.
         * we will fill it in as we go and if
         * an error occurs will will remove it from the
         * list.  No one else will be able to use it since
         * we will mark it busy.
         */
        XP_ListAddObject(http_connection_list, cd->connection);

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

    /* if the connection list is larger than MAX_CACHED_HTTP_CONNECTIONS 
     * trim it down
     */
    if(XP_ListCount(http_connection_list) > MAX_CACHED_HTTP_CONNECTIONS)
      {
      HTTPConnection *tmp_con;
          XP_List * list_entry = http_connection_list;

      TRACEMSG(("More than %d cached connections.  Deleteing one...",
            MAX_CACHED_HTTP_CONNECTIONS));
  
          while((tmp_con = (HTTPConnection *)XP_ListNextObject(list_entry)) != NULL)
              {
        if(!tmp_con->busy)
          {

          if(!PL_strncasecmp(tmp_con->hostname, "rl.", 3)
              && PL_strcasestr(tmp_con->hostname+2, ".netscape.com"))
            {
            /* if there is max plus one we are done, else
             * continue on and remove one 
             */
            if(XP_ListCount(http_connection_list) 
                    == MAX_CACHED_HTTP_CONNECTIONS+1)
              {
              break; /* from while */
              }

            }
          else
            {
            /* remove the object */
            XP_ListRemoveObject(http_connection_list, tmp_con);
                  PR_Close(tmp_con->sock);
            PR_Free(tmp_con->hostname);
            PR_Free(tmp_con);
            break; /* from while */
            }
          }
        }
      }
  
    cd->next_state = HTTP_START_CONNECT;

      }

  PR_Free(use_host);

    return STATUS (net_ProcessHTTP(ce));
}


/* NET_process_HTTP  will control the state machine that
 * loads an HTTP document
 *
 * returns negative if the transfer is finished or error'd out
 *
 * returns zero or more if the transfer needs to be continued.
 */
PRIVATE int32
net_ProcessHTTP (ActiveEntry *ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;
    TRACEMSG(("Entering NET_ProcessHTTP"));

    cd->pause_for_read = FALSE; /* already paused; reset */

    while(!cd->pause_for_read) {
	
        switch(cd->next_state) {

        case HTTP_WAIT_FOR_AUTH:
            TRACEMSG (("HTTP_WAIT_FOR_AUTH: %s", ce->URL_s->address));
            cd->pause_for_read = TRUE;
            break;
        case HTTP_RESUME_WITH_AUTH:
            TRACEMSG (("HTTP_RESUME_WITH_AUTH: %s", ce->URL_s->address));
            ce->status = net_finish_setup_http_stream(ce);
            break;
        case HTTP_START_CONNECT:
            ce->status = net_start_http_connect(ce);
            break;
        
        case HTTP_FINISH_CONNECT:
            ce->status = net_finish_http_connect(ce);
            break;
        
        case HTTP_SEND_PROXY_TUNNEL_REQUEST:
            /* send proxy tunnel init stuff */
            ce->status = net_send_proxy_tunnel_request(ce);
            break;

        case HTTP_BEGIN_UPLOAD_FILE:
            /* form a put request */
            ce->status = net_begin_upload_file (ce);
            break;
HG51096
        case HTTP_SEND_REQUEST:
            /* send HTTP request */
            ce->status = net_send_http_request(ce);
            break;

        case HTTP_SEND_POST_DATA:
            ce->status = net_http_send_post_data(ce);
            break;
        
        case HTTP_PARSE_FIRST_LINE:
            ce->status = net_parse_first_http_line(ce);
            break;
        
        case HTTP_PARSE_MIME_HEADERS:
            ce->status = net_parse_http_mime_headers(ce);
            break;
        
        case HTTP_SETUP_STREAM:
            ce->status = net_setup_http_stream(ce);
            break;

        case HTTP_BEGIN_PUSH_PARTIAL_CACHE_FILE:
            ce->status = net_http_begin_push_partial_cache_file(ce);
            break;

        case HTTP_PUSH_PARTIAL_CACHE_FILE:
            ce->status = net_http_push_partial_cache_file(ce);
            break;
        
        case HTTP_PULL_DATA:
            ce->status = net_pull_http_data(ce);
            break;
        
        case HTTP_DONE:
            TIMING_STOPCLOCK_NAME("http:xfer", ce->URL_s->address,
                                  ce->window_id, "ok");

            NET_ClearReadSelect(ce->window_id, cd->connection->sock);
            NET_TotalNumberOfOpenConnections--;

#ifdef TRUST_LABELS
            ProcessCookiesAndTrustLabels( ce );
#endif

            if(ce->URL_s->can_reuse_connection && !cd->use_proxy_tunnel) {
                cd->connection->busy = FALSE;
            } else {
                PR_Close(cd->connection->sock);

                /* remove the connection from the cache list
                 * and free the data */
                XP_ListRemoveObject(http_connection_list, cd->connection);
                if(cd->connection) {
                    PR_FREEIF(cd->connection->hostname);
                    PR_Free(cd->connection);
                }
            }

#ifdef MOZILLA_CLIENT
            /* make any meta tag changes take effect */
            NET_RefreshCacheFileExpiration(ce->URL_s);  
#endif /* MOZILLA_CLIENT */

            if(cd->stream) {
                COMPLETE_STREAM;
                PR_Free(cd->stream);
                cd->stream = 0;
            }

#if defined(SMOOTH_PROGRESS)
            /* XXX what to do if redirected to cache? */
            PM_StopBinding(ce->window_id, ce->URL_s, 0, NULL);
#endif

            cd->next_state = HTTP_FREE;
            break;
        
        case HTTP_ERROR_DONE:
            TIMING_STOPCLOCK_NAME("http:post", ce->URL_s->address, ce->window_id, "error");
            TIMING_STOPCLOCK_NAME("http:request", ce->URL_s->address, ce->window_id, "error");
            TIMING_STOPCLOCK_NAME("http:complete", ce->URL_s->address, ce->window_id, "error");
            if(cd->connection->sock != NULL) {
                NET_ClearDNSSelect(ce->window_id, cd->connection->sock);
                NET_ClearReadSelect(ce->window_id, cd->connection->sock);
                NET_ClearConnectSelect(ce->window_id, cd->connection->sock);
                PR_Close(cd->connection->sock);
                NET_TotalNumberOfOpenConnections--;
                cd->connection->sock = NULL;
            }

            if(cd->partial_cache_fp) {
                NET_ClearFileReadSelect(ce->window_id, XP_Fileno(cd->partial_cache_fp));
                NET_XP_FileClose(cd->partial_cache_fp);
                cd->partial_cache_fp = 0;
            }

            if(cd->connection->prev_cache 
                && !cd->connection_is_valid
                && ce->status != MK_INTERRUPTED) {

                if(cd->stream && !cd->reuse_stream) {
                    ABORT_STREAM(ce->status);
                    PR_Free(cd->stream);
                    cd->stream = 0;
                }

                /* the connection came from the cache and
                 * may have been stale.  Try it again
                 */
                /* clear any error message */
                if(ce->URL_s->error_msg) {
                    PR_Free(ce->URL_s->error_msg);
                    ce->URL_s->error_msg = NULL;
                }
                cd->next_state = HTTP_START_CONNECT;
                ce->status = 0;

                /* we don't know if the connection is valid anymore 
                 * because we are going to try it again
                 */
                cd->connection_is_valid = FALSE;

                cd->connection->prev_cache = FALSE;

                /* if we were posting a file and received an error put the
                 * current file we were posting back on the end of the list
                 * so that our state is correctly reset
                 */
                net_revert_post_data(ce);

            } else {
                cd->next_state = HTTP_FREE;

                if(cd->stream) {
                    ABORT_STREAM(ce->status);
                    PR_Free(cd->stream);
                    cd->stream = 0;
                }

                /* remove the connection from the cache list
                 * and free the data
                 */
                XP_ListRemoveObject(http_connection_list, cd->connection);
                PR_FREEIF(cd->connection->hostname);
                PR_Free(cd->connection);
            }

#if defined(SMOOTH_PROGRESS)
            PM_StopBinding(ce->window_id, ce->URL_s, -1, NULL);
#endif

            break; /* HTTP_ERROR_DONE */
        
        case HTTP_FREE:

            /* close the file upload progress.  If a stream was created
             * then some sort of HTTP error occured.  Send in an error code */         
#ifdef EDITOR
            if(cd->destroy_file_upload_progress_dialog) {
                /* Convert from netlib errors to editor errors. */
                ED_FileError error = ED_ERROR_NONE;
                if ( (ce->URL_s->server_status != 204 && ce->URL_s->server_status != 201 )
                    || ce->status < 0 )
                    error = ED_ERROR_PUBLISHING;
                FE_SaveDialogDestroy(ce->window_id, error, ce->URL_s->post_data);
                /* FE_SaveDialogDestroy(ce->window_id, ce->URL_s->server_status != 204 ? -1 : ce->status, ce->URL_s->post_data); */
            }
#endif /* EDITOR */

            if(ce->URL_s->files_to_post && ce->URL_s->post_data) {
                /* we shoved the file to post into the post data.
                 * remove it so the history doesn't get confused
                 * and try and delete the file. */
                PR_FREEIF(ce->URL_s->post_data);
                ce->URL_s->post_data = NULL;
                ce->URL_s->post_data_is_file = FALSE;
            }

#if !defined(SMOOTH_PROGRESS)
            if(cd->destroy_graph_progress)
                FE_GraphProgressDestroy(ce->window_id,
                    ce->URL_s, 
                    cd->original_content_length,
                    ce->bytes_received);
#endif
      
            PR_FREEIF(cd->line_buffer);
            PR_Free(cd->stream); /* don't forget the stream */
            PR_FREEIF(cd->server_headers);
            PR_FREEIF(cd->orig_host);
            if(cd->tcp_con_data)
                NET_FreeTCPConData(cd->tcp_con_data);
            PR_FREEIF(cd);
            JSCF_Cleanup();
            return STATUS (-1); /* final end HTTP_FREE */
        
        default: /* should never happen !!! */
            TRACEMSG(("HTTP: BAD STATE!"));
            cd->next_state = HTTP_ERROR_DONE;
            break;
        } /* end switch */

        /* check for errors during load and call error state if found */
        if(ce->status < 0 
            && ce->status != MK_USE_COPY_FROM_CACHE
            && cd->next_state != HTTP_FREE) {

            if (ce->status == MK_MULTIPART_MESSAGE_COMPLETED) {
                /* We found the end of a multipart/mixed response
                 * from a CGI script in a http keep-alive response
                 * That signifies the end of a message. */
                TRACEMSG(("mkhttp.c: End of multipart keep-alive response"));
    
                ce->status = MK_DATA_LOADED;
                cd->next_state = HTTP_DONE;

            } else if (ce->status == MK_HTTP_TYPE_CONFLICT
                    /* Don't retry if were HTTP/.9 */
                    && !cd->send_http1
                    /* Don't retry if we're posting. */
                    && !cd->posting) {
                /* Could be a HTTP 0/1 compability problem. */
                TRACEMSG(("HTTP: Read error trying again with HTTP0 request."));
#if defined(SMOOTH_PROGRESS)
                PM_Status(ce->window_id, ce->URL_s, XP_GetString(XP_PROGRESS_TRYAGAIN));
#else
                NET_Progress (ce->window_id, XP_GetString(XP_PROGRESS_TRYAGAIN));
#endif

                NET_ClearReadSelect(ce->window_id, cd->connection->sock);
                NET_ClearConnectSelect(ce->window_id, cd->connection->sock);
#ifdef XP_WIN
                if(cd->calling_netlib_all_the_time)
                  NET_ClearCallNetlibAllTheTime(ce->window_id, "mkhttp");
#endif /* XP_WIN */
                NET_ClearDNSSelect(ce->window_id, cd->connection->sock);
                PR_Close(cd->connection->sock);
                NET_TotalNumberOfOpenConnections--;
                cd->connection->sock = NULL;

                if(cd->stream)
                    (*cd->stream->abort)(cd->stream, ce->status);
                cd->send_http1 = FALSE;
                /* go back and send an HTTP0 request */
                cd->next_state = HTTP_START_CONNECT;
            } else {
                cd->next_state = HTTP_ERROR_DONE;
            }
                /* don't exit! loop around again and do the free case */
                cd->pause_for_read = FALSE;
        } /* ce->status < 0 */
    } /* while(!cd->pause_for_read) */
    
    return STATUS(ce->status);
}


/* abort the connection in progress
 */
PRIVATE int32
net_InterruptHTTP(ActiveEntry * ce)
{
    HTTPConData * cd = (HTTPConData *)ce->con_data;

    /* if we are currently pulling data and the data is
   * Textual then truncate the file, leave notification and
     * end normally
   */
  if(cd->next_state == HTTP_PULL_DATA 
    && ce->URL_s->content_type
     && !PL_strcasecmp(ce->URL_s->content_type, TEXT_HTML))
    {
    char buffer[127];

    if(!PL_strcasecmp(ce->URL_s->content_type, TEXT_HTML))
      PR_snprintf(buffer, sizeof(buffer),
        XP_GetString(XP_HR_TRANSFER_INTERRUPTED));
    else
      PR_snprintf(buffer, sizeof(buffer),
        XP_GetString(XP_TRANSFER_INTERRUPTED));

    PUTSTRING(buffer);

    /* steps from HTTP_DONE duplicated, with the addition of
     * NET_RefreshCacheFileExpiration 
     */
        NET_ClearReadSelect(ce->window_id, cd->connection->sock);
        PR_Close(cd->connection->sock);
    NET_TotalNumberOfOpenConnections--;
        ABORT_STREAM(MK_INTERRUPTED);
    PR_Free(cd->stream);
    cd->stream = 0;

    ce->URL_s->last_modified = 0;
#ifdef MOZILLA_CLIENT
    /* to make the last_modified change take effect 
     */
        NET_RefreshCacheFileExpiration(ce->URL_s);  
#endif /* MOZILLA_CLIENT */

        cd->next_state = HTTP_FREE;

        ce->status = MK_DATA_LOADED;
    }
  else
    {
        cd->next_state = HTTP_ERROR_DONE;
        ce->status = MK_INTERRUPTED;
    }
 
    return STATUS (net_ProcessHTTP(ce));
}

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

PRIVATE void
HTTP_ReadPrefs(void)
{
    PRBool b;
     if ( (PREF_OK != PREF_GetBoolPref(pref_sendRefererHeader, &b)) ) {
        b = TRUE;
    }
    NET_SetSendRefererHeader(b);
}

static int PR_CALLBACK HTTP_PrefChangedFunc(const char *pref, void *data)
{
        HTTP_ReadPrefs();
        return TRUE;
}

PRIVATE void
HTTP_InitPrefs(void)
{
    HTTP_ReadPrefs();
    PREF_RegisterCallback(pref_sendRefererHeader ,HTTP_PrefChangedFunc,NULL);
}

#define HTTP_SCHEME "http:"

MODULE_PRIVATE void
NET_InitHTTPProtocol(void)
{
  static NET_ProtoImpl http_proto_impl;

  HTTP_InitPrefs();

  http_proto_impl.init = net_HTTPLoad;
  http_proto_impl.process = net_ProcessHTTP;
  http_proto_impl.interrupt = net_InterruptHTTP;
  http_proto_impl.resume = net_ResumeHTTP;
  http_proto_impl.cleanup = net_CleanupHTTP;
  StrAllocCopy(http_proto_impl.scheme, HTTP_SCHEME); 


  NET_RegisterProtocolImplementation(&http_proto_impl, HTTP_TYPE_URL);
  HG93898
}

#ifdef TRUST_LABELS
PUBLIC
void ProcessCookiesAndTrustLabels( ActiveEntry *ce )
{
    unsigned int i;
    if ( IsTrustLabelsEnabled() && ce && ce->URL_s) {
        /*
         * if the trust label parsing is enabled then look at each cookie
         * and try to match it to a trust label on the trust list to see
         * if one matches the cookie
         */
        for(i=0 ;i < ce->URL_s->all_headers.empty_index; i++) {
            /* look for a cookie field - allow Set-cookie: or Set-Cookie2: - CASE INSENSITIVE COMPARE	*/
            if(!PL_strncasecmp(ce->URL_s->all_headers.key[i], "Set-Cookie", 10)) {
                NET_SetCookieStringFromHttp(CE_FORMAT_OUT, ce->URL_s, CE_WINDOW_ID, ce->URL_s->address, ce->URL_s->all_headers.value[i]);
            }
        }
    }
}
#endif

#ifdef PROFILE
#pragma profile off
#endif
