mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 06:11:37 +00:00
3372 lines
86 KiB
C
3372 lines
86 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.
|
|
*/
|
|
/*
|
|
*
|
|
* 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 "mknews.h"
|
|
#include "mkparse.h"
|
|
#include "mkaccess.h"
|
|
#include "libi18n.h"
|
|
#include "msgcom.h"
|
|
#include "mkcache.h"
|
|
#include "mkextcac.h"
|
|
#include "mime.h"
|
|
#include "secrng.h"
|
|
#include "ssl.h"
|
|
#include "prefapi.h"
|
|
#include "secnav.h"
|
|
#include "preenc.h"
|
|
#include "mkselect.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
|
|
|
|
XP_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 = XP_STRLEN(local_dir_name)-1;
|
|
have_slash = (local_dir_name[end] == '/') || (local_dir_name[end] == '\\');
|
|
|
|
files_to_post = (char**) XP_ALLOC(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(!XP_STRCMP(dir_entry->d_name, ".") || !XP_STRCMP(dir_entry->d_name, ".."))
|
|
continue;
|
|
|
|
/* assemble full pathname first so we can test if its a directory */
|
|
file_to_post = XP_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) )
|
|
{
|
|
XP_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**) XP_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++] = XP_STRDUP(file_to_post);
|
|
|
|
XP_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;
|
|
|
|
XP_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) {
|
|
XP_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 = XP_STRDUP(username);
|
|
if (password)
|
|
URL_s->password = XP_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 ) XP_FREE(*full_location);
|
|
|
|
iSize = strlen(location) + 4;
|
|
if( user_name ) iSize += strlen(user_name);
|
|
if( password ) iSize += strlen(password);
|
|
|
|
*full_location = (char*)XP_ALLOC(iSize);
|
|
if( !*full_location ){
|
|
/* Return an empty string */
|
|
*full_location = strdup("");
|
|
return FALSE;
|
|
}
|
|
**full_location = '\0';
|
|
|
|
/* Find start just past http:// or ftp:// */
|
|
start = XP_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 = XP_STRCHR(start, '@');
|
|
if( at_ptr ){
|
|
start = at_ptr + 1;
|
|
}
|
|
/* Append supplied "user:password@"
|
|
* (This can be used without password)
|
|
*/
|
|
if( user_name && XP_STRLEN(user_name) > 0 ){
|
|
XP_STRCAT(*full_location, user_name);
|
|
if ( password && XP_STRLEN(password) > 0 ){
|
|
XP_STRCAT(*full_location,":");
|
|
XP_STRCAT(*full_location, password);
|
|
}
|
|
XP_STRCAT(*full_location, "@");
|
|
}
|
|
/* Append the rest of location */
|
|
XP_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) XP_FREE(*location);
|
|
if( user_name && *user_name) XP_FREE(*user_name);
|
|
if( password && *password) XP_FREE(*password);
|
|
|
|
/* Find start just past http:// or ftp:// */
|
|
start = XP_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 = XP_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 = XP_STRCHR(start, '@');
|
|
colon_ptr = XP_STRCHR(start, ':');
|
|
|
|
if( at_ptr ){
|
|
/* save character */
|
|
at = *at_ptr;
|
|
|
|
/* Copy part past the @ over previously-copied full string */
|
|
XP_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 = XP_STRDUP((colon_ptr+1));
|
|
}
|
|
/* terminate for the user string */
|
|
*colon_ptr = '\0';
|
|
} else if( password ) {
|
|
*password = XP_STRDUP("");
|
|
}
|
|
if( user_name ){
|
|
*user_name = XP_STRDUP(start);
|
|
}
|
|
/* restore characters */
|
|
*at_ptr = at;
|
|
if( colon_ptr ) *colon_ptr = colon;
|
|
|
|
} else {
|
|
/* Supply empty strings for these */
|
|
if( user_name ){
|
|
*user_name = XP_STRDUP("");
|
|
}
|
|
if( password ){
|
|
*password = XP_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 *) XP_ALLOC((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 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[XP_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)
|
|
{
|
|
XP_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,
|
|
XP_Bool add_crlf_to_line_endings)
|
|
{
|
|
struct WritePostDataData *data_obj = (struct WritePostDataData *)
|
|
*write_post_data_data;
|
|
|
|
/* init the data object */
|
|
if(!data_obj)
|
|
{
|
|
data_obj = XP_NEW(struct WritePostDataData);
|
|
*write_post_data_data = data_obj;
|
|
|
|
if(!data_obj)
|
|
return(MK_OUT_OF_MEMORY);
|
|
|
|
XP_MEMSET(data_obj, 0, sizeof(struct WritePostDataData));
|
|
|
|
data_obj->last_line_was_complete = TRUE;
|
|
data_obj->buffer = (char *) XP_ALLOC(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 = XP_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 = XP_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);
|
|
*/
|
|
XP_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, XP_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, XP_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 */
|
|
XP_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, XP_Bool is_http)
|
|
{
|
|
char *first_arg, *next_arg;
|
|
|
|
if(URL_s->preset_content_type)
|
|
return;
|
|
|
|
first_arg = XP_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 = XP_STRTOK(NULL, ";")) != NULL)
|
|
{
|
|
next_arg = XP_StripLine(next_arg);
|
|
|
|
if(!strncasecomp(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
|
|
XP_ASSERT(0);
|
|
#endif /* MOZILLA_CLIENT */
|
|
}
|
|
else if(!strncasecomp(next_arg,"BOUNDARY=", 9))
|
|
{
|
|
StrAllocCopy(URL_s->boundary, NET_RemoveQuotes(next_arg+9));
|
|
TRACEMSG(("Found boundary: %s", URL_s->boundary));
|
|
}
|
|
else if(!strncasecomp(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 = XP_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(!strncasecomp(name,"ACCEPT-RANGES:",14)
|
|
|| !strncasecomp(name, "ALLOW-RANGES:",13))
|
|
{
|
|
char * next_arg = XP_STRTOK(value, ";");
|
|
|
|
found_one = TRUE;
|
|
|
|
while(next_arg)
|
|
{
|
|
next_arg = XP_StripLine(next_arg);
|
|
|
|
if(!strncasecomp(next_arg,"BYTES", 5))
|
|
{
|
|
TRACEMSG(("Document Allows for BYTERANGES!"));
|
|
URL_s->server_can_do_byteranges = TRUE;
|
|
}
|
|
|
|
next_arg = XP_STRTOK(NULL, ";");
|
|
}
|
|
}
|
|
else if(!strncasecomp(name, "AGE:",4))
|
|
{
|
|
long age;
|
|
|
|
XP_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 (!strncasecomp(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 (!strncasecomp(control, "NO-CACHE", 8))
|
|
{
|
|
/* same as pragma=no-cache */
|
|
URL_s->dont_cache = TRUE;
|
|
}
|
|
else if (!strncasecomp(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(XP_STRTOK(control+8,";"));
|
|
}
|
|
}
|
|
#if 0 /* Unimplemented */
|
|
else if (!strncasecomp(control, "PUBLIC", 6))
|
|
{
|
|
/* Place holder for shared cache concepts */
|
|
}
|
|
else if (!strncasecomp(control, "PRIVATE", 7))
|
|
{
|
|
/* Place holder for shared cache concepts */
|
|
}
|
|
else if (!strncasecomp(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 (!strncasecomp(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 (!strncasecomp(control, "PROXY-REVALIDATE", 16))
|
|
{
|
|
/* Same as must-revalidate except that it does not apply to
|
|
non-shared user agents (like us) */
|
|
}
|
|
else if (!strncasecomp(control, "S-MAXAGE", 8))
|
|
{
|
|
/* Applicable for shared caches. Place holder. */
|
|
}
|
|
else
|
|
{
|
|
/* A new cache extension */
|
|
}
|
|
#endif /* Unimplemented */
|
|
}
|
|
else if(!strncasecomp(name,"CONTENT-DISPOSITION:",20))
|
|
{
|
|
char *next_arg;
|
|
|
|
found_one = TRUE;
|
|
|
|
next_arg = XP_STRTOK(value, ";");
|
|
|
|
while(next_arg)
|
|
{
|
|
next_arg = XP_StripLine(next_arg);
|
|
|
|
if(!strncasecomp(next_arg,"filename=", 9))
|
|
{
|
|
StrAllocCopy(URL_s->content_name,
|
|
NET_RemoveQuotes(next_arg+9));
|
|
}
|
|
next_arg = XP_STRTOK(NULL, ";");
|
|
}
|
|
|
|
}
|
|
else if(!strncasecomp(name,"CONTENT-TYPE:",13))
|
|
{
|
|
found_one = TRUE;
|
|
NET_ParseContentTypeHeader(context, value, URL_s, is_http);
|
|
}
|
|
else if(!strncasecomp(name,"CONTENT-LENGTH:",15))
|
|
{
|
|
found_one = TRUE;
|
|
|
|
XP_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(!strncasecomp(name,"CONTENT-ENCODING:",17))
|
|
{
|
|
found_one = TRUE;
|
|
|
|
XP_STRTOK(value, ";"); /* terminate at ';' */
|
|
StrAllocCopy(URL_s->content_encoding, value);
|
|
}
|
|
else if(!strncasecomp(name,"CONTENT-RANGE:",14))
|
|
{
|
|
unsigned long low, high, length;
|
|
XP_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(!strncasecomp(name,"CONNECTION:",11))
|
|
{
|
|
XP_STRTOK(value, ";"); /* terminate at ';' */
|
|
XP_STRTOK(value, ","); /* terminate at ',' */
|
|
if(!strcasecomp("KEEP-ALIVE", XP_StripLine(value)))
|
|
URL_s->can_reuse_connection = TRUE;
|
|
if(!strcasecomp("CLOSE", XP_StripLine(value)))
|
|
URL_s->can_reuse_connection = FALSE;
|
|
}
|
|
break;
|
|
|
|
case 'D':
|
|
if(!strncasecomp(name,"DATE:",5))
|
|
{
|
|
found_one = TRUE;
|
|
URL_s->server_date = NET_ParseDate(value);
|
|
}
|
|
#if 0
|
|
else if(!strncasecomp(name,"DEST-IP:",8))
|
|
{
|
|
found_one = TRUE;
|
|
URL_s->destIP = XP_STRDUP(value);
|
|
}
|
|
#endif
|
|
break;
|
|
case 'E':
|
|
if(!strncasecomp(name,"EXPIRES:",8))
|
|
{
|
|
char *cp, *expires = NET_RemoveQuotes(value);
|
|
Bool is_number=TRUE;
|
|
|
|
/* Expires: 123 - number of seconds
|
|
* Expires: date - absolute date
|
|
*/
|
|
|
|
XP_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(!strncasecomp(name,"EXT-CACHE:",10))
|
|
{
|
|
#ifdef MOZILLA_CLIENT
|
|
char * next_arg = XP_STRTOK(value, ";");
|
|
char * name=0;
|
|
char * instructions=0;
|
|
|
|
found_one = TRUE;
|
|
|
|
while(next_arg)
|
|
{
|
|
next_arg = XP_StripLine(next_arg);
|
|
|
|
if(!strncasecomp(next_arg,"name=", 5))
|
|
{
|
|
TRACEMSG(("Found external cache name: %s", next_arg+5));
|
|
name = NET_RemoveQuotes(next_arg+5);
|
|
}
|
|
else if(!strncasecomp(next_arg,"instructions=", 13))
|
|
{
|
|
TRACEMSG(("Found external cache instructions: %s",
|
|
next_arg+13));
|
|
instructions = NET_RemoveQuotes(next_arg+13);
|
|
}
|
|
|
|
next_arg = XP_STRTOK(NULL, ";");
|
|
}
|
|
|
|
if(name)
|
|
NET_OpenExtCacheFAT(context, name, instructions);
|
|
#else
|
|
XP_ASSERT(0);
|
|
#endif /* MOZILLA_CLIENT */
|
|
}
|
|
else if (!strncasecomp(name, "ETAG:",5))
|
|
{
|
|
/* Weak Validators are skipped for now*/
|
|
char* etag;
|
|
if (!strncasecomp(value, "W/", 2))
|
|
etag = NET_RemoveQuotes(value+2);
|
|
else
|
|
etag = NET_RemoveQuotes(value);
|
|
|
|
StrAllocCopy(URL_s->etag,etag);
|
|
}
|
|
break;
|
|
|
|
case 'L':
|
|
if(!strncasecomp(name,"LOCATION:",9))
|
|
{
|
|
found_one = TRUE;
|
|
|
|
/* don't do this here because a url can
|
|
* contain a ';'
|
|
* XP_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(!strncasecomp(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(!strncasecomp(name,"LINK:",5))
|
|
{
|
|
#define PAGE_SERVICES_REL "pageServices"
|
|
char * next_arg = XP_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[XP_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(!strncasecomp(next_arg,"rel=", 4))
|
|
{
|
|
char * rel = NET_RemoveQuotes(next_arg+4);
|
|
|
|
if(!strcasecomp(rel, PAGE_SERVICES_REL))
|
|
rel_type = PAGE_SERVICES_REL_TYPE;
|
|
}
|
|
|
|
next_arg = XP_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
|
|
XP_FREEIF(link_val);
|
|
|
|
}
|
|
break;
|
|
|
|
case 'P':
|
|
if(!strncasecomp(name,"PROXY-AUTHENTICATE:",19))
|
|
{
|
|
char *auth = value;
|
|
|
|
found_one = TRUE;
|
|
|
|
XP_STRTOK(value, ";"); /* terminate at ';' */
|
|
|
|
if (net_IsBetterAuth(auth, URL_s->proxy_authenticate))
|
|
StrAllocCopy(URL_s->proxy_authenticate, auth);
|
|
}
|
|
else if(!strncasecomp(name,"PROXY-CONNECTION:",17))
|
|
{
|
|
XP_STRTOK(value, ";"); /* terminate at ';' */
|
|
XP_STRTOK(value, ","); /* terminate at ',' */
|
|
if(!strcasecomp("KEEP-ALIVE", XP_StripLine(value)))
|
|
URL_s->can_reuse_connection = TRUE;
|
|
}
|
|
else if(!strncasecomp(name,"PRAGMA:",7))
|
|
{
|
|
found_one = TRUE;
|
|
|
|
XP_STRTOK(value, ";"); /* terminate at ';' */
|
|
|
|
if(!strcasecomp(value, "NO-CACHE"))
|
|
URL_s->dont_cache = TRUE;
|
|
}
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
if(!strncasecomp(name,"RANGE:",6))
|
|
{
|
|
unsigned long low, high, length;
|
|
XP_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(!strncasecomp(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 = XP_STRTOK(value, ";");
|
|
|
|
URL_s->refresh = atol(value);
|
|
TRACEMSG(("Found refresh header: %d",URL_s->refresh));
|
|
|
|
/* assign and compare
|
|
*/
|
|
while((next_arg = XP_STRTOK(NULL, ";")) != NULL)
|
|
{
|
|
next_arg = XP_StripLine(next_arg);
|
|
|
|
|
|
if(!strncasecomp(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(!strncasecomp(name,"SET-COOKIE:",11))
|
|
{
|
|
found_one = TRUE;
|
|
NET_SetCookieStringFromHttp(outputFormat, URL_s, context, URL_s->address, value);
|
|
}
|
|
else if(!strncasecomp(name, "SERVER:", 7))
|
|
{
|
|
found_one = TRUE;
|
|
|
|
if(strcasestr(value, "NETSITE"))
|
|
URL_s->is_netsite = TRUE;
|
|
else if(strcasestr(value, "NETSCAPE"))
|
|
URL_s->is_netsite = TRUE;
|
|
}
|
|
break;
|
|
|
|
case 'T':
|
|
if(!strncasecomp(name,"TRANSFER-ENCODING:",18))
|
|
{
|
|
found_one = TRUE;
|
|
|
|
XP_STRTOK(value, ";"); /* terminate at ';' */
|
|
StrAllocCopy(URL_s->transfer_encoding, value);
|
|
}
|
|
|
|
case 'W':
|
|
if(!strncasecomp(name,"WWW-AUTHENTICATE:",17))
|
|
{
|
|
found_one = TRUE;
|
|
|
|
XP_STRTOK(value, ";"); /* terminate at ';' */
|
|
|
|
StrAllocCopy(URL_s->authenticate, value);
|
|
}
|
|
else if(!strncasecomp(name, "WWW-PROTECTION-TEMPLATE:", 24))
|
|
{
|
|
found_one = TRUE;
|
|
|
|
XP_STRTOK(value, ";"); /* terminate at ';' */
|
|
|
|
StrAllocCopy(URL_s->protection_template, value);
|
|
}
|
|
else if(!strncasecomp(name, "WINDOW-TARGET:", 14))
|
|
{
|
|
found_one = TRUE;
|
|
|
|
XP_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:
|
|
XP_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:
|
|
XP_ASSERT(0);
|
|
cite_open2 = cite_close2 = "";
|
|
break;
|
|
}
|
|
|
|
#else
|
|
XP_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 == '>' &&
|
|
!XP_STRNCMP (input, ">From ", 6)) /* sendmail... */
|
|
;
|
|
else if (*s == '>' || *s == ']')
|
|
{
|
|
line_is_citation = TRUE;
|
|
XP_STRCPY(output_ptr, cite_open1);
|
|
output_ptr += XP_STRLEN(cite_open1);
|
|
XP_STRCPY(output_ptr, cite_open2);
|
|
output_ptr += XP_STRLEN(cite_open2);
|
|
if (CitationColor &&
|
|
output_ptr + XP_STRLEN(CitationColor) + 20 < end_of_buffer) {
|
|
XP_STRCPY(output_ptr, "<FONT COLOR=");
|
|
output_ptr += XP_STRLEN(output_ptr);
|
|
XP_STRCPY(output_ptr, CitationColor);
|
|
output_ptr += XP_STRLEN(output_ptr);
|
|
XP_STRCPY(output_ptr, ">");
|
|
output_ptr += XP_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] == ':') ||
|
|
!XP_STRNCMP(cp, "internal-", 9))
|
|
{
|
|
XP_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;
|
|
|
|
XP_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 += XP_STRLEN(output_ptr);
|
|
XP_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 == '<')
|
|
{
|
|
XP_STRCPY(output_ptr, "<");
|
|
output_ptr += 4;
|
|
col++;
|
|
}
|
|
else if(*cp == '>')
|
|
{
|
|
XP_STRCPY(output_ptr, ">");
|
|
output_ptr += 4;
|
|
col++;
|
|
}
|
|
else if(*cp == '&')
|
|
{
|
|
XP_STRCPY(output_ptr, "&");
|
|
output_ptr += 5;
|
|
col++;
|
|
}
|
|
else
|
|
{
|
|
*output_ptr++ = *cp;
|
|
col++;
|
|
}
|
|
}
|
|
}
|
|
|
|
*output_ptr = 0;
|
|
|
|
if (line_is_citation) /* Close off the highlighting */
|
|
{
|
|
if (CitationColor) {
|
|
XP_STRCPY(output_ptr, "</FONT>");
|
|
output_ptr += XP_STRLEN(output_ptr);
|
|
}
|
|
|
|
XP_STRCPY(output_ptr, cite_close2);
|
|
output_ptr += XP_STRLEN (cite_close2);
|
|
XP_STRCPY(output_ptr, cite_close1);
|
|
output_ptr += XP_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 = XP_REALLOC(*output, *output_max);
|
|
if (!*output) return;
|
|
*curoutput = *output + offset;
|
|
}
|
|
XP_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;
|
|
|
|
XP_ASSERT(input);
|
|
if (!input) return NULL;
|
|
inputlength = XP_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 = XP_ALLOC(output_max);
|
|
if (!output) goto FAIL;
|
|
|
|
tmpbuf_max = 1024;
|
|
tmpbuf = XP_ALLOC(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;
|
|
XP_FREE(tmpbuf);
|
|
tmpbuf = XP_ALLOC(tmpbuf_max);
|
|
if (!tmpbuf) goto FAIL;
|
|
}
|
|
if (NET_ScanForURLs(NULL, linestart, length,
|
|
tmpbuf, tmpbuf_max, TRUE) < 0) {
|
|
goto FAIL;
|
|
}
|
|
length = XP_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 = strcasestr(linestart, "</a");
|
|
if (lineend) {
|
|
lineend = XP_STRCHR(lineend, '>');
|
|
if (lineend) lineend++;
|
|
}
|
|
} else {
|
|
lineend = XP_STRCHR(linestart, '>');
|
|
if (lineend) lineend++;
|
|
}
|
|
break;
|
|
case '&':
|
|
lineend = XP_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++;
|
|
}
|
|
}
|
|
XP_FREE(tmpbuf);
|
|
*curoutput = '\0';
|
|
return output;
|
|
|
|
FAIL:
|
|
if (tmpbuf) XP_FREE(tmpbuf);
|
|
if (output) XP_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 = XP_STRCHR(string, '@');
|
|
|
|
if(at_sign)
|
|
{
|
|
/* make sure it has at least one period
|
|
*/
|
|
if(XP_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 (!XP_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(!strncasecomp(address, "/mc-icons/", 10) ||
|
|
!strncasecomp(address, "/ns-icons/", 10))
|
|
return(TRUE);
|
|
|
|
if(!strncasecomp(address, "internal-external-reconnect:", 28))
|
|
return(TRUE);
|
|
|
|
if(!strcasecomp(address, "internal-external-plugin"))
|
|
return(TRUE);
|
|
|
|
if(!strncasecomp(address, "snews:", 6))
|
|
return TRUE;
|
|
|
|
/*
|
|
* IMAP URLs begin with "mailbox://" unlike POP URLs which begin
|
|
* with "mailbox:".
|
|
*/
|
|
if(!strncasecomp(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 *) XP_ALLOC(XP_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 Bool
|
|
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 &&
|
|
(!XP_STRNCMP(string, "From ", 5) ||
|
|
!XP_STRNCMP(string, ">From ", 6)))
|
|
return FALSE;
|
|
|
|
for(; count > 0; ptr++, count--)
|
|
if(*ptr == '<')
|
|
{
|
|
if(count > 3 && !strncasecomp(ptr+1, "HTML", 4))
|
|
return(TRUE);
|
|
|
|
if(count > 4 && !strncasecomp(ptr+1, "TITLE", 5))
|
|
return(TRUE);
|
|
|
|
if(count > 3 && !strncasecomp(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 (!strncasecomp(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 = XP_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 (!strncasecomp(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;
|
|
}
|
|
XP_ASSERT (new_url);
|
|
XP_ASSERT (headers);
|
|
url_struct->address_modified = TRUE;
|
|
XP_FREE (url_struct->address);
|
|
url_struct->address = new_url;
|
|
url_struct->post_headers = headers;
|
|
#else
|
|
XP_ASSERT(0);
|
|
#endif /* MOZILLA_CLIENT */
|
|
}
|
|
#endif /* MOZ_MAIL_NEWS */
|
|
|
|
if(encoding && !strcasecomp(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) {
|
|
XP_FREE(tmpfilename);
|
|
return 0;
|
|
}
|
|
|
|
if (url_struct->post_headers)
|
|
{
|
|
len = XP_FileWrite(url_struct->post_headers,
|
|
XP_STRLEN (url_struct->post_headers),
|
|
fp);
|
|
XP_FREE (url_struct->post_headers);
|
|
url_struct->post_headers = 0;
|
|
if (len < 0)
|
|
{
|
|
XP_FileClose(fp);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
XP_STRCPY (buffer,
|
|
"Content-type: text/plain" CRLF
|
|
"Content-Disposition: inline; form-data" CRLF CRLF);
|
|
len = XP_FileWrite(buffer, XP_STRLEN(buffer), fp);
|
|
|
|
for(i=0; (len >= 0) && (i < sub_data->value_cnt); i++)
|
|
{
|
|
if(name_array[i])
|
|
XP_FileWrite(name_array[i], XP_STRLEN(name_array[i]), fp);
|
|
XP_FileWrite("=", 1, fp);
|
|
if(value_array[i])
|
|
XP_FileWrite(value_array[i], XP_STRLEN(value_array[i]), fp);
|
|
len = XP_FileWrite(CRLF, 2, fp);
|
|
}
|
|
XP_FileClose(fp);
|
|
|
|
StrAllocCopy(url_struct->post_data, tmpfilename);
|
|
XP_FREE(tmpfilename);
|
|
if (len < 0)
|
|
return 0;
|
|
url_struct->post_data_is_file = TRUE;
|
|
}
|
|
else if(encoding && !strcasecomp(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) {
|
|
XP_FREE(tmpfilename);
|
|
return 0;
|
|
}
|
|
|
|
sprintf(separator, "---------------------------%d%d%d",
|
|
rand(), rand(), rand());
|
|
|
|
if(url_struct->post_headers)
|
|
{
|
|
len = XP_FileWrite(url_struct->post_headers,
|
|
XP_STRLEN (url_struct->post_headers),
|
|
fp);
|
|
XP_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, XP_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 = XP_STRLEN(separator) + 6;
|
|
cont_disp_len = XP_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 += XP_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 += XP_STRLEN(PLUS_FILENAME);
|
|
if(value_array[i])
|
|
{
|
|
/* only write the filename, not the whole path */
|
|
char * slash = XP_STRRCHR(value_array[i], '/');
|
|
if(slash)
|
|
slash++;
|
|
else
|
|
slash = value_array[i];
|
|
total_size += XP_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 += XP_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 += XP_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, XP_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, XP_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], XP_STRLEN(name_array[i]), fp);
|
|
|
|
if(type_array[i] == FORM_TYPE_FILE)
|
|
{
|
|
XP_FileWrite(PLUS_FILENAME, XP_STRLEN(PLUS_FILENAME), fp);
|
|
if(value_array[i])
|
|
{
|
|
/* only write the filename, not the whole path */
|
|
char * slash = XP_STRRCHR(value_array[i], '/');
|
|
if(slash)
|
|
slash++;
|
|
else
|
|
slash = value_array[i];
|
|
XP_FileWrite(slash, XP_STRLEN(slash), fp);
|
|
|
|
}
|
|
}
|
|
/* end the content disposition line */
|
|
len = XP_FileWrite("\"" CRLF, XP_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,
|
|
XP_STRLEN(CONTENT_TYPE_HEADER),
|
|
fp);
|
|
XP_FileWrite(APPLICATION_MACBINARY,
|
|
XP_STRLEN(APPLICATION_MACBINARY),
|
|
fp);
|
|
len = XP_FileWrite(CRLF, XP_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,
|
|
XP_STRLEN(CONTENT_TYPE_HEADER),
|
|
fp);
|
|
XP_FileWrite(ctype->type,
|
|
XP_STRLEN(ctype->type),
|
|
fp);
|
|
len = XP_FileWrite(CRLF, XP_STRLEN(CRLF), fp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* end the header */
|
|
len = XP_FileWrite(CRLF, XP_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], XP_STRLEN(value_array[i]), fp);
|
|
}
|
|
}
|
|
|
|
sprintf(buffer, "%s--%s--%s", CRLF, separator, CRLF);
|
|
XP_FileWrite(buffer, XP_STRLEN(buffer), fp);
|
|
|
|
XP_FileClose(fp);
|
|
|
|
StrAllocCopy(url_struct->post_data, tmpfilename);
|
|
XP_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 = XP_STRCHR(url_struct->address, '?');
|
|
if(punc)
|
|
*punc = '\0'; /* terminate here */
|
|
punc = XP_STRCHR(url_struct->address, '#');
|
|
if(punc)
|
|
*punc = '\0'; /* terminate here */
|
|
|
|
/* add the size of the url plus one for the '?'
|
|
*/
|
|
total_size += XP_STRLEN(url_struct->address)+1;
|
|
}
|
|
|
|
url_struct->post_data = (char *) XP_ALLOC(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 && !strcasecomp(name_array[0], "isindex"))
|
|
{
|
|
if(value_array && value_array[0])
|
|
XP_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;
|
|
XP_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;
|
|
XP_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 = XP_STRLEN(url_struct->post_data);
|
|
XP_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];
|
|
|
|
XP_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 = XP_STRCHR(url_struct->address, '?');
|
|
if(punc)
|
|
*punc = '\0'; /* terminate here */
|
|
punc = XP_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) {
|
|
XP_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(!strncasecomp(URL,"about:security", 14))
|
|
return(SECURITY_TYPE_URL);
|
|
else if(!strncasecomp(URL,"about:",6))
|
|
return(ABOUT_TYPE_URL);
|
|
else if(!strncasecomp(URL,"addbook:",8))
|
|
return(ADDRESS_BOOK_TYPE_URL);
|
|
else if (!strncasecomp(URL, "addbook-ldap", 12)) /*no colon includes addbook-ldaps:*/
|
|
return(ADDRESS_BOOK_LDAP_TYPE_URL);
|
|
break;
|
|
|
|
case 'd':
|
|
case 'D':
|
|
if(!strncasecomp(URL,"data:",5))
|
|
return(DATA_TYPE_URL);
|
|
break;
|
|
|
|
case 'c':
|
|
case 'C':
|
|
if(!strncasecomp(URL,"castanet:",9))
|
|
return(0);
|
|
break;
|
|
|
|
case 'f':
|
|
case 'F':
|
|
if(!strncasecomp(URL,"ftp:",4))
|
|
return(FTP_TYPE_URL);
|
|
else if(!strncasecomp(URL,"file:",5))
|
|
return(FILE_TYPE_URL);
|
|
break;
|
|
case 'g':
|
|
case 'G':
|
|
if(!strncasecomp(URL,"gopher:",7))
|
|
return(GOPHER_TYPE_URL);
|
|
break;
|
|
case 'h':
|
|
case 'H':
|
|
if(!strncasecomp(URL,"http:",5))
|
|
return(HTTP_TYPE_URL);
|
|
else if(!strncasecomp(URL,"https:",6))
|
|
return(SECURE_HTTP_TYPE_URL);
|
|
break;
|
|
case 'i':
|
|
case 'I':
|
|
if(!strncasecomp(URL,"internal-gopher-",16))
|
|
return(INTERNAL_IMAGE_TYPE_URL);
|
|
else if(!strncasecomp(URL,"internal-news-",14))
|
|
return(INTERNAL_IMAGE_TYPE_URL);
|
|
else if(!strncasecomp(URL,"internal-edit-",14))
|
|
return(INTERNAL_IMAGE_TYPE_URL);
|
|
else if(!strncasecomp(URL,"internal-attachment-",20))
|
|
return(INTERNAL_IMAGE_TYPE_URL);
|
|
else if(!strncasecomp(URL,"internal-sa-",12))
|
|
return(INTERNAL_IMAGE_TYPE_URL);
|
|
else if(!strncasecomp(URL,"internal-smime-",15))
|
|
return(INTERNAL_IMAGE_TYPE_URL);
|
|
else if(!strncasecomp(URL,"internal-dialog-handler",23))
|
|
return(HTML_DIALOG_HANDLER_TYPE_URL);
|
|
else if(!strncasecomp(URL,"internal-panel-handler",22))
|
|
return(HTML_PANEL_HANDLER_TYPE_URL);
|
|
else if(!strncasecomp(URL,"internal-security-",18))
|
|
return(INTERNAL_SECLIB_TYPE_URL);
|
|
else if(!strncasecomp(URL,"internal-certldap",17))
|
|
return(INTERNAL_CERTLDAP_TYPE_URL);
|
|
else if(!strncasecomp(URL,"IMAP:",5))
|
|
return(IMAP_TYPE_URL);
|
|
break;
|
|
case 'j':
|
|
case 'J':
|
|
if(!strncasecomp(URL, "javascript:",11))
|
|
return(MOCHA_TYPE_URL);
|
|
break;
|
|
case 'l':
|
|
case 'L':
|
|
if(!strncasecomp(URL, "livescript:",11))
|
|
return(MOCHA_TYPE_URL);
|
|
else if (!strncasecomp(URL, "ldap:",5))
|
|
return(LDAP_TYPE_URL);
|
|
else if (!strncasecomp(URL, "ldaps:",6))
|
|
return(SECURE_LDAP_TYPE_URL);
|
|
break;
|
|
case 'm':
|
|
case 'M':
|
|
if(!strncasecomp(URL,"mailto:",7))
|
|
return(MAILTO_TYPE_URL);
|
|
else if(!strncasecomp(URL,"mailbox:",8))
|
|
return(MAILBOX_TYPE_URL);
|
|
else if(!strncasecomp(URL, "mocha:",6))
|
|
return(MOCHA_TYPE_URL);
|
|
break;
|
|
case 'n':
|
|
case 'N':
|
|
if(!strncasecomp(URL,"news:",5))
|
|
return(NEWS_TYPE_URL);
|
|
else if(!strncasecomp(URL,"nfs:",4))
|
|
return(NFS_TYPE_URL);
|
|
else if(!strncasecomp(URL, NETHELP_URL_PREFIX, sizeof(NETHELP_URL_PREFIX)-1))
|
|
return(NETHELP_TYPE_URL);
|
|
break;
|
|
case 'p':
|
|
case 'P':
|
|
if(!strncasecomp(URL,"pop3:",5))
|
|
return(POP3_TYPE_URL);
|
|
break;
|
|
case 'r':
|
|
case 'R':
|
|
if(!strncasecomp(URL,"rlogin:",7))
|
|
return(RLOGIN_TYPE_URL);
|
|
break;
|
|
case 's':
|
|
case 'S':
|
|
if(!strncasecomp(URL,"snews:",6))
|
|
return(NEWS_TYPE_URL);
|
|
else if (!strncasecomp(URL,"search-libmsg:",14))
|
|
return(MSG_SEARCH_TYPE_URL);
|
|
case 't':
|
|
case 'T':
|
|
if(!strncasecomp(URL,"telnet:",7))
|
|
return(TELNET_TYPE_URL);
|
|
else if(!strncasecomp(URL,"tn3270:",7))
|
|
return(TN3270_TYPE_URL);
|
|
break;
|
|
case 'u':
|
|
case 'U':
|
|
if(!strncasecomp(URL,"URN:",4))
|
|
return(URN_TYPE_URL);
|
|
break;
|
|
case 'v':
|
|
case 'V':
|
|
if(!strncasecomp(URL, VIEW_SOURCE_URL_PREFIX,
|
|
sizeof(VIEW_SOURCE_URL_PREFIX)-1))
|
|
return(VIEW_SOURCE_TYPE_URL);
|
|
break;
|
|
case 'w':
|
|
case 'W':
|
|
if(!strncasecomp(URL,"wais:",5))
|
|
return(WAIS_TYPE_URL);
|
|
if(!strncasecomp(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 = ' ';
|
|
}
|
|
|
|
|
|
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
|
|
*/
|
|
XP_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 = XP_REALLOC (bs->buffer, len);
|
|
else
|
|
newBuffer = XP_ALLOC(len);
|
|
if (!newBuffer)
|
|
return MK_OUT_OF_MEMORY;
|
|
XP_MEMSET(newBuffer+bs->pos, 0, len-bs->pos);
|
|
bs->size = len;
|
|
bs->buffer = newBuffer;
|
|
}
|
|
XP_MEMCPY (bs->buffer+bs->pos, buf, size);
|
|
bs->pos += size;
|
|
return 0;
|
|
}
|
|
|
|
PRIVATE int32
|
|
net_URNProtoLoad(ActiveEntry *ce)
|
|
{
|
|
char buffer[256];
|
|
|
|
XP_STRCPY(buffer, XP_GetString(XP_ALERT_URN_USEHTTP));
|
|
XP_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)
|
|
{
|
|
XP_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];
|
|
|
|
XP_STRCPY(buffer, XP_GetString(XP_ALERT_NFS_USEHTTP));
|
|
XP_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)
|
|
{
|
|
XP_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)
|
|
{
|
|
XP_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;
|
|
|
|
XP_ASSERT (src);
|
|
if (!src)
|
|
return NULL;
|
|
else if (srclen == 0)
|
|
return XP_STRDUP("");
|
|
|
|
XP_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);
|
|
XP_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;
|
|
|
|
XP_ASSERT (src);
|
|
if (!src)
|
|
return NULL;
|
|
else if (srclen == 0)
|
|
return XP_STRDUP("");
|
|
|
|
XP_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);
|
|
XP_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 */
|