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 static char THIS_FILE[]=__FILE__;
\r
19 #define new DEBUG_NEW
\r
22 // search for a character in the current line (before CRLF)
\r
23 static const char* LineFind(const char* pszString, int ch)
\r
25 ASSERT(pszString != NULL);
\r
26 while (*pszString != 0 && *pszString != ch && *pszString != '\r' && *pszString != '\n')
\r
28 return *pszString == ch ? pszString : NULL;
\r
31 // search for string2 in string1 (strstr)
\r
32 static const char* FindString(const char* pszStr1, const char* pszStr2, const char* pszEnd)
\r
34 pszEnd -= ::strlen(pszStr2);
\r
35 const char *s1, *s2;
\r
36 while (pszStr1 <= pszEnd)
\r
40 while (*s1 == *s2 && *s2)
\r
49 //////////////////////////////////////////////////////////////////////
\r
50 // CMimeField class - Represents a field of a MIME body part header
\r
51 //////////////////////////////////////////////////////////////////////
\r
53 void CMimeField::GetValue(string& strValue) const
\r
55 string::size_type nEnd = m_strValue.find(';');
\r
56 if (nEnd != string::npos)
\r
58 while (nEnd > 0 && CMimeChar::IsSpace((unsigned char)m_strValue[nEnd-1]))
\r
60 strValue.assign(m_strValue.c_str(), nEnd);
\r
63 strValue = m_strValue;
\r
66 // set a parameter (attribute=value) of the field
\r
67 void CMimeField::SetParameter(const char* pszAttr, const char* pszValue)
\r
69 int nSize = pszValue ? (int)::strlen(pszValue) : 0;
\r
71 strValue.reserve(nSize+3);
\r
72 if (!pszValue || *pszValue != '"')
\r
74 if (pszValue != NULL)
\r
75 strValue += pszValue;
\r
76 if (nSize < 2 || pszValue[nSize-1] != '"')
\r
80 if (!FindParameter(pszAttr, nPos, nSize)) // add new parameter
\r
82 m_strValue.reserve(m_strValue.size() + ::strlen(pszAttr) + strValue.size() + 5);
\r
83 //if (CMimeEnvironment::AutoFolding())
\r
84 // m_strValue += ";\r\n\t";
\r
86 // m_strValue += "; ";
\r
88 m_strValue += pszAttr;
\r
90 m_strValue += strValue;
\r
92 else // update existing parameter
\r
93 m_strValue.replace(nPos, nSize, strValue);
\r
96 // get the value of a parameter
\r
97 bool CMimeField::GetParameter(const char* pszAttr, string& strValue) const
\r
100 if (!FindParameter(pszAttr, nPos, nSize))
\r
106 if (m_strValue[nPos] == '"')
\r
110 if (nSize > 0 && m_strValue[nPos+nSize-1] == '"')
\r
113 strValue.assign(m_strValue.data()+nPos, nSize);
\r
117 int CMimeField::GetLength() const
\r
119 int nLength = (int) m_strName.size() + 4;
\r
120 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
121 pCoder->SetCharset(m_strCharset.c_str());
\r
122 pCoder->SetInput(m_strValue.c_str(), (int)m_strValue.size(), true);
\r
123 nLength += pCoder->GetOutputLength();
\r
128 // store a field to string buffer
\r
129 int CMimeField::Store(char* pszData, int nMaxSize) const
\r
131 ASSERT(pszData != NULL);
\r
132 int nMinSize = (int)m_strName.size() + 4;
\r
133 if (nMaxSize < nMinSize)
\r
135 ::strcpy(pszData, m_strName.c_str());
\r
136 pszData += m_strName.size();
\r
140 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
141 pCoder->SetCharset(m_strCharset.c_str());
\r
142 pCoder->SetInput(m_strValue.c_str(), (int)m_strValue.size(), true);
\r
143 int nEncoded = pCoder->GetOutput((unsigned char*) pszData, nMaxSize-nMinSize);
\r
145 pszData += nEncoded;
\r
149 return nMinSize + nEncoded;
\r
152 // load a field from string buffer
\r
153 int CMimeField::Load(const char* pszData, int nDataSize)
\r
156 ASSERT(pszData != NULL);
\r
157 const char *pszEnd, *pszStart = pszData;
\r
158 // find the next field (e.g. "\r\nContent...")
\r
159 while (CMimeChar::IsSpace((unsigned char)*pszStart))
\r
161 if (*pszStart == '\r') // end of header ?
\r
163 pszStart = ::FindString(pszStart, "\r\n", pszData+nDataSize);
\r
169 // get the field name
\r
170 pszEnd = ::LineFind(pszStart, ':');
\r
171 if (pszEnd != NULL) // if colon not found, Name would be empty
\r
173 m_strName.assign(pszStart, (pszEnd-pszStart));
\r
174 pszStart = pszEnd + 1;
\r
177 // find the end of the field
\r
178 while (*pszStart == ' ' || *pszStart == '\t')
\r
183 pszEnd = ::FindString(pszEnd, "\r\n", pszData+nDataSize);
\r
187 } while (*pszEnd == '\t' || *pszEnd == ' '); // linear-white-space
\r
189 // decode and unfold the field value
\r
190 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
191 pCoder->SetInput(pszStart, (int)(pszEnd-pszStart)-2, false);
\r
192 m_strValue.resize(pCoder->GetOutputLength());
\r
193 int nSize = pCoder->GetOutput((unsigned char*) m_strValue.c_str(), (int) m_strValue.capacity());
\r
194 m_strValue.resize(nSize);
\r
195 m_strCharset = pCoder->GetCharset();
\r
197 return (int) (pszEnd - pszData);
\r
200 bool CMimeField::FindParameter(const char* pszAttr, int& nPos, int& nSize) const
\r
202 ASSERT(pszAttr != NULL);
\r
203 const char* pszParms = ::strchr(m_strValue.data(), ';');
\r
204 int nAttrSize = (int)::strlen(pszAttr);
\r
205 while (pszParms != NULL)
\r
207 while (CMimeChar::IsSpace((unsigned char)*pszParms) || *pszParms == ';')
\r
210 const char* pszName = pszParms; // pszName -> attribute
\r
211 pszParms = ::strchr(pszParms, '=');
\r
215 pszParms++; // pszParams -> parameter value
\r
216 //while (*pszParms == ' ' || *pszParms == '\t')
\r
219 const char* pszParmEnd = NULL;
\r
220 if (*pszParms == '"') // quoted string
\r
221 pszParmEnd = ::strchr(pszParms+1, '"');
\r
222 if (!pszParmEnd) // non quoted string
\r
224 pszParmEnd = pszParms;
\r
225 while (CMimeChar::IsToken(*pszParmEnd))
\r
228 else pszParmEnd++; // pszParmEnd -> end of parameter value
\r
230 if (!::memicmp(pszAttr, pszName, nAttrSize) &&
\r
231 (CMimeChar::IsSpace((unsigned char)pszName[nAttrSize]) || pszName[nAttrSize] == '='))
\r
233 nPos = (int)(pszParms - m_strValue.data());
\r
234 nSize = (int)(pszParmEnd - pszParms);
\r
238 pszParms = pszParmEnd;
\r
243 //////////////////////////////////////////////////////////////////////
\r
244 // CMimeHeader class - Represents the header of a MIME body part
\r
245 //////////////////////////////////////////////////////////////////////
\r
247 // Return the media type represented by Content-Type field (see RFC 2046)
\r
248 CMimeHeader::MediaType CMimeHeader::GetMediaType() const
\r
250 const char* pszType = GetContentType();
\r
255 while (m_TypeTable[nIndex] != NULL &&
\r
256 ::memicmp(pszType, m_TypeTable[nIndex], ::strlen(m_TypeTable[nIndex])) != 0)
\r
258 return (MediaType) nIndex;
\r
261 // get the top-level media type
\r
262 string CMimeHeader::GetMainType() const
\r
265 const char* pszType = GetContentType();
\r
266 if (pszType != NULL)
\r
268 const char* pszSlash = ::strchr(pszType, '/');
\r
269 if (pszSlash != NULL)
\r
270 strType.assign(pszType, pszSlash-pszType);
\r
280 string CMimeHeader::GetSubType() const
\r
283 const CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
287 pfd->GetValue(strType);
\r
288 string::size_type nSlash = strType.find('/');
\r
290 strSubType = strType.substr(nSlash+1);
\r
293 strSubType = "plain";
\r
297 // set the 'charset' parameter (for text) of Content-Type field
\r
298 void CMimeHeader::SetCharset(const char* pszCharset)
\r
300 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
304 fd.SetName(CMimeConst::ContentType());
\r
305 fd.SetValue("text/plain");
\r
306 fd.SetParameter(CMimeConst::Charset(), pszCharset);
\r
307 m_listFields.push_back(fd);
\r
310 pfd->SetParameter(CMimeConst::Charset(), pszCharset);
\r
313 // set the 'name' parameter (for attachment) of Content-Type field
\r
314 void CMimeHeader::SetName(const char* pszName)
\r
316 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
319 // get the appropriate media-type/subtype according to file extension
\r
320 ASSERT(pszName != NULL);
\r
322 const char* pszType = "application/octet-stream";
\r
323 const char* pszFileExt = ::strrchr(pszName, '.');
\r
324 if (pszFileExt != NULL)
\r
328 while (m_TypeCvtTable[nIndex].nMediaType != MEDIA_UNKNOWN)
\r
330 if (!::stricmp(pszFileExt, m_TypeCvtTable[nIndex].pszFileExt))
\r
332 strType = m_TypeTable[m_TypeCvtTable[nIndex].nMediaType];
\r
334 strType += m_TypeCvtTable[nIndex].pszSubType;
\r
335 pszType = strType.c_str();
\r
343 fd.SetName(CMimeConst::ContentType());
\r
344 fd.SetValue(pszType);
\r
345 fd.SetParameter(CMimeConst::Name(), pszName);
\r
346 m_listFields.push_back(fd);
\r
349 pfd->SetParameter(CMimeConst::Name(), pszName);
\r
352 // set 'boundary' parameter (for multipart) of Content-Type field
\r
353 void CMimeHeader::SetBoundary(const char* pszBoundary/*=NULL*/)
\r
355 static int s_nPartNumber = 0;
\r
357 if (!pszBoundary) // generate a new boundary delimeter
\r
359 ::srand(((unsigned)::time(NULL)) ^ (unsigned)this);
\r
360 ::sprintf(buf, "__=_Part_Boundary_%03d_%06d.%06d", ++s_nPartNumber, rand(), rand());
\r
361 if (s_nPartNumber >= 9)
\r
366 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
370 fd.SetName(CMimeConst::ContentType());
\r
371 fd.SetValue("multipart/mixed");
\r
372 fd.SetParameter(CMimeConst::Boundary(), pszBoundary);
\r
373 m_listFields.push_back(fd);
\r
377 if (::memicmp(pfd->GetValue(), "multipart", 9) != 0)
\r
378 pfd->SetValue("multipart/mixed");
\r
379 pfd->SetParameter(CMimeConst::Boundary(), pszBoundary);
\r
383 void CMimeHeader::Clear()
\r
385 m_listFields.clear();
\r
388 // return the length needed to store this header to string buffer
\r
389 int CMimeHeader::GetLength() const
\r
392 list<CMimeField>::const_iterator it;
\r
393 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
394 nLength += (*it).GetLength();
\r
395 return nLength + 2; // a pair of CRLF indicate the end of header
\r
398 // store the header to string buffer
\r
399 int CMimeHeader::Store(char* pszData, int nMaxSize) const
\r
401 ASSERT(pszData != NULL);
\r
403 list<CMimeField>::const_iterator it;
\r
404 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
406 const CMimeField& fd = *it;
\r
407 int nSize = fd.Store(pszData+nOutput, nMaxSize-nOutput);
\r
413 pszData[nOutput++] = '\r'; // add CRLF indicating the end of header
\r
414 pszData[nOutput++] = '\n';
\r
418 // load a header from string buffer
\r
419 int CMimeHeader::Load(const char* pszData, int nDataSize)
\r
421 ASSERT(pszData != NULL);
\r
423 while (pszData[nInput] != 0 && pszData[nInput] != '\r')
\r
426 int nSize = fd.Load(pszData+nInput, nDataSize-nInput);
\r
431 m_listFields.push_back(fd); // don't use SetField in case of same name fields
\r
434 return nInput + 2; // skip the ending CRLF
\r
437 list<CMimeField>::const_iterator CMimeHeader::FindField(const char* pszFieldName) const
\r
439 list<CMimeField>::const_iterator it;
\r
440 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
442 const CMimeField& fd = *it;
\r
443 if (!::stricmp(fd.GetName(), pszFieldName))
\r
449 list<CMimeField>::iterator CMimeHeader::FindField(const char* pszFieldName)
\r
451 list<CMimeField>::iterator it;
\r
452 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
454 CMimeField& fd = *it;
\r
455 if (!::stricmp(fd.GetName(), pszFieldName))
\r
461 //////////////////////////////////////////////////////////////////////
\r
462 // CMimeBody class - Represents a body part in a MIME message
\r
463 //////////////////////////////////////////////////////////////////////
\r
465 #include <sys/types.h>
\r
466 #include <sys/stat.h>
\r
469 // initialize the content with text
\r
470 int CMimeBody::SetText(const char* pbText, int nLength/*=0*/)
\r
472 ASSERT(pbText != NULL);
\r
474 nLength = (int)::strlen((char*)pbText);
\r
476 if (!AllocateBuffer(nLength+4))
\r
479 ::memcpy(m_pbText, pbText, nLength);
\r
480 m_pbText[nLength] = 0;
\r
481 m_nTextSize = nLength;
\r
485 int CMimeBody::GetText(char* pbText, int nMaxSize)
\r
487 int nSize = min(nMaxSize, m_nTextSize);
\r
488 if (m_pbText != NULL)
\r
489 ::memcpy(pbText, m_pbText, nSize);
\r
493 int CMimeBody::GetText(string& strText)
\r
495 if (m_pbText != NULL)
\r
496 strText.assign((const char*) m_pbText, m_nTextSize);
\r
497 return m_nTextSize;
\r
500 // initialize the content of this body part with a mail message
\r
501 bool CMimeBody::SetMessage(const CMimeMessage* pMM)
\r
503 ASSERT(pMM != NULL);
\r
504 int nSize = pMM->GetLength();
\r
505 if (!AllocateBuffer(nSize+4))
\r
508 nSize = pMM->Store((char*)m_pbText, nSize);
\r
509 m_pbText[nSize] = 0;
\r
510 m_nTextSize = nSize;
\r
512 const char* pszType = GetContentType();
\r
513 if (!pszType || ::memicmp(pszType, "message", 7) != 0)
\r
514 SetContentType("message/rfc822");
\r
515 //SetTransferEncoding(CMimeConst::EncodingBinary()); // in case the default 7bit cause folding
\r
519 void CMimeBody::GetMessage(CMimeMessage* pMM) const
\r
521 ASSERT(pMM != NULL);
\r
522 ASSERT(m_pbText != NULL);
\r
523 pMM->Load((const char*)m_pbText, m_nTextSize);
\r
526 // initialize the content (attachment) by reading from a file
\r
527 bool CMimeBody::ReadFromFile(const char* pszFilename)
\r
529 int hFile = ::open(pszFilename, O_RDONLY | O_BINARY);
\r
535 int nFileSize = (int)::lseek(hFile, 0L, SEEK_END); // get file length
\r
536 ::lseek(hFile, 0L, SEEK_SET);
\r
541 AllocateBuffer(nFileSize+4);
\r
542 unsigned char* pszData = m_pbText;
\r
546 int nRead = ::read(hFile, pszData, 512);
\r
557 m_nTextSize = nFileSize;
\r
567 const char* pszName = ::strrchr(pszFilename, '\\');
\r
569 pszName = pszFilename;
\r
572 SetName(pszName); // set 'name' parameter:
\r
576 // write the content (attachment) to a file
\r
577 bool CMimeBody::WriteToFile(const char* pszFilename)
\r
581 int hFile = ::open(pszFilename, O_CREAT | O_TRUNC | O_RDWR | O_BINARY, S_IREAD | S_IWRITE);
\r
585 const unsigned char* pszData = m_pbText;
\r
586 int nLeft = m_nTextSize;
\r
592 int nWritten = ::write(hFile, pszData, min(512, nLeft));
\r
598 pszData += nWritten;
\r
614 // delete all child body parts
\r
615 void CMimeBody::DeleteAll()
\r
617 while (!m_listBodies.empty())
\r
619 CMimeBody* pBP = m_listBodies.back();
\r
620 m_listBodies.pop_back();
\r
621 ASSERT(pBP != NULL);
\r
622 delete pBP; // surely delete because it was allocated by CreatePart()
\r
626 // create a new child body part, and add it to body part list
\r
627 CMimeBody* CMimeBody::CreatePart(const char* pszMediaType/*=NULL*/, CMimeBody* pWhere/*=NULL*/)
\r
629 CMimeBody* pBP = CMimeEnvironment::CreateBodyPart(pszMediaType);
\r
630 ASSERT(pBP != NULL);
\r
631 if (pWhere != NULL)
\r
633 for (CBodyList::iterator it = m_listBodies.begin(); it != m_listBodies.end(); it++)
\r
636 m_listBodies.insert(it, pBP);
\r
640 m_listBodies.push_back(pBP);
\r
644 // remove and delete a child body part
\r
645 void CMimeBody::ErasePart(CMimeBody* pBP)
\r
647 ASSERT(pBP != NULL);
\r
648 m_listBodies.remove(pBP);
\r
652 // return a list of all child body parts belong to this body part
\r
653 int CMimeBody::GetBodyPartList(CBodyList& rList) const
\r
656 int nMediaType = GetMediaType();
\r
658 if (MEDIA_MULTIPART != nMediaType)
\r
660 rList.push_back((CMimeBody*)this);
\r
665 list<CMimeBody*>::const_iterator it;
\r
666 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
668 CMimeBody* pBP = *it;
\r
669 ASSERT(pBP != NULL);
\r
670 nCount += pBP->GetBodyPartList(rList);
\r
676 // return a list of all attachment body parts belong to this body part
\r
677 int CMimeBody::GetAttachmentList(CBodyList& rList) const
\r
680 int nMediaType = GetMediaType();
\r
682 if (MEDIA_MULTIPART != nMediaType)
\r
684 string strName = GetName();
\r
685 if (strName.size() > 0)
\r
687 rList.push_back((CMimeBody*)this);
\r
693 list<CMimeBody*>::const_iterator it;
\r
694 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
696 CMimeBody* pBP = *it;
\r
697 ASSERT(pBP != NULL);
\r
698 nCount += pBP->GetAttachmentList(rList);
\r
704 void CMimeBody::Clear()
\r
707 m_itFind = m_listBodies.end();
\r
709 CMimeHeader::Clear();
\r
712 // return the length needed to store this body part to string buffer
\r
713 int CMimeBody::GetLength() const
\r
715 int nLength = CMimeHeader::GetLength();
\r
716 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
717 ASSERT(pCoder != NULL);
\r
718 pCoder->SetInput((const char*)m_pbText, m_nTextSize, true);
\r
719 nLength += pCoder->GetOutputLength();
\r
722 if (m_listBodies.empty())
\r
725 string strBoundary = GetBoundary();
\r
726 int nBoundSize = (int) strBoundary.size();
\r
727 list<CMimeBody*>::const_iterator it;
\r
728 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
730 nLength += nBoundSize + 6; // include 2 leading hyphens and 2 pair of CRLFs
\r
731 CMimeBody* pBP = *it;
\r
732 ASSERT(pBP != NULL);
\r
733 nLength += pBP->GetLength();
\r
735 nLength += nBoundSize + 8; // include 2 leading hyphens, 2 trailng hyphens and 2 pair of CRLFs
\r
739 // store the body part to string buffer
\r
740 int CMimeBody::Store(char* pszData, int nMaxSize) const
\r
742 // store header fields
\r
743 int nSize = CMimeHeader::Store(pszData, nMaxSize);
\r
748 char* pszDataBegin = pszData; // preserve start position
\r
752 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
753 ASSERT(pCoder != NULL);
\r
754 pCoder->SetInput((const char*)m_pbText, m_nTextSize, true);
\r
755 int nOutput = pCoder->GetOutput((unsigned char*)pszData, nMaxSize);
\r
760 pszData += nOutput;
\r
761 nMaxSize -= nOutput;
\r
762 if (m_listBodies.empty())
\r
763 return (int)(pszData - pszDataBegin);
\r
765 // store child body parts
\r
766 string strBoundary = GetBoundary();
\r
767 if (strBoundary.empty())
\r
768 return -1; // boundary not be set
\r
770 int nBoundSize = (int)strBoundary.size() + 6;
\r
771 for (CBodyList::const_iterator it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
773 if (nMaxSize < nBoundSize)
\r
775 if (m_listBodies.begin() == it && *(pszData-2) == '\r' && *(pszData-1) == '\n')
\r
780 ::sprintf(pszData, "\r\n--%s\r\n", strBoundary.c_str());
\r
781 pszData += nBoundSize;
\r
782 nMaxSize -= nBoundSize;
\r
784 CMimeBody* pBP = *it;
\r
785 ASSERT(pBP != NULL);
\r
786 nOutput = pBP->Store(pszData, nMaxSize);
\r
789 pszData += nOutput;
\r
790 nMaxSize -= nOutput;
\r
793 if (nMaxSize >= nBoundSize+2) // add closing boundary delimiter
\r
795 ::sprintf(pszData, "\r\n--%s--\r\n", strBoundary.c_str());
\r
796 pszData += nBoundSize + 2;
\r
798 return (int)(pszData - pszDataBegin);
\r
801 void CMimeBody::Store(std::string &str) const
\r
803 char *temp=new char[GetLength()+1];
\r
804 Store(temp,GetLength());
\r
805 temp[GetLength()]='\0';
\r
810 // load a body part from string buffer
\r
811 int CMimeBody::Load(const char* pszData, int nDataSize)
\r
813 // load header fields
\r
814 int nSize = CMimeHeader::Load(pszData, nDataSize);
\r
818 const char* pszDataBegin = pszData; // preserve start position
\r
820 nDataSize -= nSize;
\r
823 // determine the length of the content
\r
824 const char* pszEnd = pszData + nDataSize;
\r
825 int nMediaType = GetMediaType();
\r
826 if (MEDIA_MULTIPART == nMediaType)
\r
828 // find the begin boundary
\r
829 string strBoundary = GetBoundary();
\r
830 if (!strBoundary.empty())
\r
832 strBoundary = "\r\n--" + strBoundary;
\r
833 pszEnd = ::FindString(pszData-2, strBoundary.c_str(), pszEnd);
\r
835 pszEnd = pszData + nDataSize;
\r
842 nSize = (int)(pszEnd - pszData);
\r
845 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
846 ASSERT(pCoder != NULL);
\r
847 pCoder->SetInput(pszData, nSize, false);
\r
848 int nOutput = pCoder->GetOutputLength();
\r
849 if (AllocateBuffer(nOutput+4))
\r
850 nOutput = pCoder->GetOutput(m_pbText, nOutput);
\r
857 ASSERT(nOutput < m_nTextSize);
\r
858 m_pbText[nOutput] = 0;
\r
859 m_nTextSize = nOutput;
\r
861 nDataSize -= nSize;
\r
863 if (nDataSize <= 0)
\r
864 return (int)(pszData - pszDataBegin);
\r
866 // load child body parts
\r
867 string strBoundary = GetBoundary();
\r
868 ASSERT(strBoundary.size() > 0);
\r
869 strBoundary = "\r\n--" + strBoundary;
\r
871 // look for the first boundary (case sensitive)
\r
872 pszData -= 2; // go back to CRLF
\r
874 pszEnd = pszData + nDataSize;
\r
875 const char* pszBound1 = ::FindString(pszData, strBoundary.c_str(), pszEnd);
\r
876 while (pszBound1 != NULL && pszBound1 < pszEnd)
\r
878 const char* pszStart = ::FindString(pszBound1+2, "\r\n", pszEnd);
\r
882 if (pszBound1[strBoundary.size()] == '-' && pszBound1[strBoundary.size()+1] == '-')
\r
883 return (int)(pszStart - pszDataBegin); // reach the closing boundary
\r
885 // look for the next boundary
\r
886 const char* pszBound2 = ::FindString(pszStart, strBoundary.c_str(), pszEnd);
\r
887 if (!pszBound2) // overflow, boundary may be truncated
\r
888 pszBound2 = pszEnd;
\r
889 int nEntitySize = (int) (pszBound2 - pszStart);
\r
891 // find the media type of this body part:
\r
892 CMimeHeader header;
\r
893 header.Load(pszStart, nEntitySize);
\r
894 string strMediaType = header.GetMainType();
\r
895 CMimeBody* pBP = CreatePart(strMediaType.c_str());
\r
897 int nInputSize = pBP->Load(pszStart, nEntitySize);
\r
898 if (nInputSize < 0)
\r
903 pszBound1 = pszBound2;
\r
905 return (int)(pszEnd - pszDataBegin);
\r
908 //////////////////////////////////////////////////////////////////////
\r
909 // CMimeMessage - Represents a MIME message
\r
910 //////////////////////////////////////////////////////////////////////
\r
912 void CMimeMessage::SetDate()
\r
914 time_t timeNow = ::time(NULL);
\r
915 struct tm *ptm = ::localtime(&timeNow);
\r
916 SetDate(ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
\r
919 void CMimeMessage::SetDate(int nYear, int nMonth, int nDay, int nHour, int nMinute, int nSecond)
\r
921 static const char* s_MonthNames[] =
\r
922 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
\r
923 static const char* s_DayNames[] =
\r
924 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
\r
927 ::memset(&tmDate, 0, sizeof(tmDate));
\r
928 tmDate.tm_year = nYear - 1900;
\r
929 tmDate.tm_mon = nMonth - 1;
\r
930 tmDate.tm_mday = nDay;
\r
931 tmDate.tm_hour = nHour;
\r
932 tmDate.tm_min = nMinute;
\r
933 tmDate.tm_sec = nSecond;
\r
934 tmDate.tm_isdst = -1;
\r
936 time_t timeDate = ::mktime(&tmDate);
\r
943 tmDate = *::localtime(&timeDate); // adjusted local time
\r
944 struct tm *ptmGmt = ::gmtime(&timeDate); // Greenwich Mean Time
\r
945 long nTimeDiff = tmDate.tm_mday - ptmGmt->tm_mday;
\r
948 else if (nTimeDiff < -1)
\r
950 nTimeDiff *= 60 * 24;
\r
952 (tmDate.tm_hour - ptmGmt->tm_hour) * 60 +
\r
953 tmDate.tm_min - ptmGmt->tm_min;
\r
954 if (tmDate.tm_isdst > 0)
\r
958 ASSERT(tmDate.tm_wday < 7);
\r
959 ASSERT(tmDate.tm_mon < 12);
\r
960 ::sprintf(szDate, "%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
\r
961 s_DayNames[tmDate.tm_wday],
\r
962 tmDate.tm_mday, s_MonthNames[tmDate.tm_mon], tmDate.tm_year+1900,
\r
963 tmDate.tm_hour, tmDate.tm_min, tmDate.tm_sec,
\r
964 (nTimeDiff >= 0 ? '+' : '-'), abs(nTimeDiff / 60), abs(nTimeDiff % 60));
\r
966 SetFieldValue("Date", szDate);
\r