/* -*- 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"


#ifdef MOZ_ENDER_MIME
#include "secrng.h"	/* for RNG_GenerateGlobalRandomBytes() */
extern "C" {
#include "xp_file.h"
#include "edt.h" /*safelist calls*/
}
#include "mprmime.h"
#include "memstrem.h"
#define MESSAGE_START_SIZE 500
#endif /*MOZ_ENDER_MIME*/

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_pEncoding(NULL), m_pContentName(NULL),
	  m_pStreamOut(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.
#ifdef MOZ_ENDER_MIME
        char *tmp = WH_TempName(xpFileToPost, "nswebm");
#else
        char *tmp = WH_TempName(xpFileToPost, "nsmail");
#endif
        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 !defined(MOZ_ENDER_MIME) || defined(MOZ_MAIL_COMPOSE)
  if (subpart->m_state)
		returnVal = mime_write_message_body(subpart->m_state, (char *) buf, size);
#endif /* !MOZ_ENDER_MIME || MOZ_MAIL_COMPOSE */

    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))
	{
#if !defined(MOZ_ENDER_MIME) || defined(MOZ_MAIL_COMPOSE)
    // 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));
        }
#endif /* !MOZ_ENDER_MIME || MOZ_MAIL_COMPOSE */
	}

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

    // 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)
    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)
{
}

#ifdef MOZ_ENDER_MIME
/*
----------------------------------------------------------------------
MSG_MimeRelatedStreamSaver
----------------------------------------------------------------------
*/

// Constructor
MSG_MimeRelatedStreamSaver::MSG_MimeRelatedStreamSaver(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,
                                                       char **ppStreamOut)
                                                       : MSG_MimeRelatedSaver(pane,context,fields,digest_p,deliver_mode,
                                                       body,body_length,attachedFiles,cb,ppOriginalRootURL),m_pFilename(NULL),m_ppStreamOut(ppStreamOut)

{
    
}



MSG_MimeRelatedStreamSaver::~MSG_MimeRelatedStreamSaver()
{
    XP_FREEIF(m_pFilename);
}



extern "C" char *mime_make_separator(const char *prefix);

extern "C" int mimer_outputfile_func(const char *p_buffer,int32 p_size,void *closure)
{
    XP_File t_file;
    t_file = (XP_File)closure;
    return XP_FileWrite(p_buffer,p_size,t_file);
}



extern "C" int mimer_outputmem_func(const char *p_buffer,int32 p_size,void *closure)
{
    memstream *t_buf = (memstream *)closure;
    (*t_buf).write(p_buffer,p_size);
    return p_size;
}



void
MSG_MimeRelatedStreamSaver::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 && m_pPart)
    {
        //make new message!
        GenericMimeRelatedData *t_data;
        memstream *t_buf= NULL;
        XP_File t_file(NULL);
        if (!m_ppStreamOut)
        {
            m_pFilename = WH_TempName (xpFileToPost, "nswebm");
            if (m_pFilename)
            {
                t_file = XP_FileOpen(m_pFilename,xpTemporary,XP_FILE_WRITE);
                t_data = GenericMime_Init(mime_make_separator(""),mimer_outputfile_func,t_file);
            }
        }
        else
        {
            t_buf = new memstream(MESSAGE_START_SIZE);//arbitrary starting size
            t_data = GenericMime_Init(mime_make_separator(""),mimer_outputmem_func,t_buf);
        }
        int32 i;
        for ( i=0 ;i< m_pPart->GetNumChildren();i++)
        {
            MSG_MimeRelatedSubpart *t_part;
            t_part = (MSG_MimeRelatedSubpart *)m_pPart->GetChild(i);
            if (t_part->GetType() && !XP_STRCMP(t_part->GetType(),"text/html"))
            {
                GenericMime_AddTextFile(t_data,XP_STRDUP(t_part->GetFilename()),t_part->GetCSID());
            }
            else /*base64*/
            {
                AttachmentFields *t_fields = AttachmentFields_Init(XP_STRDUP(t_part->GetFilename()),XP_STRDUP(t_part->m_pOriginalURL),
                    XP_STRDUP(t_part->GetType()),XP_STRDUP(t_part->m_pContentID));
                if (t_fields && m_pContext)
                {
                    /*is this a SAFE file???? lets see...*/
                    void* t_id = EDT_GetIdFromContext( m_pContext );
                    if (t_id)
                    {
                        if (EDT_URLOnSafeList(t_id,t_part->m_pOriginalURL))
                            GenericMime_AddBase64File(t_data,t_fields);
                    }
                }
            }
        }
        GenericMime_Begin(t_data);
        GenericMime_Destroy(t_data);
        if (t_file)
            XP_FileClose(t_file);
        if (t_buf)
        {
            (*t_buf).write("",1);
            *m_ppStreamOut = t_buf->str();
            delete t_buf;
        }
        for (i=0;i< m_pPart->GetNumChildren();i++)
        {
            MSG_MimeRelatedSubpart *t_part;
            t_part = (MSG_MimeRelatedSubpart *)m_pPart->GetChild(i);
            if (t_part->GetType() && XP_STRCMP(t_part->GetType(),"text/html")) /*not text*/
            {
                XP_FileRemove(t_part->GetFilename(), xpFileToPost);
            }
        }
        // Call our UrlExit routine to perform cleanup.
        MSG_MimeRelatedSaver::UrlExit(m_pContext, this, 0, NULL);
    }
    else
    {
        // delete the contained part since we failed
        delete m_pPart;
        m_pPart = NULL;
        // Call our UrlExit routine to perform cleanup.
        MSG_MimeRelatedSaver::UrlExit(m_pContext, this, MK_INTERRUPTED, NULL);
    }
}
#endif //MOZ_ENDER_MIME



/*
----------------------------------------------------------------------
MSG_MimeRelatedSaver
----------------------------------------------------------------------
*/

#if defined(MOZ_ENDER_MIME) && !defined(MOZ_MAIL_COMPOSE)

extern char * msg_generate_message_id(void);

char *
msg_generate_message_id (void)
{
    time_t now = XP_TIME();
    uint32 salt = 0;
    const char *host = 0;
    const char *from = FE_UsersMailAddress ();

    RNG_GenerateGlobalRandomBytes((void *) &salt, sizeof(salt));

    if (from)
    {
        host = XP_STRCHR (from, '@');
        if (host)
        {
            const char *s;
            for (s = ++host; *s; s++)
                if (!XP_IS_ALPHA(*s) && !XP_IS_DIGIT(*s) &&
                    *s != '-' && *s != '_' && *s != '.')
                {
                    host = 0;
                    break;
                }
        }
    }

    if (! host)
    /* If we couldn't find a valid host name to use, we can't generate a
    valid message ID, so bail, and let NNTP and SMTP generate them. */
    return 0;

    return PR_smprintf("<%lX.%lX@%s>",
        (unsigned long) now, (unsigned long) salt, host);
}
#endif //MOZ_ENDER_MIME && !MOZ_MAIL_COMPOSE



// 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_pSourceBaseURL(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)

{
  // 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.
#ifdef MOZ_ENDER_MIME
        m_rootFilename = WH_TempName(xpFileToPost,"nswebm");
#else
        m_rootFilename = WH_TempName(xpFileToPost,"nsmail");
#endif
        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;(i<m_pPart->GetNumChildren()) && (!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 )
{
#if !defined(MOZ_ENDER_MIME) || defined(MOZ_MAIL_COMPOSE)
 	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);
	}
#endif /* !MOZ_ENDER_MIME || MOZ_MAIL_COMPOSE */
}

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

extern "C" void *
MSG_CreateMimeRelatedStreamSaver(MWContext *context,
								 char **origRootURL, char **streamOut)
{
	return ((void *)new MSG_MimeRelatedStreamSaver(
					NULL, context, NULL, FALSE, MSG_DeliverNow,
					NULL, 0, NULL, NULL, origRootURL, streamOut));
}

//#endif
