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
19 #define stricmp strcasecmp
\r
20 #define strnicmp strncasecmp
\r
21 #define memicmp memcmp
\r
26 static char THIS_FILE[]=__FILE__;
\r
27 #define new DEBUG_NEW
\r
30 // search for a character in the current line (before CRLF)
\r
31 static const char* LineFind(const char* pszString, int ch)
\r
33 ASSERT(pszString != NULL);
\r
34 while (*pszString != 0 && *pszString != ch && *pszString != '\r' && *pszString != '\n')
\r
36 return *pszString == ch ? pszString : NULL;
\r
39 // search for string2 in string1 (strstr)
\r
40 static const char* FindString(const char* pszStr1, const char* pszStr2, const char* pszEnd)
\r
42 pszEnd -= ::strlen(pszStr2);
\r
43 const char *s1, *s2;
\r
44 while (pszStr1 <= pszEnd)
\r
48 while (*s1 == *s2 && *s2)
\r
57 //////////////////////////////////////////////////////////////////////
\r
58 // CMimeField class - Represents a field of a MIME body part header
\r
59 //////////////////////////////////////////////////////////////////////
\r
61 void CMimeField::GetValue(string& strValue) const
\r
63 string::size_type nEnd = m_strValue.find(';');
\r
64 if (nEnd != string::npos)
\r
66 while (nEnd > 0 && CMimeChar::IsSpace((unsigned char)m_strValue[nEnd-1]))
\r
68 strValue.assign(m_strValue.c_str(), nEnd);
\r
71 strValue = m_strValue;
\r
74 // set a parameter (attribute=value) of the field
\r
75 void CMimeField::SetParameter(const char* pszAttr, const char* pszValue)
\r
77 int nSize = pszValue ? (int)::strlen(pszValue) : 0;
\r
79 strValue.reserve(nSize+3);
\r
80 if (!pszValue || *pszValue != '"')
\r
82 if (pszValue != NULL)
\r
83 strValue += pszValue;
\r
84 if (nSize < 2 || pszValue[nSize-1] != '"')
\r
88 if (!FindParameter(pszAttr, nPos, nSize)) // add new parameter
\r
90 m_strValue.reserve(m_strValue.size() + ::strlen(pszAttr) + strValue.size() + 5);
\r
91 //if (CMimeEnvironment::AutoFolding())
\r
92 // m_strValue += ";\r\n\t";
\r
94 // m_strValue += "; ";
\r
96 m_strValue += pszAttr;
\r
98 m_strValue += strValue;
\r
100 else // update existing parameter
\r
101 m_strValue.replace(nPos, nSize, strValue);
\r
104 // get the value of a parameter
\r
105 bool CMimeField::GetParameter(const char* pszAttr, string& strValue) const
\r
108 if (!FindParameter(pszAttr, nPos, nSize))
\r
114 if (m_strValue[nPos] == '"')
\r
118 if (nSize > 0 && m_strValue[nPos+nSize-1] == '"')
\r
121 strValue.assign(m_strValue.data()+nPos, nSize);
\r
125 int CMimeField::GetLength() const
\r
127 int nLength = (int) m_strName.size() + 4;
\r
128 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
129 pCoder->SetCharset(m_strCharset.c_str());
\r
130 pCoder->SetInput(m_strValue.c_str(), (int)m_strValue.size(), true);
\r
131 nLength += pCoder->GetOutputLength();
\r
136 // store a field to string buffer
\r
137 int CMimeField::Store(char* pszData, int nMaxSize) const
\r
139 ASSERT(pszData != NULL);
\r
140 int nMinSize = (int)m_strName.size() + 4;
\r
141 if (nMaxSize < nMinSize)
\r
143 ::strcpy(pszData, m_strName.c_str());
\r
144 pszData += m_strName.size();
\r
148 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
149 pCoder->SetCharset(m_strCharset.c_str());
\r
150 pCoder->SetInput(m_strValue.c_str(), (int)m_strValue.size(), true);
\r
151 int nEncoded = pCoder->GetOutput((unsigned char*) pszData, nMaxSize-nMinSize);
\r
153 pszData += nEncoded;
\r
157 return nMinSize + nEncoded;
\r
160 // load a field from string buffer
\r
161 int CMimeField::Load(const char* pszData, int nDataSize)
\r
164 ASSERT(pszData != NULL);
\r
165 const char *pszEnd, *pszStart = pszData;
\r
166 // find the next field (e.g. "\r\nContent...")
\r
167 while (CMimeChar::IsSpace((unsigned char)*pszStart))
\r
169 if (*pszStart == '\r') // end of header ?
\r
171 pszStart = ::FindString(pszStart, "\r\n", pszData+nDataSize);
\r
177 // get the field name
\r
178 pszEnd = ::LineFind(pszStart, ':');
\r
179 if (pszEnd != NULL) // if colon not found, Name would be empty
\r
181 m_strName.assign(pszStart, (pszEnd-pszStart));
\r
182 pszStart = pszEnd + 1;
\r
185 // find the end of the field
\r
186 while (*pszStart == ' ' || *pszStart == '\t')
\r
191 pszEnd = ::FindString(pszEnd, "\r\n", pszData+nDataSize);
\r
195 } while (*pszEnd == '\t' || *pszEnd == ' '); // linear-white-space
\r
197 // decode and unfold the field value
\r
198 CFieldCodeBase* pCoder = CMimeEnvironment::CreateFieldCoder(GetName());
\r
199 pCoder->SetInput(pszStart, (int)(pszEnd-pszStart)-2, false);
\r
200 m_strValue.resize(pCoder->GetOutputLength());
\r
201 int nSize = pCoder->GetOutput((unsigned char*) m_strValue.c_str(), (int) m_strValue.capacity());
\r
202 m_strValue.resize(nSize);
\r
203 m_strCharset = pCoder->GetCharset();
\r
205 return (int) (pszEnd - pszData);
\r
208 bool CMimeField::FindParameter(const char* pszAttr, int& nPos, int& nSize) const
\r
210 ASSERT(pszAttr != NULL);
\r
211 const char* pszParms = ::strchr(m_strValue.data(), ';');
\r
212 int nAttrSize = (int)::strlen(pszAttr);
\r
213 while (pszParms != NULL)
\r
215 while (CMimeChar::IsSpace((unsigned char)*pszParms) || *pszParms == ';')
\r
218 const char* pszName = pszParms; // pszName -> attribute
\r
219 pszParms = ::strchr(pszParms, '=');
\r
223 pszParms++; // pszParams -> parameter value
\r
224 //while (*pszParms == ' ' || *pszParms == '\t')
\r
227 const char* pszParmEnd = NULL;
\r
228 if (*pszParms == '"') // quoted string
\r
229 pszParmEnd = ::strchr(pszParms+1, '"');
\r
230 if (!pszParmEnd) // non quoted string
\r
232 pszParmEnd = pszParms;
\r
233 while (CMimeChar::IsToken(*pszParmEnd))
\r
236 else pszParmEnd++; // pszParmEnd -> end of parameter value
\r
238 if (!::memicmp(pszAttr, pszName, nAttrSize) &&
\r
239 (CMimeChar::IsSpace((unsigned char)pszName[nAttrSize]) || pszName[nAttrSize] == '='))
\r
241 nPos = (int)(pszParms - m_strValue.data());
\r
242 nSize = (int)(pszParmEnd - pszParms);
\r
246 pszParms = pszParmEnd;
\r
251 //////////////////////////////////////////////////////////////////////
\r
252 // CMimeHeader class - Represents the header of a MIME body part
\r
253 //////////////////////////////////////////////////////////////////////
\r
255 // Return the media type represented by Content-Type field (see RFC 2046)
\r
256 CMimeHeader::MediaType CMimeHeader::GetMediaType() const
\r
258 const char* pszType = GetContentType();
\r
263 while (m_TypeTable[nIndex] != NULL &&
\r
264 ::memicmp(pszType, m_TypeTable[nIndex], ::strlen(m_TypeTable[nIndex])) != 0)
\r
266 return (MediaType) nIndex;
\r
269 // get the top-level media type
\r
270 string CMimeHeader::GetMainType() const
\r
273 const char* pszType = GetContentType();
\r
274 if (pszType != NULL)
\r
276 const char* pszSlash = ::strchr(pszType, '/');
\r
277 if (pszSlash != NULL)
\r
278 strType.assign(pszType, pszSlash-pszType);
\r
288 string CMimeHeader::GetSubType() const
\r
291 const CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
295 pfd->GetValue(strType);
\r
296 string::size_type nSlash = strType.find('/');
\r
298 strSubType = strType.substr(nSlash+1);
\r
301 strSubType = "plain";
\r
305 // set the 'charset' parameter (for text) of Content-Type field
\r
306 void CMimeHeader::SetCharset(const char* pszCharset)
\r
308 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
312 fd.SetName(CMimeConst::ContentType());
\r
313 fd.SetValue("text/plain");
\r
314 fd.SetParameter(CMimeConst::Charset(), pszCharset);
\r
315 m_listFields.push_back(fd);
\r
318 pfd->SetParameter(CMimeConst::Charset(), pszCharset);
\r
321 // set the 'name' parameter (for attachment) of Content-Type field
\r
322 void CMimeHeader::SetName(const char* pszName)
\r
324 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
327 // get the appropriate media-type/subtype according to file extension
\r
328 ASSERT(pszName != NULL);
\r
330 const char* pszType = "application/octet-stream";
\r
331 const char* pszFileExt = ::strrchr(pszName, '.');
\r
332 if (pszFileExt != NULL)
\r
336 while (m_TypeCvtTable[nIndex].nMediaType != MEDIA_UNKNOWN)
\r
338 if (!::stricmp(pszFileExt, m_TypeCvtTable[nIndex].pszFileExt))
\r
340 strType = m_TypeTable[m_TypeCvtTable[nIndex].nMediaType];
\r
342 strType += m_TypeCvtTable[nIndex].pszSubType;
\r
343 pszType = strType.c_str();
\r
351 fd.SetName(CMimeConst::ContentType());
\r
352 fd.SetValue(pszType);
\r
353 fd.SetParameter(CMimeConst::Name(), pszName);
\r
354 m_listFields.push_back(fd);
\r
357 pfd->SetParameter(CMimeConst::Name(), pszName);
\r
360 // set 'boundary' parameter (for multipart) of Content-Type field
\r
361 void CMimeHeader::SetBoundary(const char* pszBoundary/*=NULL*/)
\r
363 static int s_nPartNumber = 0;
\r
365 if (!pszBoundary) // generate a new boundary delimeter
\r
367 ::srand(((unsigned)::time(NULL)));// ^ reinterpret_cast<unsigned>(this));
\r
368 ::sprintf(buf, "__=_Part_Boundary_%03d_%06d.%06d", ++s_nPartNumber, rand(), rand());
\r
369 if (s_nPartNumber >= 9)
\r
374 CMimeField *pfd = GetField(CMimeConst::ContentType());
\r
378 fd.SetName(CMimeConst::ContentType());
\r
379 fd.SetValue("multipart/mixed");
\r
380 fd.SetParameter(CMimeConst::Boundary(), pszBoundary);
\r
381 m_listFields.push_back(fd);
\r
385 if (::memicmp(pfd->GetValue(), "multipart", 9) != 0)
\r
386 pfd->SetValue("multipart/mixed");
\r
387 pfd->SetParameter(CMimeConst::Boundary(), pszBoundary);
\r
391 void CMimeHeader::Clear()
\r
393 m_listFields.clear();
\r
396 // return the length needed to store this header to string buffer
\r
397 int CMimeHeader::GetLength() const
\r
400 list<CMimeField>::const_iterator it;
\r
401 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
402 nLength += (*it).GetLength();
\r
403 return nLength + 2; // a pair of CRLF indicate the end of header
\r
406 // store the header to string buffer
\r
407 int CMimeHeader::Store(char* pszData, int nMaxSize) const
\r
409 ASSERT(pszData != NULL);
\r
411 list<CMimeField>::const_iterator it;
\r
412 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
414 const CMimeField& fd = *it;
\r
415 int nSize = fd.Store(pszData+nOutput, nMaxSize-nOutput);
\r
421 pszData[nOutput++] = '\r'; // add CRLF indicating the end of header
\r
422 pszData[nOutput++] = '\n';
\r
426 // load a header from string buffer
\r
427 int CMimeHeader::Load(const char* pszData, int nDataSize)
\r
429 ASSERT(pszData != NULL);
\r
431 while (pszData[nInput] != 0 && pszData[nInput] != '\r')
\r
434 int nSize = fd.Load(pszData+nInput, nDataSize-nInput);
\r
439 m_listFields.push_back(fd); // don't use SetField in case of same name fields
\r
442 return nInput + 2; // skip the ending CRLF
\r
445 list<CMimeField>::const_iterator CMimeHeader::FindField(const char* pszFieldName) const
\r
447 list<CMimeField>::const_iterator it;
\r
448 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
450 const CMimeField& fd = *it;
\r
451 if (!::stricmp(fd.GetName(), pszFieldName))
\r
457 list<CMimeField>::iterator CMimeHeader::FindField(const char* pszFieldName)
\r
459 list<CMimeField>::iterator it;
\r
460 for (it = m_listFields.begin(); it != m_listFields.end(); it++)
\r
462 CMimeField& fd = *it;
\r
463 if (!::stricmp(fd.GetName(), pszFieldName))
\r
469 //////////////////////////////////////////////////////////////////////
\r
470 // CMimeBody class - Represents a body part in a MIME message
\r
471 //////////////////////////////////////////////////////////////////////
\r
473 #include <sys/types.h>
\r
474 #include <sys/stat.h>
\r
479 #if !defined(__APPLE__) && !defined(__DARWIN__)
\r
480 #if !defined(__FreeBSD__) && !defined(solaris) && !defined(__sun)
\r
481 #include <sys/io.h>
\r
492 // initialize the content with text
\r
493 int CMimeBody::SetText(const char* pbText, int nLength/*=0*/)
\r
495 ASSERT(pbText != NULL);
\r
497 nLength = (int)::strlen((char*)pbText);
\r
499 if (!AllocateBuffer(nLength+4))
\r
502 ::memcpy(m_pbText, pbText, nLength);
\r
503 m_pbText[nLength] = 0;
\r
504 m_nTextSize = nLength;
\r
508 int CMimeBody::GetText(char* pbText, int nMaxSize)
\r
510 int nSize = min(nMaxSize, m_nTextSize);
\r
511 if (m_pbText != NULL)
\r
512 ::memcpy(pbText, m_pbText, nSize);
\r
516 int CMimeBody::GetText(string& strText)
\r
518 if (m_pbText != NULL)
\r
519 strText.assign((const char*) m_pbText, m_nTextSize);
\r
520 return m_nTextSize;
\r
523 // initialize the content of this body part with a mail message
\r
524 bool CMimeBody::SetMessage(const CMimeMessage* pMM)
\r
526 ASSERT(pMM != NULL);
\r
527 int nSize = pMM->GetLength();
\r
528 if (!AllocateBuffer(nSize+4))
\r
531 nSize = pMM->Store((char*)m_pbText, nSize);
\r
532 m_pbText[nSize] = 0;
\r
533 m_nTextSize = nSize;
\r
535 const char* pszType = GetContentType();
\r
536 if (!pszType || ::memicmp(pszType, "message", 7) != 0)
\r
537 SetContentType("message/rfc822");
\r
538 //SetTransferEncoding(CMimeConst::EncodingBinary()); // in case the default 7bit cause folding
\r
542 void CMimeBody::GetMessage(CMimeMessage* pMM) const
\r
544 ASSERT(pMM != NULL);
\r
545 ASSERT(m_pbText != NULL);
\r
546 pMM->Load((const char*)m_pbText, m_nTextSize);
\r
549 // initialize the content (attachment) by reading from a file
\r
550 bool CMimeBody::ReadFromFile(const char* pszFilename)
\r
552 int hFile = ::open(pszFilename, O_RDONLY | O_BINARY);
\r
558 int nFileSize = (int)::lseek(hFile, 0L, SEEK_END); // get file length
\r
559 ::lseek(hFile, 0L, SEEK_SET);
\r
564 AllocateBuffer(nFileSize+4);
\r
565 unsigned char* pszData = m_pbText;
\r
569 int nRead = ::read(hFile, pszData, 512);
\r
580 m_nTextSize = nFileSize;
\r
590 const char* pszName = ::strrchr(pszFilename, '\\');
\r
592 pszName = pszFilename;
\r
595 SetName(pszName); // set 'name' parameter:
\r
599 // write the content (attachment) to a file
\r
600 bool CMimeBody::WriteToFile(const char* pszFilename)
\r
604 int hFile = ::open(pszFilename, O_CREAT | O_TRUNC | O_RDWR | O_BINARY, S_IREAD | S_IWRITE);
\r
608 const unsigned char* pszData = m_pbText;
\r
609 int nLeft = m_nTextSize;
\r
615 int nWritten = ::write(hFile, pszData, min(512, nLeft));
\r
621 pszData += nWritten;
\r
637 // delete all child body parts
\r
638 void CMimeBody::DeleteAll()
\r
640 while (!m_listBodies.empty())
\r
642 CMimeBody* pBP = m_listBodies.back();
\r
643 m_listBodies.pop_back();
\r
644 ASSERT(pBP != NULL);
\r
645 delete pBP; // surely delete because it was allocated by CreatePart()
\r
649 // create a new child body part, and add it to body part list
\r
650 CMimeBody* CMimeBody::CreatePart(const char* pszMediaType/*=NULL*/, CMimeBody* pWhere/*=NULL*/)
\r
652 CMimeBody* pBP = CMimeEnvironment::CreateBodyPart(pszMediaType);
\r
653 ASSERT(pBP != NULL);
\r
654 if (pWhere != NULL)
\r
656 for (CBodyList::iterator it = m_listBodies.begin(); it != m_listBodies.end(); it++)
\r
659 m_listBodies.insert(it, pBP);
\r
663 m_listBodies.push_back(pBP);
\r
667 // remove and delete a child body part
\r
668 void CMimeBody::ErasePart(CMimeBody* pBP)
\r
670 ASSERT(pBP != NULL);
\r
671 m_listBodies.remove(pBP);
\r
675 // return a list of all child body parts belong to this body part
\r
676 int CMimeBody::GetBodyPartList(CBodyList& rList) const
\r
679 int nMediaType = GetMediaType();
\r
681 if (MEDIA_MULTIPART != nMediaType)
\r
683 rList.push_back((CMimeBody*)this);
\r
688 list<CMimeBody*>::const_iterator it;
\r
689 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
691 CMimeBody* pBP = *it;
\r
692 ASSERT(pBP != NULL);
\r
693 nCount += pBP->GetBodyPartList(rList);
\r
699 // return a list of all attachment body parts belong to this body part
\r
700 int CMimeBody::GetAttachmentList(CBodyList& rList) const
\r
703 int nMediaType = GetMediaType();
\r
705 if (MEDIA_MULTIPART != nMediaType)
\r
707 string strName = GetName();
\r
708 if (strName.size() > 0)
\r
710 rList.push_back((CMimeBody*)this);
\r
716 list<CMimeBody*>::const_iterator it;
\r
717 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
719 CMimeBody* pBP = *it;
\r
720 ASSERT(pBP != NULL);
\r
721 nCount += pBP->GetAttachmentList(rList);
\r
727 void CMimeBody::Clear()
\r
730 m_itFind = m_listBodies.end();
\r
732 CMimeHeader::Clear();
\r
735 // return the length needed to store this body part to string buffer
\r
736 int CMimeBody::GetLength() const
\r
738 int nLength = CMimeHeader::GetLength();
\r
739 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
740 ASSERT(pCoder != NULL);
\r
741 pCoder->SetInput((const char*)m_pbText, m_nTextSize, true);
\r
742 nLength += pCoder->GetOutputLength();
\r
745 if (m_listBodies.empty())
\r
748 string strBoundary = GetBoundary();
\r
749 int nBoundSize = (int) strBoundary.size();
\r
750 list<CMimeBody*>::const_iterator it;
\r
751 for (it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
753 nLength += nBoundSize + 6; // include 2 leading hyphens and 2 pair of CRLFs
\r
754 CMimeBody* pBP = *it;
\r
755 ASSERT(pBP != NULL);
\r
756 nLength += pBP->GetLength();
\r
758 nLength += nBoundSize + 8; // include 2 leading hyphens, 2 trailng hyphens and 2 pair of CRLFs
\r
762 // store the body part to string buffer
\r
763 int CMimeBody::Store(char* pszData, int nMaxSize) const
\r
765 // store header fields
\r
766 int nSize = CMimeHeader::Store(pszData, nMaxSize);
\r
771 char* pszDataBegin = pszData; // preserve start position
\r
775 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
776 ASSERT(pCoder != NULL);
\r
777 pCoder->SetInput((const char*)m_pbText, m_nTextSize, true);
\r
778 int nOutput = pCoder->GetOutput((unsigned char*)pszData, nMaxSize);
\r
783 pszData += nOutput;
\r
784 nMaxSize -= nOutput;
\r
785 if (m_listBodies.empty())
\r
786 return (int)(pszData - pszDataBegin);
\r
788 // store child body parts
\r
789 string strBoundary = GetBoundary();
\r
790 if (strBoundary.empty())
\r
791 return -1; // boundary not be set
\r
793 int nBoundSize = (int)strBoundary.size() + 6;
\r
794 for (CBodyList::const_iterator it=m_listBodies.begin(); it!=m_listBodies.end(); it++)
\r
796 if (nMaxSize < nBoundSize)
\r
798 if (m_listBodies.begin() == it && *(pszData-2) == '\r' && *(pszData-1) == '\n')
\r
803 ::sprintf(pszData, "\r\n--%s\r\n", strBoundary.c_str());
\r
804 pszData += nBoundSize;
\r
805 nMaxSize -= nBoundSize;
\r
807 CMimeBody* pBP = *it;
\r
808 ASSERT(pBP != NULL);
\r
809 nOutput = pBP->Store(pszData, nMaxSize);
\r
812 pszData += nOutput;
\r
813 nMaxSize -= nOutput;
\r
816 if (nMaxSize >= nBoundSize+2) // add closing boundary delimiter
\r
818 ::sprintf(pszData, "\r\n--%s--\r\n", strBoundary.c_str());
\r
819 pszData += nBoundSize + 2;
\r
821 return (int)(pszData - pszDataBegin);
\r
824 void CMimeBody::Store(std::string &str) const
\r
826 char *temp=new char[GetLength()+1];
\r
827 Store(temp,GetLength());
\r
828 temp[GetLength()]='\0';
\r
833 // load a body part from string buffer
\r
834 int CMimeBody::Load(const char* pszData, int nDataSize)
\r
836 // load header fields
\r
837 int nSize = CMimeHeader::Load(pszData, nDataSize);
\r
841 const char* pszDataBegin = pszData; // preserve start position
\r
843 nDataSize -= nSize;
\r
846 // determine the length of the content
\r
847 const char* pszEnd = pszData + nDataSize;
\r
848 int nMediaType = GetMediaType();
\r
849 if (MEDIA_MULTIPART == nMediaType)
\r
851 // find the begin boundary
\r
852 string strBoundary = GetBoundary();
\r
853 if (!strBoundary.empty())
\r
855 strBoundary = "\r\n--" + strBoundary;
\r
856 pszEnd = ::FindString(pszData-2, strBoundary.c_str(), pszEnd);
\r
858 pszEnd = pszData + nDataSize;
\r
865 nSize = (int)(pszEnd - pszData);
\r
868 CMimeCodeBase* pCoder = CMimeEnvironment::CreateCoder(GetTransferEncoding());
\r
869 ASSERT(pCoder != NULL);
\r
870 pCoder->SetInput(pszData, nSize, false);
\r
871 int nOutput = pCoder->GetOutputLength();
\r
872 if (AllocateBuffer(nOutput+4))
\r
873 nOutput = pCoder->GetOutput(m_pbText, nOutput);
\r
880 ASSERT(nOutput < m_nTextSize);
\r
881 m_pbText[nOutput] = 0;
\r
882 m_nTextSize = nOutput;
\r
884 nDataSize -= nSize;
\r
886 if (nDataSize <= 0)
\r
887 return (int)(pszData - pszDataBegin);
\r
889 // load child body parts
\r
890 string strBoundary = GetBoundary();
\r
891 ASSERT(strBoundary.size() > 0);
\r
892 strBoundary = "\r\n--" + strBoundary;
\r
894 // look for the first boundary (case sensitive)
\r
895 pszData -= 2; // go back to CRLF
\r
897 pszEnd = pszData + nDataSize;
\r
898 const char* pszBound1 = ::FindString(pszData, strBoundary.c_str(), pszEnd);
\r
899 while (pszBound1 != NULL && pszBound1 < pszEnd)
\r
901 const char* pszStart = ::FindString(pszBound1+2, "\r\n", pszEnd);
\r
905 if (pszBound1[strBoundary.size()] == '-' && pszBound1[strBoundary.size()+1] == '-')
\r
906 return (int)(pszStart - pszDataBegin); // reach the closing boundary
\r
908 // look for the next boundary
\r
909 const char* pszBound2 = ::FindString(pszStart, strBoundary.c_str(), pszEnd);
\r
910 if (!pszBound2) // overflow, boundary may be truncated
\r
911 pszBound2 = pszEnd;
\r
912 int nEntitySize = (int) (pszBound2 - pszStart);
\r
914 // find the media type of this body part:
\r
915 CMimeHeader header;
\r
916 header.Load(pszStart, nEntitySize);
\r
917 string strMediaType = header.GetMainType();
\r
918 CMimeBody* pBP = CreatePart(strMediaType.c_str());
\r
920 int nInputSize = pBP->Load(pszStart, nEntitySize);
\r
921 if (nInputSize < 0)
\r
926 pszBound1 = pszBound2;
\r
928 return (int)(pszEnd - pszDataBegin);
\r
931 //////////////////////////////////////////////////////////////////////
\r
932 // CMimeMessage - Represents a MIME message
\r
933 //////////////////////////////////////////////////////////////////////
\r
935 void CMimeMessage::SetDate()
\r
937 time_t timeNow = ::time(NULL);
\r
938 struct tm *ptm = ::localtime(&timeNow);
\r
939 SetDate(ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
\r
942 void CMimeMessage::SetDate(int nYear, int nMonth, int nDay, int nHour, int nMinute, int nSecond)
\r
944 static const char* s_MonthNames[] =
\r
945 { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
\r
946 static const char* s_DayNames[] =
\r
947 { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
\r
950 ::memset(&tmDate, 0, sizeof(tmDate));
\r
951 tmDate.tm_year = nYear - 1900;
\r
952 tmDate.tm_mon = nMonth - 1;
\r
953 tmDate.tm_mday = nDay;
\r
954 tmDate.tm_hour = nHour;
\r
955 tmDate.tm_min = nMinute;
\r
956 tmDate.tm_sec = nSecond;
\r
957 tmDate.tm_isdst = -1;
\r
959 time_t timeDate = ::mktime(&tmDate);
\r
966 tmDate = *::localtime(&timeDate); // adjusted local time
\r
967 struct tm *ptmGmt = ::gmtime(&timeDate); // Greenwich Mean Time
\r
968 long nTimeDiff = tmDate.tm_mday - ptmGmt->tm_mday;
\r
971 else if (nTimeDiff < -1)
\r
973 nTimeDiff *= 60 * 24;
\r
975 (tmDate.tm_hour - ptmGmt->tm_hour) * 60 +
\r
976 tmDate.tm_min - ptmGmt->tm_min;
\r
977 if (tmDate.tm_isdst > 0)
\r
981 ASSERT(tmDate.tm_wday < 7);
\r
982 ASSERT(tmDate.tm_mon < 12);
\r
983 ::sprintf(szDate, "%s, %d %s %d %02d:%02d:%02d %c%02d%02d",
\r
984 s_DayNames[tmDate.tm_wday],
\r
985 tmDate.tm_mday, s_MonthNames[tmDate.tm_mon], tmDate.tm_year+1900,
\r
986 tmDate.tm_hour, tmDate.tm_min, tmDate.tm_sec,
\r
987 (nTimeDiff >= 0 ? '+' : '-'), abs(nTimeDiff / 60), abs(nTimeDiff % 60));
\r
989 SetFieldValue("Date", szDate);
\r