/* -*- 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;
}
