gecko-dev/network/main/mkutils.c

3526 lines
89 KiB
C
Raw Normal View History

/* -*- 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.
*/
/*
*
* Implemented by Lou Montulli '94-'98
*
* this is the dumping grounds for random Netlib functions
* Home for utility functions and anything else that
* doesn't fit elsewhere.
*/
#include "rosetta.h"
#include "mkutils.h"
#include "gui.h"
#include "mkparse.h"
#include "mkgeturl.h"
#include "cookies.h"
#include "httpauth.h"
#include "libi18n.h"
#include "msgcom.h"
#include "mime.h"
#include "secrng.h"
#include "ssl.h"
#include "prefapi.h"
#include "secnav.h"
#include "preenc.h"
#include "mkselect.h"
#include "prerror.h"
#include "netcache.h"
#include "mktcp.h"
#include "netutils.h"
#include "xp_error.h"
#include "xpgetstr.h"
#include "mimeenc.h"
#include "intl_csi.h"
#ifdef XP_MAC
#include "MacBinSupport.h"
#endif
typedef struct {
char *buffer;
int32 size;
int32 pos;
} BufferStruct;
#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
static int
net_buffer_output_fn ( const char *buf, int32 size, void *closure);
extern int MK_OUT_OF_MEMORY;
extern int MK_UNABLE_TO_LOCATE_FILE;
extern int XP_ERRNO_EWOULDBLOCK;
extern int MK_TCP_WRITE_ERROR;
extern int XP_ERRNO_EALREADY;
extern int XP_ALERT_URN_USEHTTP;
extern int XP_ALERT_NFS_USEHTTP;
extern int MK_NO_WAIS_PROXY;
/* print network progress to the front end
*/
MODULE_PRIVATE void
NET_Progress(MWContext *context, char *msg)
{
FE_Progress(context, msg);
}
/* note: on the Macintosh local_dir_name will be in the following format: */
/* file:///Hard%20Disk/Folder%20Name/File.html */
PUBLIC char **
NET_AssembleAllFilesInDirectory(MWContext *context, char * local_dir_name)
{
XP_Dir dir_ptr;
XP_DirEntryStruct *dir_entry;
XP_StatStruct stat_entry;
char **files_to_post;
char *file_to_post = 0;
int32 i, cur_array_size;
int end;
Bool have_slash;
#define INITIAL_ARRAY_SIZE 10
#define EXPAND_ARRAY_BY 5
PR_ASSERT(local_dir_name);
#ifdef XP_MAC
local_dir_name += 7; // chop-off "file://"
#endif
if(NULL == (dir_ptr = XP_OpenDir(local_dir_name, xpFileToPost)))
{
FE_Alert(context, "Unable to open local directory");
return NULL;
}
/* make sure local_dir_name doesn't have a slash at the end */
end = PL_strlen(local_dir_name)-1;
have_slash = (local_dir_name[end] == '/') || (local_dir_name[end] == '\\');
files_to_post = (char**) PR_Malloc(INITIAL_ARRAY_SIZE * sizeof(char*));
if(!files_to_post)
return NULL;
cur_array_size = INITIAL_ARRAY_SIZE;
i=0;
while((dir_entry = XP_ReadDir(dir_ptr)) != NULL)
{
/* skip . and .. */
if(!PL_strcmp(dir_entry->d_name, ".") || !PL_strcmp(dir_entry->d_name, ".."))
continue;
/* assemble full pathname first so we can test if its a directory */
file_to_post = PL_strdup(local_dir_name);
if ( ! file_to_post ){
return NULL;
}
/* Append slash to directory if we don't already have one */
if( !have_slash )
{
#ifdef XP_WIN
StrAllocCat(file_to_post, "\\");
#else
StrAllocCat(file_to_post, "/");
#endif
}
if ( ! file_to_post )
{
return NULL;
}
StrAllocCat(file_to_post, dir_entry->d_name);
if ( ! file_to_post )
{
return NULL;
}
/* skip over subdirectory names */
if(-1 != XP_Stat(file_to_post, &stat_entry, xpFileToPost) && S_ISDIR(stat_entry.st_mode) )
{
PR_Free(file_to_post);
continue;
}
/* expand array if necessary
* always leave room for the NULL terminator */
if(i >= cur_array_size-1)
{
files_to_post = (char**) PR_Realloc(files_to_post, (cur_array_size + EXPAND_ARRAY_BY) * sizeof(char*));
if(!files_to_post)
return NULL;
cur_array_size += EXPAND_ARRAY_BY;
}
files_to_post[i++] = PL_strdup(file_to_post);
PR_Free(file_to_post);
}
/* NULL terminate the array, space is guarenteed above */
files_to_post[i] = NULL;
return(files_to_post);
}
/********KILL this, use NET_PublishFilesTo ************/
/* upload a set of local files (array of char*)
* all files must have full path name
* first file is primary html document,
* others are included images or other files
* in the same directory as main file
*/
PUBLIC void
NET_PublishFiles(MWContext *context,
char **files_to_publish,
char *remote_directory)
{
URL_Struct *URL_s;
PR_ASSERT(context && files_to_publish && remote_directory);
if(!context || !files_to_publish || !*files_to_publish || !remote_directory)
return;
/* create a URL struct */
URL_s = NET_CreateURLStruct(remote_directory, NET_SUPER_RELOAD);
if(!URL_s)
{
FE_Alert(context, "Error: not enough memory for file upload");
return; /* does not exist */
}
FREE_AND_CLEAR(URL_s->content_type);
/* add the files that we are posting and set the method to POST */
URL_s->files_to_post = files_to_publish;
URL_s->method = URL_POST_METHOD;
/* start the load */
FE_GetURL(context, URL_s);
}
/* upload a set of local files (array of char*)
* first file is primary html document,
* others are included images or other files.
*
* It is legal to pass in NULL as the value of publish_to. This will duplicate
the functionality of the old NET_PublishFiles.
* files_to_publish and publish_to are used by and will be freed by NET_PublishFilesTo,
* base_url is copied */
PUBLIC void
NET_PublishFilesTo(MWContext *context,
char **files_to_publish,
char **publish_to, /* Absolute URLs of the location to
* publish the files to. Used only if HTTP.
* Ignored if FTP (except to delete memory.)*/
XP_Bool *add_crlf, /* For each file in files_to_publish, should every line
end in a CRLF. */
char *base_url, /* Directory to publish to, or the destination
* URL of the root HTML document. */
char *username,
char *password,
Net_GetUrlExitFunc *exit_func,
void *fe_data)
{
URL_Struct *URL_s;
if(!context || !files_to_publish || !*files_to_publish || !base_url) {
PR_ASSERT(0);
return;
}
/* create a URL struct */
URL_s = NET_CreateURLStruct(base_url, NET_SUPER_RELOAD);
if(!URL_s)
{
FE_Alert(context, "Error: not enough memory for file upload");
return; /* does not exist */
}
if (username)
URL_s->username = PL_strdup(username);
if (password)
URL_s->password = PL_strdup(password);
FREE_AND_CLEAR(URL_s->content_type);
/* add the files that we are posting and set the method to POST */
URL_s->files_to_post = files_to_publish;
URL_s->post_to = publish_to;
URL_s->add_crlf = add_crlf;
URL_s->method = URL_POST_METHOD;
URL_s->pre_exit_fn = exit_func; /* May be NULL */
URL_s->fe_data = fe_data;
/* start the load */
FE_GetURL(context, URL_s);
}
/* assemble username, password, and ftp:// or http:// URL into
* ftp://user:password@/blah format for uploading
*/
PUBLIC Bool
NET_MakeUploadURL( char **full_location, char *location,
char *user_name, char *password )
{
char *start;
char *pSrc;
char *destination;
char *at_ptr;
int iSize;
if( !full_location || !location ) return FALSE;
if( *full_location ) PR_Free(*full_location);
iSize = strlen(location) + 4;
if( user_name ) iSize += strlen(user_name);
if( password ) iSize += strlen(password);
*full_location = (char*)PR_Malloc(iSize);
if( !*full_location ){
/* Return an empty string */
*full_location = strdup("");
return FALSE;
}
**full_location = '\0';
/* Find start just past http:// or ftp:// */
start = PL_strstr(location, "//");
if( !start ) return FALSE;
/* Point to just past the host part */
start += 2;
pSrc = location;
destination = *full_location;
/* Copy up to and including "//" */
while( pSrc < start ) *destination++ = *pSrc++;
*destination = '\0';
/* Skip over any user:password in supplied location */
at_ptr = PL_strchr(start, '@');
if( at_ptr ){
start = at_ptr + 1;
}
/* Append supplied "user:password@"
* (This can be used without password)
*/
if( user_name && PL_strlen(user_name) > 0 ){
PL_strcat(*full_location, user_name);
if ( password && PL_strlen(password) > 0 ){
PL_strcat(*full_location,":");
PL_strcat(*full_location, password);
}
PL_strcat(*full_location, "@");
}
/* Append the rest of location */
PL_strcat(*full_location, start);
return TRUE;
}
/* extract the username, password, and reassembled location string
* from an ftp:// or http:// URL
*/
PUBLIC Bool
NET_ParseUploadURL( char *full_location, char **location,
char **user_name, char **password )
{
char *start;
char *skip_dest;
char *at_ptr;
char *colon_ptr;
char at;
char colon;
if( !full_location || !location ) return FALSE;
/* Empty exitisting strings... */
if(*location) PR_Free(*location);
if( user_name && *user_name) PR_Free(*user_name);
if( password && *password) PR_Free(*password);
/* Find start just past http:// or ftp:// */
start = PL_strstr(full_location, "//");
if( !start ) return FALSE;
/* Point to just past the host part */
start += 2;
/* Start by simply copying full location
* (may waste some bytes, but much simpler!)
*/
*location = PL_strdup(full_location);
if( !*location ) return FALSE;
/* Destination to append location without
* user:password is same place copied string
*/
skip_dest = *location + (start - full_location);
/* Skip over any user:password in supplied location
* while copying those to other strings
*/
at_ptr = PL_strchr(start, '@');
colon_ptr = PL_strchr(start, ':');
if( at_ptr ){
/* save character */
at = *at_ptr;
/* Copy part past the @ over previously-copied full string */
PL_strcpy(skip_dest, (at_ptr + 1));
/* Terminate for the password (or user) string */
*at_ptr = '\0';
if( colon_ptr ){
/* save character */
colon = *colon_ptr;
if( password ){
*password = PL_strdup((colon_ptr+1));
}
/* terminate for the user string */
*colon_ptr = '\0';
} else if( password ) {
*password = PL_strdup("");
}
if( user_name ){
*user_name = PL_strdup(start);
}
/* restore characters */
*at_ptr = at;
if( colon_ptr ) *colon_ptr = colon;
} else {
/* Supply empty strings for these */
if( user_name ){
*user_name = PL_strdup("");
}
if( password ){
*password = PL_strdup("");
}
}
return TRUE;
}
/* returns a malloc'd string containing a unique id
* generated from the sec random stuff.
*/
PUBLIC char *
NET_GetUniqueIdString(void)
{
#define BYTES_OF_RANDOMNESS 20
char rand_buf[BYTES_OF_RANDOMNESS+10];
char *rv=0;
RNG_GenerateGlobalRandomBytes(rand_buf, BYTES_OF_RANDOMNESS);
/* now uuencode it so it goes across the wire right */
rv = (char *) PR_Malloc((BYTES_OF_RANDOMNESS * (4/3)) + 10);
if(rv)
NET_UUEncode((unsigned char *)rand_buf, (unsigned char *)rv, BYTES_OF_RANDOMNESS);
return(rv);
#undef BYTES_OF_RANDOMNESS
}
/* The magic set of 64 chars in the uuencoded data */
PRIVATE unsigned char uuset[] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+'
,'/' };
MODULE_PRIVATE int
NET_UUEncode(unsigned char *src, unsigned char *dst, int srclen)
{
int i, r;
unsigned char *p;
/* To uuencode, we snip 8 bits from 3 bytes and store them as
6 bits in 4 bytes. 6*4 == 8*3 (get it?) and 6 bits per byte
yields nice clean bytes
It goes like this:
AAAAAAAA BBBBBBBB CCCCCCCC
turns into the standard set of uuencode ascii chars indexed by numbers:
00AAAAAA 00AABBBB 00BBBBCC 00CCCCCC
Snip-n-shift, snip-n-shift, etc....
*/
for (p=dst,i=0; i < srclen; i += 3) {
/* Do 3 bytes of src */
register unsigned char b0, b1, b2;
b0 = src[0];
if (i==srclen-1)
b1 = b2 = '\0';
else if (i==srclen-2) {
b1 = src[1];
b2 = '\0';
}
else {
b1 = src[1];
b2 = src[2];
}
*p++ = uuset[b0>>2];
*p++ = uuset[(((b0 & 0x03) << 4) | ((b1 & 0xf0) >> 4))];
*p++ = uuset[(((b1 & 0x0f) << 2) | ((b2 & 0xc0) >> 6))];
*p++ = uuset[b2 & 0x3f];
src += 3;
}
*p = 0; /* terminate the string */
r = (unsigned char *)p - (unsigned char *)dst; /* remember how many we
did */
/* Always do 4-for-3, but if not round threesome, have to go
clean up the last extra bytes */
for( ; i != srclen; i--)
*--p = '=';
return r;
}
PRIVATE char *
NET_RemoveQuotes(char * string)
{
char *rv;
char *end;
rv = XP_StripLine(string);
if(*rv == '"' || *rv == '\'')
rv++;
end = &rv[PL_strlen(rv)-1];
if(*end == '"' || *end == '\'')
*end = '\0';
return(rv); }
#define POST_DATA_BUFFER_SIZE 2048
struct WritePostDataData {
char *buffer;
XP_Bool last_line_was_complete;
int32 amt_in_buffer;
int32 amt_sent;
int32 total_amt_sent;
int32 file_size;
XP_File fp;
int32 headerSize;
int32 headerAmountSent;
XP_Bool CRSent;
XP_Bool LFSent;
};
PUBLIC void
NET_free_write_post_data_object(struct WritePostDataData *obj)
{
PR_ASSERT(obj);
if(!obj)
return;
if (obj->fp)
XP_FileClose(obj->fp);
FREEIF(obj->buffer);
FREE(obj);
}
/* returns whether or not the socket error was expected. If it was not, all the write post data
object was free'd, and the url_s->error_msg was set */
PRIVATE XP_Bool
net_expected_error(URL_Struct *URL_s, struct WritePostDataData **data_obj)
{
int err = PR_GetError();
if(err == PR_WOULD_BLOCK_ERROR || err == PR_IS_CONNECTED_ERROR)
return TRUE;
URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, err);
NET_free_write_post_data_object(*data_obj);
*data_obj = NULL;
return FALSE;
}
/* this function is called repeatably to write
* the post data body and headers onto the network.
*
* Returns negative on fatal error
* Returns zero when done
* Returns positive if not yet completed
*/
PUBLIC int
NET_WritePostData(MWContext *context,
URL_Struct *URL_s,
PRFileDesc *sock,
void **write_post_data_data,
PRBool add_crlf_to_line_endings)
{
struct WritePostDataData *data_obj = (struct WritePostDataData *)
*write_post_data_data;
/* init the data object */
if(!data_obj)
{
data_obj = PR_NEW(struct WritePostDataData);
*write_post_data_data = data_obj;
if(!data_obj)
return(MK_OUT_OF_MEMORY);
memset(data_obj, 0, sizeof(struct WritePostDataData));
data_obj->last_line_was_complete = TRUE;
data_obj->buffer = (char *) PR_Malloc(POST_DATA_BUFFER_SIZE);
if(!data_obj->buffer)
{
NET_free_write_post_data_object(data_obj);
*write_post_data_data=NULL;
return 0;
}
if(URL_s->post_headers)
data_obj->headerSize = PL_strlen(URL_s->post_headers);
else
data_obj->headerSize = 0;
data_obj->headerAmountSent = 0;
data_obj->CRSent = FALSE;
data_obj->LFSent = FALSE;
data_obj->amt_sent = 0;
}
if(!data_obj->fp && URL_s->post_data_is_file)
{
XP_StatStruct stat_entry;
/* Send the headers */
if( URL_s->post_headers &&
(data_obj->headerSize > data_obj->headerAmountSent) )
{
int32 amtWritten = 0;
amtWritten = PR_Write(sock,
URL_s->post_headers + data_obj->headerAmountSent,
data_obj->headerSize - data_obj->headerAmountSent);
/* if there was a problem */
if( amtWritten < 0 ) {
/* determine whether it was expected */
if( net_expected_error(URL_s, (struct WritePostDataData **) write_post_data_data) )
return 1;
else
return(MK_TCP_WRITE_ERROR);
}
#ifdef DEBUG
NET_NTrace("Tx: ", 4);
NET_NTrace(URL_s->post_headers + data_obj->headerAmountSent,
amtWritten);
#endif
data_obj->headerAmountSent += amtWritten;
if(data_obj->headerSize > data_obj->headerAmountSent)
return 1;
} /* END: URL_s->post_headers ... */
/* stat the file to get the size
*/
if(-1 != XP_Stat(URL_s->post_data, &stat_entry, xpFileToPost))
{
data_obj->file_size = stat_entry.st_size;
}
/* open the post data file
*/
data_obj->fp = XP_FileOpen(URL_s->post_data, xpFileToPost, XP_FILE_READ_BIN);
if(!data_obj->fp)
{
URL_s->error_msg = NET_ExplainErrorDetails(
MK_UNABLE_TO_LOCATE_FILE,
URL_s->post_data);
NET_free_write_post_data_object(data_obj);
*write_post_data_data = NULL;
return(MK_UNABLE_TO_LOCATE_FILE);
}
}
if(URL_s->post_data_is_file)
{
int32 amt_to_wrt;
int32 amt_wrt;
int type = NET_URL_Type (URL_s->address);
XP_Bool quote_lines_p = (type == MAILTO_TYPE_URL ||
type == NEWS_TYPE_URL ||
type == INTERNAL_NEWS_TYPE_URL);
/* do file based operations
*/
if(data_obj->amt_in_buffer < 1 || data_obj->amt_sent >= data_obj->amt_in_buffer)
{
/* read more data from the file
*/
if (quote_lines_p || add_crlf_to_line_endings)
{
char *line;
char *b = data_obj->buffer;
int32 bsize = POST_DATA_BUFFER_SIZE;
data_obj->amt_in_buffer = 0;
do {
int L;
line = XP_FileReadLine(b, bsize-5, data_obj->fp);
if (!line)
break;
L = PL_strlen(line);
/* escape periods only if quote_lines_p is set
*/
if (quote_lines_p &&
data_obj->last_line_was_complete && line[0] == '.')
{
/* This line begins with "." so we need to quote it
by adding another "." to the beginning of the line.
*/
int i;
line[L+1] = 0;
for (i = L; i > 0; i--)
line[i] = line[i-1];
L++;
}
/* set default */
data_obj->last_line_was_complete = TRUE;
if (L > 1 && line[L-2] == CR && line[L-1] == LF)
{
/* already ok */
}
else if(L > 0 && (line[L-1] == LF || line[L-1] == CR))
{
/* only add the crlf if required
* we still need to do all the
* if comparisons here to know
* if the line was complete
*/
if(add_crlf_to_line_endings)
{
/* Change newline to CRLF. */
L--;
line[L++] = CR;
line[L++] = LF;
line[L] = 0;
}
}
else
{
data_obj->last_line_was_complete = FALSE;
}
bsize -= L;
b += L;
data_obj->amt_in_buffer += L;
} while (line && bsize > 100);
}
else
{
data_obj->amt_in_buffer = XP_FileRead(data_obj->buffer,
POST_DATA_BUFFER_SIZE-1,
data_obj->fp);
}
HG29784
if(data_obj->amt_in_buffer < 1)
{
/* end of file, all done
*/
/*
* handled by NET_free_write_post_data_object
*
XP_FileClose(data_obj->fp);
*/
PR_ASSERT(data_obj->total_amt_sent >= data_obj->file_size);
NET_free_write_post_data_object(data_obj);
*write_post_data_data = NULL;
return(0);
}
data_obj->amt_sent = 0;
}
amt_to_wrt = data_obj->amt_in_buffer-data_obj->amt_sent;
/* write some data to the socket
*/
amt_wrt = PR_Write(sock,
data_obj->buffer+data_obj->amt_sent,
amt_to_wrt);
#ifdef DEBUG
NET_NTrace("Tx: ", 4);
NET_NTrace(data_obj->buffer+data_obj->amt_sent,
amt_to_wrt);
#endif /* DEBUG */
if(amt_wrt < 0)
{
int err = PR_GetError();
if(err == PR_WOULD_BLOCK_ERROR
|| err == PR_IS_CONNECTED_ERROR)
return(1); /* continue */
URL_s->error_msg = NET_ExplainErrorDetails(MK_TCP_WRITE_ERROR, err);
/*
* handled by net_free_write_post_data
XP_FileClose(data_obj->fp);
*/
NET_free_write_post_data_object(data_obj);
*write_post_data_data = NULL;
return(MK_TCP_WRITE_ERROR);
}
#if defined(XP_UNIX) && defined(DEBUG)
if(amt_wrt < amt_to_wrt)
fprintf(stderr, "Fwrite wrote less than requested!\n");
#endif
/* safty for broken SSL_write */
if(amt_wrt > amt_to_wrt)
amt_wrt = amt_to_wrt;
#if defined(XP_UNIX) && defined(DEBUG)
if(MKLib_trace_flag)
{
fwrite("Tx: ", 1, 4, stderr);
fwrite(data_obj->buffer+data_obj->amt_sent, 1, amt_wrt, stderr);
fwrite("\n", 1, 1, stderr);
}
#endif
data_obj->amt_sent += amt_wrt;
data_obj->total_amt_sent += amt_wrt;
/* return the amount of data written
* so that callers can provide progress
* if necessary. If the amt_wrt was
* zero (I don't think it can ever happen)
* return 1 because zero means all done
*/
if(amt_wrt > 0)
return(amt_wrt);
else
return(1);
}
else
{
/* do memory based operations */
int32 amtWritten;
/* send the headers if there are any && and they haven't been sent */
if( URL_s->post_headers && (data_obj->headerSize > data_obj->headerAmountSent) )
{
amtWritten = PR_Write(sock,
URL_s->post_headers + data_obj->headerAmountSent,
data_obj->headerSize - data_obj->headerAmountSent);
/* if there was a problem */
if(amtWritten < 0) {
/* determine whether it was expected */
if( net_expected_error(URL_s, (struct WritePostDataData **) write_post_data_data) )
return 1;
else
return(MK_TCP_WRITE_ERROR);
}
#ifdef DEBUG
NET_NTrace("Tx: ", 4);
NET_NTrace(URL_s->post_headers + data_obj->headerAmountSent,
amtWritten);
NET_NTrace("\n", 1);
#endif
data_obj->headerAmountSent += amtWritten;
/* If all the header data hasn't been sent, return 1 (not done). */
if(data_obj->headerSize > data_obj->headerAmountSent)
return 1;
}
/* Separate the headers from the data with a CR and LF. */
if(!data_obj->CRSent) {
data_obj->buffer[0] = CR;
data_obj->buffer[1] = LF;
data_obj->buffer[2] = '\0';
amtWritten = PR_Write(sock, data_obj->buffer, PL_strlen(data_obj->buffer));
if(amtWritten < 0) {
/* determine whether it was expected */
if( net_expected_error(URL_s, (struct WritePostDataData **) write_post_data_data) )
return 1;
else
return(MK_TCP_WRITE_ERROR);
}
/* If no data was sent, return 1 (not done). */
if(amtWritten == 0)
return 1;
if(amtWritten >= 1)
data_obj->CRSent = TRUE;
if(amtWritten >= 2)
data_obj->LFSent = TRUE;
}
if(!data_obj->LFSent) {
data_obj->buffer[0] = LF;
data_obj->buffer[1] = '\0';
amtWritten = PR_Write(sock, data_obj->buffer, PL_strlen(data_obj->buffer));
if(amtWritten < 0) {
/* determine whether it was expected */
if( net_expected_error(URL_s, (struct WritePostDataData **) write_post_data_data) )
return 1;
else
return(MK_TCP_WRITE_ERROR);
}
/* If no data was sent, return 1 (not done). */
if(amtWritten == 0)
return 1;
data_obj->LFSent = TRUE;
TRACEMSG(("Adding \\n to command"));
}
if(!URL_s->post_data)
{
NET_free_write_post_data_object(data_obj);
*write_post_data_data = NULL;
return(MK_OUT_OF_MEMORY);
}
/* Send the data if we haven't already sent it */
if(URL_s->post_data_size > data_obj->amt_sent) {
amtWritten = PR_Write(sock,
URL_s->post_data+data_obj->amt_sent,
URL_s->post_data_size-data_obj->amt_sent);
if(amtWritten < 0) {
/* determine whether it was expected */
if( net_expected_error(URL_s, (struct WritePostDataData **) write_post_data_data) )
return 1;
else
return(MK_TCP_WRITE_ERROR);
}
/* If no data was sent, return 1 (not done). */
if(amtWritten == 0)
return 1;
#ifdef DEBUG
NET_NTrace("Tx: ", 4);
NET_NTrace(URL_s->post_data+data_obj->amt_sent,
amtWritten);
NET_NTrace("\n", 1);
#endif
data_obj->amt_sent += amtWritten;
/* if all data has been written, return done */
if(data_obj->amt_sent >= URL_s->post_data_size) {
NET_free_write_post_data_object(data_obj);
*write_post_data_data = NULL;
return 0;
}
else {
return amtWritten;
}
}
else {
NET_free_write_post_data_object(data_obj);
*write_post_data_data = NULL;
return 0;
}
}
/* By this time all headers and data should have been sent, shouldn't get here */
PR_ASSERT(0);
NET_free_write_post_data_object(data_obj);
*write_post_data_data = NULL;
return(-1); /* shouldn't ever get here */
}
void
NET_ParseContentTypeHeader(MWContext *context, char *value, URL_Struct *URL_s, PRBool is_http)
{
char *first_arg, *next_arg;
if(URL_s->preset_content_type)
return;
first_arg = strtok(value, ";");
StrAllocCopy(URL_s->content_type, XP_StripLine(value));
TRACEMSG(("Found content_type: %s",URL_s->content_type));
/* assign and compare
*
* search for charset
* and boundry
*/
while((next_arg = strtok(NULL, ";")) != NULL)
{
next_arg = XP_StripLine(next_arg);
if(!PL_strncasecmp(next_arg,"CHARSET=", 8))
{
#ifdef MOZILLA_CLIENT
char *charset_tag = NET_RemoveQuotes(next_arg+8);
/*
* To make http LOAD-FROM-NET and LOAD-FROM-CACHE
* charset tag handling consistant just put the http
* charset info in the url struct.
* We will use this later when setting up charset converter.
* (we do this because: on a reload from cache this code will not get called)
*/
if (is_http)
{
/* record HTTP charset tag so we can: */
/* 1) use it to figure out the doc_csid */
/* 2) report it to the user */
StrAllocCopy(URL_s->charset, charset_tag);
TRACEMSG(("Found HTTP charset: %s", charset_tag));
}
else
{
INTL_CCCReportMetaCharsetTag(context, charset_tag);
TRACEMSG(("Found Meta charset: %s", charset_tag));
}
#else
PR_ASSERT(0);
#endif /* MOZILLA_CLIENT */
}
else if(!PL_strncasecmp(next_arg,"BOUNDARY=", 9))
{
StrAllocCopy(URL_s->boundary, NET_RemoveQuotes(next_arg+9));
TRACEMSG(("Found boundary: %s", URL_s->boundary));
}
else if(!PL_strncasecmp(next_arg,"AUTOSCROLL", 10))
{
#define DEFAULT_AUTO_SCROLL_BUFFER 100
if(*next_arg+10 == '=')
URL_s->auto_scroll = atoi(NET_RemoveQuotes(next_arg+11));
if(!URL_s->auto_scroll)
URL_s->auto_scroll = DEFAULT_AUTO_SCROLL_BUFFER;
TRACEMSG(("Found autoscroll attr.: %ud", URL_s->auto_scroll));
}
}
}
/* parse a mime header.
*
* Both name and value strings will be modified during
* parsing. If you need to retain your strings make a
* copy before passing them in.
*
* Values will be placed in the URL struct.
*
* returns TRUE if it found a valid header
* and FALSE if it didn't
*/
PUBLIC Bool
NET_ParseMimeHeader(FO_Present_Types outputFormat,
MWContext *context,
URL_Struct *URL_s,
char *name,
char *value,
XP_Bool is_http)
{
Bool found_one = FALSE;
Bool ret_value = FALSE;
char empty_string[2];
char *colon_ptr;
if(!name || !URL_s)
return(FALSE);
name = XP_StripLine(name);
if(value)
{
value = XP_StripLine(value);
}
else
{
*empty_string = '\0';
value = empty_string;
}
colon_ptr = PL_strchr(name, ':');
if (colon_ptr) {
*colon_ptr = '\0';
}
ret_value = NET_AddToAllHeaders(URL_s, name, value);
if (colon_ptr) {
*colon_ptr = ':';
}
if (!ret_value) {
return(FALSE);
}
switch(toupper(*name))
{
case 'A':
if(!PL_strncasecmp(name,"ACCEPT-RANGES:",14)
|| !PL_strncasecmp(name, "ALLOW-RANGES:",13))
{
char * next_arg = strtok(value, ";");
found_one = TRUE;
while(next_arg)
{
next_arg = XP_StripLine(next_arg);
if(!PL_strncasecmp(next_arg,"BYTES", 5))
{
TRACEMSG(("Document Allows for BYTERANGES!"));
URL_s->server_can_do_byteranges = TRUE;
}
next_arg = strtok(NULL, ";");
}
}
else if(!PL_strncasecmp(name, "AGE:",4))
{
long age;
strtok(value, ";"); /* terminate at ';' */
age = atol(value);
/* Small deviation from the spec. Assumption being made-
Req. time ~= Resp Time ~= now */
if (URL_s->server_date)
{
time_t now = time(NULL);
time_t correction = now - MAX(age, MAX(0, now- URL_s->server_date));
if (URL_s->expires)
URL_s->expires += correction;
}
}
case 'C':
if (!PL_strncasecmp(name,"CACHE-CONTROL:",14))
{
/* Potential values include */
/* public */
/* private[=field-name] */
/* no-cache [=field-name] */
/* no-store */
/* no-transform - this is only applicable for proxies and hence not put here*/
/* must-revalidate */
/* proxy-revalidate */
/* max-age=delta-seconds */
/* s-maxage=delta-seconds */
/* any cache extension */
char * control = NET_RemoveQuotes(value);
if (!PL_strncasecmp(control, "NO-CACHE", 8))
{
/* same as pragma=no-cache */
URL_s->dont_cache = TRUE;
}
else if (!PL_strncasecmp(control, "MAX-AGE=", 8))
{
/* Takes precedence over the Expires header.
Corrected Expires is Max-age + Server Date */
if (URL_s->server_date)
{
URL_s->expires = URL_s->server_date + atol(strtok(control+8,";"));
}
}
#if 0 /* Unimplemented */
else if (!PL_strncasecmp(control, "PUBLIC", 6))
{
/* Place holder for shared cache concepts */
}
else if (!PL_strncasecmp(control, "PRIVATE", 7))
{
/* Place holder for shared cache concepts */
}
else if (!PL_strncasecmp(control, "NO-STORE", 8))
{
/* According to the spec this "should" disable explicit saving
of the document outside the caching system, eg. in "Save As..."
dialogs. Since this idea sucks, this is a place holder. */
}
else if (!PL_strncasecmp(control, "MUST-REVALIDATE", 15))
{
/* The cache must do an end-to-end revalidation every time, if
the response headers suggest an entity is stale. We do this
anyway. */
}
else if (!PL_strncasecmp(control, "PROXY-REVALIDATE", 16))
{
/* Same as must-revalidate except that it does not apply to
non-shared user agents (like us) */
}
else if (!PL_strncasecmp(control, "S-MAXAGE", 8))
{
/* Applicable for shared caches. Place holder. */
}
else
{
/* A new cache extension */
}
#endif /* Unimplemented */
}
else if(!PL_strncasecmp(name,"CONTENT-DISPOSITION:",20))
{
char *next_arg;
found_one = TRUE;
next_arg = strtok(value, ";");
while(next_arg)
{
next_arg = XP_StripLine(next_arg);
if(!PL_strncasecmp(next_arg,"filename=", 9))
{
StrAllocCopy(URL_s->content_name,
NET_RemoveQuotes(next_arg+9));
}
next_arg = strtok(NULL, ";");
}
}
else if(!PL_strncasecmp(name,"CONTENT-TYPE:",13))
{
found_one = TRUE;
NET_ParseContentTypeHeader(context, value, URL_s, is_http);
}
else if(!PL_strncasecmp(name,"CONTENT-LENGTH:",15))
{
found_one = TRUE;
strtok(value, ";"); /* terminate at ';' */
/* don't reset the content-length if
* we have already set it from the content-range
* header. If high_range is set then we must
* have seen a content-range header.
*/
if(!URL_s->high_range)
URL_s->content_length = atol(value);
}
else if(!PL_strncasecmp(name,"CONTENT-ENCODING:",17))
{
found_one = TRUE;
strtok(value, ";"); /* terminate at ';' */
StrAllocCopy(URL_s->content_encoding, value);
}
else if(!PL_strncasecmp(name,"CONTENT-RANGE:",14))
{
unsigned long low, high, length;
strtok(value, ";"); /* terminate at ';' */
/* range header looks like:
* Range: bytes x-y/z
* where:
*
* X is the number of the first byte returned
* (the first byte is byte number zero).
*
* Y is the number of the last byte returned
* (in case of the end of the document this
* is one smaller than the size of the document
* in bytes).
*
* Z is the total size of the document in bytes.
*
* Scan through temp variables because %l is 64 bits on OSF1
*/
sscanf(value, "bytes %lu-%lu/%lu", &low, &high, &length);
URL_s->low_range = (int32) low;
URL_s->high_range = (int32) high;
URL_s->content_length = (int32) length;
/* if we get a range header set the "can-do-byteranges"
* since it must be doing byteranges
*/
URL_s->server_can_do_byteranges = TRUE;
}
else if(!PL_strncasecmp(name,"CONNECTION:",11))
{
strtok(value, ";"); /* terminate at ';' */
strtok(value, ","); /* terminate at ',' */
if(!PL_strcasecmp("KEEP-ALIVE", XP_StripLine(value)))
URL_s->can_reuse_connection = TRUE;
if(!PL_strcasecmp("CLOSE", XP_StripLine(value)))
URL_s->can_reuse_connection = FALSE;
}
break;
case 'D':
if(!PL_strncasecmp(name,"DATE:",5))
{
found_one = TRUE;
URL_s->server_date = NET_ParseDate(value);
}
#if 0
else if(!PL_strncasecmp(name,"DEST-IP:",8))
{
found_one = TRUE;
URL_s->destIP = PL_strdup(value);
}
#endif
break;
case 'E':
if(!PL_strncasecmp(name,"EXPIRES:",8))
{
char *cp, *expires = NET_RemoveQuotes(value);
Bool is_number=TRUE;
/* Expires: 123 - number of seconds
* Expires: date - absolute date
*/
strtok(value, ";"); /* terminate at ';' */
/* check to see if the expires date is just a
* value in seconds
*/
for(cp = expires; *cp != '\0'; cp++)
if(!isdigit(*cp))
{
is_number=FALSE;
break;
}
if(is_number)
URL_s->expires = time(NULL) + atol(expires);
else
URL_s->expires = NET_ParseDate(expires);
/* if we couldn't parse the date correctly
* make it already expired, as per the HTTP spec
*/
if(!URL_s->expires)
URL_s->expires = 1;
found_one = TRUE;
}
else if(!PL_strncasecmp(name,"EXT-CACHE:",10))
{
#ifdef MOZILLA_CLIENT
char * next_arg = strtok(value, ";");
char * name=0;
char * instructions=0;
found_one = TRUE;
while(next_arg)
{
next_arg = XP_StripLine(next_arg);
if(!PL_strncasecmp(next_arg,"name=", 5))
{
TRACEMSG(("Found external cache name: %s", next_arg+5));
name = NET_RemoveQuotes(next_arg+5);
}
else if(!PL_strncasecmp(next_arg,"instructions=", 13))
{
TRACEMSG(("Found external cache instructions: %s",
next_arg+13));
instructions = NET_RemoveQuotes(next_arg+13);
}
next_arg = strtok(NULL, ";");
}
if(name)
NET_OpenExtCacheFAT(context, name, instructions);
#else
PR_ASSERT(0);
#endif /* MOZILLA_CLIENT */
}
else if (!PL_strncasecmp(name, "ETAG:",5))
{
/* Weak Validators are skipped for now*/
char* etag;
if (!PL_strncasecmp(value, "W/", 2))
etag = NET_RemoveQuotes(value+2);
else
etag = NET_RemoveQuotes(value);
StrAllocCopy(URL_s->etag,etag);
}
break;
case 'L':
if(!PL_strncasecmp(name,"LOCATION:",9))
{
found_one = TRUE;
/* don't do this here because a url can
* contain a ';'
* strtok(value, ";");
*/
URL_s->redirecting_url = NET_MakeAbsoluteURL(
URL_s->address,
XP_StripLine(value));
TRACEMSG(("Found location: %s\n",URL_s->redirecting_url));
if(MOCHA_TYPE_URL == NET_URL_Type(URL_s->redirecting_url))
{
/* don't allow mocha URL's as refresh
*/
FREE_AND_CLEAR(URL_s->redirecting_url);
}
}
else if(!PL_strncasecmp(name,"LAST-MODIFIED:",14))
{
found_one = TRUE;
URL_s->last_modified = NET_ParseDate(value);
TRACEMSG(("Found last modified date: %d\n",URL_s->last_modified));
}
else if(!PL_strncasecmp(name,"LINK:",5))
{
#define PAGE_SERVICES_REL "pageServices"
char * next_arg = strtok(value, ";");
char * link_val;
enum { UNKNOWN_REL_TYPE, PAGE_SERVICES_REL_TYPE } rel_type;
found_one = TRUE;
rel_type = UNKNOWN_REL_TYPE;
/* strip the < and > from the url */
if(*value == '<')
{
value++;
/* strip the end one too */
value[PL_strlen(value)-1] = '\0';
}
/* ok if malloc fails */
link_val = NET_MakeAbsoluteURL(URL_s->address,
XP_StripLine(value));
while(next_arg)
{
next_arg = XP_StripLine(next_arg);
if(!PL_strncasecmp(next_arg,"rel=", 4))
{
char * rel = NET_RemoveQuotes(next_arg+4);
if(!PL_strcasecmp(rel, PAGE_SERVICES_REL))
rel_type = PAGE_SERVICES_REL_TYPE;
}
next_arg = strtok(NULL, ";");
}
/* if we fount a rel for page services assign it */
if(rel_type == PAGE_SERVICES_REL_TYPE)
URL_s->page_services_url = link_val;
else
PR_FREEIF(link_val);
}
break;
case 'P':
if(!PL_strncasecmp(name,"PROXY-AUTHENTICATE:",19))
{
char *auth = value;
found_one = TRUE;
strtok(value, ";"); /* terminate at ';' */
if (net_IsBetterAuth(auth, URL_s->proxy_authenticate))
StrAllocCopy(URL_s->proxy_authenticate, auth);
}
else if(!PL_strncasecmp(name,"PROXY-CONNECTION:",17))
{
strtok(value, ";"); /* terminate at ';' */
strtok(value, ","); /* terminate at ',' */
if(!PL_strcasecmp("KEEP-ALIVE", XP_StripLine(value)))
URL_s->can_reuse_connection = TRUE;
}
else if(!PL_strncasecmp(name,"PRAGMA:",7))
{
found_one = TRUE;
strtok(value, ";"); /* terminate at ';' */
if(!PL_strcasecmp(value, "NO-CACHE"))
URL_s->dont_cache = TRUE;
}
break;
case 'R':
if(!PL_strncasecmp(name,"RANGE:",6))
{
unsigned long low, high, length;
strtok(value, ";"); /* terminate at ';' */
/* range header looks like:
* Range: bytes x-y/z
* where:
*
* X is the number of the first byte returned
* (the first byte is byte number zero).
*
* Y is the number of the last byte returned
* (in case of the end of the document this
* is one smaller than the size of the document
* in bytes).
*
* Z is the total size of the document in bytes.
*
* Scan through temp variables because %l is 64 bits on OSF1
*/
sscanf(value, "bytes %lu-%lu/%lu", &low, &high, &length);
URL_s->low_range = (int32) low;
URL_s->high_range = (int32) high;
URL_s->content_length = (int32) length;
/* if we get a range header set the "can-do-byteranges"
* since it must be doing byteranges
*/
URL_s->server_can_do_byteranges = TRUE;
}
else if(!PL_strncasecmp(name,"REFRESH:",8) && !EDT_IS_EDITOR(context))
{
char *first_arg, *next_arg;
found_one = TRUE;
/* clear any previous refresh URL */
if(URL_s->refresh_url)
{
FREE(URL_s->refresh_url);
URL_s->refresh_url=0;
}
first_arg = strtok(value, ";");
URL_s->refresh = atol(value);
TRACEMSG(("Found refresh header: %d",URL_s->refresh));
/* assign and compare
*/
while((next_arg = strtok(NULL, ";")) != NULL)
{
next_arg = XP_StripLine(next_arg);
if(!PL_strncasecmp(next_arg,"URL=", 4))
{
URL_s->refresh_url = NET_MakeAbsoluteURL(
URL_s->address,
NET_RemoveQuotes(next_arg+4));
TRACEMSG(("Found refresh url: %s",
URL_s->refresh_url));
if(MOCHA_TYPE_URL == NET_URL_Type(URL_s->refresh_url))
{
/* don't allow mocha URL's as refresh
*/
FREE_AND_CLEAR(URL_s->refresh_url);
}
}
}
if(!URL_s->refresh_url)
StrAllocCopy(URL_s->refresh_url, URL_s->address);
}
break;
case 'S':
if(!PL_strncasecmp(name,"SET-COOKIE:",11))
{
found_one = TRUE;
NET_SetCookieStringFromHttp(outputFormat, URL_s, context, URL_s->address, value);
}
else if(!PL_strncasecmp(name, "SERVER:", 7))
{
found_one = TRUE;
if(PL_strcasestr(value, "NETSITE"))
URL_s->is_netsite = TRUE;
else if(PL_strcasestr(value, "NETSCAPE"))
URL_s->is_netsite = TRUE;
}
break;
case 'T':
if(!PL_strncasecmp(name,"TRANSFER-ENCODING:",18))
{
found_one = TRUE;
strtok(value, ";"); /* terminate at ';' */
StrAllocCopy(URL_s->transfer_encoding, value);
}
case 'W':
if(!PL_strncasecmp(name,"WWW-AUTHENTICATE:",17))
{
found_one = TRUE;
strtok(value, ";"); /* terminate at ';' */
StrAllocCopy(URL_s->authenticate, value);
}
else if(!PL_strncasecmp(name, "WWW-PROTECTION-TEMPLATE:", 24))
{
found_one = TRUE;
strtok(value, ";"); /* terminate at ';' */
StrAllocCopy(URL_s->protection_template, value);
}
else if(!PL_strncasecmp(name, "WINDOW-TARGET:", 14))
{
found_one = TRUE;
strtok(value, ";"); /* terminate at ';' */
if (URL_s->window_target == NULL)
{
if ((XP_IS_ALPHA(value[0]) != FALSE)||
(XP_IS_DIGIT(value[0]) != FALSE)||
(value[0] == '_'))
{
StrAllocCopy(URL_s->window_target, value);
}
}
}
break;
default:
/* ignore other headers */
break;
}
return(found_one);
}
/* scans a line for references to URL's and turns them into active
* links. If the output size is exceeded the line will be
* truncated. "output" must be at least "output_size" characters
* long
*
* This also quotes other HTML forms, and italicizes citations,
* unless `urls_only' is true.
*/
#ifndef MOZILLA_CLIENT
/* If we're not in the client, stub out the libmsg interface to the
citation-highlighting code.
*/
# define MSG_PlainFont 0
# define MSG_BoldFont 1
# define MSG_ItalicFont 2
# define MSG_BoldItalicFont 3
# define MSG_NormalSize 4
# define MSG_Bigger 5
# define MSG_Smaller 6
#endif /* MOZILLA_CLIENT */
static MSG_FONT CitationFont = MSG_ItalicFont;
static int32 CitationSize = 0;
static char *CitationColor = 0;
static int CitationDataValid = -1; /* -1=first time, 0=changed; 1=data ok */
/* fix Mac warning about missing prototype */
int PR_CALLBACK
net_citation_style_changed(const char* name, void* closure);
int PR_CALLBACK
net_citation_style_changed(const char* name, void* closure)
{
CitationDataValid = 0;
return 0;
}
PUBLIC int
NET_ScanForURLs(MSG_Pane* pane, const char *input, int32 input_size,
char *output, int output_size, XP_Bool urls_only)
{
int col = 0;
const char *cp;
const char *end = input + input_size;
char *output_ptr = output;
char *end_of_buffer = output + output_size - 40; /* add safty zone :( */
Bool line_is_citation = FALSE;
const char *cite_open1, *cite_close1;
const char *cite_open2, *cite_close2;
const char* color = NULL;
if (urls_only)
{
cite_open1 = cite_close1 = "";
cite_open2 = cite_close2 = "";
}
else
{
#ifdef MOZILLA_CLIENT
if (CitationDataValid != 1) {
int32 value = (int32) MSG_ItalicFont;
if (CitationDataValid < 0) {
PREF_RegisterCallback("mail.quoted_style",
net_citation_style_changed,
NULL);
PREF_RegisterCallback("mail.quoted_size",
net_citation_style_changed,
NULL);
PREF_RegisterCallback("mail.citation_color",
net_citation_style_changed,
NULL);
}
PREF_GetIntPref("mail.quoted_style", &value);
CitationFont = (MSG_FONT) value;
CitationSize = 0;
PREF_GetIntPref("mail.quoted_size", &CitationSize);
FREEIF(CitationColor);
CitationColor = NULL;
PREF_CopyCharPref("mail.citation_color", &CitationColor);
CitationDataValid = 1;
}
switch (CitationFont)
{
case MSG_PlainFont:
cite_open1 = "", cite_close1 = "";
break;
case MSG_BoldFont:
cite_open1 = "<B>", cite_close1 = "</B>";
break;
case MSG_ItalicFont:
cite_open1 = "<I>", cite_close1 = "</I>";
break;
case MSG_BoldItalicFont:
cite_open1 = "<B><I>", cite_close1 = "</I></B>";
break;
default:
PR_ASSERT(0);
cite_open1 = cite_close1 = "";
break;
}
switch (CitationSize) {
case 0: /* Normal */
cite_open2 = "", cite_close2 = "";
break;
case 1: /* Bigger */
cite_open2 = "<FONT SIZE=+1>", cite_close2 = "</FONT>";
break;
case 2: /* Smaller */
case -1: /* backwards compatability with some old code */
cite_open2 = "<FONT SIZE=-1>", cite_close2 = "</FONT>";
break;
default:
PR_ASSERT(0);
cite_open2 = cite_close2 = "";
break;
}
#else
PR_ASSERT(0);
#endif /* MOZILLA_CLIENT */
}
if (!urls_only)
{
/* Decide whether this line is a quotation, and should be italicized.
This implements the following case-sensitive regular expression:
^[ \t]*[A-Z]*[]>]
Which matches these lines:
> blah blah blah
> blah blah blah
LOSER> blah blah blah
LOSER] blah blah blah
*/
const char *s = input;
while (s < end && XP_IS_SPACE (*s)) s++;
while (s < end && *s >= 'A' && *s <= 'Z') s++;
if (s >= end)
;
else if (input_size >= 6 && *s == '>' &&
!PL_strncmp (input, ">From ", 6)) /* sendmail... */
;
else if (*s == '>' || *s == ']')
{
line_is_citation = TRUE;
PL_strcpy(output_ptr, cite_open1);
output_ptr += PL_strlen(cite_open1);
PL_strcpy(output_ptr, cite_open2);
output_ptr += PL_strlen(cite_open2);
if (CitationColor &&
output_ptr + PL_strlen(CitationColor) + 20 < end_of_buffer) {
PL_strcpy(output_ptr, "<FONT COLOR=");
output_ptr += PL_strlen(output_ptr);
PL_strcpy(output_ptr, CitationColor);
output_ptr += PL_strlen(output_ptr);
PL_strcpy(output_ptr, ">");
output_ptr += PL_strlen(output_ptr);
}
}
}
/* Normal lines are scanned for buried references to URL's
Unfortunately, it may screw up once in a while (nobody's perfect)
*/
for(cp = input; cp < end && output_ptr < end_of_buffer; cp++)
{
/* if NET_URL_Type returns true then it is most likely a URL
But only match protocol names if at the very beginning of
the string, or if the preceeding character was not alphanumeric;
this lets us match inside "---HTTP://XXX" but not inside of
things like "NotHTTP://xxx"
*/
int type = 0;
if(!XP_IS_SPACE(*cp) &&
(cp == input || (!XP_IS_ALPHA(cp[-1]) && !XP_IS_DIGIT(cp[-1]))) &&
(type = NET_URL_Type(cp)) != 0)
{
const char *cp2;
for(cp2=cp; cp2 < end; cp2++)
{
/* These characters always mark the end of the URL. */
if (XP_IS_SPACE(*cp2) ||
*cp2 == '<' || *cp2 == '>' ||
*cp2 == '`' || *cp2 == ')' ||
*cp2 == '\'' || *cp2 == '"' ||
*cp2 == ']' || *cp2 == '}') break;
}
/* Check for certain punctuation characters on the end, and strip
them off. */
while (cp2 > cp &&
(cp2[-1] == '.' || cp2[-1] == ',' || cp2[-1] == '!' ||
cp2[-1] == ';' || cp2[-1] == '-' || cp2[-1] == '?' ||
cp2[-1] == '#'))
cp2--;
col += (cp2 - cp);
/* if the url is less than 7 characters then we screwed up
* and got a "news:" url or something which is worthless
* to us. Exclude the A tag in this case.
*
* Also exclude any URL that ends in a colon; those tend
* to be internal and magic and uninteresting.
*
* And also exclude the builtin icons, whose URLs look
* like "internal-gopher-binary".
*/
if (cp2-cp < 7 ||
(cp2 > cp && cp2[-1] == ':') ||
!PL_strncmp(cp, "internal-", 9))
{
memcpy(output_ptr, cp, cp2-cp);
output_ptr += (cp2-cp);
*output_ptr = 0;
}
else
{
char *quoted_url;
int32 size_left = output_size - (output_ptr-output);
if(cp2-cp > size_left)
return MK_OUT_OF_MEMORY;
memcpy(output_ptr, cp, cp2-cp);
output_ptr[cp2-cp] = 0;
quoted_url = NET_EscapeHTML(output_ptr);
if (!quoted_url) return MK_OUT_OF_MEMORY;
PR_snprintf(output_ptr, size_left,
"<A HREF=\"%s\">%s</A>",
quoted_url,
quoted_url);
output_ptr += PL_strlen(output_ptr);
PR_Free(quoted_url);
}
cp = cp2-1; /* go to next word */
}
else
{
/* Make sure that special symbols don't screw up the HTML parser
*/
if(*cp == '<')
{
PL_strcpy(output_ptr, "&lt;");
output_ptr += 4;
col++;
}
else if(*cp == '>')
{
PL_strcpy(output_ptr, "&gt;");
output_ptr += 4;
col++;
}
else if(*cp == '&')
{
PL_strcpy(output_ptr, "&amp;");
output_ptr += 5;
col++;
}
else
{
*output_ptr++ = *cp;
col++;
}
}
}
*output_ptr = 0;
if (line_is_citation) /* Close off the highlighting */
{
if (CitationColor) {
PL_strcpy(output_ptr, "</FONT>");
output_ptr += PL_strlen(output_ptr);
}
PL_strcpy(output_ptr, cite_close2);
output_ptr += PL_strlen (cite_close2);
PL_strcpy(output_ptr, cite_close1);
output_ptr += PL_strlen (cite_close1);
}
return 0;
}
static void
Append(char** output, int32* output_max, char** curoutput, const char* buf,
int32 length)
{
if (length + (*curoutput) - (*output) >= *output_max) {
int offset = (*curoutput) - (*output);
do {
(*output_max) += 1024;
} while (length + (*curoutput) - (*output) >= *output_max);
*output = PR_Realloc(*output, *output_max);
if (!*output) return;
*curoutput = *output + offset;
}
memcpy(*curoutput, buf, length);
*curoutput += length;
}
char*
NET_ScanHTMLForURLs(const char* input)
{
char* output = NULL;
char* curoutput;
int32 output_max;
char* tmpbuf = NULL;
int32 tmpbuf_max;
int32 inputlength;
const char* inputend;
const char* linestart;
const char* lineend;
PR_ASSERT(input);
if (!input) return NULL;
inputlength = PL_strlen(input);
output_max = inputlength + 1024; /* 1024 bytes ought to be enough to quote
several URLs, which ought to be as many
as most docs use. */
output = PR_Malloc(output_max);
if (!output) goto FAIL;
tmpbuf_max = 1024;
tmpbuf = PR_Malloc(tmpbuf_max);
if (!tmpbuf) goto FAIL;
inputend = input + inputlength;
linestart = input;
curoutput = output;
/* Here's the strategy. We find a chunk of plainish looking text -- no
embedded CR or LF, no "<" or "&". We feed that off to NET_ScanForURLs,
and append the result. Then we skip to the next bit of plain text. If
we stopped at an "&", go to the terminating ";". If we stopped at a
"<", well, if it was a "<A>" tag, then skip to the closing "</A>".
Otherwise, skip to the end of the tag.
*/
lineend = linestart;
while (linestart < inputend && lineend <= inputend) {
switch (*lineend) {
case '<':
case '>':
case '&':
case CR:
case LF:
case '\0':
if (lineend > linestart) {
int length = lineend - linestart;
if (length * 3 > tmpbuf_max) {
tmpbuf_max = length * 3 + 512;
PR_Free(tmpbuf);
tmpbuf = PR_Malloc(tmpbuf_max);
if (!tmpbuf) goto FAIL;
}
if (NET_ScanForURLs(NULL, linestart, length,
tmpbuf, tmpbuf_max, TRUE) < 0) {
goto FAIL;
}
length = PL_strlen(tmpbuf);
Append(&output, &output_max, &curoutput, tmpbuf, length);
if (!output) goto FAIL;
}
linestart = lineend;
lineend = NULL;
if (inputend - linestart < 5) {
/* Too little to worry about; shove the rest out. */
lineend = inputend;
} else {
switch (*linestart) {
case '<':
if ((linestart[1] == 'a' || linestart[1] == 'A') &&
linestart[2] == ' ') {
lineend = PL_strcasestr(linestart, "</a");
if (lineend) {
lineend = PL_strchr(lineend, '>');
if (lineend) lineend++;
}
} else {
lineend = PL_strchr(linestart, '>');
if (lineend) lineend++;
}
break;
case '&':
lineend = PL_strchr(linestart, ';');
if (lineend) lineend++;
break;
default:
lineend = linestart + 1;
break;
}
}
if (!lineend) lineend = inputend;
Append(&output, &output_max, &curoutput, linestart,
lineend - linestart);
if (!output) goto FAIL;
linestart = lineend;
break;
default:
lineend++;
}
}
PR_Free(tmpbuf);
*curoutput = '\0';
return output;
FAIL:
if (tmpbuf) PR_Free(tmpbuf);
if (output) PR_Free(output);
return NULL;
}
/* try to make sure that this is a fully qualified
* email address including a host and domain
*/
PUBLIC Bool
NET_IsFQDNMailAddress(const char * string)
{
/* first make sure that an @ exists
*/
char * at_sign = PL_strchr(string, '@');
if(at_sign)
{
/* make sure it has at least one period
*/
if(PL_strchr(at_sign, '.'))
return(TRUE);
}
return(FALSE);
}
static int use_ssl_for_imap4 = -1; /* -1 if uninitialized, 0 if FALSE, 1 if TRUE. */
/* fix Mac warning about missing prototype */
MODULE_PRIVATE int PR_CALLBACK net_use_ssl_for_imap4_changed_func(const char *pref,
void *data);
MODULE_PRIVATE int PR_CALLBACK net_use_ssl_for_imap4_changed_func(const char *pref,
void *data)
{
int status = PREF_NOERROR;
if (!PL_strcasecmp(pref,"mail.imap.server_ssl")) {
XP_Bool new_val;
status = PREF_GetBoolPref("mail.imap.server_ssl", &new_val);
use_ssl_for_imap4 = (int)new_val;
}
return status;
}
/* returns true if the URL is a secure URL address
*/
PUBLIC Bool
NET_IsURLSecure(char * address)
{
int type = NET_URL_Type (address);
TRACEMSG(("NET_IsURLSecure called, type: %d", type));
if(type == SECURE_HTTP_TYPE_URL
|| type == INTERNAL_IMAGE_TYPE_URL
|| type == SECURE_LDAP_TYPE_URL)
return(TRUE);
if(!PL_strncasecmp(address, "/mc-icons/", 10) ||
!PL_strncasecmp(address, "/ns-icons/", 10))
return(TRUE);
if(!PL_strncasecmp(address, "internal-external-reconnect:", 28))
return(TRUE);
if(!PL_strcasecmp(address, "internal-external-plugin"))
return(TRUE);
if(!PL_strncasecmp(address, "snews:", 6))
return TRUE;
/*
* IMAP URLs begin with "mailbox://" unlike POP URLs which begin
* with "mailbox:".
*/
if(!PL_strncasecmp(address, "mailbox://", 10)) {
if (use_ssl_for_imap4 < 0) { /* If uninitialized. */
XP_Bool new_val;
int status = PREF_GetBoolPref("mail.imap.server_ssl", &new_val);
if (status == PREF_NOERROR) {
use_ssl_for_imap4 = (int)new_val;
PREF_RegisterCallback("mail.imap.server_ssl",
net_use_ssl_for_imap4_changed_func, NULL);
}
else {
return FALSE;
}
}
return (Bool)use_ssl_for_imap4;
}
TRACEMSG(("NET_IsURLSecure: URL NOT SECURE"));
return(FALSE);
}
/* escapes all '<', '>' and '&' characters in a string
* returns a string that must be freed
*/
PUBLIC char *
NET_EscapeHTML(const char * string)
{
char *rv = (char *) PR_Malloc(PL_strlen(string)*4 + 1); /* The +1 is for
the trailing
null! */
char *ptr = rv;
if(rv)
{
for(; *string != '\0'; string++)
{
if(*string == '<')
{
*ptr++ = '&';
*ptr++ = 'l';
*ptr++ = 't';
*ptr++ = ';';
}
else if(*string == '>')
{
*ptr++ = '&';
*ptr++ = 'g';
*ptr++ = 't';
*ptr++ = ';';
}
else if(*string == '&')
{
*ptr++ = '&';
*ptr++ = 'a';
*ptr++ = 'm';
*ptr++ = 'p';
*ptr++ = ';';
}
else
{
*ptr++ = *string;
}
}
*ptr = '\0';
}
return(rv);
}
PUBLIC char *
NET_SpaceToPlus(char * string)
{
char * ptr = string;
if(!ptr)
return(NULL);
for(; *ptr != '\0'; ptr++)
if(*ptr == ' ')
*ptr = '+';
return(string);
}
/* returns true if the functions thinks the string contains
* HTML
*/
PUBLIC PRBool
NET_ContainsHTML(char * string, int32 length)
{
char * ptr = string;
register int32 count=length;
/* restrict searching to first K to limit false positives */
if(count > 1024)
count = 1024;
/* if the string begins with "#!" or "%!" then it's a script of some kind,
and it doesn't matter how many HTML tags that program references in its
source -- it ain't HTML. This false match happened all the time with,
for example, CGI scripts written in sh or perl that emit HTML. */
if (count > 2 &&
(string[0] == '#' || string[0] == '%') &&
string[1] == '!')
return FALSE;
/* If it begins with a mailbox delimiter, it's not HTML. */
if (count > 5 &&
(!PL_strncmp(string, "From ", 5) ||
!PL_strncmp(string, ">From ", 6)))
return FALSE;
for(; count > 0; ptr++, count--)
if(*ptr == '<')
{
if(count > 3 && !PL_strncasecmp(ptr+1, "HTML", 4))
return(TRUE);
if(count > 4 && !PL_strncasecmp(ptr+1, "TITLE", 5))
return(TRUE);
if(count > 3 && !PL_strncasecmp(ptr+1, "FRAMESET", 8))
return(TRUE);
if(count > 2 &&
toupper(*(ptr+1)) == 'H'
&& isdigit(*(ptr+2)) && *(ptr+3) == '>')
return(TRUE);
}
return(FALSE);
}
/* take a Layout generated LO_FormSubmitData_struct
* and use it to add post data to the URL Structure
* generated by CreateURLStruct
*
* DOES NOT Generate the URL Struct, it must be created prior to
* calling this function
*
* returns 0 on failure and 1 on success
*/
PUBLIC int
NET_AddLOSubmitDataToURLStruct(LO_FormSubmitData * sub_data,
URL_Struct * url_struct)
{
int32 i;
int32 total_size;
char *end, *tmp_ptr;
char *encoding;
char *target;
char **name_array;
char **value_array;
uint8 *type_array;
uint8 *encoding_array;
char *esc_string;
int32 len = 0;
if(!sub_data || !url_struct)
return(0);
if(sub_data->method == FORM_METHOD_GET)
url_struct->method = URL_GET_METHOD;
else
url_struct->method = URL_POST_METHOD;
if (!PL_strncasecmp(url_struct->address, "mailto:", 7)) {
url_struct->mailto_post = TRUE;
}
/* free any previous url_struct->post_data
*/
FREEIF(url_struct->post_data);
PA_LOCK(name_array, char**, sub_data->name_array);
PA_LOCK(value_array, char**, sub_data->value_array);
PA_LOCK(type_array, uint8*, sub_data->type_array);
PA_LOCK(encoding_array, uint8*, sub_data->encoding_array);
PA_LOCK(encoding, char*, sub_data->encoding);
/* free any previous target
*/
FREEIF(url_struct->window_target);
PA_LOCK(target, char*, sub_data->window_target);
if (target == NULL)
url_struct->window_target = NULL;
else
url_struct->window_target = PL_strdup (target);
FREEIF (url_struct->post_headers);
/* If we're posting to mailto, then generate the full complement
of mail headers; and allow the url to specify additional headers
as well. */
#ifdef MOZ_MAIL_NEWS
if (!PL_strncasecmp(url_struct->address, "mailto:", 7))
{
#ifdef MOZILLA_CLIENT
int status;
char *new_url = 0;
char *headers = 0;
status = MIME_GenerateMailtoFormPostHeaders (url_struct->address,
url_struct->referer,
&new_url, &headers);
if (status < 0)
{
FREEIF (new_url);
FREEIF (headers);
return status;
}
PR_ASSERT (new_url);
PR_ASSERT (headers);
url_struct->address_modified = TRUE;
PR_Free (url_struct->address);
url_struct->address = new_url;
url_struct->post_headers = headers;
#else
PR_ASSERT(0);
#endif /* MOZILLA_CLIENT */
}
#endif /* MOZ_MAIL_NEWS */
if(encoding && !PL_strcasecmp(encoding, "text/plain"))
{
char *tmpfilename;
char buffer[512];
XP_File fp;
/* always use post for this encoding type
*/
url_struct->method = URL_POST_METHOD;
/* write all the post data to a file first
* so that we can send really big stuff
*/
#ifdef XP_MAC /* This should really be for all platforms but I am fixing a bug for final release */
tmpfilename = WH_TempName (xpFileToPost, "nsform");
if (!tmpfilename) return 0;
fp = XP_FileOpen (tmpfilename, xpFileToPost, XP_FILE_WRITE_BIN);
#else
tmpfilename = WH_TempName (xpTemporary, "nsform");
if (!tmpfilename) return 0;
fp = XP_FileOpen (tmpfilename, xpTemporary, XP_FILE_WRITE_BIN);
#endif
if (!fp) {
PR_Free(tmpfilename);
return 0;
}
if (url_struct->post_headers)
{
len = XP_FileWrite(url_struct->post_headers,
PL_strlen (url_struct->post_headers),
fp);
PR_Free (url_struct->post_headers);
url_struct->post_headers = 0;
if (len < 0)
{
XP_FileClose(fp);
return 0;
}
}
PL_strcpy (buffer,
"Content-type: text/plain" CRLF
"Content-Disposition: inline; form-data" CRLF CRLF);
len = XP_FileWrite(buffer, PL_strlen(buffer), fp);
for(i=0; (len >= 0) && (i < sub_data->value_cnt); i++)
{
if(name_array[i])
XP_FileWrite(name_array[i], PL_strlen(name_array[i]), fp);
XP_FileWrite("=", 1, fp);
if(value_array[i])
XP_FileWrite(value_array[i], PL_strlen(value_array[i]), fp);
len = XP_FileWrite(CRLF, 2, fp);
}
XP_FileClose(fp);
StrAllocCopy(url_struct->post_data, tmpfilename);
PR_Free(tmpfilename);
if (len < 0)
return 0;
url_struct->post_data_is_file = TRUE;
}
else if(encoding && !PL_strcasecmp(encoding, "multipart/form-data"))
{
/* encoding using a variant of multipart/mixed
* and add files to it as well
*/
char *tmpfilename;
char separator[80];
char buffer[512];
XP_File fp;
int boundary_len;
int cont_disp_len;
NET_cinfo * ctype;
/* always use post for this encoding type
*/
url_struct->method = URL_POST_METHOD;
/* write all the post data to a file first
* so that we can send really big stuff
*/
tmpfilename = WH_TempName (xpFileToPost, "nsform");
if (!tmpfilename) return 0;
fp = XP_FileOpen (tmpfilename, xpFileToPost, XP_FILE_WRITE_BIN);
if (!fp) {
PR_Free(tmpfilename);
return 0;
}
sprintf(separator, "---------------------------%d%d%d",
rand(), rand(), rand());
if(url_struct->post_headers)
{
len = XP_FileWrite(url_struct->post_headers,
PL_strlen (url_struct->post_headers),
fp);
PR_Free (url_struct->post_headers);
url_struct->post_headers = 0;
if (len < 0)
return 0;
}
sprintf(buffer,
"Content-type: multipart/form-data;"
" boundary=%s" CRLF,
separator);
len = XP_FileWrite(buffer, PL_strlen(buffer), fp);
#define CONTENT_DISPOSITION "Content-Disposition: form-data; name=\""
#define PLUS_FILENAME "\"; filename=\""
#define CONTENT_TYPE_HEADER "Content-Type: "
#define CONTENT_ENCODING_HEADER "Content-Encoding: "
/* compute the content length */
total_size = -2; /* start at negative 2 to disregard the
* CRLF that act as a header separator
*/
boundary_len = PL_strlen(separator) + 6;
cont_disp_len = PL_strlen(CONTENT_DISPOSITION);
for(i=0; (len >= 0) && (i < sub_data->value_cnt); i++)
{
total_size += boundary_len;
/* The size of the content-disposition line is hard
* coded and must be modified any time you change
* the sprintf in the next for loop
*/
total_size += cont_disp_len;
if(name_array[i])
total_size += PL_strlen(name_array[i]);
total_size += 5; /* quote plus two CRLF's */
if(type_array[i] == FORM_TYPE_FILE)
{
XP_StatStruct stat_entry;
/* in this case we are going to send an extra
* ; filename="value_array[i]"
*/
total_size += PL_strlen(PLUS_FILENAME);
if(value_array[i])
{
/* only write the filename, not the whole path */
char * slash = PL_strrchr(value_array[i], '/');
if(slash)
slash++;
else
slash = value_array[i];
total_size += PL_strlen(slash);
#ifdef XP_MAC
if(encoding_array[i] == INPUT_TYPE_ENCODING_MACBIN)
{
/* add the size of the content type header */
/* NOTE - even though MacBinary is technically an encoding type we send
it as a content type and skip the normal routine of trying
to determine the actual file type
*/
total_size += sizeof(CONTENT_TYPE_HEADER)-1;
total_size += sizeof(APPLICATION_MACBINARY)-1;
total_size += 2; /* for the CRLF terminator */
}
else
#endif /* XP_MAC */
{
/* try and determine the content-type of the file
*/
ctype = NET_cinfo_find_type(value_array[i]);
if(!ctype->is_default)
{
/* we have determined it's type. Add enough
* space for it
*/
total_size += sizeof(CONTENT_TYPE_HEADER)-1;
total_size += PL_strlen(ctype->type);
total_size += 2; /* for the CRLF terminator */
}
}
}
#ifdef XP_MAC
if(encoding_array[i] == INPUT_TYPE_ENCODING_MACBIN)
{
/* figure out the size of the macbinary encoded file
* and add it to total_size
*/
if(value_array[i] && *value_array[i])
if(-1 != MB_Stat (value_array[i], &stat_entry, xpFileToPost))
total_size += stat_entry.st_size;
}
else
#endif /* XP_MAC */
{
/* if the type is a FILE type then we
* need to stat the file to get the size
*/
if(value_array[i] && *value_array[i])
if(-1 != XP_Stat (value_array[i], &stat_entry, xpFileToPost))
total_size += stat_entry.st_size;
/* if we can't stat the file just add zero */
}
}
else
{
if(value_array[i])
total_size += PL_strlen(value_array[i]);
}
}
/* add the size of the last separator plus
* two for the extra two dashes
*/
total_size += boundary_len+2;
sprintf(buffer, "Content-Length: %ld%s", total_size, CRLF);
len = XP_FileWrite(buffer, PL_strlen(buffer), fp);
for(i=0; (len >= 0) && (i < sub_data->value_cnt); i++)
{
sprintf(buffer, "%s--%s%s", CRLF, separator, CRLF);
XP_FileWrite(buffer, PL_strlen(buffer), fp);
/* WARNING!!! If you change the size of any of the
* sprintf's here you must change the size
* in the counting for loop above
*/
XP_FileWrite(CONTENT_DISPOSITION, cont_disp_len, fp);
if(name_array[i])
XP_FileWrite(name_array[i], PL_strlen(name_array[i]), fp);
if(type_array[i] == FORM_TYPE_FILE)
{
XP_FileWrite(PLUS_FILENAME, PL_strlen(PLUS_FILENAME), fp);
if(value_array[i])
{
/* only write the filename, not the whole path */
char * slash = PL_strrchr(value_array[i], '/');
if(slash)
slash++;
else
slash = value_array[i];
XP_FileWrite(slash, PL_strlen(slash), fp);
}
}
/* end the content disposition line */
len = XP_FileWrite("\"" CRLF, PL_strlen("\"" CRLF), fp);
if(type_array[i] == FORM_TYPE_FILE && value_array[i])
{
#ifdef XP_MAC
if(encoding_array[i] == INPUT_TYPE_ENCODING_MACBIN)
{
/* add the content_type header */
XP_FileWrite(CONTENT_TYPE_HEADER,
PL_strlen(CONTENT_TYPE_HEADER),
fp);
XP_FileWrite(APPLICATION_MACBINARY,
PL_strlen(APPLICATION_MACBINARY),
fp);
len = XP_FileWrite(CRLF, PL_strlen(CRLF), fp);
}
else
#endif /* XP_MAC */
{
/* try and determine the content-type of the file
*/
ctype = NET_cinfo_find_type(value_array[i]);
if(!ctype->is_default)
{
/* we have determined it's type. Send the
* content-type
*/
XP_FileWrite(CONTENT_TYPE_HEADER,
PL_strlen(CONTENT_TYPE_HEADER),
fp);
XP_FileWrite(ctype->type,
PL_strlen(ctype->type),
fp);
len = XP_FileWrite(CRLF, PL_strlen(CRLF), fp);
}
}
}
/* end the header */
len = XP_FileWrite(CRLF, PL_strlen(CRLF), fp);
/* send the value of the form field */
/* if the type is a FILE type, send the whole file,
* the filename is in the value field
*/
if(type_array[i] == FORM_TYPE_FILE)
{
#ifdef XP_MAC
if(encoding_array[i] == INPUT_TYPE_ENCODING_MACBIN)
{
MB_FileSpec mbFile;
int32 size;
OSErr theErr;
if (value_array[i] && *value_array[i])
{
theErr = MB_Open(value_array[i], &mbFile);
if (theErr == noErr)
{
while((size = MB_Read(NET_Socket_Buffer,
NET_Socket_Buffer_Size,
&mbFile)) != 0)
{
XP_FileWrite(NET_Socket_Buffer, size, fp);
}
MB_Close(&mbFile);
}
}
}
else
#endif /* XP_MAC */
{
XP_File ext_fp=0;
int32 size;
if(value_array[i] && *value_array[i])
ext_fp = XP_FileOpen(value_array[i],
xpFileToPost,
XP_FILE_READ_BIN);
if(ext_fp)
{
while((size = XP_FileRead(NET_Socket_Buffer,
NET_Socket_Buffer_Size,
ext_fp)) != 0)
{
XP_FileWrite(NET_Socket_Buffer, size, fp);
}
XP_FileClose(ext_fp);
}
}
}
else
{
if(value_array[i])
XP_FileWrite(value_array[i], PL_strlen(value_array[i]), fp);
}
}
sprintf(buffer, "%s--%s--%s", CRLF, separator, CRLF);
XP_FileWrite(buffer, PL_strlen(buffer), fp);
XP_FileClose(fp);
StrAllocCopy(url_struct->post_data, tmpfilename);
PR_Free(tmpfilename);
url_struct->post_data_is_file = TRUE;
}
else
{
total_size=1; /* start at one for the terminator char */
/* find out how much space we need total
*
* and also convert all spaces to pluses at the same time
*/
for(i=0; i<sub_data->value_cnt; i++)
{
total_size += NET_EscapedSize(name_array[i], URL_XPALPHAS);
total_size += NET_EscapedSize(value_array[i], URL_XPALPHAS);
total_size += 2; /* & = */
}
if(sub_data->method == FORM_METHOD_GET)
{
char *punc;
if(!url_struct->address)
return(0);
/* get rid of ? or # in the url string since we are adding it
*/
punc = PL_strchr(url_struct->address, '?');
if(punc)
*punc = '\0'; /* terminate here */
punc = PL_strchr(url_struct->address, '#');
if(punc)
*punc = '\0'; /* terminate here */
/* add the size of the url plus one for the '?'
*/
total_size += PL_strlen(url_struct->address)+1;
}
url_struct->post_data = (char *) PR_Malloc(total_size);
if(!url_struct->post_data)
{
PA_UNLOCK(sub_data->name_array);
PA_UNLOCK(sub_data->value_array);
PA_UNLOCK(sub_data->type_array);
PA_UNLOCK(sub_data->encoding);
return(0);
}
if(sub_data->method == FORM_METHOD_GET)
{
end = url_struct->post_data;
for(tmp_ptr = url_struct->address; *tmp_ptr != '\0'; tmp_ptr++)
*end++ = *tmp_ptr;
/* add the '?'
*/
*end++ = '?';
/* swap the post data and address data */
FREE(url_struct->address);
url_struct->address = url_struct->post_data;
url_struct->post_data = 0;
/* perform hack:
* To be compatible with old pre-form
* indexes, other web browsers had a hack
* wherein if a form was being submitted,
* and its method was get, and it
* had only one name/value pair, and the
* name of that pair was "isindex", then
* it would create the query url
*
* URL?value instead of URL?name=value
*/
if(sub_data->value_cnt == 1 && !PL_strcasecmp(name_array[0], "isindex"))
{
if(value_array && value_array[0])
PL_strcpy(end, NET_Escape(value_array[0], URL_XPALPHAS));
else
*end = '\0';
PA_UNLOCK(sub_data->name_array);
PA_UNLOCK(sub_data->value_array);
PA_UNLOCK(sub_data->type_array);
PA_UNLOCK(sub_data->encoding);
return(1);
}
/* the end ptr is still set to the correct address!
*/
}
else
{
StrAllocCat(url_struct->post_headers,
"Content-type: application/x-www-form-urlencoded" CRLF);
if(!url_struct->post_headers)
{
FREE_AND_CLEAR(url_struct->post_data);
PA_UNLOCK(sub_data->name_array);
PA_UNLOCK(sub_data->value_array);
PA_UNLOCK(sub_data->type_array);
PA_UNLOCK(sub_data->encoding);
return(0);
}
end = url_struct->post_data;
}
/* build the string
*/
for(i=0; i<sub_data->value_cnt; i++)
{
/* add the name
*/
esc_string = NET_Escape(name_array[i], URL_XPALPHAS);
if(esc_string)
{
for(tmp_ptr = esc_string; *tmp_ptr != '\0'; tmp_ptr++)
*end++ = *tmp_ptr;
PR_Free(esc_string);
}
/* join the name and value with a '='
*/
*end++ = '=';
/* add the value
*/
esc_string = NET_Escape(value_array[i], URL_XPALPHAS);
if(esc_string)
{
for(tmp_ptr = esc_string; *tmp_ptr != '\0'; tmp_ptr++)
*end++ = *tmp_ptr;
PR_Free(esc_string);
}
/* join pairs with a '&'
* make sure there is another pair before adding
*/
if(i+1 < sub_data->value_cnt)
*end++ = '&';
}
/* terminate the string
*/
*end = '\0';
if(sub_data->method == FORM_METHOD_POST)
{
char buffer[64];
url_struct->post_data_size = PL_strlen(url_struct->post_data);
sprintf(buffer,
"Content-length: %ld" CRLF,
url_struct->post_data_size);
StrAllocCat(url_struct->post_headers, buffer);
#ifdef ADD_EXTRA_CRLF_TO_POSTS
/* munge for broken CGIs. Add an extra CRLF to the
* post data.
*/
BlockAllocCat(url_struct->post_data,
url_struct->post_data_size,
CRLF,
3);
/* don't add the eol terminator */
url_struct->post_data_size += 2;
#endif /* ADD_EXTRA_CRLF_TO_POSTS */
}
}
PA_UNLOCK(sub_data->name_array);
PA_UNLOCK(sub_data->value_array);
PA_UNLOCK(sub_data->type_array);
PA_UNLOCK(sub_data->encoding);
return(1); /* success */
}
PUBLIC int
NET_AddCoordinatesToURLStruct(URL_Struct * url_struct, int32 x_coord, int32 y_coord)
{
if(url_struct->address)
{
char buffer[32];
sprintf(buffer, "?%ld,%ld", x_coord, y_coord);
/* get rid of ? or # in the url string since we are adding it
*/
#undef STRIP_SEARCH_DATA_FROM_ISMAP_URLS
#ifdef STRIP_SEARCH_DATA_FROM_ISMAP_URLS
{
char *punc;
punc = PL_strchr(url_struct->address, '?');
if(punc)
*punc = '\0'; /* terminate here */
punc = PL_strchr(url_struct->address, '#');
if(punc)
*punc = '\0'; /* terminate here */
}
#endif /* STRIP_SEARCH_DATA_FROM_ISMAP_URLS */
StrAllocCat(url_struct->address, buffer);
}
return(1); /* success */
}
/* FREE_AND_CLEAR will free a pointer if it is non-zero and
* then set it to zero
*/
MODULE_PRIVATE void
NET_f_a_c (char **pointer)
{
if(*pointer) {
PR_Free(*pointer);
*pointer = 0;
}
}
/* recognizes URLs and their types. Returns 0 (zero) if
* it is unrecognized.
*/
PUBLIC int
NET_URL_Type (CONST char *URL)
{
/* Protect from SEGV */
if (!URL || (URL && *URL == '\0'))
return(0);
switch(*URL) {
case 'a':
case 'A':
if(!PL_strncasecmp(URL,"about:security", 14))
return(SECURITY_TYPE_URL);
else if(!PL_strncasecmp(URL,"about:",6))
return(ABOUT_TYPE_URL);
else if(!PL_strncasecmp(URL,"addbook:",8))
return(ADDRESS_BOOK_TYPE_URL);
else if (!PL_strncasecmp(URL, "addbook-ldap", 12)) /*no colon includes addbook-ldaps:*/
return(ADDRESS_BOOK_LDAP_TYPE_URL);
break;
case 'd':
case 'D':
if(!PL_strncasecmp(URL,"data:",5))
return(DATA_TYPE_URL);
break;
case 'c':
case 'C':
if(!PL_strncasecmp(URL,"castanet:",9))
return(0);
break;
case 'f':
case 'F':
if(!PL_strncasecmp(URL,"ftp:",4))
return(FTP_TYPE_URL);
else if(!PL_strncasecmp(URL,"file:",5))
return(FILE_TYPE_URL);
break;
case 'g':
case 'G':
if(!PL_strncasecmp(URL,"gopher:",7))
return(GOPHER_TYPE_URL);
break;
case 'h':
case 'H':
if(!PL_strncasecmp(URL,"http:",5))
return(HTTP_TYPE_URL);
else if(!PL_strncasecmp(URL,"https:",6))
return(SECURE_HTTP_TYPE_URL);
break;
case 'i':
case 'I':
if(!PL_strncasecmp(URL,"internal-gopher-",16))
return(INTERNAL_IMAGE_TYPE_URL);
else if(!PL_strncasecmp(URL,"internal-news-",14))
return(INTERNAL_IMAGE_TYPE_URL);
else if(!PL_strncasecmp(URL,"internal-edit-",14))
return(INTERNAL_IMAGE_TYPE_URL);
else if(!PL_strncasecmp(URL,"internal-attachment-",20))
return(INTERNAL_IMAGE_TYPE_URL);
else if(!PL_strncasecmp(URL,"internal-sa-",12))
return(INTERNAL_IMAGE_TYPE_URL);
else if(!PL_strncasecmp(URL,"internal-smime-",15))
return(INTERNAL_IMAGE_TYPE_URL);
else if(!PL_strncasecmp(URL,"internal-dialog-handler",23))
return(HTML_DIALOG_HANDLER_TYPE_URL);
else if(!PL_strncasecmp(URL,"internal-panel-handler",22))
return(HTML_PANEL_HANDLER_TYPE_URL);
else if(!PL_strncasecmp(URL,"internal-security-",18))
return(INTERNAL_SECLIB_TYPE_URL);
else if(!PL_strncasecmp(URL,"internal-certldap",17))
return(INTERNAL_CERTLDAP_TYPE_URL);
else if(!PL_strncasecmp(URL,"IMAP:",5))
return(IMAP_TYPE_URL);
break;
case 'j':
case 'J':
if(!PL_strncasecmp(URL, "javascript:",11))
return(MOCHA_TYPE_URL);
break;
case 'l':
case 'L':
if(!PL_strncasecmp(URL, "livescript:",11))
return(MOCHA_TYPE_URL);
else if (!PL_strncasecmp(URL, "ldap:",5))
return(LDAP_TYPE_URL);
else if (!PL_strncasecmp(URL, "ldaps:",6))
return(SECURE_LDAP_TYPE_URL);
break;
case 'm':
case 'M':
if(!PL_strncasecmp(URL,"mailto:",7))
return(MAILTO_TYPE_URL);
else if(!PL_strncasecmp(URL,"mailbox:",8))
return(MAILBOX_TYPE_URL);
else if(!PL_strncasecmp(URL, "mocha:",6))
return(MOCHA_TYPE_URL);
break;
case 'n':
case 'N':
if(!PL_strncasecmp(URL,"news:",5))
return(NEWS_TYPE_URL);
else if(!PL_strncasecmp(URL,"nfs:",4))
return(NFS_TYPE_URL);
else if(!PL_strncasecmp(URL, NETHELP_URL_PREFIX, sizeof(NETHELP_URL_PREFIX)-1))
return(NETHELP_TYPE_URL);
break;
case 'p':
case 'P':
if(!PL_strncasecmp(URL,"pop3:",5))
return(POP3_TYPE_URL);
break;
case 'r':
case 'R':
if(!PL_strncasecmp(URL,"rlogin:",7))
return(RLOGIN_TYPE_URL);
break;
case 's':
case 'S':
if(!PL_strncasecmp(URL,"snews:",6))
return(NEWS_TYPE_URL);
else if (!PL_strncasecmp(URL,"search-libmsg:",14))
return(MSG_SEARCH_TYPE_URL);
case 't':
case 'T':
if(!PL_strncasecmp(URL,"telnet:",7))
return(TELNET_TYPE_URL);
else if(!PL_strncasecmp(URL,"tn3270:",7))
return(TN3270_TYPE_URL);
break;
case 'u':
case 'U':
if(!PL_strncasecmp(URL,"URN:",4))
return(URN_TYPE_URL);
break;
case 'v':
case 'V':
if(!PL_strncasecmp(URL, VIEW_SOURCE_URL_PREFIX,
sizeof(VIEW_SOURCE_URL_PREFIX)-1))
return(VIEW_SOURCE_TYPE_URL);
break;
case 'w':
case 'W':
if(!PL_strncasecmp(URL,"wais:",5))
return(WAIS_TYPE_URL);
if(!PL_strncasecmp(URL,"wysiwyg:",8))
return(WYSIWYG_TYPE_URL);
break;
}
/* no type match :( */
return(0);
}
PUBLIC void
NET_PlusToSpace(char *str)
{
for (; *str != '\0'; str++)
if (*str == '+')
*str = ' ';
}
/* if the url is a local file this function returns
* the portion of the url that represents the
* file path as a malloc'd string. If the
* url is not a local file url this function
* returns NULL
*/
PRIVATE char *
net_return_local_file_part_from_url(char *address)
{
char *host;
#ifdef XP_WIN
char *new_address;
char *rv;
#endif
if (address == NULL)
return NULL;
/* imap urls are never local */
if (!PL_strncasecmp(address,"mailbox://",10))
return NULL;
/* mailbox url's are always local, but don't always point to a file */
if(!PL_strncasecmp(address, "mailbox:", 8))
{
char *filename = NET_ParseURL(address, GET_PATH_PART);
if (!filename)
filename = PL_strdup("");
return filename;
}
if(PL_strncasecmp(address, "file:", 5))
return(NULL);
host = NET_ParseURL(address, GET_HOST_PART);
if(!host || *host == '\0' || !PL_strcasecmp(host, "LOCALHOST"))
{
PR_FREEIF(host);
return(NET_UnEscape(NET_ParseURL(address, GET_PATH_PART)));
}
#ifdef XP_WIN
/* get the address minus the search and hash data */
new_address = NET_ParseURL(address,
GET_PROTOCOL_PART | GET_HOST_PART | GET_PATH_PART);
NET_UnEscape(new_address);
/* check for local drives as hostnames
*/
if(PL_strlen(host) == 2
&& isalpha(host[0])
&& (host[1] == '|' || host[1] == ':'))
{
PR_Free(host);
/* skip "file:/" */
rv = PL_strdup(new_address+6);
PR_Free(new_address);
return(rv);
}
if(1)
{
XP_StatStruct stat_entry;
/* try stating the url just in case since
* the local file system can
* have url's of the form \\prydain\dist
*/
if(-1 != XP_Stat(address+5, &stat_entry, xpURL))
{
PR_Free(host);
/* skip "file:" */
rv = PL_strdup(address+5);
PR_Free(new_address);
return(rv);
}
}
#endif /* XP_WIN */
PR_Free(host);
return(NULL);
}
/* returns TRUE if the url is a local file
* url
*/
PUBLIC XP_Bool
NET_IsLocalFileURL(char *address)
{
char * cp = net_return_local_file_part_from_url(address);
if(cp)
{
PR_Free(cp);
return(TRUE);
}
else
{
return(FALSE);
}
}
static int
net_buffer_output_fn ( const char *buf, int32 size, void *closure)
{
BufferStruct *bs = (BufferStruct *) closure;
/* if the size greater or equal to the available buffer size
* reallocate the buffer
*/
PR_ASSERT (buf && bs && size > 0);
if ( !buf || !bs || size <= 0 )
return -1;
if (size >= bs->size - bs->pos)
{
int32 len;
char *newBuffer;
len = (bs->size << 1) - bs->pos + size + 1; /* null terminated */
if (bs->buffer)
newBuffer = PR_Realloc (bs->buffer, len);
else
newBuffer = PR_Malloc(len);
if (!newBuffer)
return MK_OUT_OF_MEMORY;
memset(newBuffer+bs->pos, 0, len-bs->pos);
bs->size = len;
bs->buffer = newBuffer;
}
memcpy (bs->buffer+bs->pos, buf, size);
bs->pos += size;
return 0;
}
PRIVATE int32
net_URNProtoLoad(ActiveEntry *ce)
{
char buffer[256];
PL_strcpy(buffer, XP_GetString(XP_ALERT_URN_USEHTTP));
PL_strncat(buffer, ce->URL_s->address, 150);
buffer[255] = '\0'; /* in case strncat doesn't add one */
FE_Alert(ce->window_id, buffer);
return -1;
}
PRIVATE int32
net_URNProtoStub(ActiveEntry *ce)
{
PR_ASSERT(0);
return -1;
}
PRIVATE void
net_URNProtoCleanupStub(void)
{
}
/* a stub function for URN protocol converter.
* right now we only proxy URN's
* if URN's ever get worked on move this to another file
*/
PUBLIC void
NET_InitURNProtocol(void)
{
static NET_ProtoImpl urn_proto_impl;
urn_proto_impl.init = net_URNProtoLoad;
urn_proto_impl.process = net_URNProtoStub;
urn_proto_impl.interrupt = net_URNProtoStub;
urn_proto_impl.cleanup = net_URNProtoCleanupStub;
NET_RegisterProtocolImplementation(&urn_proto_impl, URN_TYPE_URL);
}
PRIVATE int32
net_NFSProtoLoad(ActiveEntry *ce)
{
char buffer[256];
PL_strcpy(buffer, XP_GetString(XP_ALERT_NFS_USEHTTP));
PL_strncat(buffer, ce->URL_s->address, 150);
buffer[255] = '\0'; /* in case strncat doesn't add one */
FE_Alert(ce->window_id, buffer);
return -1;
}
PRIVATE int32
net_NFSProtoStub(ActiveEntry *ce)
{
PR_ASSERT(0);
return -1;
}
PRIVATE void
net_NFSProtoCleanupStub(void)
{
}
/* a stub function for NFS protocol converter.
* right now we only proxy NFS's
* if NFS's ever get worked on move this to another file
*/
PUBLIC void
NET_InitNFSProtocol(void)
{
static NET_ProtoImpl nfs_proto_impl;
nfs_proto_impl.init = net_NFSProtoLoad;
nfs_proto_impl.process = net_NFSProtoStub;
nfs_proto_impl.interrupt = net_NFSProtoStub;
nfs_proto_impl.cleanup = net_NFSProtoCleanupStub;
NET_RegisterProtocolImplementation(&nfs_proto_impl, NFS_TYPE_URL);
}
PRIVATE int32
net_WAISProtoLoad(ActiveEntry *ce)
{
char * alert = NET_ExplainErrorDetails(MK_NO_WAIS_PROXY);
FE_Alert(ce->window_id, alert);
FREE(alert);
return -1;
}
PRIVATE int32
net_WAISProtoStub(ActiveEntry *ce)
{
PR_ASSERT(0);
return -1;
}
PRIVATE void
net_WAISProtoCleanupStub(void)
{
}
/* a stub function for WAIS protocol converter.
* right now we only proxy WAIS's
* if WAIS's ever get worked on move this to another file
*/
PUBLIC void
NET_InitWAISProtocol(void)
{
static NET_ProtoImpl wais_proto_impl;
wais_proto_impl.init = net_WAISProtoLoad;
wais_proto_impl.process = net_WAISProtoStub;
wais_proto_impl.interrupt = net_WAISProtoStub;
wais_proto_impl.cleanup = net_WAISProtoCleanupStub;
NET_RegisterProtocolImplementation(&wais_proto_impl, WAIS_TYPE_URL);
}
#ifdef MOZ_MAIL_NEWS
PUBLIC char *
NET_Base64Encode (char *src, int32 srclen)
{
BufferStruct bs;
MimeEncoderData *encoder_data = NULL;
PR_ASSERT (src);
if (!src)
return NULL;
else if (srclen == 0)
return PL_strdup("");
memset (&bs, 0, sizeof (BufferStruct));
encoder_data = MimeB64EncoderInit(net_buffer_output_fn, (void *) &bs);
if (!encoder_data)
return NULL;
if (MimeEncoderWrite(encoder_data, src, srclen) < 0)
{
MimeEncoderDestroy(encoder_data, FALSE);
PR_FREEIF(bs.buffer);
return NULL;
}
MimeEncoderDestroy(encoder_data, FALSE);
/* caller must free the returned pointer to prevent
* memory leak problem.
*/
return bs.buffer;
}
PUBLIC char *
NET_Base64Decode (char *src,
int32 srclen)
{
BufferStruct bs;
MimeDecoderData *decoder_data = NULL;
PR_ASSERT (src);
if (!src)
return NULL;
else if (srclen == 0)
return PL_strdup("");
memset (&bs, 0, sizeof (BufferStruct));
decoder_data = MimeB64DecoderInit(net_buffer_output_fn, (void *) &bs);
if (!decoder_data)
return NULL;
if (MimeDecoderWrite(decoder_data, src, srclen) < 0)
{
MimeDecoderDestroy(decoder_data, FALSE);
PR_FREEIF(bs.buffer);
return NULL;
}
MimeDecoderDestroy(decoder_data, FALSE);
/* caller must free the returned pointer to prevent
* memory leak problem.
*/
return bs.buffer;
}
#endif /* MOZ_MAIL_NEWS */
/* A utility function to fetch a file from cache right away,
* and update it (from the original server) after its used.
* Used in/Required by Mr. R.D.F. Guha
*
* Note that if the item is not in the cache, this defaults
* to retrieving it from the server.
*
* Variables and returns : see NET_GetURL in include/net.h
*
* Warning: Possibility of a stale copy!
*
*/
PUBLIC int
NET_GetURLQuick (URL_Struct * URL_s,
FO_Present_Types output_format,
MWContext * context,
Net_GetUrlExitFunc* exit_routine)
{
if (!NET_FindURLInMemCache(URL_s, context) &&
!NET_FindURLInExtCache(URL_s, context))
{
/* default */
return NET_GetURL(
URL_s,
output_format,
context,
exit_routine);
}
else
{
/* Only from cache */
int status =
NET_GetURL(
URL_s,
FO_ONLY_FROM_CACHE || output_format,
context,
exit_routine);
/* Update request */
NET_GetURL(
URL_s,
FO_CACHE_ONLY,
context,
NULL);
return status;
}
}