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
17 #define stricmp strcasecmp
\r
18 #define strnicmp strncasecmp
\r
19 #define memicmp memcmp
\r
24 static char THIS_FILE[]=__FILE__;
\r
25 #define new DEBUG_NEW
\r
28 // search for a character in the current line (before CRLF)
\r
29 static const char* LineFind(const char* pszString, int ch)
\r
31 ASSERT(pszString != NULL);
\r
32 while (*pszString != 0 && *pszString != ch && *pszString != '\r' && *pszString != '\n')
\r
34 return *pszString == ch ? pszString : NULL;
\r
37 // search for string2 in string1 (strstr)
\r
38 static const char* FindString(const char* pszStr1, const char* pszStr2, const char* pszEnd)
\r
40 pszEnd -= ::strlen(pszStr2);
\r
41 const char *s1, *s2;
\r
42 while (pszStr1 <= pszEnd)
\r
46 while (*s1 == *s2 && *s2)
\r
55 //////////////////////////////////////////////////////////////////////
\r
56 // CMimeField class - Represents a field of a MIME body part header
\r
57 //////////////////////////////////////////////////////////////////////
\r
59 void CMimeField::GetValue(string& strValue) const
\r
61 string::size_type nEnd = m_strValue.find(';');
\r
62 if (nEnd != string::npos)
\r
64 while (nEnd > 0 && CMimeChar::IsSpace((unsigned char)m_strValue[nEnd-1]))
\r
66 strValue.assign(m_strValue.c_str(), nEnd);
\r
69 strValue = m_strValue;
\r
72 // set a parameter (attribute=value) of the field
\r
73 void CMimeField::SetParameter(const char* pszAttr, const char* pszValue)
\r
75 int nSize = pszValue ? (int)::strlen(pszValue) : 0;
\r
77 strValue.reserve(nSize+3);
\r
78 if (!pszValue || *pszValue != '"')
\r
80 if (pszValue != NULL)
\r
81 strValue += pszValue;
\r
82 if (nSize < 2 || pszValue[nSize-1] != '"')
\r
86 if (!FindParameter(pszAttr, nPos, nSize)) // add new parameter
\r
88 m_strValue.reserve(m_strValue.size() + ::strlen(pszAttr) + strValue.size() + 5);
\r
89 //if (CMimeEnvironment::AutoFolding())
\r
90 // m_strValue += ";\r\n\t";
\r
92 // m_strValue += "; ";
\r
94 m_strValue += pszAttr;
\r
96 m_strValue += strValue;
\r
98 else // update existing parameter
\r
99 m_strValue.replace(nPos, nSize, strValue);
\r
102 // get the value of a parameter
\r
103 bool CMimeField::GetParameter(const char* pszAttr, string& strValue) const
\r
106 if (!FindParameter(pszAttr, nPos, nSize))
\r
112 if (m_strValue[nPos] == '"')
\r
116 if (nSize > 0 && m_strValue[nPos+nSize-1] == '"')
\r
119 strValue.assign(m_strValue.data()+nPos, nSize);
\r
123 int CMimeField::GetLength() const
\r
125 int nLength = (int) m_strName.size() + 4;
\r
126 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
127 pCoder->SetCharset(m_strCharset.c_str());
\r
128 pCoder->SetInput(m_strValue.c_str(), (int)m_strValue.size(), true);
\r
129 nLength += pCoder->GetOutputLength();
\r
134 // store a field to string buffer
\r
135 int CMimeField::Store(char* pszData, int nMaxSize) const
\r
137 ASSERT(pszData != NULL);
\r
138 int nMinSize = (int)m_strName.size() + 4;
\r
139 if (nMaxSize < nMinSize)
\r
141 ::strcpy(pszData, m_strName.c_str());
\r
142 pszData += m_strName.size();
\r
146 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
147 pCoder->SetCharset(m_strCharset.c_str());
\r
148 pCoder->SetInput(m_strValue.c_str(), (int)m_strValue.size(), true);
\r
149 int nEncoded = pCoder->GetOutput((unsigned char*) pszData, nMaxSize-nMinSize);
\r
151 pszData += nEncoded;
\r
155 return nMinSize + nEncoded;
\r
158 // load a field from string buffer
\r
159 int CMimeField::Load(const char* pszData, int nDataSize)
\r
162 ASSERT(pszData != NULL);
\r
163 const char *pszEnd, *pszStart = pszData;
\r
164 // find the next field (e.g. "\r\nContent...")
\r
165 while (CMimeChar::IsSpace((unsigned char)*pszStart))
\r
167 if (*pszStart == '\r') // end of header ?
\r
169 pszStart = ::FindString(pszStart, "\r\n", pszData+nDataSize);
\r
175 // get the field name
\r
176 pszEnd = ::LineFind(pszStart, ':');
\r
177 if (pszEnd != NULL) // if colon not found, Name would be empty
\r
179 m_strName.assign(pszStart, (pszEnd-pszStart));
\r
180 pszStart = pszEnd + 1;
\r
183 // find the end of the field
\r
184 while (*pszStart == ' ' || *pszStart == '\t')
\r
189 pszEnd = ::FindString(pszEnd, "\r\n", pszData+nDataSize);
\r
193 } while (*pszEnd == '\t' || *pszEnd == ' '); // linear-white-space
\r
195 // decode and unfold the field value
\r
196 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
197 pCoder->SetInput(pszStart, (int)(pszEnd-pszStart)-2, false);
\r
198 m_strValue.resize(pCoder->GetOutputLength());
\r
199 int nSize = pCoder->GetOutput((unsigned char*) m_strValue.c_str(), (int) m_strValue.capacity());
\r
200 m_strValue.resize(nSize);
\r
201 m_strCharset = pCoder->GetCharset();
\r
203 return (int) (pszEnd - pszData);
\r
206 bool CMimeField::FindParameter(const char* pszAttr, int& nPos, int& nSize) const
\r
208 ASSERT(pszAttr != NULL);
\r
209 const char* pszParms = ::strchr(m_strValue.data(), ';');
\r
210 int nAttrSize = (int)::strlen(pszAttr);
\r
211 while (pszParms != NULL)
\r
213 while (CMimeChar::IsSpace((unsigned char)*pszParms) || *pszParms == ';')
\r
216 const char* pszName = pszParms; // pszName -> attribute
\r
217 pszParms = ::strchr(pszParms, '=');
\r
221 pszParms++; // pszParams -> parameter value
\r
222 //while (*pszParms == ' ' || *pszParms == '\t')
\r
225 const char* pszParmEnd = NULL;
\r
226 if (*pszParms == '"') // quoted string
\r
227 pszParmEnd = ::strchr(pszParms+1, '"');
\r
228 if (!pszParmEnd) // non quoted string
\r
230 pszParmEnd = pszParms;
\r
231 while (CMimeChar::IsToken(*pszParmEnd))
\r
234 else pszParmEnd++; // pszParmEnd -> end of parameter value
\r
236 if (!::memicmp(pszAttr, pszName, nAttrSize) &&
\r
237 (CMimeChar::IsSpace((unsigned char)pszName[nAttrSize]) || pszName[nAttrSize] == '='))
\r
239 nPos = (int)(pszParms - m_strValue.data());
\r
240 nSize = (int)(pszParmEnd - pszParms);
\r
244 pszParms = pszParmEnd;
\r
249 //////////////////////////////////////////////////////////////////////
\r
250 // CMimeHeader class - Represents the header of a MIME body part
\r
251 //////////////////////////////////////////////////////////////////////
\r
253 // Return the media type represented by Content-Type field (see RFC 2046)
\r
254 CMimeHeader::MediaType CMimeHeader::GetMediaType() const
\r
256 const char* pszType = GetContentType();
\r
261 while (m_TypeTable[nIndex] != NULL &&
\r
262 ::memicmp(pszType, m_TypeTable[nIndex], ::strlen(m_TypeTable[nIndex])) != 0)
\r
264 return (MediaType) nIndex;
\r
267 // get the top-level media type
\r
268 string CMimeHeader::GetMainType() const
\r
271 const char* pszType = GetContentType();
\r
272 if (pszType != NULL)
\r
274 const char* pszSlash = ::strchr(pszType, '/');
\r
275 if (pszSlash != NULL)
\r
276 strType.assign(pszType, pszSlash-pszType);
\r
286 string CMimeHeader::GetSubType() const
\r
289 const CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
293 pfd->GetValue(strType);
\r
294 string::size_type nSlash = strType.find('/');
\r
296 strSubType = strType.substr(nSlash+1);
\r
299 strSubType = "plain";
\r
303 // set the 'charset' parameter (for text) of Content-Type field
\r
304 void CMimeHeader::SetCharset(const char* pszCharset)
\r
306 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
310 fd.SetName(CMimeConst::ContentType());
\r
311 fd.SetValue("text/plain");
\r
312 fd.SetParameter(CMimeConst::Charset(), pszCharset);
\r
313 m_listFields.push_back(fd);
\r
316 pfd->SetParameter(CMimeConst::Charset(), pszCharset);
\r
319 // set the 'name' parameter (for attachment) of Content-Type field
\r
320 void CMimeHeader::SetName(const char* pszName)
\r
322 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
325 // get the appropriate media-type/subtype according to file extension
\r
326 ASSERT(pszName != NULL);
\r
328 const char* pszType = "application/octet-stream";
\r
329 const char* pszFileExt = ::strrchr(pszName, '.');
\r
330 if (pszFileExt != NULL)
\r
334 while (m_TypeCvtTable[nIndex].nMediaType != MEDIA_UNKNOWN)
\r
336 if (!::stricmp(pszFileExt, m_TypeCvtTable[nIndex].pszFileExt))
\r
338 strType = m_TypeTable[m_TypeCvtTable[nIndex].nMediaType];
\r
340 strType += m_TypeCvtTable[nIndex].pszSubType;
\r
341 pszType = strType.c_str();
\r
349 fd.SetName(CMimeConst::ContentType());
\r
350 fd.SetValue(pszType);
\r
351 fd.SetParameter(CMimeConst::Name(), pszName);
\r
352 m_listFields.push_back(fd);
\r
355 pfd->SetParameter(CMimeConst::Name(), pszName);
\r
358 // set 'boundary' parameter (for multipart) of Content-Type field
\r
359 void CMimeHeader::SetBoundary(const char* pszBoundary/*=NULL*/)
\r
361 static int s_nPartNumber = 0;
\r
363 if (!pszBoundary) // generate a new boundary delimeter
\r
365 ::srand(((unsigned)::time(NULL)));// ^ reinterpret_cast<unsigned>(this));
\r
366 ::sprintf(buf, "__=_Part_Boundary_%03d_%06d.%06d", ++s_nPartNumber, rand(), rand());
\r
367 if (s_nPartNumber >= 9)
\r
372 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
376 fd.SetName(CMimeConst::ContentType());
\r
377 fd.SetValue("multipart/mixed");
\r
378 fd.SetParameter(CMimeConst::Boundary(), pszBoundary);
\r
379 m_listFields.push_back(fd);
\r
383 if (::memicmp(pfd->GetValue(), "multipart", 9) != 0)
\r
384 pfd->SetValue("multipart/mixed");
\r
385 pfd->SetParameter(CMimeConst::Boundary(), pszBoundary);
\r
389 void CMimeHeader::Clear()
\r
391 m_listFields.clear();
\r
394 // return the length needed to store this header to string buffer
\r
395 int CMimeHeader::GetLength() const
\r
398 list<CMimeField>::const_iterator it;
\r
399 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
400 nLength += (*it).GetLength();
\r
401 return nLength + 2; // a pair of CRLF indicate the end of header
\r
404 // store the header to string buffer
\r
405 int CMimeHeader::Store(char* pszData, int nMaxSize) const
\r
407 ASSERT(pszData != NULL);
\r
409 list<CMimeField>::const_iterator it;
\r
410 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
412 const CMimeField& fd = *it;
\r
413 int nSize = fd.Store(pszData+nOutput, nMaxSize-nOutput);
\r
419 pszData[nOutput++] = '\r'; // add CRLF indicating the end of header
\r
420 pszData[nOutput++] = '\n';
\r
424 // load a header from string buffer
\r
425 int CMimeHeader::Load(const char* pszData, int nDataSize)
\r
427 ASSERT(pszData != NULL);
\r
429 while (pszData[nInput] != 0 && pszData[nInput] != '\r')
\r
432 int nSize = fd.Load(pszData+nInput, nDataSize-nInput);
\r
437 m_listFields.push_back(fd); // don't use SetField in case of same name fields
\r
440 return nInput + 2; // skip the ending CRLF
\r
443 list<CMimeField>::const_iterator CMimeHeader::FindField(const char* pszFieldName) const
\r
445 list<CMimeField>::const_iterator it;
\r
446 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
448 const CMimeField& fd = *it;
\r
449 if (!::stricmp(fd.GetName(), pszFieldName))
\r
455 list<CMimeField>::iterator CMimeHeader::FindField(const char* pszFieldName)
\r
457 list<CMimeField>::iterator it;
\r
458 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
460 CMimeField& fd = *it;
\r
461 if (!::stricmp(fd.GetName(), pszFieldName))
\r
467 //////////////////////////////////////////////////////////////////////
\r
468 // CMimeBody class - Represents a body part in a MIME message
\r
469 //////////////////////////////////////////////////////////////////////
\r
471 #include <sys/types.h>
\r
472 #include <sys/stat.h>
\r
477 #if !defined(__APPLE__) && !defined(__DARWIN__)
\r
478 #if !defined(__FreeBSD__) && !defined(solaris) && !defined(__sun)
\r
479 #include <sys/io.h>
\r
490 // initialize the content with text
\r
491 int CMimeBody::SetText(const char* pbText, int nLength/*=0*/)
\r
493 ASSERT(pbText != NULL);
\r
495 nLength = (int)::strlen((char*)pbText);
\r
497 if (!AllocateBuffer(nLength+4))
\r
500 ::memcpy(m_pbText, pbText, nLength);
\r
501 m_pbText[nLength] = 0;
\r
502 m_nTextSize = nLength;
\r
506 int CMimeBody::GetText(char* pbText, int nMaxSize)
\r
508 int nSize = min(nMaxSize, m_nTextSize);
\r
509 if (m_pbText != NULL)
\r
510 ::memcpy(pbText, m_pbText, nSize);
\r
514 int CMimeBody::GetText(string& strText)
\r
516 if (m_pbText != NULL)
\r
517 strText.assign((const char*) m_pbText, m_nTextSize);
\r
518 return m_nTextSize;
\r
521 // initialize the content of this body part with a mail message
\r
522 bool CMimeBody::SetMessage(const CMimeMessage* pMM)
\r
524 ASSERT(pMM != NULL);
\r
525 int nSize = pMM->GetLength();
\r
526 if (!AllocateBuffer(nSize+4))
\r
529 nSize = pMM->Store((char*)m_pbText, nSize);
\r
530 m_pbText[nSize] = 0;
\r
531 m_nTextSize = nSize;
\r
533 const char* pszType = GetContentType();
\r
534 if (!pszType || ::memicmp(pszType, "message", 7) != 0)
\r
535 SetContentType("message/rfc822");
\r
536 //SetTransferEncoding(CMimeConst::EncodingBinary()); // in case the default 7bit cause folding
\r
540 void CMimeBody::GetMessage(CMimeMessage* pMM) const
\r
542 ASSERT(pMM != NULL);
\r
543 ASSERT(m_pbText != NULL);
\r
544 pMM->Load((const char*)m_pbText, m_nTextSize);
\r
547 // initialize the content (attachment) by reading from a file
\r
548 bool CMimeBody::ReadFromFile(const char* pszFilename)
\r
550 int hFile = ::open(pszFilename, O_RDONLY | O_BINARY);
\r
556 int nFileSize = (int)::lseek(hFile, 0L, SEEK_END); // get file length
\r
557 ::lseek(hFile, 0L, SEEK_SET);
\r
562 AllocateBuffer(nFileSize+4);
\r
563 unsigned char* pszData = m_pbText;
\r
567 int nRead = ::read(hFile, pszData, 512);
\r
578 m_nTextSize = nFileSize;
\r
588 const char* pszName = ::strrchr(pszFilename, '\\');
\r
590 pszName = pszFilename;
\r
593 SetName(pszName); // set 'name' parameter:
\r
597 // write the content (attachment) to a file
\r
598 bool CMimeBody::WriteToFile(const char* pszFilename)
\r
602 int hFile = ::open(pszFilename, O_CREAT | O_TRUNC | O_RDWR | O_BINARY, S_IREAD | S_IWRITE);
\r
606 const unsigned char* pszData = m_pbText;
\r
607 int nLeft = m_nTextSize;
\r
613 int nWritten = ::write(hFile, pszData, min(512, nLeft));
\r
619 pszData += nWritten;
\r
635 // delete all child body parts
\r
636 void CMimeBody::DeleteAll()
\r
638 while (!m_listBodies.empty())
\r
640 CMimeBody* pBP = m_listBodies.back();
\r
641 m_listBodies.pop_back();
\r
642 ASSERT(pBP != NULL);
\r
643 delete pBP; // surely delete because it was allocated by CreatePart()
\r
647 // create a new child body part, and add it to body part list
\r
648 CMimeBody* CMimeBody::CreatePart(const char* pszMediaType/*=NULL*/, CMimeBody* pWhere/*=NULL*/)
\r
650 CMimeBody* pBP = CMimeEnvironment::CreateBodyPart(pszMediaType);
\r
651 ASSERT(pBP != NULL);
\r
652 if (pWhere != NULL)
\r
654 for (CBodyList::iterator it = m_listBodies.begin(); it != m_listBodies.end(); it++)
\r
657 m_listBodies.insert(it, pBP);
\r
661 m_listBodies.push_back(pBP);
\r
665 // remove and delete a child body part
\r
666 void CMimeBody::ErasePart(CMimeBody* pBP)
\r
668 ASSERT(pBP != NULL);
\r
669 m_listBodies.remove(pBP);
\r
673 // return a list of all child body parts belong to this body part
\r
674 int CMimeBody::GetBodyPartList(CBodyList& rList) const
\r
677 int nMediaType = GetMediaType();
\r
679 if (MEDIA_MULTIPART != nMediaType)
\r
681 rList.push_back((CMimeBody*)this);
\r
686 list<CMimeBody*>::const_iterator it;
\r
687 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
689 CMimeBody* pBP = *it;
\r
690 ASSERT(pBP != NULL);
\r
691 nCount += pBP->GetBodyPartList(rList);
\r
697 // return a list of all attachment body parts belong to this body part
\r
698 int CMimeBody::GetAttachmentList(CBodyList& rList) const
\r
701 int nMediaType = GetMediaType();
\r
703 if (MEDIA_MULTIPART != nMediaType)
\r
705 string strName = GetName();
\r
706 if (strName.size() > 0)
\r
708 rList.push_back((CMimeBody*)this);
\r
714 list<CMimeBody*>::const_iterator it;
\r
715 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
717 CMimeBody* pBP = *it;
\r
718 ASSERT(pBP != NULL);
\r
719 nCount += pBP->GetAttachmentList(rList);
\r
725 void CMimeBody::Clear()
\r
728 m_itFind = m_listBodies.end();
\r
730 CMimeHeader::Clear();
\r
733 // return the length needed to store this body part to string buffer
\r
734 int CMimeBody::GetLength() const
\r
736 int nLength = CMimeHeader::GetLength();
\r
737 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
738 ASSERT(pCoder != NULL);
\r
739 pCoder->SetInput((const char*)m_pbText, m_nTextSize, true);
\r
740 nLength += pCoder->GetOutputLength();
\r
743 if (m_listBodies.empty())
\r
746 string strBoundary = GetBoundary();
\r
747 int nBoundSize = (int) strBoundary.size();
\r
748 list<CMimeBody*>::const_iterator it;
\r
749 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
751 nLength += nBoundSize + 6; // include 2 leading hyphens and 2 pair of CRLFs
\r
752 CMimeBody* pBP = *it;
\r
753 ASSERT(pBP != NULL);
\r
754 nLength += pBP->GetLength();
\r
756 nLength += nBoundSize + 8; // include 2 leading hyphens, 2 trailng hyphens and 2 pair of CRLFs
\r
760 // store the body part to string buffer
\r
761 int CMimeBody::Store(char* pszData, int nMaxSize) const
\r
763 // store header fields
\r
764 int nSize = CMimeHeader::Store(pszData, nMaxSize);
\r
769 char* pszDataBegin = pszData; // preserve start position
\r
773 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
774 ASSERT(pCoder != NULL);
\r
775 pCoder->SetInput((const char*)m_pbText, m_nTextSize, true);
\r
776 int nOutput = pCoder->GetOutput((unsigned char*)pszData, nMaxSize);
\r
781 pszData += nOutput;
\r
782 nMaxSize -= nOutput;
\r
783 if (m_listBodies.empty())
\r
784 return (int)(pszData - pszDataBegin);
\r
786 // store child body parts
\r
787 string strBoundary = GetBoundary();
\r
788 if (strBoundary.empty())
\r
789 return -1; // boundary not be set
\r
791 int nBoundSize = (int)strBoundary.size() + 6;
\r
792 for (CBodyList::const_iterator it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
794 if (nMaxSize < nBoundSize)
\r
796 if (m_listBodies.begin() == it && *(pszData-2) == '\r' && *(pszData-1) == '\n')
\r
801 ::sprintf(pszData, "\r\n--%s\r\n", strBoundary.c_str());
\r
802 pszData += nBoundSize;
\r
803 nMaxSize -= nBoundSize;
\r
805 CMimeBody* pBP = *it;
\r
806 ASSERT(pBP != NULL);
\r
807 nOutput = pBP->Store(pszData, nMaxSize);
\r
810 pszData += nOutput;
\r
811 nMaxSize -= nOutput;
\r
814 if (nMaxSize >= nBoundSize+2) // add closing boundary delimiter
\r
816 ::sprintf(pszData, "\r\n--%s--\r\n", strBoundary.c_str());
\r
817 pszData += nBoundSize + 2;
\r
819 return (int)(pszData - pszDataBegin);
\r
822 void CMimeBody::Store(std::string &str) const
\r
824 char *temp=new char[GetLength()+1];
\r
825 Store(temp,GetLength());
\r
826 temp[GetLength()]='\0';
\r
831 // load a body part from string buffer
\r
832 int CMimeBody::Load(const char* pszData, int nDataSize)
\r
834 // load header fields
\r
835 int nSize = CMimeHeader::Load(pszData, nDataSize);
\r
839 const char* pszDataBegin = pszData; // preserve start position
\r
841 nDataSize -= nSize;
\r
844 // determine the length of the content
\r
845 const char* pszEnd = pszData + nDataSize;
\r
846 int nMediaType = GetMediaType();
\r
847 if (MEDIA_MULTIPART == nMediaType)
\r
849 // find the begin boundary
\r
850 string strBoundary = GetBoundary();
\r
851 if (!strBoundary.empty())
\r
853 strBoundary = "\r\n--" + strBoundary;
\r
854 pszEnd = ::FindString(pszData-2, strBoundary.c_str(), pszEnd);
\r
856 pszEnd = pszData + nDataSize;
\r
863 nSize = (int)(pszEnd - pszData);
\r
866 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
867 ASSERT(pCoder != NULL);
\r
868 pCoder->SetInput(pszData, nSize, false);
\r
869 int nOutput = pCoder->GetOutputLength();
\r
870 if (AllocateBuffer(nOutput+4))
\r
871 nOutput = pCoder->GetOutput(m_pbText, nOutput);
\r
878 ASSERT(nOutput < m_nTextSize);
\r
879 m_pbText[nOutput] = 0;
\r
880 m_nTextSize = nOutput;
\r
882 nDataSize -= nSize;
\r
884 if (nDataSize <= 0)
\r
885 return (int)(pszData - pszDataBegin);
\r
887 // load child body parts
\r
888 string strBoundary = GetBoundary();
\r
889 ASSERT(strBoundary.size() > 0);
\r
890 strBoundary = "\r\n--" + strBoundary;
\r
892 // look for the first boundary (case sensitive)
\r
893 pszData -= 2; // go back to CRLF
\r
895 pszEnd = pszData + nDataSize;
\r
896 const char* pszBound1 = ::FindString(pszData, strBoundary.c_str(), pszEnd);
\r
897 while (pszBound1 != NULL && pszBound1 < pszEnd)
\r
899 const char* pszStart = ::FindString(pszBound1+2, "\r\n", pszEnd);
\r
903 if (pszBound1[strBoundary.size()] == '-' && pszBound1[strBoundary.size()+1] == '-')
\r
904 return (int)(pszStart - pszDataBegin); // reach the closing boundary
\r
906 // look for the next boundary
\r
907 const char* pszBound2 = ::FindString(pszStart, strBoundary.c_str(), pszEnd);
\r
908 if (!pszBound2) // overflow, boundary may be truncated
\r
909 pszBound2 = pszEnd;
\r
910 int nEntitySize = (int) (pszBound2 - pszStart);
\r
912 // find the media type of this body part:
\r
913 CMimeHeader header;
\r
914 header.Load(pszStart, nEntitySize);
\r
915 string strMediaType = header.GetMainType();
\r
916 CMimeBody* pBP = CreatePart(strMediaType.c_str());
\r
918 int nInputSize = pBP->Load(pszStart, nEntitySize);
\r
919 if (nInputSize < 0)
\r
924 pszBound1 = pszBound2;
\r
926 return (int)(pszEnd - pszDataBegin);
\r
929 //////////////////////////////////////////////////////////////////////
\r
930 // CMimeMessage - Represents a MIME message
\r
931 //////////////////////////////////////////////////////////////////////
\r
933 void CMimeMessage::SetDate()
\r
935 time_t timeNow = ::time(NULL);
\r
936 struct tm *ptm = ::localtime(&timeNow);
\r
937 SetDate(ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
\r
940 void CMimeMessage::SetDate(int nYear, int nMonth, int nDay, int nHour, int nMinute, int nSecond)
\r
942 static const char* s_MonthNames[] =
\r
943 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
\r
944 static const char* s_DayNames[] =
\r
945 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
\r
948 ::memset(&tmDate, 0, sizeof(tmDate));
\r
949 tmDate.tm_year = nYear - 1900;
\r
950 tmDate.tm_mon = nMonth - 1;
\r
951 tmDate.tm_mday = nDay;
\r
952 tmDate.tm_hour = nHour;
\r
953 tmDate.tm_min = nMinute;
\r
954 tmDate.tm_sec = nSecond;
\r
955 tmDate.tm_isdst = -1;
\r
957 time_t timeDate = ::mktime(&tmDate);
\r
964 tmDate = *::localtime(&timeDate); // adjusted local time
\r
965 struct tm *ptmGmt = ::gmtime(&timeDate); // Greenwich Mean Time
\r
966 long nTimeDiff = tmDate.tm_mday - ptmGmt->tm_mday;
\r
969 else if (nTimeDiff < -1)
\r
971 nTimeDiff *= 60 * 24;
\r
973 (tmDate.tm_hour - ptmGmt->tm_hour) * 60 +
\r
974 tmDate.tm_min - ptmGmt->tm_min;
\r
975 if (tmDate.tm_isdst > 0)
\r
979 ASSERT(tmDate.tm_wday < 7);
\r
980 ASSERT(tmDate.tm_mon < 12);
\r
981 ::sprintf(szDate, "%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
\r
982 s_DayNames[tmDate.tm_wday],
\r
983 tmDate.tm_mday, s_MonthNames[tmDate.tm_mon], tmDate.tm_year+1900,
\r
984 tmDate.tm_hour, tmDate.tm_min, tmDate.tm_sec,
\r
985 (nTimeDiff >= 0 ? '+' : '-'), abs(nTimeDiff / 60), abs(nTimeDiff % 60));
\r
987 SetFieldValue("Date", szDate);
\r