1 //////////////////////////////////////////////////////////////////////
\r
3 // MIME message encoding/decoding
\r
8 //////////////////////////////////////////////////////////////////////
\r
9 //#include "stdafx.h"
\r
10 #include "../../../include/nntp/mime/MimeCode.h"
\r
11 #include "../../../include/nntp/mime/MimeChar.h"
\r
12 #include "../../../include/nntp/mime/Mime.h"
\r
18 #define stricmp strcasecmp
\r
19 #define strnicmp strncasecmp
\r
20 #define memicmp memcmp
\r
25 static char THIS_FILE[]=__FILE__;
\r
26 #define new DEBUG_NEW
\r
29 // search for a character in the current line (before CRLF)
\r
30 static const char* LineFind(const char* pszString, int ch)
\r
32 ASSERT(pszString != NULL);
\r
33 while (*pszString != 0 && *pszString != ch && *pszString != '\r' && *pszString != '\n')
\r
35 return *pszString == ch ? pszString : NULL;
\r
38 // search for string2 in string1 (strstr)
\r
39 static const char* FindString(const char* pszStr1, const char* pszStr2, const char* pszEnd)
\r
41 pszEnd -= ::strlen(pszStr2);
\r
42 const char *s1, *s2;
\r
43 while (pszStr1 <= pszEnd)
\r
47 while (*s1 == *s2 && *s2)
\r
56 //////////////////////////////////////////////////////////////////////
\r
57 // CMimeField class - Represents a field of a MIME body part header
\r
58 //////////////////////////////////////////////////////////////////////
\r
60 void CMimeField::GetValue(string& strValue) const
\r
62 string::size_type nEnd = m_strValue.find(';');
\r
63 if (nEnd != string::npos)
\r
65 while (nEnd > 0 && CMimeChar::IsSpace((unsigned char)m_strValue[nEnd-1]))
\r
67 strValue.assign(m_strValue.c_str(), nEnd);
\r
70 strValue = m_strValue;
\r
73 // set a parameter (attribute=value) of the field
\r
74 void CMimeField::SetParameter(const char* pszAttr, const char* pszValue)
\r
76 int nSize = pszValue ? (int)::strlen(pszValue) : 0;
\r
78 strValue.reserve(nSize+3);
\r
79 if (!pszValue || *pszValue != '"')
\r
81 if (pszValue != NULL)
\r
82 strValue += pszValue;
\r
83 if (nSize < 2 || pszValue[nSize-1] != '"')
\r
87 if (!FindParameter(pszAttr, nPos, nSize)) // add new parameter
\r
89 m_strValue.reserve(m_strValue.size() + ::strlen(pszAttr) + strValue.size() + 5);
\r
90 //if (CMimeEnvironment::AutoFolding())
\r
91 // m_strValue += ";\r\n\t";
\r
93 // m_strValue += "; ";
\r
95 m_strValue += pszAttr;
\r
97 m_strValue += strValue;
\r
99 else // update existing parameter
\r
100 m_strValue.replace(nPos, nSize, strValue);
\r
103 // get the value of a parameter
\r
104 bool CMimeField::GetParameter(const char* pszAttr, string& strValue) const
\r
107 if (!FindParameter(pszAttr, nPos, nSize))
\r
113 if (m_strValue[nPos] == '"')
\r
117 if (nSize > 0 && m_strValue[nPos+nSize-1] == '"')
\r
120 strValue.assign(m_strValue.data()+nPos, nSize);
\r
124 int CMimeField::GetLength() const
\r
126 int nLength = (int) m_strName.size() + 4;
\r
127 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
128 pCoder->SetCharset(m_strCharset.c_str());
\r
129 pCoder->SetInput(m_strValue.c_str(), (int)m_strValue.size(), true);
\r
130 nLength += pCoder->GetOutputLength();
\r
135 // store a field to string buffer
\r
136 int CMimeField::Store(char* pszData, int nMaxSize) const
\r
138 ASSERT(pszData != NULL);
\r
139 int nMinSize = (int)m_strName.size() + 4;
\r
140 if (nMaxSize < nMinSize)
\r
142 ::strcpy(pszData, m_strName.c_str());
\r
143 pszData += m_strName.size();
\r
147 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
148 pCoder->SetCharset(m_strCharset.c_str());
\r
149 pCoder->SetInput(m_strValue.c_str(), (int)m_strValue.size(), true);
\r
150 int nEncoded = pCoder->GetOutput((unsigned char*) pszData, nMaxSize-nMinSize);
\r
152 pszData += nEncoded;
\r
156 return nMinSize + nEncoded;
\r
159 // load a field from string buffer
\r
160 int CMimeField::Load(const char* pszData, int nDataSize)
\r
163 ASSERT(pszData != NULL);
\r
164 const char *pszEnd, *pszStart = pszData;
\r
165 // find the next field (e.g. "\r\nContent...")
\r
166 while (CMimeChar::IsSpace((unsigned char)*pszStart))
\r
168 if (*pszStart == '\r') // end of header ?
\r
170 pszStart = ::FindString(pszStart, "\r\n", pszData+nDataSize);
\r
176 // get the field name
\r
177 pszEnd = ::LineFind(pszStart, ':');
\r
178 if (pszEnd != NULL) // if colon not found, Name would be empty
\r
180 m_strName.assign(pszStart, (pszEnd-pszStart));
\r
181 pszStart = pszEnd + 1;
\r
184 // find the end of the field
\r
185 while (*pszStart == ' ' || *pszStart == '\t')
\r
190 pszEnd = ::FindString(pszEnd, "\r\n", pszData+nDataSize);
\r
194 } while (*pszEnd == '\t' || *pszEnd == ' '); // linear-white-space
\r
196 // decode and unfold the field value
\r
197 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
198 pCoder->SetInput(pszStart, (int)(pszEnd-pszStart)-2, false);
\r
199 m_strValue.resize(pCoder->GetOutputLength());
\r
200 int nSize = pCoder->GetOutput((unsigned char*) m_strValue.c_str(), (int) m_strValue.capacity());
\r
201 m_strValue.resize(nSize);
\r
202 m_strCharset = pCoder->GetCharset();
\r
204 return (int) (pszEnd - pszData);
\r
207 bool CMimeField::FindParameter(const char* pszAttr, int& nPos, int& nSize) const
\r
209 ASSERT(pszAttr != NULL);
\r
210 const char* pszParms = ::strchr(m_strValue.data(), ';');
\r
211 int nAttrSize = (int)::strlen(pszAttr);
\r
212 while (pszParms != NULL)
\r
214 while (CMimeChar::IsSpace((unsigned char)*pszParms) || *pszParms == ';')
\r
217 const char* pszName = pszParms; // pszName -> attribute
\r
218 pszParms = ::strchr(pszParms, '=');
\r
222 pszParms++; // pszParams -> parameter value
\r
223 //while (*pszParms == ' ' || *pszParms == '\t')
\r
226 const char* pszParmEnd = NULL;
\r
227 if (*pszParms == '"') // quoted string
\r
228 pszParmEnd = ::strchr(pszParms+1, '"');
\r
229 if (!pszParmEnd) // non quoted string
\r
231 pszParmEnd = pszParms;
\r
232 while (CMimeChar::IsToken(*pszParmEnd))
\r
235 else pszParmEnd++; // pszParmEnd -> end of parameter value
\r
237 if (!::memicmp(pszAttr, pszName, nAttrSize) &&
\r
238 (CMimeChar::IsSpace((unsigned char)pszName[nAttrSize]) || pszName[nAttrSize] == '='))
\r
240 nPos = (int)(pszParms - m_strValue.data());
\r
241 nSize = (int)(pszParmEnd - pszParms);
\r
245 pszParms = pszParmEnd;
\r
250 //////////////////////////////////////////////////////////////////////
\r
251 // CMimeHeader class - Represents the header of a MIME body part
\r
252 //////////////////////////////////////////////////////////////////////
\r
254 // Return the media type represented by Content-Type field (see RFC 2046)
\r
255 CMimeHeader::MediaType CMimeHeader::GetMediaType() const
\r
257 const char* pszType = GetContentType();
\r
262 while (m_TypeTable[nIndex] != NULL &&
\r
263 ::memicmp(pszType, m_TypeTable[nIndex], ::strlen(m_TypeTable[nIndex])) != 0)
\r
265 return (MediaType) nIndex;
\r
268 // get the top-level media type
\r
269 string CMimeHeader::GetMainType() const
\r
272 const char* pszType = GetContentType();
\r
273 if (pszType != NULL)
\r
275 const char* pszSlash = ::strchr(pszType, '/');
\r
276 if (pszSlash != NULL)
\r
277 strType.assign(pszType, pszSlash-pszType);
\r
287 string CMimeHeader::GetSubType() const
\r
290 const CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
294 pfd->GetValue(strType);
\r
295 string::size_type nSlash = strType.find('/');
\r
297 strSubType = strType.substr(nSlash+1);
\r
300 strSubType = "plain";
\r
304 // set the 'charset' parameter (for text) of Content-Type field
\r
305 void CMimeHeader::SetCharset(const char* pszCharset)
\r
307 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
311 fd.SetName(CMimeConst::ContentType());
\r
312 fd.SetValue("text/plain");
\r
313 fd.SetParameter(CMimeConst::Charset(), pszCharset);
\r
314 m_listFields.push_back(fd);
\r
317 pfd->SetParameter(CMimeConst::Charset(), pszCharset);
\r
320 // set the 'name' parameter (for attachment) of Content-Type field
\r
321 void CMimeHeader::SetName(const char* pszName)
\r
323 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
326 // get the appropriate media-type/subtype according to file extension
\r
327 ASSERT(pszName != NULL);
\r
329 const char* pszType = "application/octet-stream";
\r
330 const char* pszFileExt = ::strrchr(pszName, '.');
\r
331 if (pszFileExt != NULL)
\r
335 while (m_TypeCvtTable[nIndex].nMediaType != MEDIA_UNKNOWN)
\r
337 if (!::stricmp(pszFileExt, m_TypeCvtTable[nIndex].pszFileExt))
\r
339 strType = m_TypeTable[m_TypeCvtTable[nIndex].nMediaType];
\r
341 strType += m_TypeCvtTable[nIndex].pszSubType;
\r
342 pszType = strType.c_str();
\r
350 fd.SetName(CMimeConst::ContentType());
\r
351 fd.SetValue(pszType);
\r
352 fd.SetParameter(CMimeConst::Name(), pszName);
\r
353 m_listFields.push_back(fd);
\r
356 pfd->SetParameter(CMimeConst::Name(), pszName);
\r
359 // set 'boundary' parameter (for multipart) of Content-Type field
\r
360 void CMimeHeader::SetBoundary(const char* pszBoundary/*=NULL*/)
\r
362 static int s_nPartNumber = 0;
\r
364 if (!pszBoundary) // generate a new boundary delimeter
\r
366 ::srand(((unsigned)::time(NULL)));// ^ reinterpret_cast<unsigned>(this));
\r
367 ::sprintf(buf, "__=_Part_Boundary_%03d_%06d.%06d", ++s_nPartNumber, rand(), rand());
\r
368 if (s_nPartNumber >= 9)
\r
373 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
377 fd.SetName(CMimeConst::ContentType());
\r
378 fd.SetValue("multipart/mixed");
\r
379 fd.SetParameter(CMimeConst::Boundary(), pszBoundary);
\r
380 m_listFields.push_back(fd);
\r
384 if (::memicmp(pfd->GetValue(), "multipart", 9) != 0)
\r
385 pfd->SetValue("multipart/mixed");
\r
386 pfd->SetParameter(CMimeConst::Boundary(), pszBoundary);
\r
390 void CMimeHeader::Clear()
\r
392 m_listFields.clear();
\r
395 // return the length needed to store this header to string buffer
\r
396 int CMimeHeader::GetLength() const
\r
399 list<CMimeField>::const_iterator it;
\r
400 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
401 nLength += (*it).GetLength();
\r
402 return nLength + 2; // a pair of CRLF indicate the end of header
\r
405 // store the header to string buffer
\r
406 int CMimeHeader::Store(char* pszData, int nMaxSize) const
\r
408 ASSERT(pszData != NULL);
\r
410 list<CMimeField>::const_iterator it;
\r
411 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
413 const CMimeField& fd = *it;
\r
414 int nSize = fd.Store(pszData+nOutput, nMaxSize-nOutput);
\r
420 pszData[nOutput++] = '\r'; // add CRLF indicating the end of header
\r
421 pszData[nOutput++] = '\n';
\r
425 // load a header from string buffer
\r
426 int CMimeHeader::Load(const char* pszData, int nDataSize)
\r
428 ASSERT(pszData != NULL);
\r
430 while (pszData[nInput] != 0 && pszData[nInput] != '\r')
\r
433 int nSize = fd.Load(pszData+nInput, nDataSize-nInput);
\r
438 m_listFields.push_back(fd); // don't use SetField in case of same name fields
\r
441 return nInput + 2; // skip the ending CRLF
\r
444 list<CMimeField>::const_iterator CMimeHeader::FindField(const char* pszFieldName) const
\r
446 list<CMimeField>::const_iterator it;
\r
447 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
449 const CMimeField& fd = *it;
\r
450 if (!::stricmp(fd.GetName(), pszFieldName))
\r
456 list<CMimeField>::iterator CMimeHeader::FindField(const char* pszFieldName)
\r
458 list<CMimeField>::iterator it;
\r
459 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
461 CMimeField& fd = *it;
\r
462 if (!::stricmp(fd.GetName(), pszFieldName))
\r
468 //////////////////////////////////////////////////////////////////////
\r
469 // CMimeBody class - Represents a body part in a MIME message
\r
470 //////////////////////////////////////////////////////////////////////
\r
472 #include <sys/types.h>
\r
473 #include <sys/stat.h>
\r
478 #if !defined(__APPLE__) && !defined(__DARWIN__)
\r
479 #if !defined(__FreeBSD__) && !defined(solaris) && !defined(__sun)
\r
480 #include <sys/io.h>
\r
491 // initialize the content with text
\r
492 int CMimeBody::SetText(const char* pbText, int nLength/*=0*/)
\r
494 ASSERT(pbText != NULL);
\r
496 nLength = (int)::strlen((char*)pbText);
\r
498 if (!AllocateBuffer(nLength+4))
\r
501 ::memcpy(m_pbText, pbText, nLength);
\r
502 m_pbText[nLength] = 0;
\r
503 m_nTextSize = nLength;
\r
507 int CMimeBody::GetText(char* pbText, int nMaxSize)
\r
509 int nSize = min(nMaxSize, m_nTextSize);
\r
510 if (m_pbText != NULL)
\r
511 ::memcpy(pbText, m_pbText, nSize);
\r
515 int CMimeBody::GetText(string& strText)
\r
517 if (m_pbText != NULL)
\r
518 strText.assign((const char*) m_pbText, m_nTextSize);
\r
519 return m_nTextSize;
\r
522 // initialize the content of this body part with a mail message
\r
523 bool CMimeBody::SetMessage(const CMimeMessage* pMM)
\r
525 ASSERT(pMM != NULL);
\r
526 int nSize = pMM->GetLength();
\r
527 if (!AllocateBuffer(nSize+4))
\r
530 nSize = pMM->Store((char*)m_pbText, nSize);
\r
531 m_pbText[nSize] = 0;
\r
532 m_nTextSize = nSize;
\r
534 const char* pszType = GetContentType();
\r
535 if (!pszType || ::memicmp(pszType, "message", 7) != 0)
\r
536 SetContentType("message/rfc822");
\r
537 //SetTransferEncoding(CMimeConst::EncodingBinary()); // in case the default 7bit cause folding
\r
541 void CMimeBody::GetMessage(CMimeMessage* pMM) const
\r
543 ASSERT(pMM != NULL);
\r
544 ASSERT(m_pbText != NULL);
\r
545 pMM->Load((const char*)m_pbText, m_nTextSize);
\r
548 // initialize the content (attachment) by reading from a file
\r
549 bool CMimeBody::ReadFromFile(const char* pszFilename)
\r
551 int hFile = ::open(pszFilename, O_RDONLY | O_BINARY);
\r
557 int nFileSize = (int)::lseek(hFile, 0L, SEEK_END); // get file length
\r
558 ::lseek(hFile, 0L, SEEK_SET);
\r
563 AllocateBuffer(nFileSize+4);
\r
564 unsigned char* pszData = m_pbText;
\r
568 int nRead = ::read(hFile, pszData, 512);
\r
579 m_nTextSize = nFileSize;
\r
589 const char* pszName = ::strrchr(pszFilename, '\\');
\r
591 pszName = pszFilename;
\r
594 SetName(pszName); // set 'name' parameter:
\r
598 // write the content (attachment) to a file
\r
599 bool CMimeBody::WriteToFile(const char* pszFilename)
\r
603 int hFile = ::open(pszFilename, O_CREAT | O_TRUNC | O_RDWR | O_BINARY, S_IREAD | S_IWRITE);
\r
607 const unsigned char* pszData = m_pbText;
\r
608 int nLeft = m_nTextSize;
\r
614 int nWritten = ::write(hFile, pszData, min(512, nLeft));
\r
620 pszData += nWritten;
\r
636 // delete all child body parts
\r
637 void CMimeBody::DeleteAll()
\r
639 while (!m_listBodies.empty())
\r
641 CMimeBody* pBP = m_listBodies.back();
\r
642 m_listBodies.pop_back();
\r
643 ASSERT(pBP != NULL);
\r
644 delete pBP; // surely delete because it was allocated by CreatePart()
\r
648 // create a new child body part, and add it to body part list
\r
649 CMimeBody* CMimeBody::CreatePart(const char* pszMediaType/*=NULL*/, CMimeBody* pWhere/*=NULL*/)
\r
651 CMimeBody* pBP = CMimeEnvironment::CreateBodyPart(pszMediaType);
\r
652 ASSERT(pBP != NULL);
\r
653 if (pWhere != NULL)
\r
655 for (CBodyList::iterator it = m_listBodies.begin(); it != m_listBodies.end(); it++)
\r
658 m_listBodies.insert(it, pBP);
\r
662 m_listBodies.push_back(pBP);
\r
666 // remove and delete a child body part
\r
667 void CMimeBody::ErasePart(CMimeBody* pBP)
\r
669 ASSERT(pBP != NULL);
\r
670 m_listBodies.remove(pBP);
\r
674 // return a list of all child body parts belong to this body part
\r
675 int CMimeBody::GetBodyPartList(CBodyList& rList) const
\r
678 int nMediaType = GetMediaType();
\r
680 if (MEDIA_MULTIPART != nMediaType)
\r
682 rList.push_back((CMimeBody*)this);
\r
687 list<CMimeBody*>::const_iterator it;
\r
688 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
690 CMimeBody* pBP = *it;
\r
691 ASSERT(pBP != NULL);
\r
692 nCount += pBP->GetBodyPartList(rList);
\r
698 // return a list of all attachment body parts belong to this body part
\r
699 int CMimeBody::GetAttachmentList(CBodyList& rList) const
\r
702 int nMediaType = GetMediaType();
\r
704 if (MEDIA_MULTIPART != nMediaType)
\r
706 string strName = GetName();
\r
707 if (strName.size() > 0)
\r
709 rList.push_back((CMimeBody*)this);
\r
715 list<CMimeBody*>::const_iterator it;
\r
716 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
718 CMimeBody* pBP = *it;
\r
719 ASSERT(pBP != NULL);
\r
720 nCount += pBP->GetAttachmentList(rList);
\r
726 void CMimeBody::Clear()
\r
729 m_itFind = m_listBodies.end();
\r
731 CMimeHeader::Clear();
\r
734 // return the length needed to store this body part to string buffer
\r
735 int CMimeBody::GetLength() const
\r
737 int nLength = CMimeHeader::GetLength();
\r
738 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
739 ASSERT(pCoder != NULL);
\r
740 pCoder->SetInput((const char*)m_pbText, m_nTextSize, true);
\r
741 nLength += pCoder->GetOutputLength();
\r
744 if (m_listBodies.empty())
\r
747 string strBoundary = GetBoundary();
\r
748 int nBoundSize = (int) strBoundary.size();
\r
749 list<CMimeBody*>::const_iterator it;
\r
750 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
752 nLength += nBoundSize + 6; // include 2 leading hyphens and 2 pair of CRLFs
\r
753 CMimeBody* pBP = *it;
\r
754 ASSERT(pBP != NULL);
\r
755 nLength += pBP->GetLength();
\r
757 nLength += nBoundSize + 8; // include 2 leading hyphens, 2 trailng hyphens and 2 pair of CRLFs
\r
761 // store the body part to string buffer
\r
762 int CMimeBody::Store(char* pszData, int nMaxSize) const
\r
764 // store header fields
\r
765 int nSize = CMimeHeader::Store(pszData, nMaxSize);
\r
770 char* pszDataBegin = pszData; // preserve start position
\r
774 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
775 ASSERT(pCoder != NULL);
\r
776 pCoder->SetInput((const char*)m_pbText, m_nTextSize, true);
\r
777 int nOutput = pCoder->GetOutput((unsigned char*)pszData, nMaxSize);
\r
782 pszData += nOutput;
\r
783 nMaxSize -= nOutput;
\r
784 if (m_listBodies.empty())
\r
785 return (int)(pszData - pszDataBegin);
\r
787 // store child body parts
\r
788 string strBoundary = GetBoundary();
\r
789 if (strBoundary.empty())
\r
790 return -1; // boundary not be set
\r
792 int nBoundSize = (int)strBoundary.size() + 6;
\r
793 for (CBodyList::const_iterator it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
795 if (nMaxSize < nBoundSize)
\r
797 if (m_listBodies.begin() == it && *(pszData-2) == '\r' && *(pszData-1) == '\n')
\r
802 ::sprintf(pszData, "\r\n--%s\r\n", strBoundary.c_str());
\r
803 pszData += nBoundSize;
\r
804 nMaxSize -= nBoundSize;
\r
806 CMimeBody* pBP = *it;
\r
807 ASSERT(pBP != NULL);
\r
808 nOutput = pBP->Store(pszData, nMaxSize);
\r
811 pszData += nOutput;
\r
812 nMaxSize -= nOutput;
\r
815 if (nMaxSize >= nBoundSize+2) // add closing boundary delimiter
\r
817 ::sprintf(pszData, "\r\n--%s--\r\n", strBoundary.c_str());
\r
818 pszData += nBoundSize + 2;
\r
820 return (int)(pszData - pszDataBegin);
\r
823 void CMimeBody::Store(std::string &str) const
\r
825 char *temp=new char[GetLength()+1];
\r
826 Store(temp,GetLength());
\r
827 temp[GetLength()]='\0';
\r
832 // load a body part from string buffer
\r
833 int CMimeBody::Load(const char* pszData, int nDataSize)
\r
835 // load header fields
\r
836 int nSize = CMimeHeader::Load(pszData, nDataSize);
\r
840 const char* pszDataBegin = pszData; // preserve start position
\r
842 nDataSize -= nSize;
\r
845 // determine the length of the content
\r
846 const char* pszEnd = pszData + nDataSize;
\r
847 int nMediaType = GetMediaType();
\r
848 if (MEDIA_MULTIPART == nMediaType)
\r
850 // find the begin boundary
\r
851 string strBoundary = GetBoundary();
\r
852 if (!strBoundary.empty())
\r
854 strBoundary = "\r\n--" + strBoundary;
\r
855 pszEnd = ::FindString(pszData-2, strBoundary.c_str(), pszEnd);
\r
857 pszEnd = pszData + nDataSize;
\r
864 nSize = (int)(pszEnd - pszData);
\r
867 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
868 ASSERT(pCoder != NULL);
\r
869 pCoder->SetInput(pszData, nSize, false);
\r
870 int nOutput = pCoder->GetOutputLength();
\r
871 if (AllocateBuffer(nOutput+4))
\r
872 nOutput = pCoder->GetOutput(m_pbText, nOutput);
\r
879 ASSERT(nOutput < m_nTextSize);
\r
880 m_pbText[nOutput] = 0;
\r
881 m_nTextSize = nOutput;
\r
883 nDataSize -= nSize;
\r
885 if (nDataSize <= 0)
\r
886 return (int)(pszData - pszDataBegin);
\r
888 // load child body parts
\r
889 string strBoundary = GetBoundary();
\r
890 ASSERT(strBoundary.size() > 0);
\r
891 strBoundary = "\r\n--" + strBoundary;
\r
893 // look for the first boundary (case sensitive)
\r
894 pszData -= 2; // go back to CRLF
\r
896 pszEnd = pszData + nDataSize;
\r
897 const char* pszBound1 = ::FindString(pszData, strBoundary.c_str(), pszEnd);
\r
898 while (pszBound1 != NULL && pszBound1 < pszEnd)
\r
900 const char* pszStart = ::FindString(pszBound1+2, "\r\n", pszEnd);
\r
904 if (pszBound1[strBoundary.size()] == '-' && pszBound1[strBoundary.size()+1] == '-')
\r
905 return (int)(pszStart - pszDataBegin); // reach the closing boundary
\r
907 // look for the next boundary
\r
908 const char* pszBound2 = ::FindString(pszStart, strBoundary.c_str(), pszEnd);
\r
909 if (!pszBound2) // overflow, boundary may be truncated
\r
910 pszBound2 = pszEnd;
\r
911 int nEntitySize = (int) (pszBound2 - pszStart);
\r
913 // find the media type of this body part:
\r
914 CMimeHeader header;
\r
915 header.Load(pszStart, nEntitySize);
\r
916 string strMediaType = header.GetMainType();
\r
917 CMimeBody* pBP = CreatePart(strMediaType.c_str());
\r
919 int nInputSize = pBP->Load(pszStart, nEntitySize);
\r
920 if (nInputSize < 0)
\r
925 pszBound1 = pszBound2;
\r
927 return (int)(pszEnd - pszDataBegin);
\r
930 //////////////////////////////////////////////////////////////////////
\r
931 // CMimeMessage - Represents a MIME message
\r
932 //////////////////////////////////////////////////////////////////////
\r
934 void CMimeMessage::SetDate()
\r
936 time_t timeNow = ::time(NULL);
\r
937 struct tm *ptm = ::localtime(&timeNow);
\r
938 SetDate(ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
\r
941 void CMimeMessage::SetDate(int nYear, int nMonth, int nDay, int nHour, int nMinute, int nSecond)
\r
943 static const char* s_MonthNames[] =
\r
944 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
\r
945 static const char* s_DayNames[] =
\r
946 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
\r
949 ::memset(&tmDate, 0, sizeof(tmDate));
\r
950 tmDate.tm_year = nYear - 1900;
\r
951 tmDate.tm_mon = nMonth - 1;
\r
952 tmDate.tm_mday = nDay;
\r
953 tmDate.tm_hour = nHour;
\r
954 tmDate.tm_min = nMinute;
\r
955 tmDate.tm_sec = nSecond;
\r
956 tmDate.tm_isdst = -1;
\r
958 time_t timeDate = ::mktime(&tmDate);
\r
965 tmDate = *::localtime(&timeDate); // adjusted local time
\r
966 struct tm *ptmGmt = ::gmtime(&timeDate); // Greenwich Mean Time
\r
967 long nTimeDiff = tmDate.tm_mday - ptmGmt->tm_mday;
\r
970 else if (nTimeDiff < -1)
\r
972 nTimeDiff *= 60 * 24;
\r
974 (tmDate.tm_hour - ptmGmt->tm_hour) * 60 +
\r
975 tmDate.tm_min - ptmGmt->tm_min;
\r
976 if (tmDate.tm_isdst > 0)
\r
980 ASSERT(tmDate.tm_wday < 7);
\r
981 ASSERT(tmDate.tm_mon < 12);
\r
982 ::sprintf(szDate, "%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
\r
983 s_DayNames[tmDate.tm_wday],
\r
984 tmDate.tm_mday, s_MonthNames[tmDate.tm_mon], tmDate.tm_year+1900,
\r
985 tmDate.tm_hour, tmDate.tm_min, tmDate.tm_sec,
\r
986 (nTimeDiff >= 0 ? '+' : '-'), abs(nTimeDiff / 60), abs(nTimeDiff % 60));
\r
988 SetFieldValue("Date", szDate);
\r