From 3e792437009b233b30755e42f083d1baedd31a89 Mon Sep 17 00:00:00 2001 From: "akkana%netscape.com" Date: Fri, 4 Sep 1998 19:04:30 +0000 Subject: [PATCH] Mailto landing. The mailto library is the mail compose code ripped out of the old Messenger libmsg library, then cleaned up somewhat (it could still use more cleaning). This library should only be built ifdef MOZ_MAIL_COMPOSE. git-svn-id: svn://10.0.0.236/trunk@9361 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/lib/mailto/Makefile | 90 + mozilla/lib/mailto/ad_codes.h | 129 + mozilla/lib/mailto/ad_strm.c | 719 +++++ mozilla/lib/mailto/addrutil.cpp | 1607 ++++++++++ mozilla/lib/mailto/ap_decod.c | 1776 +++++++++++ mozilla/lib/mailto/ap_encod.c | 733 +++++ mozilla/lib/mailto/appledbl.c | 615 ++++ mozilla/lib/mailto/appledbl.h | 232 ++ mozilla/lib/mailto/bh_strm.c | 411 +++ mozilla/lib/mailto/bytearr.cpp | 221 ++ mozilla/lib/mailto/bytearr.h | 64 + mozilla/lib/mailto/dwordarr.cpp | 281 ++ mozilla/lib/mailto/dwordarr.h | 68 + mozilla/lib/mailto/errcode.h | 118 + mozilla/lib/mailto/error.h | 54 + mozilla/lib/mailto/m_binhex.c | 1041 +++++++ mozilla/lib/mailto/m_binhex.h | 184 ++ mozilla/lib/mailto/mhtmlstm.cpp | 885 ++++++ mozilla/lib/mailto/mhtmlstm.h | 225 ++ mozilla/lib/mailto/msg.h | 318 ++ mozilla/lib/mailto/msgbg.cpp | 133 + mozilla/lib/mailto/msgbg.h | 83 + mozilla/lib/mailto/msgcflds.cpp | 382 +++ mozilla/lib/mailto/msgcflds.h | 288 ++ mozilla/lib/mailto/msgcpane.cpp | 3312 +++++++++++++++++++++ mozilla/lib/mailto/msgcpane.h | 274 ++ mozilla/lib/mailto/msgglue.cpp | 834 ++++++ mozilla/lib/mailto/msghdr.h | 92 + mozilla/lib/mailto/msgmast.cpp | 104 + mozilla/lib/mailto/msgmast.h | 64 + mozilla/lib/mailto/msgmdn.cpp | 885 ++++++ mozilla/lib/mailto/msgmdn.h | 127 + mozilla/lib/mailto/msgpane.cpp | 647 ++++ mozilla/lib/mailto/msgpane.h | 200 ++ mozilla/lib/mailto/msgppane.cpp | 59 + mozilla/lib/mailto/msgppane.h | 39 + mozilla/lib/mailto/msgprefs.cpp | 979 +++++++ mozilla/lib/mailto/msgprefs.h | 301 ++ mozilla/lib/mailto/msgprnot.h | 51 + mozilla/lib/mailto/msgsend.cpp | 4886 +++++++++++++++++++++++++++++++ mozilla/lib/mailto/msgsend.h | 252 ++ mozilla/lib/mailto/msgsendp.cpp | 768 +++++ mozilla/lib/mailto/msgsendp.h | 112 + mozilla/lib/mailto/msgurlq.cpp | 502 ++++ mozilla/lib/mailto/msgurlq.h | 206 ++ mozilla/lib/mailto/msgutils.c | 852 ++++++ mozilla/lib/mailto/msgutils.h | 84 + mozilla/lib/mailto/msgzap.cpp | 39 + mozilla/lib/mailto/msgzap.h | 36 + mozilla/lib/mailto/ptrarray.cpp | 525 ++++ mozilla/lib/mailto/ptrarray.h | 160 + 51 files changed, 27047 insertions(+) create mode 100644 mozilla/lib/mailto/Makefile create mode 100644 mozilla/lib/mailto/ad_codes.h create mode 100644 mozilla/lib/mailto/ad_strm.c create mode 100644 mozilla/lib/mailto/addrutil.cpp create mode 100644 mozilla/lib/mailto/ap_decod.c create mode 100644 mozilla/lib/mailto/ap_encod.c create mode 100644 mozilla/lib/mailto/appledbl.c create mode 100644 mozilla/lib/mailto/appledbl.h create mode 100644 mozilla/lib/mailto/bh_strm.c create mode 100644 mozilla/lib/mailto/bytearr.cpp create mode 100644 mozilla/lib/mailto/bytearr.h create mode 100644 mozilla/lib/mailto/dwordarr.cpp create mode 100644 mozilla/lib/mailto/dwordarr.h create mode 100644 mozilla/lib/mailto/errcode.h create mode 100644 mozilla/lib/mailto/error.h create mode 100644 mozilla/lib/mailto/m_binhex.c create mode 100644 mozilla/lib/mailto/m_binhex.h create mode 100644 mozilla/lib/mailto/mhtmlstm.cpp create mode 100644 mozilla/lib/mailto/mhtmlstm.h create mode 100644 mozilla/lib/mailto/msg.h create mode 100644 mozilla/lib/mailto/msgbg.cpp create mode 100644 mozilla/lib/mailto/msgbg.h create mode 100644 mozilla/lib/mailto/msgcflds.cpp create mode 100644 mozilla/lib/mailto/msgcflds.h create mode 100644 mozilla/lib/mailto/msgcpane.cpp create mode 100644 mozilla/lib/mailto/msgcpane.h create mode 100644 mozilla/lib/mailto/msgglue.cpp create mode 100644 mozilla/lib/mailto/msghdr.h create mode 100644 mozilla/lib/mailto/msgmast.cpp create mode 100644 mozilla/lib/mailto/msgmast.h create mode 100644 mozilla/lib/mailto/msgmdn.cpp create mode 100644 mozilla/lib/mailto/msgmdn.h create mode 100644 mozilla/lib/mailto/msgpane.cpp create mode 100644 mozilla/lib/mailto/msgpane.h create mode 100644 mozilla/lib/mailto/msgppane.cpp create mode 100644 mozilla/lib/mailto/msgppane.h create mode 100644 mozilla/lib/mailto/msgprefs.cpp create mode 100644 mozilla/lib/mailto/msgprefs.h create mode 100644 mozilla/lib/mailto/msgprnot.h create mode 100644 mozilla/lib/mailto/msgsend.cpp create mode 100644 mozilla/lib/mailto/msgsend.h create mode 100644 mozilla/lib/mailto/msgsendp.cpp create mode 100644 mozilla/lib/mailto/msgsendp.h create mode 100644 mozilla/lib/mailto/msgurlq.cpp create mode 100644 mozilla/lib/mailto/msgurlq.h create mode 100644 mozilla/lib/mailto/msgutils.c create mode 100644 mozilla/lib/mailto/msgutils.h create mode 100644 mozilla/lib/mailto/msgzap.cpp create mode 100644 mozilla/lib/mailto/msgzap.h create mode 100644 mozilla/lib/mailto/ptrarray.cpp create mode 100644 mozilla/lib/mailto/ptrarray.h 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("