1 // This is a part of the Active Template Library.
2 // Copyright (C) Microsoft Corporation
3 // All rights reserved.
5 // This source code is only intended as a supplement to the
6 // Active Template Library Reference and related
7 // electronic documentation provided with the library.
8 // See these sources for detailed information regarding the
9 // Active Template Library product.
23 #include <atlsmtputil.h>
28 #pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
29 #pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
32 #pragma warning (push)
33 #pragma warning(disable: 4702) // unreachable code
36 #pragma pack(push,_ATL_PACKING)
39 #ifndef ATLMIME_SEPARATOR
40 #define ATLMIME_SEPARATOR "\r\n\r\n--"
41 #endif//ATLMIME_SEPARATOR
43 #ifndef ATLMIME_VERSION
44 #define ATLMIME_VERSION "MIME-Version: 1.0"
45 #endif//ATLMIME_VERSION
48 #define ATLMIME_EMAIL "email"
51 extern __declspec(selectany
) const DWORD ATL_MIME_BOUNDARYLEN
= 32;
52 extern __declspec(selectany
) const DWORD ATL_MIME_DATE_LEN
= 64;
54 // Called when message is sent - sets the "Date:" field
55 inline size_t SetRfc822Time(__out_ecount_part_z_opt(dwLen
, return) LPSTR szDate
, __in
size_t dwLen
) throw()
57 // Max buffer size required(including NULL) - 38
58 const size_t s_dwMaxBufferLen
= 38;
61 return s_dwMaxBufferLen
;
68 static const LPCSTR s_months
[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
69 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
71 static const LPCSTR s_days
[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
74 DWORD dwTimeZoneId
=TIME_ZONE_ID_UNKNOWN
;
79 TIME_ZONE_INFORMATION tzi
;
83 // Gets TIME_ZONE_INFORMATION
84 memset(&tzi
, 0, sizeof(tzi
));
85 dwTimeZoneId
= GetTimeZoneInformation(&tzi
);
88 case TIME_ZONE_ID_STANDARD
:
89 ltzBias
= tzi
.Bias
+ tzi
.StandardBias
;
92 case TIME_ZONE_ID_DAYLIGHT
:
93 ltzBias
= tzi
.Bias
+ tzi
.DaylightBias
;
96 case TIME_ZONE_ID_UNKNOWN
:
102 // Set Hour Minutes and time zone dif
103 ltzHour
= ltzBias
/ 60;
104 ltzMinute
= ltzBias
% 60;
105 cDiff
= (ltzHour
< 0) ? '+' : '-';
107 int nDay
= (st
.wDayOfWeek
> 6) ? 0 : st
.wDayOfWeek
;
108 int nMonth
= st
.wMonth
= (WORD
)((st
.wMonth
< 1 || st
.wMonth
> 12) ? 0 : st
.wMonth
- 1);
111 // Constructs RFC 822 format: "ddd, dd mmm yyyy hh:mm:ss +/- hhmm\0"
112 sprintf_s(szDate
, dwLen
, "Date: %3s, %d %3s %4d %02d:%02d:%02d %c%02d%02d",
113 s_days
[nDay
], // "ddd"
115 s_months
[nMonth
], // "mmm"
121 abs (ltzHour
), // "hh"
122 abs (ltzMinute
)); // "mm"
123 return s_dwMaxBufferLen
;
126 inline DWORD
GetContentTypeFromFileName(LPCTSTR szFileName
, CSimpleString
& strContentType
) throw()
128 if (szFileName
== NULL
)
130 return ERROR_INVALID_DATA
;
133 DWORD dwErr
= ERROR_PATH_NOT_FOUND
;
136 // get the file extension
137 TCHAR szExt
[_MAX_EXT
];
138 Checked::tsplitpath_s(szFileName
, NULL
, 0, NULL
, 0, NULL
, 0, szExt
, _countof(szExt
));
141 // Query the content type from the registry
142 CRegKey rkContentType
;
143 dwErr
= rkContentType
.Open(HKEY_CLASSES_ROOT
, szExt
, KEY_READ
);
144 if (dwErr
== ERROR_SUCCESS
)
147 dwErr
= rkContentType
.QueryStringValue(_T("Content Type"), NULL
, &nChars
);
148 if (dwErr
== ERROR_SUCCESS
)
150 LPTSTR szBuf
= strContentType
.GetBuffer(nChars
);
151 dwErr
= rkContentType
.QueryStringValue(_T("Content Type"), szBuf
, &nChars
);
152 strContentType
.ReleaseBuffer(nChars
);
157 if (dwErr
!= ERROR_SUCCESS
)
159 // default to application/octet-stream
160 strContentType
.SetString(_T("application/octet-stream"), sizeof("application/octet-stream")-1);
165 dwErr
= ERROR_OUTOFMEMORY
;
171 // CMimeBodyPart is an abstract base class for the body parts
172 // CMimeAttachment, CMimeText, CMimeHeader.
177 virtual ~CMimeBodyPart() = 0 {}
179 // WriteData - pure virtual method to dump the data for a body part.
180 virtual BOOL
WriteData(HANDLE hFile
, LPOVERLAPPED pOverlapped
, LPCSTR szBoundary
, DWORD dwFlags
= 0) = 0;
182 // GetContentType - pure virtual method to get the content of a body part
183 virtual LPCSTR
GetContentType() = 0;
185 // GetCharset - virtual method to get the character set of a body part
186 // (defaults to ATLSMTP_DEFAULT_CSET).
187 virtual LPCSTR
GetCharset()
189 return ATLSMTP_DEFAULT_CSET
;
192 virtual CMimeBodyPart
* Copy() = 0;
196 // MakeMimeHeader - pure virutal method to create a MIME header for a
198 virtual BOOL
MakeMimeHeader(CStringA
& header
, LPCSTR szBoundary
) = 0;
199 }; // class CMimeBodyPart
202 // This enum is used with the X-Priority part of the message header
203 enum ATL_MIME_PRIORITY
205 ATL_MIME_HIGH_PRIORITY
= 1,
206 ATL_MIME_NORMAL_PRIORITY
= 3,
207 ATL_MIME_LOW_PRIORITY
= 5,
208 ATL_MIME_PRIORITY_ERROR
= 0
212 // CMimeHeader describes the basic RFC 822 message header.
213 // It also serves as the base class for the CMimeMessage object.
214 class CMimeHeader
: public CMimeBodyPart
218 // Pointer to MLANG's IMultiLanguage interface.
219 // This is used in doing conversion from code pages
220 // to MIME-compatible character sets.
221 CComPtr
<IMultiLanguage
> m_spMultiLanguage
;
228 CStringA m_strSubject
;
230 //Extended Header Parts
231 ATL_MIME_PRIORITY m_nPriority
;
235 CStringA m_strSenderName
;
237 //MIME Character Sets
238 char m_szSubjectCharset
[ATL_MAX_ENC_CHARSET_LENGTH
];
239 char m_szSenderCharset
[ATL_MAX_ENC_CHARSET_LENGTH
];
241 //Recipient and CC charsets are encoded in the Add methods
245 CMimeHeader() throw()
246 :m_nPriority(ATL_MIME_NORMAL_PRIORITY
)
248 m_szSubjectCharset
[0] = '\0';
249 m_szSenderCharset
[0] = '\0';
252 ~CMimeHeader() throw()
256 // Initialize MLang for multilanguage support
257 inline BOOL
Initialize(IMultiLanguage
* pMultiLanguage
= NULL
) throw()
259 if (pMultiLanguage
!= NULL
)
261 m_spMultiLanguage
= pMultiLanguage
;
265 HRESULT hr
= m_spMultiLanguage
.CoCreateInstance(__uuidof(CMultiLanguage
), NULL
, CLSCTX_INPROC_SERVER
);
272 // Get the content type
273 virtual inline LPCSTR
GetContentType() throw()
278 // Get the character set
279 virtual inline LPCSTR
GetCharset() throw()
284 virtual ATL_NOINLINE CMimeBodyPart
* Copy() throw( ... )
286 CAutoPtr
<CMimeHeader
> pNewHeader
;
287 ATLTRY(pNewHeader
.Attach(new CMimeHeader
));
291 return pNewHeader
.Detach();
294 const CMimeHeader
& operator=(const CMimeHeader
& that
) throw( ... )
298 m_spMultiLanguage
= that
.m_spMultiLanguage
;
299 m_strFrom
= that
.m_strFrom
;
300 m_strTo
= that
.m_strTo
;
301 m_strCc
= that
.m_strCc
;
302 m_strSubject
= that
.m_strSubject
;
304 m_nPriority
= that
.m_nPriority
;
305 m_XHeader
= that
.m_XHeader
;
307 m_strSenderName
= that
.m_strSenderName
;
309 Checked::strcpy_s(m_szSubjectCharset
, ATL_MAX_ENC_CHARSET_LENGTH
, that
.m_szSubjectCharset
);
310 Checked::strcpy_s(m_szSenderCharset
, ATL_MAX_ENC_CHARSET_LENGTH
, that
.m_szSenderCharset
);
316 // Set the priority of the message
317 inline BOOL
SetPriority(ATL_MIME_PRIORITY nPriority
) throw()
321 m_nPriority
= nPriority
;
325 // Get the priority of the message
326 inline ATL_MIME_PRIORITY
GetPriority() throw()
331 // Set the display (friendly) name for the header
332 inline BOOL
SetSenderName(LPCTSTR szName
, UINT uiCodePage
= 0) throw()
337 CHeapPtr
<char> szNamePtr
;
340 BOOL bRet
= AtlMimeConvertString(m_spMultiLanguage
, uiCodePage
, szName
, &szNamePtr
, &nLen
);
345 m_strSenderName
.Empty();
346 m_strSenderName
.Append(szNamePtr
, (int) nLen
);
352 bRet
= AtlMimeCharsetFromCodePage(m_szSenderCharset
, uiCodePage
, m_spMultiLanguage
, ATL_MAX_ENC_CHARSET_LENGTH
);
358 // Get the display (friendly) name for the sender
359 inline LPCSTR
GetSenderName() throw()
361 return m_strSenderName
;
364 // Append a user defined header (should not contain CRLF)
365 inline BOOL
AppendUserDefinedHeader(LPCTSTR szHeaderName
, LPCTSTR szHeader
, UINT uiCodePage
= 0) throw()
367 if ((szHeader
== NULL
) || (szHeaderName
== NULL
))
372 CHeapPtr
<char> szName
;
375 BOOL bRet
= AtlMimeConvertString(m_spMultiLanguage
, uiCodePage
, szHeader
, &szName
, &nLen
);
379 char szCharset
[ATL_MAX_ENC_CHARSET_LENGTH
];
380 bRet
= AtlMimeCharsetFromCodePage(szCharset
, uiCodePage
, m_spMultiLanguage
, ATL_MAX_ENC_CHARSET_LENGTH
);
385 str
.Append(szName
, (int)nLen
);
388 CHeapPtr
<char> szBuf
;
389 DWORD dwReqLen
= QEncodeGetRequiredLength(str
.GetLength(),
390 ATL_MAX_ENC_CHARSET_LENGTH
);
392 if (szBuf
.Allocate(dwReqLen
) == false)
398 BOOL bEncoded
= FALSE
;
399 if (!GetEncodedString(str
, szCharset
, szBuf
, dwReqLen
, dwLength
, bEncoded
))
405 m_XHeader
+= CT2CA(szHeaderName
);
406 m_XHeader
.Append(": ", 2);
407 m_XHeader
.Append(szBuf
, dwLength
);
408 m_XHeader
.Append("\r\n", 2);
420 // Add a recipient ("To:" line)
421 inline BOOL
AddRecipient(LPCTSTR szAddress
, LPCTSTR szName
= NULL
, UINT uiCodePage
= 0) throw()
423 return AddRecipientHelper(m_strTo
, szAddress
, szName
, uiCodePage
);
426 // Get the recipients string ("To:" line)
427 inline LPCSTR
GetRecipients() throw()
432 // Clear all recipients ("To:" line)
433 inline BOOL
ClearRecipients() throw()
439 // Add a recipient ("CC:" line)
440 inline BOOL
AddCc(LPCTSTR szAddress
, LPCTSTR szName
= NULL
, UINT uiCodePage
= 0) throw()
442 return AddRecipientHelper(m_strCc
, szAddress
, szName
, uiCodePage
);
445 // Get the recipients string ("CC:" line)
446 inline LPCSTR
GetCc() throw()
451 // Clear the recipients string ("CC:" line)
452 inline BOOL
ClearCc() throw()
458 // Add a Bcc recipient (not output as part of message)
459 inline BOOL
AddBcc(LPCTSTR szAddress
) throw()
461 if (szAddress
== NULL
)
468 CStringA str
= m_strBcc
;
470 if (m_strBcc
.GetLength() > 0)
473 str
+= CT2CA(szAddress
);
485 // Get the recipients string (Bcc part)
486 inline LPCSTR
GetBcc() throw()
491 // Clear the recipients string (Bcc part)
492 inline BOOL
ClearBcc() throw()
499 inline DWORD
GetRequiredRecipientsStringLength() throw()
501 DWORD dwRet
= m_strTo
.GetLength();
502 if (m_strCc
.GetLength())
504 dwRet
+= dwRet
? 1 : 0;
505 dwRet
+= m_strCc
.GetLength();
507 if (m_strBcc
.GetLength())
509 dwRet
+= dwRet
? 1 : 0;
510 dwRet
+= m_strBcc
.GetLength();
516 // returns the recipients string to be (addresses only, in comma separated format)
517 ATL_NOINLINE BOOL
GetRecipientsString(__out_ecount_part_z(*pdwLen
, *pdwLen
) LPSTR szRecip
, __inout LPDWORD pdwLen
) throw()
519 if ( (szRecip
== NULL
) || (pdwLen
== NULL
) )
524 if ( *pdwLen
< GetRequiredRecipientsStringLength())
526 *pdwLen
= GetRequiredRecipientsStringLength();
530 DWORD dwMaxLen
= *pdwLen
;
534 DWORD dwTotalLen
= 0;
535 if (m_strTo
.GetLength() > 0)
537 dwLen
= *pdwLen
- dwTotalLen
;
538 if (AtlMimeMakeRecipientsString(m_strTo
, szRecip
, &dwLen
) != TRUE
)
546 if (m_strCc
.GetLength() > 0)
553 dwLen
= *pdwLen
- dwTotalLen
;
554 if (AtlMimeMakeRecipientsString(m_strCc
, szRecip
, &dwLen
) != TRUE
)
562 if (m_strBcc
.GetLength() > 0)
564 dwLen
= m_strBcc
.GetLength();
570 dwLen
= *pdwLen
- dwTotalLen
;
571 Checked::memcpy_s(szRecip
, dwMaxLen
-dwTotalLen
, m_strBcc
, dwLen
);
577 *pdwLen
= dwTotalLen
;
584 inline LPCSTR
GetSender() throw()
590 inline BOOL
SetSender(LPCTSTR szSender
) throw()
592 if (szSender
== NULL
)
597 m_strFrom
= CT2CA(szSender
);
607 inline BOOL
SetSubject(LPCTSTR szSubject
, UINT uiCodePage
= 0) throw()
609 if (szSubject
== NULL
)
614 CHeapPtr
<char> szName
;
617 BOOL bRet
= AtlMimeConvertString(m_spMultiLanguage
, uiCodePage
, szSubject
, &szName
, &nLen
);
620 m_strSubject
.Empty();
621 m_strSubject
.Append(szName
, (int)nLen
);
622 bRet
= AtlMimeCharsetFromCodePage(m_szSubjectCharset
, uiCodePage
, m_spMultiLanguage
, ATL_MAX_ENC_CHARSET_LENGTH
);
634 inline LPCSTR
GetSubject() throw()
636 return (LPCSTR
)m_strSubject
;
639 // Dump the header to hFile
640 virtual inline BOOL
WriteData(HANDLE hFile
, LPOVERLAPPED pOverlapped
, LPCSTR
/*szBoundary*/, DWORD dwFlags
= 0) throw()
642 if (pOverlapped
== NULL
)
647 int nMaxSendLen
= GetRequiredBufferSize(ATLSMTP_MAX_LINE_LENGTH
-4);
648 CHeapPtr
<char> spSendBuffer
;
649 if (!spSendBuffer
.Allocate(nMaxSendLen
))
652 // choose QEncode here, because the max QEncodeGetRequiredLength will always
653 // return a value greater than BEncodeGetRequiredLength
654 int nBufLen
= __max(QEncodeGetRequiredLength(m_strSubject
.GetLength(),
655 ATL_MAX_ENC_CHARSET_LENGTH
),
656 QEncodeGetRequiredLength(m_strSenderName
.GetLength(),
657 ATL_MAX_ENC_CHARSET_LENGTH
)+m_strFrom
.GetLength()+2);
659 CHeapPtr
<char> spBuf
;
660 if (!spBuf
.Allocate(nBufLen
))
663 int nMaxLen
= nBufLen
;
666 char szDate
[ATL_MIME_DATE_LEN
];
668 SetRfc822Time(szDate
, ATL_MIME_DATE_LEN
);
669 char *pSendBuffer
= spSendBuffer
;
671 DWORD dwLength
= (DWORD
) strlen(szDate
);
673 if(dwLength
> ATLSMTP_MAX_LINE_LENGTH
-2 -dwOffset
)
676 Checked::memcpy_s(pSendBuffer
+dwOffset
, nMaxSendLen
-dwOffset
, szDate
, dwLength
);
677 dwOffset
+= dwLength
;
678 *(pSendBuffer
+dwOffset
++) = '\r';
679 *(pSendBuffer
+dwOffset
++) = '\n';
681 int dwHeaderPartLength
= 0;
684 // Get the sender name
686 BOOL bEncoded
= FALSE
;
687 if (m_strSenderName
.GetLength() > 0)
689 bRet
= GetEncodedString(m_strSenderName
, m_szSenderCharset
, spBuf
, nBufLen
, dwLength
, bEncoded
);
690 dwHeaderPartLength
+= dwLength
;
693 // Get the sender email address
694 if (bRet
&& m_strFrom
.GetLength() > 0)
697 if (dwHeaderPartLength
!= 0)
699 if(dwHeaderPartLength
+ 1 > nBufLen
)
702 *(spBuf
+dwHeaderPartLength
++) = ' ';
705 if(dwHeaderPartLength
+ m_strFrom
.GetLength() + 2 > nBufLen
)
708 *(spBuf
+dwHeaderPartLength
++) = '<';
709 if (dwHeaderPartLength
< 0 || dwHeaderPartLength
> nMaxLen
)
713 Checked::memcpy_s(spBuf
+dwHeaderPartLength
, nMaxLen
-dwHeaderPartLength
, (LPCSTR
)m_strFrom
, m_strFrom
.GetLength());
714 dwHeaderPartLength
+= m_strFrom
.GetLength();
715 *(spBuf
+dwHeaderPartLength
++) = '>';
718 // Output the "From: " line
719 if (bRet
&& dwHeaderPartLength
!= 0)
721 const char szFrom
[] = "From: ";
722 if(sizeof(szFrom
)/sizeof(szFrom
[0])-1 > ATLSMTP_MAX_LINE_LENGTH
-2 -dwOffset
)
724 if (dwOffset
> static_cast<DWORD
>(nMaxSendLen
))
728 Checked::memcpy_s(pSendBuffer
+dwOffset
, nMaxSendLen
-dwOffset
, szFrom
, _countof(szFrom
)-1);
729 dwOffset
+= (sizeof(szFrom
)/sizeof(szFrom
[0])-1) ;
730 DWORD dwWritten
= ATLSMTP_MAX_LINE_LENGTH
- 2 - dwOffset
;
731 bRet
= FormatField((LPBYTE
)(char*)spBuf
, dwHeaderPartLength
, (LPBYTE
)(pSendBuffer
+dwOffset
), &dwWritten
, dwFlags
);
732 dwOffset
+= dwWritten
;
733 *(pSendBuffer
+dwOffset
++) = '\r';
734 *(pSendBuffer
+dwOffset
++) = '\n';
737 // Output the subject
738 if (bRet
&& m_strSubject
.GetLength() > 0)
741 bRet
= GetEncodedString(m_strSubject
, m_szSubjectCharset
, spBuf
, nBufLen
, dwLength
, bEncoded
);
742 if (bRet
&& dwLength
!= 0)
744 const char szSubject
[] = "Subject: ";
745 if(sizeof(szSubject
)/sizeof(szSubject
[0])-1 > ATLSMTP_MAX_LINE_LENGTH
-2 -dwOffset
)
747 if (dwOffset
> static_cast<DWORD
>(nMaxSendLen
))
751 Checked::memcpy_s(pSendBuffer
+dwOffset
, nMaxSendLen
-dwOffset
, szSubject
, _countof(szSubject
)-1);
752 dwOffset
+= (sizeof(szSubject
)/sizeof(szSubject
[0])-1);
753 DWORD dwWritten
= ATLSMTP_MAX_LINE_LENGTH
- 2 - dwOffset
;
754 bRet
= FormatField((LPBYTE
)(char*)spBuf
, dwLength
, (LPBYTE
)(pSendBuffer
+dwOffset
), &dwWritten
, dwFlags
);
755 dwOffset
+= dwWritten
;
756 *(pSendBuffer
+dwOffset
++) = '\r';
757 *(pSendBuffer
+dwOffset
++) = '\n';
761 // Output the "To:" line
762 if (bRet
&& m_strTo
.GetLength() > 0)
764 const char szTo
[] = "To: ";
765 if(sizeof(szTo
)/sizeof(szTo
[0])-1 > ATLSMTP_MAX_LINE_LENGTH
-2 -dwOffset
)
767 if (dwOffset
> static_cast<DWORD
>(nMaxSendLen
))
771 Checked::memcpy_s(pSendBuffer
+dwOffset
, nMaxSendLen
-dwOffset
, szTo
, _countof(szTo
)-1);
772 dwOffset
+= (sizeof(szTo
)/sizeof(szTo
[0]) -1);
773 DWORD dwWritten
= ATLSMTP_MAX_LINE_LENGTH
- 2 - dwOffset
;
774 bRet
= FormatRecipients((LPBYTE
)((LPCSTR
)m_strTo
), m_strTo
.GetLength(), (LPBYTE
)(pSendBuffer
+dwOffset
), &dwWritten
);
775 dwOffset
+= dwWritten
;
776 *(pSendBuffer
+dwOffset
++) = '\r';
777 *(pSendBuffer
+dwOffset
++) = '\n';
780 // Output the "CC:" line
781 if (bRet
&& m_strCc
.GetLength() > 0)
783 const char szCC
[] = "CC: ";
784 if(sizeof(szCC
)/sizeof(szCC
[0])-1 > ATLSMTP_MAX_LINE_LENGTH
-2 -dwOffset
)
786 if (dwOffset
> static_cast<DWORD
>(nMaxSendLen
))
790 Checked::memcpy_s(pSendBuffer
+dwOffset
, nMaxSendLen
-dwOffset
, szCC
, _countof(szCC
)-1);
791 dwOffset
+= (sizeof(szCC
)/sizeof(szCC
[0]) -1);
792 DWORD dwWritten
= ATLSMTP_MAX_LINE_LENGTH
- 2 - dwOffset
;
793 bRet
= FormatRecipients((LPBYTE
)((LPCSTR
)m_strCc
), m_strCc
.GetLength(), (LPBYTE
)(pSendBuffer
+dwOffset
), &dwWritten
);
794 dwOffset
+= dwWritten
;
795 *(pSendBuffer
+dwOffset
++) = '\r';
796 *(pSendBuffer
+dwOffset
++) = '\n';
800 if (bRet
&& dwOffset
)
801 bRet
= AtlSmtpSendAndWait(hFile
, pSendBuffer
, dwOffset
, pOverlapped
);
808 // Make the mime header
809 virtual inline BOOL
MakeMimeHeader(CStringA
& /*header*/, LPCSTR
/*szBoundary*/) throw()
811 // The message header does not have its own MIME header
816 // Get an encoded string for a header field
817 inline BOOL
GetEncodedString(__in CStringA
& headerString
, __in LPCSTR szCharset
, __out_ecount_part_z(nBufLen
, dwLength
) LPSTR szBuf
, __in
int nBufLen
, __out DWORD
& dwLength
, __out BOOL
& bEncoded
) throw()
819 // BOOL bEncoded = FALSE;
821 if (m_spMultiLanguage
.p
)
823 // only encode if there are 8bit characters
824 int nExtendedChars
= GetExtendedChars(headerString
, headerString
.GetLength());
827 // choose smallest encoding
828 if (((nExtendedChars
*100)/headerString
.GetLength()) < 17)
831 if (!QEncode((LPBYTE
)((LPCSTR
)headerString
), headerString
.GetLength(), szBuf
, &nBufLen
, szCharset
, &nEncCnt
))
836 //if no unsafe characters were encountered, just output it
844 if (!BEncode((LPBYTE
)((LPCSTR
)headerString
), headerString
.GetLength(), szBuf
, &nBufLen
, szCharset
))
856 // there was no encoding
857 dwLength
= (DWORD
) headerString
.GetLength();
858 if(dwLength
> DWORD(nBufLen
))
860 Checked::memcpy_s(szBuf
, nBufLen
, headerString
, dwLength
);
870 // Helper function for adding recipients
871 inline BOOL
AddRecipientHelper(CStringA
& str
, LPCTSTR szAddress
, LPCTSTR szName
= NULL
, UINT uiCodePage
= 0) throw()
873 if ((szAddress
== NULL
) && (szName
== NULL
))
882 CHeapPtr
<char> szNamePtr
;
885 BOOL bRet
= AtlMimeConvertString(m_spMultiLanguage
, uiCodePage
, szName
, &szNamePtr
, &nLen
);
888 CStringA
Name(szNamePtr
, (int)nLen
);
890 char szCharset
[ATL_MAX_ENC_CHARSET_LENGTH
];
892 if (!AtlMimeCharsetFromCodePage(szCharset
, uiCodePage
, m_spMultiLanguage
, ATL_MAX_ENC_CHARSET_LENGTH
))
897 CFixedStringT
<CStringA
, 256> strBuf
;
899 int nBufLen
= QEncodeGetRequiredLength(Name
.GetLength(),
900 ATL_MAX_ENC_CHARSET_LENGTH
)+1;
902 char * szBuf
= strBuf
.GetBuffer(nBufLen
);
909 BOOL bEncoded
= FALSE
;
910 if (!GetEncodedString(Name
, szCharset
, szBuf
, nBufLen
, dwLength
, bEncoded
))
912 strBuf
.ReleaseBuffer();
916 strBuf
.ReleaseBuffer(dwLength
);
918 // append comma if there are existing recipients
919 if (str
.GetLength() != 0)
924 if (bEncoded
== FALSE
)
926 // need to escape the string if no encoding
927 strBuf
.Replace("\\", "\\\\");
928 strBuf
.Replace("\"", "\\\"");
930 // wrap the unescaped name in quotes
934 if (bEncoded
== FALSE
)
954 // append comma if there are existing recipients
955 if (str
.GetLength() != 0)
961 str
+= CT2CA(szAddress
);
972 // Get the formatted header information
973 inline BOOL
FormatField(LPBYTE pbSrcData
, int nSrcLen
, LPBYTE pbDest
,
974 DWORD
* pnBufLen
, DWORD dwFlags
= 0) throw()
981 // 9 is the length of the maximum field name : "Subject :"
982 // we set that here for simplicity
986 //subtract 2 from these because it's easier for when we have
987 //to break lines with a CRLF (and tab if necessary)
988 int nMaxLineLength
= ATLSMTP_MAX_LINE_LENGTH
-3;
989 while (nRead
< nSrcLen
)
991 //if we're at the end of the line, break it
992 if (nLineLen
== nMaxLineLength
)
994 if( nWritten
+ 2 > *pnBufLen
)
1002 if ((dwFlags
& ATLSMTP_FORMAT_SMTP
))
1004 if(nWritten
+ 1 > *pnBufLen
)
1013 //if we hit a CRLF, reset nLineLen
1014 if (*pbSrcData
== '\n' && nRead
> 0 && *(pbSrcData
-1) == '\r')
1019 if(nWritten
+ 1 > *pnBufLen
)
1022 *pbDest
++ = *pbSrcData
++;
1028 *pnBufLen
= (DWORD
)nWritten
;
1034 // Get the formatted recipient information
1035 inline BOOL
FormatRecipients(LPBYTE pbSrcData
, int nSrcLen
, LPBYTE pbDest
,
1036 DWORD
* pnBufLen
) throw()
1039 if(pnBufLen
== NULL
)
1045 while (nRead
< nSrcLen
)
1047 if (*pbSrcData
== ',')
1049 if(nWritten
+ 4 > *pnBufLen
)
1052 *pbDest
++ = *pbSrcData
++;
1054 if (nRead
+1 <= nSrcLen
&& *pbSrcData
== ' ')
1067 if(nWritten
+ 1 > *pnBufLen
)
1070 *pbDest
++ = *pbSrcData
++;
1075 *pnBufLen
= nWritten
;
1080 // Get the required buffer size for the header
1081 inline int GetRequiredBufferSize(int nMaxLineLength
) throw()
1083 const static DWORD DATELINE
= 27;
1084 const static DWORD FROMLINE
= 10;
1085 const static DWORD TOLINE
= 6;
1086 const static DWORD CCLINE
= 6;
1087 const static DWORD SUBJECTLINE
= 11;
1089 //data lengths (QEncoding potentially takes up more space than BEncoding,
1091 int nRequiredLength
= QEncodeGetRequiredLength(m_strSenderName
.GetLength(), ATL_MAX_ENC_CHARSET_LENGTH
)
1092 +QEncodeGetRequiredLength(m_strSubject
.GetLength(), ATL_MAX_ENC_CHARSET_LENGTH
);
1093 nRequiredLength
+= m_strFrom
.GetLength()+m_strTo
.GetLength()+m_strCc
.GetLength();
1095 //Add space for date
1096 nRequiredLength
+= DATELINE
;
1098 //Add space for From: line
1099 nRequiredLength
+= FROMLINE
;
1101 //Add space for To: line
1102 nRequiredLength
+= TOLINE
;
1104 //Add space for Cc: line
1105 nRequiredLength
+= CCLINE
;
1107 //Add space for Subject: line
1108 nRequiredLength
+= SUBJECTLINE
;
1110 //Add space for line breaks and tabs
1111 nRequiredLength
+= 3*(nRequiredLength
/nMaxLineLength
);
1114 nRequiredLength
+= 2;
1116 return nRequiredLength
;
1119 }; // class CMimeHeader
1122 // CMimeAttachment is an abstract base class for MIME message attachments.
1123 // It serves as a base class for CMimeFileAttachment and CMimeRawAttachment
1124 class CMimeAttachment
: public CMimeBodyPart
1128 // the encoding scheme (ATLSMTP_BASE64_ENCODE, ATLSMTP_UUENCODE, ATLSMTP_QP_ENCODE)
1129 int m_nEncodingScheme
;
1131 // the content type of the attachment
1132 CStringA m_ContentType
;
1134 // the character set
1135 char m_szCharset
[ATL_MAX_ENC_CHARSET_LENGTH
];
1137 // the encode string ("base64", "quoted-printable", "uuencode")
1138 char *m_pszEncodeString
;
1140 // the display name of the attachment
1141 TCHAR m_szDisplayName
[_MAX_FNAME
];
1144 CMimeAttachment() throw()
1145 :m_nEncodingScheme(ATLSMTP_BASE64_ENCODE
), m_pszEncodeString(NULL
)
1148 m_szDisplayName
[0] = 0;
1151 virtual ~CMimeAttachment() throw()
1155 // CMimeFileAttachment and CMimeRawAttachment have to handle their own dumping
1156 virtual inline BOOL
WriteData(HANDLE hFile
, LPOVERLAPPED pOverlapped
, LPCSTR szBoundary
, DWORD dwFlags
= 0) = 0;
1158 // Set the encoding scheme of the attachment
1159 inline BOOL
SetEncodingScheme(int nScheme
) throw()
1161 if (nScheme
!= ATLSMTP_BASE64_ENCODE
&& nScheme
!= ATLSMTP_UUENCODE
&& nScheme
!= ATLSMTP_QP_ENCODE
)
1166 m_nEncodingScheme
= nScheme
;
1170 // Set the Content-Type of the attachment
1171 inline BOOL
SetContentType(LPCTSTR szContent
) throw()
1175 m_ContentType
= CT2CA(szContent
);
1184 // Get the content type of the attachment
1185 virtual inline LPCSTR
GetContentType() throw()
1187 return m_ContentType
;
1190 // Get the character set of the attachment
1191 virtual inline LPCSTR
GetCharset() throw()
1196 virtual ATL_NOINLINE CMimeBodyPart
* Copy() = 0;
1198 const CMimeAttachment
& operator=(const CMimeAttachment
& that
) throw( ... )
1202 m_nEncodingScheme
= that
.m_nEncodingScheme
;
1203 m_ContentType
= that
.m_ContentType
;
1204 Checked::strcpy_s(m_szCharset
, ATL_MAX_ENC_CHARSET_LENGTH
, that
.m_szCharset
);
1205 m_pszEncodeString
= that
.m_pszEncodeString
;
1206 Checked::tcscpy_s(m_szDisplayName
, _countof(m_szDisplayName
), that
.m_szDisplayName
);
1214 // Make the MIME header for the attachment
1215 virtual inline BOOL
MakeMimeHeader(CStringA
& header
, LPCSTR szBoundary
) throw()
1217 // if no display name is specified, default to "rawdata"
1218 return MakeMimeHeader(header
, szBoundary
, _T("rawdata"));
1221 // Make the MIME header with the specified filename
1222 virtual inline BOOL
MakeMimeHeader(CStringA
& header
, LPCSTR szBoundary
, LPCTSTR szFileName
)
1224 ATLENSURE(szBoundary
!= NULL
);
1225 ATLASSERT(szFileName
!= NULL
);
1226 ATLASSUME(m_pszEncodeString
!= NULL
);
1231 // this is not the only body part
1232 Checked::memcpy_s(szBegin
, 256, ATLMIME_SEPARATOR
, sizeof(ATLMIME_SEPARATOR
));
1233 Checked::memcpy_s(szBegin
+6, 250, szBoundary
, ATL_MIME_BOUNDARYLEN
);
1234 *(szBegin
+(ATL_MIME_BOUNDARYLEN
+6)) = '\0';
1238 // this is the only body part, so output the MIME header
1239 Checked::memcpy_s(szBegin
, 256, ATLMIME_VERSION
, sizeof(ATLMIME_VERSION
));
1242 // Get file name with the path stripped out
1243 TCHAR szFile
[MAX_PATH
+1];
1244 TCHAR szExt
[_MAX_EXT
+1];
1245 Checked::tsplitpath_s(szFileName
, NULL
, 0, NULL
, 0, szFile
, _countof(szFile
), szExt
, _countof(szExt
));
1246 Checked::tcscat_s(szFile
, _countof(szFile
), szExt
);
1250 CT2CAEX
<MAX_PATH
+1> szFileNameA(szFile
);
1252 CStringA
szDisplayName(szFile
);
1253 if (m_szDisplayName
[0] != '\0')
1255 szDisplayName
= CT2CAEX
<_MAX_FNAME
+1>(m_szDisplayName
);
1258 header
.Format("%s\r\nContent-Type: %s;\r\n\tcharset=\"%s\"\r\n\tname=\"%s\"\r\n"
1259 "Content-Transfer-Encoding: %s\r\nContent-Disposition: attachment;\r\n\tfilename=\"%s\"\r\n\r\n",
1260 szBegin
, (LPCSTR
) m_ContentType
, m_szCharset
, (LPCSTR
) szDisplayName
, m_pszEncodeString
, (LPCSTR
) szFileNameA
);
1269 // Get encoding information
1270 inline BOOL
GetEncodingInformation(int* pnRequiredLength
, int* pnLineLength
)
1272 ATLENSURE(pnRequiredLength
!= NULL
);
1273 ATLENSURE(pnLineLength
!= NULL
);
1275 switch(m_nEncodingScheme
)
1277 case ATLSMTP_BASE64_ENCODE
:
1278 m_pszEncodeString
= "base64";
1279 *pnLineLength
= ATLSMTP_MAX_BASE64_LINE_LENGTH
;
1280 *pnRequiredLength
= Base64EncodeGetRequiredLength(ATLSMTP_MAX_BASE64_LINE_LENGTH
);
1282 case ATLSMTP_UUENCODE
:
1283 m_pszEncodeString
="uuencode";
1284 *pnLineLength
= ATLSMTP_MAX_UUENCODE_LINE_LENGTH
;
1285 *pnRequiredLength
= UUEncodeGetRequiredLength(ATLSMTP_MAX_UUENCODE_LINE_LENGTH
);
1287 case ATLSMTP_QP_ENCODE
:
1288 m_pszEncodeString
= "quoted-printable";
1289 *pnLineLength
= ATLSMTP_MAX_QP_LINE_LENGTH
;
1290 *pnRequiredLength
= QPEncodeGetRequiredLength(ATLSMTP_MAX_QP_LINE_LENGTH
);
1298 }; // class CMimeAttachment
1301 // CMimeFileAttachment represents a MIME file attachment body part
1302 class CMimeFileAttachment
: public CMimeAttachment
1307 TCHAR m_szFileName
[MAX_PATH
+1];
1310 CMimeFileAttachment() throw()
1312 m_szFileName
[0] = 0;
1315 virtual ATL_NOINLINE CMimeBodyPart
* Copy() throw( ... )
1317 CAutoPtr
<CMimeFileAttachment
> pNewAttachment
;
1318 ATLTRY(pNewAttachment
.Attach(new CMimeFileAttachment
));
1320 *pNewAttachment
= *this;
1322 return pNewAttachment
.Detach();
1325 const CMimeFileAttachment
& operator=(const CMimeFileAttachment
& that
) throw( ... )
1329 CMimeAttachment::operator=(that
);
1330 Checked::tcscpy_s(m_szFileName
, _countof(m_szFileName
), that
.m_szFileName
);
1337 // Initialize the file attachment
1338 // szFileName - the actual file name
1339 // szDisplayName - the display name for the file (optional)
1340 // pMultiLanguage - the IMulitLanguage pointer for codepage to charset conversion (optional)
1341 // uiCodePage - the code page (optional)
1342 inline BOOL
Initialize(LPCTSTR szFileName
, LPCTSTR szDisplayName
= NULL
, IMultiLanguage
* pMultiLanguage
= NULL
, UINT uiCodePage
= 0) throw()
1344 if (!AtlMimeCharsetFromCodePage(m_szCharset
, uiCodePage
, pMultiLanguage
, ATL_MAX_ENC_CHARSET_LENGTH
))
1347 if( _tcslen(szFileName
) > MAX_PATH
)
1351 Checked::tcscpy_s(m_szFileName
, _countof(m_szFileName
), szFileName
);
1355 // use the user-specified display name
1356 size_t nLen
= _tcslen(szDisplayName
)+1;
1357 if (nLen
<= _countof(m_szDisplayName
))
1359 Checked::tcscpy_s(m_szDisplayName
, _countof(m_szDisplayName
), szDisplayName
);
1363 Checked::tcsncpy_s(m_szDisplayName
, _countof(m_szDisplayName
), szDisplayName
, _countof(m_szDisplayName
) - 4);
1364 Checked::tcscpy_s(m_szDisplayName
+ _countof(m_szDisplayName
) - 4, 4, _T("..."));
1369 // otherwise there is no display name
1370 *m_szDisplayName
= '\0';
1375 // Dump the data for the file attachment
1376 virtual inline BOOL
WriteData(HANDLE hFile
, LPOVERLAPPED pOverlapped
, LPCSTR szBoundary
, DWORD dwFlags
= 0) throw()
1378 if ((pOverlapped
== NULL
) || (szBoundary
== NULL
))
1383 int nLineLength
= 0;
1384 int nRequiredLength
= 0;
1386 if (!GetEncodingInformation(&nRequiredLength
, &nLineLength
))
1389 //Try to open the file that is being attached
1391 if (FAILED(readFile
.Create(m_szFileName
, GENERIC_READ
, FILE_SHARE_READ
, OPEN_EXISTING
)))
1394 //Make the mime header
1396 if (!MakeMimeHeader(header
, szBoundary
, m_szFileName
))
1401 //Try to send the mime header
1402 if (!AtlSmtpSendAndWait(hFile
, ((LPCSTR
)header
), header
.GetLength(), pOverlapped
))
1407 int nGetLines
= ATLSMTP_GET_LINES
;
1409 nRequiredLength
*= nGetLines
;
1411 //dwToGet is the total number of characters to attempt to get
1412 DWORD dwToGet
= (DWORD
)nGetLines
*nLineLength
;
1414 //allocate the data array
1415 CHeapPtr
<BYTE
> spData
;
1416 if (!spData
.Allocate(dwToGet
+1))
1419 // if double buffering is defined, create two buffers
1420 #ifdef ATLSMTP_DOUBLE_BUFFERED
1421 CHeapPtr
<char> buffer1
;
1422 if (!buffer1
.Allocate(nRequiredLength
+3))
1425 CHeapPtr
<char> buffer2
;
1426 if (!buffer2
.Allocate(nRequiredLength
+3))
1429 char* currBuffer
= buffer1
;
1430 char* prevBuffer
= NULL
;
1431 int nCurrBuffer
= 0;
1432 DWORD dwPrevLength
= 0;
1434 CHeapPtr
<char> currBuffer
;
1435 if (!currBuffer
.Allocate(nRequiredLength
+3))
1438 #endif // ATLSMTP_DOUBLE_BUFFERED
1440 int nEncodedLength
= nRequiredLength
;
1443 DWORD dwTotalRead
= 0;
1444 DWORD dwCurrRead
= 0;
1450 //Read a chunk of data from the file increment buffer offsets and amount to read
1451 //based on what's already been read in this iteration of the loop
1452 HRESULT hr
= readFile
.Read(((LPBYTE
)spData
)+dwCurrRead
, dwToGet
-dwCurrRead
, dwRead
);
1455 if (hr
!= AtlHresultFromWin32(ERROR_MORE_DATA
))
1460 dwCurrRead
+= dwRead
;
1462 } while (dwRead
!= 0 && dwCurrRead
< dwToGet
);
1464 //reset nEncodedLength
1465 nEncodedLength
= nRequiredLength
;
1466 switch (m_nEncodingScheme
)
1468 case ATLSMTP_BASE64_ENCODE
:
1469 //if we are at the end of input (dwCurrRead < dwToGet), output the trailing padding if necessary
1471 bRet
= Base64Encode(spData
, dwCurrRead
, currBuffer
, &nEncodedLength
,
1472 (dwCurrRead
< dwToGet
? ATL_BASE64_FLAG_NONE
: ATL_BASE64_FLAG_NOPAD
));
1473 //Base64Encoding needs explicit CRLF added
1474 if (dwCurrRead
< dwToGet
)
1476 currBuffer
[nEncodedLength
++] = '\r';
1477 currBuffer
[nEncodedLength
++] = '\n';
1480 case ATLSMTP_UUENCODE
:
1481 //if we are at the beginning of the input, output the header (ATL_UUENCODE_HEADER)
1482 //if we are the end of input (dwCurrRead < dwToGet), output the 'end'
1483 //we are encoding for purposes of sending mail, so stuff dots (ATL_UUENCODE_DOT)
1484 bRet
= UUEncode(spData
, dwCurrRead
, currBuffer
, &nEncodedLength
, m_szFileName
,
1485 (dwTotalRead
> 0 ? 0 : ATLSMTP_UUENCODE_HEADER
) |
1486 (dwCurrRead
< dwToGet
? ATLSMTP_UUENCODE_END
: 0) |
1487 ((dwFlags
& ATLSMTP_FORMAT_SMTP
) ? ATLSMTP_UUENCODE_DOT
: 0));
1489 case ATLSMTP_QP_ENCODE
:
1490 //we are encoding for purposes of sending mail, so stuff dots
1491 bRet
= QPEncode(spData
, dwCurrRead
, currBuffer
, &nEncodedLength
,
1492 ((dwFlags
& ATLSMTP_FORMAT_SMTP
) ? ATLSMTP_QPENCODE_DOT
: 0) |
1493 (dwCurrRead
< dwToGet
? 0 : ATLSMTP_QPENCODE_TRAILING_SOFT
));
1496 //try to send the encoded data
1497 #ifdef ATLSMTP_DOUBLE_BUFFERED
1500 bRet
= AtlSmtpSendOverlapped(hFile
, currBuffer
, nEncodedLength
,
1501 prevBuffer
, dwPrevLength
, pOverlapped
);
1505 dwPrevLength
= nEncodedLength
;
1506 prevBuffer
= currBuffer
;
1507 currBuffer
= (nCurrBuffer
== 0 ? buffer2
: buffer1
);
1508 nCurrBuffer
= (nCurrBuffer
== 0 ? 1 : 0);
1512 bRet
= AtlSmtpSendAndWait(hFile
, currBuffer
, nEncodedLength
, pOverlapped
);
1514 #endif // ATLSMTP_DOUBLE_BUFFERED
1516 dwTotalRead
+= dwCurrRead
;
1520 nEncodedLength
= nRequiredLength
;
1522 } while (dwRead
!= 0 && bRet
);
1524 //ensure that the last Send sent all the data
1525 #ifdef ATLSMTP_DOUBLE_BUFFERED
1526 DWORD dwWritten
= 0, dwErr
= 0;
1527 if (!GetOverlappedResult(hFile
, pOverlapped
, &dwWritten
, TRUE
))
1529 if ((dwErr
= GetLastError()) != ERROR_IO_PENDING
&& dwErr
!= ERROR_IO_INCOMPLETE
)
1533 else if (dwWritten
< dwPrevLength
)
1535 bRet
= AtlSmtpSendAndWait(hFile
, prevBuffer
+dwWritten
,
1536 dwPrevLength
-dwWritten
, pOverlapped
);
1539 #endif // ATLSMTP_DOUBLE_BUFFERED
1541 //for uuencoding, if the last chunk read was of size dwToGet, but it was also the end of the file,
1542 //the "end" keyword will not get encoded, so a check is necessary
1543 if (m_nEncodingScheme
== ATLSMTP_UUENCODE
&& dwCurrRead
== dwToGet
)
1545 bRet
= UUEncode(spData
, 0, currBuffer
, &nEncodedLength
, m_szFileName
,
1546 (dwFlags
& ATLSMTP_FORMAT_SMTP
? ATLSMTP_UUENCODE_DOT
: 0) |
1547 ATLSMTP_UUENCODE_END
);
1550 bRet
= AtlSmtpSendAndWait(hFile
, currBuffer
, nEncodedLength
, pOverlapped
);
1556 }; // class CMimeFileAttachment
1558 // CMimeRawAttachment represents a file attachment MIME body part.
1559 // The data provided is not a file, but a blob of raw data.
1560 class CMimeRawAttachment
: public CMimeAttachment
1570 //whether or not we own it
1574 CMimeRawAttachment() throw()
1575 :m_dwLength(0), m_bShared(false), m_pvRaw(NULL
)
1579 ~CMimeRawAttachment() throw()
1581 //If we own the raw data, free it
1582 if (!m_bShared
&& m_pvRaw
)
1586 virtual ATL_NOINLINE CMimeBodyPart
* Copy() throw( ... )
1588 CAutoPtr
<CMimeRawAttachment
> pNewAttachment
;
1589 ATLTRY(pNewAttachment
.Attach(new CMimeRawAttachment
));
1591 *pNewAttachment
= *this;
1593 return pNewAttachment
.Detach();
1596 const CMimeRawAttachment
& operator=(const CMimeRawAttachment
& that
) throw( ... )
1600 CMimeAttachment::operator=(that
);
1601 if (!m_bShared
&& m_pvRaw
)
1604 m_bShared
= that
.m_bShared
;
1605 m_dwLength
= that
.m_dwLength
;
1609 m_pvRaw
= that
.m_pvRaw
;
1613 m_pvRaw
= malloc(m_dwLength
);
1616 Checked::memcpy_s(m_pvRaw
, m_dwLength
, that
.m_pvRaw
, m_dwLength
);
1624 // Initialize the attachment
1626 // nDataLength - the size of pData in BYTEs
1627 // bCopyData - flag specifying whether CMimeRawAttachment should make a copy of the data (optional)
1628 // pMultiLanguage - the IMultiLanguage pointer for codepage to character set conversion (optional)
1629 // uiCodePage - the codepage (optional)
1630 inline BOOL
Initialize(void* pData
, DWORD nDataLength
, BOOL bCopyData
= TRUE
, LPCTSTR szDisplayName
= NULL
,
1631 IMultiLanguage
* pMultiLanguage
= NULL
, UINT uiCodePage
= 0) throw()
1633 // if we're already attached to some data, and it's not shared, free it
1634 if (m_pvRaw
&& !m_bShared
)
1638 m_dwLength
= nDataLength
;
1641 m_pvRaw
= calloc(sizeof(BYTE
),m_dwLength
);
1646 Checked::memcpy_s(m_pvRaw
, m_dwLength
, pData
, m_dwLength
);
1655 if (!AtlMimeCharsetFromCodePage(m_szCharset
, uiCodePage
, pMultiLanguage
, ATL_MAX_ENC_CHARSET_LENGTH
))
1660 // use the user-specified display name
1661 Checked::tcscpy_s(m_szDisplayName
, _countof(m_szDisplayName
), szDisplayName
);
1662 m_szDisplayName
[_countof(m_szDisplayName
)-1] = 0;
1667 *m_szDisplayName
= '\0';
1672 // Output the data--similar to CFileAttachment::WriteData
1673 // See CFileAttachment::WriteData for comments
1674 virtual inline BOOL
WriteData(HANDLE hFile
, LPOVERLAPPED pOverlapped
, LPCSTR szBoundary
, DWORD dwFlags
= 0) throw()
1676 if ((pOverlapped
== NULL
) || (szBoundary
== NULL
))
1684 int nLineLength
= 0, nRequiredLength
= 0;
1685 if (!GetEncodingInformation(&nRequiredLength
, &nLineLength
))
1690 if (!MakeMimeHeader(header
, szBoundary
))
1695 if (!AtlSmtpSendAndWait(hFile
, ((LPCSTR
)header
), header
.GetLength(), pOverlapped
))
1700 int nGetLines
= ATLSMTP_GET_LINES
;
1701 DWORD dwCurrChunk
= 0;
1702 nRequiredLength
*= nGetLines
;
1703 DWORD dwToGet
= (DWORD
)nGetLines
*nLineLength
;
1704 int nDestLen
= nRequiredLength
;
1707 #ifdef ATLSMTP_DOUBLE_BUFFERED
1708 CHeapPtr
<char> buffer1
;
1709 if (!buffer1
.Allocate(nRequiredLength
+3))
1712 CHeapPtr
<char> buffer2
;
1713 if (!buffer2
.Allocate(nRequiredLength
+3))
1716 char* currBuffer
= buffer1
;
1717 char* prevBuffer
= NULL
;
1718 int nCurrBuffer
= 0;
1719 DWORD dwPrevLength
= 0;
1721 CHeapPtr
<char> currBuffer
;
1722 if (!currBuffer
.Allocate(nRequiredLength
+3))
1724 #endif // ATLSMTP_DOUBLE_BUFFERED
1728 if ((m_dwLength
-dwRead
) <= dwToGet
)
1729 dwCurrChunk
= m_dwLength
-dwRead
;
1731 dwCurrChunk
= dwToGet
;
1732 switch(m_nEncodingScheme
)
1734 case ATLSMTP_BASE64_ENCODE
:
1735 bRet
= Base64Encode(((LPBYTE
)(m_pvRaw
))+dwRead
, dwCurrChunk
, currBuffer
, &nDestLen
,
1736 (dwRead
< m_dwLength
) ? ATL_BASE64_FLAG_NONE
: ATL_BASE64_FLAG_NOPAD
);
1737 if (dwRead
+dwCurrChunk
== m_dwLength
)
1739 currBuffer
[nDestLen
++] = '\r';
1740 currBuffer
[nDestLen
++] = '\n';
1743 case ATLSMTP_UUENCODE
:
1744 bRet
= UUEncode(((LPBYTE
)(m_pvRaw
))+dwRead
, dwCurrChunk
, currBuffer
, &nDestLen
, _T("rawdata"),
1745 (dwRead
> 0 ? 0 : ATLSMTP_UUENCODE_HEADER
) |
1746 (dwRead
+dwCurrChunk
== m_dwLength
? ATLSMTP_UUENCODE_END
: 0) |
1747 ((dwFlags
& ATLSMTP_FORMAT_SMTP
) ? ATLSMTP_UUENCODE_DOT
: 0));
1749 case ATLSMTP_QP_ENCODE
:
1750 bRet
= QPEncode(((LPBYTE
)(m_pvRaw
))+dwRead
, dwCurrChunk
, currBuffer
, &nDestLen
,
1751 ((dwFlags
& ATLSMTP_FORMAT_SMTP
) ? ATLSMTP_QPENCODE_DOT
: 0) |
1752 (dwRead
+dwCurrChunk
== m_dwLength
? 0 : ATLSMTP_QPENCODE_TRAILING_SOFT
));
1757 #ifdef ATLSMTP_DOUBLE_BUFFERED
1758 bRet
= AtlSmtpSendOverlapped(hFile
, currBuffer
, nDestLen
, prevBuffer
, dwPrevLength
, pOverlapped
);
1759 dwPrevLength
= (DWORD
)nDestLen
;
1760 prevBuffer
= currBuffer
;
1761 currBuffer
= (nCurrBuffer
== 0 ? buffer2
: buffer1
);
1762 nCurrBuffer
= (nCurrBuffer
== 0 ? 1 : 0);
1764 bRet
= AtlSmtpSendAndWait(hFile
, currBuffer
, nDestLen
, pOverlapped
);
1765 #endif // ATLSMTP_DOUBLE_BUFFERED
1767 nDestLen
= nRequiredLength
;
1768 dwRead
+= dwCurrChunk
;
1769 } while (bRet
&& (dwRead
< m_dwLength
));
1771 //ensure all data is sent from prevBuffer
1772 #ifdef ATLSMTP_DOUBLE_BUFFERED
1773 DWORD dwWritten
= 0, dwErr
= 0;
1774 if (!GetOverlappedResult(hFile
, pOverlapped
, &dwWritten
, TRUE
))
1776 if ((dwErr
= GetLastError()) != ERROR_IO_PENDING
&& dwErr
!= ERROR_IO_INCOMPLETE
)
1778 else if (dwWritten
< dwPrevLength
)
1779 bRet
= AtlSmtpSendAndWait(hFile
, prevBuffer
+dwWritten
, dwPrevLength
-dwWritten
, pOverlapped
);
1781 #endif // ATLSMTP_DOUBLE_BUFFERED
1785 }; // class CMimeRawAttachment
1788 // CMimeText - represents a text body part in MIME body
1789 class CMimeText
: public CMimeBodyPart
1794 CHeapPtr
<char> m_szText
;
1796 // the character set
1797 char m_szCharset
[ATL_MAX_ENC_CHARSET_LENGTH
];
1806 Checked::strcpy_s(m_szCharset
, ATL_MAX_ENC_CHARSET_LENGTH
, ATLSMTP_DEFAULT_CSET
);
1809 virtual ~CMimeText() throw()
1813 // Get the content type
1814 virtual inline LPCSTR
GetContentType() throw()
1816 return "text/plain";
1819 // Get the character set
1820 virtual inline LPCSTR
GetCharset() throw()
1825 virtual ATL_NOINLINE CMimeBodyPart
* Copy() throw( ... )
1827 CAutoPtr
<CMimeText
> pNewText
;
1828 ATLTRY(pNewText
.Attach(new CMimeText
));
1832 return pNewText
.Detach();
1835 const CMimeText
& operator=(const CMimeText
& that
) throw( ... )
1839 m_nTextLen
= that
.m_nTextLen
;
1840 Checked::strcpy_s(m_szCharset
, ATL_MAX_ENC_CHARSET_LENGTH
, that
.m_szCharset
);
1842 if (m_szText
.AllocateBytes(m_nTextLen
) != false)
1844 Checked::memcpy_s((char *)m_szText
, m_nTextLen
, (char *)that
.m_szText
, m_nTextLen
);
1851 // Initialize the body part
1852 // szText - the text (required)
1853 // nTextLen - the text length in bytes (optional--if not specified a _tcslen will be done)
1854 // pMultiLanguage - the IMultiLanguagte pointer for converting codepages to MIME character sets (optional)
1855 // uiCodePage - the codepage
1856 inline BOOL
Initialize(LPCTSTR szText
, int nTextLen
= -1, IMultiLanguage
* pMultiLanguage
= NULL
, UINT uiCodePage
= 0) throw()
1860 // if IMultiLanguage is there, respect the codepage
1863 CHeapPtr
<char> szTextPtr
;
1866 bRet
= AtlMimeConvertString(pMultiLanguage
, uiCodePage
, szText
, &szTextPtr
, &nLen
);
1870 m_szText
.Attach(szTextPtr
.Detach());
1874 else // no multilanguage support
1878 nTextLen
= (int) _tcslen(szText
);
1879 nTextLen
*= sizeof(TCHAR
);
1883 if (m_szText
.AllocateBytes(nTextLen
) != false)
1885 Checked::memcpy_s((char *)m_szText
, nTextLen
, szText
, nTextLen
);
1886 m_nTextLen
= nTextLen
;
1892 bRet
= AtlMimeCharsetFromCodePage(m_szCharset
, uiCodePage
, pMultiLanguage
, ATL_MAX_ENC_CHARSET_LENGTH
);
1898 // Dump the data to hFile
1899 virtual inline BOOL
WriteData(HANDLE hFile
, LPOVERLAPPED pOverlapped
, LPCSTR szBoundary
, DWORD dwFlags
= 0) throw()
1901 if ((pOverlapped
== NULL
) || (szBoundary
== NULL
))
1907 char sendBuffer
[ATLSMTP_READBUFFER_SIZE
];
1908 LPSTR pSendBuffer
= sendBuffer
;
1909 LPSTR szText
= m_szText
;
1911 if (!MakeMimeHeader(strHeader
, szBoundary
))
1916 //copy the header into the sendbuffer
1917 int nWritten
= strHeader
.GetLength();
1918 if(nWritten
> ATLSMTP_READBUFFER_SIZE
)
1921 Checked::memcpy_s(pSendBuffer
, ATLSMTP_READBUFFER_SIZE
, (LPCSTR
)strHeader
, nWritten
);
1922 pSendBuffer
+= nWritten
;
1926 //subtract 2 from these because it's easier for when we have
1927 //to break lines with a CRLF
1928 int nMaxLineLength
= ATLSMTP_MAX_LINE_LENGTH
-2;
1929 int nMaxBufferSize
= ATLSMTP_READBUFFER_SIZE
-2;
1930 while (nRead
<= m_nTextLen
)
1932 //if the buffer is full or we've reached the end of the text,
1934 if (nWritten
>= nMaxBufferSize
|| nRead
== m_nTextLen
)
1936 if (!AtlSmtpSendAndWait(hFile
, sendBuffer
, nWritten
, pOverlapped
))
1939 pSendBuffer
= sendBuffer
;
1940 if (nRead
== m_nTextLen
)
1942 break; // job done, no need to run the code below
1946 //if we're at the end of the line, break it
1947 if (nLineLen
== nMaxLineLength
)
1949 if(nWritten
+ 2 > ATLSMTP_READBUFFER_SIZE
)
1951 *pSendBuffer
++ = '\r';
1952 *pSendBuffer
++ = '\n';
1958 //stuff dots at the start of the line
1959 if (nLineLen
== 0 && (dwFlags
& ATLSMTP_FORMAT_SMTP
) && *szText
== '.')
1961 if(nWritten
+ 1 > ATLSMTP_READBUFFER_SIZE
)
1963 *pSendBuffer
++ = '.';
1969 //if we hit a CRLF, reset nLineLen
1970 if (*szText
== '\n' && nRead
> 0 && *(szText
-1) == '\r')
1973 if(nWritten
+ 1 > ATLSMTP_READBUFFER_SIZE
)
1975 *pSendBuffer
++ = (*szText
++);
1986 // Make the MIME header
1987 virtual inline BOOL
MakeMimeHeader(CStringA
& header
, LPCSTR szBoundary
) throw()
1992 // this is not the only body part
1993 Checked::memcpy_s(szBegin
, sizeof(szBegin
), ATLMIME_SEPARATOR
, sizeof(ATLMIME_SEPARATOR
));
1994 Checked::memcpy_s(szBegin
+6, sizeof(szBegin
)-6, szBoundary
, ATL_MIME_BOUNDARYLEN
);
1995 *(szBegin
+(ATL_MIME_BOUNDARYLEN
+6)) = '\0';
1999 // this is the only body part, so output the full MIME header
2000 Checked::memcpy_s(szBegin
, sizeof(szBegin
), ATLMIME_VERSION
, sizeof(ATLMIME_VERSION
));
2005 header
.Format("%s\r\nContent-Type: text/plain;\r\n\tcharset=\"%s\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n",
2006 szBegin
, m_szCharset
);
2014 }; // class CMimeText
2017 // CMimeMessage - the MIME message class. Represents a full MIME message
2018 class CMimeMessage
: public CMimeHeader
2022 // The list of the MIME body parts
2023 CAutoPtrList
<CMimeBodyPart
> m_BodyParts
;
2025 // The display name of the message
2026 char m_szDisplayName
[MAX_PATH
+1];
2029 CMimeMessage(IMultiLanguage
*pMultiLanguage
= NULL
) throw()
2031 Initialize(pMultiLanguage
);
2032 Checked::memcpy_s(m_szDisplayName
, MAX_PATH
+1, ATLMIME_EMAIL
, sizeof(ATLMIME_EMAIL
));
2035 virtual ~CMimeMessage() throw()
2040 void RemoveParts() throw()
2042 m_BodyParts
.RemoveAll();
2046 virtual ATL_NOINLINE CMimeBodyPart
* Copy() throw( ... )
2048 CAutoPtr
<CMimeMessage
> pNewMessage
;
2049 ATLTRY(pNewMessage
.Attach(new CMimeMessage
));
2051 *pNewMessage
= *this;
2053 return pNewMessage
.Detach();
2057 const CMimeMessage
& operator=(const CMimeMessage
& that
) throw( ... )
2061 CMimeHeader::operator=(that
);
2062 Checked::strcpy_s(m_szDisplayName
, MAX_PATH
+1, that
.m_szDisplayName
);
2065 POSITION pos
= that
.m_BodyParts
.GetHeadPosition();
2068 CAutoPtr
<CMimeBodyPart
> pCopy(that
.m_BodyParts
.GetNext(pos
)->Copy());
2071 m_BodyParts
.AddTail(pCopy
);
2079 // Set the display name of the message
2080 inline BOOL
SetDisplayName(LPCTSTR szDisplayName
) throw()
2082 if (szDisplayName
== NULL
)
2089 CT2CA
szDisplayNameA(szDisplayName
);
2090 if (szDisplayNameA
== NULL
|| strlen(szDisplayNameA
) > MAX_PATH
)
2092 Checked::strcpy_s(m_szDisplayName
, MAX_PATH
+1, szDisplayNameA
);
2101 // Add some text to the message at position nPos in the body parts list
2102 // szText - the text
2103 // nTextLen - the size of the text in bytes (optional - if not specified a _tcslen will be done)
2104 // nPos - the position in the message at which to insert the text (optional)
2105 // uiCodePage - the codepage (optional)
2106 inline BOOL
AddText(LPCTSTR szText
, int nTextLen
= -1, int nPos
= 1, UINT uiCodePage
= 0) throw()
2116 CAutoPtr
<CMimeBodyPart
> spNewText
;
2117 CMimeText
*pNewText
= NULL
;
2118 ATLTRY(spNewText
.Attach(pNewText
= new CMimeText()));
2119 if (!spNewText
|| !pNewText
)
2122 BOOL bRet
= pNewText
->Initialize(szText
, nTextLen
, m_spMultiLanguage
, uiCodePage
);
2127 POSITION currPos
= m_BodyParts
.FindIndex(nPos
-1);
2131 if (!m_BodyParts
.AddTail(spNewText
))
2136 if (!m_BodyParts
.InsertBefore(currPos
, spNewText
))
2151 virtual BOOL
WriteData(HANDLE hFile
, LPOVERLAPPED pOverlapped
, LPCSTR szBoundary
=NULL
, DWORD dwFlags
= 0) throw()
2153 if (pOverlapped
== NULL
)
2158 // Make the MIME boundary for this message
2159 char szBoundaryBuf
[ATL_MIME_BOUNDARYLEN
+1];
2160 if(MakeBoundary(szBoundaryBuf
,ATL_MIME_BOUNDARYLEN
+1) == FALSE
)
2163 // if the passed boundary is valid, this is an attached message
2164 if (szBoundary
&& *szBoundary
!= '\0')
2168 // output the MIME header for a message attachment
2170 strHeader
.Format("\r\n\r\n--%s\r\nContent-Type: message/rfc822\r\n\tname=\"%s\"\r\nContent-Transfer-Encoding: 8bit\r\n"
2171 "Content-Disposition: attachment;\r\n\tfilename=\"%s\"\r\n\r\n",
2172 szBoundary
, m_szDisplayName
, m_szDisplayName
);
2174 if (!AtlSmtpSendAndWait(hFile
, ((LPCSTR
)strHeader
), strHeader
.GetLength(), pOverlapped
))
2185 if (!CMimeHeader::WriteData(hFile
, pOverlapped
, szBoundaryBuf
, dwFlags
))
2188 // Create and output the header
2191 if (!MakeMimeHeader(strHeader
, szBoundaryBuf
))
2196 if (!AtlSmtpSendAndWait(hFile
, ((LPCSTR
)strHeader
), strHeader
.GetLength(), pOverlapped
))
2201 CMimeBodyPart
* pCurrPart
;
2202 POSITION currPos
= m_BodyParts
.GetHeadPosition();
2204 //Dump the body parts
2205 while (currPos
!= NULL
)
2207 pCurrPart
= m_BodyParts
.GetAt(currPos
);
2208 if (!pCurrPart
->WriteData(hFile
, pOverlapped
, szBoundaryBuf
, dwFlags
))
2212 m_BodyParts
.GetNext(currPos
);
2215 char szBuf
[ATL_MIME_BOUNDARYLEN
+(sizeof("\r\n\r\n--%s--\r\n"))];
2216 //output a trailing boundary
2219 int nBufLen
= sprintf_s(szBuf
, ATL_MIME_BOUNDARYLEN
+(sizeof("\r\n\r\n--%s--\r\n")),
2220 "\r\n\r\n--%s--\r\n", szBoundaryBuf
);
2221 if ((nBufLen
< 0) || (!AtlSmtpSendAndWait(hFile
, szBuf
, nBufLen
, pOverlapped
)))
2231 // szFileName - the filename
2232 // szDisplayName - the display name (optional)
2233 // szContentType - the content type (optional - defaults to NULL -- lookup will be attempted, otherwise default to application/octet-stream)
2234 // nEncodingScheme - the encoding scheme to use for the attachment (optional - defaults to base64
2235 // uiCodePage - the codepage (optional)
2236 inline BOOL
AttachFile(LPCTSTR szFileName
, LPCTSTR szDisplayName
= NULL
, LPCTSTR szContentType
= NULL
,
2237 int nEncodingScheme
= ATLSMTP_BASE64_ENCODE
, UINT uiCodepage
= 0)
2239 if (szFileName
== NULL
)
2242 CAutoPtr
<CMimeBodyPart
> spFileAttach
;
2243 CMimeFileAttachment
* pFileAttach
= NULL
;
2244 ATLTRY(spFileAttach
.Attach(pFileAttach
= new CMimeFileAttachment()));
2245 if (!spFileAttach
|| !pFileAttach
)
2248 BOOL bRet
= pFileAttach
->Initialize(szFileName
, szDisplayName
, m_spMultiLanguage
, uiCodepage
);
2251 bRet
= pFileAttach
->SetEncodingScheme(nEncodingScheme
);
2253 CString strContentType
;
2254 if (bRet
&& (szContentType
== NULL
))
2256 if (GetContentTypeFromFileName(szFileName
, strContentType
) != ERROR_OUTOFMEMORY
)
2258 szContentType
= strContentType
;
2270 bRet
= pFileAttach
->SetContentType(szContentType
);
2273 if (!m_BodyParts
.AddTail(spFileAttach
))
2288 // Attach some raw data
2289 // pRawData - the data
2290 // nDataLength - the size of the data in bytes
2291 // nEncodingScheme - the encoding scheme to use for the attachment (optional - defaults to base64
2292 // uiCodePage - the codepage (optional)
2293 inline BOOL
AttachRaw(void* pRawData
, DWORD dwDataLength
, int nEncodingScheme
= ATLSMTP_BASE64_ENCODE
, BOOL bCopyData
= TRUE
,
2294 LPCTSTR szDisplayName
= NULL
, LPCTSTR szContentType
= _T("application/octet-stream"), UINT uiCodepage
= 0)
2299 CAutoPtr
<CMimeBodyPart
> spRawAttach
;
2300 CMimeRawAttachment
* pRawAttach
;
2301 ATLTRY(spRawAttach
.Attach(pRawAttach
= new CMimeRawAttachment()));
2307 BOOL bRet
= pRawAttach
->Initialize(pRawData
, dwDataLength
, bCopyData
, szDisplayName
, m_spMultiLanguage
, uiCodepage
);
2310 bRet
= pRawAttach
->SetEncodingScheme(nEncodingScheme
);
2312 bRet
= pRawAttach
->SetContentType(szContentType
);
2317 if(!m_BodyParts
.AddTail(spRawAttach
))
2328 // Attach a CMimeMessage
2329 // pMsg - pointer to the Msg object
2330 inline BOOL
AttachMessage(CMimeMessage
* pMsg
) throw( ... )
2337 CAutoPtr
<CMimeBodyPart
> spMsg(pMsg
->Copy());
2338 if (!m_BodyParts
.AddTail(spMsg
))
2350 // Make the MIME header
2351 virtual inline BOOL
MakeMimeHeader(CStringA
& header
, LPCSTR szBoundary
) throw()
2357 header
.Format("X-Priority: %d\r\n%s", m_nPriority
, (LPCSTR
) m_XHeader
);
2359 else if (m_BodyParts
.GetCount() > 1)
2361 header
.Format("X-Priority: %d\r\n%sMIME-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n\tboundary=\"%s\"\r\n",
2362 m_nPriority
, (LPCSTR
) m_XHeader
, szBoundary
);
2372 // Make the MIME boundary
2373 inline BOOL
MakeBoundary(__out_ecount_z(nBufLen
) LPSTR szBoundary
, __in
int nBufLen
)
2375 ATLENSURE(szBoundary
!= NULL
);
2382 if (m_BodyParts
.GetCount() < 2)
2388 int ret
= sprintf_s(szBoundary
, nBufLen
, "------=_Next_Part_%.10u.%.3u", GetTickCount(), rand()%1000);
2389 if (ret
== -1 || ret
>= nBufLen
)
2395 }; // class CMimeMessage
2401 #pragma warning (pop)
2404 #pragma warning(pop)
2406 #endif // __ATLMIME_H__