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