diff --git a/mozilla/lib/mailto/Makefile b/mozilla/lib/mailto/Makefile new file mode 100644 index 00000000000..18942322b99 --- /dev/null +++ b/mozilla/lib/mailto/Makefile @@ -0,0 +1,90 @@ +#!gmake +# +# 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. + + + +DEPTH = ../.. + +MODULE = mailto +LIBRARY_NAME = mailto + +CSRCS = \ + ad_strm.c \ + ap_decod.c \ + ap_encod.c \ + appledbl.c \ + bh_strm.c \ + m_binhex.c \ + msgutils.c \ + $(NULL) + + +CPPSRCS = \ + addrutil.cpp \ + bytearr.cpp \ + dwordarr.cpp \ + mhtmlstm.cpp \ + msgbg.cpp \ + msgcflds.cpp \ + msgcpane.cpp \ + msgglue.cpp \ + msgmast.cpp \ + msgmdn.cpp \ + msgpane.cpp \ + msgppane.cpp \ + msgprefs.cpp \ + msgsend.cpp \ + msgsendp.cpp \ + msgurlq.cpp \ + msgzap.cpp \ + ptrarray.cpp \ + $(NULL) +# We'd like to include msgdlqml.cpp eventually (deliver queued mail) + + +EXPORTS = \ + ad_codes.h \ + appledbl.h \ + bytearr.h \ + dwordarr.h \ + errcode.h \ + error.h \ + m_binhex.h \ + mhtmlstm.h \ + msg.h \ + msgcflds.h \ + msgcpane.h \ + msghdr.h \ + msgmast.h \ + msgmdn.h \ + msgpane.h \ + msgppane.h \ + msgprefs.h \ + msgprnot.h \ + msgsend.h \ + msgsendp.h \ + msgutils.h \ + msgzap.h \ + ptrarray.h \ + $(NULL) + +REQUIRES = nspr htmldlgs img util layer pref security js java net progress network + + +include $(DEPTH)/config/rules.mk + +# INCLUDES += -I$(DIST)/include -I$(PUBLIC)/security -I../libaddr -I$(PUBLIC)/msg diff --git a/mozilla/lib/mailto/ad_codes.h b/mozilla/lib/mailto/ad_codes.h new file mode 100644 index 00000000000..5c9301bd860 --- /dev/null +++ b/mozilla/lib/mailto/ad_codes.h @@ -0,0 +1,129 @@ +/* -*- 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. + */ + +/* +** AD_Codes.h +** +** --------------- +** +** Head file for Apple Decode/Encode enssential codes. +** +** +*/ + +#ifndef ad_codes_h +#define ad_codes_h + +#include "xp_core.h" + +/* +** applefile definitions used +*/ +#ifdef XP_MAC +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif +#endif /* XP_MAC */ + +#define APPLESINGLE_MAGIC 0x00051600L +#define APPLEDOUBLE_MAGIC 0x00051607L +#define VERSION 0x00020000 + +#define NUM_ENTRIES 6 + +#define ENT_DFORK 1L +#define ENT_RFORK 2L +#define ENT_NAME 3L +#define ENT_COMMENT 4L +#define ENT_DATES 8L +#define ENT_FINFO 9L +#define CONVERT_TIME 1265437696L + +/* +** data type used in the encoder/decoder. +*/ +typedef struct ap_header +{ + int32 magic; + int32 version; + char fill[16]; + int16 entries; + +} ap_header; + +typedef struct ap_entry +{ + uint32 id; + uint32 offset; + uint32 length; + +} ap_entry; + +typedef struct ap_dates +{ + int32 create, modify, backup, access; + +} ap_dates; + +typedef struct myFInfo /* the mac FInfo structure for the cross platform. */ +{ + int32 fdType, fdCreator; + int16 fdFlags; + int32 fdLocation; /* it really should be a pointer, but just a place-holder */ + int16 fdFldr; + +} myFInfo; + +XP_BEGIN_PROTOS +/* +** string utils. +*/ +int write_stream(appledouble_encode_object *p_ap_encode_obj,char *s,int len); + +int fill_apple_mime_header(appledouble_encode_object *p_ap_encode_obj); +int ap_encode_file_infor(appledouble_encode_object *p_ap_encode_obj); +int ap_encode_header(appledouble_encode_object* p_ap_encode_obj, XP_Bool firstTime); +int ap_encode_data( appledouble_encode_object* p_ap_encode_obj, XP_Bool firstTime); + +/* +** the prototypes for the ap_decoder. +*/ +int fetch_a_line(appledouble_decode_object* p_ap_decode_obj, char *buff); +int ParseFileHeader(appledouble_decode_object* p_ap_decode_obj); +int ap_seek_part_start(appledouble_decode_object* p_ap_decode_obj); +void parse_param(char *p, char **param, char**define, char **np); +int ap_seek_to_boundary(appledouble_decode_object* p_ap_decode_obj, XP_Bool firstime); +int ap_parse_header(appledouble_decode_object* p_ap_decode_obj,XP_Bool firstime); +int ap_decode_file_infor(appledouble_decode_object* p_ap_decode_obj); +int ap_decode_process_header(appledouble_decode_object* p_ap_decode_obj, XP_Bool firstime); +int ap_decode_process_data( appledouble_decode_object* p_ap_decode_obj, XP_Bool firstime); + +#ifdef XP_MAC +OSErr my_FSSpecFromPathname(char* src_filename, FSSpec* fspec); +char* my_PathnameFromFSSpec(FSSpec* fspec); + +#endif + XP_END_PROTOS + +#ifdef XP_MAC +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif +#endif /* XP_MAC */ + +#endif /* ad_codes_h */ diff --git a/mozilla/lib/mailto/ad_strm.c b/mozilla/lib/mailto/ad_strm.c new file mode 100644 index 00000000000..46118bc11ab --- /dev/null +++ b/mozilla/lib/mailto/ad_strm.c @@ -0,0 +1,719 @@ +/* -*- 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. + */ + +/** +* Apple Double encode/decode stream +* ---------------------------------- +* +* 11sep95 mym created. +*/ + +#include "msg.h" +#include "appledbl.h" +#include "m_binhex.h" +#include "m_cvstrm.h" +#include "ad_codes.h" +/* for XP_GetString() */ +#include "xpgetstr.h" + +extern int MK_MSG_SAVE_ATTACH_AS; + +#ifdef XP_MAC +#pragma warn_unusedarg off + +extern int MK_UNABLE_TO_OPEN_TMP_FILE; +extern int MK_MIME_ERROR_WRITING_FILE; + +/* --------------------------------------------------------------------------------- +** +** The codes for Apple-double encoding stream. --- it's only useful on Mac OS +** +** --------------------------------------------------------------------------------- +*/ + +#define WORKING_BUFF_SIZE 8192 + +typedef struct _AppledoubleEncodeObject +{ + appledouble_encode_object ap_encode_obj; + + char* buff; /* the working buff. */ + int32 s_buff; /* the working buff size. */ + + XP_File fp; /* file to hold the encoding */ + char *fname; /* and the file name. */ + +} AppleDoubleEncodeObject; + +/* + Let's go "l" characters forward of the encoding for this write. + Note: + "s" is just a dummy paramter. + */ +PRIVATE int +net_AppleDouble_Encode_Write ( + void *stream, const char* s, int32 l) +{ + int status = 0; + AppleDoubleEncodeObject * obj = (AppleDoubleEncodeObject*)stream; + int32 count, size; + + while (l > 0) + { + size = obj->s_buff * 11 / 16; + size = MIN(l, size); + status = ap_encode_next(&(obj->ap_encode_obj), + obj->buff, + size, + &count); + if (status == noErr || status == errDone) + { + /* + * we get the encode data, so call the next stream to write it to the disk. + */ + if (XP_FileWrite(obj->buff, count, obj->fp) != count) + return errFileWrite; + } + + if (status != noErr ) /* abort when error / done? */ + break; + + l -= size; + } + return status; +} + +/* +** is the stream ready for writing? + */ +PRIVATE unsigned int net_AppleDouble_Encode_Ready (void *stream) +{ + return(MAX_WRITE_READY); /* always ready for writing */ +} + + +PRIVATE void net_AppleDouble_Encode_Complete (void *stream) +{ + AppleDoubleEncodeObject * obj = (AppleDoubleEncodeObject*)stream; + + ap_encode_end(&(obj->ap_encode_obj), false); /* this is a normal ending */ + + if (obj->fp) + { + XP_FileClose(obj->fp); /* done with the target file */ + + FREEIF(obj->fname); /* and the file name too */ + } + + FREEIF(obj->buff); /* free the working buff. */ + XP_FREE(obj); +} + +PRIVATE void net_AppleDouble_Encode_Abort (void *stream, int status) +{ + AppleDoubleEncodeObject * obj = (AppleDoubleEncodeObject*)stream; + + ap_encode_end(&(obj->ap_encode_obj), true); /* it is an aborting exist... */ + + if (obj->fp) + { + XP_FileClose(obj->fp); + + XP_FileRemove (obj->fname, xpURL); /* remove the partial file. */ + + FREEIF(obj->fname); + } + FREEIF(obj->buff); /* free the working buff. */ + XP_FREE(obj); +} + +/* +** fe_MakeAppleDoubleEncodeStream +** ------------------------------ +** +** Will create a apple double encode stream: +** +** -> take the filename as the input source (it needs to be a mac file.) +** -> take a file name for the temp file we are generating. +*/ + +PUBLIC NET_StreamClass * +fe_MakeAppleDoubleEncodeStream (int format_out, + void *data_obj, + URL_Struct *URL_s, + MWContext *window_id, + char* src_filename, + char* dst_filename, + char* separator) +{ + AppleDoubleEncodeObject* obj; + NET_StreamClass* stream; + char* working_buff = NULL; + int bSize = WORKING_BUFF_SIZE; + + TRACEMSG(("Setting up apple encode stream. Have URL: %s\n", URL_s->address)); + + stream = XP_NEW(NET_StreamClass); + if(stream == NULL) + return(NULL); + + obj = XP_NEW(AppleDoubleEncodeObject); + if (obj == NULL) + { + XP_FREE (stream); + return(NULL); + } + + while (!working_buff && (bSize >= 512)) + { + working_buff = (char *)XP_ALLOC(bSize); + if (!working_buff) + bSize /= 2; + } + if (working_buff == NULL) + { + XP_FREE (obj); + XP_FREE (stream); + return (NULL); + } + + stream->name = "Apple Double Encode"; + stream->complete = (MKStreamCompleteFunc) net_AppleDouble_Encode_Complete; + stream->abort = (MKStreamAbortFunc) net_AppleDouble_Encode_Abort; + stream->put_block = (MKStreamWriteFunc) net_AppleDouble_Encode_Write; + stream->is_write_ready = (MKStreamWriteReadyFunc) net_AppleDouble_Encode_Ready; + stream->data_object = obj; + stream->window_id = window_id; + + obj->fp = XP_FileOpen(dst_filename, xpFileToPost, XP_FILE_WRITE_BIN); + if (obj->fp == NULL) + { + XP_FREE (working_buff); + XP_FREE (obj); + XP_FREE (stream); + return (NULL); + } + + obj->fname = XP_STRDUP(dst_filename); + + obj->buff = working_buff; + obj->s_buff = bSize; + + /* + ** setup all the need information on the apple double encoder. + */ + ap_encode_init(&(obj->ap_encode_obj), + src_filename, /* pass the file name of the source. */ + separator); + + TRACEMSG(("Returning stream from NET_AppleDoubleEncoder\n")); + + return stream; +} +#endif + +/* +** --------------------------------------------------------------------------------- +** +** The codes for the Apple sigle/double decoding. +** +** --------------------------------------------------------------------------------- +*/ +typedef struct AppleDoubleDecodeObject +{ + appledouble_decode_object ap_decode_obj; + + char* in_buff; /* the temporary buff to accumulate */ + /* the input, make sure the call to */ + /* the dedcoder engine big enough buff */ + int32 bytes_in_buff; /* the count for the temporary buff. */ + + NET_StreamClass* binhex_stream; /* a binhex encode stream to convert */ + /* the decoded mac file to binhex. */ + +} AppleDoubleDecodeObject; + +PRIVATE int +net_AppleDouble_Decode_Write ( + void *stream, const char* s, int32 l) +{ + int status = NOERR; + AppleDoubleDecodeObject * obj = (AppleDoubleDecodeObject*) stream; + int32 size; + + /* + ** To force an effecient decoding, we should + ** make sure that the buff pass to the decode next is great than 1024 bytes. + */ + if (obj->bytes_in_buff + l > 1024) + { + size = 1024 - obj->bytes_in_buff; + XP_MEMCPY(obj->in_buff+obj->bytes_in_buff, + s, + size); + s += size; + l -= size; + + status = ap_decode_next(&(obj->ap_decode_obj), + obj->in_buff, + 1024); + obj->bytes_in_buff = 0; + } + + if (l > 1024) + { + /* we are sure that obj->bytes_in_buff == 0 at this point. */ + status = ap_decode_next(&(obj->ap_decode_obj), + (char *)s, + l); + } + else + { + /* and we are sure we will not get overflow with the buff. */ + XP_MEMCPY(obj->in_buff+obj->bytes_in_buff, + s, + l); + obj->bytes_in_buff += l; + } + return status; +} + +PRIVATE unsigned int +net_AppleDouble_Decode_Ready (NET_StreamClass *stream) +{ + return(MAX_WRITE_READY); /* always ready for writing */ +} + + +PRIVATE void +net_AppleDouble_Decode_Complete (void *stream) +{ + AppleDoubleDecodeObject *obj = (AppleDoubleDecodeObject *)stream; + + if (obj->bytes_in_buff) + { + + ap_decode_next(&(obj->ap_decode_obj), /* do the last calls. */ + (char *)obj->in_buff, + obj->bytes_in_buff); + obj->bytes_in_buff = 0; + } + + ap_decode_end(&(obj->ap_decode_obj), FALSE); /* it is a normal clean up cases.*/ + + if (obj->binhex_stream) + XP_FREE(obj->binhex_stream); + + if (obj->in_buff) + XP_FREE(obj->in_buff); + + XP_FREE(obj); +} + +PRIVATE void +net_AppleDouble_Decode_Abort ( + void *stream, int status) +{ + AppleDoubleDecodeObject *obj = (AppleDoubleDecodeObject *)stream; + + ap_decode_end(&(obj->ap_decode_obj), TRUE); /* it is an abort. */ + + if (obj->binhex_stream) + XP_FREE(obj->binhex_stream); + + if (obj->in_buff) + XP_FREE(obj->in_buff); + + XP_FREE(obj); +} + + +/* +** fe_MakeAppleDoubleDecodeStream_1 +** --------------------------------- +** +** Create the apple double decode stream. +** +** In the Mac OS, it will create a stream to decode to an apple file; +** +** In other OS, the stream will decode apple double object, +** then encode it in binhex format, and save to the file. +*/ +#ifndef XP_MAC +static void +simple_copy(MWContext* context, char* saveName, void* closure) +{ + /* just copy the filename to the closure, so the caller can get it. */ + XP_STRCPY(closure, saveName); +} +#endif + +PUBLIC NET_StreamClass * +fe_MakeAppleDoubleDecodeStream_1 (int format_out, + void *data_obj, + URL_Struct *URL_s, + MWContext *window_id) +{ +#ifdef XP_MAC + return fe_MakeAppleDoubleDecodeStream(format_out, + data_obj, + URL_s, + window_id, + false, + NULL); +#else + +#if 0 /* just a test in the mac OS */ + NET_StreamClass *p; + char* url; + StandardFileReply reply; + + StandardPutFile("\pSave binhex encoded file as:", "\pUntitled", &reply); + if (!reply.sfGood) + { + return NULL; + } + url = my_PathnameFromFSSpec(&(reply.sfFile)); + + p = fe_MakeAppleDoubleDecodeStream(format_out, + data_obj, + URL_s, + window_id, + true, + url+7); + XP_FREE(url); + return (p); + +#else /* for the none mac-os to get a file name */ + + NET_StreamClass *p; + char* filename; + + filename = XP_ALLOC(1024); + if (filename == NULL) + return NULL; + + if (FE_PromptForFileName(window_id, + XP_GetString(MK_MSG_SAVE_ATTACH_AS), + 0, + FALSE, + FALSE, + simple_copy, + filename) == -1) + { + return NULL; + } + + p = fe_MakeAppleDoubleDecodeStream(format_out, + data_obj, + URL_s, + window_id, + TRUE, + filename); + XP_FREE(filename); + return (p); + +#endif + +#endif +} + + +PUBLIC NET_StreamClass * +fe_MakeAppleDoubleDecodeStream (int format_out, + void *data_obj, + URL_Struct *URL_s, + MWContext *window_id, + XP_Bool write_as_binhex, + char *dst_filename) +{ + AppleDoubleDecodeObject* obj; + NET_StreamClass* stream; + + TRACEMSG(("Setting up apple double decode stream. Have URL: %s\n", URL_s->address)); + + stream = XP_NEW(NET_StreamClass); + if(stream == NULL) + return(NULL); + + obj = XP_NEW(AppleDoubleDecodeObject); + if (obj == NULL) + { + XP_FREE(stream); + return(NULL); + } + + stream->name = "AppleDouble Decode"; + stream->complete = (MKStreamCompleteFunc) net_AppleDouble_Decode_Complete; + stream->abort = (MKStreamAbortFunc) net_AppleDouble_Decode_Abort; + stream->put_block = (MKStreamWriteFunc) net_AppleDouble_Decode_Write; + stream->is_write_ready = (MKStreamWriteReadyFunc) net_AppleDouble_Decode_Ready; + stream->data_object = obj; + stream->window_id = window_id; + + /* + ** setup all the need information on the apple double encoder. + */ + obj->in_buff = (char *)XP_ALLOC(1024); + if (obj->in_buff == NULL) + { + XP_FREE(obj); + XP_FREE(stream); + return (NULL); + } + + obj->bytes_in_buff = 0; + + if (write_as_binhex) + { + obj->binhex_stream = + fe_MakeBinHexEncodeStream(format_out, + data_obj, + URL_s, + window_id, + dst_filename); + if (obj->binhex_stream == NULL) + { + XP_FREE(obj); + XP_FREE(stream); + XP_FREE(obj->in_buff); + return NULL; + } + + ap_decode_init(&(obj->ap_decode_obj), + FALSE, + TRUE, + obj->binhex_stream); + } + else + { + obj->binhex_stream = NULL; + ap_decode_init(&(obj->ap_decode_obj), + FALSE, + FALSE, + window_id); + /* + * jt 8/8/97 -- I think this should be set to true. But' + * let's not touch it for now. + * + * obj->ap_decode_obj.is_binary = TRUE; + */ + } + + if (dst_filename) + { + XP_STRNCPY_SAFE(obj->ap_decode_obj.fname, dst_filename, + sizeof(obj->ap_decode_obj.fname)); + } + #ifdef XP_MAC + obj->ap_decode_obj.mSpec = (FSSpec*)( URL_s->fe_data ); + #endif + TRACEMSG(("Returning stream from NET_AppleDoubleDecode\n")); + + return stream; +} + +/* +** fe_MakeAppleSingleDecodeStream_1 +** -------------------------------- +** +** Create the apple single decode stream. +** +** In the Mac OS, it will create a stream to decode object to an apple file; +** +** In other OS, the stream will decode apple single object, +** then encode context in binhex format, and save to the file. +*/ + +PUBLIC NET_StreamClass * +fe_MakeAppleSingleDecodeStream_1 (int format_out, + void *data_obj, + URL_Struct *URL_s, + MWContext *window_id) +{ +#ifdef XP_MAC + return fe_MakeAppleSingleDecodeStream(format_out, + data_obj, + URL_s, + window_id, + FALSE, + NULL); +#else + +#if 0 /* just a test in the mac OS */ + NET_StreamClass *p; + char* url; + StandardFileReply reply; + + StandardPutFile("\pSave binhex encoded file as:", "\pUntitled", &reply); + if (!reply.sfGood) + { + return NULL; + } + url = my_PathnameFromFSSpec(&(reply.sfFile)); + + p = fe_MakeAppleSingleDecodeStream(format_out, + data_obj, + URL_s, + window_id, + true, + url+7); + XP_FREE(url); + return (p); + +#else /* for the none mac-os to get a file name */ + + NET_StreamClass *p; + char* filename; + char* defaultPath = 0; + + defaultPath = URL_s->content_name; + +#ifdef XP_WIN16 + if (XP_FileNameContainsBadChars(defaultPath)) + defaultPath = 0; +#endif + + filename = XP_ALLOC(1024); + if (filename == NULL) + return NULL; + + if (FE_PromptForFileName(window_id, + XP_GetString(MK_MSG_SAVE_ATTACH_AS), + defaultPath, + FALSE, + FALSE, + simple_copy, + filename) == -1) + { + return NULL; + } + + p = fe_MakeAppleSingleDecodeStream(format_out, + data_obj, + URL_s, + window_id, + FALSE, + filename); + XP_FREE(filename); + return (p); + +#endif + +#endif +} + +/* +** Create the Apple Doube Decode stream. +** +*/ +PUBLIC NET_StreamClass * +fe_MakeAppleSingleDecodeStream (int format_out, + void *data_obj, + URL_Struct *URL_s, + MWContext *window_id, + XP_Bool write_as_binhex, + char *dst_filename) +{ + AppleDoubleDecodeObject* obj; + NET_StreamClass* stream; + int encoding = kEncodeNone; /* default is that we don't know the encoding */ + + TRACEMSG(("Setting up apple single decode stream. Have URL: %s\n", URL_s->address)); + + stream = XP_NEW(NET_StreamClass); + if(stream == NULL) + return(NULL); + + obj = XP_NEW(AppleDoubleDecodeObject); + if (obj == NULL) + { + XP_FREE(stream); + return(NULL); + } + + stream->name = "AppleSingle Decode"; + stream->complete = (MKStreamCompleteFunc) net_AppleDouble_Decode_Complete; + stream->abort = (MKStreamAbortFunc) net_AppleDouble_Decode_Abort; + stream->put_block = (MKStreamWriteFunc) net_AppleDouble_Decode_Write; + stream->is_write_ready = (MKStreamWriteReadyFunc) net_AppleDouble_Decode_Ready; + stream->data_object = obj; + stream->window_id = window_id; + + /* + ** setup all the need information on the apple double encoder. + */ + obj->in_buff = (char *)XP_ALLOC(1024); + if (obj->in_buff == NULL) + { + XP_FREE(obj); + XP_FREE(stream); + return (NULL); + } + + obj->bytes_in_buff = 0; + + if (write_as_binhex) + { + obj->binhex_stream = + fe_MakeBinHexEncodeStream(format_out, + data_obj, + URL_s, + window_id, + dst_filename); + if (obj->binhex_stream == NULL) + { + XP_FREE(obj); + XP_FREE(stream); + XP_FREE(obj->in_buff); + return NULL; + } + + ap_decode_init(&(obj->ap_decode_obj), + TRUE, + TRUE, + obj->binhex_stream); + } + else + { + obj->binhex_stream = NULL; + ap_decode_init(&(obj->ap_decode_obj), + TRUE, + FALSE, + window_id); +#ifndef XP_MAC + obj->ap_decode_obj.is_binary = TRUE; +#endif + } + + if (dst_filename) + { + XP_STRNCPY_SAFE(obj->ap_decode_obj.fname, dst_filename, + sizeof(obj->ap_decode_obj.fname)); + } + /* If we are of a broken content-type, impose its encoding. */ + if (URL_s->content_type + && !XP_STRNCASECMP(URL_s->content_type, "x-uuencode-apple-single", 23)) + obj->ap_decode_obj.encoding = kEncodeUU; + else + obj->ap_decode_obj.encoding = kEncodeNone; + + TRACEMSG(("Returning stream from NET_AppleSingleDecode\n")); + + return stream; +} diff --git a/mozilla/lib/mailto/addrutil.cpp b/mozilla/lib/mailto/addrutil.cpp new file mode 100644 index 00000000000..7c777d69e84 --- /dev/null +++ b/mozilla/lib/mailto/addrutil.cpp @@ -0,0 +1,1607 @@ +/* -*- 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. + */ + +/* addrutil.cpp --- parsing RFC822 addresses. + */ + +#include "msg.h" +#include "msgprefs.h" + +#undef FREEIF +#define FREEIF(obj) do { if (obj) { XP_FREE (obj); obj = 0; }} while (0) + +extern "C" +{ + extern int MK_OUT_OF_MEMORY; +} + +static int msg_quote_phrase_or_addr (char *address, int32 length, + XP_Bool addr_p); +static int msg_parse_rfc822_addresses (const char *line, + char **names, + char **addresses, + XP_Bool quote_names_p, + XP_Bool quote_addrs_p, + XP_Bool first_only_p); + + +/* Given a string which contains a list of RFC822 addresses, parses it into + their component names and mailboxes. + + The returned value is the number of addresses, or a negative error code; + the names and addresses are returned into the provided pointers as + consecutive null-terminated strings. It is up to the caller to free them. + Note that some of the strings may be zero-length. + + Either of the provided pointers may be NULL if the caller is not interested + in those components. + + quote_names_p and quote_addrs_p control whether the returned strings should + be quoted as RFC822 entities, or returned in a more human-presentable (but + not necessarily parsable) form. + + If first_only_p is true, then only the first element of the list is + returned; we don't bother parsing the rest. + */ +int +msg_parse_rfc822_addresses (const char *line, + char **names, + char **addresses, + XP_Bool quote_names_p, + XP_Bool quote_addrs_p, + XP_Bool first_only_p) +{ + uint32 addr_count = 0; + uint32 line_length; + const char *line_end; + const char *this_start; + char *name_buf = 0, *name_out, *name_start; + char *addr_buf = 0, *addr_out, *addr_start; + XP_ASSERT (line); + if (! line) + return -1; + if (names) + *names = 0; + if (addresses) + *addresses = 0; + line_length = XP_STRLEN (line); + if (line_length == 0) + return 0; + + name_buf = (char *) XP_ALLOC (line_length * 2 + 10); + if (! name_buf) + return MK_OUT_OF_MEMORY; + + addr_buf = (char *) XP_ALLOC (line_length * 2 + 10); + if (! addr_buf) + { + FREEIF (name_buf); + return MK_OUT_OF_MEMORY; + } + + line_end = line; + addr_out = addr_buf; + name_out = name_buf; + name_start = name_buf; + addr_start = addr_buf; + this_start = line; + + /* Skip over extra whitespace or commas before addresses. */ + while (*line_end && + (XP_IS_SPACE (*line_end) || *line_end == ',')) + line_end++; + + while (*line_end) + { + uint32 paren_depth = 0; + const char *oparen = 0; + const char *mailbox_start = 0; + const char *mailbox_end = 0; + + while (*line_end && + !(*line_end == ',' && + paren_depth <= 0 && /* comma is ok inside () */ + (!mailbox_start || mailbox_end))) /* comma is ok inside <> */ + { + if (*line_end == '\\') + { + line_end++; + if (!*line_end) /* otherwise, we walk off end of line, right? */ + break; + } + else if (*line_end == '"') + { + int leave_quotes = 0; + + line_end++; /* remove open " */ + + /* handle '"John.Van Doe"@space.com' case */ + if (paren_depth == 0 && !mailbox_start) + { + char *end_quote = strchr(line_end, '"'); + char *mailbox = end_quote ? strchr(end_quote, '<') : (char *)NULL, + *comma = end_quote ? strchr(end_quote, ',') : (char *)NULL; + if (!mailbox || (comma && comma < mailbox)) + { + + leave_quotes = 1; /* no mailbox for this address */ + *addr_out++ = '"'; + } + } + + while (*line_end) + { + if (*line_end == '\\') + { + if ( paren_depth == 0 + && (*(line_end+1) == '\\' || *(line_end+1) == '"')) + *addr_out++ = *line_end++; + else + line_end++; + } + else if (*line_end == '"') + break; + + if (paren_depth == 0) + *addr_out++ = *line_end; + + line_end++; + } + if (*line_end) line_end++; /* remove close " */ + if (leave_quotes) *addr_out++ = '"'; + continue; + } + + if (*line_end == '(') + { + if (paren_depth == 0) + oparen = line_end; + paren_depth++; + } + else if (*line_end == '<' && paren_depth == 0) + { + mailbox_start = line_end; + } + else if (*line_end == '>' && mailbox_start && paren_depth == 0) + { + mailbox_end = line_end; + } + else if (*line_end == ')' && paren_depth > 0) + { + paren_depth--; + if (paren_depth == 0) + { + const char *s = oparen + 1; + /* Copy the characters inside the parens onto the + "name" buffer. */ + + /* Push out some whitespace before the paren, if + there is non-whitespace there already. */ + if (name_out > name_start && + !XP_IS_SPACE (name_out [-1])) + *name_out++ = ' '; + + /* Skip leading whitespace. */ + while (XP_IS_SPACE (*s) && s < line_end) + s++; + + while (s < line_end) + { + if (*s == '\"') + { + /* Strip out " within () unless backslashed */ + s++; + continue; + } + + if (*s == '\\') /* remove one \ */ + s++; + + if (XP_IS_SPACE (*s) && + name_out > name_start && + XP_IS_SPACE (name_out[-1])) + /* collapse consecutive whitespace */; + else + *name_out++ = *s; + + s++; + } + oparen = 0; + } + } + else + { + /* If we're not inside parens or a , tack this + on to the end of the addr_buf. */ + if (paren_depth == 0 && + (!mailbox_start || mailbox_end)) + { + /* Eat whitespace at the beginning of the line, + and eat consecutive whitespace within the line. */ + if (XP_IS_SPACE (*line_end) && + (addr_out == addr_start || + XP_IS_SPACE (addr_out[-1]))) + /* skip it */; + else + *addr_out++ = *line_end; + } + } + + line_end++; + } + + /* Now we have extracted a single address from the comma-separated + list of addresses. The characters have been divided among the + various buffers: the parts inside parens have been placed in the + name_buf, and everything else has been placed in the addr_buf. + Quoted strings and backslashed characters have been `expanded.' + + If there was a spec in it, we have remembered where it was. + Copy that on to the addr_buf, replacing what was there, and copy the + characters not inside <> onto the name_buf, replacing what is there + now (which was just the parenthesized parts.) (And we need to do the + quote and backslash hacking again, since we're coming from the + original source.) + + Otherwise, we're already done - the addr_buf and name_buf contain + the right data already (de-quoted.) + */ + if (mailbox_end) + { + const char *s; + XP_ASSERT (*mailbox_start == '<'); + XP_ASSERT (*mailbox_end == '>'); + + /* First, copy the name. + */ + name_out = name_start; + s = this_start; + /* Skip leading whitespace. */ + while (XP_IS_SPACE (*s) && s < mailbox_start) + s++; + /* Copy up to (not including) the < */ + while (s < mailbox_start) + { + if (*s == '\"') + { + s++; + continue; + } + if (*s == '\\') + { + if (s + 1 < mailbox_start && (*(s+1) == '\\' || *(s+1) == '\"')) + *name_out++ = *s++; + else + s++; + } + if (XP_IS_SPACE (*s) && + name_out > name_start && + XP_IS_SPACE (name_out[-1])) + /* collapse consecutive whitespace */; + else + *name_out++ = *s; + s++; + } + /* Trim trailing whitespace. */ + while (name_out > name_start && XP_IS_SPACE (name_out[-1])) + name_out--; + /* Push out one space. */ + *name_out++ = ' '; + s = mailbox_end+1; + /* Skip whitespace after > */ + while (XP_IS_SPACE (*s) && s < line_end) + s++; + /* Copy from just after > to the end. */ + while (s < line_end) + { + if (*s == '\"') + { + s++; + continue; + } + if (*s == '\\') + { + if (s + 1 < line_end && (*(s+1) == '\\' || *(s+1) == '\"')) + *name_out++ = *s++; + else + s++; + } + if (XP_IS_SPACE (*s) && + name_out > name_start && + XP_IS_SPACE (name_out[-1])) + /* collapse consecutive whitespace */; + else + *name_out++ = *s; + s++; + } + /* Trim trailing whitespace. */ + while (name_out > name_start && XP_IS_SPACE (name_out[-1])) + name_out--; + /* null-terminate. */ + *name_out++ = 0; + + /* Now, copy the address. + */ + mailbox_start++; + addr_out = addr_start; + s = mailbox_start; + /* Skip leading whitespace. */ + while (XP_IS_SPACE (*s) && s < mailbox_end) + s++; + /* Copy up to (not including) the > */ + while (s < mailbox_end) + { + if (*s == '\"') + { + s++; + continue; + } + if (*s == '\\') + { + if (s + 1 < mailbox_end && (*(s+1) == '\\' || *(s+1) == '\"')) + *addr_out++ = *s++; + else + s++; + } + *addr_out++ = *s++; + } + /* Trim trailing whitespace. */ + while (addr_out > addr_start && XP_IS_SPACE (addr_out[-1])) + addr_out--; + /* null-terminate. */ + *addr_out++ = 0; + } + else /* No component of form. */ + { + /* Trim trailing whitespace. */ + while (addr_out > addr_start && XP_IS_SPACE (addr_out[-1])) + addr_out--; + /* null-terminate. */ + *addr_out++ = 0; + + /* Trim trailing whitespace. */ + while (name_out > name_start && XP_IS_SPACE (name_out[-1])) + name_out--; + /* null-terminate. */ + *name_out++ = 0; + + /* Attempt to deal with the simple error case of a missing comma. + We can only really deal with this in the non-<> case. + If there is no name, and if the address doesn't contain + double-quotes, but the address does contain whitespace, + then assume that the whitespace is an address delimiter. + */ + if (!name_start || !*name_start) + { + char *s; + char *space = 0; + for (s = addr_start; s < addr_out; s++) + if (*s == '\\') + s++; + else if (!space && XP_IS_SPACE (*s)) + space = s; + else if (*s == '"') + { + space = 0; + break; + } + if (space) + { + for (s = space; s < addr_out; s++) + if (*s == '\\') + s++; + else if (XP_IS_SPACE (*s)) + { + *s = 0; + *name_out++ = 0; + addr_count++; + } + } + } + } + + /* Now re-quote the names and addresses if necessary. + */ + if (quote_names_p && names) + { + int L = name_out - name_start - 1; + L = msg_quote_phrase_or_addr (name_start, L, FALSE); + name_out = name_start + L + 1; + } + + if (quote_addrs_p && addresses) + { + int L = addr_out - addr_start - 1; + L = msg_quote_phrase_or_addr (addr_start, L, TRUE); + addr_out = addr_start + L + 1; + } + + addr_count++; + + if (first_only_p) + /* If we only want the first address, we can stop now. */ + break; + + if (*line_end) + line_end++; + + /* Skip over extra whitespace or commas between addresses. */ + while (*line_end && + (XP_IS_SPACE (*line_end) || *line_end == ',')) + line_end++; + + this_start = line_end; + name_start = name_out; + addr_start = addr_out; + } + + /* Make one more pass through and convert all whitespace characters + to SPC. We could do that in the first pass, but this is simpler. */ + { + char *s; + for (s = name_buf; s < name_out; s++) + if (XP_IS_SPACE (*s) && *s != ' ') + *s = ' '; + for (s = addr_buf; s < addr_out; s++) + if (XP_IS_SPACE (*s) && *s != ' ') + *s = ' '; + } + + /* #### Should we bother realloc'ing them smaller? */ + + if (names) + *names = name_buf; + else + XP_FREE (name_buf); + + if (addresses) + *addresses = addr_buf; + else + XP_FREE (addr_buf); + + return addr_count; +} + + +extern "C" int +MSG_ParseRFC822Addresses (const char *line, + char **names, + char **addresses) +{ + return msg_parse_rfc822_addresses(line, names, addresses, TRUE, TRUE, FALSE); +} + + + +/* Given a single mailbox, this quotes the characters in it which need + to be quoted; it writes into `address' and returns a new length. + `address' is assumed to be long enough; worst case, its size will + be (N*2)+2. + */ +static int +msg_quote_phrase_or_addr (char *address, int32 length, XP_Bool addr_p) +{ + int quotable_count = 0, in_quote = 0; + int unquotable_count = 0; + int32 i, new_length; + char *in, *out; + XP_Bool atsign = FALSE; + XP_Bool user_quote = FALSE; + + /* If the entire address is quoted, fall out now. */ + if (address[0] == '"' && address[length - 1] == '"') + return length; + + for (i = 0, in = address; i < length; i++, in++) + { + if (*in == 0) + return length; /* #### horrible kludge... */ + + else if (addr_p && *in == '@' && !atsign && !in_quote) + { + /* Exactly one unquoted at-sign is allowed in an address. */ + atsign = TRUE; + + /* If address is of the form '"userid"@somewhere.com' don't quote + * the quotes around 'userid'. Also reset the quotable count, since + * any quotables we've seen are already inside quotes. + */ + if (address[0] == '"' && in > address + 2 && *(in - 1) == '"' && *(in - 2) != '\\') + unquotable_count -= 2, quotable_count = 0, user_quote = TRUE; + } + + else if (*in == '\\') + { + if (i + 1 < length && (*(in + 1) == '\\' || *(in + 1) == '"')) + /* If the next character is a backslash or quote, this backslash */ + /* is an escape backslash; ignore it and the next character. */ + i++, in++; + else + /* If the name contains backslashes or quotes, they must be escaped. */ + unquotable_count++; + } + + else if (*in == '"') + /* If the name contains quotes, they must be escaped. */ + unquotable_count++, in_quote = !in_quote; + + else if ( *in >= 127 || *in < 0 + || *in == '[' || *in == ']' || *in == '(' || *in == ')' + || *in == '<' || *in == '>' || *in == '@' || *in == ',' + || *in == ';' || *in == '$') + /* If the name contains control chars or RFC822 specials, it needs to + * be enclosed in quotes. Double-quotes and backslashes will be dealt + * with seperately. + * + * The ":" character is explicitly not in this list, though RFC822 says + * it should be quoted, because that has been seen to break VMS + * systems. (Rather, it has been seen that there are Unix SMTP servers + * which accept RCPT TO: but not RCPT TO:<"host::user"> or + * RCPT TO:, which is the syntax that VMS/DECNET hosts + * use. + * + * For future reference: it is also claimed that some VMS SMTP servers + * allow \ quoting but not "" quoting; and that sendmail uses self- + * contradcitory quoting conventions that violate both RFCs 821 and + * 822, so any address quoting on a sendmail system will lose badly. + */ + quotable_count++; + + else if (addr_p && *in == ' ') + /* Naked spaces are allowed in names, but not addresses. */ + quotable_count++; + + else if ( !addr_p + && (*in == '.' || *in == '!' || *in == '$' || *in == '%')) + /* Naked dots are allowed in addresses, but not in names. + * The other characters (!$%) are technically allowed in names, but + * are surely going to cause someone trouble, so we quote them anyway. + */ + quotable_count++; + } + + if (quotable_count == 0 && unquotable_count == 0) + return length; + + /* Add 2 to the length for the quotes, plus one for each character + * which will need a backslash as well. + */ + new_length = length + unquotable_count + 2; + + /* Now walk through the string backwards (so that we can use the same + * block.) First put on the terminating quote, then push out each + * character, backslashing as necessary. Then a final quote. + * Uh, except, put the second quote just before the last @ if there + * is one. + */ + out = address + new_length - 1; + in = address + length - 1; + if (!atsign || (user_quote && quotable_count > 0)) + *out-- = '"'; + while (out > address) + { + XP_ASSERT(in >= address); + + if (*in == '@' && user_quote && quotable_count > 0) + *out-- = '"'; + + *out-- = *in; + + if (*in == '@' && atsign && !user_quote) + { + *out-- = '"'; + atsign = FALSE; + } + else if (*in == '\\' || *in == '"') + { + if ( user_quote && *in == '"' + && ( in == address + || ( in < address + length - 1 && in > address + && *(in + 1) == '@' && *(in - 1) != '\\'))) + /* Do nothing */; + else if (in > address && *(in - 1) == '\\') + *out-- = *--in; + else + { + XP_ASSERT(out > address); + *out-- = '\\'; + } + } + in--; + } + XP_ASSERT(in == address - 1 || (user_quote && in == address)); + XP_ASSERT(out == address); + *out = '"'; + address[new_length] = 0; + return new_length; +} + +/* Given a name or address that might have been quoted + it will take out the escape and double quotes + The caller is responsible for freeing the resulting + string. + */ +extern "C" int +MSG_UnquotePhraseOrAddr (char *line, char** lineout) +{ + int outlen = 0; + char *lineptr = NULL; + char *tmpLine = NULL; + char *outptr = NULL; + int result = 0; + + (*lineout) = NULL; + if (line) { + /* if the first character isnt a double quote + then there is nothing to do */ + if (*line != '"') + { + (*lineout) = XP_STRDUP (line); + if (!lineout) + return -1; + else + return 0; + } + + /* dont count the first character that is the double quote */ + lineptr = line; + lineptr++; + /* count up how many characters we are going to output */ + while (*lineptr) { + /* if the character is an '\' then + output the escaped character */ + if (*lineptr == '\\') + lineptr++; + outlen++; + lineptr++; + } + tmpLine = (char *) XP_ALLOC (outlen + 1); + if (!tmpLine) + return -1; + XP_MEMSET(tmpLine, 0, outlen); + /* dont output the first double quote */ + line++; + lineptr = line; + outptr = (tmpLine); + while ((*lineptr) != '\0') { + /* if the character is an '\' then + output the character that was escaped */ + /* if it was part of the quote then don't + output it */ + if (*lineptr == '\\' || *lineptr == '"') { + lineptr++; + } + *outptr = *lineptr; + if (*lineptr != '\0') { + outptr++; + lineptr++; + } + } + *outptr = '\0'; + if (tmpLine) + (*lineout) = XP_STRDUP (tmpLine); + else + result = -1; + XP_FREEIF (tmpLine); + } + return result; +} +/* Given a string which contains a list of RFC822 addresses, returns a + comma-seperated list of just the `mailbox' portions. + */ +extern "C" char * +MSG_ExtractRFC822AddressMailboxes (const char *line) +{ + char *addrs = 0; + char *result, *s, *out; + uint32 i, size = 0; + int status = MSG_ParseRFC822Addresses (line, 0, &addrs); + if (status <= 0) + return 0; + + s = addrs; + for (i = 0; (int) i < status; i++) + { + uint32 j = XP_STRLEN (s); + s += j + 1; + size += j + 2; + } + + result = (char*)XP_ALLOC (size + 1); + if (! result) + { + XP_FREE (addrs); + return 0; + } + out = result; + s = addrs; + for (i = 0; (int) i < status; i++) + { + uint32 j = XP_STRLEN (s); + XP_MEMCPY (out, s, j); + out += j; + if ((int) (i+1) < status) + { + *out++ = ','; + *out++ = ' '; + } + s += j + 1; + } + *out = 0; + + XP_FREE (addrs); + return result; +} + + +/* Given a string which contains a list of RFC822 addresses, returns a + comma-seperated list of just the `user name' portions. If any of + the addresses doesn't have a name, then the mailbox is used instead. + + The names are *unquoted* and therefore cannot be re-parsed in any way. + They are, however, nice and human-readable. + */ +extern "C" char * +MSG_ExtractRFC822AddressNames (const char *line) +{ + char *names = 0; + char *addrs = 0; + char *result, *s1, *s2, *out; + uint32 i, size = 0; + int status = msg_parse_rfc822_addresses(line, &names, &addrs, FALSE, FALSE, + FALSE); + if (status <= 0) + return 0; + + s1 = names; + s2 = addrs; + for (i = 0; (int) i < status; i++) + { + uint32 j1 = XP_STRLEN (s1); + uint32 j2 = XP_STRLEN (s2); + s1 += j1 + 1; + s2 += j2 + 1; + size += (j1 ? j1 : j2) + 2; + } + + result = (char*)XP_ALLOC (size + 1); + if (! result) + { + XP_FREE (names); + XP_FREE (addrs); + return 0; + } + out = result; + s1 = names; + s2 = addrs; + for (i = 0; (int) i < status; i++) + { + uint32 j1 = XP_STRLEN (s1); + uint32 j2 = XP_STRLEN (s2); + + if (j1) + { + XP_MEMCPY (out, s1, j1); + out += j1; + } + else + { + XP_MEMCPY (out, s2, j2); + out += j2; + } + + if ((int) (i+1) < status) + { + *out++ = ','; + *out++ = ' '; + } + s1 += j1 + 1; + s2 += j2 + 1; + } + *out = 0; + + XP_FREE (names); + XP_FREE (addrs); + return result; +} + +/* Like MSG_ExtractRFC822AddressNames(), but only returns the first name + in the list, if there is more than one. + */ +extern "C" char * +MSG_ExtractRFC822AddressName (const char *line) +{ + char *name = 0; + char *addr = 0; + int status = msg_parse_rfc822_addresses(line, &name, &addr, FALSE, FALSE, + TRUE); + if (status <= 0) + return 0; + /* This can happen if there is an address like "From: foo bar" which + we parse as two addresses (that's a syntax error.) In that case, + we'll return just the first one (the rest is after the NULL.) + XP_ASSERT(status == 1); + */ + if (name && *name) + { + FREEIF(addr); + return name; + } + else + { + FREEIF(name); + return addr; + } +} + + +static char * +msg_format_rfc822_addresses (const char *names, const char *addrs, + int count, XP_Bool wrap_lines_p) +{ + char *result, *out; + const char *s1, *s2; + uint32 i, size = 0; + uint32 column = 10; + + if (count <= 0) + return 0; + + s1 = names; + s2 = addrs; + for (i = 0; (int) i < count; i++) + { + uint32 j1 = XP_STRLEN (s1); + uint32 j2 = XP_STRLEN (s2); + s1 += j1 + 1; + s2 += j2 + 1; + size += j1 + j2 + 10; + } + + result = (char *) XP_ALLOC (size + 1); + if (! result) return 0; + + out = result; + s1 = names; + s2 = addrs; + + for (i = 0; (int) i < count; i++) + { + char *o; + uint32 j1 = XP_STRLEN (s1); + uint32 j2 = XP_STRLEN (s2); + + if (wrap_lines_p && i > 0 && + (column + j1 + j2 + 3 + + (((int) (i+1) < count) ? 2 : 0) + > 76)) + { + if (out > result && out[-1] == ' ') + out--; + *out++ = CR; + *out++ = LF; + *out++ = '\t'; + column = 8; + } + + o = out; + + if (j1) + { + XP_MEMCPY (out, s1, j1); + out += j1; + *out++ = ' '; + *out++ = '<'; + } + XP_MEMCPY (out, s2, j2); + out += j2; + if (j1) + *out++ = '>'; + + if ((int) (i+1) < count) + { + *out++ = ','; + *out++ = ' '; + } + s1 += j1 + 1; + s2 += j2 + 1; + + column += (out - o); + } + *out = 0; + return result; +} + +/* Given a string which contains a list of RFC822 addresses, returns a new + string with the same data, but inserts missing commas, parses and reformats + it, and wraps long lines with newline-tab. + */ +extern "C" char * +MSG_ReformatRFC822Addresses (const char *line) +{ + char *names = 0; + char *addrs = 0; + char *result; + int status = MSG_ParseRFC822Addresses (line, &names, &addrs); + if (status <= 0) + return 0; + result = msg_format_rfc822_addresses (names, addrs, status, TRUE); + XP_FREE (names); + XP_FREE (addrs); + return result; +} + +/* Returns a copy of ADDRS which may have had some addresses removed. + Addresses are removed if they are already in either ADDRS or OTHER_ADDRS. + (If OTHER_ADDRS contain addresses which are not in ADDRS, they are not + added. That argument is for passing in addresses that were already + mentioned in other header fields.) + + Addresses are considered to be the same if they contain the same mailbox + part (case-insensitive.) Real names and other comments are not compared. + + removeAliasesToMe allows the address parser to use the preference which + contains regular expressions which also mean 'me' for the purpose of + stripping the user's email address(es) out of addrs + */ +extern "C" char * +MSG_RemoveDuplicateAddresses (const char *addrs, + const char *other_addrs, + XP_Bool removeAliasesToMe) +{ + /* This is probably way more complicated than it should be... */ + char *s1 = 0, *s2 = 0; + char *output = 0, *out = 0; + char *result = 0; + int count1 = 0, count2 = 0, count3 = 0; + int size1 = 0, size2 = 0, size3 = 0; + char *names1 = 0, *names2 = 0; + char *addrs1 = 0, *addrs2 = 0; + char **a_array1 = 0, **a_array2 = 0, **a_array3 = 0; + char **n_array1 = 0, **n_array3 = 0; + int i, j; + + if (!addrs) return 0; + + count1 = MSG_ParseRFC822Addresses (addrs, &names1, &addrs1); + if (count1 < 0) goto FAIL; + if (count1 == 0) + { + result = XP_STRDUP(""); + goto FAIL; + } + if (other_addrs) + count2 = MSG_ParseRFC822Addresses (other_addrs, &names2, &addrs2); + if (count2 < 0) goto FAIL; + + s1 = names1; + s2 = addrs1; + for (i = 0; i < count1; i++) + { + uint32 j1 = XP_STRLEN (s1); + uint32 j2 = XP_STRLEN (s2); + s1 += j1 + 1; + s2 += j2 + 1; + size1 += j1 + j2 + 10; + } + + s1 = names2; + s2 = addrs2; + for (i = 0; i < count2; i++) + { + uint32 j1 = XP_STRLEN (s1); + uint32 j2 = XP_STRLEN (s2); + s1 += j1 + 1; + s2 += j2 + 1; + size2 += j1 + j2 + 10; + } + + a_array1 = (char **) XP_ALLOC (count1 * sizeof(char *)); + if (!a_array1) goto FAIL; + n_array1 = (char **) XP_ALLOC (count1 * sizeof(char *)); + if (!n_array1) goto FAIL; + + if (count2 > 0) + { + a_array2 = (char **) XP_ALLOC (count2 * sizeof(char *)); + if (!a_array2) goto FAIL; + /* don't need an n_array2 */ + } + + a_array3 = (char **) XP_ALLOC (count1 * sizeof(char *)); + if (!a_array3) goto FAIL; + n_array3 = (char **) XP_ALLOC (count1 * sizeof(char *)); + if (!n_array3) goto FAIL; + + + /* fill in the input arrays */ + s1 = names1; + s2 = addrs1; + for (i = 0; i < count1; i++) + { + n_array1[i] = s1; + a_array1[i] = s2; + s1 += XP_STRLEN (s1) + 1; + s2 += XP_STRLEN (s2) + 1; + } + + s2 = addrs2; + for (i = 0; i < count2; i++) + { + a_array2[i] = s2; + s2 += XP_STRLEN (s2) + 1; + } + + /* Iterate over all addrs in the "1" arrays. + If those addrs are not present in "3" or "2", add them to "3". + */ + for (i = 0; i < count1; i++) /* iterate over all addrs */ + { + XP_Bool found = FALSE; + for (j = 0; j < count2; j++) + if (!strcasecomp (a_array1[i], a_array2[j])) + { + found = TRUE; + break; + } + + if (!found) + for (j = 0; j < count3; j++) + if (!strcasecomp (a_array1[i], a_array3[j])) + { + found = TRUE; + break; + } + + if (!found && removeAliasesToMe) + { + found = MSG_Prefs::IsEmailAddressAnAliasForMe (a_array1[i]); + if (found) + break; + } + + if (!found) + { + n_array3[count3] = n_array1[i]; + a_array3[count3] = a_array1[i]; + size3 += (XP_STRLEN(n_array3[count3]) + XP_STRLEN(a_array3[count3]) + + 10); + count3++; + XP_ASSERT (count3 <= count1); + if (count3 > count1) break; + } + } + + output = (char *) XP_ALLOC (size3 + 1); + if (!output) goto FAIL; + + *output = 0; + out = output; + s2 = output; + for (i = 0; i < count3; i++) + { + XP_STRCPY (out, a_array3[i]); + out += XP_STRLEN (out); + *out++ = 0; + } + s1 = out; + for (i = 0; i < count3; i++) + { + XP_STRCPY (out, n_array3[i]); + out += XP_STRLEN (out); + *out++ = 0; + } + result = msg_format_rfc822_addresses (s1, s2, count3, FALSE); + + FAIL: + FREEIF (a_array1); + FREEIF (a_array2); + FREEIF (a_array3); + FREEIF (n_array1); + FREEIF (n_array3); + FREEIF (names1); + FREEIF (names2); + FREEIF (addrs1); + FREEIF (addrs2); + FREEIF (output); + return result; +} + + +/* Given an e-mail address and a person's name, cons them together into a + single string of the form "name
", doing all the necessary quoting. + A new string is returned, which you must free when you're done with it. + */ +extern "C" char * +MSG_MakeFullAddress (const char* name, const char* addr) +{ + int nl = name ? XP_STRLEN (name) : 0; + int al = addr ? XP_STRLEN (addr) : 0; + char *buf, *s; + int L; + if (al == 0) + return 0; + buf = (char *) XP_ALLOC ((nl * 2) + (al * 2) + 20); + if (!buf) + return 0; + if (nl > 0) + { + XP_STRCPY (buf, name); + L = msg_quote_phrase_or_addr (buf, nl, FALSE); + s = buf + L; + *s++ = ' '; + *s++ = '<'; + } + else + { + s = buf; + } + + XP_STRCPY (s, addr); + L = msg_quote_phrase_or_addr (s, al, TRUE); + s += L; + if (nl > 0) + *s++ = '>'; + *s = 0; + L = (s - buf) + 1; + buf = (char *) XP_REALLOC (buf, L); + return buf; +} + +#if 0 +main (int argc, char **argv) +{ + fprintf (stderr, "%s\n", + MSG_RemoveDuplicateAddresses (argv[1], argv[2], FALSE)); +} +#endif + + +#if 0 +main (int argc, char **argv) +{ + fprintf (stderr, "%s\n", MSG_MakeFullAddress (argv[1], argv[2])); +} +#endif + + +#if 0 +/* Test cases for the above routines. + */ +static void +test1 (const char *line, XP_Bool np, XP_Bool ap, + uint32 expect_count, const char *expect_n, const char *expect_a) +{ + char *names = 0, *addrs = 0; + int result; + if (! np) expect_n = 0; + if (! ap) expect_a = 0; + result = MSG_ParseRFC822Addresses (line, + (np ? &names : 0), + (ap ? &addrs : 0)); + if (result <= 0) + printf (" #### error %d\n", result); + else + { + uint32 i; + char *n = names, *a = addrs; + if (expect_count != result) + printf (" #### wrong number of results (%d instead of %d)\n", + (int) result, (int) expect_count); + for (i = 0; i < result; i++) + { + if (((!!n) != (!!expect_n)) || + (n && XP_STRCMP (n, expect_n))) + { + printf (" ####### name got: %s\n" + " #### name wanted: %s\n", + (n ? n : ""), + (expect_n ? expect_n : "")); + } + if (((!!a) != (!!expect_a)) || + (a && XP_STRCMP (a, expect_a))) + { + printf (" ####### addr got: %s\n" + " #### addr wanted: %s\n", + (a ? a : ""), + (expect_a ? expect_a : "")); + } + if (n) n += XP_STRLEN (n) + 1; + if (a) a += XP_STRLEN (a) + 1; + if (expect_n) expect_n += XP_STRLEN (expect_n) + 1; + if (expect_a) expect_a += XP_STRLEN (expect_a) + 1; + } + } + FREEIF (names); + FREEIF (addrs); +} + +static void +test (const char *line, uint32 expect_n, + const char *expect_names, const char *expect_addrs, + const char *expect_all_names, const char *expect_all_addrs, + const char *canonical) +{ + char *s; + printf ("testing %s\n", line); + test1 (line, TRUE, TRUE, expect_n, expect_names, expect_addrs); + test1 (line, TRUE, FALSE, expect_n, expect_names, expect_addrs); + test1 (line, FALSE, TRUE, expect_n, expect_names, expect_addrs); + test1 (line, FALSE, FALSE, expect_n, expect_names, expect_addrs); + + s = MSG_ExtractRFC822AddressMailboxes (line); + if (!s || XP_STRCMP (s, expect_all_addrs)) + printf (" #### expected addrs: %s\n" + " ######### got addrs: %s\n", + expect_all_addrs, (s ? s : "")); + FREEIF (s); + + s = MSG_ExtractRFC822AddressNames (line); + if (!s || XP_STRCMP (s, expect_all_names)) + printf (" #### expected names: %s\n" + " ######### got names: %s\n", + expect_all_names, (s ? s : "")); + FREEIF (s); + + s = MSG_ReformatRFC822Addresses (line); + if (!s || XP_STRCMP (s, canonical)) + printf (" #### expected canonical: %s\n" + " ######### got canonical: %s\n", + canonical, (s ? s : "")); + FREEIF (s); +} + + +void +main () +{ + test ("spanky", + 1, "", "spanky", + "spanky", "spanky", + "spanky"); + + test ("", + 1, "", "spanky", + "spanky", "spanky", + "spanky"); + + test ("< spanky> ", + 1, "", "spanky", + "spanky", "spanky", + "spanky"); + + test ("Simple Case ", + 1, + "Simple Case", "simple1", + "Simple Case", "simple1", + "Simple Case "); + + test (" Simple Case < simple1 > ", + 1, + "Simple Case", "simple1", + "Simple Case", "simple1", + "Simple Case "); + + test ("simple2 (Simple Case)", + 1, + "Simple Case", "simple2", + "Simple Case", "simple2", + "Simple Case "); + + test ("simple3 (Slightly) (Trickier)", + 1, + "Slightly Trickier", "simple3", + "Slightly Trickier", "simple3", + "Slightly Trickier "); + + test ("(Slightly) simple3 (Trickier)", + 1, + "Slightly Trickier", "simple3", + "Slightly Trickier", "simple3", + "Slightly Trickier "); + + test ("( Slightly ) simple3 ( Trickier ) ", + 1, + "Slightly Trickier", "simple3", + "Slightly Trickier", "simple3", + "Slightly Trickier "); + + test ("(Even) more (Trickier\\, I say)", + 1, + "\"(Even) more (Trickier, I say)\"", "trickier", + "(Even) more (Trickier, I say)", "trickier", + "\"(Even) more (Trickier, I say)\" "); + + test ("\"this, is\" <\"some loser\"@address> (foo)", + 1, + "\"this, is (foo)\"", "\"some loser\"@address", + "this, is (foo)", "\"some loser\"@address", + "\"this, is (foo)\" <\"some loser\"@address>"); + + test ("foo, bar", + 2, + "" "\000" "", + "foo" "\000" "bar", + "foo, bar", "foo, bar", + "foo, bar"); + + test (", ", + 2, + "" "\000" "", + "foo" "\000" "bar", + "foo, bar", "foo, bar", + "foo, bar"); + + test ("< foo > , < bar > ", + 2, + "" "\000" "", + "foo" "\000" "bar", + "foo, bar", "foo, bar", + "foo, bar"); + + test ("< foo > , , , ,,,,, , < bar > ,", + 2, + "" "\000" "", + "foo" "\000" "bar", + "foo, bar", "foo, bar", + "foo, bar"); + + test ("\"this, is\" <\"some loser\"@address> (foo), ", + 2, + "\"this, is (foo)\"" "\000" "", + "\"some loser\"@address" "\000" "bar", + "this, is (foo), bar", + "\"some loser\"@address, bar", + "\"this, is (foo)\" <\"some loser\"@address>, bar"); + + test ("\"this, is\" (foo), bar", + 2, + "\"this, is (foo)\"" "\000" "", + "\"some loser\"@address" "\000" "bar", + "this, is (foo), bar", + "\"some loser\"@address, bar", + "\"this, is (foo)\" <\"some loser\"@address>, bar"); + + test ("(I'm a (total) loser) \"space address\"", + 1, + "\"I'm a (total) loser\"", "\"space address\"", + "I'm a (total) loser", "\"space address\"", + "\"I'm a (total) loser\" <\"space address\">"); + + test ("(I'm a (total) loser) \"space address\"@host", + 1, + "\"I'm a (total) loser\"", "\"space address\"@host", + "I'm a (total) loser", "\"space address\"@host", + "\"I'm a (total) loser\" <\"space address\"@host>"); + + test ("It\\'s \"me\"
, I'm a (total) loser <\"space address\">", + 2, + "It's me" "\000" "\"I'm a (total) loser\"", + "address" "\000" "\"space address\"", + "It's me, I'm a (total) loser", + "address, \"space address\"", + "It's me
, \"I'm a (total) loser\" <\"space address\">"); + + test("It\\'s \"me\"
, I'm a (total) loser <\"space address\"@host>", + 2, + "It's me" "\000" "\"I'm a (total) loser\"", + "address" "\000" "\"space address\"@host", + "It's me, I'm a (total) loser", + "address, \"space address\"@host", + "It's me
, \"I'm a (total) loser\" <\"space address\"@host>"); + + test ("(It\\'s \"me\") address, (I'm a (total) loser) \"space address\"", + 2, + "It's me" "\000" "\"I'm a (total) loser\"", + "address" "\000" "\"space address\"", + "It's me, I'm a (total) loser", + "address, \"space address\"", + "It's me
, \"I'm a (total) loser\" <\"space address\">"); + + test ("(It\\'s \"me\") address, (I'm a (total) loser) \"space \\\"address\"", + 2, + "It's me" "\000" "\"I'm a (total) loser\"", + "address" "\000" "\"space \\\"address\"", + "It's me, I'm a (total) loser", + "address, \"space \\\"address\"", + "It's me
, \"I'm a (total) loser\" <\"space \\\"address\">"); + + test ("(It\\'s \"me\") address, (I'm a (total) loser) \"space @address\"@host", + 2, + "It's me" "\000" "\"I'm a (total) loser\"", + "address" "\000" "\"space @address\"@host", + "It's me, I'm a (total) loser", + "address, \"space @address\"@host", + "It's me
, \"I'm a (total) loser\" <\"space @address\"@host>"); + + test ("Probably Bogus ", + 1, + "Probably Bogus", + "\"some@loser\"@somewhere", + "Probably Bogus", + "\"some@loser\"@somewhere", + "Probably Bogus <\"some@loser\"@somewhere>"); + + test ("Probably Bogus <\"some$loser,666\"@somewhere>", + 1, + "Probably Bogus", + "\"some$loser,666\"@somewhere", + "Probably Bogus", + "\"some$loser,666\"@somewhere", + "Probably Bogus <\"some$loser,666\"@somewhere>"); + + test ("Probably Bogus <\"some$loser,666\"@somewhere>", + 1, + "Probably Bogus", + "\"some$loser,666\"@somewhere", + "Probably Bogus", + "\"some$loser,666\"@somewhere", + "Probably Bogus <\"some$loser,666\"@somewhere>"); + + test ("\"Probably Bogus, Jr.\" <\"some$loser,666\"@somewhere>", + 1, + "\"Probably Bogus, Jr.\"", + "\"some$loser,666\"@somewhere", + "Probably Bogus, Jr.", + "\"some$loser,666\"@somewhere", + "\"Probably Bogus, Jr.\" <\"some$loser,666\"@somewhere>"); + + test ("Probably Bogus\\, Jr. <\"some$loser,666\"@somewhere>", + 1, + "\"Probably Bogus, Jr.\"", + "\"some$loser,666\"@somewhere", + "Probably Bogus, Jr.", + "\"some$loser,666\"@somewhere", + "\"Probably Bogus, Jr.\" <\"some$loser,666\"@somewhere>"); + + test ("This isn't legal ", + 1, + "This isn't legal", "\"some$loser,666\"@somewhere", + "This isn't legal", "\"some$loser,666\"@somewhere", + "This isn't legal <\"some$loser,666\"@somewhere>"); + + test ("This isn't legal!! ", + 1, + "\"This isn't legal!!\"", "\"some$loser,666\"@somewhere", + "This isn't legal!!", "\"some$loser,666\"@somewhere", + "\"This isn't legal!!\" <\"some$loser,666\"@somewhere>"); + + test ("addr1, addr2, \n\taddr3", + 3, + "" "\000" "" "\000" "", + "addr1" "\000" "addr2" "\000" "addr3", + "addr1, addr2, addr3", + "addr1, addr2, addr3", + "addr1, addr2, addr3"); + + test ("addr1 addr2 addr3", + 3, + "" "\000" "" "\000" "", + "addr1" "\000" "addr2" "\000" "addr3", + "addr1, addr2, addr3", + "addr1, addr2, addr3", + "addr1, addr2, addr3"); + + test (" addr1 addr2 addr3 ,,,,,, ", + 3, + "" "\000" "" "\000" "", + "addr1" "\000" "addr2" "\000" "addr3", + "addr1, addr2, addr3", + "addr1, addr2, addr3", + "addr1, addr2, addr3"); + + test ("addr1, addr2 \n\t addr3", + 3, + "" "\000" "" "\000" "", + "addr1" "\000" "addr2" "\000" "addr3", + "addr1, addr2, addr3", + "addr1, addr2, addr3", + "addr1, addr2, addr3"); + + test ("addr1, addr2, addr3 addr4, , (and) addr6 (yeah)", + 6, + "" "\000" "" "\000" "" "\000" "" "\000" "" + "\000" "and yeah", + "addr1" "\000" "addr2" "\000" "addr3" "\000" "addr4" "\000" "addr5" + "\000" "addr6", + "addr1, addr2, addr3, addr4, addr5, and yeah", + "addr1, addr2, addr3, addr4, addr5, addr6", + "addr1, addr2, addr3, addr4, addr5, and yeah "); + + test ("addr1 (and some (nested) parens), addr2 , addr2"); + + test ("addr1))) ((()))()()()()()()())), addr2 addr3, addr4 (foo, bar)", + 4, + "\"(())\"" "\000" "" "\000" "" "\000" "\"foo, bar\"", + "\"addr1))) ))\"" "\000" "addr2" "\000" "addr3" "\000" "addr4", + "(()), addr2, addr3, foo, bar", + "\"addr1))) ))\", addr2, addr3, addr4", + "\"(())\" <\"addr1))) ))\">, addr2, addr3, \"foo, bar\" "); + + test ("avec le quoted quotes <\"a \\\" quote\">", + 1, + "avec le quoted quotes", "\"a \\\" quote\"", + "avec le quoted quotes", "\"a \\\" quote\"", + "avec le quoted quotes <\"a \\\" quote\">"); + + test ("avec le quoted quotes <\"a \\\" quote\"@host>", + 1, + "avec le quoted quotes", "\"a \\\" quote\"@host", + "avec le quoted quotes", "\"a \\\" quote\"@host", + "avec le quoted quotes <\"a \\\" quote\"@host>"); + + /* bang paths work, right? */ + test ("nelsonb ", + 1, + "nelsonb", "abit.com!nelsonb@netscape.com", + "nelsonb", "abit.com!nelsonb@netscape.com", + "nelsonb "); + +# if 0 /* these tests don't pass, but should. */ + + /* a perverse example from RFC822: */ + test ("Muhammed.(I am the greatest) Ali @(the)Vegas.WBA", + 1, + "I am the greatest", "Muhammed.Ali@Vegas.WBA", + "I am the greatest", "Muhammed.Ali@Vegas.WBA", + "I am the greatest "); + + /* Oops, this should work but doesn't. */ + test ("nelsonb <@abit.com.tw:nelsonb@netscape.com>", + 1, + "nelsonb", "@abit.com.tw:nelsonb@netscape.com", + "nelsonb", "@abit.com.tw:nelsonb@netscape.com", + "nelsonb <@abit.com.tw:nelsonb@netscape.com>"); + + test ("(Sat43Jan@cyberpromo.com) ((#$&'*+-/=?^_`|~)) ((\\)))", + 1, + "(Sat43Jan@cyberpromo.com)", "\"Ronald.F.Guilmette#$&'*+-/=?^_`|~\"@monkeys.com", + "(Sat43Jan@cyberpromo.com)", "\"Ronald.F.Guilmette#$&'*+-/=?^_`|~\"@monkeys.com", + "(Sat43Jan@cyberpromo.com) <\"Ronald.F.Guilmette#$&'*+-/=?^_`|~\"@monkeys.com>"); + + /* Intentionally fail this one */ + test("my . name @ my . host . com", + 1, + "", "my.name@my.host.com", + "", "my.name@my.host.com", + ""); + + /* but this one should work */ + test("loser < my . name @ my . host . com > ", + 1, + "loser", "my.name@my.host.com", + "loser", "my.name@my.host.com", + "loser "); + + test("my(@).(@)name(<)@(>)my(:).(;)host(((\\)))).(@)com", + 1, + "@", "my.name@my.host.com", + "@", "my.name@my.host.com", + "\"@\" "); + +# endif /* 0 */ + + exit (0); +} +#endif /* 0 */ diff --git a/mozilla/lib/mailto/ap_decod.c b/mozilla/lib/mailto/ap_decod.c new file mode 100644 index 00000000000..3a4e150b58b --- /dev/null +++ b/mozilla/lib/mailto/ap_decod.c @@ -0,0 +1,1776 @@ +/* -*- 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. + */ + +/* + * + * apple_double_decode.c + * + * --------------------- + * + * Codes for decoding Apple Single/Double object parts. + * + * 05aug95 mym Created. + * 25sep95 mym Add support to write to binhex encoding on non-mac system. + */ + +#include "msg.h" +#include "appledbl.h" +#include "ad_codes.h" +#include "m_binhex.h" +#ifdef XP_MAC +#include +#endif + +extern int MK_UNABLE_TO_OPEN_TMP_FILE; +extern int MK_MIME_ERROR_WRITING_FILE; + +/* +** Static functions. +*/ +PRIVATE int from_decoder(appledouble_decode_object* p_ap_decode_obj, + char *buff, + int buff_size, + uint32 *in_count); +PRIVATE int from_64(appledouble_decode_object* p_ap_decode_obj, + char *buff, + int size, + uint32 *real_size); +PRIVATE int from_qp(appledouble_decode_object* p_ap_decode_obj, + char *buff, + int size, + uint32 *real_size); +PRIVATE int from_uu(appledouble_decode_object* p_ap_decode_obj, + char *buff, + int size, + uint32 *real_size); +PRIVATE int from_none(appledouble_decode_object* p_ap_decode_obj, + char *buff, + int size, + uint32 *real_size); + +PRIVATE int decoder_seek(appledouble_decode_object* p_ap_decode_obj, + int seek_pos, + int start_pos); + +/* +** fetch_a_line +** ------------- +** +** get a line from the in stream.. +*/ +int fetch_a_line( + appledouble_decode_object* p_ap_decode_obj, + char *buff) +{ + int i, left; + char *p, c = 0; + + if (p_ap_decode_obj->s_leftover == 0 && + p_ap_decode_obj->s_inbuff <= p_ap_decode_obj->pos_inbuff) + { + *buff = '\0'; + return errEOB; + } + + if (p_ap_decode_obj->s_leftover) + { + for (p = p_ap_decode_obj->b_leftover, i = p_ap_decode_obj->s_leftover; i>0; i--) + *buff++ = *p++; + + p_ap_decode_obj->s_leftover = 0; + } + + p = p_ap_decode_obj->inbuff + p_ap_decode_obj->pos_inbuff; + left = p_ap_decode_obj->s_inbuff - p_ap_decode_obj->pos_inbuff; + + for (i = 0; i < left; ) + { + c = *p++; i++; + + if (c == CR && *p == LF) + { + p++; i++; /* make sure skip both LF & CR */ + } + + if (c == LF || c == CR) + break; + + *buff++ = c; + } + p_ap_decode_obj->pos_inbuff += i; + + if (i == left && c != LF && c != CR) + { + /* + ** we meet the buff end before we can terminate the string, + ** save the string to the left_over buff. + */ + p_ap_decode_obj->s_leftover = i; + + for (p = p_ap_decode_obj->b_leftover; i>0; i--) + *p++ = *(buff-i); + + return errEOB; + } + *buff = '\0'; + return NOERR; +} + +void parse_param( + char *p, + char **param, /* the param */ + char **define, /* the defination. */ + char **np) /* next position. */ +{ + while (*p == ' ' || *p == '\"' || *p == ';') p++; + *param = p; + + while (*p != ' ' && *p != '=' ) p++; + if (*p == ' ') + *define = p+1; + else + *define = p+2; + + while (*p && *p != ';') p++; + + if (*p == ';') + *np = p + 1; + else + *np = p; +} + +int ap_seek_part_start( + appledouble_decode_object* p_ap_decode_obj) +{ + int status; + char newline[256]; + + while (1) + { + status = fetch_a_line(p_ap_decode_obj, newline); + if(status != NOERR) + break; + + if (newline[0] == '\0' && p_ap_decode_obj->boundary0 != NULL) + return errDone; + + if (!XP_STRNCASECMP(newline, "--", 2)) + { + /* we meet the start seperator, copy it and it will be our boundary */ + p_ap_decode_obj->boundary0 = XP_STRDUP(newline+2); + return errDone; + } + } + return status; +} + +int ParseFileHeader( + appledouble_decode_object *p_ap_decode_obj) +{ + int status; + int i; + char newline[256], *p; + char *param, *define; + + while (1) + { + status = fetch_a_line(p_ap_decode_obj, newline); + if (newline[0] == '\0') + break; /* we get the end of a defination section. */ + + p = newline; + while (1) + { + parse_param(p, ¶m, &define, &p); + /* + ** we only care about these params. + */ + if (!XP_STRNCASECMP(param, "Content-Type:", 13)) + { + if (!XP_STRNCASECMP(define, MULTIPART_APPLEDOUBLE, + XP_STRLEN(MULTIPART_APPLEDOUBLE)) || + !XP_STRNCASECMP(define, MULTIPART_HEADER_SET, + XP_STRLEN(MULTIPART_HEADER_SET))) + p_ap_decode_obj->messagetype = kAppleDouble; + else + p_ap_decode_obj->messagetype = kGeneralMine; + } + else if (!XP_STRNCASECMP(param, "boundary=", 9)) + { + for (i = 0; *define && *define != '\"'; ) + p_ap_decode_obj->boundary0[i++] = *define++; + + p_ap_decode_obj->boundary0[i] = '\0'; + } + else if (!XP_STRNCASECMP(param, "Content-Disposition:", 20)) + { + if (!XP_STRNCASECMP(define, "inline", 5)) + p_ap_decode_obj->deposition = kInline; + else + p_ap_decode_obj->deposition = kDontCare; + } + else if (!XP_STRNCASECMP(param, "filename=", 9)) + { + for (i = 0, p=define; *p && *p != '\"'; ) + p_ap_decode_obj->fname[i++] = *p++; + + p_ap_decode_obj->fname[i] = '\0'; + } + + if (*p == '\0') + break; + } + } + + return NOERR; +} + +int ap_seek_to_boundary( + appledouble_decode_object *p_ap_decode_obj, + XP_Bool firstime) +{ + int status = NOERR; + char buff[256]; + + while (status == NOERR) + { + status = fetch_a_line(p_ap_decode_obj, buff); + if (status != NOERR) + break; + + if ((!XP_STRNCASECMP(buff, "--", 2) && + !XP_STRNCASECMP( buff+2, + p_ap_decode_obj->boundary0, + XP_STRLEN(p_ap_decode_obj->boundary0))) + ||!XP_STRNCASECMP( buff, + p_ap_decode_obj->boundary0, + XP_STRLEN(p_ap_decode_obj->boundary0))) + { + TRACEMSG(("Found boundary: %s", p_ap_decode_obj->boundary0)); + status = errDone; + break; + } + } + + if (firstime && status == errEOB) + status = NOERR; /* so we can do it again. */ + + return status; +} + +int ap_parse_header( + appledouble_decode_object *p_ap_decode_obj, + XP_Bool firstime) +{ + int status, i; + char newline[256], *p; + char *param, *define; + + if (firstime) + { + /* do the clean ups. */ + p_ap_decode_obj->encoding = kEncodeNone; + p_ap_decode_obj->which_part = kFinishing; + } + + while (1) + { + status = fetch_a_line(p_ap_decode_obj, newline); + if (status != NOERR) + return status; /* a possible end of buff happened. */ + + if (newline[0] == '\0') + break; /* we get the end of a defination section. */ + + p = newline; + while (1) + { + parse_param(p, ¶m, &define, &p); + /* + ** we only care about these params. + */ + if (!XP_STRNCASECMP(param, "Content-Type:", 13)) + { + if (!XP_STRNCASECMP(define, "application/applefile", 21)) + p_ap_decode_obj->which_part = kHeaderPortion; + else + { + p_ap_decode_obj->which_part = kDataPortion; + if (!XP_STRNCASECMP(define, "text/plain", 10)) + p_ap_decode_obj->is_binary = FALSE; + else + p_ap_decode_obj->is_binary = TRUE; + } + + /* Broken QuickMail messages */ + if (!XP_STRNCASECMP(define, "x-uuencode-apple-single", 23)) + p_ap_decode_obj->encoding = kEncodeUU; + } + else if (!XP_STRNCASECMP(param, "Content-Transfer-Encoding:",26)) + { + if (!XP_STRNCASECMP(define, "base64", 6)) + p_ap_decode_obj->encoding = kEncodeBase64; + else if (!XP_STRNCASECMP(define, "quoted-printable", 16)) + p_ap_decode_obj->encoding = kEncodeQP; + else + p_ap_decode_obj->encoding = kEncodeNone; + } + else if (!XP_STRNCASECMP(param, "Content-Disposition:", 20)) + { + if (!XP_STRNCASECMP(define, "inline", 5)) + p_ap_decode_obj->deposition = kInline; + else + p_ap_decode_obj->deposition = kDontCare; + } + else if (!XP_STRNCASECMP(param, "filename=", 9)) + { + if (p_ap_decode_obj->fname[0] == '\0') + { + for (i = 0; *define && *define != '\"'; ) + p_ap_decode_obj->fname[i++] = *define++; + + p_ap_decode_obj->fname[i] = '\0'; + } + } + + if (*p == '\0') + break; + } + } + return errDone; +} + + +/* +** decode the head portion. +*/ + + +int ap_decode_file_infor(appledouble_decode_object *p_ap_decode_obj) +{ + ap_header head; + ap_entry entries[NUM_ENTRIES + 1]; + int i, j; + int st_pt; + uint32 in_count; + int status; + char name[256]; + XP_Bool positionedAtRFork = FALSE; + + st_pt = p_ap_decode_obj->pos_inbuff; + + /* + ** Read & verify header + */ + status = from_decoder( + p_ap_decode_obj, + (char *) &head, + 26, /* sizeof (head), */ + &in_count); + if (status != NOERR) + return status; + + if (p_ap_decode_obj->is_apple_single) + { + if (ntohl(head.magic) != APPLESINGLE_MAGIC) + return errVersion; + } + else + { + if(ntohl(head.magic) != APPLEDOUBLE_MAGIC) + return errVersion; + } + + if (ntohl(head.version) != VERSION) + { + return errVersion; + } + + /* read entries */ + head.entries = ntohs(head.entries); + for (i = j = 0; i < head.entries; ++i) + { + status = from_decoder( + p_ap_decode_obj, + (char *) (entries + j), + sizeof (ap_entry), + &in_count); + if (status != NOERR) + return errDecoding; + + /* + ** correct the byte order now. + */ + entries[j].id = ntohl(entries[j].id); + entries[j].offset = ntohl(entries[j].offset); + entries[j].length = ntohl(entries[j].length); + /* + ** only care about these entries... + */ + if (j < NUM_ENTRIES) + switch (entries[j].id) + { + case ENT_NAME: + case ENT_FINFO: + case ENT_DATES: + case ENT_COMMENT: + case ENT_RFORK: + case ENT_DFORK: + ++j; + break; + } + } + + in_count = XP_STRLEN(p_ap_decode_obj->fname); + + /* if the user has not provided the output file name, read it + * from the ENT_NAME entry + */ + + if (in_count == 0) + { + /* read name */ + for (i = 0; i < j && entries[i].id != ENT_NAME; ++i) + ; + if (i == j) + return errDecoding; + + status = decoder_seek( + p_ap_decode_obj, + entries[i].offset, + st_pt); + if (status != NOERR) + return status; + + if (entries[i].length > 63) + entries[i].length = 63; + + status = from_decoder( + p_ap_decode_obj, + p_ap_decode_obj->fname, + entries[i].length, + &in_count); + if (status != NOERR) + return status; + + p_ap_decode_obj->fname[in_count] = '\0'; + } + + /* P_String version of the file name. */ + XP_STRCPY((char *)name+1, p_ap_decode_obj->fname); + name[0] = (char) in_count; + + if (p_ap_decode_obj->write_as_binhex) + { + /* + ** fill out the simple the binhex head. + */ + binhex_header head; + myFInfo myFInfo; + + status = (*p_ap_decode_obj->binhex_stream->put_block) + (p_ap_decode_obj->binhex_stream->data_object, + name, + name[0] + 2); + if (status != NOERR) + return status; + + /* get finder info */ + for (i = 0; i < j && entries[i].id != ENT_FINFO; ++i) + ; + if (i < j) + { + status = decoder_seek(p_ap_decode_obj, + entries[i].offset, + st_pt); + if (status != NOERR) + return status; + + status = from_decoder(p_ap_decode_obj, + (char *) &myFInfo, + sizeof (myFInfo), + &in_count); + if (status != NOERR) + return status; + } + + head.type = myFInfo.fdType; + head.creator = myFInfo.fdCreator; + head.flags = myFInfo.fdFlags; + + for (i = 0; i < j && entries[i].id != ENT_DFORK; ++i) + ; + if (i < j && entries[i].length != 0) + { + head.dlen = entries[i].length; /* set the data fork length */ + } + else + { + head.dlen = 0; + } + + for (i = 0; i < j && entries[i].id != ENT_RFORK; ++i) + ; + if (i < j && entries[i].length != 0) + { + head.rlen = entries[i].length; /* set the resource fork length */ + } + else + { + head.rlen = 0; + } + + /* + ** and the dlen, rlen is in the host byte order, correct it if needed ... + */ + head.dlen = htonl(head.dlen); + head.rlen = htonl(head.rlen); + /* + ** then encode them in binhex. + */ + status = (*p_ap_decode_obj->binhex_stream->put_block) + (p_ap_decode_obj->binhex_stream->data_object, + (char*)&head, + sizeof(binhex_header)); + if (status != NOERR) + return status; + + /* + ** after we have done with the header, end the binhex header part. + */ + status = (*p_ap_decode_obj->binhex_stream->put_block) + (p_ap_decode_obj->binhex_stream->data_object, + NULL, + 0); + } + else + { + +#ifdef XP_MAC + + ap_dates dates; + HFileInfo *fpb; + CInfoPBRec cipbr; + IOParam vinfo; + GetVolParmsInfoBuffer vp; + DTPBRec dtp; + char comment[256]; + + unsigned char filename[256]; /* this is a pascal string - should be unsigned char. */ + StandardFileReply reply; + + /* convert char* p_ap_decode_obj->fname to a pascal string */ + XP_STRCPY((char*)filename + 1, p_ap_decode_obj->fname); + filename[0] = XP_STRLEN(p_ap_decode_obj->fname); + + if( !p_ap_decode_obj->mSpec ) + { + StandardPutFile("\pSave decoded file as:", + (const unsigned char*)filename, + &reply); + + if (!reply.sfGood) + { + return errUsrCancel; + } + } + else + { + reply.sfFile.vRefNum = p_ap_decode_obj->mSpec->vRefNum; + reply.sfFile.parID = p_ap_decode_obj->mSpec->parID; + XP_MEMCPY(&reply.sfFile.name, p_ap_decode_obj->mSpec->name , 63 ); + } + + XP_MEMCPY(p_ap_decode_obj->fname, + reply.sfFile.name+1, + *(reply.sfFile.name)+1); + p_ap_decode_obj->fname[*(reply.sfFile.name)] = '\0'; + + p_ap_decode_obj->vRefNum = reply.sfFile.vRefNum; + p_ap_decode_obj->dirId = reply.sfFile.parID; + + /* create & get info for file */ + HDelete(reply.sfFile.vRefNum, + reply.sfFile.parID, + reply.sfFile.name); + +#define DONT_CARE_TYPE 0x3f3f3f3f + + if (HCreate(reply.sfFile.vRefNum, + reply.sfFile.parID, + reply.sfFile.name, + DONT_CARE_TYPE, + DONT_CARE_TYPE) != NOERR) + { + return errFileOpen; + } + + fpb = (HFileInfo *) &cipbr; + fpb->ioVRefNum = reply.sfFile.vRefNum; + fpb->ioDirID = reply.sfFile.parID; + fpb->ioNamePtr = reply.sfFile.name; + fpb->ioFDirIndex = 0; + PBGetCatInfoSync(&cipbr); + + /* get finder info */ + for (i = 0; i < j && entries[i].id != ENT_FINFO; ++i) + ; + if (i < j) + { + status = decoder_seek(p_ap_decode_obj, + entries[i].offset, + st_pt); + if (status != NOERR) + return status; + + status = from_decoder(p_ap_decode_obj, + (char *) &fpb->ioFlFndrInfo, + sizeof (FInfo), + &in_count); + if (status != NOERR) + return status; + + status = from_decoder(p_ap_decode_obj, + (char *) &fpb->ioFlXFndrInfo, + sizeof (FXInfo), + &in_count); + + if (status != NOERR && status != errEOP ) + return status; + + fpb->ioFlFndrInfo.fdFlags &= 0xfc00; /* clear flags maintained by finder */ + } + + /* + ** get file date info + */ + for (i = 0; i < j && entries[i].id != ENT_DATES; ++i) + ; + if (i < j) + { + status = decoder_seek(p_ap_decode_obj, + entries[i].offset, + st_pt); + if (status != NOERR && status != errEOP ) + return status; + + status = from_decoder(p_ap_decode_obj, + (char *) &dates, + sizeof (dates), + &in_count); + if (status != NOERR) + return status; + + fpb->ioFlCrDat = dates.create - CONVERT_TIME; + fpb->ioFlMdDat = dates.modify - CONVERT_TIME; + fpb->ioFlBkDat = dates.backup - CONVERT_TIME; + } + + /* + ** update info + */ + fpb->ioDirID = fpb->ioFlParID; + PBSetCatInfoSync(&cipbr); + + /* + ** get comment & save it + */ + for (i = 0; i < j && entries[i].id != ENT_COMMENT; ++i) + ; + if (i < j && entries[i].length != 0) + { + memset((void *) &vinfo, '\0', sizeof (vinfo)); + vinfo.ioVRefNum = fpb->ioVRefNum; + vinfo.ioBuffer = (Ptr) &vp; + vinfo.ioReqCount = sizeof (vp); + if (PBHGetVolParmsSync((HParmBlkPtr) &vinfo) == NOERR && + ((vp.vMAttrib >> bHasDesktopMgr) & 1)) + { + memset((void *) &dtp, '\0', sizeof (dtp)); + dtp.ioVRefNum = fpb->ioVRefNum; + if (PBDTGetPath(&dtp) == NOERR) + { + if (entries[i].length > 255) + entries[i].length = 255; + + status = decoder_seek(p_ap_decode_obj, + entries[i].offset, + st_pt); + if (status != NOERR) + return status; + + status = from_decoder(p_ap_decode_obj, + comment, + entries[i].length, + &in_count); + if (status != NOERR) + return status; + + dtp.ioDTBuffer = (Ptr) comment; + dtp.ioNamePtr = fpb->ioNamePtr; + dtp.ioDirID = fpb->ioDirID; + dtp.ioDTReqCount = entries[i].length; + if (PBDTSetCommentSync(&dtp) == NOERR) + { + PBDTFlushSync(&dtp); + } + } + } + } + +#else + /* + ** in non-mac system, creating a data fork file will be it. + */ +#endif + } + + /* + ** Get the size of the resource fork, and (maybe) position to the beginning of it. + */ + for (i = 0; i < j && entries[i].id != ENT_RFORK; ++i) + ; + if (i < j && entries[i].length != 0) + { +#ifdef XP_MAC + /* Seek to the start of the resource fork only if we're on a Mac */ + status = decoder_seek(p_ap_decode_obj, + entries[i].offset, + st_pt); + positionedAtRFork = TRUE; +#endif + p_ap_decode_obj->rksize = entries[i].length; + } + else + p_ap_decode_obj->rksize = 0; + + /* + ** Get the size of the data fork, and (maybe) position to the beginning of it. + */ + for (i = 0; i < j && entries[i].id != ENT_DFORK; ++i) + ; + if (i < j && entries[i].length != 0) + { + if (p_ap_decode_obj->is_apple_single && !positionedAtRFork) + status = decoder_seek(p_ap_decode_obj, + entries[i].offset, + st_pt); + p_ap_decode_obj->dksize = entries[i].length; + } + else + p_ap_decode_obj->dksize = 0; + + /* + ** Prepare a tempfile to hold the resource fork decoded by the decoder, + ** because in binhex, resource fork appears after the data fork!!! + */ + if (p_ap_decode_obj->write_as_binhex) + { + if (p_ap_decode_obj->rksize != 0) + { + /* we need a temp file to hold all the resource data, because the */ + p_ap_decode_obj->tmpfname + = WH_TempName(xpTemporary, "apmail"); + + p_ap_decode_obj->tmpfd + = XP_FileOpen(p_ap_decode_obj->tmpfname, + xpTemporary, + XP_FILE_TRUNCATE_BIN); + + if (p_ap_decode_obj->tmpfd == NULL) + return errFileOpen; + } + } + return NOERR; +} + +/* +** ap_decode_process_header +** +** +*/ +int ap_decode_process_header( + appledouble_decode_object* p_ap_decode_obj, + XP_Bool firstime) +{ + uint32 in_count; + int status = NOERR; + char wr_buff[1024]; + + if (firstime) + { + status = ap_decode_file_infor(p_ap_decode_obj); + if (status != NOERR) + return status; + + if (p_ap_decode_obj->rksize > 0) + { +#ifdef XP_MAC + if(!p_ap_decode_obj->write_as_binhex) + { + Str63 fname; + short refNum; + + fname[0] = XP_STRLEN(p_ap_decode_obj->fname); + XP_STRCPY((char*)fname+1, p_ap_decode_obj->fname); + + if (HOpenRF(p_ap_decode_obj->vRefNum, + p_ap_decode_obj->dirId, + fname, + fsWrPerm, + &refNum) != NOERR) + { + return (errFileOpen); + } + p_ap_decode_obj->fileId = refNum; + } +#endif + } + else + { + status = errDone; + } + } + + /* + ** Time to continue decoding all the resource data. + */ + while (status == NOERR && p_ap_decode_obj->rksize > 0) + { + in_count = MIN(1024, p_ap_decode_obj->rksize); + + status = from_decoder(p_ap_decode_obj, + wr_buff, + in_count, + &in_count); + + if (p_ap_decode_obj->write_as_binhex) + { + /* + ** Write to the temp file first, because the resource fork appears after + ** the data fork in the binhex encoding. + */ + if (XP_FileWrite(wr_buff, + in_count, + p_ap_decode_obj->tmpfd) != in_count) + { + status = errFileWrite; + break; + } + p_ap_decode_obj->data_size += in_count; + } + else + { +#ifdef XP_MAC + long howMuch = in_count; + + if (FSWrite(p_ap_decode_obj->fileId, + &howMuch, + wr_buff) != NOERR) + { + status = errFileWrite; + break; + } +#else + /* ====== Write nothing in a non mac file system ============ */ +#endif + } + + p_ap_decode_obj->rksize -= in_count; + } + + if (p_ap_decode_obj->rksize <= 0 || status == errEOP) + { + if (p_ap_decode_obj->write_as_binhex) + { + /* + ** No more resource data, but we are not done + ** with tempfile yet, just seek back to the start point, + ** -- ready for a readback later + */ + if (p_ap_decode_obj->tmpfd) + XP_FileSeek(p_ap_decode_obj->tmpfd, 0L, 1); + } + +#ifdef XP_MAC + else if (p_ap_decode_obj->fileId) /* close the resource fork of the macfile */ + { + FSClose(p_ap_decode_obj->fileId); + p_ap_decode_obj->fileId = 0; + } +#endif + if (!p_ap_decode_obj->is_apple_single) + { + p_ap_decode_obj->left = 0; + p_ap_decode_obj->state64 = 0; + } + status = errDone; + } + return status; +} + +int ap_decode_process_data( + appledouble_decode_object* p_ap_decode_obj, + XP_Bool firstime) +{ + char wr_buff[1024]; + uint32 in_count; + int status = NOERR; + int retval = NOERR; + + if (firstime) + { + if (!p_ap_decode_obj->write_as_binhex) + { +#ifdef XP_MAC + char *filename; + FSSpec fspec; + + fspec.vRefNum = p_ap_decode_obj->vRefNum; + fspec.parID = p_ap_decode_obj->dirId; + fspec.name[0] = XP_STRLEN(p_ap_decode_obj->fname); + XP_STRCPY((char*)fspec.name+1, p_ap_decode_obj->fname); + + filename = my_PathnameFromFSSpec(&fspec); + if (p_ap_decode_obj->is_binary) + p_ap_decode_obj->fd = + XP_FileOpen(filename+7, xpURL, XP_FILE_TRUNCATE_BIN); + else + p_ap_decode_obj->fd = + XP_FileOpen(filename+7, xpURL, XP_FILE_TRUNCATE); + XP_FREE(filename); +#else + if (p_ap_decode_obj->is_binary) + p_ap_decode_obj->fd = + XP_FileOpen(p_ap_decode_obj->fname, xpURL, XP_FILE_TRUNCATE_BIN); + else + p_ap_decode_obj->fd = + XP_FileOpen(p_ap_decode_obj->fname, xpURL, XP_FILE_TRUNCATE); +#endif + } + else + { + ; /* == don't need do anything to binhex stream, it is ready already == */ + } + } + + if (p_ap_decode_obj->is_apple_single && + p_ap_decode_obj->dksize == 0) + { + /* if no data in apple single, we already done then. */ + status = errDone; + } + + while (status == NOERR && retval == NOERR) + { + retval = from_decoder(p_ap_decode_obj, + wr_buff, + 1024, + &in_count); + + if (p_ap_decode_obj->is_apple_single) /* we know the data fork size in */ + p_ap_decode_obj->dksize -= in_count; /* apple single, use it to decide the end */ + + if (p_ap_decode_obj->write_as_binhex) + status = (*p_ap_decode_obj->binhex_stream->put_block) + (p_ap_decode_obj->binhex_stream->data_object, + wr_buff, + in_count); + else + status = XP_FileWrite(wr_buff, + in_count, + p_ap_decode_obj->fd) == in_count ? NOERR : errFileWrite; + + if (retval == errEOP || /* for apple double, we meet the boundary */ + ( p_ap_decode_obj->is_apple_single && + p_ap_decode_obj->dksize <= 0)) /* for apple single, we know it is ending */ + { + status = errDone; + break; + } + } + + if (status == errDone) + { + if (p_ap_decode_obj->write_as_binhex) + { + /* CALL with data == NULL && size == 0 to end a part object in binhex encoding */ + status = (*p_ap_decode_obj->binhex_stream->put_block) + (p_ap_decode_obj->binhex_stream->data_object, + NULL, + 0); + if (status != NOERR) + return status; + } + else if (p_ap_decode_obj->fd) + { + XP_FileClose(p_ap_decode_obj->fd); + p_ap_decode_obj->fd = 0; + } + + status = errDone; + } + return status; +} + +/* +** Fill the data from the decoder stream. +*/ +PRIVATE int from_decoder( + appledouble_decode_object* p_ap_decode_obj, + char *buff, + int buff_size, + uint32 *in_count) +{ + int status; + + switch (p_ap_decode_obj->encoding) + { + case kEncodeQP: + status = from_qp(p_ap_decode_obj, + buff, + buff_size, + in_count); + break; + case kEncodeBase64: + status = from_64(p_ap_decode_obj, + buff, + buff_size, + in_count); + break; + case kEncodeUU: + status = from_uu(p_ap_decode_obj, + buff, + buff_size, + in_count); + break; + case kEncodeNone: + default: + status = from_none(p_ap_decode_obj, + buff, + buff_size, + in_count); + break; + } + return status; +} + +/* +** decoder_seek +** +** simulate a stream seeking on the encoded stream. +*/ +PRIVATE int decoder_seek( + appledouble_decode_object* p_ap_decode_obj, + int seek_pos, + int start_pos) +{ + char tmp[1024]; + int status = NOERR; + uint32 in_count; + + /* + ** force a reset on the in buffer. + */ + p_ap_decode_obj->state64 = 0; + p_ap_decode_obj->left = 0; + p_ap_decode_obj->pos_inbuff = start_pos; + p_ap_decode_obj->uu_starts_line = TRUE; + p_ap_decode_obj->uu_bytes_written = p_ap_decode_obj->uu_line_bytes = 0; + p_ap_decode_obj->uu_state = kWaitingForBegin; + + while (seek_pos > 0) + { + status = from_decoder(p_ap_decode_obj, + tmp, + MIN(1024, seek_pos), + &in_count); + if (status != NOERR) + break; + + seek_pos -= in_count; + } + return status; +} + +#define XX 127 +/* + * Table for decoding base64 + */ +static char index_64[256] = { + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, XX,XX,XX,63, + 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX, + XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX, + XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, +}; + +#ifdef XP_OS2_HACK +/*DSR102196 - the OS/2 Visual Age compiler croaks when it tries*/ +/*to optomize this macro (/O+ on CSD4) */ +char CHAR64(int c) +{ + unsigned char index; + char rc; + + index = (unsigned char) c; + rc = index_64[index]; + return rc; +} +#else /*normal code...*/ +#define CHAR64(c) (index_64[(unsigned char)(c)]) +#endif +#define EndOfBuff(p) ((p)->pos_inbuff >= (p)->s_inbuff) + +PRIVATE int fetch_next_char_64( + appledouble_decode_object* p_ap_decode_obj) +{ + char c; + + c = p_ap_decode_obj->inbuff[p_ap_decode_obj->pos_inbuff++]; + if (c == '-') + --p_ap_decode_obj->pos_inbuff; /* put back */ + + while (c == LF || c == CR) /* skip the CR character. */ + { + if (EndOfBuff(p_ap_decode_obj)) + { + c = 0; + break; + } + + c = p_ap_decode_obj->inbuff[p_ap_decode_obj->pos_inbuff++]; + if (c == '-') + { + --p_ap_decode_obj->pos_inbuff; /* put back */ + } + } + return (int)c; +} + + +PRIVATE int from_64( + appledouble_decode_object* p_ap_decode_obj, + char *buff, + int size, + uint32 *real_size) +{ + int i, j, buf[4]; + int c1, c2, c3, c4; + + (*real_size) = 0; + + /* + ** decode 4 by 4s characters + */ + for (i = p_ap_decode_obj->state64; i<4; i++) + { + if (EndOfBuff(p_ap_decode_obj)) + { + p_ap_decode_obj->state64 = i; + break; + } + if ((p_ap_decode_obj->c[i] = fetch_next_char_64(p_ap_decode_obj)) == 0) + break; + } + + if (i != 4) + { + /* + ** not enough data to fill the decode buff. + */ + return errEOB; /* end of buff */ + } + + while (size > 0) + { + c1 = p_ap_decode_obj->c[0]; + c2 = p_ap_decode_obj->c[1]; + c3 = p_ap_decode_obj->c[2]; + c4 = p_ap_decode_obj->c[3]; + + if (c1 == '-' || c2 == '-' || c3 == '-' || c4 == '-') + { + return errEOP; /* we meet the part boundary. */ + } + + if (c1 == '=' || c2 == '=') + { + return errDecoding; + } + + c1 = CHAR64(c1); + c2 = CHAR64(c2); + buf[0] = ((c1<<2) | ((c2&0x30)>>4)); + + if (c3 != '=') + { + c3 = CHAR64(c3); + buf[1] = (((c2&0x0F) << 4) | ((c3&0x3C) >> 2)); + + if (c4 != '=') + { + c4 = CHAR64(c4); + buf[2] = (((c3&0x03) << 6) | c4); + } + else + { + if (p_ap_decode_obj->left == 0) + { + *buff++ = buf[0]; (*real_size)++; + } + *buff++ = buf[1]; (*real_size)++; + /* return errEOP; */ /* bug 87784 */ + return EndOfBuff(p_ap_decode_obj) ? errEOP : NOERR; + } + } + else + { + *buff++ = *buf; + (*real_size)++; + /* return errEOP; *bug 87784*/ /* we meet the the end */ + return EndOfBuff(p_ap_decode_obj) ? errEOP : NOERR; + } + /* + ** copy the content + */ + for (j = p_ap_decode_obj->left; j<3; ) + { + *buff++ = buf[j++]; + (*real_size)++; + if (--size <= 0) + break; + } + p_ap_decode_obj->left = j % 3; + + if (size <=0) + { + if (j == 3) + p_ap_decode_obj->state64 = 0; /* See if we used up all data, */ + /* ifnot, keep the data, */ + /* we need it for next time. */ + else + p_ap_decode_obj->state64 = 4; + + break; + } + + /* + ** fetch the next 4 character group. + */ + for (i = 0; i < 4; i++) + { + if (EndOfBuff(p_ap_decode_obj)) + break; + + if ((p_ap_decode_obj->c[i] = fetch_next_char_64(p_ap_decode_obj)) == 0) + break; + } + + p_ap_decode_obj->state64 = i % 4; + + if (i != 4) + break; /* some kind of end of buff met.*/ + } + + /* + ** decide the size and status. + */ + return EndOfBuff(p_ap_decode_obj) ? errEOB : NOERR; +} + +/* + * Table for decoding hexadecimal in quoted-printable + */ +static char index_hex[256] = { + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,XX,XX, XX,XX,XX,XX, + XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,10,11,12, 13,14,15,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, + XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, +}; + +#define HEXCHAR(c) (index_hex[(unsigned char)(c)]) +#define NEXT_CHAR(p) ((int)((p)->inbuff[(p)->pos_inbuff++])) +#define CURRENT_CHAR(p) ((int)((p)->inbuff[(p)->pos_inbuff])) +/* +** quoted printable decode, as defined in RFC 1521, page18 - 20 +*/ +PRIVATE int from_qp( + appledouble_decode_object* p_ap_decode_obj, + char *buff, + int size, + uint32 *real_size) +{ + char c; + int c1, c2; + + *real_size = 0; + + if (p_ap_decode_obj->c[0] == '=') + { + /* + ** continue with the last time's left over. + */ + p_ap_decode_obj->c[0] = 0; + + c1 = p_ap_decode_obj->c[1]; p_ap_decode_obj->c[1] = 0; + + if ( c1 == 0) + { + c1 = NEXT_CHAR(p_ap_decode_obj); + c2 = NEXT_CHAR(p_ap_decode_obj); + } + else + { + c2 = NEXT_CHAR(p_ap_decode_obj); + } + c = HEXCHAR(c1) << 4 | HEXCHAR(c2); + + size --; + *buff ++ = c; + (*real_size) ++; + } + + /* + ** Then start to work on the new data + */ + while (size > 0) + { + if (EndOfBuff(p_ap_decode_obj)) + break; + + c1 = NEXT_CHAR(p_ap_decode_obj); + + if (c1 == '=') + { + if (EndOfBuff(p_ap_decode_obj)) + { + p_ap_decode_obj->c[0] = c1; + break; + } + + c1 = NEXT_CHAR(p_ap_decode_obj); + if (c1 != '\n') + { + /* + ** Rule #2 + */ + c1 = HEXCHAR(c1); + if (EndOfBuff(p_ap_decode_obj)) + { + p_ap_decode_obj->c[0] = '='; + p_ap_decode_obj->c[1] = c1; + break; + } + + c2 = NEXT_CHAR(p_ap_decode_obj); + c2 = HEXCHAR(c2); + c = c1 << 4 | c2; + if (c != '\r') + { + size --; + *buff++ = c; + (*real_size)++; + } + } + else + { + /* ignore the line break -- soft line break, rule #5 */ + } + } + else + { + if (c1 == CR || c1 == LF) + { + if (p_ap_decode_obj->pos_inbuff < p_ap_decode_obj->s_inbuff) + { + if (p_ap_decode_obj->boundary0 && + (!XP_STRNCASECMP(p_ap_decode_obj->pos_inbuff+p_ap_decode_obj->inbuff, + "--", + 2) + && + !XP_STRNCASECMP(p_ap_decode_obj->pos_inbuff+p_ap_decode_obj->inbuff+2, + p_ap_decode_obj->boundary0, + XP_STRLEN(p_ap_decode_obj->boundary0)))) + { + return errEOP; + } + } + } + + /* + ** general 8bits case, Rule #1 + */ + size -- ; + *buff++ = c1; + (*real_size) ++; + } + } + return EndOfBuff(p_ap_decode_obj) ? errEOB : NOERR; +} + +#define UUEOL(c) (((c) == CR) || ((c) == LF)) +# undef UUDEC +# define UUDEC(c) (((c) - ' ') & 077) + +/* Check for and skip past the "begin" line of a uuencode body. */ +PRIVATE void ensure_uu_body_state(appledouble_decode_object* p) +{ + char *end = &(p->inbuff[p->s_inbuff]); + char *current = &(p->inbuff[p->pos_inbuff]); + + if (p->uu_state == kMainBody && p->uu_starts_line + && !XP_STRNCASECMP(current, "end", MIN(3, end - current))) + p->uu_state = kEnd; + + while (p->uu_state != kMainBody && (current < end)) + { + switch(p->uu_state) + { + case kWaitingForBegin: + case kBegin: + /* If we're not at the beginning of a line, move to the next line. */ + if (! p->uu_starts_line) + { + while(current < end && !UUEOL(*current)) + current++; + while(current < end && UUEOL(*current)) + current++; + + p->uu_starts_line = TRUE; /* we reached the start of a line */ + if (p->uu_state == kBegin) + p->uu_state = kMainBody; + + continue; + } + else + { + /* + At the start of a line. Test for "begin". + + ### mwelch: + + There is a potential danger here. If a buffer ends with a line + starting with some substring of "begin", this code will be fooled + into thinking that the uuencode body starts with the following line. + If the message itself contains lines that begin with a substring of + "begin", such as "be", "because", or "bezoar", and if those lines happen + to end a 1024-byte chunk, this becomes Really Bad. However, there is + no good, safe way to overcome this problem. So, for now, I hope and + pray that the 1024 character limit will always incorporate the entire + first line of a uuencode body. + + It should be noted that broken messages that have the body text in + the same MIME part as the uuencode attachment also risk this same + pitfall if any line in the message starts with "begin". + */ + + if ((p->uu_state == kWaitingForBegin) + && !XP_STRNCASECMP(current, "begin", MIN(5, end - current))) + p->uu_state = kBegin; + p->uu_starts_line = FALSE; /* make us advance to next line */ + } + break; + case kEnd: + /* Run out the buffer. */ + current = end; + } + } + + /* Record where we stopped scanning. */ + p->pos_inbuff = p->s_inbuff - (end - current); +} + +#define UU_VOID_CHAR 0 + +PRIVATE int fetch_next_char_uu(appledouble_decode_object* p, XP_Bool newBunch) +{ + char c=0; + XP_Bool gotChar = FALSE; + + if (EndOfBuff(p)) + return 0; + + while(!gotChar) + { + if (EndOfBuff(p)) + { + c = 0; + gotChar = TRUE; + } + else if (p->uu_starts_line) + { + char *end = &(p->inbuff[p->s_inbuff]); + char *current = &(p->inbuff[p->pos_inbuff]); + + /* Look here for 'end' line signifying end of uuencode body. */ + if (!XP_STRNCASECMP(current, "end", MIN(3, end - current))) + { + p->uu_state = kEnd; /* set the uuencode state to end */ + p->pos_inbuff = p->s_inbuff; /* run out the current buffer */ + + c = 0; /* return a 0 to uudecoder */ + gotChar = TRUE; + } + } + if (gotChar) + continue; + + c = NEXT_CHAR(p); + + if ((c == CR) || (c == LF)) + { + if (newBunch) + { + /* A new line could immediately follow either a CR or an LF. + If we reach the end of a buffer, simply assume the next buffer + will start a line (as it should in the current libmime implementation). + If it starts with CR or LF, that line will be skipped as well. */ + if (EndOfBuff(p) || ((CURRENT_CHAR(p) != CR) && (CURRENT_CHAR(p) != LF))) + p->uu_starts_line = TRUE; + + continue; + } + + /* End of line, but we have to finish a 4-tuple. Stop here. */ + -- p->pos_inbuff; /* give back the end-of-line character */ + c = UU_VOID_CHAR; /* flag as truncated */ + gotChar = TRUE; + } + + /* At this point, we have a valid char. */ + + else if (p->uu_starts_line) + { + /* read length char at start of each line */ + p->uu_line_bytes = UUDEC(c); + p->uu_starts_line = FALSE; + continue; + } + + else if (p->uu_line_bytes <= 0) + /* We ran out of bytes to decode on this line. Skip spare chars until + we reach the end of (line or buffer). */ + continue; + + else + gotChar = TRUE; /* valid returnable char */ + } + + return (int) c; +} + +/* +** uudecode +*/ + +PRIVATE int from_uu( + appledouble_decode_object* p_ap_decode_obj, + char *buff, + int size, + uint32 *real_size) +{ + char c; + int i; + int returnVal = NOERR; + int c1, c2, c3, c4; + + *real_size = 0; + + /* Make sure that we're in the uuencode body, or run out the buffer if + we don't have any body text in this buffer. */ + ensure_uu_body_state(p_ap_decode_obj); + + if (p_ap_decode_obj->uu_state == kEnd) + return errEOP; + + /* Continue with what was left over last time. */ + for (i = p_ap_decode_obj->state64; i<4; i++) + { + if (EndOfBuff(p_ap_decode_obj)) + { + p_ap_decode_obj->state64 = i; + break; + } + if ((p_ap_decode_obj->c[i] = fetch_next_char_uu(p_ap_decode_obj, (i==0))) == 0) + break; + } + + if ( (i < p_ap_decode_obj->uu_line_bytes+1) + && (EndOfBuff(p_ap_decode_obj))) + /* not enough data to decode, return here. */ + return errEOB; + + while((size > 0) && (!EndOfBuff(p_ap_decode_obj))) + { + c1 = p_ap_decode_obj->c[0]; + c2 = p_ap_decode_obj->c[1]; + c3 = p_ap_decode_obj->c[2]; + c4 = p_ap_decode_obj->c[3]; + + /* + At this point we have characters ready to decode. + Convert them to binary bytes. + */ + if ((i > 1) + && (p_ap_decode_obj->uu_bytes_written < 1) + && (p_ap_decode_obj->uu_line_bytes > 0)) + { + c = UUDEC(c1) << 2 | UUDEC(c2) >> 4; + size --; + *buff ++ = c; + (*real_size) ++; + p_ap_decode_obj->uu_line_bytes--; + p_ap_decode_obj->uu_bytes_written++; + } + + if ((i > 2) && (size > 0) + && (p_ap_decode_obj->uu_bytes_written < 2) + && (p_ap_decode_obj->uu_line_bytes > 0)) + { + c = UUDEC(c2) << 4 | UUDEC(c3) >> 2; + size --; + *buff ++ = c; + (*real_size) ++; + p_ap_decode_obj->uu_line_bytes--; + p_ap_decode_obj->uu_bytes_written++; + } + + if ((i > 3) && (size > 0) + && (p_ap_decode_obj->uu_line_bytes > 0)) + { + c = UUDEC(c3) << 6 | UUDEC(c4); + size --; + *buff ++ = c; + (*real_size) ++; + p_ap_decode_obj->uu_line_bytes--; + p_ap_decode_obj->uu_bytes_written = 0; + } + + if (p_ap_decode_obj->uu_state == kEnd) + continue; + + /* If this line is finished, this tuple is also finished. */ + if (p_ap_decode_obj->uu_line_bytes <= 0) + p_ap_decode_obj->uu_bytes_written = 0; + + if (p_ap_decode_obj->uu_bytes_written > 0) + { + /* size == 0, but we have bytes left in current tuple */ + p_ap_decode_obj->state64 = i; + continue; + } + + /* + ** fetch the next 4 character group. + */ + + for (i = 0; i < 4; i++) + { + if (EndOfBuff(p_ap_decode_obj)) + break; + + if ((p_ap_decode_obj->c[i] = fetch_next_char_uu(p_ap_decode_obj, (i == 0))) == 0) + break; + } + + p_ap_decode_obj->state64 = i; + + if ( (i < p_ap_decode_obj->uu_line_bytes+1) + && (EndOfBuff(p_ap_decode_obj))) + /* not enough data to decode, return here. */ + continue; + } + + if (p_ap_decode_obj->uu_state == kEnd) + returnVal = errEOP; + else if (EndOfBuff(p_ap_decode_obj)) + returnVal = errEOB; + + return returnVal; +} + +/* +** from_none +** +** plain text transfer. +*/ +PRIVATE int from_none( + appledouble_decode_object* p_ap_decode_obj, + char *buff, + int size, + uint32 *real_size) +{ + char c; + int i, status = NOERR; + int left = p_ap_decode_obj->s_inbuff - p_ap_decode_obj->pos_inbuff; + int total = MIN(size, left); + + for (i = 0; i < total; i++) + { + *buff ++ = c = NEXT_CHAR(p_ap_decode_obj); + if (c == CR || c == LF) + { + /* make sure the next thing is not a boundary string */ + if (p_ap_decode_obj->pos_inbuff < p_ap_decode_obj->s_inbuff) + { + if (p_ap_decode_obj->boundary0 && + (!XP_STRNCASECMP(p_ap_decode_obj->pos_inbuff+p_ap_decode_obj->inbuff, + "--", + 2) + && + !XP_STRNCASECMP(p_ap_decode_obj->pos_inbuff+p_ap_decode_obj->inbuff+2, + p_ap_decode_obj->boundary0, + XP_STRLEN(p_ap_decode_obj->boundary0)))) + { + status = errEOP; + break; + } + } + } + } + + *real_size = i; + if (status == NOERR) + status = (left == i) ? errEOB : status; + return status; +} + diff --git a/mozilla/lib/mailto/ap_encod.c b/mozilla/lib/mailto/ap_encod.c new file mode 100644 index 00000000000..6e820900c78 --- /dev/null +++ b/mozilla/lib/mailto/ap_encod.c @@ -0,0 +1,733 @@ +/* -*- 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. + */ + +/* + * + * apple_double_encode.c + * --------------------- + * + * The routines doing the Apple Double Encoding. + * + * 2aug95 mym Created. + * + */ + +#include "msg.h" +#include "appledbl.h" +#include "ad_codes.h" + +#ifdef XP_MAC + +extern int MK_UNABLE_TO_OPEN_TMP_FILE; +extern int MK_MIME_ERROR_WRITING_FILE; + +#include + +/* +** Local Functions prototypes. +*/ +PRIVATE int output64chunk( appledouble_encode_object* p_ap_encode_obj, + int c1, int c2, int c3, int pads); + +PRIVATE int to64(appledouble_encode_object* p_ap_encode_obj, + char *p, + int in_size); + +PRIVATE int finish64(appledouble_encode_object* p_ap_encode_obj); + + +#define BUFF_LEFT(p) ((p)->s_outbuff - (p)->pos_outbuff) + +/* +** write_stream. +*/ +int write_stream( + appledouble_encode_object *p_ap_encode_obj, + char *out_string, + int len) +{ + if (p_ap_encode_obj->pos_outbuff + len < p_ap_encode_obj->s_outbuff) + { + XP_MEMCPY(p_ap_encode_obj->outbuff + p_ap_encode_obj->pos_outbuff, + out_string, + len); + p_ap_encode_obj->pos_outbuff += len; + return noErr; + } + else + { + /* + ** If the buff doesn't have enough space, use the overflow buffer then. + */ + int s_len = p_ap_encode_obj->s_outbuff - p_ap_encode_obj->pos_outbuff; + + XP_MEMCPY(p_ap_encode_obj->outbuff + p_ap_encode_obj->pos_outbuff, + out_string, + s_len); + XP_MEMCPY(p_ap_encode_obj->b_overflow + p_ap_encode_obj->s_overflow, + out_string + s_len, + p_ap_encode_obj->s_overflow += (len - s_len)); + p_ap_encode_obj->pos_outbuff += s_len; + return errEOB; + } +} + +int fill_apple_mime_header( + appledouble_encode_object *p_ap_encode_obj) +{ + int status; + + char tmpstr[266]; + +#if 0 +// strcpy(tmpstr, "Content-Type: multipart/mixed; boundary=\"-\"\n\n---\n"); +// status = write_stream(p_ap_encode_env, +// tmpstr, +// strlen(tmpstr)); +// if (status != noErr) +// return status; + + sprintf(tmpstr, + "Content-Type: multipart/appledouble; boundary=\"=\"; name=\""); + status = write_stream(p_ap_encode_obj, + tmpstr, + strlen(tmpstr)); + if (status != noErr) + return status; + + status = write_stream(p_ap_encode_obj, + p_ap_encode_obj->fname, + XP_STRLEN(p_ap_encode_obj->fname)); + if (status != noErr) + return status; + + XP_SPRINTF(tmpstr, + "\"\nContent-Disposition: inline; filename=\"%s\"\n\n\n--=\n", + p_ap_encode_obj->fname); +#endif + XP_SPRINTF(tmpstr, "--%s"CRLF, p_ap_encode_obj->boundary); + status = write_stream(p_ap_encode_obj, + tmpstr, + XP_STRLEN(tmpstr)); + return status; +} + +int ap_encode_file_infor( + appledouble_encode_object *p_ap_encode_obj) +{ + CInfoPBRec cipbr; + HFileInfo *fpb = (HFileInfo *)&cipbr; + ap_header head; + ap_entry entries[NUM_ENTRIES]; + ap_dates dates; + short i; + long comlen, procID; + DateTimeRec cur_time; + unsigned long cur_secs; + IOParam vinfo; + GetVolParmsInfoBuffer vp; + DTPBRec dtp; + char comment[256]; + Str63 fname; + int status; + + strcpy((char *)fname+1,p_ap_encode_obj->fname); + fname[0] = XP_STRLEN(p_ap_encode_obj->fname); + + fpb->ioNamePtr = fname; + fpb->ioDirID = p_ap_encode_obj->dirId; + fpb->ioVRefNum = p_ap_encode_obj->vRefNum; + fpb->ioFDirIndex = 0; + if (PBGetCatInfoSync(&cipbr) != noErr) + { + return errFileOpen; + } + + /* get a file comment, if possible */ + procID = 0; + GetWDInfo(p_ap_encode_obj->vRefNum, &fpb->ioVRefNum, &fpb->ioDirID, &procID); + memset((void *) &vinfo, '\0', sizeof (vinfo)); + vinfo.ioCompletion = nil; + vinfo.ioVRefNum = fpb->ioVRefNum; + vinfo.ioBuffer = (Ptr) &vp; + vinfo.ioReqCount = sizeof (vp); + comlen = 0; + if (PBHGetVolParmsSync((HParmBlkPtr) &vinfo) == noErr && + ((vp.vMAttrib >> bHasDesktopMgr) & 1)) + { + memset((void *) &dtp, '\0', sizeof (dtp)); + dtp.ioVRefNum = fpb->ioVRefNum; + if (PBDTGetPath(&dtp) == noErr) + { + dtp.ioCompletion = nil; + dtp.ioDTBuffer = (Ptr) comment; + dtp.ioNamePtr = fpb->ioNamePtr; + dtp.ioDirID = fpb->ioFlParID; + if (PBDTGetCommentSync(&dtp) == noErr) + comlen = dtp.ioDTActCount; + } + } + + /* write header */ +// head.magic = dfork ? APPLESINGLE_MAGIC : APPLEDOUBLE_MAGIC; + head.magic = APPLEDOUBLE_MAGIC; /* always do apple double */ + head.version = VERSION; + memset(head.fill, '\0', sizeof (head.fill)); + head.entries = NUM_ENTRIES - 1; + status = to64(p_ap_encode_obj, + (char *) &head, + sizeof (head)); + if (status != noErr) + return status; + + /* write entry descriptors */ + entries[0].offset = sizeof (head) + sizeof (ap_entry) * head.entries; + entries[0].id = ENT_NAME; + entries[0].length = *fpb->ioNamePtr; + entries[1].id = ENT_FINFO; + entries[1].length = sizeof (FInfo) + sizeof (FXInfo); + entries[2].id = ENT_DATES; + entries[2].length = sizeof (ap_dates); + entries[3].id = ENT_COMMENT; + entries[3].length = comlen; + entries[4].id = ENT_RFORK; + entries[4].length = fpb->ioFlRLgLen; + entries[5].id = ENT_DFORK; + entries[5].length = fpb->ioFlLgLen; + + /* correct the link in the entries. */ + for (i = 1; i < NUM_ENTRIES; ++i) + { + entries[i].offset = entries[i-1].offset + entries[i-1].length; + } + status = to64(p_ap_encode_obj, + (char *) entries, + sizeof (ap_entry) * head.entries); + if (status != noErr) + return status; + + /* write name */ + status = to64(p_ap_encode_obj, + (char *) fpb->ioNamePtr + 1, + *fpb->ioNamePtr); + if (status != noErr) + return status; + + /* write finder info */ + status = to64(p_ap_encode_obj, + (char *) &fpb->ioFlFndrInfo, + sizeof (FInfo)); + if (status != noErr) + return status; + + status = to64(p_ap_encode_obj, + (char *) &fpb->ioFlXFndrInfo, + sizeof (FXInfo)); + if (status != noErr) + return status; + + /* write dates */ + GetTime(&cur_time); + DateToSeconds(&cur_time, &cur_secs); + dates.create = fpb->ioFlCrDat + CONVERT_TIME; + dates.modify = fpb->ioFlMdDat + CONVERT_TIME; + dates.backup = fpb->ioFlBkDat + CONVERT_TIME; + dates.access = cur_secs + CONVERT_TIME; + status = to64(p_ap_encode_obj, + (char *) &dates, + sizeof (ap_dates)); + if (status != noErr) + return status; + + /* write comment */ + if (comlen) + { + status = to64(p_ap_encode_obj, + comment, + comlen * sizeof(char)); + } + /* + ** Get some help information on deciding the file type. + */ + if (fpb->ioFlFndrInfo.fdType == 'TEXT' || fpb->ioFlFndrInfo.fdType == 'text') + { + p_ap_encode_obj->text_file_type = true; + } + + return status; +} +/* +** ap_encode_header +** +** encode the file header and the resource fork. +** +*/ +int ap_encode_header( + appledouble_encode_object* p_ap_encode_obj, + XP_Bool firstime) +{ + Str255 name; + char rd_buff[256]; + short fileId; + OSErr retval = noErr; + int status; + long inCount; + + if (firstime) + { + XP_STRCPY(rd_buff, + "Content-Type: application/applefile\nContent-Transfer-Encoding: base64\n\n"); + status = write_stream(p_ap_encode_obj, + rd_buff, + strlen(rd_buff)); + if (status != noErr) + return status; + + status = ap_encode_file_infor(p_ap_encode_obj); + if (status != noErr) + return status; + + /* + ** preparing to encode the resource fork. + */ + name[0] = strlen(p_ap_encode_obj->fname); + strcpy((char *)name+1, p_ap_encode_obj->fname); + if (HOpenRF(p_ap_encode_obj->vRefNum, p_ap_encode_obj->dirId, + name, fsRdPerm, + &p_ap_encode_obj->fileId) != noErr) + { + return errFileOpen; + } + } + + fileId = p_ap_encode_obj->fileId; + while (retval == noErr) + { + if (BUFF_LEFT(p_ap_encode_obj) < 400) + break; + + inCount = 256; + retval = FSRead(fileId, &inCount, rd_buff); + if (inCount) + { + status = to64(p_ap_encode_obj, + rd_buff, + inCount); + if (status != noErr) + return status; + } + } + + if (retval == eofErr) + { + FSClose(fileId); + + status = finish64(p_ap_encode_obj); + if (status != noErr) + return status; + + /* + ** write out the boundary + */ + XP_SPRINTF(rd_buff, + CRLF"--%s"CRLF, + p_ap_encode_obj->boundary); + + status = write_stream(p_ap_encode_obj, + rd_buff, + XP_STRLEN(rd_buff)); + if (status == noErr) + status = errDone; + } + return status; +} + +static void replace(char *p, int len, char frm, char to) +{ + for (; len > 0; len--, p++) + if (*p == frm) *p = to; +} + +/* Description of the various file formats and their magic numbers */ +struct magic +{ + char *name; /* Name of the file format */ + char *num; /* The magic number */ + int len; /* Length (0 means strlen(magicnum)) */ +}; + +/* The magic numbers of the file formats we know about */ +static struct magic magic[] = +{ + { "image/gif", "GIF", 0 }, + { "image/jpeg", "\377\330\377", 0 }, + { "video/mpeg", "\0\0\001\263", 4 }, + { "application/postscript", "%!", 0 }, +}; +static int num_magic = (sizeof(magic)/sizeof(magic[0])); + +static char *text_type = TEXT_PLAIN; /* the text file type. */ +static char *default_type = APPLICATION_OCTET_STREAM; + + +/* + * Determins the format of the file "inputf". The name + * of the file format (or NULL on error) is returned. + */ +PRIVATE char *magic_look(char *inbuff, int numread) +{ + int i, j; + + for (i=0; i= magic[i].len) + { + for (j=0; jfname); + XP_STRCPY((char*)name+1, p_ap_encode_obj->fname); + if (HOpen( p_ap_encode_obj->vRefNum, + p_ap_encode_obj->dirId, + name, + fsRdPerm, + &fileId) != noErr) + { + return errFileOpen; + } + p_ap_encode_obj->fileId = fileId; + + + if (!p_ap_encode_obj->text_file_type) + { + OSErr err; + FSSpec file_spec; + char* path; + Bool do_magic = true; + + /* First attempt to get the file's mime type via FE_FileType. + If that fails, we'll do a "magic_look" + */ + + err = FSMakeFSSpec(p_ap_encode_obj->vRefNum, p_ap_encode_obj->dirId, name, &file_spec); + if (err == noErr) + { + path = my_PathnameFromFSSpec(&file_spec); + if (path != NULL) + { + char* ignore; + FE_FileType(path, &do_magic, &magic_type, &ignore); + + /* + if we ended up with the default type, dispose of it + so we can do a magic_look + */ + + if (do_magic && magic_type) + XP_FREE(magic_type); + } + } + + if (do_magic) + { + /* + ** do a smart check for the file type. + */ + in_count = 256; + retval = FSRead(fileId, &in_count, rd_buff); + magic_type = magic_look(rd_buff, in_count); + + /* don't forget to rewind the index to start point. */ + SetFPos(fileId, fsFromStart, 0L); + } + } + else + { + magic_type = text_type; /* we already know it is a text type. */ + } + + /* + ** the data portion header information. + */ + XP_SPRINTF(rd_buff, + "Content-Type: %s; name=\"%s\"" CRLF "Content-Transfer-Encoding: base64" CRLF "Content-Disposition: inline; filename=\"%s\""CRLF CRLF, + magic_type, + p_ap_encode_obj->fname, + p_ap_encode_obj->fname); + + status = write_stream(p_ap_encode_obj, + rd_buff, + XP_STRLEN(rd_buff)); + if (status != noErr) + return status; + } + + while (retval == noErr) + { + if (BUFF_LEFT(p_ap_encode_obj) < 400) + break; + + in_count = 256; + retval = FSRead(p_ap_encode_obj->fileId, + &in_count, + rd_buff); + if (in_count) + { +/* replace(rd_buff, in_count, '\r', '\n'); */ +/* ** may be need to do character set conversion here for localization. ** */ + status = to64(p_ap_encode_obj, + rd_buff, + in_count); + if (status != noErr) + return status; + } + } + + if (retval == eofErr) + { + FSClose(p_ap_encode_obj->fileId); + + status = finish64(p_ap_encode_obj); + if (status != noErr) + return status; + + /* write out the boundary */ + + XP_SPRINTF(rd_buff, + CRLF"--%s--"CRLF CRLF, + p_ap_encode_obj->boundary); + + status = write_stream(p_ap_encode_obj, + rd_buff, + XP_STRLEN(rd_buff)); + + if (status == noErr) + status = errDone; + } + return status; +} + +static char basis_64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* +** convert the stream in the inbuff to 64 format and put it in the out buff. +** To make the life easier, the caller will responcable of the cheking of the outbuff's bundary. +*/ +PRIVATE int +to64(appledouble_encode_object* p_ap_encode_obj, + char *p, + int in_size) +{ + int status; + int c1, c2, c3, ct; + unsigned char *inbuff = (unsigned char*)p; + + ct = p_ap_encode_obj->ct; /* the char count left last time. */ + + /* + ** resume the left state of the last conversion. + */ + switch (p_ap_encode_obj->state64) + { + case 0: + p_ap_encode_obj->c1 = c1 = *inbuff ++; + if (--in_size <= 0) + { + p_ap_encode_obj->state64 = 1; + return noErr; + } + p_ap_encode_obj->c2 = c2 = *inbuff ++; + if (--in_size <= 0) + { + p_ap_encode_obj->state64 = 2; + return noErr; + } + c3 = *inbuff ++; --in_size; + break; + case 1: + c1 = p_ap_encode_obj->c1; + p_ap_encode_obj->c2 = c2 = *inbuff ++; + if (--in_size <= 0) + { + p_ap_encode_obj->state64 = 2; + return noErr; + } + c3 = *inbuff ++; --in_size; + break; + case 2: + c1 = p_ap_encode_obj->c1; + c2 = p_ap_encode_obj->c2; + c3 = *inbuff ++; --in_size; + break; + } + + while (in_size >= 0) + { + status = output64chunk(p_ap_encode_obj, + c1, + c2, + c3, + 0); + if (status != noErr) + return status; + + ct += 4; + if (ct > 71) + { + status = write_stream(p_ap_encode_obj, + CRLF, + 2); + if (status != noErr) + return status; + + ct = 0; + } + + if (in_size <= 0) + { + p_ap_encode_obj->state64 = 0; + break; + } + + c1 = (int)*inbuff++; + if (--in_size <= 0) + { + p_ap_encode_obj->c1 = c1; + p_ap_encode_obj->state64 = 1; + break; + } + c2 = *inbuff++; + if (--in_size <= 0) + { + p_ap_encode_obj->c1 = c1; + p_ap_encode_obj->c2 = c2; + p_ap_encode_obj->state64 = 2; + break; + } + c3 = *inbuff++; + in_size--; + } + p_ap_encode_obj->ct = ct; + return status; +} + +/* +** clear the left base64 encodes. +*/ +PRIVATE int +finish64(appledouble_encode_object* p_ap_encode_obj) +{ + int status; + + switch (p_ap_encode_obj->state64) + { + case 0: + break; + case 1: + status = output64chunk(p_ap_encode_obj, + p_ap_encode_obj->c1, + 0, + 0, + 2); + break; + case 2: + status = output64chunk(p_ap_encode_obj, + p_ap_encode_obj->c1, + p_ap_encode_obj->c2, + 0, + 1); + break; + } + status = write_stream(p_ap_encode_obj, CRLF, 2); + p_ap_encode_obj->state64 = 0; + p_ap_encode_obj->ct = 0; + return status; +} + +PRIVATE int output64chunk( + appledouble_encode_object* p_ap_encode_obj, + int c1, int c2, int c3, int pads) +{ + char tmpstr[32]; + char *p = tmpstr; + + *p++ = basis_64[c1>>2]; + *p++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)]; + if (pads == 2) + { + *p++ = '='; + *p++ = '='; + } + else if (pads) + { + *p++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; + *p++ = '='; + } + else + { + *p++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; + *p++ = basis_64[c3 & 0x3F]; + } + return write_stream(p_ap_encode_obj, + tmpstr, + p-tmpstr); +} + +#endif /* if define XP_MAC */ diff --git a/mozilla/lib/mailto/appledbl.c b/mozilla/lib/mailto/appledbl.c new file mode 100644 index 00000000000..f498518f69b --- /dev/null +++ b/mozilla/lib/mailto/appledbl.c @@ -0,0 +1,615 @@ +/* -*- 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. + */ + +/* +* +* apple-double.c +* -------------- +* +* The codes to do apple double encoding/decoding. +* +* 02aug95 mym created. +* 27sep95 mym Add the XP_Mac to ensure the cross-platform. +* +*/ + +#include "msg.h" +#include "appledbl.h" +#include "ad_codes.h" + +#ifdef XP_MAC + +#pragma warn_unusedarg off +#include "m_cvstrm.h" + +#pragma cplusplus on +#include "InternetConfig.h" +#include "ufilemgr.h" +#include "BufferStream.h" +#include "Umimemap.h" +#include "uprefd.h" +#include "ulaunch.h" +void DecodingDone( appledouble_decode_object* p_ap_decode_obj ); + +OSErr my_FSSpecFromPathname(char* src_filename, FSSpec* fspec) +{ + /* don't resolve aliases... */ + return CFileMgr::FSSpecFromLocalUnixPath(src_filename, fspec, false); +} + +char* my_PathnameFromFSSpec(FSSpec* fspec) +{ + return CFileMgr::GetURLFromFileSpec(*fspec); +} + +/* returns true if the resource fork should be sent */ +XP_Bool isMacFile(char* filename) +{ + Boolean returnValue = FALSE; + + FSSpec fspec; + my_FSSpecFromPathname(filename, &fspec); + + returnValue = CFileMgr::FileHasResourceFork(fspec); + /* always use IC even if the pref isn't checked since we have no + other way to determine if the resource is significant */ + if ( returnValue ) + { + + CMimeMapper * mapper = CPrefs::sMimeTypes.FindMimeType(fspec); + if ( mapper ) + { + returnValue = mapper->GetFileFlags()& ICmap_resource_fork_mask; + return returnValue; + } + + // Get the Internet Config file mapping for this type + // and see if the existing resources are significant. + + // First, get the type/creator of the file. + FInfo fileInfo; + OSErr err = ::FSpGetFInfo(&fspec, &fileInfo); + if (err == noErr) + { + if ( fileInfo.fdType == 'APPL' ) + return TRUE; + ICMapEntry ent; + err = CInternetConfigInterface::GetInternetConfigFileMapping(fileInfo.fdType, + fileInfo.fdCreator, + fspec.name, &ent); + if (err == noErr) + { + // resource fork is significant if the resource fork mask bit is set + returnValue = (ent.flags & ICmap_resource_fork_mask) != 0; + } + } + } + + return (XP_Bool) returnValue; +} + +void DecodingDone( appledouble_decode_object* p_ap_decode_obj ) +{ + FSSpec fspec; + + fspec.vRefNum = p_ap_decode_obj->vRefNum; + fspec.parID = p_ap_decode_obj->dirId; + fspec.name[0] = XP_STRLEN(p_ap_decode_obj->fname); + XP_STRCPY((char*)fspec.name+1, p_ap_decode_obj->fname); + CMimeMapper * mapper = CPrefs::sMimeTypes.FindMimeType(fspec); + if( mapper && (mapper->GetLoadAction() == CMimeMapper::Launch ) ) + { + LFileBufferStream file( fspec ); + LaunchFile( &file ); + } +} + +#pragma cplusplus reset + +/* +* ap_encode_init +* -------------- +* +* Setup the encode envirment +*/ + +int ap_encode_init( + appledouble_encode_object *p_ap_encode_obj, + char* fname, + char* separator) +{ + FSSpec fspec; + + if (my_FSSpecFromPathname(fname, &fspec) != noErr ) + return -1; + + XP_MEMSET(p_ap_encode_obj, 0, sizeof(appledouble_encode_object)); + + /* + ** Fill out the source file inforamtion. + */ + XP_MEMCPY(p_ap_encode_obj->fname, fspec.name+1, *fspec.name); + p_ap_encode_obj->fname[*fspec.name] = '\0'; + p_ap_encode_obj->vRefNum = fspec.vRefNum; + p_ap_encode_obj->dirId = fspec.parID; + + p_ap_encode_obj->boundary = XP_STRDUP(separator); + return noErr; +} +/* +** ap_encode_next +** -------------- +** +** return : +** noErr : everything is ok +** errDone : when encoding is done. +** errors : otherwise. +*/ +int ap_encode_next( + appledouble_encode_object* p_ap_encode_obj, + char *to_buff, + int32 buff_size, + int32* real_size) +{ + int status; + + /* + ** install the out buff now. + */ + p_ap_encode_obj->outbuff = to_buff; + p_ap_encode_obj->s_outbuff = buff_size; + p_ap_encode_obj->pos_outbuff = 0; + + /* + ** first copy the outstandind data in the overflow buff to the out buffer. + */ + if (p_ap_encode_obj->s_overflow) + { + status = write_stream(p_ap_encode_obj, + p_ap_encode_obj->b_overflow, + p_ap_encode_obj->s_overflow); + if (status != noErr) + return status; + + p_ap_encode_obj->s_overflow = 0; + } + + /* + ** go the next processing stage based on the current state. + */ + switch (p_ap_encode_obj->state) + { + case kInit: + /* + ** We are in the starting position, fill out the header. + */ + status = fill_apple_mime_header(p_ap_encode_obj); + if (status != noErr) + break; /* some error happens */ + + p_ap_encode_obj->state = kDoingHeaderPortion; + status = ap_encode_header(p_ap_encode_obj, true); + /* it is the first time to calling */ + if (status == errDone) + { + p_ap_encode_obj->state = kDoneHeaderPortion; + } + else + { + break; /* we need more work on header portion. */ + } + + /* + ** we are done with the header, so let's go to the data port. + */ + p_ap_encode_obj->state = kDoingDataPortion; + status = ap_encode_data(p_ap_encode_obj, true); + /* it is first time call do data portion */ + + if (status == errDone) + { + p_ap_encode_obj->state = kDoneDataPortion; + status = noErr; + } + break; + + case kDoingHeaderPortion: + + status = ap_encode_header(p_ap_encode_obj, false); + /* continue with the header portion. */ + if (status == errDone) + { + p_ap_encode_obj->state = kDoneHeaderPortion; + } + else + { + break; /* we need more work on header portion. */ + } + + /* + ** start the data portion. + */ + p_ap_encode_obj->state = kDoingDataPortion; + status = ap_encode_data(p_ap_encode_obj, true); + /* it is the first time calling */ + if (status == errDone) + { + p_ap_encode_obj->state = kDoneDataPortion; + status = noErr; + } + break; + + case kDoingDataPortion: + + status = ap_encode_data(p_ap_encode_obj, false); + /* it is not the first time */ + + if (status == errDone) + { + p_ap_encode_obj->state = kDoneDataPortion; + status = noErr; + } + break; + + case kDoneDataPortion: +#if 0 + status = write_stream(p_ap_encode_obj, + "\n-----\n\n", + 8); + if (status == noErr) +#endif + status = errDone; /* we are really done. */ + + break; + } + + *real_size = p_ap_encode_obj->pos_outbuff; + return status; +} + +/* +** ap_encode_end +** ------------- +** +** clear the apple encoding. +*/ + +int ap_encode_end( + appledouble_encode_object *p_ap_encode_obj, + XP_Bool is_aborting) +{ + /* + ** clear up the apple doubler. + */ + if (p_ap_encode_obj == NULL) + return noErr; + + if (p_ap_encode_obj->fileId) /* close the file if it is open. */ + FSClose(p_ap_encode_obj->fileId); + + FREEIF(p_ap_encode_obj->boundary); /* the boundary string. */ + + return noErr; +} + +#endif /* the ifdef of XP_MAC */ + + +/* +** The initial of the apple double decoder. +** +** Set up the next output stream based on the input. +*/ +int ap_decode_init( + appledouble_decode_object* p_ap_decode_obj, + XP_Bool is_apple_single, + XP_Bool write_as_binhex, + void *closure) +{ + XP_MEMSET(p_ap_decode_obj, 0, sizeof(appledouble_decode_object)); + + /* presume first buff starts a line */ + p_ap_decode_obj->uu_starts_line = TRUE; + + if (write_as_binhex) + { + p_ap_decode_obj->write_as_binhex = TRUE; + p_ap_decode_obj->binhex_stream = (NET_StreamClass*)closure; + p_ap_decode_obj->data_size = 0; + } + else + { + p_ap_decode_obj->write_as_binhex = FALSE; + p_ap_decode_obj->binhex_stream = NULL; + + p_ap_decode_obj->context = (MWContext*)closure; + } + + p_ap_decode_obj->is_apple_single = is_apple_single; + + if (is_apple_single) + { + p_ap_decode_obj->encoding = kEncodeNone; + } + + return NOERR; +} + +static int ap_decode_state_machine(appledouble_decode_object* p_ap_decode_obj); +/* +* process the buffer +*/ +int ap_decode_next( + appledouble_decode_object* p_ap_decode_obj, + char *in_buff, + int32 buff_size) +{ + /* + ** install the buff to the decoder. + */ + p_ap_decode_obj->inbuff = in_buff; + p_ap_decode_obj->s_inbuff = buff_size; + p_ap_decode_obj->pos_inbuff = 0; + + /* + ** run off the decode state machine + */ + return ap_decode_state_machine(p_ap_decode_obj); +} + +PRIVATE int ap_decode_state_machine( + appledouble_decode_object* p_ap_decode_obj) +{ + int status = NOERR; + int32 size; + + switch (p_ap_decode_obj->state) + { + case kInit: + /* + ** Make sure that there are stuff in the buff + ** before we can parse the file head . + */ + if (p_ap_decode_obj->s_inbuff <=1 ) + return NOERR; + + if (p_ap_decode_obj->is_apple_single) + { + p_ap_decode_obj->state = kBeginHeaderPortion; + } + else + { + status = ap_seek_part_start(p_ap_decode_obj); + if (status != errDone) + return status; + + p_ap_decode_obj->state = kBeginParseHeader; + } + status = ap_decode_state_machine(p_ap_decode_obj); + break; + + case kBeginSeekBoundary: + p_ap_decode_obj->state = kSeekingBoundary; + status = ap_seek_to_boundary(p_ap_decode_obj, TRUE); + if (status == errDone) + { + p_ap_decode_obj->state = kBeginParseHeader; + status = ap_decode_state_machine(p_ap_decode_obj); + } + break; + + case kSeekingBoundary: + status = ap_seek_to_boundary(p_ap_decode_obj, FALSE); + if (status == errDone) + { + p_ap_decode_obj->state = kBeginParseHeader; + status = ap_decode_state_machine(p_ap_decode_obj); + } + break; + + case kBeginParseHeader: + p_ap_decode_obj->state = kParsingHeader; + status = ap_parse_header(p_ap_decode_obj, TRUE); + if (status == errDone) + { + if (p_ap_decode_obj->which_part == kDataPortion) + p_ap_decode_obj->state = kBeginDataPortion; + else if (p_ap_decode_obj->which_part == kHeaderPortion) + p_ap_decode_obj->state = kBeginHeaderPortion; + else + p_ap_decode_obj->state = kFinishing; + + status = ap_decode_state_machine(p_ap_decode_obj); + } + break; + + case kParsingHeader: + status = ap_parse_header(p_ap_decode_obj, FALSE); + if (status == errDone) + { + if (p_ap_decode_obj->which_part == kDataPortion) + p_ap_decode_obj->state = kBeginDataPortion; + else if (p_ap_decode_obj->which_part == kHeaderPortion) + p_ap_decode_obj->state = kBeginHeaderPortion; + else + p_ap_decode_obj->state = kFinishing; + + status = ap_decode_state_machine(p_ap_decode_obj); + + } + break; + + case kBeginHeaderPortion: + p_ap_decode_obj->state = kProcessingHeaderPortion; + status = ap_decode_process_header(p_ap_decode_obj, TRUE); + if (status == errDone) + { + if (p_ap_decode_obj->is_apple_single) + p_ap_decode_obj->state = kBeginDataPortion; + else + p_ap_decode_obj->state = kBeginSeekBoundary; + + status = ap_decode_state_machine(p_ap_decode_obj); + } + break; + case kProcessingHeaderPortion: + status = ap_decode_process_header(p_ap_decode_obj, FALSE); + if (status == errDone) + { + if (p_ap_decode_obj->is_apple_single) + p_ap_decode_obj->state = kBeginDataPortion; + else + p_ap_decode_obj->state = kBeginSeekBoundary; + + status = ap_decode_state_machine(p_ap_decode_obj); + } + break; + + case kBeginDataPortion: + p_ap_decode_obj->state = kProcessingDataPortion; + status = ap_decode_process_data(p_ap_decode_obj, TRUE); + if (status == errDone) + { + if (p_ap_decode_obj->is_apple_single) + p_ap_decode_obj->state = kFinishing; + else + p_ap_decode_obj->state = kBeginSeekBoundary; + + status = ap_decode_state_machine(p_ap_decode_obj); + } + break; + + case kProcessingDataPortion: + status = ap_decode_process_data(p_ap_decode_obj, FALSE); + if (status == errDone) + { + if (p_ap_decode_obj->is_apple_single) + p_ap_decode_obj->state = kFinishing; + else + p_ap_decode_obj->state = kBeginSeekBoundary; + + status = ap_decode_state_machine(p_ap_decode_obj); + } + break; + + case kFinishing: + if (p_ap_decode_obj->write_as_binhex) + { + if (p_ap_decode_obj->tmpfd) + { + /* + ** It is time to append the data fork to bin hex encoder. + ** + ** The reason behind this dirt work is resource fork is the last + ** piece in the binhex, while it is the first piece in apple double. + */ + XP_FileSeek(p_ap_decode_obj->tmpfd, 0L, SEEK_SET); + + while (p_ap_decode_obj->data_size > 0) + { + char buff[1024]; + + size = MIN(1024, p_ap_decode_obj->data_size); + XP_FileRead(buff, size, p_ap_decode_obj->tmpfd); + + status = (*p_ap_decode_obj->binhex_stream->put_block) + (p_ap_decode_obj->binhex_stream->data_object, + buff, + size); + + p_ap_decode_obj->data_size -= size; + } + } + + if (p_ap_decode_obj->data_size <= 0) + { + /* CALL put_block with size == 0 to close a part. */ + status = (*p_ap_decode_obj->binhex_stream->put_block) + (p_ap_decode_obj->binhex_stream->data_object, + NULL, + 0); + if (status != NOERR) + break; + + /* and now we are really done. */ + status = errDone; + } + else + status = NOERR; + } + break; + } + return (status == errEOB) ? NOERR : status; +} + +int ap_decode_end( + appledouble_decode_object* p_ap_decode_obj, + XP_Bool is_aborting) +{ + /* + ** clear up the apple doubler object. + */ + if (p_ap_decode_obj == NULL) + return NOERR; + + FREEIF(p_ap_decode_obj->boundary0); + +#ifdef XP_MAC + if (p_ap_decode_obj->fileId) + FSClose(p_ap_decode_obj->fileId); + if( p_ap_decode_obj->vRefNum ) + FlushVol(nil, p_ap_decode_obj->vRefNum ); +#endif + + if (p_ap_decode_obj->write_as_binhex) + { + /* + ** make sure close the binhex stream too. + */ + if (is_aborting) + { + (*p_ap_decode_obj->binhex_stream->abort) + (p_ap_decode_obj->binhex_stream->data_object, 0); + } + else + { + (*p_ap_decode_obj->binhex_stream->complete) + (p_ap_decode_obj->binhex_stream->data_object); + } + + if (p_ap_decode_obj->tmpfd) + XP_FileClose(p_ap_decode_obj->tmpfd); + + if (p_ap_decode_obj->tmpfname) + { + XP_FileRemove(p_ap_decode_obj->tmpfname, xpTemporary); + /* remove tmp file if we used it */ + XP_FREE(p_ap_decode_obj->tmpfname); /* and release the file name too. */ + } + } + else if (p_ap_decode_obj->fd) + { + XP_FileClose(p_ap_decode_obj->fd); + } +#ifdef XP_MAC + if( !is_aborting ) + DecodingDone( p_ap_decode_obj); +#endif + return NOERR; + +} diff --git a/mozilla/lib/mailto/appledbl.h b/mozilla/lib/mailto/appledbl.h new file mode 100644 index 00000000000..2d255ea0db0 --- /dev/null +++ b/mozilla/lib/mailto/appledbl.h @@ -0,0 +1,232 @@ +/* -*- 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. + */ + +/* +* AppleDouble.h +* ------------- +* +* The header file for a stream based apple single/double encodor/decodor. +* +* 2aug95 mym +* +*/ + + +#ifndef AppleDouble_h +#define AppleDouble_h + +#include "xp.h" +#include "xp_file.h" +#include "msg.h" + +#define NOERR 0 +#define errDone 1 + /* Done with current operation. */ +#define errEOB 2 + /* End of a buffer. */ +#define errEOP 3 + /* End of a Part. */ + + +#define errMemoryAlloc MK_OUT_OF_MEMORY +#define errDataCrupt -1 +#define errDiskFull MK_DISK_FULL +#define errFileOpen MK_UNABLE_TO_OPEN_TMP_FILE + +#define errVersion -1 +#define errFileWrite MK_MIME_ERROR_WRITING_FILE +#define errDecoding -1 + +#define errUsrCancel MK_INTERRUPTED +/* +** The envirment block data type. +*/ +enum +{ + kInit, + kDoingHeaderPortion, + kDoneHeaderPortion, + kDoingDataPortion, + kDoneDataPortion +}; + +typedef struct _appledouble_encode_object +{ + char fname[64]; + int32 dirId; + int16 vRefNum; + int16 fileId; /* the id for the open file (data/resource fork) */ + + int state; + int text_file_type; /* if the file has a text file type with it. */ + char *boundary; /* the boundary string. */ + + int status; /* the error code if anyerror happens. */ + char b_overflow[200]; + int s_overflow; + + int state64; /* the left over state of base64 enocding */ + int ct; /* the character count of base64 encoding */ + int c1, c2; /* the left of the last base64 encoding */ + + char *outbuff; /* the outbuff by the caller. */ + int s_outbuff; /* the size of the buffer. */ + int pos_outbuff; /* the offset in the current buffer. */ + +} appledouble_encode_object; + +/* The possible content transfer encodings */ + +enum +{ + kEncodeNone, + kEncodeQP, + kEncodeBase64, + kEncodeUU +}; + +enum +{ + kGeneralMine, + kAppleDouble, + kAppleSingle +}; + +enum +{ + kInline, + kDontCare +}; + +enum +{ + kHeaderPortion, + kDataPortion +}; + +/* the decode states. */ +enum +{ + kBeginParseHeader = 3, + kParsingHeader, + kBeginSeekBoundary, + kSeekingBoundary, + kBeginHeaderPortion, + kProcessingHeaderPortion, + kBeginDataPortion, + kProcessingDataPortion, + kFinishing +}; + +/* uuencode states */ +enum +{ + kWaitingForBegin = (int) 0, + kBegin, + kMainBody, + kEnd +}; + +typedef struct _appledouble_decode_object +{ + int is_binary; + int is_apple_single; /* if the object encoded is in apple single */ + int write_as_binhex; + + int messagetype; + char* boundary0; /* the boundary for the enclosure. */ + int deposition; /* the deposition. */ + int encoding; /* the encoding method. */ + int which_part; + + char fname[256]; +#ifdef XP_MAC + FSSpec* mSpec; /* the filespec to save the file to*/ + int16 vRefNum; + int32 dirId; + int16 fileId; /* the id for the open file (data/resource fork) */ +#endif + XP_File fd; /* the fd for data fork work. */ + + MWContext *context; + NET_StreamClass* binhex_stream; /* the stream to output as binhex output.*/ + + int state; + + int rksize; /* the resource fork size count. */ + int dksize; /* the data fork size count. */ + + int status; /* the error code if anyerror happens. */ + char b_leftover[256]; + int s_leftover; + + int encode; /* the encode type of the message. */ + int state64; /* the left over state of base64 enocding */ + int left; /* the character count of base64 encoding */ + int c[4]; /* the left of the last base64 encoding */ + int uu_starts_line; /* is decoder at the start of a line? (uuencode) */ + int uu_state; /* state w/r/t the uuencode body */ + int uu_bytes_written; /* bytes written from the current tuple (uuencode) */ + int uu_line_bytes; /* encoded bytes remaining in the current line (uuencode) */ + + char *inbuff; /* the outbuff by the caller. */ + int s_inbuff; /* the size of the buffer. */ + int pos_inbuff; /* the offset in the current buffer. */ + + + char* tmpfname; /* the temp file to hold the decode data fork */ + /* when doing the binhex exporting. */ + XP_File tmpfd; + int32 data_size; /* the size of the data in the tmp file. */ + +} appledouble_decode_object; + + +/* +** The protypes. +*/ + +XP_BEGIN_PROTOS + +int ap_encode_init(appledouble_encode_object *p_ap_encode_obj, + char* fname, + char* separator); + +int ap_encode_next(appledouble_encode_object* p_ap_encode_obj, + char *to_buff, + int32 buff_size, + int32* real_size); + +int ap_encode_end(appledouble_encode_object* p_ap_encode_obj, + XP_Bool is_aborting); + +int ap_decode_init(appledouble_decode_object* p_ap_decode_obj, + XP_Bool is_apple_single, + XP_Bool write_as_bin_hex, + void *closure); + +int ap_decode_next(appledouble_decode_object* p_ap_decode_obj, + char *in_buff, + int32 buff_size); + +int ap_decode_end(appledouble_decode_object* p_ap_decode_obj, + XP_Bool is_aborting); + +XP_END_PROTOS + +#endif diff --git a/mozilla/lib/mailto/bh_strm.c b/mozilla/lib/mailto/bh_strm.c new file mode 100644 index 00000000000..a280f39170d --- /dev/null +++ b/mozilla/lib/mailto/bh_strm.c @@ -0,0 +1,411 @@ +/* -*- 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. + */ + +/* +** +** Binhex_stream.c +** --------------- +** +** The code for the binhex encode/decode stream. +** +** 20sep95 mym Created. +** +*/ + +#include "msg.h" +#include "appledbl.h" +#include "m_binhex.h" +#include "m_cvstrm.h" +#include "ad_codes.h" + +#ifdef XP_MAC +#pragma warn_unusedarg off +#endif /* XP_MAC */ + +/* +** ----------------------------------------------------------- +** +** The BinHex encode writer stream. +** +** ----------------------------------------------------------- +*/ +extern int MK_MIME_ERROR_WRITING_FILE; + +#define WORKING_BUFF_SIZE 8192 + +typedef struct +{ + binhex_encode_object bh_encode_obj; + + char *buff; /* the working buff */ + int32 s_buff; /* the size of workiong buff. */ + + XP_File fp; + char *fname; /* the filename for file holding the encoding */ + +} BinHexEncodeObject ; + + +/* +** Let's go "l" characters forward of the encoding for this write. +** Note: +** "s" is just a dummy paramter. +*/ +PRIVATE int net_BinHex_Encode_Write (void *dataObject, const char* s, int32 l) +{ + int status = 0; + BinHexEncodeObject * obj = (BinHexEncodeObject*)dataObject; + int32 count; + int32 size; + + do + { + size = obj->s_buff * 11 / 16; + size = MIN(l, size); + status = binhex_encode_next(&(obj->bh_encode_obj), + (char*)s, + size, + obj->buff, + obj->s_buff, + &count); + if (status == NOERR || status == errDone) + { + /* + * we get the encode data, so call the next stream to write it to the disk. + */ + if ((int) XP_FileWrite(obj->buff, count, obj->fp) != count ) + return errFileWrite; + } + + if (status < 0) /* abort */ + break; + + l -= size; + s += size; + + } while (l > 0); + + return status; +} + +PRIVATE unsigned int net_BinHex_Encode_Ready (NET_StreamClass *dataObject) +{ + return(MAX_WRITE_READY); /* always ready for writing */ +} + + +PRIVATE void net_BinHex_Encode_Complete (void *dataObject) +{ + int32 count, len = 0; + BinHexEncodeObject *obj = (BinHexEncodeObject *)dataObject; + + /* + ** push the close part. + */ + len = binhex_encode_next(&(obj->bh_encode_obj), + NULL, + 0, + obj->buff, + obj->s_buff, + &count); /* this help us generate the finishing */ + + len = XP_FileWrite(obj->buff, count, obj->fp); + + /* + ** time to do some dirty work -- fix the real file size. + ** (since we can only know by now) + */ + + binhex_reencode_head(&(obj->bh_encode_obj), + obj->buff, + obj->s_buff, + &count); /* get the head part encoded again */ + + XP_FileSeek(obj->fp, 0L, SEEK_SET); /* and override the previous dummy */ + XP_FileWrite(obj->buff, count, obj->fp); + + binhex_encode_end(&(obj->bh_encode_obj), FALSE); /* now we get a real ending */ + + if (obj->fp) + { + XP_FileClose(obj->fp); /* we are done with the target file */ + FREEIF(obj->fname); /* free the space for the file name */ + } + + FREEIF(obj->buff); /* and free the working buff. */ + XP_FREE(obj); +} + +PRIVATE void net_BinHex_Encode_Abort (void *dataObject, int status) +{ + BinHexEncodeObject * obj = (BinHexEncodeObject*)dataObject; + + binhex_encode_end(&(obj->bh_encode_obj), TRUE); /* it is an aborting exist... */ + + if (obj->fp) + { + XP_FileClose(obj->fp); /* we are aboring with the decoding */ + XP_FileRemove(obj->fname, xpURL); + /* remove the incomplete file. */ + FREEIF (obj->fname); /* free the space for the file name */ + } + + FREEIF(obj->buff); /* free the working buff. */ + XP_FREE(obj); +} + + +/* +** Will create a apple double encode stream: +** +** -> take the filename as the input source (it needs to be a mac file.) +** -> tkae a stream to take care of the writing to a temp file. +*/ +PUBLIC NET_StreamClass * +fe_MakeBinHexEncodeStream (int format_out, + void *data_obj, + URL_Struct *URL_s, + MWContext *window_id, + char* dst_filename ) +{ + BinHexEncodeObject* obj; + NET_StreamClass* stream; + char* working_buff = NULL; + int bSize = WORKING_BUFF_SIZE; + + TRACEMSG(("Setting up apple encode stream. Have URL: %s\n", URL_s->address)); + + stream = XP_NEW(NET_StreamClass); + if(stream == NULL) + return(NULL); + + obj = XP_NEW(BinHexEncodeObject); + if (obj == NULL) + { + XP_FREE (stream); + return(NULL); + } + + while (!working_buff && (bSize >= 512)) + { + working_buff = (char *)XP_ALLOC(bSize); + if (!working_buff) + bSize /= 2; + } + if (working_buff == NULL) + { + XP_FREE (obj); + XP_FREE (stream); + return (NULL); + } + + stream->name = "BinHex Encode"; + stream->complete = (MKStreamCompleteFunc) net_BinHex_Encode_Complete; + stream->abort = (MKStreamAbortFunc) net_BinHex_Encode_Abort; + stream->put_block = (MKStreamWriteFunc) net_BinHex_Encode_Write; + stream->is_write_ready = (MKStreamWriteReadyFunc) net_BinHex_Encode_Ready; + stream->data_object = obj; /* document info object */ + stream->window_id = window_id; + + obj->fname = XP_STRDUP(dst_filename); + obj->fp = XP_FileOpen(obj->fname, xpURL, XP_FILE_TRUNCATE); + /* this file will hold all the encoded data */ + if (obj->fp == NULL) + { + XP_FREE(working_buff); /* if we can't open the target file, roll back then */ + if(obj->fname) XP_FREE(obj->fname); + XP_FREE (obj); + XP_FREE (stream); + return (NULL); + } + + obj->buff = working_buff; + obj->s_buff = WORKING_BUFF_SIZE; + + /* + ** setup all the need information on the apple double encoder. + */ + binhex_encode_init(&(obj->bh_encode_obj)); /* pass the file name of the source.*/ + + TRACEMSG(("Returning stream from NET_BinHexEncoder\n")); + + return stream; +} + +/* +** ----------------------------------------------------------- +** +** The BinHex decode writer stream. +** +** ----------------------------------------------------------- +*/ + +typedef struct BinHexDecodeObject +{ + binhex_decode_object bh_decode_obj; + + char* in_buff; + int32 bytes_in_buff; + +} BinHexDecodeObject; + + +PRIVATE int +net_BinHex_Decode_Write ( + void *dataObject, const char* s, int32 l) +{ + int status = NOERR; + BinHexDecodeObject * obj = (BinHexDecodeObject*)dataObject; + int32 size; + + if (obj->bytes_in_buff + l > 1024) + { + size = 1024 - obj->bytes_in_buff; + XP_MEMCPY(obj->in_buff+obj->bytes_in_buff, + s, + size); + s += size; + l -= size; + + status = binhex_decode_next(&(obj->bh_decode_obj), + obj->in_buff, + 1024); + if (status != NOERR) + return status; + + obj->bytes_in_buff = 0; + } + + if (l > 1024) + { + /* we are sure that obj->bytes_in_buff == 0 at this point. */ + status = binhex_decode_next(&(obj->bh_decode_obj), + s, + l); + } + else + { + XP_MEMCPY(obj->in_buff+obj->bytes_in_buff, + s, + l); + obj->bytes_in_buff += l; + } + return status; +} + +/* + * is the stream ready for writeing? + */ +PRIVATE unsigned int net_BinHex_Decode_Ready (void *stream) +{ + return(MAX_WRITE_READY); /* always ready for writing */ +} + + +PRIVATE void net_BinHex_Decode_Complete (void *dataObject) +{ + BinHexDecodeObject *obj = (BinHexDecodeObject *) dataObject; + + if (obj->bytes_in_buff) + { + /* do the last calls. */ + binhex_decode_next(&(obj->bh_decode_obj), + (char *)obj->in_buff, + obj->bytes_in_buff); + obj->bytes_in_buff = 0; + } + + binhex_decode_end(&(obj->bh_decode_obj), FALSE); /* it is a normal clean up classes. */ + + if (obj->in_buff) + XP_FREE(obj->in_buff); + + XP_FREE(obj); +} + +PRIVATE void net_BinHex_Decode_Abort (void *dataObject, int status) +{ + BinHexDecodeObject *obj = (BinHexDecodeObject *)dataObject; + + binhex_decode_end(&(obj->bh_decode_obj), TRUE); /* it is an abort. */ + + if (obj->in_buff) + XP_FREE(obj->in_buff); + + XP_FREE(obj); +} + +/* +** Create the bin hex decode stream. +** +*/ +PUBLIC NET_StreamClass * +fe_MakeBinHexDecodeStream (int format_out, + void *data_obj, + URL_Struct *URL_s, + MWContext *window_id ) +{ + BinHexDecodeObject* obj; + NET_StreamClass* stream; + + TRACEMSG(("Setting up bin hex decode stream. Have URL: %s\n", URL_s->address)); + + stream = XP_NEW(NET_StreamClass); + if(stream == NULL) + return(NULL); + + obj = XP_NEW(BinHexDecodeObject); + if (obj == NULL) + { + XP_FREE(stream); + return(NULL); + } + + if ((obj->in_buff = (char *)XP_ALLOC(1024)) == NULL) + { + XP_FREE(obj); + XP_FREE(stream); + return (NULL); + } + + stream->name = "BinHex Decoder"; + stream->complete = (MKStreamCompleteFunc) net_BinHex_Decode_Complete; + stream->abort = (MKStreamAbortFunc) net_BinHex_Decode_Abort; + stream->put_block = (MKStreamWriteFunc) net_BinHex_Decode_Write; + stream->is_write_ready = (MKStreamWriteReadyFunc) net_BinHex_Decode_Ready; + stream->data_object = obj; + stream->window_id = window_id; + + /* + ** Some initial to the object. + */ + obj->bytes_in_buff = 0; + + /* + ** setup all the need information on the apple double encoder. + */ + binhex_decode_init(&(obj->bh_decode_obj),window_id); + #ifdef XP_MAC + obj->bh_decode_obj.mSpec = (FSSpec*)( URL_s->fe_data ); + #endif + TRACEMSG(("Returning stream from NET_BinHexDecode\n")); + + return stream; +} + + diff --git a/mozilla/lib/mailto/bytearr.cpp b/mozilla/lib/mailto/bytearr.cpp new file mode 100644 index 00000000000..c9254978926 --- /dev/null +++ b/mozilla/lib/mailto/bytearr.cpp @@ -0,0 +1,221 @@ +/* -*- 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. + */ + +#include "msg.h" +#include "xp.h" +#include "bytearr.h" + +#ifdef XP_WIN16 +#define SIZE_T_MAX 0xFF80 // Maximum allocation size +#define MAX_ARR_ELEMS SIZE_T_MAX/sizeof(BYTE) +#endif + + +XPByteArray::XPByteArray() +{ + m_nSize = 0; + m_nMaxSize = 0; + m_pData = NULL; +} + +XPByteArray::~XPByteArray() +{ + SetSize(0); +} + +///////////////////////////////////////////////////////////////////////////// + +int XPByteArray::GetSize() const +{ + return m_nSize; +} + +XP_Bool XPByteArray::SetSize(int nSize) +{ + XP_ASSERT(nSize >= 0); + +#ifdef MAX_ARR_ELEMS + if (nSize > MAX_ARR_ELEMS); + { + XP_ASSERT(nSize <= MAX_ARR_ELEMS); // Will fail + return FALSE; + } +#endif + + if (nSize == 0) + { + // Remove all elements + XP_FREE(m_pData); + m_nSize = 0; + m_nMaxSize = 0; + m_pData = NULL; + } + else if (m_pData == NULL) + { + // Create a new array + m_nMaxSize = MAX(8, nSize); + m_pData = (BYTE *)XP_CALLOC(1, m_nMaxSize * sizeof(BYTE)); + if (m_pData) + m_nSize = nSize; + else + m_nSize = m_nMaxSize = 0; + } + else if (nSize <= m_nMaxSize) + { + // The new size is within the current maximum size, make sure new + // elements are to initialized to zero + if (nSize > m_nSize) + XP_MEMSET(&m_pData[m_nSize], 0, (nSize - m_nSize) * sizeof(BYTE)); + + m_nSize = nSize; + } + else + { + // The array needs to grow, figure out how much + int nGrowBy, nMaxSize; + nGrowBy = MIN(1024, MAX(8, m_nSize / 8)); + nMaxSize = MAX(nSize, m_nMaxSize + nGrowBy); +#ifdef MAX_ARR_ELEMS + nMaxSize = MIN(MAX_ARR_ELEMS, nMaxSize); +#endif + + BYTE *pNewData = (BYTE *)XP_ALLOC(nMaxSize * sizeof(BYTE)); + if (pNewData) + { + // Copy the data from the old array to the new one + XP_MEMCPY(pNewData, m_pData, m_nSize * sizeof(BYTE)); + + // Zero out the remaining elements + XP_MEMSET(&pNewData[m_nSize], 0, (nSize - m_nSize) * sizeof(BYTE)); + m_nSize = nSize; + m_nMaxSize = nMaxSize; + + // Free the old array + XP_FREE(m_pData); + m_pData = pNewData; + } + } + + return nSize == m_nSize; +} + +///////////////////////////////////////////////////////////////////////////// + +BYTE &XPByteArray::ElementAt(int nIndex) +{ + XP_ASSERT(nIndex >= 0 && nIndex < m_nSize); + return m_pData[nIndex]; +} + +BYTE XPByteArray::GetAt(int nIndex) const +{ + XP_ASSERT(nIndex >= 0 && nIndex < m_nSize); + return m_pData[nIndex]; +} + +void XPByteArray::SetAt(int nIndex, BYTE newElement) +{ + XP_ASSERT(nIndex >= 0 && nIndex < m_nSize); + m_pData[nIndex] = newElement; +} + +///////////////////////////////////////////////////////////////////////////// + +int XPByteArray::Add(BYTE newElement) +{ + int nIndex = m_nSize; + +#ifdef MAX_ARR_ELEMS + if (nIndex >= MAX_ARR_ELEMS) + return -1; +#endif + + SetAtGrow(nIndex, newElement); + return nIndex; +} + +void XPByteArray::InsertAt(int nIndex, BYTE newElement, int nCount) +{ + XP_ASSERT(nIndex >= 0); + XP_ASSERT(nCount > 0); + + if (nIndex >= m_nSize) + { + // If the new element is after the end of the array, grow the array + SetSize(nIndex + nCount); + } + else + { + // The element is being insert inside the array + int nOldSize = m_nSize; + SetSize(m_nSize + nCount); + + // Move the data after the insertion point + XP_MEMMOVE(&m_pData[nIndex + nCount], &m_pData[nIndex], + (nOldSize - nIndex) * sizeof(BYTE)); + } + + // Insert the new elements + XP_ASSERT(nIndex + nCount <= m_nSize); + while (nCount--) + m_pData[nIndex++] = newElement; +} + +void XPByteArray::InsertAt(int nStartIndex, const XPByteArray *pNewArray) +{ + XP_ASSERT(nStartIndex >= 0); + XP_ASSERT(pNewArray != NULL); + + if (pNewArray->GetSize() > 0) + { + InsertAt(nStartIndex, pNewArray->GetAt(0), pNewArray->GetSize()); + for (int i = 1; i < pNewArray->GetSize(); i++) + m_pData[nStartIndex + i] = pNewArray->GetAt(i); + } +} + +void XPByteArray::RemoveAll() +{ + SetSize(0); +} + +void XPByteArray::RemoveAt(int nIndex, int nCount) +{ + XP_ASSERT(nIndex >= 0); + XP_ASSERT(nIndex + nCount <= m_nSize); + + if (nCount > 0) + { + // Make sure not to overstep the end of the array + int nMoveCount = m_nSize - (nIndex + nCount); + if (nCount && nMoveCount >= 0) + XP_MEMMOVE(&m_pData[nIndex], &m_pData[nIndex + nCount], + nMoveCount * sizeof(BYTE)); + + m_nSize -= nCount; + } +} + +void XPByteArray::SetAtGrow(int nIndex, BYTE newElement) +{ + XP_ASSERT(nIndex >= 0); + + if (nIndex >= m_nSize) + SetSize(nIndex+1); + m_pData[nIndex] = newElement; +} diff --git a/mozilla/lib/mailto/bytearr.h b/mozilla/lib/mailto/bytearr.h new file mode 100644 index 00000000000..7b3e6914c9d --- /dev/null +++ b/mozilla/lib/mailto/bytearr.h @@ -0,0 +1,64 @@ +/* -*- 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. + */ + +#ifndef _ByteArray_H_ +#define _ByteArray_H_ + +#if !defined(_WINDOWS) && !defined(XP_OS2) +typedef uint8 BYTE; +#endif + +class XPByteArray +{ +public: + // Construction/destruction + XPByteArray(); + ~XPByteArray(); + + // State/attribute member functions + int GetSize() const; + XP_Bool SetSize(int nNewSize); + + // Accessor member functions + BYTE &ElementAt(int nIndex); + BYTE GetAt(int nIndex) const; + void SetAt(int nIndex, BYTE newElement); + + // Insertion/deletion member functions + int Add(BYTE newElement); + void InsertAt(int nIndex, BYTE newElement, int nCount = 1); + void InsertAt(int nStartIndex, const XPByteArray *pNewArray); + void RemoveAll(); + void RemoveAt(int nIndex, int nCount = 1); + void SetAtGrow(int nIndex, BYTE newElement); + + // Overloaded operators + BYTE operator[](int nIndex) const { return GetAt(nIndex); } + BYTE &operator[](int nIndex) { return ElementAt(nIndex); } + + // Use the result carefully, it is only valid until another function called on the array + BYTE *GetArray(void) {return((BYTE *)m_pData);} + +protected: + // Member data + int m_nSize; + int m_nMaxSize; + BYTE* m_pData; +}; + +#endif diff --git a/mozilla/lib/mailto/dwordarr.cpp b/mozilla/lib/mailto/dwordarr.cpp new file mode 100644 index 00000000000..c5c01eba195 --- /dev/null +++ b/mozilla/lib/mailto/dwordarr.cpp @@ -0,0 +1,281 @@ +/* -*- 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. + */ +#include "msg.h" +#include "xp.h" +#include "dwordarr.h" +#include "xp_qsort.h" + +#ifdef XP_WIN16 +#define SIZE_T_MAX 0xFF80 // Maximum allocation size +#define MAX_ARR_ELEMS SIZE_T_MAX/sizeof(uint32) +#endif + + +XPDWordArray::XPDWordArray() +{ + m_nSize = 0; + m_nMaxSize = 0; + m_nGrowBy = 0; + m_pData = NULL; +} + +XPDWordArray::~XPDWordArray() +{ + SetSize(0); +} + +///////////////////////////////////////////////////////////////////////////// + +int XPDWordArray::GetSize() const +{ + return m_nSize; +} + +XP_Bool XPDWordArray::SetSize(int nSize, int nGrowBy) +{ + XP_ASSERT(nSize >= 0); + + if (nGrowBy >= 0) + m_nGrowBy = nGrowBy; + +#ifdef MAX_ARR_ELEMS + if (nSize > MAX_ARR_ELEMS); + { + XP_ASSERT(nSize <= MAX_ARR_ELEMS); // Will fail + return FALSE; + } +#endif + + if (nSize == 0) + { + // Remove all elements + XP_FREE(m_pData); + m_nSize = 0; + m_nMaxSize = 0; + m_pData = NULL; + } + else if (m_pData == NULL) + { + // Create a new array + m_nMaxSize = MAX(8, nSize); + m_pData = (uint32 *)XP_CALLOC(1, m_nMaxSize * sizeof(uint32)); + if (m_pData) + m_nSize = nSize; + else + m_nSize = m_nMaxSize = 0; + } + else if (nSize <= m_nMaxSize) + { + // The new size is within the current maximum size, make sure new + // elements are to initialized to zero + if (nSize > m_nSize) + XP_MEMSET(&m_pData[m_nSize], 0, (nSize - m_nSize) * sizeof(uint32)); + + m_nSize = nSize; + } + else + { + // The array needs to grow, figure out how much + int nMaxSize; + nGrowBy = MAX(m_nGrowBy, MIN(1024, MAX(8, m_nSize / 8))); + nMaxSize = MAX(nSize, m_nMaxSize + nGrowBy); +#ifdef MAX_ARR_ELEMS + nMaxSize = MIN(MAX_ARR_ELEMS, nMaxSize); +#endif + + uint32 *pNewData = (uint32 *)XP_ALLOC(nMaxSize * sizeof(uint32)); + if (pNewData) + { + // Copy the data from the old array to the new one + XP_MEMCPY(pNewData, m_pData, m_nSize * sizeof(uint32)); + + // Zero out the remaining elements + XP_MEMSET(&pNewData[m_nSize], 0, (nSize - m_nSize) * sizeof(uint32)); + m_nSize = nSize; + m_nMaxSize = nMaxSize; + + // Free the old array + XP_FREE(m_pData); + m_pData = pNewData; + } + } + + return nSize == m_nSize; +} + +///////////////////////////////////////////////////////////////////////////// + +uint32 &XPDWordArray::ElementAt(int nIndex) +{ + XP_ASSERT(nIndex >= 0 && nIndex < m_nSize); + return m_pData[nIndex]; +} + +uint32 XPDWordArray::GetAt(int nIndex) const +{ + XP_ASSERT(nIndex >= 0 && nIndex < m_nSize); + return m_pData[nIndex]; +} + +uint32 *XPDWordArray::GetData() +{ + return m_pData; +} + +void XPDWordArray::SetAt(int nIndex, uint32 newElement) +{ + XP_ASSERT(nIndex >= 0 && nIndex < m_nSize); + m_pData[nIndex] = newElement; +} + +///////////////////////////////////////////////////////////////////////////// + +int XPDWordArray::Add(uint32 newElement) +{ + int nIndex = m_nSize; + +#ifdef MAX_ARR_ELEMS + if (nIndex >= MAX_ARR_ELEMS) + return -1; +#endif + + SetAtGrow(nIndex, newElement); + return nIndex; +} + +uint XPDWordArray::Add(uint32 *elementPtr, uint numElements) +{ + if (SetSize(m_nSize + numElements)) + XP_MEMCPY(m_pData + m_nSize, elementPtr, numElements * sizeof(uint32)); + + return m_nSize; +} + +uint32 *XPDWordArray::CloneData() +{ + uint32 *copyOfData = (uint32 *)XP_ALLOC(m_nSize * sizeof(uint32)); + if (copyOfData) + XP_MEMCPY(copyOfData, m_pData, m_nSize * sizeof(uint32)); + + return copyOfData; +} + +void XPDWordArray::InsertAt(int nIndex, uint32 newElement, int nCount) +{ + XP_ASSERT(nIndex >= 0); + XP_ASSERT(nCount > 0); + + if (nIndex >= m_nSize) + { + // If the new element is after the end of the array, grow the array + SetSize(nIndex + nCount); + } + else + { + // The element is being insert inside the array + int nOldSize = m_nSize; + SetSize(m_nSize + nCount); + + // Move the data after the insertion point + XP_MEMMOVE(&m_pData[nIndex + nCount], &m_pData[nIndex], + (nOldSize - nIndex) * sizeof(uint32)); + } + + // Insert the new elements + XP_ASSERT(nIndex + nCount <= m_nSize); + while (nCount--) + m_pData[nIndex++] = newElement; +} + +void XPDWordArray::InsertAt(int nStartIndex, const XPDWordArray *pNewArray) +{ + XP_ASSERT(nStartIndex >= 0); + XP_ASSERT(pNewArray != NULL); + + if (pNewArray->GetSize() > 0) + { + InsertAt(nStartIndex, pNewArray->GetAt(0), pNewArray->GetSize()); + for (int i = 1; i < pNewArray->GetSize(); i++) + m_pData[nStartIndex + i] = pNewArray->GetAt(i); + } +} + +void XPDWordArray::RemoveAll() +{ + SetSize(0); +} + +void XPDWordArray::RemoveAt(int nIndex, int nCount) +{ + XP_ASSERT(nIndex >= 0); + XP_ASSERT(nIndex + nCount <= m_nSize); + + if (nCount > 0) + { + // Make sure not to overstep the end of the array + int nMoveCount = m_nSize - (nIndex + nCount); + if (nCount && nMoveCount) + XP_MEMMOVE(&m_pData[nIndex], &m_pData[nIndex + nCount], + nMoveCount * sizeof(uint32)); + + m_nSize -= nCount; + } +} + +void XPDWordArray::SetAtGrow(int nIndex, uint32 newElement) +{ + XP_ASSERT(nIndex >= 0); + + if (nIndex >= m_nSize) + SetSize(nIndex+1); + m_pData[nIndex] = newElement; +} + +///////////////////////////////////////////////////////////////////////////// + +void XPDWordArray::CopyArray(XPDWordArray *oldA) +{ + CopyArray(*oldA); +} + +void XPDWordArray::CopyArray(XPDWordArray &oldA) +{ + if (m_pData) + XP_FREE(m_pData); + m_nSize = oldA.m_nSize; + m_nMaxSize = oldA.m_nMaxSize; + m_pData = (uint32 *)XP_ALLOC(m_nSize * sizeof(uint32)); + if (m_pData) + XP_MEMCPY(m_pData, oldA.m_pData, m_nSize * sizeof(uint32)); +} + +///////////////////////////////////////////////////////////////////////////// + +static int CompareDWord (const void *v1, const void *v2) +{ + // QuickSort callback to compare array values + uint32 i1 = *(uint32 *)v1; + uint32 i2 = *(uint32 *)v2; + return i1 - i2; +} + +void XPDWordArray::QuickSort (int (*compare) (const void *elem1, const void *elem2)) +{ + if (m_nSize > 1) + XP_QSORT (m_pData, m_nSize, sizeof(void*), compare ? compare : CompareDWord); +} diff --git a/mozilla/lib/mailto/dwordarr.h b/mozilla/lib/mailto/dwordarr.h new file mode 100644 index 00000000000..e47fd19c63e --- /dev/null +++ b/mozilla/lib/mailto/dwordarr.h @@ -0,0 +1,68 @@ +/* -*- 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. + */ +#ifndef _DWordArray_H_ +#define _DWordArray_H_ + +class XPDWordArray +{ +public: + // Construction/destruction + XPDWordArray(); + ~XPDWordArray(); + + // State/attribute member functions + int GetSize() const; + XP_Bool SetSize(int nNewSize, int nGrowBy = -1); + + // Accessor member functions + uint32 &ElementAt(int nIndex); + uint32 GetAt(int nIndex) const; + uint32 *GetData(); + void SetAt(int nIndex, uint32 newElement); + + // Insertion/deletion member functions + int Add(uint32 newElement); + uint Add(uint32 *elementPtr, uint numElements); + void InsertAt(int nIndex, uint32 newElement, int nCount = 1); + void InsertAt(int nStartIndex, const XPDWordArray *pNewArray); + void RemoveAll(); + void RemoveAt(int nIndex, int nCount = 1); + void SetAtGrow(int nIndex, uint32 newElement); + + // Sorting member functions + void QuickSort(int (*compare) (const void *elem1, const void *elem2) = NULL); + + // Overloaded operators + uint32 operator[](int nIndex) const { return GetAt(nIndex); } + uint32 &operator[](int nIndex) { return ElementAt(nIndex); } + + // Miscellaneous member functions + uint32 *CloneData(); + void CopyArray(XPDWordArray *oldA); + void CopyArray(XPDWordArray &oldA); + +protected: + // Member data + int m_nSize; + int m_nMaxSize; + int m_nGrowBy; + uint32* m_pData; +}; + + +#endif // _DWordArray_H_ diff --git a/mozilla/lib/mailto/errcode.h b/mozilla/lib/mailto/errcode.h new file mode 100644 index 00000000000..147bebddee2 --- /dev/null +++ b/mozilla/lib/mailto/errcode.h @@ -0,0 +1,118 @@ +/* -*- 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. + */ +// errcode.h + +#ifndef _ERRCODE_H_ +#define _ERRCODE_H_ + + +/* +** Error classifications (Database, RPC, Network, etc) +** +** Each error classification defines a range of up to 0xffff error codes. +** +** The top word of an msgERR is the classification, the low byte is an error +** offset within that classification. +** +** For diagnostic purposes, each classification can register it's strings for GetErrorString +** support. These are NOT nationalizable strings; the string tables are autogenerated from the +** MsgError() macro machinery, not stored in .rc files. They're intended for use in debugging, +** logfiles, etc. +*/ + +typedef uint32 MsgERR; +typedef MsgERR msgErrorClass; + +#define msgErrorDb 0xFF000000 /* DB errors (generic) */ +#define msgErrorVw 0xF8000000 /* View layer */ +#define msgErrorMisc 0xF0000000 /* Random miscellaneous errors. */ +#define msgErrorOk 0x00000000 + +/* +** GetErrorClass(msgERR) +** +** Returns classification range for an error. +** +** For example, +** if (GetErrorClass(err) == msgErrorRp) +** Alert("Problem chatting with forum service."); +** +*/ +#define GetErrorClass(err) ((err) & ((MsgERR) 0xFFFF0000)) + +/* +** RegisterErrorClass (msgErrorClass, pFn) +** +** Register a function to return error-strings in the given class. +** +** (Note: this could be best done by automatically building a linked list of +** diagnostic routines/classes - by simply using a C++ class for error +** classes, and a static constructor to link each class into the queue. +** However, we've had problems with static constructors within DLL's +** that use MFC and DLL's on NT - so we're avoiding them for now.) +** +** See dberror.cpp (in the database) for an example of an auto-generated diagnostic +** map. The error-code to string mapping function implementation can be generated +** automatically, but the client must register the function manually. +** +** There is no limit on how many ranges a given diagnostic function might handle. +** The function must be registered once for each class, of course. +** +** Normally there will be a function for every specific .h that defines error codes +** (whether that .h defines one or more classes of error codes). For example, +** dberror.h defines 3 error class ranges, so the DB registers one diagnostic function +** that handles those 3 classes accordingly. +** +*/ +//typedef const char * (msgCALLBACK *msgErrorDiagnosticCallback) (msgERR); + +//extern void RegisterErrorClass (msgErrorClass, msgErrorDiagnosticCallback pFn); + +/* +** NOTE: remainder of this file may be multiply-included +** place all one-shot definitions above this point +*/ +#endif // __msgERROR_H__ + + +/* +** Within an error-class range, the errors are allocated/declared independently. +** See, for example, dberror.h, rperror.h, ecerror.h, etc. +*/ + +/* msgError: wrapper-macro for an error code definition */ +#ifndef MsgError +#define MsgError(errorclass, name, offset) const MsgERR name = (MsgERR) (errorclass + offset); +#endif + +/* msgErrorDiagnostic: causes wrapper-macro to generate a diagnostic string */ +#ifndef MsgErrorDiagnostic +#define MsgErrorDiagnostic(errorclass, name, offset) case ((errorclass)+(offset)): return #name; +#endif + +/* +** msgSUCCESS - universal no-error indication (zero). +** '! msgERR' is guaranteed to mean success. +*/ +#ifndef MsgErrorBasic +#define MsgErrorBasic +MsgError (msgErrorOk, eSUCCESS, 0x0000) /* 0 Success */ +MsgError (msgErrorMisc, eUNKNOWN, 0x0001) // 1 Something weird + // happened. +#endif + diff --git a/mozilla/lib/mailto/error.h b/mozilla/lib/mailto/error.h new file mode 100644 index 00000000000..0de180f0764 --- /dev/null +++ b/mozilla/lib/mailto/error.h @@ -0,0 +1,54 @@ +/* -*- 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. + */ +#ifndef DBERROR_H +#define DBERROR_H + +#include "errcode.h" + +/* General errors */ +MsgError (msgErrorDb, eFAILURE, 0xFFFF) /* -1 Unqualified DB failure */ +MsgError (msgErrorDb, eBAD_PARAMETER, 0xFFFE) /* -2 Null pointer, empty string, etc. */ +MsgError (msgErrorDb, eMORE, 0xFFFD) /* -3 More work to do. */ +MsgError (msgErrorDb, eNYI, 0xFFFC) /* -4 Not yet implemented */ +MsgError (msgErrorDb, eEXCEPTION, 0xFFFB) /* -5 An unexpected exception occurred */ +MsgError (msgErrorDb, eBAD_VIEW_INTF, 0xFFFA) /* -6 COM intf to view layer is out of sync */ +MsgError (msgErrorDb, eBAD_DB_INTF, 0xFFF9) /* -7 COM intf to db is out of sync */ +MsgError (msgErrorDb, eID_NOT_FOUND, 0xFFF8) /* -8 Message id not found */ +MsgError (msgErrorDb, eDBEndOfList, 0xFFF7) /* -9 iterator reached end of db */ +MsgError (msgErrorDb, eBAD_URL, 0xFFF5) /* -11 Unable to parse URL */ +/* NewsRC errors */ +MsgError (msgErrorDb, eNewsRCError, 0xFFF4) /* -10 General news rc error */ +/* News errors */ +MsgError (msgErrorDb, eXOverParseError, 0xFFF3) /* -13 Error parsing XOver data */ +/* Internal db errors */ +MsgError (msgErrorDb, eDBExistsNot, 0xFFF2) /* -14 db doesn't exist */ +MsgError (msgErrorDb, eDBNotOpen, 0xFFF1) /* -15 our db is not open (or ptr NULL) */ +MsgError (msgErrorDb, eErrorOpeningDB, 0xFFF0) /* -16 Error opening internal db */ +MsgError (msgErrorDb, eOldSummaryFile, 0xFFE0) /* -32 Attempt to oepn old summary file format */ +MsgError (msgErrorDb, eNullView, 0xFFE1) /* -31 View Null */ +MsgError (msgErrorDb, eNotThread, 0xFFE2) /* -30 operation requires a thread */ +MsgError (msgErrorDb, eNoSummaryFile, 0xFFE3) /* -31 summary file was gone */ +MsgError (msgErrorDb, eBuildViewInBackground, 0xFFE4) /* -32 db is huge - build view in background */ +MsgError (msgErrorDb, eCorruptDB, 0xFFE5) /* -33 db is corrupt - throw it away */ +/* Platform System Errors */ + +MsgError (msgErrorDb, eOUT_OF_MEMORY, 0xFF7F) /* -129 Out of memory for buffer or file-level objects */ + +/* file errors */ +MsgError (msgErrorDb, eWRITE_ERROR, 0xFFC0) /* -64 Error writing to disk */ +#endif diff --git a/mozilla/lib/mailto/m_binhex.c b/mozilla/lib/mailto/m_binhex.c new file mode 100644 index 00000000000..8947378df42 --- /dev/null +++ b/mozilla/lib/mailto/m_binhex.c @@ -0,0 +1,1041 @@ +/* -*- 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. + */ +/* +* +* Mac_BinHex.c +* ------------ +* +* The decode and encode for BinHex 4.0 +* +* 09sep95 mym Created +* 18sep95 mym Added the functions to do encoding from +* the input stream instead of file. +*/ + +#include "msg.h" +#include "appledbl.h" +#include "m_binhex.h" +#include "ad_codes.h" +/* for XP_GetString() */ +#include "xpgetstr.h" + +#ifdef XP_MAC +#include +#pragma warn_unusedarg off +#endif + +extern int MK_MSG_SAVE_DECODED_AS; + +extern int MK_UNABLE_TO_OPEN_TMP_FILE; +extern int MK_MIME_ERROR_WRITING_FILE; +extern int MK_MIME_DATA_CORRUPTED; + +static char BinHexTable[64] = +{ + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x30, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36, 0x38, 0x39, 0x40, 0x41, + 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x50, 0x51, 0x52, + 0x53, 0x54, 0x55, 0x56, 0x58, 0x59, 0x5a, 0x5b, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x70, 0x71, 0x72 +}; + +/* + * The encode for bin hex format. + */ + +PRIVATE int binhex_fill_data( + binhex_encode_object* p_bh_encode_obj, char c) +{ + int i; + + if (p_bh_encode_obj->pos_outbuff >= p_bh_encode_obj->s_outbuff) + { + p_bh_encode_obj->overflow[p_bh_encode_obj->s_overflow++] = c; + } + else + { + p_bh_encode_obj->outbuff[p_bh_encode_obj->pos_outbuff++] =c; + } + + if (++p_bh_encode_obj->line_length == 64) + { + /* + ** Incase the new line is 2 character. LRCR + */ + for(i = 1; i <= p_bh_encode_obj->newline[0]; i++) + binhex_fill_data(p_bh_encode_obj, p_bh_encode_obj->newline[i]); + + p_bh_encode_obj->line_length = 0; + } + + return p_bh_encode_obj->s_overflow ? errEOB : NOERR; +} + +/************************************************************************ + * EncodeDataChar - encode an 8-bit data char into a six-bit buffer + * returns the number of valid encoded characters generated + ************************************************************************/ +PRIVATE int binhex_encode_data_char( + binhex_encode_object *p_bh_encode_obj, + unsigned char c) +{ + int status = 0; + + switch (p_bh_encode_obj->state86++) + { + case 0: + status = binhex_fill_data(p_bh_encode_obj, + BinHexTable[(c>>2)&0x3f]); + p_bh_encode_obj->saved_bits = (c&0x3)<<4; + break; + case 1: + status = binhex_fill_data(p_bh_encode_obj, + BinHexTable[p_bh_encode_obj->saved_bits | ((c>>4)&0xf)]); + p_bh_encode_obj->saved_bits = (c&0xf)<<2; + break; + case 2: + status = binhex_fill_data(p_bh_encode_obj, + BinHexTable[p_bh_encode_obj->saved_bits | ((c>>6)&0x3)]); + if (status != NOERR) + break; + + status = binhex_fill_data(p_bh_encode_obj, + BinHexTable[c&0x3f]); + p_bh_encode_obj->state86 = 0; + break; + } + return status; +} + +#define BYTEMASK 0xff +#define BYTEBIT 0x100 +#define WORDMASK 0xffff +#define WORDBIT 0x10000 +#define CRCCONSTANT 0x1021 + +#define WOW { \ + c <<= 1; \ + if ((temp <<= 1) & WORDBIT) \ + temp = (temp & WORDMASK) ^ CRCCONSTANT; \ + temp ^= (c >> 8); \ + c &= BYTEMASK; \ + } + +PRIVATE void binhex_comp_q_crc_out( + binhex_encode_object *p_bh_encode_obj, uint16 c) +{ + register uint32 temp = p_bh_encode_obj->CRC; + + WOW; + WOW; + WOW; + WOW; + WOW; + WOW; + WOW; + WOW; + p_bh_encode_obj->CRC = temp; +} + +PRIVATE int binhex_encode_buff( + binhex_encode_object *p_bh_encode_obj, + unsigned char* data, + int size) +{ + int i, status = 0; + unsigned char dc; + + for (i = 0; i < size; i++) + { + dc = *data++; + + status = binhex_encode_data_char(p_bh_encode_obj, dc); + if ((char)dc == (char)0x90) + status = binhex_encode_data_char(p_bh_encode_obj, 0); + + if (status != NOERR) + break; + + binhex_comp_q_crc_out(p_bh_encode_obj, dc); /* and compute the CRC too */ + } + return status; +} + +PRIVATE int binhex_encode_end_a_part( + binhex_encode_object* p_bh_encode_obj) +{ + int status; + uint16 tempCrc; + + /* + ** write the CRC to the encode. + */ + binhex_comp_q_crc_out(p_bh_encode_obj, 0); + binhex_comp_q_crc_out(p_bh_encode_obj, 0); + tempCrc = (uint16)(p_bh_encode_obj->CRC & 0xffff); + tempCrc = htons(tempCrc); + status = binhex_encode_buff(p_bh_encode_obj, + (unsigned char*)&tempCrc, + sizeof(uint16)); + p_bh_encode_obj->CRC = 0; + return status; +} + +PRIVATE int binhex_encode_finishing( + binhex_encode_object *p_bh_encode_obj) +{ + int i, status = 0; + + if (p_bh_encode_obj->state86) + status = binhex_encode_buff(p_bh_encode_obj, (unsigned char*)&status, 1); + + /* + ** The close token. + */ + status = binhex_fill_data(p_bh_encode_obj, ':'); + + for (i=1; i <= p_bh_encode_obj->newline[0]; i++) + status = binhex_fill_data(p_bh_encode_obj, + p_bh_encode_obj->newline[i]); + + return errDone; +} + + +int binhex_encode_init(binhex_encode_object *p_bh_encode_obj) +{ + /* + ** init all the status. + */ + XP_MEMSET(p_bh_encode_obj, 0, sizeof(binhex_encode_object)); + + p_bh_encode_obj->line_length = 1; + + p_bh_encode_obj->newline[0] = 2; + p_bh_encode_obj->newline[1] = CR; + p_bh_encode_obj->newline[2] = LF; /* to confirm with rfc822, use CRLF */ + + return NOERR; +} + +int binhex_encode_next( + binhex_encode_object* p_bh_encode_obj, + char *in_buff, + int32 in_size, + char *out_buff, + int32 buff_size, + int32 *real_size) +{ + int status = 0; + /* + ** setup the buffer information. + */ + p_bh_encode_obj->outbuff = out_buff; + p_bh_encode_obj->s_outbuff = buff_size; + p_bh_encode_obj->pos_outbuff = 0; + + /* + ** copy over the left over from last time. + */ + if (p_bh_encode_obj->s_overflow) + { + XP_MEMCPY(p_bh_encode_obj->overflow, + p_bh_encode_obj->outbuff, + p_bh_encode_obj->s_overflow); + + p_bh_encode_obj->pos_outbuff = p_bh_encode_obj->s_overflow; + p_bh_encode_obj->s_overflow = 0; + } + + /* + ** Jump to the right state. + */ + if ( p_bh_encode_obj->state < BINHEX_STATE_DONE) + { + if (in_buff == NULL && in_size == 0) + { + /* this is our special token of end of a part, time to append crc codes */ + if (p_bh_encode_obj->state != BINHEX_STATE_FINISH) + status = binhex_encode_end_a_part(p_bh_encode_obj); + else + status = binhex_encode_finishing(p_bh_encode_obj); + + p_bh_encode_obj->state += 2; /* so we can jump to the next state.*/ + } + else + { + if (p_bh_encode_obj->state == BINHEX_STATE_START) + { + XP_STRCPY(p_bh_encode_obj->outbuff + p_bh_encode_obj->pos_outbuff, + "\r\n(This file must be converted with BinHex 4.0)\r\n\r\n:"); + p_bh_encode_obj->pos_outbuff += 52; + + p_bh_encode_obj->state = BINHEX_STATE_HEADER; + + XP_MEMCPY(p_bh_encode_obj->name, + in_buff, + in_size); + } + else if (p_bh_encode_obj->state == BINHEX_STATE_HEADER) + { + XP_MEMCPY(&(p_bh_encode_obj->head), + in_buff, + sizeof(binhex_header)); + + if (in_size == 20) /* in the platform that alignment is 4-bytes. */ + in_size = 18; + + p_bh_encode_obj->head.dlen = 0; /* we just can't trust the dlen from */ + /* apple double decoder told us. */ + /* do our own counting. */ + } + else if (p_bh_encode_obj->state == BINHEX_STATE_DFORK) + { + if (p_bh_encode_obj->head.dlen == 0) + { + p_bh_encode_obj->c[0] = in_buff[0]; /* save the first 2 bytes, in case */ + p_bh_encode_obj->c[1] = in_buff[1]; /* head and data share 1 code block */ + } + p_bh_encode_obj->head.dlen += in_size; + } + + status = binhex_encode_buff(p_bh_encode_obj, + (unsigned char *)in_buff, + in_size); + } + } + *real_size = p_bh_encode_obj->pos_outbuff; + return status; +} + +/* +** Only generate the header part of the encoding, +** so we can fix up the +*/ +int binhex_reencode_head( + binhex_encode_object *p_bh_encode_obj, + char* outbuff, + int32 buff_size, + int32* real_size) +{ + int32 size, dlen; + int status; + char buff[64]; + + p_bh_encode_obj->state = 0; + p_bh_encode_obj->state86 = 0; + p_bh_encode_obj->CRC = 0; + p_bh_encode_obj->line_length= 1; + p_bh_encode_obj->saved_bits = 0; + p_bh_encode_obj->s_overflow = 0 ; + + status = binhex_encode_next( + p_bh_encode_obj, + p_bh_encode_obj->name, + p_bh_encode_obj->name[0]+2, /* in_size */ + outbuff, + buff_size, + real_size); + if (status != NOERR) + return status; + + size = *real_size; + + /* now we should have the right data length in the head structure, but don't */ + /* forget convert it back to the net byte order (i.e., Motolora) before write it */ + /* */ + /* Note: since we don't change the size of rlen, so don't need to worry about it */ + + p_bh_encode_obj->head.dlen = htonl(dlen = p_bh_encode_obj->head.dlen); + + /* make a copy before do the encoding, -- it may modify the head!!!. */ + XP_MEMCPY(buff, (char*)&p_bh_encode_obj->head, + sizeof(binhex_header)); + if (18 < sizeof(binhex_header)) + { + /* we get an alignment problem here. */ + XP_MEMCPY(buff + 10, buff + 12, 8); + } + + status = binhex_encode_next( + p_bh_encode_obj, + (char*)buff, + 18, /* sizeof(binhex_header),*/ + outbuff + size, + buff_size - size, + real_size); + if (status != NOERR) + return status; + + size += *real_size; + + status = binhex_encode_next( /* for CRC */ + p_bh_encode_obj, + NULL, + 0, /* in_size */ + outbuff + size, + buff_size - size, + real_size); + + if (status != NOERR) + return status; + + size += *real_size; + + if (p_bh_encode_obj->state86 != 0) + { + /* + ** Make sure we don't destroy the orignal valid coding. + ** + ** (Keep in mind that 3 characters share 4 coding chars, + ** so it is possible for the head and data stream share one 4 code group. + ** + ** How about only one or zero character in the data fork? + ** ---- just rerun the encoding, not a big deal. + */ + if (dlen <= 1) + { + /* why just rerun the encoding once more. */ + status = binhex_encode_next( + p_bh_encode_obj, + p_bh_encode_obj->c, + dlen, + outbuff + size, + buff_size - size, + real_size); + if (status != NOERR) + return status; + + size += *real_size; /* encode the data fork */ + + status = binhex_encode_next( + p_bh_encode_obj, + NULL, + 0, + outbuff + size, + buff_size - size, + real_size); + if (status != NOERR) + return status; + + size += *real_size; /* for the end up data fork */ + + status = binhex_encode_next( + p_bh_encode_obj, + NULL, + 0, + outbuff + size, + buff_size - size, + real_size); /* for the end up encoding*/ + } + else + { + status = binhex_encode_next( + p_bh_encode_obj, + p_bh_encode_obj->c, + 3 - p_bh_encode_obj->state86, /* in_size */ + outbuff + size, + buff_size - size, + real_size); + } + size += *real_size; + } + *real_size = size; + + return status; +} + +int binhex_encode_end ( + binhex_encode_object *p_bh_encode_obj, + XP_Bool is_aborting) +{ + return NOERR; +} + + +/* +** The decode's. +*/ +static char binhex_decode[256] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, + 13, 14, 15, 16, 17, 18, 19, -1, 20, 21, -1, -1, -1, -1, -1, -1, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, + 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, -1, -1, -1, -1, + 48, 49, 50, 51, 52, 53, 54, -1, 55, 56, 57, 58, 59, 60, -1, -1, + 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +#define BHEXVAL(c) (binhex_decode[(unsigned char) c]) + +/* +** the decode for bin hex format. +*/ +int binhex_decode_init ( + binhex_decode_object *p_bh_decode_obj, + MWContext *context) +{ + XP_MEMSET(p_bh_decode_obj, 0, sizeof(binhex_decode_object)); + + p_bh_decode_obj->octetin = 26; + p_bh_decode_obj->donepos = 3; + p_bh_decode_obj->context = context; + + return NOERR; +} + +static void +simple_copy(MWContext* context, char* newFile, void* closure) +{ + XP_STRCPY((char *)closure, newFile); +} + +PRIVATE void binhex_process( + binhex_decode_object *p_bh_decode_obj) +{ + int32 status; + uint16 tmpcrc, cval; + unsigned char ctmp, c = p_bh_decode_obj->rlebuf; + + /* do CRC */ + ctmp = p_bh_decode_obj->inCRC ? c : 0; + cval = p_bh_decode_obj->CRC & 0xf000; + tmpcrc = ((uint16) (p_bh_decode_obj->CRC << 4) | + (ctmp >> 4)) + ^ (cval | (cval >> 7) | + (cval >> 12)); + cval = tmpcrc & 0xf000; + p_bh_decode_obj->CRC = ((uint16) (tmpcrc << 4) | + (ctmp & 0x0f)) + ^ (cval | (cval >> 7) | + (cval >> 12)); + + /* handle state */ + switch (p_bh_decode_obj->state) + { + case BINHEX_STATE_START: + p_bh_decode_obj->state = BINHEX_STATE_FNAME; + p_bh_decode_obj->count = 1; +#ifndef XP_MAC + p_bh_decode_obj->name = XP_ALLOC(64); +#endif + *(p_bh_decode_obj->name) = (c & 63); + break; + + case BINHEX_STATE_FNAME: + p_bh_decode_obj->name[p_bh_decode_obj->count] = c; + + if (p_bh_decode_obj->count++ > *(p_bh_decode_obj->name)) + { +#if 0 + char* p; + /* convert it to the c-string too. */ + c = *(p_bh_decode_obj->name); + p = p_bh_decode_obj->name; + + while (c--) + { + *p = *(p+1); p++; + } + + *p = '\0'; +#endif + p_bh_decode_obj->state = BINHEX_STATE_HEADER; + p_bh_decode_obj->count = 0; + } + break; + + case BINHEX_STATE_HEADER: + ((char *)&p_bh_decode_obj->head)[p_bh_decode_obj->count] = c; + if (++p_bh_decode_obj->count == 18) + { +#ifndef XP_MAC + if (sizeof(binhex_header) != 18) /* fix the alignment problem in some OS */ + { + char *p = (char *)&p_bh_decode_obj->head; + p += 19; + for (c = 0; c < 8; c++) + { + *p = *(p-2); p--; + } + } +#endif + p_bh_decode_obj->state = BINHEX_STATE_HCRC; + p_bh_decode_obj->inCRC = 1; + p_bh_decode_obj->count = 0; + } + break; + + case BINHEX_STATE_DFORK: + case BINHEX_STATE_RFORK: + p_bh_decode_obj->outbuff[p_bh_decode_obj->pos_outbuff++] = c; + if (-- p_bh_decode_obj->count == 0) + { +#ifdef XP_MAC + long howMuch = p_bh_decode_obj->pos_outbuff; + status = FSWrite(p_bh_decode_obj->fileId, + &howMuch, + p_bh_decode_obj->outbuff); + FSClose(p_bh_decode_obj->fileId); +#else + /* only output data fork in the non-mac system. */ + if (p_bh_decode_obj->state == BINHEX_STATE_DFORK) + { + status = XP_FileWrite(p_bh_decode_obj->outbuff, + p_bh_decode_obj->pos_outbuff, + p_bh_decode_obj->fileId) + == p_bh_decode_obj->pos_outbuff ? NOERR : errFileWrite; + + XP_FileClose(p_bh_decode_obj->fileId); + } + else + { + status = NOERR; /* do nothing for resource fork. */ + } +#endif + p_bh_decode_obj->pos_outbuff = 0; + + if (status != NOERR) + p_bh_decode_obj->state = status; + else + { + p_bh_decode_obj->state ++; + p_bh_decode_obj->fileId = 0; + } + p_bh_decode_obj->inCRC = 1; + } + else if (p_bh_decode_obj->pos_outbuff >= MAX_BUFF_SIZE) + { +#ifdef XP_MAC + long howMuch = p_bh_decode_obj->pos_outbuff; + status = FSWrite(p_bh_decode_obj->fileId, + &howMuch, + p_bh_decode_obj->outbuff); +#else + if (p_bh_decode_obj->state == BINHEX_STATE_DFORK) + { + status = XP_FileWrite(p_bh_decode_obj->outbuff, + p_bh_decode_obj->pos_outbuff, + p_bh_decode_obj->fileId) + == p_bh_decode_obj->pos_outbuff ? NOERR : errFileWrite; + } + else + { + status = NOERR; /* don't care about the resource fork. */ + } +#endif + if (status != NOERR) + p_bh_decode_obj->state = status; + + p_bh_decode_obj->pos_outbuff = 0; + } + break; + + case BINHEX_STATE_HCRC: + case BINHEX_STATE_DCRC: + case BINHEX_STATE_RCRC: + if (!p_bh_decode_obj->count++) + { + p_bh_decode_obj->fileCRC = (unsigned short) c << 8; + } + else + { + if ((p_bh_decode_obj->fileCRC | c) != p_bh_decode_obj->CRC) + { + if (p_bh_decode_obj->state > BINHEX_STATE_HCRC) + { +#ifdef XP_MAC + HDelete(p_bh_decode_obj->vRefNum, + p_bh_decode_obj->parID, + (unsigned char*)p_bh_decode_obj->name); +#else + XP_FileRemove(p_bh_decode_obj->name, xpURL); +#endif + } + p_bh_decode_obj->state = errDecoding; + break; + } + + /* + ** passed the CRC check!!! + */ + p_bh_decode_obj->CRC = 0; + if (++ p_bh_decode_obj->state == BINHEX_STATE_FINISH) + { +#ifdef XP_MAC + FInfo finfo; + + /* set back the file information.before we declare done ! */ + finfo.fdType = p_bh_decode_obj->head.type; + finfo.fdCreator = p_bh_decode_obj->head.creator; + finfo.fdFlags = p_bh_decode_obj->head.flags & 0xf800; + + HSetFInfo(p_bh_decode_obj->vRefNum, + p_bh_decode_obj->parID, + (unsigned char *)p_bh_decode_obj->name, + &finfo); +#endif + /* now We are done with everything. */ + p_bh_decode_obj->state++; + break; + } + + if (p_bh_decode_obj->state == BINHEX_STATE_DFORK) + { +#ifdef XP_MAC + StandardFileReply reply; + if( !p_bh_decode_obj->mSpec ) + { + StandardPutFile("\pSave decoded file as:", + (unsigned char *)p_bh_decode_obj->name, + &reply); + + if (!reply.sfGood) + { + p_bh_decode_obj->state = errUsrCancel; + break; + } + } + else + { + reply.sfFile.vRefNum = p_bh_decode_obj->mSpec->vRefNum; + reply.sfFile.parID = p_bh_decode_obj->mSpec->parID; + XP_MEMCPY(&reply.sfFile.name, p_bh_decode_obj->mSpec->name , 63 ); + } + + XP_MEMCPY(p_bh_decode_obj->name, + reply.sfFile.name, + *(reply.sfFile.name)+1); /* save the new file name. */ + + p_bh_decode_obj->vRefNum = reply.sfFile.vRefNum; + p_bh_decode_obj->parID = reply.sfFile.parID; + + HDelete(reply.sfFile.vRefNum, + reply.sfFile.parID, + reply.sfFile.name); + + status = HCreate(p_bh_decode_obj->vRefNum, + p_bh_decode_obj->parID, + reply.sfFile.name, + p_bh_decode_obj->head.creator, + p_bh_decode_obj->head.type); +#else /* non-mac OS case */ + char* filename; + + filename = XP_ALLOC(1024); + if (filename == NULL || + FE_PromptForFileName(p_bh_decode_obj->context, + XP_GetString(MK_MSG_SAVE_DECODED_AS), + 0, + FALSE, + FALSE, + simple_copy, + filename) == -1) + { + FREEIF(filename); + p_bh_decode_obj->state = errUsrCancel; + break; + } + + FREEIF(p_bh_decode_obj->name); + p_bh_decode_obj->name = XP_STRDUP(filename); + p_bh_decode_obj->fileId + = XP_FileOpen(filename, + xpURL, + XP_FILE_TRUNCATE_BIN); + if (p_bh_decode_obj->fileId == NULL) + status = errFileOpen; + else + status = NOERR; + + XP_FREE(filename); + +#endif + if (status != NOERR) + p_bh_decode_obj->state = status; + + p_bh_decode_obj->count + = ntohl(p_bh_decode_obj->head.dlen); + } + else + { + p_bh_decode_obj->count + = ntohl(p_bh_decode_obj->head.rlen); /* it should in host byte order */ + } + + if (p_bh_decode_obj->count) + { + p_bh_decode_obj->inCRC = 0; +#ifdef XP_MAC + if (p_bh_decode_obj->state == BINHEX_STATE_DFORK) + status = HOpen(p_bh_decode_obj->vRefNum, + p_bh_decode_obj->parID, + (unsigned char*)p_bh_decode_obj->name, + fsWrPerm, + &(p_bh_decode_obj->fileId)); + else + status = HOpenRF(p_bh_decode_obj->vRefNum, + p_bh_decode_obj->parID, + (unsigned char*)p_bh_decode_obj->name, + fsWrPerm, + &(p_bh_decode_obj->fileId)); + if (status != NOERR) + { + p_bh_decode_obj->state = errFileOpen; + HDelete(p_bh_decode_obj->vRefNum, + p_bh_decode_obj->parID, + (unsigned char*)p_bh_decode_obj->name); + break; + } +#else + /* for None Mac OS -- nothing is required, file already open. */ + +#endif + } + else + { + /* nothing inside, so skip to the next state. */ + p_bh_decode_obj->state ++; + } + } + break; + } + + return; +} + +static int get_next_char(binhex_decode_object *p_bh_decode_obj) +{ + char c = 0; + + while (p_bh_decode_obj->pos_inbuff < p_bh_decode_obj->s_inbuff) + { + c = p_bh_decode_obj->inbuff[p_bh_decode_obj->pos_inbuff++]; + if (c != LF && c != CR) + break; + } + return (c == LF || c == CR) ? 0 : (int) c; +} + +int binhex_decode_next ( + binhex_decode_object *p_bh_decode_obj, + const char *in_buff, + int32 buff_size) +{ + int found_start; + int octetpos, c = 0; + uint32 val; + + /* + ** reset the buff first. + */ + p_bh_decode_obj->inbuff = (char*)in_buff; + p_bh_decode_obj->s_inbuff = buff_size; + p_bh_decode_obj->pos_inbuff = 0; + + /* + ** if it is the first time, seek to the right start place. + */ + if (p_bh_decode_obj->state == BINHEX_STATE_START) + { + found_start = FALSE; + /* + ** go through the line, until we get a ':' + */ + while (p_bh_decode_obj->pos_inbuff < p_bh_decode_obj->s_inbuff) + { + c = p_bh_decode_obj->inbuff[p_bh_decode_obj->pos_inbuff++]; + while (c == CR || c == LF) + { + if (p_bh_decode_obj->pos_inbuff >= p_bh_decode_obj->s_inbuff) + break; + + c = p_bh_decode_obj->inbuff[p_bh_decode_obj->pos_inbuff++]; + if (c == ':') + { + found_start = TRUE; + break; + } + } + if (found_start) break; /* we got the start point. */ + } + + if (p_bh_decode_obj->pos_inbuff >= p_bh_decode_obj->s_inbuff) + return NOERR; /* we meet buff end before we get the */ + /* start point, wait till next fills. */ + + if (c != ':') + return errDecoding; /* can't find the start character. */ + } + + /* + ** run - through the in-stream now. + */ + while (p_bh_decode_obj->state >= 0 && + p_bh_decode_obj->state < BINHEX_STATE_DONE) + { + /* fill in octetbuf */ + do + { + if (p_bh_decode_obj->pos_inbuff >= p_bh_decode_obj->s_inbuff) + return NOERR; /* end of buff, go on for the nxet calls. */ + + c = get_next_char(p_bh_decode_obj); + if (c == 0) + return NOERR; + + if ((val = BHEXVAL(c)) == -1) + { + /* + ** we incount an invalid character. + */ + if (c) + { + /* + ** rolling back. + */ + p_bh_decode_obj->donepos --; + if (p_bh_decode_obj->octetin >= 14) p_bh_decode_obj->donepos--; + if (p_bh_decode_obj->octetin >= 20) p_bh_decode_obj->donepos--; + } + break; + } + p_bh_decode_obj->octetbuf.val |= val << p_bh_decode_obj->octetin; + } + while ((p_bh_decode_obj->octetin -= 6) > 2); + + /* handle decoded characters -- run length encoding (rle) detection */ + +#ifndef XP_MAC + p_bh_decode_obj->octetbuf.val + = ntohl(p_bh_decode_obj->octetbuf.val); +#endif + + for (octetpos = 0; octetpos < p_bh_decode_obj->donepos; ++octetpos) + { + c = p_bh_decode_obj->octetbuf.c[octetpos]; + + if (c == 0x90 && !p_bh_decode_obj->marker++) + continue; + + if (p_bh_decode_obj->marker) + { + if (c == 0) + { + p_bh_decode_obj->rlebuf = 0x90; + binhex_process(p_bh_decode_obj); + } + else + { + while (--c > 0) /* we are in the run lenght mode */ + { + binhex_process(p_bh_decode_obj); + } + } + p_bh_decode_obj->marker = 0; + } + else + { + p_bh_decode_obj->rlebuf = (unsigned char) c; + binhex_process(p_bh_decode_obj); + } + + + if (p_bh_decode_obj->state >= BINHEX_STATE_FINISH) + break; + } + + /* prepare for next 3 characters. */ + if (p_bh_decode_obj->donepos < 3 && p_bh_decode_obj->state < BINHEX_STATE_FINISH) + p_bh_decode_obj->state = errDecoding; + + p_bh_decode_obj->octetin = 26; + p_bh_decode_obj->octetbuf.val = 0; + } + + /* + ** Error clean-ups + */ + if (p_bh_decode_obj->state < 0 && p_bh_decode_obj->fileId) + { +#ifdef XP_MAC + FSClose(p_bh_decode_obj->fileId); + p_bh_decode_obj->fileId = 0; + HDelete(p_bh_decode_obj->vRefNum, + p_bh_decode_obj->parID, + (unsigned char*)p_bh_decode_obj->name); +#else + XP_FileClose(p_bh_decode_obj->fileId); + p_bh_decode_obj->fileId = 0; + XP_FileRemove(p_bh_decode_obj->name, xpURL); +#endif + } + + + return p_bh_decode_obj->state < 0 ? (p_bh_decode_obj->state) : + p_bh_decode_obj->state >= BINHEX_STATE_FINISH ? errDone : NOERR; +} + +int binhex_decode_end ( + binhex_decode_object *p_bh_decode_obj, + XP_Bool is_aborting) +{ +#ifdef XP_MAC + if (p_bh_decode_obj->fileId) + { + FSClose(p_bh_decode_obj->fileId); + p_bh_decode_obj->fileId = 0; + + if (is_aborting) + { + HDelete(p_bh_decode_obj->vRefNum, + p_bh_decode_obj->parID, + (unsigned char*)p_bh_decode_obj->name); + } + } + +#else + + if (p_bh_decode_obj->fileId) + { + XP_FileClose(p_bh_decode_obj->fileId); + p_bh_decode_obj->fileId = NULL; + + if (is_aborting) + XP_FileRemove(p_bh_decode_obj->name, xpURL); + } + FREEIF(p_bh_decode_obj->name); +#endif + + return NOERR; +} + + diff --git a/mozilla/lib/mailto/m_binhex.h b/mozilla/lib/mailto/m_binhex.h new file mode 100644 index 00000000000..7f55e76d036 --- /dev/null +++ b/mozilla/lib/mailto/m_binhex.h @@ -0,0 +1,184 @@ +/* -*- 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. + */ +/* +* The head file for Bin Hex 4.0 encode/decode +* ------------------------------------------- +* +* 10sep95 mym created +*/ + +#ifndef binhex_h +#define binhex_h + +#ifdef XP_MAC +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=mac68k +#endif +#endif /* XP_MAC */ + +#define BINHEX_STATE_START 0 +#define BINHEX_STATE_FNAME 1 +#define BINHEX_STATE_HEADER 2 +#define BINHEX_STATE_HCRC 3 +#define BINHEX_STATE_DFORK 4 +#define BINHEX_STATE_DCRC 5 +#define BINHEX_STATE_RFORK 6 +#define BINHEX_STATE_RCRC 7 +#define BINHEX_STATE_FINISH 8 +#define BINHEX_STATE_DONE 9 +/* #define BINHEX_STATE_ERROR 10 */ + +/* +** The Definitions for the binhex encoder +*/ +typedef struct _binhex_header +{ + uint32 type, creator; + uint16 flags; + int32 dlen, rlen ; + +} binhex_header; + +typedef struct _binhex_encode_object +{ + int state; /* progress state. */ + + int state86; /* binhex encode state. */ + unsigned long CRC; /* accumulated CRC */ + int line_length; /* the line length count */ + char saved_bits; + + int s_inbuff; /* the inbuff size */ + int pos_inbuff; /* the inbuff position */ + char* inbuff; /* the inbuff pool */ + + int s_outbuff; /* the outbuff size */ + int pos_outbuff; /* the outbuff position */ + char* outbuff; /* the outbuf pool */ + + int s_overflow; /* the real size of overflow */ + char overflow[32]; /* a small overflow buffer */ + + char c[2]; + char newline[4]; /* the new line char seq. */ + + /* -- for last fix up. -- */ + + char name[64]; + binhex_header head; + +} binhex_encode_object; + +/* +** The defination for the binhex decoder. +** NOTE: This define is for Mac only. +*/ + +typedef union +{ + unsigned char c[4]; + uint32 val; + +} longbuf; + +#define MAX_BUFF_SIZE 256 + +typedef struct _binhex_decode_object +{ + int state; /* current state */ + uint16 CRC; /* cumulative CRC */ + uint16 fileCRC; /* CRC value from file */ + + longbuf octetbuf; /* buffer for decoded 6-bit values */ + int16 octetin; /* current input position in octetbuf */ + int16 donepos; /* ending position in octetbuf */ + int16 inCRC; /* flag set when reading a CRC */ + + int32 count; /* generic counter */ + int16 marker; /* flag indicating maker */ + unsigned char rlebuf; /* buffer for last run length encoding value */ + + binhex_header head; /* buffer for header */ + +#ifdef XP_MAC + FSSpec* mSpec; + char name[64]; /* fsspec for the output file */ + int16 vRefNum; + int32 parID ; + int16 fileId; /* the refnum of the output file */ +#else + char *name; /* file name for the output file in non-mac OS */ + XP_File fileId; /* the file if for the outpur file. non-mac OS */ +#endif + + MWContext* context; /* context for call back function. */ + + int32 s_inbuff; /* the valid size of the inbuff */ + int32 pos_inbuff; /* the index of the inbuff. */ + char* inbuff; /* the inbuff pointer. */ + int32 pos_outbuff; /* the position of the out buff. */ + char outbuff[MAX_BUFF_SIZE]; + +} binhex_decode_object; + +XP_BEGIN_PROTOS + +/* +** The binhex file encode prototypes. +*/ +int binhex_encode_init(binhex_encode_object *p_bh_encode_obj); + +int binhex_encode_next(binhex_encode_object *p_bh_encode_obj, + char *in_buff, + int32 in_size, + char *out_buff, + int32 buff_size, + int32 *real_size); + +int binhex_encode_end (binhex_encode_object *p_bh_encode_obj, + XP_Bool is_aborting); + +int binhex_reencode_head( + binhex_encode_object *p_bh_encode_obj, + char* outbuff, + int32 buff_size, + int32* real_size); + + +/* +** The binhex stream decode prototypes. +*/ + +int binhex_decode_init(binhex_decode_object *p_bh_decode_env, + MWContext *context); + +int binhex_decode_next(binhex_decode_object *p_bh_decode_env, + const char *in_buff, + int32 buff_size); +int binhex_decode_end (binhex_decode_object *p_bh_decode_env, + XP_Bool is_aborting); + +XP_END_PROTOS + +#ifdef XP_MAC +#if PRAGMA_ALIGN_SUPPORTED +#pragma options align=reset +#endif +#endif /* XP_MAC */ + +#endif /* binhex_h */ diff --git a/mozilla/lib/mailto/mhtmlstm.cpp b/mozilla/lib/mailto/mhtmlstm.cpp new file mode 100644 index 00000000000..fbe1c0a602b --- /dev/null +++ b/mozilla/lib/mailto/mhtmlstm.cpp @@ -0,0 +1,885 @@ +/* -*- 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. + */ +/* mhtmlstm.c --- generation of MIME HTML. + */ + +//#ifdef MSG_SEND_MULTIPART_RELATED + +#include "msg.h" +#include "net.h" +#include "structs.h" +#include "xp_core.h" +#include "mhtmlstm.h" +#include "xp_mem.h" +#include "xp_str.h" +#include "xp_mcom.h" +#include "xp_file.h" +#include "prprf.h" +#include "xpassert.h" +#include "msgsend.h" +#include "msgsendp.h" +#include "mimeenc.h" +#include "libi18n.h" + +extern "C" { + extern int MK_UNABLE_TO_OPEN_FILE; +} + + + +/* Defined in libnet/mkcache.c */ +extern "C" int NET_FindURLInCache(URL_Struct * URL_s, MWContext *ctxt); + +/* Defined in layout/editor.cpp */ +extern "C" XP_Bool EDT_IsSameURL(char *url1,char *url2,char *base1,char *base2); + +/* +---------------------------------------------------------------------- +MSG_MimeRelatedFileInfo +---------------------------------------------------------------------- +*/ + + +/* +---------------------------------------------------------------------- +MSG_MimeRelatedStreamOut +---------------------------------------------------------------------- +*/ + +class MSG_MimeRelatedStreamOut : public IStreamOut +{ +public: + MSG_MimeRelatedStreamOut(XP_File file); + ~MSG_MimeRelatedStreamOut(void); + + virtual void Write( char *pBuffer, int32 iCount ); + //static int WriteMimeData( const char *pBuffer, int32 iCount, void *closure); + + virtual EOutStreamStatus Status(); + + virtual intn Close(void); + + XP_File m_file; + XP_Bool m_hasWritten; + //MimeEncoderData *m_encoder; + EOutStreamStatus m_status; +}; + + + +MSG_MimeRelatedStreamOut::MSG_MimeRelatedStreamOut(XP_File file) + : m_file(file), m_hasWritten(FALSE), m_status(EOS_NoError) +{ +} + +MSG_MimeRelatedStreamOut::~MSG_MimeRelatedStreamOut(void) +{ + Close(); +} + +void +MSG_MimeRelatedStreamOut::Write(char *pBuffer, int32 iCount) +{ +// if (!m_encoder) +// (void) WriteMimeData(pBuffer, iCount, this); +// else +// (void) MimeEncoderWrite(m_encoder, pBuffer, iCount); + + if (m_status != EOS_NoError) + return; + + // For now, pass through the information. + XP_ASSERT(m_file); + int numBytes = 0; + if (m_file) + { + numBytes = XP_FileWrite(pBuffer, iCount, m_file); + if (numBytes != iCount) + { + TRACEMSG(("MSG_MimeRelatedStreamOut::Write(): error: %d written != %ld to write",numBytes, (long)iCount)); + if (m_hasWritten) + m_status = EOS_DeviceFull; // wrote in the past, so this means the disk is full + else + m_status = EOS_FileError; // couldn't even write once, so assume it's some other problem + } + } + + if (!m_hasWritten) m_hasWritten = TRUE; +} + +//int +//MSG_MimeRelatedStreamOut::WriteMimeData(const char *pBuffer, int32 iCount, void *closure) +//{ +//} + +IStreamOut::EOutStreamStatus +MSG_MimeRelatedStreamOut::Status(void) +{ + return m_status; +} + +intn +MSG_MimeRelatedStreamOut::Close(void) +{ + if (m_file) + { + XP_FileClose(m_file); + m_file = 0; + } + return 0; +} + +/* +---------------------------------------------------------------------- +MSG_MimeRelatedSubpart +---------------------------------------------------------------------- +*/ + +char * +MSG_MimeRelatedSubpart::GenerateCIDHeader(void) +{ + char *header; + + // If we have a Content-ID, generate that. + header = (m_pContentID ? + PR_smprintf("Content-ID: <%s>", m_pContentID) : 0); + // If we have no Content-ID but an original URL, send that. + if (!header && m_pOriginalURL) + header = PR_smprintf("Content-Location: %s", m_pOriginalURL); + // If none of the above and we have a local URL, use that instead. + if (!header && m_pLocalURL) + header = PR_smprintf("Content-Location: %s", m_pLocalURL); + if (!header) + header = XP_STRDUP(""); + + return header; +} + +char * +MSG_MimeRelatedSubpart::GenerateEncodingHeader(void) +{ + char *header = NULL; + + if (m_pEncoding) + header = PR_smprintf("Content-Transfer-Encoding: %s", m_pEncoding); + else + header = XP_STRDUP(""); + + return header; +} + +char * +MSG_MimeRelatedSubpart::GetContentID(XP_Bool bAttachMIMEPrefix) +{ + char *result = NULL; + + if (m_pContentID) + { + result = PR_smprintf("%s%s", (bAttachMIMEPrefix ? "cid:" : ""), + m_pContentID); + } + + return result; +} + + +int +MSG_MimeRelatedSubpart::GetStreamOut(IStreamOut **pReturn) +{ + int result = 0; + + if (!m_pStreamOut) + { + if (m_filename) + { + XP_File file = XP_FileOpen(m_filename, xpFileToPost, XP_FILE_WRITE_BIN); + if (file) + { + m_pStreamOut = new MSG_MimeRelatedStreamOut(file); + } + } + } + if (!m_pStreamOut) + result = MK_UNABLE_TO_OPEN_FILE; /* -1; rb */ + *pReturn = (IStreamOut *) m_pStreamOut; + + return result; +} + +int +MSG_MimeRelatedSubpart::CloseStreamOut(void) +{ + int result = 0; + + if (m_pStreamOut) + { + delete m_pStreamOut; // this will close the stream + m_pStreamOut = NULL; + } + + return result; +} + +MSG_MimeRelatedSubpart::MSG_MimeRelatedSubpart(MSG_MimeRelatedSaver *parent, + char *pContentID, + char *pOriginal, char *pLocal, + char *pMime, int16 part_csid, char *pFilename) + : MSG_SendPart(NULL, part_csid), m_pOriginalURL(NULL), + m_pLocalURL(NULL), m_pParentFS(parent), + m_pContentID(NULL), m_pContentName(NULL), m_rootPart(FALSE) +{ + m_filetype = xpFileToPost; + + if (pOriginal) + m_pOriginalURL = XP_STRDUP(pOriginal); + if (pLocal) + m_pLocalURL = XP_STRDUP(pLocal); + if (pMime) + m_type = XP_STRDUP(pMime); + if (pContentID) + m_pContentID = XP_STRDUP(pContentID); + + if ((!m_pOriginalURL) && (!m_type)) + { + // Assume we're saving an untitled HTML document (the root part) if + // we're given neither the name nor the type. + m_type = XP_STRDUP(TEXT_HTML); + } + + if (pFilename) + { + CopyString(&m_filename, pFilename); + m_rootPart = TRUE; + } + else + { + // Generate a temp name for a file to which to write. + char *tmp = WH_TempName(xpFileToPost, "nsmail"); + if (tmp) + { + CopyString(&m_filename, tmp); + XP_FREE(tmp); + } + } + + // If we have a filename, create the file now so that + // the Mac doesn't generate the same file name twice. + if (m_filename) + { + XP_File fp = XP_FileOpen(m_filename, xpFileToPost, XP_FILE_WRITE_BIN); + if (fp) + XP_FileClose(fp); + } + + //XP_ASSERT(m_type != NULL); + XP_ASSERT(m_filename != NULL); + //XP_ASSERT(m_pContentID != NULL); +} + +MSG_MimeRelatedSubpart::~MSG_MimeRelatedSubpart(void) +{ + // Close any streams we may have had open. + if (m_pStreamOut) + delete m_pStreamOut; + + // Delete the file we represent. + if (m_filename) + { + XP_FileRemove(m_filename, xpFileToPost); + } + + XP_FREEIF(m_pOriginalURL); + XP_FREEIF(m_pLocalURL); + XP_FREEIF(m_pContentID); + XP_FREEIF(m_pEncoding); + XP_FREEIF(m_pContentName); + + m_pOriginalURL = m_pLocalURL = NULL; +} + +int +MSG_MimeRelatedSubpart::WriteEncodedMessageBody(const char *buf, int32 size, + void *pPart) +{ + MSG_MimeRelatedSubpart *subpart = (MSG_MimeRelatedSubpart *) pPart; + int returnVal = 0; + + XP_ASSERT(subpart->m_state != NULL); + if (subpart->m_state) + returnVal = mime_write_message_body(subpart->m_state, (char *) buf, size); + + return returnVal; +} + +void +MSG_MimeRelatedSubpart::CopyURLInfo(const URL_Struct *pURL) +{ + char *suffix = NULL; + if (pURL != NULL) + { + // Get the MIME type if we have it. + if (pURL->content_type && *(pURL->content_type)) + SetType(pURL->content_type); + + // Look for a content name in this order: + // 1. If we have a content name, use that as is. + // 2. If we have a content type, find an extension + // corresponding to the content type, and attach it to + // the temp filename. + // 3. If we have neither a content name nor a content type, + // duplicate the temp filename as is. (Yuck.) + if (pURL->content_name && *(pURL->content_name)) + m_pContentName = XP_STRDUP(pURL->content_name); + + else if (pURL->content_type && (suffix = NET_cinfo_find_ext(pURL->content_type)) != NULL) + { + // We found an extension locally, add it to the temp name. + char *end = XP_STRRCHR(m_filename, '.'); + + if (end) + *end = '\0'; + + m_pContentName = PR_smprintf("%s.%s", m_filename, suffix); + + if (end) + *end = '.'; + } + } + + if (!m_pContentName) + m_pContentName = XP_STRDUP(m_filename); +} + +int +MSG_MimeRelatedSubpart::Write(void) +{ + // If we weren't given the mime type by the editor, + // then attempt to deduce it from what information we can get. + + if ((m_pOriginalURL) && (!m_type)) + { + // We weren't explicitly given the MIME type, so + // we ask the cache if it knows anything about this URL. + URL_Struct testUrl; + XP_MEMSET(&testUrl, 0, sizeof(URL_Struct)); + + testUrl.address = m_pOriginalURL; + int findResult = NET_FindURLInCache(&testUrl, + m_pParentFS->GetContext()); + if ((findResult != 0) && (testUrl.content_type)) + { + // Got a MIME type from the cache. + m_type = XP_STRDUP(testUrl.content_type); + } + } + + if ((m_pOriginalURL) && (!m_type)) + { + // Either we didn't find the URL in the cache, or + // we have it but don't know its MIME type. + // So, we trot out our last resort: attempt to grok + // the MIME type based on the filename. (Mr. Yuk says: "Yuk.") + + NET_cinfo *pMimeInfo = NET_cinfo_find_type(m_pOriginalURL); + if ((pMimeInfo) && (pMimeInfo->type)) + { + // Got a MIME type based on the filename. + m_type = XP_STRDUP(pMimeInfo->type); + } + } + + if (!m_type) + { + // No matter what we've done, we will never figure out the type. + // So, we punt and call it an application/octet-stream. + m_type = XP_STRDUP(APPLICATION_OCTET_STREAM); + } + + // Determine what the encoding of this data should be depending + // on the MIME type. This is fairly braindead: base64 encode anything + // that isn't text. + if (m_type && (!m_rootPart)) + { + // Uuencode only if we have to, otherwise use base64 + if (m_pParentFS->m_pPane-> + GetCompBoolHeader(MSG_UUENCODE_BINARY_BOOL_HEADER_MASK)) + { + m_pEncoding = XP_STRDUP(ENCODING_UUENCODE); + SetEncoderData(MimeUUEncoderInit(m_pContentName ? m_pContentName : "", +#ifdef XP_OS2 + (int (_Optlink*) (const char*,int32,void*)) +#endif + WriteEncodedMessageBody, + this)); + } + else + { + m_pEncoding = XP_STRDUP(ENCODING_BASE64); + SetEncoderData(MimeB64EncoderInit( +#ifdef XP_OS2 + (int (_Optlink*) (const char*,int32,void*)) +#endif + WriteEncodedMessageBody, + this)); + } + } + + // Horrible hack: if we got a local filename then we're the root lump, + // hence we don't generate a content ID header in that case + // (but we do in all other cases where we have a content ID) + char *cidHeader = NULL; + if ((!m_rootPart) && (m_pContentID)) + { + cidHeader = GenerateCIDHeader(); + if (cidHeader) + { + AppendOtherHeaders(cidHeader); + AppendOtherHeaders(CRLF); + XP_FREE(cidHeader); + } + } + + if (m_pEncoding) + { + char *encHeader = GenerateEncodingHeader(); + if (encHeader) + { + AppendOtherHeaders(encHeader); + AppendOtherHeaders(CRLF); + XP_FREE(encHeader); + } + } + + if ((!m_rootPart) && (m_pOriginalURL)) + { + char *fileHeader = PR_smprintf("Content-Disposition: inline; filename=\"%s\"", + m_pContentName ? m_pContentName : ""); + if (fileHeader) + { + AppendOtherHeaders(fileHeader); + AppendOtherHeaders(CRLF); + XP_FREE(fileHeader); + } + } + + return MSG_SendPart::Write(); +} + +/* +---------------------------------------------------------------------- +MSG_MimeRelatedParentPart +---------------------------------------------------------------------- +*/ + +MSG_MimeRelatedParentPart::MSG_MimeRelatedParentPart(int16 part_csid) +: MSG_SendPart(NULL, part_csid) +{ +} + +MSG_MimeRelatedParentPart::~MSG_MimeRelatedParentPart(void) +{ +} + +/* +---------------------------------------------------------------------- +MSG_MimeRelatedSaver +---------------------------------------------------------------------- +*/ + +extern char * msg_generate_message_id(void); + +// Constructor +MSG_MimeRelatedSaver::MSG_MimeRelatedSaver(MSG_CompositionPane *pane, + MWContext *context, + MSG_CompositionFields *fields, + XP_Bool digest_p, + MSG_Deliver_Mode deliver_mode, + const char *body, + uint32 body_length, + MSG_AttachedFile *attachedFiles, + DeliveryDoneCallback cb, + char **ppOriginalRootURL) + : m_pContext(context), m_pBaseURL(NULL), m_pPane(pane), + m_pFields(fields), m_digest(digest_p), m_deliverMode(deliver_mode), + m_pBody(body), m_bodyLength(body_length), + m_pAttachedFiles(attachedFiles), m_cbDeliveryDone(cb), m_pSourceBaseURL(NULL) + +{ + // Generate the message ID. + m_pMessageID = msg_generate_message_id(); + if (m_pMessageID) + { + // Massage the message ID so that it can be used for generating + // part IDs. For now, just remove the angle brackets. + m_pMessageID[strlen(m_pMessageID)-1] = '\0'; // shorten the end by 1 + char *temp = XP_STRDUP(&(m_pMessageID[1])); // and strip off the leading '<' + if (temp) + { + XP_FREE(m_pMessageID); + m_pMessageID = temp; + } + } + XP_ASSERT(m_pMessageID); + + // Create the part object that we represent. + m_pPart = new MSG_MimeRelatedParentPart(INTL_DefaultWinCharSetID(context)); + XP_ASSERT(m_pPart); + + if ((ppOriginalRootURL != NULL) && *(ppOriginalRootURL)) + { + // Have a valid string of some sort, wait to be added. + } + else if (ppOriginalRootURL != NULL) + { + // ### mwelch This is a hack, required because EDT_SaveFileTo + // requires a source URL string, even if the document + // is currently untitled. The hack consists of adding + // the return parameter in the constructor, and passing + // back an improvised source URL if we were not given one. + // + // Autogenerate the title and pass it back. + m_rootFilename = WH_TempName(xpFileToPost,"nsmail"); + XP_ASSERT(m_rootFilename); + char * temp = WH_FileName(m_rootFilename, xpFileToPost); + *ppOriginalRootURL = XP_PlatformFileToURL(temp); + if (temp) + XP_FREE( temp ); + } + + // Set our type to be multipart/related. + m_pPart->SetType(MULTIPART_RELATED); +} + +// Destructor +MSG_MimeRelatedSaver::~MSG_MimeRelatedSaver(void) +{ + XP_FREEIF(m_pSourceBaseURL); + if (m_rootFilename) + { + XP_FileRemove(m_rootFilename, xpFileToPost); + XP_FREEIF(m_rootFilename); + } + XP_FREEIF(m_pMessageID); +} + +intn MSG_MimeRelatedSaver::GetType() +{ + return ITapeFileSystem::MailSend; +} + +MSG_MimeRelatedSubpart * +MSG_MimeRelatedSaver::GetSubpart(intn iFileIndex) +{ + XP_ASSERT(m_pPart != NULL); + + MSG_MimeRelatedSubpart *part = + (MSG_MimeRelatedSubpart *) m_pPart->GetChild(iFileIndex); + + return part; +} + +// This function is called before anything else. +// Tell the file system the base URL it is going to see. +void +MSG_MimeRelatedSaver::SetSourceBaseURL(char *pURL) +{ + XP_FREEIF(m_pSourceBaseURL); + m_pSourceBaseURL = XP_STRDUP(pURL); + +#if 0 + MSG_MimeRelatedSubpart *part = NULL; + // Remember the URL for later. + m_pBaseURL = pURL; + + // Add this URL as the first in the list. + if (m_pPart->GetNumChildren() == 0) + { + AddFile(pURL); + } + else + { + part = GetSubpart(0); + if (part) + { + XP_FREEIF(part->m_pOriginalURL); + part->m_pOriginalURL = (pURL == NULL) ? NULL : XP_STRDUP(pURL); + } + } + + // Fix the local URL/filename reference if we haven't already opened the file. + if (!part) + part = GetSubpart(0); + if (part && (part->m_pStreamOut == NULL)) + { + XP_FREEIF(part->m_pLocalURL); + part->m_pLocalURL = PR_smprintf("file:%s",part->GetFilename()); + XP_ASSERT(part->m_pLocalURL); + + part->SetOtherHeaders(""); // no headers for the lead part + } +#endif +} + +char* +MSG_MimeRelatedSaver::GetSourceURL(intn iFileIndex) +{ + char *result = NULL; + + MSG_MimeRelatedSubpart *part = GetSubpart(iFileIndex); + if (part->m_pOriginalURL) { + // Try to make absolute relative to value set in MSG_MimeRelatedSaver::SetSourceBaseURL(). + if (m_pSourceBaseURL) { + result = NET_MakeAbsoluteURL(m_pSourceBaseURL,part->m_pOriginalURL); + } + else { + result = XP_STRDUP(part->m_pOriginalURL); + } + } + + return result; +} + +// Add a name to the file system. +// Returns the index of the file added (0 based). +intn +MSG_MimeRelatedSaver::AddFile(char * pURL, char * pMimeType, int16 part_csid) +{ + intn returnValue = 0; + MSG_MimeRelatedSubpart *newPart = NULL; + int i; + + // See if a part with this url already exists. + for(i=0;(iGetNumChildren()) && (!newPart);i++) + { + newPart = GetSubpart(i); + // Use EDT_IsSameURL to deal with case insensitivity on MAC and Win16 + if (!newPart || + !EDT_IsSameURL(newPart->m_pOriginalURL, pURL,m_pSourceBaseURL,m_pSourceBaseURL)) { + newPart = NULL; // not this one, try the next one + } + else { + // found it. + returnValue = i; + } + } + + if (newPart == NULL) + { + // Generate a Content ID. This will look a lot like our message ID, + // except that we will add the part number. + XP_ASSERT(m_pMessageID != NULL); + char *newPartID = PR_smprintf("part%ld.%s", (long) m_pPart->GetNumChildren(), + m_pMessageID); + XP_ASSERT(newPartID != NULL); + + char *newLocalURL = PR_smprintf("cid:%s",newPartID); + XP_ASSERT(newLocalURL != NULL); + + returnValue = m_pPart->GetNumChildren(); + + if (m_pPart->GetNumChildren() == 0) + // This is the root file in the file system. + newPart = new MSG_MimeRelatedSubpart(this, newPartID, pURL, + pURL, TEXT_HTML, part_csid, + m_rootFilename); + else + newPart = new MSG_MimeRelatedSubpart(this, newPartID, pURL, + newLocalURL, pMimeType, part_csid ); + + if (newPart) + m_pPart->AddChild(newPart); + else + returnValue = (intn) ITapeFileSystem::Error; // an error since 0 is the base URL (??) + + XP_FREEIF(newPartID); + XP_FREEIF(newLocalURL); + } + + return returnValue; +} + +intn +MSG_MimeRelatedSaver::GetNumFiles(void) +{ + return m_pPart->GetNumChildren(); +} + +char * +MSG_MimeRelatedSaver::GetDestAbsURL() +{ + // No meaningful destination URL for sending mail. + return NULL; +} + +// Get the name of the relative url to place in the file. +char * +MSG_MimeRelatedSaver::GetDestURL(intn iFileIndex) +{ + char *result = NULL; + + MSG_MimeRelatedSubpart *thePart = GetSubpart(iFileIndex); + if (thePart != NULL) + { + result = XP_STRDUP(thePart->m_pLocalURL); + } + + return result; +} + +char * +MSG_MimeRelatedSaver::GetDestPathURL(void) +{ + //return XP_STRDUP(""); // no path prefix for content IDs + return NULL; +} + +// Return the name to display when saving the file. +char * +MSG_MimeRelatedSaver::GetHumanName(intn iFileIndex) +{ + char *result = NULL; + + MSG_MimeRelatedSubpart *thePart = GetSubpart(iFileIndex); + if (thePart != NULL) + { + result = thePart->m_pOriginalURL; + char *end = XP_STRRCHR(result, '/'); + if (end) + result = XP_STRDUP(++end); + else + result = XP_STRDUP(result); + } + return result; +} + +// Open the output stream. +IStreamOut * +MSG_MimeRelatedSaver::OpenStream(intn iFileIndex) +{ + intn theError = 0; + IStreamOut *pStream = NULL; // in case we fail + + // Create a stream object that can be written to. + MSG_MimeRelatedSubpart *thePart = GetSubpart(iFileIndex); + if (thePart) + { + theError = (intn) thePart->GetStreamOut(&pStream); + } + return pStream; +} + +void +MSG_MimeRelatedSaver::CopyURLInfo(intn iFileIndex, const URL_Struct *pURL) +{ + MSG_MimeRelatedSubpart *thePart = GetSubpart(iFileIndex); + if (thePart != NULL) + thePart->CopyURLInfo(pURL); +} + +// Close the output stream. +// Called on completion. bSuccess is TRUE if completed successfully, +// FALSE if it failed. +void +MSG_MimeRelatedSaver::Complete(Bool bSuccess, + EDT_ITapeFileSystemComplete *pfComplete, void *pArg ) +{ + m_pEditorCompletionFunc = pfComplete; + m_pEditorCompletionArg = pArg; + + // Call StartMessageDelivery (and should) if we + // were told to at creation time. + if (bSuccess) + { + // If we only generated a single HTML part, treat that as + // the root part. + if (m_pPart->GetNumChildren() == 1) + { + MSG_SendPart *tempPart = m_pPart->DetachChild(0); + delete m_pPart; + m_pPart = tempPart; + } + + msg_StartMessageDeliveryWithAttachments(m_pPane, this, + m_pFields, + m_digest, FALSE, + m_deliverMode, + TEXT_HTML, + m_pBody, m_bodyLength, + m_pAttachedFiles, + m_pPart, +#ifdef XP_OS2 + (void (_Optlink*) (MWContext*,void*,int,const char*)) +#endif + MSG_MimeRelatedSaver::UrlExit); + } + else + { + // delete the contained part since we failed + delete m_pPart; + m_pPart = NULL; + + // Call our UrlExit routine to perform cleanup. + UrlExit(m_pPane->GetContext(), this, MK_INTERRUPTED, NULL); + } +} + +void +MSG_MimeRelatedSaver::UrlExit(MWContext *context, void *fe_data, int status, + const char *error_message) +{ + MSG_MimeRelatedSaver *saver = (MSG_MimeRelatedSaver *) fe_data; + XP_ASSERT(saver != NULL); + if (saver) + { + if (saver->m_pEditorCompletionFunc) + { + (*(saver->m_pEditorCompletionFunc))((status == 0), + saver->m_pEditorCompletionArg); + } + if (saver->m_cbDeliveryDone) + { + (*(saver->m_cbDeliveryDone))(context,saver->m_pPane, + status,error_message); + } + } + delete saver; // the part within stays around, we don't +} + +void +MSG_MimeRelatedSaver::CloseStream( intn iFileIndex ) +{ + // Get the piece whose stream we will close. + MSG_MimeRelatedSubpart *thePart = GetSubpart(iFileIndex); + if (thePart) + { + thePart->CloseStreamOut(); + } +} + +XP_Bool +MSG_MimeRelatedSaver::FileExists( intn /*iFileIndex*/ ) +{ + return FALSE; +} + +XP_Bool +MSG_MimeRelatedSaver::IsLocalPersistentFile(intn /*iFileIndex*/) { + return FALSE; +} + +//#endif diff --git a/mozilla/lib/mailto/mhtmlstm.h b/mozilla/lib/mailto/mhtmlstm.h new file mode 100644 index 00000000000..6634156f2e7 --- /dev/null +++ b/mozilla/lib/mailto/mhtmlstm.h @@ -0,0 +1,225 @@ +/* -*- 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. + */ +/* + mhtmlstm.h --- generation of MIME HTML from the editor. + */ + +#ifndef LIBMSG_MHTMLSTM_H +#define LIBMSG_MHTMLSTM_H + +// Comment out the next line if you want to turn off the sending of multipart/related info. +#define MSG_SEND_MULTIPART_RELATED + +//#ifdef MSG_SEND_MULTIPART_RELATED + +#include "msg.h" +#include "itapefs.h" +#include "ptrarray.h" +#include "xp_file.h" +#include "ntypes.h" +#include "msgcpane.h" +#include "msgsendp.h" +#include "itapefs.h" + +class MSG_MimeRelatedStreamIn; +class MSG_MimeRelatedStreamOut; +class MSG_MimeRelatedSubpart; +class MSG_MimeRelatedSaver; +class MSG_SendMimeDeliveryState; + +typedef void (*DeliveryDoneCallback) (MWContext *context, + void *fe_data, + int status, + const char *error_message); + +// A record which represents a part of this multipart. +// Encapsulates the stuff necessary to convert MIME part data +// into message text. +class MSG_MimeRelatedSubpart : public MSG_SendPart +{ +public: + char *m_pOriginalURL; // Original URL for this lump + char *m_pLocalURL; // Local relative URL for this lump + MSG_MimeRelatedSaver *m_pParentFS; // The parent FS that includes us + char *m_pContentID; // Content ID of this lump + char *m_pEncoding; // Content transfer encoding + char *m_pContentName; // Content name (as conveyed by CopyURLInfo) + MSG_MimeRelatedStreamOut *m_pStreamOut; // Current output (to file) stream + XP_Bool m_rootPart; // Are we the first part of the multipart? + + MSG_MimeRelatedSubpart(MSG_MimeRelatedSaver *parent, + char *pContentID, + char *pOriginal, + char *pLocal, + char *pMime, + int16 part_csid, + char *pRootFileName = NULL); + + ~MSG_MimeRelatedSubpart(void); + + char *GetContentID(XP_Bool bAddMIMEPrefix); + int GetStreamOut(IStreamOut **pReturn); + int CloseStreamOut(void); + int Write(void); + + // Copy information out of the URL used to fetch its data + void CopyURLInfo(const URL_Struct *pURL); + + // Function used by the mime encoder when writing to message file + static int WriteEncodedMessageBody(const char *buf, int32 size, void *pPart); + + // Generate any additional header strings for this part + // (such as the "Content-ID:" string) + char * GenerateCIDHeader(void); + char * GenerateEncodingHeader(void); +}; + +class MSG_MimeRelatedSaver; + +class MSG_MimeRelatedParentPart : public MSG_SendPart +{ +public: + MSG_MimeRelatedParentPart(int16 part_csid); + virtual ~MSG_MimeRelatedParentPart(); + + //virtual int Write(); + + //virtual int SetFile(const char* filename, XP_FileType filetype); + //virtual int SetBuffer(const char* buffer); + //virtual int SetOtherHeaders(const char* other); + //virtual int AppendOtherHeaders(const char* moreother); + //virtual int AddChild(MSG_SendPart* child); +}; + +class MSG_MimeRelatedSaver : public ITapeFileSystem +{ +public: + MWContext * m_pContext; // Context + char * m_pBaseURL; // Base URL of this lump + char * m_pMessageID; // Message ID that we will use to generate + // unique names for each content ID + char * m_rootFilename; // Filename for root object (created at const. time) + char * m_pSourceBaseURL; // Only used to return absolute URLs for GetSourceURL() + + MSG_SendPart *m_pPart; + + // All these are parameters to be passed to StartMessageDelivery + MSG_CompositionPane *m_pPane; + MSG_CompositionFields *m_pFields; + XP_Bool m_digest; + MSG_Deliver_Mode m_deliverMode; + const char *m_pBody; + uint32 m_bodyLength; + MSG_AttachedFile *m_pAttachedFiles; + DeliveryDoneCallback m_cbDeliveryDone; + void (*m_pEditorCompletionFunc)(XP_Bool success, void *data); + void *m_pEditorCompletionArg; + + void ClearAllParts(void); + +public: + MSG_MimeRelatedSaver(MSG_CompositionPane *pane, MWContext *context, + MSG_CompositionFields *fields, + XP_Bool digest_p, MSG_Deliver_Mode deliver_mode, + const char *body, uint32 body_length, + MSG_AttachedFile *attachedFiles, + DeliveryDoneCallback cb, + char **ppOriginalRootURL); + virtual ~MSG_MimeRelatedSaver(); + + + virtual intn GetType(); + + // This function is called before anything else. It tells the file + // system the base url it is going to see. + virtual void SetSourceBaseURL( char* pURL ); + + virtual char* GetSourceURL(intn iFileIndex); + + // DESCRIPTION: + // + // Add a name to the file system. It is up to the file system to localize + // the name. For example, I could add 'http://home.netscape.com/' + // and the file system might decide that it should be called 'index.html' + // if the file system were DOS, the url might be converted to INDEX.HTML + // + // RETURNS: index of the file (0 based) + // + virtual intn AddFile( char* pURL, char *pMimeType, int16 iDocCharSetID ); + + // Count the number of files we know about. + virtual intn GetNumFiles(void); + + virtual char* GetDestAbsURL(); + + // Gets the name of the RELATIVE url to place in the file. String is + // allocated with XP_STRDUP(); + // + virtual char* GetDestURL( intn iFileIndex ); + + // String is allocated with XP_STRDUP(). + virtual char* GetDestPathURL(); + + // + // Returns the name to display when saving the file, can be the same as + // GetURLName. String is allocated with XP_STRDUP(); + // + virtual char* GetHumanName( intn iFileIndex ); + + virtual XP_Bool IsLocalPersistentFile(intn iFileIndex); + + // Does the file referenced by iFileIndex already exist? + // For the MHTML version, this will always return FALSE. + virtual XP_Bool FileExists(intn iFileIndex); + + // + // Opens the output stream. Returns a stream that can be written to. All + // 'AddFile's occur before the first OpenStream. + // + virtual IStreamOut * OpenStream( intn iFileIndex ); + + virtual void CloseStream( intn iFileIndex ); + + // ### mwelch Added so that multipart/related message saver can properly construct + // messages using quoted/forwarded part data. + // Tell the tape file system the mime type of a particular part. + // (Calling this overrides any previously determined mime type for this part.) + virtual void CopyURLInfo(intn iFileIndex, const URL_Struct *pURL); + + // + // Called on completion, TRUE if completed successfully, FALSE if it failed. + // + virtual void Complete( Bool bSuccess, EDT_ITapeFileSystemComplete *pfComplete, void *pArg ); + // Has this file system received a true multipart or just some HTML? + char *GetMimeType(void); + + MWContext *GetContext(void) const { return m_pContext; } + + char *GetMessageID(void) const { return m_pMessageID; } + + MSG_MimeRelatedSubpart *GetSubpart( intn iFileIndex ); + + // Used when the mail system is done sending the message. + // Causes the editor to clean up from the save. + static void UrlExit(MWContext *context, void *fe_data, int status, + const char *error_message); +}; + +//#endif + +#endif diff --git a/mozilla/lib/mailto/msg.h b/mozilla/lib/mailto/msg.h new file mode 100644 index 00000000000..7330853390b --- /dev/null +++ b/mozilla/lib/mailto/msg.h @@ -0,0 +1,318 @@ +/* -*- 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. + */ +/* + msg.h --- internal defs for the msg library + */ +#ifndef _MSG_H_ +#define _MSG_H_ + +#include "xp.h" +#include "msgcom.h" +#include "msgnet.h" +#include "msgutils.h" +#include "xpgetstr.h" + +#ifdef XP_CPLUSPLUS +class MessageDBView; +class MSG_SendPart; +#endif + +#define MANGLE_INTERNAL_ENVELOPE_LINES /* We always need to do this, for now */ +#undef FIXED_SEPARATORS /* this doesn't work yet */ +#define EMIT_CONTENT_LENGTH /* Experimental; and anyway, we only + emit it, we don't parse it, so this + is only the first step. */ + +/* This string gets appended to the beginning of an attachment field + during a forward quoted operation */ + +#define MSG_FORWARD_COOKIE "$forward_quoted$" + + +/* The PRINTF macro is for debugging messages of unusual events (summary */ +/* files out of date or invalid or the like. It's so that as I use the mail */ +/* to actually read my e-mail, I can look at the shell output whenever */ +/* something unusual happens so I can get some clues as to what's going on. */ +/* Please don't remove any PRINTF calls you see, and be sparing of adding */ +/* any additional ones. Thanks. - Terry */ + +#ifdef DEBUG +#define PRINTF(msg) XP_Trace msg +#else +#define PRINTF(msg) +#endif + +#ifdef FREEIF +#undef FREEIF +#endif +#define FREEIF(obj) do { if (obj) { XP_FREE (obj); obj = 0; }} while (0) + +/* The Netscape-specific header fields that we use for storing our + various bits of state in mail folders. + */ +#define X_MOZILLA_STATUS "X-Mozilla-Status" +#define X_MOZILLA_STATUS_FORMAT X_MOZILLA_STATUS ": %04.4x" +#define X_MOZILLA_STATUS_LEN /*1234567890123456*/ 16 + +#define X_MOZILLA_STATUS2 "X-Mozilla-Status2" +#define X_MOZILLA_STATUS2_FORMAT X_MOZILLA_STATUS2 ": %08.8x" +#define X_MOZILLA_STATUS2_LEN /*12345678901234567*/ 17 + +#define X_MOZILLA_DRAFT_INFO "X-Mozilla-Draft-Info" +#define X_MOZILLA_DRAFT_INFO_LEN /*12345678901234567890*/ 20 + +#define X_MOZILLA_NEWSHOST "X-Mozilla-News-Host" +#define X_MOZILLA_NEWSHOST_LEN /*1234567890123456789*/ 19 + +#define X_UIDL "X-UIDL" +#define X_UIDL_LEN /*123456*/ 6 + +#define CONTENT_LENGTH "Content-Length" +#define CONTENT_LENGTH_LEN /*12345678901234*/ 14 + +/* Provide a common means of detecting empty lines in a message. i.e. to detect the end of headers among other things...*/ +#define EMPTY_MESSAGE_LINE(buf) (buf[0] == CR || buf[0] == LF || buf[0] == '\0') + +typedef int32 MsgChangeCookie; /* used to unregister change notification */ + +/* The three ways the list of newsgroups can be pruned. + */ +typedef enum +{ + MSG_ShowAll, + MSG_ShowSubscribed, + MSG_ShowSubscribedWithArticles +} MSG_NEWSGROUP_DISPLAY_STYLE; + +/* The three ways to deliver a message. + */ +typedef enum +{ + MSG_DeliverNow, + MSG_QueueForLater, + MSG_SaveAsDraft, + MSG_SaveAsTemplate +} MSG_Deliver_Mode; + + +/* A little enum for things we'd like to learn lazily. + * e.g. displaying recipients for this pane? Yes we are, + * no we're not, haven't figured it out yet + */ +typedef enum +{ + msg_No, + msg_Yes, + msg_DontKnow +} msg_YesNoDontKnow; + +/* The MSG_REPLY_TYPE shares the same space as MSG_CommandType, to avoid + possible weird errors, but is restricted to the `composition' commands + (MSG_ReplyToSender through MSG_ForwardMessage.) + */ +typedef MSG_CommandType MSG_REPLY_TYPE; + +/* The list of all message flags to not write to disk. */ +#define MSG_FLAG_RUNTIME_ONLY (MSG_FLAG_ELIDED) + + +/* =========================================================================== + Structures. + =========================================================================== + */ + +/* Used for the various things that parse RFC822 headers... + */ +typedef struct message_header +{ + const char *value; /* The contents of a header (after ": ") */ + int32 length; /* The length of the data (it is not NULL-terminated.) */ +} message_header; + + + +/* Argument to msg_NewsgroupNameMapper() */ +typedef int (*msg_SubscribedGroupNameMapper) (MWContext *context, + const char *name, + void *closure); + +XP_BEGIN_PROTOS + +/* we'll need this for localized folder names */ + +extern int MK_MSG_INBOX_L10N_NAME; +extern int MK_MSG_OUTBOX_L10N_NAME; /* win16 variations are in allxpstr.h */ +extern int MK_MSG_OUTBOX_L10N_NAME_OLD; +extern int MK_MSG_TRASH_L10N_NAME; +extern int MK_MSG_DRAFTS_L10N_NAME; +extern int MK_MSG_SENT_L10N_NAME; +extern int MK_MSG_TEMPLATES_L10N_NAME; + +#define INBOX_FOLDER_NAME MSG_GetSpecialFolderName(MK_MSG_INBOX_L10N_NAME) +#define QUEUE_FOLDER_NAME MSG_GetSpecialFolderName(MK_MSG_OUTBOX_L10N_NAME) +#define QUEUE_FOLDER_NAME_OLD MSG_GetSpecialFolderName(MK_MSG_OUTBOX_L10N_NAME_OLD) +#define TRASH_FOLDER_NAME MSG_GetSpecialFolderName(MK_MSG_TRASH_L10N_NAME) +#define DRAFTS_FOLDER_NAME MSG_GetSpecialFolderName(MK_MSG_DRAFTS_L10N_NAME) +#define SENT_FOLDER_NAME MSG_GetSpecialFolderName(MK_MSG_SENT_L10N_NAME) +#define TEMPLATES_FOLDER_NAME MSG_GetSpecialFolderName(MK_MSG_TEMPLATES_L10N_NAME) +#ifdef XP_OS2 +#define INBOX_FOLDER_PRETTY_NAME MSG_GetSpecialFolderPrettyName(MK_MSG_INBOX_L10N_NAME) +#define QUEUE_FOLDER_PRETTY_NAME MSG_GetSpecialFolderPrettyName(MK_MSG_OUTBOX_L10N_NAME) +#define QUEUE_FOLDER_PRETTY_NAME_OLD MSG_GetSpecialFolderPrettyName(MK_MSG_OUTBOX_L10N_NAME_OLD) +#define TRASH_FOLDER_PRETTY_NAME MSG_GetSpecialFolderPrettyName(MK_MSG_TRASH_L10N_NAME) +#define DRAFTS_FOLDER_PRETTY_NAME MSG_GetSpecialFolderPrettyName(MK_MSG_DRAFTS_L10N_NAME) +#define SENT_FOLDER_PRETTY_NAME MSG_GetSpecialFolderPrettyName(MK_MSG_SENT_L10N_NAME) +#define TEMPLATES_FOLDER_PRETTY_NAME MSG_GetSpecialFolderPrettyName(MK_MSG_TEMPLATES_L10N_NAME) +#endif + + +int ConvertMsgErrToMKErr(uint32 err); /* this routine might live - the rest are + probably already dead */ + + +/* =========================================================================== + Redisplay-related stuff + =========================================================================== + */ + +/* Clear out the message display (make Layout be displaying no document.) */ +extern void msg_ClearMessageArea (MWContext *context); + + +/* Returns a line suitable for using as the envelope line in a BSD + mail folder. The returned string is stored in a static, and so + should be used before msg_GetDummyEnvelope is called again. */ +extern char * msg_GetDummyEnvelope(void); + +/* Returns TRUE if the buffer looks like a valid envelope. + This test is somewhat more restrictive than XP_STRNCMP(buf, "From ", 5). + */ +extern XP_Bool msg_IsEnvelopeLine(const char *buf, int32 buf_size); + + +/* =========================================================================== + Utilities specific to mail folders and their Folders and ThreadEntries. + =========================================================================== + */ + +/* returns the name of a magic folder - returns a new string. */ +extern char *msg_MagicFolderName(MSG_Prefs* prefs, uint32 flag, int *pStatus); + + +/* Get the current fcc folder name, so we can sort them up at the top of the + other folder lists with the other magic folders. */ +const char* msg_GetDefaultFcc(XP_Bool news_p); + + +/* Reads the first few bytes of the file and returns FALSE if it doesn't + seem to be a mail folder. (Empty and nonexistent files return TRUE.) + If it doesn't seem to be one, the user is asked whether it should be + written to anyway, and their answer is returned. + */ +extern XP_Bool msg_ConfirmMailFile (MWContext *context, const char *file_name); + +/* =========================================================================== + The content-type converters for the MIME types. These are provided by + compose.c, but are registered by netlib rather than msglib, for some + destined-to-be-mysterious reason. + =========================================================================== + */ +extern NET_StreamClass *MIME_MessageConverter (int format_out, void *closure, + URL_Struct *url, + MWContext *context); + +extern NET_StreamClass *MIME_RichtextConverter (int format_out, void *data_obj, + URL_Struct *url, + MWContext *context); + +extern NET_StreamClass *MIME_EnrichedTextConverter (int format_out, + void *data_obj, + URL_Struct *url, + MWContext *context); + +extern NET_StreamClass *MIME_ToDraftConverter (int format_out, void *closure, + URL_Struct *url, + MWContext *context); + +extern NET_StreamClass *MIME_VCardConverter (int format_out, void *data_obj, + URL_Struct *url, + MWContext *context); + +extern NET_StreamClass *MIME_JulianConverter (int format_out, void *data_obj, + URL_Struct *url, + MWContext *context); + + +/* This nastiness is how msg_SaveSelectedNewsMessages() works. */ +extern NET_StreamClass *msg_MakeAppendToFolderStream (int format_out, + void *closure, + URL_Struct *url, + MWContext *); + +/* This probably should be in mime.h, except that mime.h should mostly be + private to libmsg. So it's here. */ +extern void +msg_StartMessageDeliveryWithAttachments (MSG_Pane *pane, + void *fe_data, + MSG_CompositionFields *fields, + XP_Bool digest_p, + XP_Bool dont_deliver_p, + MSG_Deliver_Mode deliver_mode, + const char *attachment1_type, + const char *attachment1_body, + uint32 attachment1_body_length, + const struct MSG_AttachedFile *attachments, + void *mimeRelatedPart, + void (*message_delivery_done_callback) + (MWContext *context, + void *fe_data, + int status, + const char *error_message)); + +extern int +msg_DownloadAttachments (MSG_Pane *pane, + void *fe_data, + const struct MSG_AttachmentData *attachments, + void (*attachments_done_callback) + (MWContext *context, + void *fe_data, + int status, const char *error_message, + struct MSG_AttachedFile *attachments)); + + +extern +int msg_DoFCC (MSG_Pane *pane, + const char *input_file, XP_FileType input_file_type, + const char *output_file, XP_FileType output_file_type, + const char *bcc_header_value, + const char *fcc_header_value); + +extern char* msg_generate_message_id (void); + +#ifdef XP_UNIX +extern int msg_DeliverMessageExternally(MWContext *, const char *msg_file); +#endif /* XP_UNIX */ + + +XP_END_PROTOS + + + + + +#endif /* !_MSG_H_ */ diff --git a/mozilla/lib/mailto/msgbg.cpp b/mozilla/lib/mailto/msgbg.cpp new file mode 100644 index 00000000000..81c6d5a2702 --- /dev/null +++ b/mozilla/lib/mailto/msgbg.cpp @@ -0,0 +1,133 @@ +/* -*- 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. + */ + +#include "msg.h" +#include "msgbg.h" +#include "msgpane.h" +#include "msgurlq.h" + +extern "C" { + extern int MK_OUT_OF_MEMORY; +} + +msg_Background::msg_Background() +{ +} + + +msg_Background::~msg_Background() +{ + if (m_pane) Interrupt(); +} + + +int +msg_Background::Begin(MSG_Pane* pane) +{ + XP_ASSERT(!m_pane); + if (m_pane) return -1; + char* url = XP_STRDUP("mailbox:?background"); + if (!url) return MK_OUT_OF_MEMORY; + m_urlstruct = NET_CreateURLStruct(url, NET_NORMAL_RELOAD); + if (!m_urlstruct) { + XP_FREE(url); + return MK_OUT_OF_MEMORY; + } + XP_FREE(url); + m_urlstruct->internal_url = TRUE; + m_pane = pane; + msg_InterruptContext(pane->GetContext(), TRUE); + XP_ASSERT(pane->GetCurrentBackgroundJob() == NULL); + pane->SetCurrentBackgroundJob(this); + MSG_UrlQueue::AddUrlToPane(m_urlstruct, msg_Background::PreExit_s, pane); + return 0; +} + + + +void +msg_Background::Interrupt() +{ + XP_ASSERT(m_pane); + if (m_pane) { + msg_InterruptContext(m_pane->GetContext(), FALSE); + XP_ASSERT(m_pane == NULL); + } +} + + +msg_Background* +msg_Background::FindBGObj(URL_Struct* urlstruct) +{ + XP_ASSERT(urlstruct && urlstruct->msg_pane); + if (!urlstruct || !urlstruct->msg_pane) return NULL; + msg_Background* result = urlstruct->msg_pane->GetCurrentBackgroundJob(); + // OK, I'm truly evil, but I'm using this as an empty url that goes through + // netlib once. so result will be null... + XP_ASSERT(!result || result->m_pane == urlstruct->msg_pane); + return result; +} + + +int +msg_Background::ProcessBackground(URL_Struct* urlstruct) +{ + msg_Background* obj = FindBGObj(urlstruct); + if (obj) { + return obj->DoSomeWork(); + } + return MK_CONNECTED; +} + + + +void +msg_Background::PreExit_s(URL_Struct* urlstruct, int status, + MWContext* context) +{ + msg_Background* obj = FindBGObj(urlstruct); + if (obj) { + obj->PreExit(urlstruct, status, context); + } +} + +void +msg_Background::PreExit(URL_Struct* /*urlstruct*/, int status, + MWContext*) +{ + XP_Bool deleteself = AllDone(status); + XP_ASSERT(m_pane); + if (m_pane) { + m_pane->SetCurrentBackgroundJob(NULL); + m_pane = NULL; + } + if (deleteself) delete this; +} + + +XP_Bool +msg_Background::AllDone(int /*status*/) +{ + return FALSE; +} + +XP_Bool +msg_Background::IsRunning() +{ + return m_pane != NULL; +} diff --git a/mozilla/lib/mailto/msgbg.h b/mozilla/lib/mailto/msgbg.h new file mode 100644 index 00000000000..2a9bfba0014 --- /dev/null +++ b/mozilla/lib/mailto/msgbg.h @@ -0,0 +1,83 @@ +/* -*- 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. + */ + +#ifndef _MsgBg_H_ +#define _MsgBg_H_ + +#include "msgzap.h" + +class MSG_Pane; + +class msg_Background : public MSG_ZapIt { +public: + msg_Background(); + virtual ~msg_Background(); + + /* Begin() kicks things off. Sometime after making this call, calls will + be made to DoSomeWork(). This will interrupt any current background + operation or URL running on the given MSG_Pane. */ + virtual int Begin(MSG_Pane* pane); + + + /* Interrupt() interrupts the running background operation. Just does an + InterruptContext on the pane's context. */ + virtual void Interrupt(); + + /* Whether we are currently doing our background operation. */ + virtual XP_Bool IsRunning(); + + + // This routine is called from netlib (via msgglue) to cause us to actually + // do something. + static int ProcessBackground(URL_Struct* urlstruct); + +protected: + + static msg_Background* FindBGObj(URL_Struct* urlstruct); + + static void PreExit_s(URL_Struct* urlstruct, int status, + MWContext* context); + virtual void PreExit(URL_Struct* urlstruct, int status, + MWContext* context); + + + + /* The below are the only routines typically redefined by subclasses. */ + + + /* DoSomeWork() keeps getting called. If it returns + MK_WAITING_FOR_CONNECTION, that means it hasn't finished its stuff. If + it returns MK_CONNECTED, that means it has successfully finished. If it + returns a negative value, that means we failed and it's an error + condition. */ + virtual int DoSomeWork() = 0; + + + /* AllDone() gets called when things are finished. If the given status is + negative, then we were interrupted or had an error. This is a good + place to kick off another background operation. If it returns TRUE, + then this background object will be destroyed. */ + virtual XP_Bool AllDone(int status); + + MSG_Pane* m_pane; + URL_Struct* m_urlstruct; + +}; + + +#endif /* _MsgBg_H_ */ diff --git a/mozilla/lib/mailto/msgcflds.cpp b/mozilla/lib/mailto/msgcflds.cpp new file mode 100644 index 00000000000..51b14ebfdb4 --- /dev/null +++ b/mozilla/lib/mailto/msgcflds.cpp @@ -0,0 +1,382 @@ +/* -*- 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. + */ + +#include "rosetta.h" +#include "msg.h" +#include "errcode.h" + +#include "msgcflds.h" +#include "prefapi.h" +#include "ptrarray.h" + +extern "C" { + extern int MK_OUT_OF_MEMORY; +} + +MSG_CompositionFields::MSG_CompositionFields() +{ + XP_Bool bReturnReceiptOn = FALSE; + + PREF_GetBoolPref("mail.request.return_receipt_on", &bReturnReceiptOn); + PREF_GetIntPref("mail.request.return_receipt", &m_receiptType); + SetReturnReceipt (bReturnReceiptOn); +} + + +MSG_CompositionFields::MSG_CompositionFields(MSG_CompositionFields* c) +{ + int i; + for (i=0 ; im_headers[i]) { + m_headers[i] = XP_STRDUP(c->m_headers[i]); + } + } + if (c->m_body) { + m_body = XP_STRDUP(c->m_body); + } + for (i=0 ; im_numforward ; i++) { + AddForwardURL(c->m_forwardurl[i]); + } + for (i=0; im_boolHeaders[i]; + } + m_receiptType = c->m_receiptType; +} + + + +MSG_CompositionFields::~MSG_CompositionFields() +{ + int i; + for (i=0 ; i= 0) + { + char* old = m_headers[i]; // Done with careful paranoia, in case the + // value given is the old value (or worse, + // a substring of the old value, as does + // happen here and there.) + if (value != old) + { + if (value) + { + m_headers[i] = XP_STRDUP(value); + if (!m_headers[i]) + status = MK_OUT_OF_MEMORY; + } + else + m_headers[i] = NULL; + FREEIF(old); + } + } + + return status; +} + +extern "C"const char* MSG_GetCompFieldsHeader(MSG_CompositionFields *fields, + MSG_HEADER_SET header) +{ + return fields->GetHeader(header); +} + +const char* +MSG_CompositionFields::GetHeader(MSG_HEADER_SET header) +{ + int i = DecodeHeader(header); + if (i >= 0) { + return m_headers[i] ? m_headers[i] : ""; + } + return NULL; +} + + +int +MSG_CompositionFields::SetBoolHeader(MSG_BOOL_HEADER_SET header, XP_Bool bValue) +{ + int status = 0; + XP_ASSERT ((int) header >= (int) MSG_RETURN_RECEIPT_BOOL_HEADER_MASK && + (int) header < (int) MSG_LAST_BOOL_HEADER_MASK); + + if ( (int) header < (int) MSG_RETURN_RECEIPT_BOOL_HEADER_MASK || + (int) header >= (int) MSG_LAST_BOOL_HEADER_MASK ) + return -1; + + m_boolHeaders[header] = bValue; + return status; +} + + +XP_Bool +MSG_CompositionFields::GetBoolHeader(MSG_BOOL_HEADER_SET header) +{ + XP_ASSERT ((int) header >= (int) MSG_RETURN_RECEIPT_BOOL_HEADER_MASK && + (int) header < (int) MSG_LAST_BOOL_HEADER_MASK); + + if ( (int) header < (int) MSG_RETURN_RECEIPT_BOOL_HEADER_MASK || + (int) header >= (int) MSG_LAST_BOOL_HEADER_MASK ) + return FALSE; + + return m_boolHeaders[header]; +} + + +int +MSG_CompositionFields::SetBody(const char* value) +{ + FREEIF(m_body); + if (value) { + m_body = XP_STRDUP(value); + if (!m_body) return MK_OUT_OF_MEMORY; + } + return 0; +} + + +const char* +MSG_CompositionFields::GetBody() +{ + return m_body ? m_body : ""; +} + + +int +MSG_CompositionFields::AppendBody(const char* value) +{ + if (!value || !*value) return 0; + if (!m_body) { + return SetBody(value); + } else { + char* tmp = (char*) XP_ALLOC(XP_STRLEN(m_body) + XP_STRLEN(value) + 1); + if (tmp) { + XP_STRCPY(tmp, m_body); + XP_STRCAT(tmp, value); + XP_FREE(m_body); + m_body = tmp; + } else { + return MK_OUT_OF_MEMORY; + } + } + return 0; +} + + +int +MSG_CompositionFields::DecodeHeader(MSG_HEADER_SET header) +{ + int result; + switch(header) { + case MSG_FROM_HEADER_MASK: + result = 0; + break; + case MSG_REPLY_TO_HEADER_MASK: + result = 1; + break; + case MSG_TO_HEADER_MASK: + result = 2; + break; + case MSG_CC_HEADER_MASK: + result = 3; + break; + case MSG_BCC_HEADER_MASK: + result = 4; + break; + case MSG_FCC_HEADER_MASK: + result = 5; + break; + case MSG_NEWSGROUPS_HEADER_MASK: + result = 6; + break; + case MSG_FOLLOWUP_TO_HEADER_MASK: + result = 7; + break; + case MSG_SUBJECT_HEADER_MASK: + result = 8; + break; + case MSG_ATTACHMENTS_HEADER_MASK: + result = 9; + break; + case MSG_ORGANIZATION_HEADER_MASK: + result = 10; + break; + case MSG_REFERENCES_HEADER_MASK: + result = 11; + break; + case MSG_OTHERRANDOMHEADERS_HEADER_MASK: + result = 12; + break; + case MSG_NEWSPOSTURL_HEADER_MASK: + result = 13; + break; + case MSG_PRIORITY_HEADER_MASK: + result = 14; + break; + case MSG_NEWS_FCC_HEADER_MASK: + result = 15; + break; + case MSG_MESSAGE_ENCODING_HEADER_MASK: + result = 16; + break; + case MSG_CHARACTER_SET_HEADER_MASK: + result = 17; + break; + case MSG_MESSAGE_ID_HEADER_MASK: + result = 18; + break; + case MSG_NEWS_BCC_HEADER_MASK: + result = 19; + break; + case MSG_HTML_PART_HEADER_MASK: + result = 20; + break; + case MSG_DEFAULTBODY_HEADER_MASK: + result = 21; + break; + case MSG_X_TEMPLATE_HEADER_MASK: + result = 22; + break; + default: + XP_ASSERT(0); + result = -1; + break; + } + XP_ASSERT(result < sizeof(m_headers) / sizeof(char*)); + return result; +} + +int +MSG_CompositionFields::AddForwardURL(const char* url) +{ + XP_ASSERT(url && *url); + if (!url || !*url) return -1; + if (m_numforward >= m_maxforward) { + m_maxforward += 10; + char** tmp = new char* [m_maxforward]; + if (!tmp) return MK_OUT_OF_MEMORY; + for (int32 i=0 ; i= 0 && which < m_numforward); + if (which >= 0 && which < m_numforward) { + return m_forwardurl[which]; + } + return NULL; +} diff --git a/mozilla/lib/mailto/msgcflds.h b/mozilla/lib/mailto/msgcflds.h new file mode 100644 index 00000000000..2161ba6ae24 --- /dev/null +++ b/mozilla/lib/mailto/msgcflds.h @@ -0,0 +1,288 @@ +/* -*- 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. + */ + +#ifndef _MsgCFlds_H_ +#define _MsgCFlds_H_ + +#include "rosetta.h" +#include "msgzap.h" + + +// Note that all the "Get" methods never return NULL (except in case of serious +// error, like an illegal parameter); rather, they return "" if things were set +// to NULL. This makes it real handy for the callers. + +class MSG_CompositionFields : public MSG_ZapIt { +public: + MSG_CompositionFields(); + MSG_CompositionFields(MSG_CompositionFields*); // Makes a copy. + virtual ~MSG_CompositionFields(); + + int SetHeader(MSG_HEADER_SET header, const char* value); + const char* GetHeader(MSG_HEADER_SET header); + + int SetBoolHeader(MSG_BOOL_HEADER_SET header, XP_Bool bValue); + XP_Bool GetBoolHeader(MSG_BOOL_HEADER_SET header); + + int SetBody(const char*); + const char* GetBody(); + + int AppendBody(const char*); + + // When forwarding a bunch of messages, we can have a bunch of + // "forward url's" instead of an attachment. + + int AddForwardURL(const char*); + + int32 GetNumForwardURL(); + const char* GetForwardURL(int32 which); + + int32 GetReturnReceiptType() { return m_receiptType; }; + void SetReturnReceiptType(int32 type) {m_receiptType = type;}; + + + // Convenience routines... + + int SetFrom(const char* value) { + return SetHeader(MSG_FROM_HEADER_MASK, value); + } + const char* GetFrom() { + return GetHeader(MSG_FROM_HEADER_MASK); + } + + int SetReplyTo(const char* value) { + return SetHeader(MSG_REPLY_TO_HEADER_MASK, value); + } + const char* GetReplyTo() { + return GetHeader(MSG_REPLY_TO_HEADER_MASK); + } + + int SetTo(const char* value) { + return SetHeader(MSG_TO_HEADER_MASK, value); + } + const char* GetTo() { + return GetHeader(MSG_TO_HEADER_MASK); + } + + int SetCc(const char* value) { + return SetHeader(MSG_CC_HEADER_MASK, value); + } + const char* GetCc() { + return GetHeader(MSG_CC_HEADER_MASK); + } + + int SetBcc(const char* value) { + return SetHeader(MSG_BCC_HEADER_MASK, value); + } + const char* GetBcc() { + return GetHeader(MSG_BCC_HEADER_MASK); + } + + int SetFcc(const char* value) { + return SetHeader(MSG_FCC_HEADER_MASK, value); + } + const char* GetFcc() { + return GetHeader(MSG_FCC_HEADER_MASK); + } + + int SetNewsFcc(const char* value) { + return SetHeader(MSG_NEWS_FCC_HEADER_MASK, value); + } + const char* GetNewsFcc() { + return GetHeader(MSG_NEWS_FCC_HEADER_MASK); + } + int SetNewsBcc(const char* value) { + return SetHeader(MSG_NEWS_BCC_HEADER_MASK, value); + } + const char* GetNewsBcc() { + return GetHeader(MSG_NEWS_BCC_HEADER_MASK); + } + + int SetNewsgroups(const char* value) { + return SetHeader(MSG_NEWSGROUPS_HEADER_MASK, value); + } + const char* GetNewsgroups() { + return GetHeader(MSG_NEWSGROUPS_HEADER_MASK); + } + + int SetFollowupTo(const char* value) { + return SetHeader(MSG_FOLLOWUP_TO_HEADER_MASK, value); + } + const char* GetFollowupTo() { + return GetHeader(MSG_FOLLOWUP_TO_HEADER_MASK); + } + + int SetSubject(const char* value) { + return SetHeader(MSG_SUBJECT_HEADER_MASK, value); + } + const char* GetSubject() { + return GetHeader(MSG_SUBJECT_HEADER_MASK); + } + + int SetAttachments(const char* value) { + return SetHeader(MSG_ATTACHMENTS_HEADER_MASK, value); + } + const char* GetAttachments() { + return GetHeader(MSG_ATTACHMENTS_HEADER_MASK); + } + + int SetOrganization(const char* value) { + return SetHeader(MSG_ORGANIZATION_HEADER_MASK, value); + } + const char* GetOrganization() { + return GetHeader(MSG_ORGANIZATION_HEADER_MASK); + } + + int SetReferences(const char* value) { + return SetHeader(MSG_REFERENCES_HEADER_MASK, value); + } + const char* GetReferences() { + return GetHeader(MSG_REFERENCES_HEADER_MASK); + } + + int SetOtherRandomHeaders(const char* value) { + return SetHeader(MSG_OTHERRANDOMHEADERS_HEADER_MASK, value); + } + const char* GetOtherRandomHeaders() { + return GetHeader(MSG_OTHERRANDOMHEADERS_HEADER_MASK); + } + + int SetNewspostUrl(const char* value) { + return SetHeader(MSG_NEWSPOSTURL_HEADER_MASK, value); + } + const char* GetNewspostUrl() { + return GetHeader(MSG_NEWSPOSTURL_HEADER_MASK); + } + + int SetDefaultBody(const char* value) { + return SetHeader(MSG_DEFAULTBODY_HEADER_MASK, value); + } + const char* GetDefaultBody() { + return GetHeader(MSG_DEFAULTBODY_HEADER_MASK); + } + + int SetPriority(const char* value) { + return SetHeader(MSG_PRIORITY_HEADER_MASK, value); + } + + const char* GetPriority() { + return GetHeader(MSG_PRIORITY_HEADER_MASK); + } + + int SetMessageEncoding(const char* value) { + return SetHeader(MSG_MESSAGE_ENCODING_HEADER_MASK, value); + } + + const char* GetMessageEncoding() { + return GetHeader(MSG_MESSAGE_ENCODING_HEADER_MASK); + } + + int SetCharacterSet(const char* value) { + return SetHeader (MSG_CHARACTER_SET_HEADER_MASK, value); + } + + const char* GetCharacterSet() { + return GetHeader(MSG_CHARACTER_SET_HEADER_MASK); + } + + int SetMessageId(const char* value) { + return SetHeader (MSG_MESSAGE_ID_HEADER_MASK, value); + } + + const char* GetMessageId() { + return GetHeader(MSG_MESSAGE_ID_HEADER_MASK); + } + + int SetHTMLPart(const char* value) { + return SetHeader(MSG_HTML_PART_HEADER_MASK, value); + } + + const char* GetHTMLPart() { + return GetHeader(MSG_HTML_PART_HEADER_MASK); + } + + int SetTemplateName(const char* value) { + return SetHeader(MSG_X_TEMPLATE_HEADER_MASK, value); + } + + const char* GetTemplateName() { + return GetHeader(MSG_X_TEMPLATE_HEADER_MASK); + } + + // Bool headers + + int SetReturnReceipt(XP_Bool value) { + return SetBoolHeader(MSG_RETURN_RECEIPT_BOOL_HEADER_MASK, value); + } + + XP_Bool GetReturnReceipt() { + return GetBoolHeader(MSG_RETURN_RECEIPT_BOOL_HEADER_MASK); + } + +HG87266 + + int SetSigned(XP_Bool value) { + return SetBoolHeader(MSG_SIGNED_BOOL_HEADER_MASK, value); + } + + XP_Bool GetSigned() { + return GetBoolHeader(MSG_SIGNED_BOOL_HEADER_MASK); + } + + int SetAttachVCard(XP_Bool value) { + return SetBoolHeader(MSG_ATTACH_VCARD_BOOL_HEADER_MASK, value); + } + + XP_Bool GetAttachVCard() { + return GetBoolHeader(MSG_ATTACH_VCARD_BOOL_HEADER_MASK); + } + + + void SetOwner(MSG_Pane *pane) { + m_owner = pane; + } + + MSG_Pane * GetOwner() { return m_owner; } + + + void SetForcePlainText(XP_Bool value) {m_force_plain_text = value;} + XP_Bool GetForcePlainText() {return m_force_plain_text;} + void SetUseMultipartAlternative(XP_Bool value) {m_multipart_alt = value;} + XP_Bool GetUseMultipartAlternative() {return m_multipart_alt;} + +protected: + int DecodeHeader(MSG_HEADER_SET header); + + // These methods allow news URLs in the newsgroups header + int SetNewsUrlHeader (const char *hostPort, XP_Bool xxx, const char *group); + int ParseNewsgroupsForUrls (const char *value); + + MSG_Pane *m_owner; + char* m_headers[32]; + char* m_body; + char** m_forwardurl; + int32 m_numforward; + int32 m_maxforward; + XP_Bool m_boolHeaders[MSG_LAST_BOOL_HEADER_MASK]; + XP_Bool m_force_plain_text; + XP_Bool m_multipart_alt; + int32 m_receiptType; /* 0:None 1:DSN 2:MDN 3:BOTH */ +}; + + +#endif /* _MsgCFlds_H_ */ diff --git a/mozilla/lib/mailto/msgcpane.cpp b/mozilla/lib/mailto/msgcpane.cpp new file mode 100644 index 00000000000..b98802d6f42 --- /dev/null +++ b/mozilla/lib/mailto/msgcpane.cpp @@ -0,0 +1,3312 @@ +/* -*- 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. + */ + +#include "rosetta.h" +#include "msg.h" +#include "errcode.h" +#include "error.h" + +#include "mime.h" +#include "shist.h" +#include "xlate.h" +#include "libi18n.h" +#include "xpgetstr.h" + +#include "msgcpane.h" +#include "msgprefs.h" +#include "msgcflds.h" +#include "dirprefs.h" +#include "msgurlq.h" + +#include "edt.h" // to invoke save on the html compose pane +#include "mhtmlstm.h" + +#include "prefapi.h" +#include "htmldlgs.h" +#include "xp_qsort.h" +#include "intl_csi.h" + +extern "C" +{ +extern int MK_MSG_MSG_COMPOSITION; + +extern int MK_COMMUNICATIONS_ERROR; +extern int MK_OUT_OF_MEMORY; + +extern int MK_MSG_EMPTY_MESSAGE; +extern int MK_MSG_DOUBLE_INCLUDE; + +extern int MK_MSG_WHY_QUEUE_SPECIAL; +extern int MK_MSG_WHY_QUEUE_SPECIAL_OLD; +extern int MK_MSG_NOT_AS_SENT_FOLDER; + +extern int MK_MSG_MISSING_SUBJECT; + +HG65256 + +extern int MK_MSG_SEND; +extern int MK_MSG_SEND_LATER; +extern int MK_MSG_ATTACH_ETC; +extern int MK_MSG_QUOTE_MESSAGE; +extern int MK_MSG_FROM; +extern int MK_MSG_REPLY_TO; +extern int MK_MSG_MAIL_TO; +extern int MK_MSG_MAIL_CC; +extern int MK_MSG_MAIL_BCC; +extern int MK_MSG_FILE_CC; +extern int MK_MSG_POST_TO; +extern int MK_MSG_FOLLOWUPS_TO; +extern int MK_MSG_SUBJECT; +extern int MK_MSG_ATTACHMENT; +extern int MK_MSG_ATTACH_AS_TEXT; +extern int MK_MSG_SAVE_DRAFT; +extern int MK_ADDR_BOOK_CARD; + +extern int MK_MSG_ASK_HTML_MAIL; +extern int MK_MSG_ASK_HTML_MAIL_TITLE; +extern int MK_MSG_HTML_RECIPIENTS; +extern int MK_MSG_HTML_RECIPIENTS_TITLE; +extern int MK_MSG_EVERYONE; + +extern int MK_MSG_ENTER_NAME_FOR_TEMPLATE; + +#include "xp_help.h" +} + + + +HG33234 + +#define ALL_HEADERS (MSG_FROM_HEADER_MASK | \ + MSG_REPLY_TO_HEADER_MASK | \ + MSG_TO_HEADER_MASK | \ + MSG_CC_HEADER_MASK | \ + MSG_BCC_HEADER_MASK | \ + MSG_FCC_HEADER_MASK | \ + MSG_NEWSGROUPS_HEADER_MASK | \ + MSG_FOLLOWUP_TO_HEADER_MASK | \ + MSG_SUBJECT_HEADER_MASK | \ + MSG_ATTACHMENTS_HEADER_MASK) + + +enum RecipientType { + Address = 1, Domain = 2, Newsgroup = 3, GroupHierarchy = 4 +}; + +class RecipientEntry : public MSG_ZapIt { +public: + RecipientEntry(const char* name, const char* description, + RecipientType type, XP_Bool htmlok); + ~RecipientEntry(); + + char* GetName() {return m_name;} + char* GetDescription() {return m_description;} + RecipientType GetType() {return m_type;} + XP_Bool GetHTMLOk() {return m_htmlok;} + XP_Bool GetNewHTMLOk() {return m_newhtmlok;} + void SetNewOK(XP_Bool value); + XP_Bool GetTouched() {return m_touched;} + +protected: + char* m_name; + char* m_description; + RecipientType m_type; + XP_Bool m_htmlok; + XP_Bool m_newhtmlok; + XP_Bool m_touched; +}; + + +RecipientEntry::RecipientEntry(const char* name, const char* description, + RecipientType type, XP_Bool htmlok) +{ + m_name = XP_STRDUP(name); + m_description = XP_STRDUP(description); + if (!m_description) { + FREEIF(m_name); // Checking for name being NULL is the hack + // used to see if we're out of memory. + } + m_type = type; + m_htmlok = htmlok; +} + +RecipientEntry::~RecipientEntry() +{ + FREEIF(m_name); + FREEIF(m_description); +} + +void +RecipientEntry::SetNewOK(XP_Bool value) +{ + XP_ASSERT(!m_touched); + m_touched = TRUE; + m_newhtmlok = value; +} + + + +class MSG_HTMLRecipients : public MSG_ZapIt { +public: + MSG_HTMLRecipients(); + ~MSG_HTMLRecipients(); + + int AddOne(const char* name, const char* description, + RecipientType type, XP_Bool htmlok); + MSG_RecipientList* GetList(XP_Bool htmlok); + + int SetNewList(int32* notoklist, int32* oklist); + + char** GetChangedList(RecipientType type, XP_Bool htmlok); + void FreeChangedList(char** list); + int GetNum() {return m_num;} + +protected: + RecipientEntry** m_list; + int32 m_num; + int32 m_max; + MSG_RecipientList* m_generatedList[2]; +}; + +MSG_HTMLRecipients::MSG_HTMLRecipients() { +} + +MSG_HTMLRecipients::~MSG_HTMLRecipients() { + delete m_generatedList[0]; + delete m_generatedList[1]; + for (int32 i=0 ; iGetType() == type && + XP_STRCMP(m_list[i]->GetName(), name) == 0) return 0; + } + if (m_num >= m_max) { + RecipientEntry** tmp = new RecipientEntry* [m_max + 10]; + if (!tmp) return MK_OUT_OF_MEMORY; + m_max += 10; + for (i=0 ; iGetName()) { + delete m_list[m_num]; + return MK_OUT_OF_MEMORY; + } + m_num++; + return 0; +} + + +MSG_RecipientList* +MSG_HTMLRecipients::GetList(XP_Bool htmlok) +{ + int32 i, j; + if (m_generatedList[0] == NULL) { + // Sort the entries in the list. Within a given type, we want to + // keep things in the order they were generated, but they need to + // be grouped by type. So, it's bubble-sort time. Whee... + for (i=1 ; i 0 && m_list[j]->GetType() < m_list[j-1]->GetType(); + j--) { + RecipientEntry* tmp = m_list[j]; + m_list[j] = m_list[j-1]; + m_list[j-1] = tmp; + } + } + + m_generatedList[0] = new MSG_RecipientList [m_num + 1]; + if (!m_generatedList[0]) return NULL; + m_generatedList[1] = new MSG_RecipientList [m_num + 1]; + if (!m_generatedList[1]) { + delete [] m_generatedList[0]; + return NULL; + } + int32 cur[2]; + cur[0] = cur[1] = 0; + for (i=0 ; iGetHTMLOk()); + m_generatedList[w][cur[w]].name = m_list[i]->GetDescription(); + m_generatedList[w][cur[w]].value = i; + (cur[w])++; + } + for (i=0 ; i<2 ; i++) { + m_generatedList[i][cur[i]].name = NULL; + m_generatedList[i][cur[i]].value = -1; + } + } + return m_generatedList[int(htmlok)]; +} + + +int +MSG_HTMLRecipients::SetNewList(int32* notoklist, int32* oklist) +{ + int32 i; +#ifdef DEBUG + for (i=0 ; iGetTouched()); + } +#endif + for (int w=0 ; w<2 ; w++) { + XP_Bool ok = (w == 1); + int32* list = ok ? oklist : notoklist; + XP_ASSERT(list); + if (!list) continue; + for ( ; *list >= 0 ; list++) { + XP_ASSERT(*list < m_num); + if (*list >= m_num) break; + m_list[*list]->SetNewOK(ok); + } + } + int status = 0; + for (i=0 ; iGetTouched()); + if (!m_list[i]->GetTouched()) { + status = -1; + } + } + return status; +} + + +char** +MSG_HTMLRecipients::GetChangedList(RecipientType type, XP_Bool htmlok) +{ + char** result = new char * [m_num + 1]; + if (!result) return NULL; + char** tmp = result; + for (int32 i=0 ; iGetType() == type && + m_list[i]->GetNewHTMLOk() == htmlok && + m_list[i]->GetHTMLOk() != htmlok) { + *tmp = m_list[i]->GetName(); + tmp++; + } + } + *tmp = NULL; + return result; +} + + +void +MSG_HTMLRecipients::FreeChangedList(char** list) +{ + delete [] list; +} + + +static void msg_free_attachment_list(struct MSG_AttachmentData *list); + +static void +msg_delete_attached_files(struct MSG_AttachedFile *attachments) +{ + struct MSG_AttachedFile *tmp; + if (!attachments) return; + for (tmp = attachments; tmp->orig_url; tmp++) { + FREEIF(tmp->orig_url); + FREEIF(tmp->type); + FREEIF(tmp->real_name); + FREEIF(tmp->encoding); + FREEIF(tmp->description); + FREEIF(tmp->x_mac_type); + FREEIF(tmp->x_mac_creator); + if (tmp->file_name) { + XP_FileRemove(tmp->file_name, xpFileToPost); + XP_FREE(tmp->file_name); + } + } + XP_FREEIF(attachments); +} + + +MSG_CompositionPane::MSG_CompositionPane(MWContext* context, + MWContext* old_context, + MSG_Prefs* prefs, + MSG_CompositionFields* fields, + MSG_Master* master) +: MSG_Pane(context, master) +{ + m_prefs = prefs; + m_htmlaction = MSG_HTMLAskUser; + Initialize(old_context, fields); +} + + +MSG_CompositionPane::MSG_CompositionPane(MWContext* context, + MSG_Prefs* prefs, + MSG_Master* master) +: MSG_Pane(context, master) +{ + m_prefs = prefs; +} + +int +MSG_CompositionPane::Initialize(MWContext* old_context, + MSG_CompositionFields* fields) +{ + m_print = new PrintSetup; + HG22658 + // ###tw Should do something if this failed! + + InitializeHeaders(old_context, fields); + m_visible_headers = GetInterestingHeaders(); + m_deliver_mode = MSG_DeliverNow; + m_haveAttachedVcard = FALSE; + + m_fields->SetForcePlainText(FALSE); // Coming into us, this field meant + // "bring up the editor in plaintext + // mode". Well, that's already been + // done at this point. Now, we want + // it to mean "convert this message + // to plaintext on send". Which we + // do only if DetermineHTMLAction() + // tells us to. + + return 0; +} + + + +MSG_CompositionPane::~MSG_CompositionPane() { + // Don't interrupt if there's nothing to interrupt because we might lose + // mocha messages. + if (NET_AreThereActiveConnectionsForWindow(m_context)) + msg_InterruptContext (m_context, FALSE); + if (m_textContext != NULL) { + msg_InterruptContext(m_textContext, TRUE); + } + + msg_delete_attached_files (m_attachedFiles); + + FREEIF(m_defaultUrl); + FREEIF(m_attachmentString); + + msg_free_attachment_list(m_attachData); + + delete m_print; + m_print = NULL; + + HG28266 + + if (m_context) FE_DestroyMailCompositionContext(m_context); + m_context = NULL; + + delete m_fields; + m_fields = NULL; + delete m_initfields; + m_initfields = NULL; + delete m_htmlrecip; + m_htmlrecip = NULL; + + FREEIF(m_quotedText); + FREEIF(m_messageId); +} + + +MSG_PaneType +MSG_CompositionPane::GetPaneType() { + return MSG_COMPOSITIONPANE; +} + + +void MSG_CompositionPane::NotifyPrefsChange(NotifyCode) { + // ###tw Write me! +} + + +HG02070 + + +char* +MSG_CompositionPane::FigureBcc(XP_Bool newsBcc) +{ + char* result = NULL; + FREEIF(result); + const char* tmp = + GetPrefs()->GetDefaultHeaderContents( + newsBcc ? MSG_NEWS_BCC_HEADER_MASK : MSG_BCC_HEADER_MASK); + if (!GetPrefs()->GetDefaultBccSelf(newsBcc)) { + result = XP_STRDUP(tmp ? tmp : ""); + } else if (!tmp || !*tmp) { + result = XP_STRDUP(FE_UsersMailAddress()); + } else { + result = PR_smprintf("%s, %s", FE_UsersMailAddress(), tmp); + } + return result; +} + + +MsgERR +MSG_CompositionPane::GetCommandStatus(MSG_CommandType command, + const MSG_ViewIndex* indices, + int32 numindices, + XP_Bool *selectable_pP, + MSG_COMMAND_CHECK_STATE *selected_pP, + const char **display_stringP, + XP_Bool *plural_pP) +{ + const char *display_string = 0; + XP_Bool plural_p = FALSE; + // N.B. default is TRUE, so you don't need to set it in each case + XP_Bool selectable_p = TRUE; + XP_Bool selected_p = FALSE; + XP_Bool selected_used_p = FALSE; + + switch (command) + { + case MSG_AttachAsText: + // the WinFE uses this for lots of update, so pretend we handle it. + display_string = XP_GetString(MK_MSG_ATTACH_AS_TEXT); + break; + case MSG_SendMessage: + display_string = XP_GetString(MK_MSG_SEND); + if (m_attachmentInProgress) + selectable_p = FALSE; + break; + case MSG_SendMessageLater: + display_string = XP_GetString(MK_MSG_SEND_LATER); + if (m_attachmentInProgress) + selectable_p = FALSE; + break; + case MSG_SaveDraft: + case MSG_SaveDraftThenClose: + case MSG_SaveTemplate: + display_string = XP_GetString(MK_MSG_SAVE_DRAFT); + if (m_attachmentInProgress) + selectable_p = FALSE; + break; + case MSG_Attach: + display_string = XP_GetString(MK_MSG_ATTACH_ETC); + break; + + case MSG_ShowFrom: + display_string = XP_GetString(MK_MSG_FROM); + selected_p = ShowingCompositionHeader(MSG_FROM_HEADER_MASK); + selected_used_p = TRUE; + break; + case MSG_ShowReplyTo: + display_string = XP_GetString(MK_MSG_REPLY_TO); + selected_p = ShowingCompositionHeader(MSG_REPLY_TO_HEADER_MASK); + selected_used_p = TRUE; + break; + case MSG_ShowTo: + display_string = XP_GetString(MK_MSG_MAIL_TO); + selected_p = ShowingCompositionHeader(MSG_TO_HEADER_MASK); + selected_used_p = TRUE; + break; + case MSG_ShowCC: + display_string = XP_GetString(MK_MSG_MAIL_CC); + selected_p = ShowingCompositionHeader(MSG_CC_HEADER_MASK); + selected_used_p = TRUE; + break; + case MSG_ShowBCC: + display_string = XP_GetString(MK_MSG_MAIL_BCC); + selected_p = ShowingCompositionHeader(MSG_BCC_HEADER_MASK); + selected_used_p = TRUE; + break; + case MSG_ShowFCC: + display_string = XP_GetString(MK_MSG_FILE_CC); + selected_p = ShowingCompositionHeader(MSG_FCC_HEADER_MASK); + selected_used_p = TRUE; + break; + case MSG_ShowPostTo: + display_string = XP_GetString(MK_MSG_POST_TO); + selected_p = ShowingCompositionHeader(MSG_NEWSGROUPS_HEADER_MASK); + selected_used_p = TRUE; + break; + case MSG_ShowFollowupTo: + display_string = XP_GetString(MK_MSG_FOLLOWUPS_TO); + selected_p = ShowingCompositionHeader(MSG_FOLLOWUP_TO_HEADER_MASK); + selected_used_p = TRUE; + break; + case MSG_ShowSubject: + display_string = XP_GetString(MK_MSG_SUBJECT); + selected_p = ShowingCompositionHeader(MSG_SUBJECT_HEADER_MASK); + selected_used_p = TRUE; + break; + case MSG_ShowAttachments: + display_string = XP_GetString(MK_MSG_ATTACHMENT); + selected_p = ShowingCompositionHeader(MSG_ATTACHMENTS_HEADER_MASK); + selected_used_p = TRUE; + break; + default: + selectable_p = FALSE; + return MSG_Pane::GetCommandStatus(command, indices, numindices, + selectable_pP, selected_pP, display_stringP, plural_pP); + } + if (selectable_pP) + *selectable_pP = selectable_p; + if (selected_pP) + { + if (selected_used_p) + { + if (selected_p) + *selected_pP = MSG_Checked; + else + *selected_pP = MSG_Unchecked; + } + else + { + *selected_pP = MSG_NotUsed; + } + } + if (display_stringP) + *display_stringP = display_string; + if (plural_pP) + *plural_pP = plural_p; + + return 0; +} + + +MsgERR +MSG_CompositionPane::DoCommand(MSG_CommandType command, MSG_ViewIndex* indices, + int32 numindices) +{ + MsgERR status = 0; + InterruptContext(FALSE); + switch (command) { + case MSG_SendMessage: + status = SendMessageNow(); /* ###tw Error-return-type mismatch! */ + break; + case MSG_SendMessageLater: + status = QueueMessageForLater();/* ###tw Error-return-type mismatch! */ + break; + case MSG_SaveDraft: + case MSG_SaveDraftThenClose: + if (command == MSG_SaveDraftThenClose) + m_closeAfterSave = TRUE; + status = SaveMessageAsDraft(); /* ### Error-return-type mismatch! */ + break; + case MSG_SaveTemplate: + status = SaveMessageAsTemplate(); + break; + case MSG_ShowPostTo: // how to do this? + ToggleCompositionHeader(MSG_NEWSGROUPS_HEADER_MASK); + break; + HG82762 + case MSG_ShowFrom: + ToggleCompositionHeader(MSG_FROM_HEADER_MASK); + break; + case MSG_ShowReplyTo: + ToggleCompositionHeader(MSG_REPLY_TO_HEADER_MASK); + break; + case MSG_ShowTo: + ToggleCompositionHeader(MSG_TO_HEADER_MASK); + break; + case MSG_ShowCC: + ToggleCompositionHeader(MSG_CC_HEADER_MASK); + break; + case MSG_ShowBCC: + ToggleCompositionHeader(MSG_BCC_HEADER_MASK); + break; + case MSG_ShowFCC: + ToggleCompositionHeader(MSG_FCC_HEADER_MASK); + break; + case MSG_ShowFollowupTo: + ToggleCompositionHeader(MSG_FOLLOWUP_TO_HEADER_MASK); + break; + case MSG_ShowSubject: + ToggleCompositionHeader(MSG_SUBJECT_HEADER_MASK); + break; + case MSG_ShowAttachments: + ToggleCompositionHeader(MSG_ATTACHMENTS_HEADER_MASK); + break; + default: + status = MSG_Pane::DoCommand(command, indices, numindices); + break; + } + return status; +} + +extern "C" void FE_MsgShowHeaders(MSG_Pane *pPane, MSG_HEADER_SET mhsHeaders); + +void MSG_CompositionPane::ToggleCompositionHeader(uint32 header) +{ + if (m_visible_headers & header) { + m_visible_headers &= ~header; + } else { + m_visible_headers |= header; + } + FE_MsgShowHeaders(this, m_visible_headers); +} + +XP_Bool +MSG_CompositionPane::ShowingAllCompositionHeaders() +{ + return m_visible_headers == ALL_HEADERS; +} + +XP_Bool +MSG_CompositionPane::ShowingCompositionHeader(uint32 mask) +{ + return (m_visible_headers & mask) == mask; +} + + +int +MSG_CompositionPane::SetCallbacks(MSG_CompositionPaneCallbacks* callbacks, + void* closure) +{ + m_callbacks = *callbacks; + m_callbackclosure = closure; + return 0; +} + + +void +MSG_CompositionPane::InitializeHeaders(MWContext* old_context, + MSG_CompositionFields* fields) +{ + XP_ASSERT(m_fields == NULL); + XP_ASSERT(m_initfields == NULL); + + const char *real_addr = FE_UsersMailAddress (); + char *real_return_address; + const char* sig; + XP_Bool forward_quoted; + forward_quoted = FALSE; + + m_fields = new MSG_CompositionFields(fields); + // ###tw Should check for failure! + + /* hack for forward quoted. Checks the attachment field for a cookie + string indicating that this is a forward quoted operation. If a cookie + is found, the attachment string is slid back down over the cookie. This + will put the original string back in tact. */ + + const char* attachment = m_fields->GetAttachments(); + + if (attachment) { + if (!XP_STRNCMP(attachment, MSG_FORWARD_COOKIE, + strlen(MSG_FORWARD_COOKIE))) { + attachment += XP_STRLEN(MSG_FORWARD_COOKIE); + forward_quoted = TRUE; /* set forward with quote flag */ + m_fields->SetAttachments(attachment); + attachment = m_fields->GetAttachments(); + } + } + + m_status = -1; + + if (MISC_ValidateReturnAddress(old_context, real_addr) < 0) { + return; + } + + real_return_address = MIME_MakeFromField(); + + XP_ASSERT (m_context->type == MWContextMessageComposition); + XP_ASSERT (XP_FindContextOfType(0, MWContextMessageComposition)); + XP_ASSERT (!m_context->msg_cframe); + + int32 count = m_fields->GetNumForwardURL(); + if (count > 0) { + // if forwarding one or more messages + XP_ASSERT(*attachment == '\0'); + MSG_AttachmentData *alist = (struct MSG_AttachmentData *) + XP_ALLOC((count + 1) * sizeof(MSG_AttachmentData)); + if (alist) { + XP_MEMSET(alist, 0, (count + 1) * sizeof(*alist)); + for (count--; count >= 0; count--) { + alist[count].url = (char*) m_fields->GetForwardURL(count); + alist[count].real_name = (char*) m_fields->GetForwardURL(count); + } + SetAttachmentList(alist); + XP_FREE(alist); + } + } else if (*attachment) { + // forwarding a single url + // typically a web page + MSG_AttachmentData *alist; + count = 1; + alist = (struct MSG_AttachmentData *) + XP_ALLOC((count + 1) * sizeof(MSG_AttachmentData)); + if (alist) { + XP_MEMSET(alist, 0, (count + 1) * sizeof(*alist)); + alist[0].url = (char *)attachment; + alist[0].real_name = XP_STRDUP ((char *)attachment); + SetAttachmentList(alist); + } + } // else if (*attachment) + + if (*attachment) { + if (*attachment != '(') { + m_defaultUrl = XP_STRDUP(attachment); + } + } + else if (old_context) { + History_entry *h = SHIST_GetCurrent(&old_context->hist); + if (h && h->address) { + m_defaultUrl = XP_STRDUP(h->address); + } + } + + if (!*m_fields->GetFrom()) { + m_fields->SetFrom(real_return_address); + } + + /* Guess what kind of reply this is based on the headers we passed in. + */ + + const char* newsgroups = m_fields->GetNewsgroups(); + const char* to = m_fields->GetTo(); + const char* cc = m_fields->GetCc(); + const char* references = m_fields->GetReferences(); + + if (count > 0 || *attachment) { + /* if an attachment exists and the forward_quoted flag is set, this + is a forward quoted operation. */ + if (forward_quoted) { + m_replyType = MSG_ForwardMessageQuoted; + /* clear out the attachment list for forward quoted messages. */ + SetAttachmentList(NULL); + m_pendingAttachmentsCount = 0; + } else { + m_replyType = MSG_ForwardMessageAttachment; + } + } else if (*references && *newsgroups && (*to || *cc)) { + m_replyType = MSG_PostAndMailReply; + } else if (*references && *newsgroups) { + m_replyType = MSG_PostReply; + } else if (*references && *cc) { + m_replyType = MSG_ReplyToAll; + } else if (*references && *to) { + m_replyType = MSG_ReplyToSender; + } else if (*newsgroups) { + m_replyType = MSG_PostNew; + } else { + m_replyType = MSG_MailNew; + } + + +HG00729 + + + if (!*m_fields->GetOrganization()) { + m_fields->SetOrganization(FE_UsersOrganization()); + } + + if (!*m_fields->GetReplyTo()) { + m_fields-> + SetReplyTo(GetPrefs()-> + GetDefaultHeaderContents(MSG_REPLY_TO_HEADER_MASK)); + } + if (!*m_fields->GetFcc()) + { + XP_Bool useDefaultFcc = TRUE; + /*int prefError =*/ PREF_GetBoolPref(*newsgroups ? "news.use_fcc" : "mail.use_fcc", + &useDefaultFcc); + if (useDefaultFcc) + { + m_fields->SetFcc(GetPrefs()-> + GetDefaultHeaderContents(*newsgroups ? + MSG_NEWS_FCC_HEADER_MASK : MSG_FCC_HEADER_MASK)); + } + } + if (!*m_fields->GetBcc()) { + char* bcc = FigureBcc(*newsgroups); + m_fields->SetBcc(bcc); + FREEIF(bcc); + } + + { + const char *body = m_fields->GetDefaultBody(); + if (body && *body) + { + m_fields->AppendBody(body); + m_fields->AppendBody(LINEBREAK); + /* m_bodyEdited = TRUE; */ + } + } + + HG93653 + sig = FE_UsersSignature (); + if (sig && *sig) { + m_fields->AppendBody(LINEBREAK); + /* If the sig doesn't begin with "--" followed by whitespace or a + newline, insert "-- \n" (the pseudo-standard sig delimiter.) */ + if (sig[0] != '-' || sig[1] != '-' || + (sig[2] != ' ' && sig[2] != CR && sig[2] != LF)) { + m_fields->AppendBody("-- " LINEBREAK); + } + m_fields->AppendBody(sig); + } + + FREEIF (real_return_address); + + + FE_SetDocTitle(m_context, (char*) GetWindowTitle()); + + + m_initfields = new MSG_CompositionFields(m_fields); + // ###tw Should check for failure! + +} + + +XP_Bool MSG_CompositionPane::ShouldAutoQuote() { + if (m_haveQuoted) return FALSE; + if (m_replyType == MSG_ForwardMessageQuoted || + GetPrefs()->GetAutoQuoteReply()) { + switch (m_replyType) { + case MSG_ForwardMessageQuoted: + case MSG_PostAndMailReply: + case MSG_PostReply: + case MSG_ReplyToAll: + case MSG_ReplyToSender: + return TRUE; + + default: + break; + } + } + return FALSE; +} + + + + +const char* MSG_CompositionPane::GetDefaultURL() { + return m_defaultUrl; +} + + + +MSG_CompositionFields* +MSG_CompositionPane::GetInitialFields() +{ + return m_initfields; +} + + + + +#define ALL_HEADERS (MSG_FROM_HEADER_MASK | \ + MSG_REPLY_TO_HEADER_MASK | \ + MSG_TO_HEADER_MASK | \ + MSG_CC_HEADER_MASK | \ + MSG_BCC_HEADER_MASK | \ + MSG_FCC_HEADER_MASK | \ + MSG_NEWSGROUPS_HEADER_MASK | \ + MSG_FOLLOWUP_TO_HEADER_MASK | \ + MSG_SUBJECT_HEADER_MASK | \ + MSG_ATTACHMENTS_HEADER_MASK) + + +MSG_HEADER_SET MSG_CompositionPane::GetInterestingHeaders() +{ + MSG_HEADER_SET desired_mask = 0; + /* The FE has requested the list of "interesting" header fields. + The logic here is a bit complicated, in the interest of DWIMity. + */ + + /* Cc, Subject, and Attachments are always interesting. + */ + desired_mask |= (MSG_CC_HEADER_MASK | + MSG_SUBJECT_HEADER_MASK /* | + MSG_ATTACHMENTS_HEADER_MASK */); + + /* To is interesting if: + - it is non-empty, or + - this composition window was brought up with a "mail sending" + command (Mail New, Reply-*, Forward, or Post and Mail). + */ + if (*m_fields->GetTo() || + m_replyType == MSG_MailNew || + m_replyType == MSG_ReplyToSender || + m_replyType == MSG_ReplyToAll || + m_replyType == MSG_PostAndMailReply || + m_replyType == MSG_ForwardMessageAttachment || + m_replyType == MSG_ForwardMessageQuoted) + desired_mask |= MSG_TO_HEADER_MASK; + + /* CC is interesting if: + - it is non-empty, or + - this composition window was brought up as a reply to another + mail message. (Should mail-and-post do this too?) + */ + if ((*m_fields->GetCc()) || + m_replyType == MSG_ReplyToSender || + m_replyType == MSG_ReplyToAll) + desired_mask |= MSG_CC_HEADER_MASK; + + /* Reply-To and BCC are interesting if: + - they are non-empty, AND + - they are different from the default value + (meaning the user has edited them this session.) + */ + const char* reply_to = m_fields->GetReplyTo(); + const char* default_reply_to = + GetPrefs()->GetDefaultHeaderContents(MSG_REPLY_TO_HEADER_MASK); + if (reply_to && *reply_to && + ((default_reply_to && *default_reply_to) + ? !!XP_STRCMP (reply_to, default_reply_to) : TRUE)) + desired_mask |= MSG_REPLY_TO_HEADER_MASK; + + /* (see above.) */ + const char* bcc = m_fields->GetBcc(); + const char* default_bcc = + GetPrefs()->GetDefaultHeaderContents(MSG_BCC_HEADER_MASK); + if (bcc && *bcc && + ((default_bcc && *default_bcc) + ? !!XP_STRCMP (bcc, default_bcc) : TRUE)) + desired_mask |= MSG_BCC_HEADER_MASK; + + /* FCC is never interesting. + */ + + /* Newsgroups is interesting if: + - it is non-empty, or + - this composition window was brought up with a "news posting" + command (Post New, Post Reply, or Post and Mail). + */ + + const char* newsgroups = m_fields->GetNewsgroups(); + if ((newsgroups && *newsgroups) || + m_replyType == MSG_PostNew || + m_replyType == MSG_PostReply || + m_replyType == MSG_PostAndMailReply) + desired_mask |= MSG_NEWSGROUPS_HEADER_MASK; + + /* Followup-To is interesting if: + - it is non-empty, AND + - it differs from the Newsgroups field. + */ + const char* followup_to = m_fields->GetFollowupTo(); + if (followup_to && *followup_to && + (newsgroups ? XP_STRCMP (followup_to, newsgroups) : TRUE)) + desired_mask |= MSG_FOLLOWUP_TO_HEADER_MASK; + + return desired_mask; +} + + +void +MSG_CompositionPane::GetUrlDone_S(PrintSetup* pptr) +{ + ((MSG_CompositionPane*) (pptr->carg))->GetUrlDone(pptr); +} + + +#define QUOTE_BUFFER_SIZE 10240 + +void +MSG_CompositionPane::GetUrlDone(PrintSetup* /*pptr*/) +{ + XP_File file; + FREEIF(m_quoteUrl); + m_textContext = NULL; /* since this is called as a result of + TXFE_AllConnectionsComplete, we know this context + is going away by natural means */ + int bufSize = QUOTE_BUFFER_SIZE; + + XP_FileClose(m_print->out); + + XP_StatStruct stat; + char* curquote = NULL; + int32 replyOnTop = 0, replyWithExtraLines = 0; + + PREF_GetIntPref("mailnews.reply_on_top", &replyOnTop); + PREF_GetIntPref("mailnews.reply_with_extra_lines", &replyWithExtraLines); + + int32 extra = (m_markup ? 0 : + (replyWithExtraLines ? LINEBREAK_LEN * replyWithExtraLines + : 0)); + + if (XP_Stat(m_print->filename, &stat, xpTemporary) == 0) { + m_quotedText = (char*) XP_ALLOC(stat.st_size + 1 + extra); + + /* Insert two line break at the begining of the quoted text */ + if (!m_quotedText) return; + + curquote = m_quotedText; + + if (!m_markup && extra && replyOnTop == 1) { + for (; replyWithExtraLines > 0; replyWithExtraLines--) { + XP_STRCPY(curquote, LINEBREAK); + curquote += LINEBREAK_LEN; + if (m_quotefunc) + (*m_quotefunc)(m_quoteclosure, LINEBREAK); + } + } + } + + /* Open hateful temporary file as input */ + file = XP_FileOpen (m_print->filename, xpTemporary, XP_FILE_READ); + if (file) { + char* buf = NULL; + while (!buf && (bufSize >= 512)) + { + buf = (char*)XP_ALLOC(bufSize + 1); + if (!buf) + bufSize /= 2; + } + if (buf) { + int32 bufferLen; + CCCDataObject conv; + int doConv; + INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_context); + int16 win_csid = INTL_GetCSIWinCSID(c); + + /* + * We aren't actually converting character encodings here. + * (Note that both the "from" and "to" are the win_csid.) + * This makes it call a special routine that makes sure we + * deal with whole multibyte characters instead of partial + * ones that happen to lie on the boundary of the buffer. -- erik + */ + conv = INTL_CreateCharCodeConverter(); + if (conv) { + doConv = INTL_GetCharCodeConverter(win_csid, win_csid, conv); + } else { + doConv = 0; + } + + while (0 < (bufferLen = XP_FileRead(buf, bufSize, file))) { + char *newBuf; + buf[bufferLen] = '\0'; + if (doConv) { + newBuf = (char *) + INTL_CallCharCodeConverter(conv, + (unsigned char *) buf, + bufferLen); + if (!newBuf) { + newBuf = buf; + } + } else { + newBuf = buf; + } + if (m_quotefunc) { + (*m_quotefunc)(m_quoteclosure, newBuf); + } + + if (m_quotedText && curquote) { + XP_ASSERT(curquote + bufferLen <= m_quotedText + stat.st_size + extra); + if (curquote + bufferLen <= m_quotedText + stat.st_size + extra) { + XP_STRCPY(curquote, newBuf); + curquote += bufferLen; + } + } + + if (newBuf != buf) { + XP_FREE(newBuf); + } + } + + if (!m_markup && extra && replyOnTop == 0) { + for (; replyWithExtraLines > 1; replyWithExtraLines--) { + XP_STRCPY(curquote, LINEBREAK); + curquote += LINEBREAK_LEN; + if (m_quotefunc) + (*m_quotefunc)(m_quoteclosure, LINEBREAK); + } + } + + XP_FREE(buf); + if (conv) { + INTL_DestroyCharCodeConverter(conv); + } + } + XP_FileClose(file); + } + if (curquote) *curquote = '\0'; + m_cited = TRUE; + XP_FileRemove(m_print->filename, xpTemporary); + FREEIF(m_print->filename); + if (m_exitQuoting) { + (*m_exitQuoting)(m_dummyUrl, 0, m_context); + m_exitQuoting = NULL; + m_dummyUrl = NULL; + + /* hack that manages to get the cursor back to normal. */ + NET_SilentInterruptWindow(m_context); + } + if (m_quotefunc) { + (*m_quotefunc)(m_quoteclosure, NULL); + m_quotefunc = NULL; + } + + + /* Re-enable the UI. */ + FE_UpdateCompToolbar (this); +} + + + +class QuotePlainIntoHTML : public MSG_ZapIt { +public: + QuotePlainIntoHTML(MWContext* context); + ~QuotePlainIntoHTML(); + + int DoQuote(const char* data); + static int32 QuoteLine_s(char* line, uint32 line_length, void* closure); + int32 QuoteLine(char* line, uint32 line_length); +protected: + MWContext* m_context; + char* m_buffer; + uint32 m_size; + uint32 m_fp; + XP_Bool m_insertedpre; + char* m_outbuf; + int32 m_outbufsize; + int m_maxLineWidth; + int32 m_replyOnTop; + int32 m_replyWithExtraLines; +}; + + +static int +MyQuoteFunc(void* closure, const char* data) +{ + return ((QuotePlainIntoHTML*) closure)->DoQuote(data); +} + + + + + +QuotePlainIntoHTML::QuotePlainIntoHTML(MWContext* context) +{ + m_context = context; + if (EDT_PasteQuoteBegin(m_context, TRUE) != EDT_COP_OK) { + m_context = NULL; + } + PREF_GetIntPref("mailnews.reply_on_top", &m_replyOnTop); + PREF_GetIntPref("mailnews.reply_with_extra_lines", &m_replyWithExtraLines); +} + +QuotePlainIntoHTML::~QuotePlainIntoHTML() +{ + FREEIF(m_buffer); + delete [] m_outbuf; +} + + + + +int +QuotePlainIntoHTML::DoQuote(const char* data) +{ + if (data) { + if (!m_context) return 0; + return msg_LineBuffer(data, XP_STRLEN(data), &m_buffer, &m_size, &m_fp, FALSE, +#ifdef XP_OS2 + (int32 (_Optlink*) (char*,uint32,void*)) +#endif + QuoteLine_s, this); + + } else { + if (m_context) { + if (m_fp > 0) { + QuoteLine(m_buffer, m_fp); + } + if (m_insertedpre) { + EDT_PasteQuote(m_context, ""); + } + if ( 0 == m_replyOnTop && m_replyWithExtraLines) + for (;m_replyWithExtraLines > 0; m_replyWithExtraLines--) + EDT_PasteQuote(m_context, "
"); + EDT_PasteQuoteEnd(m_context); + + MSG_CompositionPane *cpane = (MSG_CompositionPane *) + MSG_FindPane(m_context, MSG_COMPOSITIONPANE); + if (cpane) + cpane->SetLineWidth(m_maxLineWidth); + } + delete this; + return 0; + } +} + + +int32 +QuotePlainIntoHTML::QuoteLine_s(char* line, uint32 length, void* closure) +{ + return ((QuotePlainIntoHTML*)closure)->QuoteLine(line, length); +} + +int32 +QuotePlainIntoHTML::QuoteLine(char* line, uint32 length) +{ + if (length > m_maxLineWidth) + m_maxLineWidth = length; + + if (length >= 2 && line[0] == '>' && line[1] == ' ') { + line += 2; + length -= 2; + if (!m_insertedpre) { + EDT_PasteQuote(m_context, "
");
+			m_insertedpre = TRUE;
+		}
+	}
+	else if (!m_insertedpre) {
+		if (1 == m_replyOnTop && m_replyWithExtraLines)
+		  for (; m_replyWithExtraLines > 0; m_replyWithExtraLines--)
+				EDT_PasteQuote(m_context, "
"); + } + + int l = length * 2 + 50; + if (l > m_outbufsize) { + if (l < 512) l = 512; + m_outbufsize = l; + delete [] m_outbuf; + m_outbuf = new char [m_outbufsize]; + } + if (m_outbuf) { + *m_outbuf = '\0'; + NET_ScanForURLs(NULL, line, length, m_outbuf, m_outbufsize, TRUE); + EDT_PasteQuote(m_context, m_outbuf); + } + return 0; +} + + +void +MSG_CompositionPane::QuoteHTMLDone_S(URL_Struct* url, int /*status*/, MWContext* /*context*/) +{ + MSG_CompositionPane *pane = (MSG_CompositionPane *) url->fe_data; + if (pane) { + FREEIF(pane->m_quoteUrl); + pane->m_quotefunc = NULL; + if (pane->m_quoteclosure) { + delete pane->m_quoteclosure; + pane->m_quoteclosure = NULL; + } + } + NET_FreeURLStruct(url); +} + + + +MsgERR MSG_CompositionPane::QuoteMessage(int (*func)(void* closure, + const char* data), + void* closure) +{ + MsgERR status = 0; + char* ptr; + m_haveQuoted = TRUE; + if (!m_defaultUrl) return 0; /* Nothing to quote. */ + if (m_quoteUrl) return 0; /* Currently already quoting! */ + + XP_ASSERT(m_quotefunc == NULL); + + if (m_markup) { + func = MyQuoteFunc; + closure = new QuotePlainIntoHTML(GetContext()); + if (!closure) return MK_OUT_OF_MEMORY; + } + + const char* htmlpart = m_fields->GetHTMLPart(); + XP_Bool quotehtml = (m_markup && htmlpart != NULL && *htmlpart != '\0'); + + if (m_quotedText) { + if (func) { +#ifdef EXTRA_QUOTE_BEGIN + if (m_markup) { + if (EDT_PasteQuoteBegin(GetContext(), + TRUE) != EDT_COP_OK) { + return eUNKNOWN; + } + } +#endif + (*func)(closure, m_quotedText); + (*func)(closure, NULL); + } + return 0; + } + + + m_quotefunc = func; + m_quoteclosure = closure; + + + m_quoteUrl = XP_STRDUP(m_defaultUrl); + if (!m_quoteUrl) return eOUT_OF_MEMORY; + + /* remove any position information from the url + */ + ptr = XP_STRCHR(m_quoteUrl, '#'); + if (ptr) *ptr = '\0'; + + if (quotehtml) { + URL_Struct* url = NET_CreateURLStruct(m_quoteUrl, NET_DONT_RELOAD); + if (!url) return MK_OUT_OF_MEMORY; + + // This is a hack, really should be url->msg_pane, but this prevents mail + // quoting from working at all. We just need SOME sort of way to give + // the msg_page to the completion function. + url->fe_data = (void *)this; + + // Set this because when quoting messages that might have been + // downloaded through IMAP MIME Parts on Demand (with some parts left out) + // we think it is OK to only quote the inline parts that have been + // downloaded. + // (That is, we don't think it's necessary to re-download the entire message + // from the server. We might be wrong about this, but so far haven't seen + // any examples to the contrary.) + url->allow_content_change = TRUE; + MSG_UrlQueue::AddUrlToPane (url, QuoteHTMLDone_S, this, TRUE, FO_QUOTE_HTML_MESSAGE); + return 0; + } + + + XL_InitializeTextSetup(m_print); + m_print->out = NULL; + m_print->prefix = "> "; + if (m_markup) { + if (htmlpart && *htmlpart) { + // Quoting html into html + // We are kind of doomed on this case when force sending plain text + // if there are hard line breaks. + m_print->width = 999; + } + else { + // Quoting plain text into html; there are hard line breaks + // We should not reformat the line when force sending plain text + // message + // This is done via recording the max line width when we quote each plain + // text line and then use the max line width if it is greater than default + // wraplinewidth. This prevent from reformatting the qutoed line in an + // unwanted fashion. + m_print->width = 998; + } + + // m_print->width = 999; // Cheap hack. The EDT_PasteQuote routine + // generates HTML that is better at wrapping + // than the TextFE is, so try and let it do the + // wrapping instead. + } + else { + if (htmlpart && *htmlpart) { + // We are quoting html message into plain text message + // Use wrapline width from preference + int32 width = 72; + PREF_GetIntPref("mailnews.wraplength", &width); + if (width == 0) width = 72; + else if (width < 10) width = 10; + else if (width > 30000) width = 30000; + m_print->width = width - 2; + } + else { + // We are quoting plain text message.to plain text + // We shouldn't reformat the original message since everyline already + // has hard line break we simply set the m_print->width to 999. + m_print->width = 997; + } + // m_print->width = 70; // The default window is 72 wide; subtract 2 for "> ". + } + m_print->carg = this; + m_print->url = NET_CreateURLStruct(m_defaultUrl, NET_DONT_RELOAD); + if (!m_print->url) { + status = eOUT_OF_MEMORY; + goto FAIL; + } + HG98265 + m_print->url->position_tag = 0; + m_print->completion = MSG_CompositionPane::GetUrlDone_S; + m_print->filename = WH_TempName(xpTemporary, "ns"); + if (!m_print->filename) { + status = eOUT_OF_MEMORY; + goto FAIL; + } + m_print->out = XP_FileOpen(m_print->filename, xpTemporary, XP_FILE_WRITE); + if (!m_print->out) { + status = 9999; /* ###tw Need the right error code! */ + goto FAIL; + } + m_print->cx = m_context; + m_exitQuoting = NULL; + m_dummyUrl = NET_CreateURLStruct("about:", NET_DONT_RELOAD); + m_dummyUrl->internal_url = TRUE; + if (m_dummyUrl) { + FE_SetWindowLoading(m_context, m_dummyUrl, &m_exitQuoting); + XP_ASSERT(m_exitQuoting != NULL); + } + + /* Start the URL loading... (msg_get_url_done gets called later.) */ + + + m_textContext = (MWContext*) XL_TranslateText(m_context, m_print->url, + m_print); + // ###tw I'm not at all sure this cast is the + // right thing to do here... + + return 0; +FAIL: + FREEIF(m_print->filename); + FREEIF(m_quoteUrl); + if (m_print->out) { + XP_FileClose(m_print->out); + m_print->out = NULL; + } + if (m_print->url) { + NET_FreeURLStruct(m_print->url); + m_print->url = NULL; + } + return status; +} + + +int +MSG_CompositionPane::PastePlaintextQuotation(const char* str) +{ + if (str && *str) { + if (EDT_PasteQuoteBegin(m_context, TRUE) != EDT_COP_OK) { + return -1; + } + EDT_PasteQuote(m_context, "
");
+		EDT_PasteQuote(m_context, (char*) str);
+		EDT_PasteQuote(m_context, "
"); + EDT_PasteQuoteEnd(m_context); + } + return 0; +} + + + +int +MSG_CompositionPane::SetAttachmentList(struct MSG_AttachmentData* list) +{ + int count = 0; + MSG_AttachmentData *tmp; + MSG_AttachmentData *tmp2; + int status = 0; + + ClearCompositionMessageID(); /* Since the attachment list has changed, + the message has changed, so make sure + we're using a fresh message-id when we + try to send it. */ + + msg_free_attachment_list(m_attachData); + m_attachData = NULL; + + for (tmp = list; tmp && tmp->url; tmp++) count++; + + if (count > 0) { + m_attachData = (MSG_AttachmentData*) + XP_ALLOC((count + 1) * sizeof(MSG_AttachmentData)); + if (!m_attachData) { + FE_Alert(m_context, XP_GetString(MK_OUT_OF_MEMORY)); + return MK_OUT_OF_MEMORY; + } + + XP_MEMSET(m_attachData, 0, (count + 1) * sizeof(MSG_AttachmentData)); + } + + if (count > 0) { + for (tmp = list, tmp2 = m_attachData; tmp->url; tmp++, tmp2++) { + tmp2->url = XP_STRDUP(tmp->url); + if (tmp->desired_type) { + tmp2->desired_type = XP_STRDUP(tmp->desired_type); + } + if (tmp->real_type) { + tmp2->real_type = XP_STRDUP(tmp->real_type); + } + if (tmp->real_encoding) { + tmp2->real_encoding = XP_STRDUP(tmp->real_encoding); + } + if (tmp->real_name) { + tmp2->real_name = XP_STRDUP(tmp->real_name); + } + if (tmp->description) { + tmp2->description = XP_STRDUP(tmp->description); + } + if (tmp->x_mac_type) { + tmp2->x_mac_type = XP_STRDUP(tmp->x_mac_type); + } + if (tmp->x_mac_creator) { + tmp2->x_mac_creator = XP_STRDUP(tmp->x_mac_creator); + } + } + } + status = DownloadAttachments(); + return status; +} + + +const struct MSG_AttachmentData * +MSG_CompositionPane::GetAttachmentList() +{ + if (m_attachData && m_attachData[0].url != NULL) return m_attachData; + return NULL; +} + + +static void +msg_free_attachment_list(struct MSG_AttachmentData *list) +{ + MSG_AttachmentData* tmp; + if (!list) return; + for (tmp = list ; tmp->url ; tmp++) { + XP_FREE((char*) tmp->url); + if (tmp->desired_type) XP_FREE((char*) tmp->desired_type); + if (tmp->real_type) XP_FREE((char*) tmp->real_type); + if (tmp->real_encoding) XP_FREE((char*) tmp->real_encoding); + if (tmp->real_name) XP_FREE((char*) tmp->real_name); + if (tmp->description) XP_FREE((char*) tmp->description); + if (tmp->x_mac_type) XP_FREE((char*) tmp->x_mac_type); + if (tmp->x_mac_creator) XP_FREE((char*) tmp->x_mac_creator); + } + XP_FREEIF(list); +} + + + +/* Whether the given saved-attachment-file thing is a match for the given + URL (in source and type-conversion.) + */ +static XP_Bool +msg_attachments_match (MSG_AttachmentData *attachment, + MSG_AttachedFile *file) +{ + const char *dt; + XP_ASSERT(attachment && file); + if (!attachment || !file) return FALSE; + XP_ASSERT(attachment->url && file->orig_url); + if (!attachment->url || !file->orig_url) return FALSE; + + XP_ASSERT(file->type); + if (!file->type) return FALSE; + XP_ASSERT(file->file_name); + if (XP_STRCMP(attachment->url, file->orig_url)) return FALSE; + + /* If the attachment has a conversion type specified (and it's not the + "no conversion" type) then this is only a match if the saved document + ended up with that type as well. + */ + dt = ((attachment->desired_type && *attachment->desired_type) + ? attachment->desired_type + : 0); + if (dt && !strcasecomp(dt, TEXT_HTML)) + dt = 0; + + /* dt only has a value if it's "not `As Is', ie, text/plain or app/ps. */ + if (dt && XP_STRCMP(dt, file->type)) + return FALSE; + + return TRUE; +} + + +int +MSG_CompositionPane::DownloadAttachments() +{ + int attachment_count = 0; + int new_download_count = 0; + int download_overlap_count = 0; + MSG_AttachmentData *tmp; + MSG_AttachmentData *downloads = 0; + MSG_AttachedFile *tmp2; + int returnValue = 0; + + // *** Relax the rule a little bit to enable resume downloading at + // *** send time. + // XP_ASSERT(!m_deliveryInProgress); + + // Make sure we do not have an attachement already pending. If we do, + // then we do not want to interrupt it. The new attachement will be picked up + // when we go to send the message. + + if (m_attachmentInProgress) + return MK_INTERRUPTED; // this status value is ignored by the caller + + m_pendingAttachmentsCount = 0; // reset m_pendingAttachmentsCount + // in case the attachmentlist has been + // cleared + + if (m_attachData) + for (tmp = m_attachData; tmp->url; tmp++) + attachment_count++; + + /* First, go through the list of desired attachments, and the list of + currently-saved attachments, and delete the files (and data) of the + ones which were attached/saved but are no longer. + */ + tmp2 = m_attachedFiles; + while (tmp2 && tmp2->orig_url) { + XP_Bool match = FALSE; + for (tmp = m_attachData; tmp && tmp->url; tmp++) { + if (msg_attachments_match(tmp, tmp2)) { + match = TRUE; + break; + } + } + if (match) { + tmp2++; + download_overlap_count++; + } else { + /* Delete the file, free the strings, and pull the other entries + forward to cover this one. */ + int i = 0; + + if (tmp2->file_name) { + XP_FileRemove(tmp2->file_name, xpFileToPost); + XP_FREE(tmp2->file_name); + } + FREEIF(tmp2->orig_url); + FREEIF(tmp2->type); + FREEIF(tmp2->encoding); + FREEIF(tmp2->description); + FREEIF(tmp2->x_mac_type); + FREEIF(tmp2->x_mac_creator); + FREEIF(tmp2->real_name); + + do { + tmp2[i]=tmp2[i+1]; + } while (tmp2[i++].orig_url); + } + } + + /* Now download any new files that are in the list. + */ + if (download_overlap_count != attachment_count) { + MSG_AttachmentData *dfp; + new_download_count = attachment_count - download_overlap_count; + m_pendingAttachmentsCount = new_download_count; + downloads = (MSG_AttachmentData *) + XP_ALLOC(sizeof(MSG_AttachmentData) * (new_download_count + 1)); + if (!downloads) { + FE_Alert(m_context, XP_GetString(MK_OUT_OF_MEMORY)); + return MK_OUT_OF_MEMORY; + } + XP_MEMSET(downloads, 0, sizeof(*downloads) * (new_download_count + 1)); + + dfp = downloads; + for (tmp = m_attachData; tmp && tmp->url; tmp++) { + XP_Bool match = FALSE; + if (m_attachedFiles) + for (tmp2 = m_attachedFiles; tmp2->orig_url; tmp2++) { + if (msg_attachments_match(tmp, tmp2)) { + match = TRUE; + break; + } + } + if (!match) { + *dfp = *tmp; + dfp++; + } + } + if (!downloads[0].url) return 0; + // *** Relax the rule a little bit to enable resume downloading at + // *** send time. + // XP_ASSERT(!m_deliveryInProgress); + XP_ASSERT(!m_attachmentInProgress); + m_attachmentInProgress = TRUE; + FE_UpdateCompToolbar (this); + returnValue = msg_DownloadAttachments(this, this, downloads, +#ifdef XP_OS2 + (void (_Optlink*) (MWContext*,void*,int,const char*,MSG_AttachedFile*)) +#endif + MSG_CompositionPane::DownloadAttachmentsDone_S); + XP_FREE(downloads); + } + return returnValue; +} + +void +MSG_CompositionPane::DownloadAttachmentsDone_S(MWContext *context, + void *fe_data, + int status, + const char *error_message, + struct MSG_AttachedFile *attachments) +{ + ((MSG_CompositionPane*) fe_data)->DownloadAttachmentsDone(context, status, + error_message, + attachments); +} + +void +MSG_CompositionPane::DownloadAttachmentsDone(MWContext* context, int status, + const char* error_message, + struct MSG_AttachedFile *attachments) +{ + XP_ASSERT(context == m_context); + + int old_count = 0; + int new_count = 0; + struct MSG_AttachedFile *tmp; + MSG_AttachedFile *newd; + + // *** Relax the rule a little bit to enable resume downloading at + // *** send time. + // XP_ASSERT(!m_deliveryInProgress); + if (m_attachmentInProgress) { + m_attachmentInProgress = FALSE; + FE_UpdateCompToolbar (this); + } + + if (status < 0) goto FAIL; + + status = MK_INTERRUPTED; + if (!m_attachData) goto FAIL; + + HG98229 + if (m_attachedFiles) { + for (tmp = m_attachedFiles; tmp->orig_url; tmp++) old_count++; + } + if (attachments) { + for (tmp = attachments; tmp->orig_url; tmp++) new_count++; + } + + if (old_count + new_count == 0) goto FAIL; + newd = (MSG_AttachedFile *) + XP_REALLOC(m_attachedFiles, + ((old_count + new_count + 1) + * sizeof(MSG_AttachedFile))); + + if (!newd) { + status = MK_OUT_OF_MEMORY; + error_message = XP_GetString(status); + goto FAIL; + } + m_attachedFiles = newd; + XP_MEMCPY(newd + old_count, + attachments, + sizeof(MSG_AttachedFile) * (new_count + 1)); + + XP_ASSERT (m_pendingAttachmentsCount >= new_count); + m_pendingAttachmentsCount -= new_count; + if (m_deliveryInProgress) { + m_deliveryInProgress = FALSE; + DoneComposeMessage(m_deliver_mode); + } + return; + +FAIL: + XP_ASSERT(status < 0); + if (error_message) { + FE_Alert(context, error_message); + } + else if (status != MK_INTERRUPTED) { + char *errmsg; + errmsg = PR_smprintf(XP_GetString(MK_COMMUNICATIONS_ERROR), status); + if (errmsg) { + FE_Alert(context, errmsg); + XP_FREE(errmsg); + } + } + + /* Since we weren't able to store it, ditch the files and the strings + describing them. */ + msg_delete_attached_files(attachments); +} + + +/* How many implementations of this are there now? 4? */ +static void +msg_mid_truncate_string (const char *input, char *output, int max_length) +{ + int L = XP_STRLEN(input); + if (L <= max_length) { + XP_MEMCPY(output, input, L+1); + } else { + int mid = (max_length - 3) / 2; + char *tmp = 0; + if (input == output) { + tmp = output; + output = (char *) XP_ALLOC(max_length + 1); + *tmp = 0; + if (!output) return; + } + XP_MEMCPY(output, input, mid); + XP_MEMCPY(output + mid, "...", 3); + XP_MEMCPY(output + mid + 3, input + L - mid, mid + 1); + + if (tmp) { + XP_MEMCPY(tmp, output, max_length + 1); + XP_FREE(output); + } + } +} + + +char * +MSG_CompositionPane::GetAttachmentString() +{ + /* #### bug 8688 */ + + MSG_AttachmentData *tmp; + int count; + int chars_per_attachment; + int default_field_width = 63; /* 72 - some space for the word + "Attachment" */ + + count = 0; + for (tmp = m_attachData; tmp && tmp->url ; tmp++) count++; + if (count <= 0) return 0; + + chars_per_attachment = (default_field_width - (count * 2)) / count; + if (chars_per_attachment < 15) chars_per_attachment = 15; + + FREEIF(m_attachmentString); + + m_attachmentString = + (char *) XP_ALLOC(count * (chars_per_attachment + 3) + 20); + if (!m_attachmentString) return 0; + *m_attachmentString = 0; + + for (tmp = m_attachData ; tmp && tmp->url ; tmp++) { + const char *url = tmp->real_name ? tmp->real_name : tmp->url; + const char *ptr = XP_STRCHR(url, ':'); + char *result = 0; + + if (!ptr) { + /* No colon? Must be a file name. */ + ptr = url; + goto DO_FILE; + } + + if (!XP_STRNCMP(url, "news:", 5) || + !XP_STRNCMP(url, "snews:", 6) || + !XP_STRNCMP(url, "IMAP:", 5) || + !XP_STRNCMP(url, "mailbox:", 8)) { + /* ###tw Unfortunately, I don't think this stuff quite ports + directly to the new world, so I'm gonna disable it for now... */ + + goto DONE; + } + + /* Ok, so it must be something vaguely file-name-like. + Look for a slash. + */ + DO_FILE: + { + char *ptr2 = XP_STRDUP(ptr); + if (!ptr2) goto DONE; + char* s = XP_STRCHR(ptr2, '?'); + if (s) *s = 0; + s = XP_STRCHR(ptr2, '#'); + if (s) *s = 0; + s = XP_STRRCHR(ptr2, '/'); + if(!s) { + XP_FREE(ptr2); + goto DONE; + } + s++; + if (!*s || !strcasecomp(s,"index.html") || + !strcasecomp(s,"index.htm")) { + /* This had a useless file name; take the last directory name. */ + char *s2 = s-1; + if (*s2 == '/') s2--; + while (s2 > ptr2 && *s2 != '/') s2--; + if (*s2 == ':' || *s2 == '/') s2++; + result = (char *) XP_ALLOC (s - s2 + 1); + XP_MEMCPY (result, s2, s - s2); + result[s - s2] = 0; + } else { + /* The file name is ok; use it. */ + result = XP_STRDUP (s); + } + NET_UnEscape (result); + XP_FREE(ptr2); + goto DONE; + } + + DONE: + if (tmp != m_attachData) { + XP_STRCAT(m_attachmentString, "; "); + } + + if (!result) { + if (!XP_STRNCMP(url, "news:", 5) || + !XP_STRNCMP(url, "snews:", 6) || + !XP_STRNCMP(url, "IMAP:", 5) || + !XP_STRNCMP(url, "mailbox:", 8)) { + result = XP_STRDUP(""); + } else { + result = XP_STRDUP(url); + } + if (!result) break; + } + + msg_mid_truncate_string(result, + (m_attachmentString + + XP_STRLEN(m_attachmentString)), + chars_per_attachment); + XP_FREE(result); + } + + return m_attachmentString; +} + + +char* +MSG_CompositionPane::UpdateHeaderContents(MSG_HEADER_SET which_header, + const char* ) +{ + switch (which_header) { + case MSG_TO_HEADER_MASK: + case MSG_CC_HEADER_MASK: + case MSG_BCC_HEADER_MASK: + case MSG_REPLY_TO_HEADER_MASK: + break; + } + return NULL; +} + +int +MSG_CompositionPane::SetCompHeader(MSG_HEADER_SET header, + const char *value) +{ + XP_ASSERT(header != MSG_ATTACHMENTS_HEADER_MASK); + ClearCompositionMessageID(); + m_fields->SetHeader(header, value); + return 0; +} + +const char* +MSG_CompositionPane::GetCompHeader(MSG_HEADER_SET header) +{ + if (header == MSG_ATTACHMENTS_HEADER_MASK) { + return GetAttachmentString(); + } else { + return m_fields ? m_fields->GetHeader(header) : (char *)NULL; + } +} + +int +MSG_CompositionPane::SetCompBoolHeader(MSG_BOOL_HEADER_SET header, + XP_Bool bValue) +{ + return m_fields->SetBoolHeader(header, bValue); +} + +XP_Bool +MSG_CompositionPane::GetCompBoolHeader(MSG_BOOL_HEADER_SET header) +{ + return m_fields ? m_fields->GetBoolHeader(header) : FALSE; +} + +const char* +MSG_CompositionPane::GetCompBody() +{ + return m_fields ? m_fields->GetBody() : (char *)NULL; +} + +int +MSG_CompositionPane::SetCompBody(const char* value) +{ + return m_fields->SetBody(value); +} + +const char* +MSG_CompositionPane::GetWindowTitle() +{ + const char *s; + + if (*m_fields->GetSubject()) { + s = m_fields->GetSubject(); + } else if (*m_fields->GetTo()) { + s = m_fields->GetTo(); + } else if (*m_fields->GetNewsgroups()) { + s = m_fields->GetNewsgroups(); + } else { + s = XP_GetString(MK_MSG_MSG_COMPOSITION); + } + return s; +} + + + +int +MSG_CompositionPane::SanityCheck(int skippast) +{ + const char* body = m_fields->GetBody(); + const char* sub = m_fields->GetSubject(); + + + if (skippast == MK_MSG_DOUBLE_INCLUDE) goto AFTER_DOUBLE_INCLUDE; + if (skippast == MK_MSG_EMPTY_MESSAGE) goto AFTER_EMPTY_MESSAGE; + if (skippast == MK_MSG_MISSING_SUBJECT) goto AFTER_MISSING_SUBJECT; + HG60283 + + // Check if they have quoted a document and not edited it, and also + // attached the same document. + if (m_quotedText && + XP_STRNCMP(body, m_quotedText, XP_STRLEN(m_quotedText)) == 0 && + m_attachData && + m_attachData[0].url && + m_defaultUrl && + !XP_STRCMP (m_attachData[0].url, m_defaultUrl)) { + return MK_MSG_DOUBLE_INCLUDE; + } + +AFTER_DOUBLE_INCLUDE: + // Check if this message has no attachments, and the body has not been + // edited. + if (XP_STRCMP(body, m_initfields->GetBody()) == 0 && + (!m_attachData || !m_attachData[0].url)) { + return MK_MSG_EMPTY_MESSAGE; + } + +AFTER_EMPTY_MESSAGE: + // Check if they neglected to type a subject. + if (sub) { + while (XP_IS_SPACE(*sub)) sub++; + } + if (!sub || !*sub) { + return MK_MSG_MISSING_SUBJECT; + } + +AFTER_MISSING_SUBJECT: +HG60207 + return 0; + +} + + +void +MSG_CompositionPane::DeliveryDoneCB_s(MWContext *context, void *fe_data, + int status, const char *error_message) +{ + ((MSG_CompositionPane*) fe_data)->DeliveryDoneCB(context, status, + error_message); +} + +void +MSG_CompositionPane::DeliveryDoneCB(MWContext* context, int status, + const char* error_message) +{ + XP_ASSERT(context == m_context); + + // *** We don't want to set m_status to status. The default value + // of m_status (-1) prevents the composition pane from closing down + // once we done with saving draft. The composition pane should remain up. + if ((m_deliver_mode != MSG_SaveAsDraft && m_deliver_mode != MSG_SaveAsTemplate) + || (m_deliver_mode == MSG_SaveAsDraft && m_closeAfterSave)) + m_status = status; + + XP_ASSERT(!m_attachmentInProgress); + if (m_deliveryInProgress) { + m_deliveryInProgress = FALSE; +#ifndef XP_UNIX /* Does not need this function call for UNIX. + This will prevent toolbar to be enabled after msg sent on + UNIX. */ + FE_UpdateCompToolbar (this); +#endif + } + + if (status < 0) { + if (error_message) { + FE_Alert(context, error_message); + } else if (status != MK_INTERRUPTED) { + char *errmsg; + + HG92755 + errmsg = PR_smprintf(XP_GetString(MK_COMMUNICATIONS_ERROR), + status); + if (errmsg) { + FE_Alert(context, errmsg); + XP_FREE(errmsg); + } + } + } else { + /* ### jht bug 45220 -- do following only when successfully deliver the message */ + /* time to delete message from draft/outbox */ + } +} + +void +MSG_CompositionPane::MailCompositionAllConnectionsComplete () +{ + /* This may be redundant, I'm not sure... */ + if (m_deliveryInProgress) { + m_deliveryInProgress = FALSE; + FE_UpdateCompToolbar(this); + } + if (m_attachmentInProgress) { + m_attachmentInProgress = FALSE; + FE_UpdateCompToolbar(this); + } + + if (m_status >= 0) { + delete this; + } +} + + +XP_Bool +MSG_CompositionPane::DeliveryInProgress () +{ + /* Disable the UI if delivery, attachment loading, or quoting is in + progress. */ + return m_deliveryInProgress || m_attachmentInProgress || (m_quoteUrl != 0); +} + +/* This function sets the markup flag to indicate that the message + body is HTML. Returns the previously set value. +*/ + +void +MSG_CompositionPane::SetHTMLMarkup(XP_Bool flag) +{ + m_markup = flag; +} + +XP_Bool +MSG_CompositionPane::GetHTMLMarkup(void) +{ + return m_markup; +} + +int +MSG_CompositionPane::DoneComposeMessage( MSG_Deliver_Mode deliver_mode ) +{ + int attachment_count = 0; + XP_Bool digest_p = FALSE; + int status = 0; + + if (m_pendingAttachmentsCount) { + m_deliveryInProgress = TRUE; // so that DoneComposeMessage is called again + status = DownloadAttachments(); + return status; + } + + HG92762 + + if (m_markup && (deliver_mode != MSG_SaveAsDraft && + deliver_mode != MSG_SaveAsTemplate)) { + MSG_HTMLComposeAction action = DetermineHTMLAction(); + if (action == MSG_HTMLAskUser) + { + status = -1; +#ifndef XP_UNIX // Unix will have to make this dialog a blocking dialog for this logic to work + if (m_callbacks.CreateAskHTMLDialog) { + status = (*m_callbacks.CreateAskHTMLDialog)(this, m_callbackclosure); + // if status == 0, then user wants to Send, so we continue + // if status < 0, do the HTML dialogs + // if status > 0, cancel + if (status == 0) { + action = DetermineHTMLAction(); // reget the new action + XP_ASSERT(action != MSG_HTMLAskUser); + if (action == MSG_HTMLAskUser) { + // Boy, the FE is busted. Use our own. + status = -1; + } + } + if (status > 0) return 0; // we have to return 0, even in the cancel case so that an error won't get displayed + } +#endif + // if status still negative, do the HTML thing + if (status < 0) { + static XPDialogInfo dialogInfo = { + 0, // I'll provide all my own buttons, thanks. +#ifdef XP_OS2 + (PRBool (_Optlink*) (XPDialogState*,char**,int,unsigned int)) +#endif + MSG_CompositionPane::AskDialogDone_s, + 500, + 300 + }; + XPDialogStrings* strings = + XP_GetDialogStrings(MK_MSG_ASK_HTML_MAIL); + if (!strings) return MK_OUT_OF_MEMORY; + XP_MakeHTMLDialog(GetContext(), &dialogInfo, + MK_MSG_ASK_HTML_MAIL_TITLE, strings, this, PR_FALSE); + return 0; + } + + } + + switch (action) { + case MSG_HTMLUseMultipartAlternative: + m_fields->SetUseMultipartAlternative(TRUE); + break; + case MSG_HTMLConvertToPlaintext: + m_fields->SetForcePlainText(TRUE); + break; + case MSG_HTMLSendAsHTML: + break; + default: + XP_ASSERT(0); + return -1; + } + } + + HG42440 + + const char* body = m_fields->GetBody(); + uint32 body_length = XP_STRLEN(body); + + + for (attachment_count = 0; + m_attachData && m_attachData[attachment_count].url; + attachment_count++) + ; + + if (m_attachData && m_attachData[0].url && m_attachData[1].url ) { + MSG_AttachmentData* s; + digest_p = TRUE; + for (s = m_attachData ; s->url ; s++) { + /* When there are attachments, start out assuming it is a digest, + and then decide that it is not if any of the attached URLs are + not mail or news messages. */ + if (XP_STRNCMP(s->url, "news:", 5) != 0 && + XP_STRNCMP(s->url, "snews:", 6) != 0 && + XP_STRNCMP(s->url, "IMAP:", 5) != 0 && + XP_STRNCMP(s->url, "mailbox:", 8) != 0) { + digest_p = FALSE; + } + } + } + + XP_ASSERT(!m_attachmentInProgress); + XP_ASSERT(!m_deliveryInProgress); + m_deliveryInProgress = TRUE; + FE_UpdateCompToolbar(this); + + if (m_messageId == NULL) { + m_messageId = msg_generate_message_id(); + m_duplicatePost = FALSE; + } else { + m_duplicatePost = TRUE; + } + + m_fields->SetMessageId(m_messageId); + + MSG_MimeRelatedSaver *fs = NULL; + +#ifdef MSG_SEND_MULTIPART_RELATED + if (m_markup) + { + char *pRootPartName = NULL; // set to NULL so that we're given a temp filename + fs = new MSG_MimeRelatedSaver(this, m_context, m_fields, + digest_p, deliver_mode, + body, body_length, + m_attachedFiles, + DeliveryDoneCB_s, + &pRootPartName); + if (fs) + { + EDT_SaveFileTo(m_context, + ((deliver_mode == MSG_SaveAsDraft && !m_closeAfterSave) || + deliver_mode == MSG_SaveAsTemplate) ? + ED_FINISHED_SAVE_DRAFT : ED_FINISHED_MAIL_SEND, + pRootPartName, fs, TRUE, TRUE); + // Note: EDT_SaveFileTo will delete fs, even if it returns an error. So + // it is incorrect to delete it here. Also, we ignore the result, because + // it calls FE_Alert itself. + } + FREEIF(pRootPartName); + } + else + +#endif // MSG_SEND_MULTIPART_RELATED + + { + msg_StartMessageDeliveryWithAttachments(this, this, + m_fields, + digest_p, FALSE, deliver_mode, + (m_markup ? TEXT_HTML : + TEXT_PLAIN), + body, body_length, + m_attachedFiles, + NULL, +#ifdef XP_OS2 + (void (_Optlink*) (MWContext*,void*,int,const char*)) +#endif + DeliveryDoneCB_s); + } + return 0; // Always success, because Errors were reported and handled by EDT_SaveFileTo. +} + +int +MSG_CompositionPane::SendMessageNow() +{ + PREF_SetBoolPref("network.online", TRUE); // make sure we're online. + // remember if we're queued so we know which folder + m_deliver_mode = MSG_DeliverNow; + + HG73943 + if (m_fields->GetAttachVCard()) + CreateVcardAttachment(); + // counts we need to update. + return DoneComposeMessage(MSG_DeliverNow); +} + +int +MSG_CompositionPane::QueueMessageForLater() +{ + // remember if we're queued so we know which folder + m_deliver_mode = MSG_QueueForLater; + if (m_fields->GetAttachVCard()) + CreateVcardAttachment(); + // counts we need to update. + return DoneComposeMessage(MSG_QueueForLater); +} + +int +MSG_CompositionPane::SaveMessageAsDraft() +{ + + m_deliver_mode = MSG_SaveAsDraft; // remember if we're saved as draft so we know which + // folder counts we need to update. + return DoneComposeMessage(MSG_SaveAsDraft); +} + +int +MSG_CompositionPane::SaveMessageAsTemplate() +{ + m_deliver_mode = MSG_SaveAsTemplate; + +#ifdef SUPPORT_X_TEMPLATE_NAME + char *defaultName = NULL; + + defaultName = FE_Prompt (GetContext(), + XP_GetString(MK_MSG_ENTER_NAME_FOR_TEMPLATE), + m_fields->GetSubject()); + if (defaultName && *defaultName) + { + m_fields->SetTemplateName(defaultName); + XP_FREEIF(defaultName); + } +#endif /* SUPPORT_X_TEMPLATE_NAME */ + + return DoneComposeMessage(MSG_SaveAsTemplate); +} + +static int +StuffParams(char** params, const char* name, int32 value) +{ + char* escaped = NET_EscapeHTML(name); + if (!escaped) return MK_OUT_OF_MEMORY; + char* tmp = PR_smprintf("