gecko-dev/lib/mailto/mprdecod.cpp

778 lines
22 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.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/*mjudge*/
#include "xp_core.h"
#include "xp_file.h"
#include "xp_mem.h"
#include "xpassert.h"
#include "xp_mcom.h"
#include "mprdecod.h"
#include "memstrem.h"
#include "mimeenc.h"
#define CONTENT_TYPE_MPR_MIMEREL "multipart/related;"
#define CONTENT_TYPE_TXT_MIMEREL "text/html;"
#define CONENT_CHARSEL_MIMEREL "charset="
#define CONTENT_TYPE_MIMEREL "Content-Type:"
#define CONTENT_ID_MIMEREL "Content-ID:"
#define CONTENT_TRANS_MIMEREL "Content-Transfer-Encoding:"
#define BASE64_MIMEREL "base64"
#define UUENCODE_MIMEREL "uuencode"
#define BIT7_MIMEREL "7bit"
#define BOUNDARYSTR_MIMEREL "boundary"
#define XMOZSTATUS_MIMEREL "X-Mozilla-Status: 8001"
#define CONTENT_DISPOSITION_MIMEREL "Content-Disposition:"
#define IMAGE_GIF_MIMEREL "image/gif"
#define IMAGE_JPG_MIMEREL "image/jpeg"
#define IMAGE_PNG_MIMEREL "image/png"
#define FILENAME_MIMEREL "filename"
/*
SimpleMultiPart
SimpleMultiPart
SimpleMultiPart
*/
SimpleMultiPart::SimpleMultiPart()
{
m_pFileName = NULL;
m_pContentId = NULL;
m_pCharset = NULL;
m_pEncoding = NULL;
m_iContentType = UNKNOWN_TYPE;
m_pUrlFileName = NULL;
}
SimpleMultiPart::~SimpleMultiPart()
{
XP_FREEIF(m_pFileName);
XP_FREEIF(m_pContentId);
XP_FREEIF(m_pCharset);
XP_FREEIF(m_pEncoding);
}
void
SimpleMultiPart::setFileName(const char *p_pFileName)
{
XP_FREEIF(m_pFileName);
m_pFileName = XP_STRDUP(p_pFileName);
}
const char *
SimpleMultiPart::getUrlFileName()
{
XP_FREEIF(m_pUrlFileName);
m_pUrlFileName = XP_PlatformFileToURL(m_pFileName);
return m_pUrlFileName;
}
void
SimpleMultiPart::setCharset(const char *p_pCharset)
{
XP_FREEIF(m_pCharset);
m_pCharset = XP_STRDUP(p_pCharset);
}
void
SimpleMultiPart::setContentId(const char *p_pContentId)
{
XP_FREEIF(m_pContentId);
m_pContentId = XP_STRDUP(p_pContentId);
}
void
SimpleMultiPart::setEncoding(const char *p_pEncoding)
{
XP_FREEIF(m_pEncoding);
m_pEncoding = XP_STRDUP(p_pEncoding);
}
/*
SimpleMultipartRelatedMimeDecoder
SimpleMultipartRelatedMimeDecoder
SimpleMultipartRelatedMimeDecoder
*/
SimpleMultipartRelatedMimeDecoder::SimpleMultipartRelatedMimeDecoder(XP_File p_stream)
{
m_pBoundaryName = NULL;
m_ppParts = NULL;
m_iNumParts = 0;
m_pFileStream = p_stream;
m_pMemStream = NULL;
m_iMemStreamLoc = 0;
m_iMemStreamLen = 0;
}
SimpleMultipartRelatedMimeDecoder::SimpleMultipartRelatedMimeDecoder(const char *p_stream, int32 p_len)
{
m_pBoundaryName = NULL;
m_ppParts = NULL;
m_iNumParts = 0;
m_pFileStream = NULL;
m_pMemStream = p_stream;
m_iMemStreamLoc = 0;
m_iMemStreamLen = p_len;
m_pHeaderFileName = NULL;
}
SimpleMultipartRelatedMimeDecoder::~SimpleMultipartRelatedMimeDecoder()
{
if (m_pHeaderFileName)
XP_FileRemove(m_pHeaderFileName,xpFileToPost);
clear_all();
}
void
SimpleMultipartRelatedMimeDecoder::clear_all()
{
XP_FREEIF(m_pBoundaryName);
XP_FREEIF(m_pHeaderFileName);
for (int i= 0; i< m_iNumParts; i++)
{
delete m_ppParts[i];
}
XP_FREEIF(m_ppParts);
m_ppParts = NULL;
m_iNumParts = 0;
}
SimpleMultiPart *
SimpleMultipartRelatedMimeDecoder::getPart(int32 p_index)
{
XP_ASSERT(p_index < m_iNumParts);
if (p_index < m_iNumParts)
return m_ppParts[p_index];
return NULL;
}
XP_Bool
SimpleMultipartRelatedMimeDecoder::begin()
{
char t_tempbuffer[MAX_BUFFER_LEN + 1];//no parameter allowed longer than MAX_BUFFER_LEN. +1 for the NULL termination
int t_index = 0; //used to index t_tempbuffer
XP_FREEIF(m_pHeaderFileName);
m_pHeaderFileName = WH_TempName (xpFileToPost, m_pPrefix);
XP_File t_pHeaderFile;
if (m_pHeaderFileName)
t_pHeaderFile = XP_FileOpen(m_pHeaderFileName, xpFileToPost, XP_FILE_WRITE);//write text only
else
return FALSE;
if (!m_pHeaderFileName || !t_pHeaderFile)
return FALSE;
// find first string. take all data and throw it into the header file if necessary
XP_Bool t_foundstr = FALSE;
t_foundstr = searchForString(CONTENT_TYPE_MIMEREL, t_pHeaderFile);
XP_FileClose(t_pHeaderFile);
if (!t_foundstr)
return TRUE; /*just data, no multipartrelated*/
t_foundstr = searchForString(CONTENT_TYPE_MPR_MIMEREL, NULL);
if (!t_foundstr)
return FALSE;
//find boundary marker.
t_foundstr = searchForString(BOUNDARYSTR_MIMEREL, NULL);
if (!t_foundstr)
return FALSE;
if (!eatWhite())
return FALSE;
if (getCh() != '=')
return FALSE;
if (!eatWhite())
return FALSE;
if (getCh() != '\"')
return FALSE;
char t_char;
while( (t_char = getCh()) )
{
if (t_index >= MAX_BUFFER_LEN || isspace(t_char) || t_char == '\"')
break; //done
t_tempbuffer[t_index++] = t_char;
}
if (t_index >= MAX_BUFFER_LEN)
return FALSE;
t_tempbuffer[t_index] = 0; //nul terminate the string
m_pBoundaryName = XP_STRDUP(t_tempbuffer);
if (!isspace(getCh()))
return FALSE;
//fun part
XP_Bool t_done = !searchForString(m_pBoundaryName, NULL);
//found 1st boundary. keep going until we reach the last
while(!t_done)
{
if ( getCh() == '-')
if (getCh() == '-')
{
t_done = TRUE; //reached the end
break; //just break here. if we need to check t_done someother time thats ok
}
else
backUp();
backUp();
if (!getNextString(t_tempbuffer, MAX_BUFFER_LEN))
return FALSE;
if (XP_STRCASECMP(CONTENT_TYPE_MIMEREL,t_tempbuffer))
return FALSE;
if (!eatWhite())
return FALSE;
if (!getNextString(t_tempbuffer, MAX_BUFFER_LEN))
return FALSE;
if (!XP_STRCASECMP(CONTENT_TYPE_TXT_MIMEREL,t_tempbuffer))
{//text/html
if (!readTextHtml(t_tempbuffer))
return FALSE;
}
else
{//image ect
if (!readEncoded(t_tempbuffer))
return FALSE;
}
}
return TRUE;
}
//why waste space on a temp buffer?
XP_Bool
SimpleMultipartRelatedMimeDecoder::readTextHtml(char p_buffer[MAX_BUFFER_LEN])
{
SimpleMultiPart *t_part = new SimpleMultiPart();
if (!t_part)
return FALSE;
t_part->setType(SimpleMultiPart::TEXTHTML);
if (!getNextString(p_buffer, MAX_BUFFER_LEN))
{ delete t_part; return FALSE; }
if (!XP_STRCASECMP(CONENT_CHARSEL_MIMEREL,p_buffer))
{//we have a charset!
if (!getNextString(p_buffer, MAX_BUFFER_LEN))
{ delete t_part; return FALSE; }
t_part->setCharset(p_buffer);
if (!getNextString(p_buffer, MAX_BUFFER_LEN))
{ delete t_part; return FALSE; }
}
if (XP_STRCASECMP(CONTENT_TRANS_MIMEREL,p_buffer))
if (!searchForString(CONTENT_TRANS_MIMEREL, NULL))
{ delete t_part; return FALSE; }
if (!getNextString(p_buffer, MAX_BUFFER_LEN))
{ delete t_part; return FALSE; }
t_part->setEncoding(p_buffer);
/*
now it gets interesting. we need a new file to be created. we will search
for the boundary string. until we find it, we will throw each char into the file.
perhaps this isnt the fastest.
*/
eatWhite();
t_part->setFileName(WH_TempName (xpFileToPost, m_pPrefix));
if (!t_part->getFileName())
{ delete t_part; return FALSE; }
XP_File t_file;
//write text only
t_file = XP_FileOpen(t_part->getFileName(), xpFileToPost, XP_FILE_WRITE);
if (!t_file)
{ delete t_part; return FALSE; }
if (!searchForString(m_pBoundaryName, t_file))//writes to file
{ delete t_part; return FALSE; }
XP_FileClose(t_file);
m_iNumParts++;
if (m_ppParts)
m_ppParts = (SimpleMultiPart **)XP_REALLOC(m_ppParts, m_iNumParts * sizeof (SimpleMultiPart *));
else
m_ppParts = (SimpleMultiPart **)XP_ALLOC(m_iNumParts * sizeof (SimpleMultiPart *));
m_ppParts[m_iNumParts -1] = t_part;
return TRUE;
}
//why waste space on a temp buffer?
XP_Bool
SimpleMultipartRelatedMimeDecoder::readEncoded(char p_buffer[MAX_BUFFER_LEN])
{
SimpleMultiPart *t_part = new SimpleMultiPart();
if (!t_part)
return FALSE;
if (!XP_STRCASECMP(IMAGE_GIF_MIMEREL,p_buffer))
t_part->setType(SimpleMultiPart::IMAGEGIF);
else if (!XP_STRCASECMP(IMAGE_JPG_MIMEREL,p_buffer))
t_part->setType(SimpleMultiPart::IMAGEJPG);
else if (!XP_STRCASECMP(IMAGE_PNG_MIMEREL,p_buffer))
t_part->setType(SimpleMultiPart::IMAGEPNG);
else
{ delete t_part; return FALSE; }
/*
we must find a content id or this part is useless. and all bets are off.
*/
if (!getNextString(p_buffer, MAX_BUFFER_LEN))
{ delete t_part; return FALSE; }
if (XP_STRCASECMP(CONTENT_ID_MIMEREL,p_buffer))
{ delete t_part; return FALSE; }
eatWhite();
if (getCh() != '<')
{ delete t_part; return FALSE; }
if (!getNextString(p_buffer, MAX_BUFFER_LEN))
{ delete t_part; return FALSE; }
if (getCh() != '>')
{ delete t_part; return FALSE; }
//we have a content-id
t_part->setContentId(p_buffer);
if (!getNextString(p_buffer, MAX_BUFFER_LEN))
{ delete t_part; return FALSE; }
if (XP_STRCASECMP(CONTENT_TRANS_MIMEREL,p_buffer))
{ delete t_part; return FALSE; }
if (!getNextString(p_buffer, MAX_BUFFER_LEN))
{ delete t_part; return FALSE; }
t_part->setEncoding(p_buffer);
if (!getNextString(p_buffer, MAX_BUFFER_LEN))
{ delete t_part; return FALSE; }
if (XP_STRCASECMP(CONTENT_DISPOSITION_MIMEREL,p_buffer))
{ delete t_part; return FALSE; }
if (!searchForString(FILENAME_MIMEREL, NULL))
{ delete t_part; return FALSE; }
eatWhite();
if (getCh()!='=')
{ delete t_part; return FALSE; }
eatWhite();
char t_char; //eat the disposition filename
while ((t_char = getCh()) && !isspace(t_char));
backUp();
/*we didnt care about the disposition. we need to make a completely unknown file for
security purposes*/
/*
now it gets interesting. we need a new file to be created. we will search
for the boundary string. until we find it, we will throw each char into the file.
perhaps this isnt the fastest.
*/
eatWhite();
t_part->setFileName(WH_TempName (xpFileToPost, m_pPrefix));
if (!t_part->getFileName())
{ delete t_part; return FALSE; }
XP_File t_file;
//write text only
t_file = XP_FileOpen(t_part->getFileName(), xpFileToPost, XP_FILE_WRITE);
if (!t_file)
{ delete t_part; return FALSE; }
if (!searchForString(m_pBoundaryName, t_file))//writes to file
{
XP_FileClose(t_file);
delete t_part;
return FALSE;
}
XP_FileClose(t_file);
m_iNumParts++;
if (m_ppParts)
m_ppParts = (SimpleMultiPart **)XP_REALLOC(m_ppParts, m_iNumParts * sizeof (SimpleMultiPart *));
else
m_ppParts = (SimpleMultiPart **)XP_ALLOC(m_iNumParts * sizeof (SimpleMultiPart *));
m_ppParts[m_iNumParts -1] = t_part;
return TRUE;
}
XP_Bool
SimpleMultipartRelatedMimeDecoder::getNextString(char *p_buffer, int32 p_maxbuflen)
{
if (!eatWhite())
return FALSE;
int t_index = 0;
char t_char = 0;
while( t_index < p_maxbuflen )
{
t_char = getCh();
if (!t_char || t_char ==':' || t_char ==';' || t_char == '=' )
{
p_buffer[t_index++] = t_char;
if (t_char)
{
if (t_index < p_maxbuflen)
p_buffer[t_index] = 0;
else
return FALSE;//no room for null termination
}
return TRUE;
}
if (isspace(t_char) || t_char == '\"' || t_char =='<' || t_char == '>')
{
backUp();
p_buffer[t_index] = 0;
return TRUE;
}
p_buffer[t_index++] = t_char;
}
return FALSE; //ran out of room
}
XP_Bool
SimpleMultipartRelatedMimeDecoder::searchForString(const char *t_searchstring, XP_File p_output)
{
char t_char;
int32 t_strlen = XP_STRLEN(t_searchstring);
int32 t_stringindex = 0;
while ((t_char = getCh()) && t_stringindex < t_strlen)
{
if (t_searchstring[t_stringindex] == t_char)
{
t_stringindex++;
}
else
{
if (t_stringindex && p_output)
XP_FileWrite(t_searchstring,t_stringindex,p_output);
t_stringindex = 0;
if (p_output)
XP_FileWrite(&t_char,1,p_output);
}
}
if (t_char)
{
backUp();
return TRUE;
}
return FALSE;
}
char
SimpleMultipartRelatedMimeDecoder::getCh()
{
if (m_pMemStream)
{
if (m_iMemStreamLoc < m_iMemStreamLen)
return m_pMemStream[m_iMemStreamLoc++];
else
{
m_iMemStreamLoc--;
return 0;
}
}
else if (m_pFileStream)
{
char t_char[2];
if (1 == XP_FileRead(t_char,1,m_pFileStream))
return t_char[0];
else
return 0;
}
else
{
XP_ASSERT(FALSE);
return 0;
}
}
void
SimpleMultipartRelatedMimeDecoder::backUp()
{
if (m_pMemStream)
{
m_iMemStreamLoc--;
}
else if (m_pFileStream)
{
XP_FileSeek(m_pFileStream, -1, SEEK_CUR);
}
else
{
XP_ASSERT(FALSE);
}
}
XP_Bool
SimpleMultipartRelatedMimeDecoder::eatWhite()
{
char t_char;
while( (t_char = getCh()) && isspace( t_char ) );
if (t_char)
{
backUp();
return TRUE;
}
return FALSE;
}
int32
SimpleMultipartRelatedMimeDecoder::lookUpByPartId(const char *p_id)
{
for (int i = 0;i < getNumberOfParts(); i++ )
{
SimpleMultiPart *t_part = getPart(i);
if (t_part->getContentId())
if (! XP_STRNCMP(t_part->getContentId(), p_id, XP_STRLEN(t_part->getContentId())))
{
return i;
}
}
return -1;
}
/*
OTHER API CALLS
*/
#define CHUNK_SIZE 255
char *ReadBufferFromFile(const char *p_pFileName)
{
memstream t_stream(CHUNK_SIZE,CHUNK_SIZE);
XP_File t_inputfile;
char t_readbuffer[CHUNK_SIZE];
if( (t_inputfile = XP_FileOpen(p_pFileName,xpFileToPost,XP_FILE_READ)) != NULL )
{
/* Attempt to read in READBUFLEN characters */
while (!feof( t_inputfile ))
{
int numread = XP_FileRead( t_readbuffer, CHUNK_SIZE -1, t_inputfile );
if (ferror(t_inputfile))
{
XP_ASSERT(FALSE);
break;
}
t_stream.write(t_readbuffer,numread);
}
XP_FileClose( t_inputfile );
}
t_stream.write("",1); /*null terminate*/
return t_stream.str();
}
extern "C" int mimer_outputfile_func(const char *p_buffer,int32 p_size,void *closure);
XP_Bool DecodeSimpleMime(SimpleMultipartRelatedMimeDecoder &p_rDecoder, const char *p_pPrefix)
{
char *t_pNewFileName = NULL;
for (int i = 0 ; i < p_rDecoder.getNumberOfParts(); i++)
{
SimpleMultiPart *t_part = p_rDecoder.getPart(i);
XP_File t_file;
XP_FREEIF(t_pNewFileName);
t_pNewFileName = WH_TempName (xpFileToPost, p_pPrefix);
if (!t_pNewFileName)
return FALSE;
char *t_suffix;
t_suffix = XP_STRRCHR(t_pNewFileName,'.'); //searching for suffix .XXX
if (t_suffix && !XP_STRNCASECMP(t_suffix,".TMP ",4))
{
if (t_part->getType() == SimpleMultiPart::IMAGEGIF)
XP_STRNCPY_SAFE(t_suffix,".gif",5);
if (t_part->getType() == SimpleMultiPart::IMAGEJPG)
XP_STRNCPY_SAFE(t_suffix,".jpg",5);
if (t_part->getType() == SimpleMultiPart::IMAGEPNG)
XP_STRNCPY_SAFE(t_suffix,".png",5);
}
MimeDecoderData *t_data;
if ( !XP_STRCMP(t_part->getEncoding() , BASE64_MIMEREL))
{
t_file = XP_FileOpen(t_pNewFileName, xpFileToPost, XP_FILE_WRITE_BIN);//write binary only
if (!t_file)
{
XP_FREEIF(t_pNewFileName);
return FALSE;
}
t_data = MimeB64DecoderInit(mimer_outputfile_func, t_file);
}
else if ( !XP_STRCMP(t_part->getEncoding() , UUENCODE_MIMEREL))
{
t_file = XP_FileOpen(t_pNewFileName, xpFileToPost, XP_FILE_WRITE_BIN);//write binary only
if (!t_file)
{
XP_FREEIF(t_pNewFileName);
return FALSE;
}
t_data = MimeUUDecoderInit(mimer_outputfile_func, t_file);
}
else if ( !XP_STRCMP(t_part->getEncoding() , BIT7_MIMEREL))
{
continue; /*dont do anything to this one*/
}
else
continue;
XP_File t_inputfile;
char t_readbuffer[CHUNK_SIZE];
if( (t_inputfile = XP_FileOpen(t_part->getFileName(),xpFileToPost,XP_FILE_READ)) != NULL )
{
/* Attempt to read in READBUFLEN characters */
while (!feof( t_inputfile ))
{
int numread = XP_FileRead( t_readbuffer, CHUNK_SIZE -1, t_inputfile );
if (ferror(t_inputfile))
{
XP_ASSERT(FALSE);
break;
}
MimeDecoderWrite(t_data,t_readbuffer,numread);
}
XP_FileClose( t_inputfile );
}
XP_FileClose( t_file );
XP_FileRemove(t_part->getFileName(),xpFileToPost);
t_part->setFileName(t_pNewFileName);
}
return TRUE;
}
char *ParseBuffer(char *p_pOldBuffer,SimpleMultipartRelatedMimeDecoder &p_rDecoder)
{
if (!p_pOldBuffer)
return NULL;
/*find occurrances if image SRC urls*/
int t_index = 0;
memstream t_stream(CHUNK_SIZE,CHUNK_SIZE);
memstream t_urlstream;
while (p_pOldBuffer[t_index])
{
char t_char = p_pOldBuffer[t_index++];
if (t_char != '<')
continue;
while(p_pOldBuffer[t_index] && isspace(t_char))
t_char = p_pOldBuffer[t_index++];
if (!p_pOldBuffer[t_index] || p_pOldBuffer[t_index] != 'I')
continue;
if (XP_STRNCASECMP(p_pOldBuffer + t_index,"IMG ",4))
continue;
t_index+=4;
XP_Bool t_found = TRUE;
while( t_found && p_pOldBuffer[t_index])
{
if (t_char == '>')
{
t_found = FALSE;
break;
}
if (!p_pOldBuffer[t_index] || p_pOldBuffer[t_index] != 'S')
{
t_index++;
continue;
}
if (XP_STRNCASECMP(p_pOldBuffer + t_index,"SRC",3))
{
t_index++;
continue;
}
t_index+=3;
while(p_pOldBuffer[t_index] && isspace(t_char))
t_char = p_pOldBuffer[t_index++];
if (!p_pOldBuffer[t_index] || p_pOldBuffer[t_index++] != '=')
continue;
while(p_pOldBuffer[t_index] && isspace(t_char))
t_char = p_pOldBuffer[t_index++];
if (!p_pOldBuffer[t_index] || p_pOldBuffer[t_index++] != '\"')
continue;
while(p_pOldBuffer[t_index] && isspace(t_char))
t_char = p_pOldBuffer[t_index++];
int t_marker = t_index;
if (XP_STRNCASECMP(p_pOldBuffer + t_index, "cid:", 4))
{
t_found = FALSE;
continue;
}
t_index += 4;
while(p_pOldBuffer[t_index] && p_pOldBuffer[t_index]!='\"')
t_urlstream.write(p_pOldBuffer + t_index++,1);
if (!p_pOldBuffer[t_index])
{
t_found = FALSE;
continue;
}
//lookup t_urlstream for new filename
int t_partidx = p_rDecoder.lookUpByPartId(t_urlstream.str());
t_urlstream.clear();
if (t_partidx < 0)
continue;
SimpleMultiPart *t_part = p_rDecoder.getPart(t_partidx);
if (!t_part)
continue;
t_stream.write(p_pOldBuffer,t_marker);
t_stream.write(t_part->getUrlFileName(),XP_STRLEN(t_part->getUrlFileName()));//gets new filename MUST MAKE THIS A URL
p_pOldBuffer += t_index;
t_index = 0;
break;
}
}
t_stream.write(p_pOldBuffer,t_index);
t_stream.write("",1); /*null terminate*/
return t_stream.str();//does not free memory unless unfrozen.
}