--- /dev/null
+//////////////////////////////////////////////////////////////////////\r
+//\r
+// MIME message encoding/decoding\r
+//\r
+// Jeff Lee\r
+// Dec 11, 2000\r
+//\r
+//////////////////////////////////////////////////////////////////////\r
+//#include "stdafx.h"\r
+#include "../../../include/nntp/mime/MimeCode.h"\r
+#include "../../../include/nntp/mime/MimeChar.h"\r
+#include "../../../include/nntp/mime/Mime.h"\r
+#include <stdlib.h>\r
+#include <time.h>\r
+\r
+#ifdef _DEBUG\r
+#undef THIS_FILE\r
+static char THIS_FILE[]=__FILE__;\r
+#define new DEBUG_NEW\r
+#endif\r
+\r
+// search for a character in the current line (before CRLF)\r
+static const char* LineFind(const char* pszString, int ch)\r
+{\r
+ ASSERT(pszString != NULL);\r
+ while (*pszString != 0 && *pszString != ch && *pszString != '\r' && *pszString != '\n')\r
+ pszString++;\r
+ return *pszString == ch ? pszString : NULL;\r
+}\r
+\r
+// search for string2 in string1 (strstr)\r
+static const char* FindString(const char* pszStr1, const char* pszStr2, const char* pszEnd)\r
+{\r
+ pszEnd -= ::strlen(pszStr2);\r
+ const char *s1, *s2;\r
+ while (pszStr1 <= pszEnd)\r
+ {\r
+ s1 = pszStr1;\r
+ s2 = pszStr2;\r
+ while (*s1 == *s2 && *s2)\r
+ s1++, s2++;\r
+ if (!*s2)\r
+ return pszStr1;\r
+ pszStr1++;\r
+ }\r
+ return NULL;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CMimeField class - Represents a field of a MIME body part header\r
+//////////////////////////////////////////////////////////////////////\r
+\r
+void CMimeField::GetValue(string& strValue) const\r
+{\r
+ string::size_type nEnd = m_strValue.find(';');\r
+ if (nEnd != string::npos)\r
+ {\r
+ while (nEnd > 0 && CMimeChar::IsSpace((unsigned char)m_strValue[nEnd-1]))\r
+ nEnd--;\r
+ strValue.assign(m_strValue.c_str(), nEnd);\r
+ }\r
+ else\r
+ strValue = m_strValue;\r
+}\r
+\r
+// set a parameter (attribute=value) of the field\r
+void CMimeField::SetParameter(const char* pszAttr, const char* pszValue)\r
+{\r
+ int nSize = pszValue ? (int)::strlen(pszValue) : 0;\r
+ string strValue;\r
+ strValue.reserve(nSize+3);\r
+ if (!pszValue || *pszValue != '"')\r
+ strValue = "\"";\r
+ if (pszValue != NULL)\r
+ strValue += pszValue;\r
+ if (nSize < 2 || pszValue[nSize-1] != '"')\r
+ strValue += "\"";\r
+\r
+ int nPos;\r
+ if (!FindParameter(pszAttr, nPos, nSize)) // add new parameter\r
+ {\r
+ m_strValue.reserve(m_strValue.size() + ::strlen(pszAttr) + strValue.size() + 5);\r
+ //if (CMimeEnvironment::AutoFolding())\r
+ // m_strValue += ";\r\n\t";\r
+ //else\r
+ // m_strValue += "; ";\r
+ m_strValue += "; ";\r
+ m_strValue += pszAttr;\r
+ m_strValue += '=';\r
+ m_strValue += strValue;\r
+ }\r
+ else // update existing parameter\r
+ m_strValue.replace(nPos, nSize, strValue);\r
+}\r
+\r
+// get the value of a parameter\r
+bool CMimeField::GetParameter(const char* pszAttr, string& strValue) const\r
+{\r
+ int nPos, nSize;\r
+ if (!FindParameter(pszAttr, nPos, nSize))\r
+ {\r
+ strValue.clear();\r
+ return false;\r
+ }\r
+\r
+ if (m_strValue[nPos] == '"')\r
+ {\r
+ nPos++;\r
+ nSize--;\r
+ if (nSize > 0 && m_strValue[nPos+nSize-1] == '"')\r
+ nSize--;\r
+ }\r
+ strValue.assign(m_strValue.data()+nPos, nSize);\r
+ return true;\r
+}\r
+\r
+int CMimeField::GetLength() const\r
+{\r
+ int nLength = (int) m_strName.size() + 4;\r
+ CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());\r
+ pCoder->SetCharset(m_strCharset.c_str());\r
+ pCoder->SetInput(m_strValue.c_str(), (int)m_strValue.size(), true);\r
+ nLength += pCoder->GetOutputLength();\r
+ delete pCoder;\r
+ return nLength;\r
+}\r
+\r
+// store a field to string buffer\r
+int CMimeField::Store(char* pszData, int nMaxSize) const\r
+{\r
+ ASSERT(pszData != NULL);\r
+ int nMinSize = (int)m_strName.size() + 4;\r
+ if (nMaxSize < nMinSize)\r
+ return 0;\r
+ ::strcpy(pszData, m_strName.c_str());\r
+ pszData += m_strName.size();\r
+ *pszData++ = ':';\r
+ *pszData++ = ' ';\r
+\r
+ CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());\r
+ pCoder->SetCharset(m_strCharset.c_str());\r
+ pCoder->SetInput(m_strValue.c_str(), (int)m_strValue.size(), true);\r
+ int nEncoded = pCoder->GetOutput((unsigned char*) pszData, nMaxSize-nMinSize);\r
+ delete pCoder;\r
+ pszData += nEncoded;\r
+\r
+ *pszData++ = '\r';\r
+ *pszData++ = '\n';\r
+ return nMinSize + nEncoded;\r
+}\r
+\r
+// load a field from string buffer\r
+int CMimeField::Load(const char* pszData, int nDataSize)\r
+{\r
+ Clear();\r
+ ASSERT(pszData != NULL);\r
+ const char *pszEnd, *pszStart = pszData;\r
+ // find the next field (e.g. "\r\nContent...")\r
+ while (CMimeChar::IsSpace((unsigned char)*pszStart))\r
+ {\r
+ if (*pszStart == '\r') // end of header ?\r
+ return 0;\r
+ pszStart = ::FindString(pszStart, "\r\n", pszData+nDataSize);\r
+ if (!pszStart)\r
+ return 0;\r
+ pszStart += 2;\r
+ }\r
+\r
+ // get the field name\r
+ pszEnd = ::LineFind(pszStart, ':');\r
+ if (pszEnd != NULL) // if colon not found, Name would be empty\r
+ {\r
+ m_strName.assign(pszStart, (pszEnd-pszStart));\r
+ pszStart = pszEnd + 1;\r
+ }\r
+\r
+ // find the end of the field\r
+ while (*pszStart == ' ' || *pszStart == '\t')\r
+ pszStart++;\r
+ pszEnd = pszStart;\r
+ do\r
+ {\r
+ pszEnd = ::FindString(pszEnd, "\r\n", pszData+nDataSize);\r
+ if (!pszEnd)\r
+ return 0;\r
+ pszEnd += 2;\r
+ } while (*pszEnd == '\t' || *pszEnd == ' '); // linear-white-space\r
+\r
+ // decode and unfold the field value\r
+ CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());\r
+ pCoder->SetInput(pszStart, (int)(pszEnd-pszStart)-2, false);\r
+ m_strValue.resize(pCoder->GetOutputLength());\r
+ int nSize = pCoder->GetOutput((unsigned char*) m_strValue.c_str(), (int) m_strValue.capacity());\r
+ m_strValue.resize(nSize);\r
+ m_strCharset = pCoder->GetCharset();\r
+ delete pCoder;\r
+ return (int) (pszEnd - pszData);\r
+}\r
+\r
+bool CMimeField::FindParameter(const char* pszAttr, int& nPos, int& nSize) const\r
+{\r
+ ASSERT(pszAttr != NULL);\r
+ const char* pszParms = ::strchr(m_strValue.data(), ';');\r
+ int nAttrSize = (int)::strlen(pszAttr);\r
+ while (pszParms != NULL)\r
+ {\r
+ while (CMimeChar::IsSpace((unsigned char)*pszParms) || *pszParms == ';')\r
+ pszParms++;\r
+\r
+ const char* pszName = pszParms; // pszName -> attribute\r
+ pszParms = ::strchr(pszParms, '=');\r
+ if (!pszParms)\r
+ break;\r
+\r
+ pszParms++; // pszParams -> parameter value\r
+ //while (*pszParms == ' ' || *pszParms == '\t')\r
+ // pszParms++;\r
+\r
+ const char* pszParmEnd = NULL;\r
+ if (*pszParms == '"') // quoted string\r
+ pszParmEnd = ::strchr(pszParms+1, '"');\r
+ if (!pszParmEnd) // non quoted string\r
+ {\r
+ pszParmEnd = pszParms;\r
+ while (CMimeChar::IsToken(*pszParmEnd))\r
+ pszParmEnd++;\r
+ }\r
+ else pszParmEnd++; // pszParmEnd -> end of parameter value\r
+\r
+ if (!::memicmp(pszAttr, pszName, nAttrSize) &&\r
+ (CMimeChar::IsSpace((unsigned char)pszName[nAttrSize]) || pszName[nAttrSize] == '='))\r
+ {\r
+ nPos = (int)(pszParms - m_strValue.data());\r
+ nSize = (int)(pszParmEnd - pszParms);\r
+ return true;\r
+ }\r
+\r
+ pszParms = pszParmEnd;\r
+ }\r
+ return false;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CMimeHeader class - Represents the header of a MIME body part\r
+//////////////////////////////////////////////////////////////////////\r
+\r
+// Return the media type represented by Content-Type field (see RFC 2046)\r
+CMimeHeader::MediaType CMimeHeader::GetMediaType() const\r
+{\r
+ const char* pszType = GetContentType();\r
+ if (!pszType)\r
+ pszType = "text";\r
+\r
+ int nIndex = 0;\r
+ while (m_TypeTable[nIndex] != NULL &&\r
+ ::memicmp(pszType, m_TypeTable[nIndex], ::strlen(m_TypeTable[nIndex])) != 0)\r
+ nIndex++;\r
+ return (MediaType) nIndex;\r
+}\r
+\r
+// get the top-level media type\r
+string CMimeHeader::GetMainType() const\r
+{\r
+ string strType;\r
+ const char* pszType = GetContentType();\r
+ if (pszType != NULL)\r
+ {\r
+ const char* pszSlash = ::strchr(pszType, '/');\r
+ if (pszSlash != NULL)\r
+ strType.assign(pszType, pszSlash-pszType);\r
+ else\r
+ strType = pszType;\r
+ }\r
+ else\r
+ strType = "text";\r
+ return strType;\r
+}\r
+\r
+// get the subtype\r
+string CMimeHeader::GetSubType() const\r
+{\r
+ string strSubType;\r
+ const CMimeField *pfd = GetField(CMimeConst::ContentType());\r
+ if (pfd != NULL)\r
+ {\r
+ string strType;\r
+ pfd->GetValue(strType);\r
+ string::size_type nSlash = strType.find('/');\r
+ if (nSlash > 0)\r
+ strSubType = strType.substr(nSlash+1);\r
+ }\r
+ else\r
+ strSubType = "plain";\r
+ return strSubType;\r
+}\r
+\r
+// set the 'charset' parameter (for text) of Content-Type field\r
+void CMimeHeader::SetCharset(const char* pszCharset)\r
+{\r
+ CMimeField *pfd = GetField(CMimeConst::ContentType());\r
+ if (!pfd)\r
+ {\r
+ CMimeField fd;\r
+ fd.SetName(CMimeConst::ContentType());\r
+ fd.SetValue("text/plain");\r
+ fd.SetParameter(CMimeConst::Charset(), pszCharset);\r
+ m_listFields.push_back(fd);\r
+ }\r
+ else\r
+ pfd->SetParameter(CMimeConst::Charset(), pszCharset);\r
+}\r
+\r
+// set the 'name' parameter (for attachment) of Content-Type field\r
+void CMimeHeader::SetName(const char* pszName)\r
+{\r
+ CMimeField *pfd = GetField(CMimeConst::ContentType());\r
+ if (!pfd)\r
+ {\r
+ // get the appropriate media-type/subtype according to file extension\r
+ ASSERT(pszName != NULL);\r
+ string strType;\r
+ const char* pszType = "application/octet-stream";\r
+ const char* pszFileExt = ::strrchr(pszName, '.');\r
+ if (pszFileExt != NULL)\r
+ {\r
+ pszFileExt++;\r
+ int nIndex = 0;\r
+ while (m_TypeCvtTable[nIndex].nMediaType != MEDIA_UNKNOWN)\r
+ {\r
+ if (!::stricmp(pszFileExt, m_TypeCvtTable[nIndex].pszFileExt))\r
+ {\r
+ strType = m_TypeTable[m_TypeCvtTable[nIndex].nMediaType];\r
+ strType += '/';\r
+ strType += m_TypeCvtTable[nIndex].pszSubType;\r
+ pszType = strType.c_str();\r
+ break;\r
+ }\r
+ nIndex++;\r
+ }\r
+ }\r
+\r
+ CMimeField fd;\r
+ fd.SetName(CMimeConst::ContentType());\r
+ fd.SetValue(pszType);\r
+ fd.SetParameter(CMimeConst::Name(), pszName);\r
+ m_listFields.push_back(fd);\r
+ }\r
+ else\r
+ pfd->SetParameter(CMimeConst::Name(), pszName);\r
+}\r
+\r
+// set 'boundary' parameter (for multipart) of Content-Type field\r
+void CMimeHeader::SetBoundary(const char* pszBoundary/*=NULL*/)\r
+{\r
+ static int s_nPartNumber = 0;\r
+ char buf[80];\r
+ if (!pszBoundary) // generate a new boundary delimeter\r
+ {\r
+ ::srand(((unsigned)::time(NULL)) ^ (unsigned)this);\r
+ ::sprintf(buf, "__=_Part_Boundary_%03d_%06d.%06d", ++s_nPartNumber, rand(), rand());\r
+ if (s_nPartNumber >= 9)\r
+ s_nPartNumber = 0;\r
+ pszBoundary = buf;\r
+ }\r
+\r
+ CMimeField *pfd = GetField(CMimeConst::ContentType());\r
+ if (!pfd)\r
+ {\r
+ CMimeField fd;\r
+ fd.SetName(CMimeConst::ContentType());\r
+ fd.SetValue("multipart/mixed");\r
+ fd.SetParameter(CMimeConst::Boundary(), pszBoundary);\r
+ m_listFields.push_back(fd);\r
+ }\r
+ else\r
+ {\r
+ if (::memicmp(pfd->GetValue(), "multipart", 9) != 0)\r
+ pfd->SetValue("multipart/mixed");\r
+ pfd->SetParameter(CMimeConst::Boundary(), pszBoundary);\r
+ }\r
+}\r
+\r
+void CMimeHeader::Clear()\r
+{\r
+ m_listFields.clear();\r
+}\r
+\r
+// return the length needed to store this header to string buffer\r
+int CMimeHeader::GetLength() const\r
+{\r
+ int nLength = 0;\r
+ list<CMimeField>::const_iterator it;\r
+ for (it = m_listFields.begin(); it != m_listFields.end(); it++)\r
+ nLength += (*it).GetLength();\r
+ return nLength + 2; // a pair of CRLF indicate the end of header\r
+}\r
+\r
+// store the header to string buffer\r
+int CMimeHeader::Store(char* pszData, int nMaxSize) const\r
+{\r
+ ASSERT(pszData != NULL);\r
+ int nOutput = 0;\r
+ list<CMimeField>::const_iterator it;\r
+ for (it = m_listFields.begin(); it != m_listFields.end(); it++)\r
+ {\r
+ const CMimeField& fd = *it;\r
+ int nSize = fd.Store(pszData+nOutput, nMaxSize-nOutput);\r
+ if (nSize <= 0)\r
+ return nSize;\r
+ nOutput += nSize;\r
+ }\r
+\r
+ pszData[nOutput++] = '\r'; // add CRLF indicating the end of header\r
+ pszData[nOutput++] = '\n';\r
+ return nOutput;\r
+}\r
+\r
+// load a header from string buffer\r
+int CMimeHeader::Load(const char* pszData, int nDataSize)\r
+{\r
+ ASSERT(pszData != NULL);\r
+ int nInput = 0;\r
+ while (pszData[nInput] != 0 && pszData[nInput] != '\r')\r
+ {\r
+ CMimeField fd;\r
+ int nSize = fd.Load(pszData+nInput, nDataSize-nInput);\r
+ if (nSize <= 0)\r
+ return nSize;\r
+\r
+ nInput += nSize;\r
+ m_listFields.push_back(fd); // don't use SetField in case of same name fields\r
+ }\r
+\r
+ return nInput + 2; // skip the ending CRLF\r
+}\r
+\r
+list<CMimeField>::const_iterator CMimeHeader::FindField(const char* pszFieldName) const\r
+{\r
+ list<CMimeField>::const_iterator it;\r
+ for (it = m_listFields.begin(); it != m_listFields.end(); it++)\r
+ {\r
+ const CMimeField& fd = *it;\r
+ if (!::stricmp(fd.GetName(), pszFieldName))\r
+ break;\r
+ }\r
+ return it;\r
+}\r
+\r
+list<CMimeField>::iterator CMimeHeader::FindField(const char* pszFieldName)\r
+{\r
+ list<CMimeField>::iterator it;\r
+ for (it = m_listFields.begin(); it != m_listFields.end(); it++)\r
+ {\r
+ CMimeField& fd = *it;\r
+ if (!::stricmp(fd.GetName(), pszFieldName))\r
+ break;\r
+ }\r
+ return it;\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CMimeBody class - Represents a body part in a MIME message\r
+//////////////////////////////////////////////////////////////////////\r
+#include <fcntl.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#include <io.h>\r
+\r
+// initialize the content with text\r
+int CMimeBody::SetText(const char* pbText, int nLength/*=0*/)\r
+{\r
+ ASSERT(pbText != NULL);\r
+ if (!nLength)\r
+ nLength = (int)::strlen((char*)pbText);\r
+\r
+ if (!AllocateBuffer(nLength+4))\r
+ return -1;\r
+\r
+ ::memcpy(m_pbText, pbText, nLength);\r
+ m_pbText[nLength] = 0;\r
+ m_nTextSize = nLength;\r
+ return nLength;\r
+}\r
+\r
+int CMimeBody::GetText(char* pbText, int nMaxSize)\r
+{\r
+ int nSize = min(nMaxSize, m_nTextSize);\r
+ if (m_pbText != NULL)\r
+ ::memcpy(pbText, m_pbText, nSize);\r
+ return nSize;\r
+}\r
+\r
+int CMimeBody::GetText(string& strText)\r
+{\r
+ if (m_pbText != NULL)\r
+ strText.assign((const char*) m_pbText, m_nTextSize);\r
+ return m_nTextSize;\r
+}\r
+\r
+// initialize the content of this body part with a mail message\r
+bool CMimeBody::SetMessage(const CMimeMessage* pMM)\r
+{\r
+ ASSERT(pMM != NULL);\r
+ int nSize = pMM->GetLength();\r
+ if (!AllocateBuffer(nSize+4))\r
+ return false;\r
+\r
+ nSize = pMM->Store((char*)m_pbText, nSize);\r
+ m_pbText[nSize] = 0;\r
+ m_nTextSize = nSize;\r
+\r
+ const char* pszType = GetContentType();\r
+ if (!pszType || ::memicmp(pszType, "message", 7) != 0)\r
+ SetContentType("message/rfc822");\r
+ //SetTransferEncoding(CMimeConst::EncodingBinary()); // in case the default 7bit cause folding\r
+ return true;\r
+}\r
+\r
+void CMimeBody::GetMessage(CMimeMessage* pMM) const\r
+{\r
+ ASSERT(pMM != NULL);\r
+ ASSERT(m_pbText != NULL);\r
+ pMM->Load((const char*)m_pbText, m_nTextSize);\r
+}\r
+\r
+// initialize the content (attachment) by reading from a file\r
+bool CMimeBody::ReadFromFile(const char* pszFilename)\r
+{\r
+ int hFile = ::open(pszFilename, O_RDONLY | O_BINARY);\r
+ if (hFile < 0)\r
+ return false;\r
+\r
+ try\r
+ {\r
+ int nFileSize = (int)::lseek(hFile, 0L, SEEK_END); // get file length\r
+ ::lseek(hFile, 0L, SEEK_SET);\r
+\r
+ FreeBuffer();\r
+ if (nFileSize > 0)\r
+ {\r
+ AllocateBuffer(nFileSize+4);\r
+ unsigned char* pszData = m_pbText;\r
+\r
+ for (;;)\r
+ {\r
+ int nRead = ::read(hFile, pszData, 512);\r
+ if (nRead < 0)\r
+ {\r
+ ::close(hFile);\r
+ return false;\r
+ }\r
+ pszData += nRead;\r
+ if (nRead < 512)\r
+ break;\r
+ }\r
+ *pszData = 0;\r
+ m_nTextSize = nFileSize;\r
+ }\r
+ }\r
+ catch (...)\r
+ {\r
+ ::close(hFile);\r
+ throw;\r
+ }\r
+\r
+ ::close(hFile);\r
+ const char* pszName = ::strrchr(pszFilename, '\\');\r
+ if (!pszName)\r
+ pszName = pszFilename;\r
+ else\r
+ pszName++;\r
+ SetName(pszName); // set 'name' parameter:\r
+ return true;\r
+}\r
+\r
+// write the content (attachment) to a file\r
+bool CMimeBody::WriteToFile(const char* pszFilename)\r
+{\r
+ if (!m_nTextSize)\r
+ return true;\r
+ int hFile = ::open(pszFilename, O_CREAT | O_TRUNC | O_RDWR | O_BINARY, S_IREAD | S_IWRITE);\r
+ if (hFile < 0)\r
+ return false;\r
+\r
+ const unsigned char* pszData = m_pbText;\r
+ int nLeft = m_nTextSize;\r
+\r
+ try\r
+ {\r
+ for (;;)\r
+ {\r
+ int nWritten = ::write(hFile, pszData, min(512, nLeft));\r
+ if (nWritten <= 0)\r
+ {\r
+ ::close(hFile);\r
+ return false;\r
+ }\r
+ pszData += nWritten;\r
+ nLeft -= nWritten;\r
+ if (nLeft <= 0)\r
+ break;\r
+ }\r
+ }\r
+ catch (...)\r
+ {\r
+ ::close(hFile);\r
+ throw;\r
+ }\r
+\r
+ ::close(hFile);\r
+ return true;\r
+}\r
+\r
+// delete all child body parts\r
+void CMimeBody::DeleteAll()\r
+{\r
+ while (!m_listBodies.empty())\r
+ {\r
+ CMimeBody* pBP = m_listBodies.back();\r
+ m_listBodies.pop_back();\r
+ ASSERT(pBP != NULL);\r
+ delete pBP; // surely delete because it was allocated by CreatePart()\r
+ }\r
+}\r
+\r
+// create a new child body part, and add it to body part list\r
+CMimeBody* CMimeBody::CreatePart(const char* pszMediaType/*=NULL*/, CMimeBody* pWhere/*=NULL*/)\r
+{\r
+ CMimeBody* pBP = CMimeEnvironment::CreateBodyPart(pszMediaType);\r
+ ASSERT(pBP != NULL);\r
+ if (pWhere != NULL)\r
+ {\r
+ for (CBodyList::iterator it = m_listBodies.begin(); it != m_listBodies.end(); it++)\r
+ if (*it == pWhere)\r
+ {\r
+ m_listBodies.insert(it, pBP);\r
+ return pBP;\r
+ }\r
+ }\r
+ m_listBodies.push_back(pBP);\r
+ return pBP;\r
+}\r
+\r
+// remove and delete a child body part\r
+void CMimeBody::ErasePart(CMimeBody* pBP)\r
+{\r
+ ASSERT(pBP != NULL);\r
+ m_listBodies.remove(pBP);\r
+ delete pBP;\r
+}\r
+\r
+// return a list of all child body parts belong to this body part\r
+int CMimeBody::GetBodyPartList(CBodyList& rList) const\r
+{\r
+ int nCount = 0;\r
+ int nMediaType = GetMediaType();\r
+\r
+ if (MEDIA_MULTIPART != nMediaType)\r
+ {\r
+ rList.push_back((CMimeBody*)this);\r
+ nCount++;\r
+ }\r
+ else\r
+ {\r
+ list<CMimeBody*>::const_iterator it;\r
+ for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)\r
+ {\r
+ CMimeBody* pBP = *it;\r
+ ASSERT(pBP != NULL);\r
+ nCount += pBP->GetBodyPartList(rList);\r
+ }\r
+ }\r
+ return nCount;\r
+}\r
+\r
+// return a list of all attachment body parts belong to this body part\r
+int CMimeBody::GetAttachmentList(CBodyList& rList) const\r
+{\r
+ int nCount = 0;\r
+ int nMediaType = GetMediaType();\r
+\r
+ if (MEDIA_MULTIPART != nMediaType)\r
+ {\r
+ string strName = GetName();\r
+ if (strName.size() > 0)\r
+ {\r
+ rList.push_back((CMimeBody*)this);\r
+ nCount++;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ list<CMimeBody*>::const_iterator it;\r
+ for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)\r
+ {\r
+ CMimeBody* pBP = *it;\r
+ ASSERT(pBP != NULL);\r
+ nCount += pBP->GetAttachmentList(rList);\r
+ }\r
+ }\r
+ return nCount;\r
+}\r
+\r
+void CMimeBody::Clear()\r
+{\r
+ DeleteAll();\r
+ m_itFind = m_listBodies.end();\r
+ FreeBuffer();\r
+ CMimeHeader::Clear();\r
+}\r
+\r
+// return the length needed to store this body part to string buffer\r
+int CMimeBody::GetLength() const\r
+{\r
+ int nLength = CMimeHeader::GetLength();\r
+ CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());\r
+ ASSERT(pCoder != NULL);\r
+ pCoder->SetInput((const char*)m_pbText, m_nTextSize, true);\r
+ nLength += pCoder->GetOutputLength();\r
+ delete pCoder;\r
+\r
+ if (m_listBodies.empty())\r
+ return nLength;\r
+\r
+ string strBoundary = GetBoundary();\r
+ int nBoundSize = (int) strBoundary.size();\r
+ list<CMimeBody*>::const_iterator it;\r
+ for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)\r
+ {\r
+ nLength += nBoundSize + 6; // include 2 leading hyphens and 2 pair of CRLFs\r
+ CMimeBody* pBP = *it;\r
+ ASSERT(pBP != NULL);\r
+ nLength += pBP->GetLength();\r
+ }\r
+ nLength += nBoundSize + 8; // include 2 leading hyphens, 2 trailng hyphens and 2 pair of CRLFs\r
+ return nLength;\r
+}\r
+\r
+// store the body part to string buffer\r
+int CMimeBody::Store(char* pszData, int nMaxSize) const\r
+{\r
+ // store header fields\r
+ int nSize = CMimeHeader::Store(pszData, nMaxSize);\r
+ if (nSize <= 0)\r
+ return nSize;\r
+\r
+ // store content\r
+ char* pszDataBegin = pszData; // preserve start position\r
+ pszData += nSize;\r
+ nMaxSize -= nSize;\r
+\r
+ CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());\r
+ ASSERT(pCoder != NULL);\r
+ pCoder->SetInput((const char*)m_pbText, m_nTextSize, true);\r
+ int nOutput = pCoder->GetOutput((unsigned char*)pszData, nMaxSize);\r
+ delete pCoder;\r
+ if (nOutput < 0)\r
+ return nOutput;\r
+\r
+ pszData += nOutput;\r
+ nMaxSize -= nOutput;\r
+ if (m_listBodies.empty())\r
+ return (int)(pszData - pszDataBegin);\r
+\r
+ // store child body parts\r
+ string strBoundary = GetBoundary();\r
+ if (strBoundary.empty())\r
+ return -1; // boundary not be set\r
+\r
+ int nBoundSize = (int)strBoundary.size() + 6;\r
+ for (CBodyList::const_iterator it=m_listBodies.begin(); it!=m_listBodies.end(); it++)\r
+ {\r
+ if (nMaxSize < nBoundSize)\r
+ break;\r
+ if (m_listBodies.begin() == it && *(pszData-2) == '\r' && *(pszData-1) == '\n')\r
+ {\r
+ pszData -= 2;\r
+ nMaxSize += 2;\r
+ }\r
+ ::sprintf(pszData, "\r\n--%s\r\n", strBoundary.c_str());\r
+ pszData += nBoundSize;\r
+ nMaxSize -= nBoundSize;\r
+\r
+ CMimeBody* pBP = *it;\r
+ ASSERT(pBP != NULL);\r
+ nOutput = pBP->Store(pszData, nMaxSize);\r
+ if (nOutput < 0)\r
+ return nOutput;\r
+ pszData += nOutput;\r
+ nMaxSize -= nOutput;\r
+ }\r
+\r
+ if (nMaxSize >= nBoundSize+2) // add closing boundary delimiter\r
+ {\r
+ ::sprintf(pszData, "\r\n--%s--\r\n", strBoundary.c_str());\r
+ pszData += nBoundSize + 2;\r
+ }\r
+ return (int)(pszData - pszDataBegin);\r
+}\r
+\r
+void CMimeBody::Store(std::string &str) const\r
+{\r
+ char *temp=new char[GetLength()+1];\r
+ Store(temp,GetLength());\r
+ temp[GetLength()]='\0';\r
+ str=temp;\r
+ delete [] temp;\r
+}\r
+\r
+// load a body part from string buffer\r
+int CMimeBody::Load(const char* pszData, int nDataSize)\r
+{\r
+ // load header fields\r
+ int nSize = CMimeHeader::Load(pszData, nDataSize);\r
+ if (nSize <= 0)\r
+ return nSize;\r
+\r
+ const char* pszDataBegin = pszData; // preserve start position\r
+ pszData += nSize;\r
+ nDataSize -= nSize;\r
+ FreeBuffer();\r
+\r
+ // determine the length of the content\r
+ const char* pszEnd = pszData + nDataSize;\r
+ int nMediaType = GetMediaType();\r
+ if (MEDIA_MULTIPART == nMediaType)\r
+ {\r
+ // find the begin boundary\r
+ string strBoundary = GetBoundary();\r
+ if (!strBoundary.empty())\r
+ {\r
+ strBoundary = "\r\n--" + strBoundary;\r
+ pszEnd = ::FindString(pszData-2, strBoundary.c_str(), pszEnd);\r
+ if (!pszEnd)\r
+ pszEnd = pszData + nDataSize;\r
+ else\r
+ pszEnd += 2;\r
+ }\r
+ }\r
+\r
+ // load content\r
+ nSize = (int)(pszEnd - pszData);\r
+ if (nSize > 0)\r
+ {\r
+ CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());\r
+ ASSERT(pCoder != NULL);\r
+ pCoder->SetInput(pszData, nSize, false);\r
+ int nOutput = pCoder->GetOutputLength();\r
+ if (AllocateBuffer(nOutput+4))\r
+ nOutput = pCoder->GetOutput(m_pbText, nOutput);\r
+ else\r
+ nOutput = -1;\r
+ delete pCoder;\r
+ if (nOutput < 0)\r
+ return nOutput;\r
+\r
+ ASSERT(nOutput < m_nTextSize);\r
+ m_pbText[nOutput] = 0;\r
+ m_nTextSize = nOutput;\r
+ pszData += nSize;\r
+ nDataSize -= nSize;\r
+ }\r
+ if (nDataSize <= 0)\r
+ return (int)(pszData - pszDataBegin);\r
+\r
+ // load child body parts\r
+ string strBoundary = GetBoundary();\r
+ ASSERT(strBoundary.size() > 0);\r
+ strBoundary = "\r\n--" + strBoundary;\r
+\r
+ // look for the first boundary (case sensitive)\r
+ pszData -= 2; // go back to CRLF\r
+ nDataSize += 2;\r
+ pszEnd = pszData + nDataSize;\r
+ const char* pszBound1 = ::FindString(pszData, strBoundary.c_str(), pszEnd);\r
+ while (pszBound1 != NULL && pszBound1 < pszEnd)\r
+ {\r
+ const char* pszStart = ::FindString(pszBound1+2, "\r\n", pszEnd);\r
+ if (!pszStart)\r
+ break;\r
+ pszStart += 2;\r
+ if (pszBound1[strBoundary.size()] == '-' && pszBound1[strBoundary.size()+1] == '-')\r
+ return (int)(pszStart - pszDataBegin); // reach the closing boundary\r
+\r
+ // look for the next boundary\r
+ const char* pszBound2 = ::FindString(pszStart, strBoundary.c_str(), pszEnd);\r
+ if (!pszBound2) // overflow, boundary may be truncated\r
+ pszBound2 = pszEnd;\r
+ int nEntitySize = (int) (pszBound2 - pszStart);\r
+\r
+ // find the media type of this body part:\r
+ CMimeHeader header;\r
+ header.Load(pszStart, nEntitySize);\r
+ string strMediaType = header.GetMainType();\r
+ CMimeBody* pBP = CreatePart(strMediaType.c_str());\r
+\r
+ int nInputSize = pBP->Load(pszStart, nEntitySize);\r
+ if (nInputSize < 0)\r
+ {\r
+ ErasePart(pBP);\r
+ return nInputSize;\r
+ }\r
+ pszBound1 = pszBound2;\r
+ }\r
+ return (int)(pszEnd - pszDataBegin);\r
+}\r
+\r
+//////////////////////////////////////////////////////////////////////\r
+// CMimeMessage - Represents a MIME message\r
+//////////////////////////////////////////////////////////////////////\r
+\r
+void CMimeMessage::SetDate()\r
+{\r
+ time_t timeNow = ::time(NULL);\r
+ struct tm *ptm = ::localtime(&timeNow);\r
+ SetDate(ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);\r
+}\r
+\r
+void CMimeMessage::SetDate(int nYear, int nMonth, int nDay, int nHour, int nMinute, int nSecond)\r
+{\r
+ static const char* s_MonthNames[] =\r
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };\r
+ static const char* s_DayNames[] =\r
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };\r
+\r
+ struct tm tmDate;\r
+ ::memset(&tmDate, 0, sizeof(tmDate));\r
+ tmDate.tm_year = nYear - 1900;\r
+ tmDate.tm_mon = nMonth - 1;\r
+ tmDate.tm_mday = nDay;\r
+ tmDate.tm_hour = nHour;\r
+ tmDate.tm_min = nMinute;\r
+ tmDate.tm_sec = nSecond;\r
+ tmDate.tm_isdst = -1;\r
+\r
+ time_t timeDate = ::mktime(&tmDate);\r
+ if (timeDate < 0)\r
+ {\r
+ ASSERT(false);\r
+ return;\r
+ }\r
+\r
+ tmDate = *::localtime(&timeDate); // adjusted local time\r
+ struct tm *ptmGmt = ::gmtime(&timeDate); // Greenwich Mean Time\r
+ long nTimeDiff = tmDate.tm_mday - ptmGmt->tm_mday;\r
+ if (nTimeDiff > 1)\r
+ nTimeDiff = -1;\r
+ else if (nTimeDiff < -1)\r
+ nTimeDiff = 1;\r
+ nTimeDiff *= 60 * 24;\r
+ nTimeDiff +=\r
+ (tmDate.tm_hour - ptmGmt->tm_hour) * 60 +\r
+ tmDate.tm_min - ptmGmt->tm_min;\r
+ if (tmDate.tm_isdst > 0)\r
+ nTimeDiff -= 60;\r
+\r
+ char szDate[40];\r
+ ASSERT(tmDate.tm_wday < 7);\r
+ ASSERT(tmDate.tm_mon < 12);\r
+ ::sprintf(szDate, "%s, %d %s %d %02d:%02d:%02d %c%02d%02d",\r
+ s_DayNames[tmDate.tm_wday],\r
+ tmDate.tm_mday, s_MonthNames[tmDate.tm_mon], tmDate.tm_year+1900,\r
+ tmDate.tm_hour, tmDate.tm_min, tmDate.tm_sec,\r
+ (nTimeDiff >= 0 ? '+' : '-'), abs(nTimeDiff / 60), abs(nTimeDiff % 60));\r
+\r
+ SetFieldValue("Date", szDate);\r
+}\r