gecko-dev/network/protocol/http/mkhttp.c

3931 строка
128 KiB
C

/* -*- 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, */*" CRLF,
TEXT_HTML, TEXT_XML, IMAGE_PNG, IMAGE_GIF, IMAGE_JPG, IMAGE_PJPG);
} else {
sprintf(line_buffer, "Accept: %s, %s, %s, %s" CRLF,
IMAGE_PNG, IMAGE_GIF, 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;
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