gecko-dev/lib/libmsg/mhtmlstm.cpp
1998-06-22 22:39:40 +00:00

886 lines
21 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/* 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;(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 )
{
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