Refactoring in order to cache more operation.
[xy_vsfilter.git] / include / atl / atlmime.h
blob06d4fd2f4d3f9df02c06acf0e38cc7520368a27d
1 // This is a part of the Active Template Library.
2 // Copyright (C) Microsoft Corporation
3 // All rights reserved.
4 //
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.
11 #ifndef __ATLMIME_H__
12 #define __ATLMIME_H__
14 #pragma once
16 #include <tchar.h>
17 #include <time.h>
18 #include <atlbase.h>
19 #include <mlang.h>
20 #include <atlfile.h>
21 #include <atlcoll.h>
22 #include <atlstr.h>
23 #include <atlsmtputil.h>
24 #include <atlenc.h>
25 #include <atlspriv.h>
27 #pragma warning(push)
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
31 #ifndef _CPPUNWIND
32 #pragma warning (push)
33 #pragma warning(disable: 4702) // unreachable code
34 #endif // _CPPUNWIND
36 #pragma pack(push,_ATL_PACKING)
37 namespace ATL {
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
47 #ifndef ATLMIME_EMAIL
48 #define ATLMIME_EMAIL "email"
49 #endif//ATLMIME_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;
59 if (szDate == NULL)
61 return s_dwMaxBufferLen;
64 if (dwLen < 38)
66 return 0;
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" };
73 SYSTEMTIME st;
74 DWORD dwTimeZoneId=TIME_ZONE_ID_UNKNOWN;
75 CHAR cDiff;
76 LONG ltzBias=0;
77 LONG ltzHour;
78 LONG ltzMinute;
79 TIME_ZONE_INFORMATION tzi;
81 GetLocalTime(&st);
83 // Gets TIME_ZONE_INFORMATION
84 memset(&tzi, 0, sizeof(tzi));
85 dwTimeZoneId = GetTimeZoneInformation(&tzi);
86 switch (dwTimeZoneId)
88 case TIME_ZONE_ID_STANDARD:
89 ltzBias = tzi.Bias + tzi.StandardBias;
90 break;
92 case TIME_ZONE_ID_DAYLIGHT:
93 ltzBias = tzi.Bias + tzi.DaylightBias;
94 break;
96 case TIME_ZONE_ID_UNKNOWN:
97 default:
98 ltzBias = tzi.Bias;
99 break;
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"
114 st.wDay, // "dd"
115 s_months[nMonth], // "mmm"
116 st.wYear, // "yyyy"
117 st.wHour, // "hh"
118 st.wMinute, // "mm"
119 st.wSecond, // "ss"
120 cDiff, // "+" / "-"
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;
134 _ATLTRY
136 // get the file extension
137 TCHAR szExt[_MAX_EXT];
138 Checked::tsplitpath_s(szFileName, NULL, 0, NULL, 0, NULL, 0, szExt, _countof(szExt));
139 if (*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)
146 ULONG nChars=0;
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);
163 _ATLCATCHALL()
165 dwErr = ERROR_OUTOFMEMORY;
168 return dwErr;
171 // CMimeBodyPart is an abstract base class for the body parts
172 // CMimeAttachment, CMimeText, CMimeHeader.
173 class CMimeBodyPart
175 public:
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;
194 protected:
196 // MakeMimeHeader - pure virutal method to create a MIME header for a
197 // body part.
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
216 protected:
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;
223 //Basic Header Parts
224 CStringA m_strFrom;
225 CStringA m_strTo;
226 CStringA m_strCc;
227 CStringA m_strBcc;
228 CStringA m_strSubject;
230 //Extended Header Parts
231 ATL_MIME_PRIORITY m_nPriority;
232 CStringA m_XHeader;
234 //Display Names
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
243 public:
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;
263 else
265 HRESULT hr = m_spMultiLanguage.CoCreateInstance(__uuidof(CMultiLanguage), NULL, CLSCTX_INPROC_SERVER);
266 if (hr != S_OK)
267 return FALSE;
269 return TRUE;
272 // Get the content type
273 virtual inline LPCSTR GetContentType() throw()
275 return "text/plain";
278 // Get the character set
279 virtual inline LPCSTR GetCharset() throw()
281 return "iso-8859-1";
284 virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
286 CAutoPtr<CMimeHeader> pNewHeader;
287 ATLTRY(pNewHeader.Attach(new CMimeHeader));
288 if (pNewHeader)
289 *pNewHeader = *this;
291 return pNewHeader.Detach();
294 const CMimeHeader& operator=(const CMimeHeader& that) throw( ... )
296 if (this != &that)
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);
313 return *this;
316 // Set the priority of the message
317 inline BOOL SetPriority(ATL_MIME_PRIORITY nPriority) throw()
319 if (nPriority < 0)
320 return FALSE;
321 m_nPriority = nPriority;
322 return TRUE;
325 // Get the priority of the message
326 inline ATL_MIME_PRIORITY GetPriority() throw()
328 return m_nPriority;
331 // Set the display (friendly) name for the header
332 inline BOOL SetSenderName(LPCTSTR szName, UINT uiCodePage = 0) throw()
334 if (szName == NULL)
335 return FALSE;
337 CHeapPtr<char> szNamePtr;
338 UINT nLen(0);
340 BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szName, &szNamePtr, &nLen);
341 if (bRet)
343 _ATLTRY
345 m_strSenderName.Empty();
346 m_strSenderName.Append(szNamePtr, (int) nLen);
348 _ATLCATCHALL()
350 return FALSE;
352 bRet = AtlMimeCharsetFromCodePage(m_szSenderCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
355 return bRet;
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))
368 return FALSE;
370 _ATLTRY
372 CHeapPtr<char> szName;
373 UINT nLen(0);
375 BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szHeader, &szName, &nLen);
376 if (bRet)
378 // get the charset
379 char szCharset[ATL_MAX_ENC_CHARSET_LENGTH];
380 bRet = AtlMimeCharsetFromCodePage(szCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
382 if (bRet)
384 CStringA str;
385 str.Append(szName, (int)nLen);
387 // encode the string
388 CHeapPtr<char> szBuf;
389 DWORD dwReqLen = QEncodeGetRequiredLength(str.GetLength(),
390 ATL_MAX_ENC_CHARSET_LENGTH);
392 if (szBuf.Allocate(dwReqLen) == false)
394 return FALSE;
397 DWORD dwLength(0);
398 BOOL bEncoded = FALSE;
399 if (!GetEncodedString(str, szCharset, szBuf, dwReqLen, dwLength, bEncoded))
401 return FALSE;
404 // add to m_XHeader
405 m_XHeader += CT2CA(szHeaderName);
406 m_XHeader.Append(": ", 2);
407 m_XHeader.Append(szBuf, dwLength);
408 m_XHeader.Append("\r\n", 2);
412 return bRet;
414 _ATLCATCHALL()
416 return FALSE;
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()
429 return m_strTo;
432 // Clear all recipients ("To:" line)
433 inline BOOL ClearRecipients() throw()
435 m_strTo.Empty();
436 return TRUE;
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()
448 return m_strCc;
451 // Clear the recipients string ("CC:" line)
452 inline BOOL ClearCc() throw()
454 m_strCc.Empty();
455 return TRUE;
458 // Add a Bcc recipient (not output as part of message)
459 inline BOOL AddBcc(LPCTSTR szAddress) throw()
461 if (szAddress == NULL)
463 return FALSE;
466 _ATLTRY
468 CStringA str = m_strBcc;
470 if (m_strBcc.GetLength() > 0)
471 str.Append(",", 1);
473 str += CT2CA(szAddress);
475 m_strBcc = str;
477 return TRUE;
479 _ATLCATCHALL()
481 return FALSE;
485 // Get the recipients string (Bcc part)
486 inline LPCSTR GetBcc() throw()
488 return m_strBcc;
491 // Clear the recipients string (Bcc part)
492 inline BOOL ClearBcc() throw()
494 m_strBcc.Empty();
495 return TRUE;
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();
512 dwRet++;
513 return dwRet;
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) )
521 return FALSE;
524 if ( *pdwLen < GetRequiredRecipientsStringLength())
526 *pdwLen = GetRequiredRecipientsStringLength();
527 return FALSE;
530 DWORD dwMaxLen = *pdwLen;
531 *pdwLen = 0;
533 DWORD dwLen = 0;
534 DWORD dwTotalLen = 0;
535 if (m_strTo.GetLength() > 0)
537 dwLen = *pdwLen - dwTotalLen;
538 if (AtlMimeMakeRecipientsString(m_strTo, szRecip, &dwLen) != TRUE)
540 return FALSE;
542 szRecip+= dwLen;
543 dwTotalLen = dwLen;
546 if (m_strCc.GetLength() > 0)
548 if (dwTotalLen)
550 *szRecip++ = ',';
551 dwTotalLen++;
553 dwLen = *pdwLen - dwTotalLen;
554 if (AtlMimeMakeRecipientsString(m_strCc, szRecip, &dwLen) != TRUE)
556 return FALSE;
558 szRecip+= dwLen;
559 dwTotalLen+= dwLen;
562 if (m_strBcc.GetLength() > 0)
564 dwLen = m_strBcc.GetLength();
565 if (dwTotalLen)
567 *szRecip++ = ',';
568 dwTotalLen++;
570 dwLen = *pdwLen - dwTotalLen;
571 Checked::memcpy_s(szRecip, dwMaxLen-dwTotalLen, m_strBcc, dwLen);
572 szRecip+= dwLen;
573 dwTotalLen+= dwLen;
576 *szRecip = '\0';
577 *pdwLen = dwTotalLen;
579 return TRUE;
583 // Get the sender
584 inline LPCSTR GetSender() throw()
586 return m_strFrom;
589 // Set the sender
590 inline BOOL SetSender(LPCTSTR szSender) throw()
592 if (szSender == NULL)
593 return FALSE;
595 _ATLTRY
597 m_strFrom = CT2CA(szSender);
598 return TRUE;
600 _ATLCATCHALL()
602 return FALSE;
606 // Set the subject
607 inline BOOL SetSubject(LPCTSTR szSubject, UINT uiCodePage = 0) throw()
609 if (szSubject == NULL)
610 return FALSE;
612 _ATLTRY
614 CHeapPtr<char> szName;
615 UINT nLen(0);
617 BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szSubject, &szName, &nLen);
618 if (bRet)
620 m_strSubject.Empty();
621 m_strSubject.Append(szName, (int)nLen);
622 bRet = AtlMimeCharsetFromCodePage(m_szSubjectCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
625 return bRet;
627 _ATLCATCHALL()
629 return FALSE;
633 // Get the subject
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)
644 return FALSE;
647 int nMaxSendLen = GetRequiredBufferSize(ATLSMTP_MAX_LINE_LENGTH-4);
648 CHeapPtr<char> spSendBuffer;
649 if (!spSendBuffer.Allocate(nMaxSendLen))
650 return FALSE;
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))
661 return FALSE;
663 int nMaxLen = nBufLen;
664 DWORD dwOffset = 0;
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 )
674 return FALSE;
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;
682 *spBuf = '\0';
684 // Get the sender name
685 BOOL bRet = TRUE;
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)
700 return FALSE;
702 *(spBuf+dwHeaderPartLength++) = ' ';
705 if(dwHeaderPartLength + m_strFrom.GetLength() + 2 > nBufLen)
706 return FALSE;
708 *(spBuf+dwHeaderPartLength++) = '<';
709 if (dwHeaderPartLength < 0 || dwHeaderPartLength > nMaxLen)
711 return FALSE;
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 )
723 return FALSE;
724 if (dwOffset > static_cast<DWORD>(nMaxSendLen))
726 return FALSE;
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)
740 dwLength = 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 )
746 return FALSE;
747 if (dwOffset > static_cast<DWORD>(nMaxSendLen))
749 return FALSE;
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 )
766 return FALSE;
767 if (dwOffset > static_cast<DWORD>(nMaxSendLen))
769 return FALSE;
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 )
785 return FALSE;
786 if (dwOffset > static_cast<DWORD>(nMaxSendLen))
788 return FALSE;
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';
799 // Send the header
800 if (bRet && dwOffset)
801 bRet = AtlSmtpSendAndWait(hFile, pSendBuffer, dwOffset, pOverlapped);
803 return bRet;
806 protected:
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
812 ATLASSERT(FALSE);
813 return TRUE;
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;
820 bEncoded = FALSE;
821 if (m_spMultiLanguage.p)
823 // only encode if there are 8bit characters
824 int nExtendedChars = GetExtendedChars(headerString, headerString.GetLength());
825 if (nExtendedChars)
827 // choose smallest encoding
828 if (((nExtendedChars*100)/headerString.GetLength()) < 17)
830 int nEncCnt = 0;
831 if (!QEncode((LPBYTE)((LPCSTR)headerString), headerString.GetLength(), szBuf, &nBufLen, szCharset, &nEncCnt))
833 return FALSE;
836 //if no unsafe characters were encountered, just output it
837 if (nEncCnt != 0)
839 bEncoded = TRUE;
842 else
844 if (!BEncode((LPBYTE)((LPCSTR)headerString), headerString.GetLength(), szBuf, &nBufLen, szCharset))
846 return FALSE;
849 bEncoded = TRUE;
854 if (!bEncoded)
856 // there was no encoding
857 dwLength = (DWORD) headerString.GetLength();
858 if(dwLength > DWORD(nBufLen))
859 return FALSE;
860 Checked::memcpy_s(szBuf, nBufLen, headerString, dwLength);
862 else
864 dwLength = nBufLen;
866 return TRUE;
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))
875 return FALSE;
878 _ATLTRY
880 if (szName)
882 CHeapPtr<char> szNamePtr;
883 UINT nLen(0);
885 BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szName, &szNamePtr, &nLen);
886 if (bRet)
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))
894 return FALSE;
897 CFixedStringT<CStringA, 256> strBuf;
899 int nBufLen = QEncodeGetRequiredLength(Name.GetLength(),
900 ATL_MAX_ENC_CHARSET_LENGTH)+1;
902 char * szBuf = strBuf.GetBuffer(nBufLen);
903 if (szBuf == NULL)
905 return FALSE;
908 DWORD dwLength = 0;
909 BOOL bEncoded = FALSE;
910 if (!GetEncodedString(Name, szCharset, szBuf, nBufLen, dwLength, bEncoded))
912 strBuf.ReleaseBuffer();
913 return FALSE;
916 strBuf.ReleaseBuffer(dwLength);
918 // append comma if there are existing recipients
919 if (str.GetLength() != 0)
921 str.Append(", ", 2);
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
931 str.Append("\"", 1);
933 str += strBuf;
934 if (bEncoded == FALSE)
936 // close quote
937 str.Append("\"", 1);
940 else
942 return bRet;
946 if (szAddress)
948 if (szName)
950 str.Append(" ", 1);
952 else
954 // append comma if there are existing recipients
955 if (str.GetLength() != 0)
957 str.Append(", ", 2);
960 str.Append("<", 1);
961 str += CT2CA(szAddress);
962 str.Append(">", 1);
964 return TRUE;
966 _ATLCATCHALL()
968 return FALSE;
972 // Get the formatted header information
973 inline BOOL FormatField(LPBYTE pbSrcData, int nSrcLen, LPBYTE pbDest,
974 DWORD* pnBufLen, DWORD dwFlags = 0) throw()
976 if(pnBufLen == NULL)
977 return FALSE;
979 int nRead = 0;
981 // 9 is the length of the maximum field name : "Subject :"
982 // we set that here for simplicity
983 int nLineLen = 9;
984 DWORD nWritten = 0;
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)
995 return FALSE;
997 *pbDest++ = '\r';
998 *pbDest++ = '\n';
999 nWritten+= 2;
1000 nLineLen = -1;
1002 if ((dwFlags & ATLSMTP_FORMAT_SMTP))
1004 if(nWritten + 1 > *pnBufLen)
1005 return FALSE;
1007 *pbDest++ = '\t';
1008 nWritten++;
1009 nLineLen++;
1013 //if we hit a CRLF, reset nLineLen
1014 if (*pbSrcData == '\n' && nRead > 0 && *(pbSrcData-1) == '\r')
1016 nLineLen = -1;
1019 if(nWritten + 1 > *pnBufLen)
1020 return FALSE;
1022 *pbDest++ = *pbSrcData++;
1023 nRead++;
1024 nWritten++;
1025 nLineLen++;
1028 *pnBufLen = (DWORD)nWritten;
1030 return TRUE;
1034 // Get the formatted recipient information
1035 inline BOOL FormatRecipients(LPBYTE pbSrcData, int nSrcLen, LPBYTE pbDest,
1036 DWORD* pnBufLen) throw()
1039 if(pnBufLen == NULL)
1040 return FALSE;
1042 int nRead = 0;
1043 DWORD nWritten = 0;
1045 while (nRead < nSrcLen)
1047 if (*pbSrcData == ',')
1049 if(nWritten + 4 > *pnBufLen)
1050 return FALSE;
1052 *pbDest++ = *pbSrcData++;
1053 nRead++;
1054 if (nRead+1 <= nSrcLen && *pbSrcData == ' ')
1056 pbSrcData++;
1057 nRead++;
1059 *pbDest++ = '\r';
1060 *pbDest++ = '\n';
1061 *pbDest++ = '\t';
1062 nWritten+= 4;
1064 continue;
1067 if(nWritten + 1 > *pnBufLen)
1068 return FALSE;
1070 *pbDest++ = *pbSrcData++;
1071 nRead++;
1072 nWritten++;
1075 *pnBufLen = nWritten;
1077 return TRUE;
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,
1090 //so default to it)
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);
1113 //Trailing CRLF
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
1126 protected:
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];
1143 public:
1144 CMimeAttachment() throw()
1145 :m_nEncodingScheme(ATLSMTP_BASE64_ENCODE), m_pszEncodeString(NULL)
1147 m_szCharset[0] = 0;
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)
1163 return FALSE;
1166 m_nEncodingScheme = nScheme;
1167 return TRUE;
1170 // Set the Content-Type of the attachment
1171 inline BOOL SetContentType(LPCTSTR szContent) throw()
1173 _ATLTRY
1175 m_ContentType = CT2CA(szContent);
1176 return TRUE;
1178 _ATLCATCHALL()
1180 return FALSE;
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()
1193 return m_szCharset;
1196 virtual ATL_NOINLINE CMimeBodyPart* Copy() = 0;
1198 const CMimeAttachment& operator=(const CMimeAttachment& that) throw( ... )
1200 if (this != &that)
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);
1209 return *this;
1212 protected:
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);
1228 char szBegin[256];
1229 if (*szBoundary)
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';
1236 else
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);
1248 _ATLTRY
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);
1261 return TRUE;
1263 _ATLCATCHALL()
1265 return FALSE;
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);
1281 break;
1282 case ATLSMTP_UUENCODE:
1283 m_pszEncodeString ="uuencode";
1284 *pnLineLength = ATLSMTP_MAX_UUENCODE_LINE_LENGTH;
1285 *pnRequiredLength = UUEncodeGetRequiredLength(ATLSMTP_MAX_UUENCODE_LINE_LENGTH);
1286 break;
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);
1291 break;
1292 default:
1293 return FALSE;
1295 return TRUE;
1298 }; // class CMimeAttachment
1301 // CMimeFileAttachment represents a MIME file attachment body part
1302 class CMimeFileAttachment : public CMimeAttachment
1305 protected:
1306 // The filename
1307 TCHAR m_szFileName[MAX_PATH+1];
1309 public:
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));
1319 if (pNewAttachment)
1320 *pNewAttachment = *this;
1322 return pNewAttachment.Detach();
1325 const CMimeFileAttachment& operator=(const CMimeFileAttachment& that) throw( ... )
1327 if (this != &that)
1329 CMimeAttachment::operator=(that);
1330 Checked::tcscpy_s(m_szFileName, _countof(m_szFileName), that.m_szFileName);
1333 return *this;
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))
1345 return FALSE;
1347 if( _tcslen(szFileName) > MAX_PATH )
1349 return FALSE;
1351 Checked::tcscpy_s(m_szFileName, _countof(m_szFileName), szFileName);
1353 if (szDisplayName)
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);
1361 else
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("..."));
1367 else
1369 // otherwise there is no display name
1370 *m_szDisplayName = '\0';
1372 return TRUE;
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))
1380 return FALSE;
1383 int nLineLength = 0;
1384 int nRequiredLength = 0;
1386 if (!GetEncodingInformation(&nRequiredLength, &nLineLength))
1387 return FALSE;
1389 //Try to open the file that is being attached
1390 CAtlFile readFile;
1391 if (FAILED(readFile.Create(m_szFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)))
1392 return FALSE;
1394 //Make the mime header
1395 CStringA header;
1396 if (!MakeMimeHeader(header, szBoundary, m_szFileName))
1398 return FALSE;
1401 //Try to send the mime header
1402 if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)header), header.GetLength(), pOverlapped))
1404 return FALSE;
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))
1417 return FALSE;
1419 // if double buffering is defined, create two buffers
1420 #ifdef ATLSMTP_DOUBLE_BUFFERED
1421 CHeapPtr<char> buffer1;
1422 if (!buffer1.Allocate(nRequiredLength+3))
1423 return FALSE;
1425 CHeapPtr<char> buffer2;
1426 if (!buffer2.Allocate(nRequiredLength+3))
1427 return FALSE;
1429 char* currBuffer = buffer1;
1430 char* prevBuffer = NULL;
1431 int nCurrBuffer = 0;
1432 DWORD dwPrevLength = 0;
1433 #else
1434 CHeapPtr<char> currBuffer;
1435 if (!currBuffer.Allocate(nRequiredLength+3))
1436 return FALSE;
1438 #endif // ATLSMTP_DOUBLE_BUFFERED
1440 int nEncodedLength = nRequiredLength;
1441 BOOL bRet = FALSE;
1442 DWORD dwRead = 0;
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);
1453 if (FAILED(hr))
1455 if (hr != AtlHresultFromWin32(ERROR_MORE_DATA))
1457 return FALSE;
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
1470 //(ATL_FLAG_NONE)
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';
1479 break;
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));
1488 break;
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));
1494 break;
1496 //try to send the encoded data
1497 #ifdef ATLSMTP_DOUBLE_BUFFERED
1498 if (bRet)
1500 bRet = AtlSmtpSendOverlapped(hFile, currBuffer, nEncodedLength,
1501 prevBuffer, dwPrevLength, pOverlapped);
1504 //swap the buffers
1505 dwPrevLength = nEncodedLength;
1506 prevBuffer = currBuffer;
1507 currBuffer = (nCurrBuffer == 0 ? buffer2 : buffer1);
1508 nCurrBuffer = (nCurrBuffer == 0 ? 1 : 0);
1509 #else
1510 if (bRet)
1512 bRet = AtlSmtpSendAndWait(hFile, currBuffer, nEncodedLength, pOverlapped);
1514 #endif // ATLSMTP_DOUBLE_BUFFERED
1516 dwTotalRead += dwCurrRead;
1517 if (dwRead != 0)
1518 dwCurrRead = 0;
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)
1531 bRet = FALSE;
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);
1548 if (bRet)
1550 bRet = AtlSmtpSendAndWait(hFile, currBuffer, nEncodedLength, pOverlapped);
1554 return bRet;
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
1562 protected:
1564 //the raw data
1565 void* m_pvRaw;
1567 //the length
1568 DWORD m_dwLength;
1570 //whether or not we own it
1571 bool m_bShared;
1573 public:
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)
1583 free(m_pvRaw);
1586 virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
1588 CAutoPtr<CMimeRawAttachment> pNewAttachment;
1589 ATLTRY(pNewAttachment.Attach(new CMimeRawAttachment));
1590 if (pNewAttachment)
1591 *pNewAttachment = *this;
1593 return pNewAttachment.Detach();
1596 const CMimeRawAttachment& operator=(const CMimeRawAttachment& that) throw( ... )
1598 if (this != &that)
1600 CMimeAttachment::operator=(that);
1601 if (!m_bShared && m_pvRaw)
1602 free(m_pvRaw);
1604 m_bShared = that.m_bShared;
1605 m_dwLength = that.m_dwLength;
1607 if (m_bShared)
1609 m_pvRaw = that.m_pvRaw;
1611 else
1613 m_pvRaw = malloc(m_dwLength);
1614 if (m_pvRaw)
1616 Checked::memcpy_s(m_pvRaw, m_dwLength, that.m_pvRaw, m_dwLength);
1621 return *this;
1624 // Initialize the attachment
1625 // pData - the data
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)
1635 free(m_pvRaw);
1636 m_pvRaw = NULL;
1638 m_dwLength = nDataLength;
1639 if (bCopyData)
1641 m_pvRaw = calloc(sizeof(BYTE),m_dwLength);
1642 if (!m_pvRaw)
1644 return FALSE;
1646 Checked::memcpy_s(m_pvRaw, m_dwLength, pData, m_dwLength);
1647 m_bShared = false;
1649 else
1651 m_pvRaw = pData;
1652 m_bShared = true;
1655 if (!AtlMimeCharsetFromCodePage(m_szCharset, uiCodePage, pMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH))
1656 return FALSE;
1658 if (szDisplayName)
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;
1664 else
1666 // no display name
1667 *m_szDisplayName = '\0';
1669 return TRUE;
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))
1678 return FALSE;
1681 if (!m_pvRaw)
1682 return FALSE;
1684 int nLineLength = 0, nRequiredLength = 0;
1685 if (!GetEncodingInformation(&nRequiredLength, &nLineLength))
1686 return FALSE;
1688 CStringA header;
1690 if (!MakeMimeHeader(header, szBoundary))
1692 return FALSE;
1695 if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)header), header.GetLength(), pOverlapped))
1697 return FALSE;
1700 int nGetLines = ATLSMTP_GET_LINES;
1701 DWORD dwCurrChunk = 0;
1702 nRequiredLength *= nGetLines;
1703 DWORD dwToGet = (DWORD)nGetLines*nLineLength;
1704 int nDestLen = nRequiredLength;
1705 BOOL bRet = FALSE;
1706 DWORD dwRead = 0;
1707 #ifdef ATLSMTP_DOUBLE_BUFFERED
1708 CHeapPtr<char> buffer1;
1709 if (!buffer1.Allocate(nRequiredLength+3))
1710 return FALSE;
1712 CHeapPtr<char> buffer2;
1713 if (!buffer2.Allocate(nRequiredLength+3))
1714 return FALSE;
1716 char* currBuffer = buffer1;
1717 char* prevBuffer = NULL;
1718 int nCurrBuffer = 0;
1719 DWORD dwPrevLength = 0;
1720 #else
1721 CHeapPtr<char> currBuffer;
1722 if (!currBuffer.Allocate(nRequiredLength+3))
1723 return FALSE;
1724 #endif // ATLSMTP_DOUBLE_BUFFERED
1728 if ((m_dwLength-dwRead) <= dwToGet)
1729 dwCurrChunk = m_dwLength-dwRead;
1730 else
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';
1742 break;
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));
1748 break;
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));
1753 break;
1755 if (!bRet)
1756 break;
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);
1763 #else
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)
1777 bRet = FALSE;
1778 else if (dwWritten < dwPrevLength)
1779 bRet = AtlSmtpSendAndWait(hFile, prevBuffer+dwWritten, dwPrevLength-dwWritten, pOverlapped);
1781 #endif // ATLSMTP_DOUBLE_BUFFERED
1783 return bRet;
1785 }; // class CMimeRawAttachment
1788 // CMimeText - represents a text body part in MIME body
1789 class CMimeText : public CMimeBodyPart
1791 protected:
1793 // the text
1794 CHeapPtr<char> m_szText;
1796 // the character set
1797 char m_szCharset[ATL_MAX_ENC_CHARSET_LENGTH];
1799 // the text length
1800 int m_nTextLen;
1802 public:
1803 CMimeText() throw()
1804 :m_nTextLen(0)
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()
1822 return m_szCharset;
1825 virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
1827 CAutoPtr<CMimeText> pNewText;
1828 ATLTRY(pNewText.Attach(new CMimeText));
1829 if (pNewText)
1830 *pNewText = *this;
1832 return pNewText.Detach();
1835 const CMimeText& operator=(const CMimeText& that) throw( ... )
1837 if (this != &that)
1839 m_nTextLen = that.m_nTextLen;
1840 Checked::strcpy_s(m_szCharset, ATL_MAX_ENC_CHARSET_LENGTH, that.m_szCharset);
1841 m_szText.Free();
1842 if (m_szText.AllocateBytes(m_nTextLen) != false)
1844 Checked::memcpy_s((char *)m_szText, m_nTextLen, (char *)that.m_szText, m_nTextLen);
1848 return *this;
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()
1858 BOOL bRet = TRUE;
1860 // if IMultiLanguage is there, respect the codepage
1861 if (pMultiLanguage)
1863 CHeapPtr<char> szTextPtr;
1864 UINT nLen(0);
1866 bRet = AtlMimeConvertString(pMultiLanguage, uiCodePage, szText, &szTextPtr, &nLen);
1867 if (bRet)
1869 m_szText.Free();
1870 m_szText.Attach(szTextPtr.Detach());
1871 m_nTextLen = nLen;
1874 else // no multilanguage support
1876 if (nTextLen < 0)
1878 nTextLen = (int) _tcslen(szText);
1879 nTextLen*= sizeof(TCHAR);
1882 m_szText.Free();
1883 if (m_szText.AllocateBytes(nTextLen) != false)
1885 Checked::memcpy_s((char *)m_szText, nTextLen, szText, nTextLen);
1886 m_nTextLen = nTextLen;
1890 if (bRet)
1892 bRet = AtlMimeCharsetFromCodePage(m_szCharset, uiCodePage, pMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
1895 return bRet;
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))
1903 return FALSE;
1906 CStringA strHeader;
1907 char sendBuffer[ATLSMTP_READBUFFER_SIZE];
1908 LPSTR pSendBuffer = sendBuffer;
1909 LPSTR szText = m_szText;
1911 if (!MakeMimeHeader(strHeader, szBoundary))
1913 return FALSE;
1916 //copy the header into the sendbuffer
1917 int nWritten = strHeader.GetLength();
1918 if(nWritten > ATLSMTP_READBUFFER_SIZE)
1919 return FALSE;
1921 Checked::memcpy_s(pSendBuffer, ATLSMTP_READBUFFER_SIZE, (LPCSTR)strHeader, nWritten);
1922 pSendBuffer+= nWritten;
1923 int nRead = 0;
1924 int nLineLen = 0;
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,
1933 //send it
1934 if (nWritten >= nMaxBufferSize || nRead == m_nTextLen)
1936 if (!AtlSmtpSendAndWait(hFile, sendBuffer, nWritten, pOverlapped))
1937 return FALSE;
1938 nWritten = 0;
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)
1950 return FALSE;
1951 *pSendBuffer++ = '\r';
1952 *pSendBuffer++ = '\n';
1953 nWritten+= 2;
1954 nLineLen = -1;
1955 continue;
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)
1962 return FALSE;
1963 *pSendBuffer++ = '.';
1964 nWritten++;
1965 nLineLen++;
1966 continue;
1969 //if we hit a CRLF, reset nLineLen
1970 if (*szText == '\n' && nRead > 0 && *(szText-1) == '\r')
1971 nLineLen = -1;
1973 if(nWritten + 1 > ATLSMTP_READBUFFER_SIZE)
1974 return FALSE;
1975 *pSendBuffer++ = (*szText++);
1976 nRead++;
1977 nWritten++;
1978 nLineLen++;
1981 return TRUE;
1984 protected:
1986 // Make the MIME header
1987 virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) throw()
1989 char szBegin[256];
1990 if (*szBoundary)
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';
1997 else
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));
2003 _ATLTRY
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);
2007 return TRUE;
2009 _ATLCATCHALL()
2011 return FALSE;
2014 }; // class CMimeText
2017 // CMimeMessage - the MIME message class. Represents a full MIME message
2018 class CMimeMessage : public CMimeHeader
2020 protected:
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];
2028 public:
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()
2037 RemoveParts();
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));
2050 if (pNewMessage)
2051 *pNewMessage = *this;
2053 return pNewMessage.Detach();
2057 const CMimeMessage& operator=(const CMimeMessage& that) throw( ... )
2059 if (this != &that)
2061 CMimeHeader::operator=(that);
2062 Checked::strcpy_s(m_szDisplayName, MAX_PATH+1, that.m_szDisplayName);
2064 RemoveParts();
2065 POSITION pos = that.m_BodyParts.GetHeadPosition();
2066 while (pos != NULL)
2068 CAutoPtr<CMimeBodyPart> pCopy(that.m_BodyParts.GetNext(pos)->Copy());
2069 if (pCopy)
2071 m_BodyParts.AddTail(pCopy);
2076 return *this;
2079 // Set the display name of the message
2080 inline BOOL SetDisplayName(LPCTSTR szDisplayName) throw()
2082 if (szDisplayName == NULL)
2084 return FALSE;
2087 _ATLTRY
2089 CT2CA szDisplayNameA(szDisplayName);
2090 if (szDisplayNameA == NULL || strlen(szDisplayNameA) > MAX_PATH)
2091 return FALSE;
2092 Checked::strcpy_s(m_szDisplayName, MAX_PATH+1, szDisplayNameA);
2093 return TRUE;
2095 _ATLCATCHALL()
2097 return FALSE;
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()
2108 if (szText == NULL)
2109 return FALSE;
2111 if (nPos < 1)
2113 nPos = 1;
2116 CAutoPtr<CMimeBodyPart> spNewText;
2117 CMimeText *pNewText = NULL;
2118 ATLTRY(spNewText.Attach(pNewText = new CMimeText()));
2119 if (!spNewText || !pNewText)
2120 return FALSE;
2122 BOOL bRet = pNewText->Initialize(szText, nTextLen, m_spMultiLanguage, uiCodePage);
2123 if (bRet)
2125 _ATLTRY
2127 POSITION currPos = m_BodyParts.FindIndex(nPos-1);
2129 if (!currPos)
2131 if (!m_BodyParts.AddTail(spNewText))
2132 bRet = FALSE;
2134 else
2136 if (!m_BodyParts.InsertBefore(currPos, spNewText))
2137 bRet = FALSE;
2141 _ATLCATCHALL()
2143 bRet = FALSE;
2147 return bRet;
2150 // Dump the data
2151 virtual BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary=NULL, DWORD dwFlags = 0) throw()
2153 if (pOverlapped == NULL)
2155 return FALSE;
2158 // Make the MIME boundary for this message
2159 char szBoundaryBuf[ATL_MIME_BOUNDARYLEN+1];
2160 if(MakeBoundary(szBoundaryBuf,ATL_MIME_BOUNDARYLEN+1) == FALSE)
2161 return FALSE;
2163 // if the passed boundary is valid, this is an attached message
2164 if (szBoundary && *szBoundary != '\0')
2166 _ATLTRY
2168 // output the MIME header for a message attachment
2169 CStringA strHeader;
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))
2176 return FALSE;
2179 _ATLCATCHALL()
2181 return FALSE;
2185 if (!CMimeHeader::WriteData(hFile, pOverlapped, szBoundaryBuf, dwFlags))
2186 return FALSE;
2188 // Create and output the header
2189 CStringA strHeader;
2191 if (!MakeMimeHeader(strHeader, szBoundaryBuf))
2193 return FALSE;
2196 if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)strHeader), strHeader.GetLength(), pOverlapped))
2198 return FALSE;
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))
2210 return FALSE;
2212 m_BodyParts.GetNext(currPos);
2215 char szBuf[ATL_MIME_BOUNDARYLEN+(sizeof("\r\n\r\n--%s--\r\n"))];
2216 //output a trailing boundary
2217 if (*szBoundaryBuf)
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)))
2223 return FALSE;
2227 return TRUE;
2230 // Attach a file.
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)
2240 return FALSE;
2242 CAutoPtr<CMimeBodyPart> spFileAttach;
2243 CMimeFileAttachment* pFileAttach = NULL;
2244 ATLTRY(spFileAttach.Attach(pFileAttach = new CMimeFileAttachment()));
2245 if (!spFileAttach || !pFileAttach)
2246 return FALSE;
2248 BOOL bRet = pFileAttach->Initialize(szFileName, szDisplayName, m_spMultiLanguage, uiCodepage);
2250 if (bRet)
2251 bRet = pFileAttach->SetEncodingScheme(nEncodingScheme);
2253 CString strContentType;
2254 if (bRet && (szContentType == NULL))
2256 if (GetContentTypeFromFileName(szFileName, strContentType) != ERROR_OUTOFMEMORY)
2258 szContentType = strContentType;
2260 else
2262 bRet = FALSE;
2266 _ATLTRY
2268 if (bRet)
2270 bRet = pFileAttach->SetContentType(szContentType);
2271 if (bRet)
2273 if (!m_BodyParts.AddTail(spFileAttach))
2275 bRet = FALSE;
2280 _ATLCATCHALL()
2282 bRet = FALSE;
2285 return bRet;
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)
2296 if (!pRawData)
2297 return FALSE;
2299 CAutoPtr<CMimeBodyPart> spRawAttach;
2300 CMimeRawAttachment* pRawAttach;
2301 ATLTRY(spRawAttach.Attach(pRawAttach = new CMimeRawAttachment()));
2302 if (!spRawAttach)
2304 return FALSE;
2307 BOOL bRet = pRawAttach->Initialize(pRawData, dwDataLength, bCopyData, szDisplayName, m_spMultiLanguage, uiCodepage);
2309 if (bRet)
2310 bRet = pRawAttach->SetEncodingScheme(nEncodingScheme);
2311 if (bRet)
2312 bRet = pRawAttach->SetContentType(szContentType);
2314 _ATLTRY
2316 if (bRet)
2317 if(!m_BodyParts.AddTail(spRawAttach))
2318 bRet = FALSE;
2320 _ATLCATCHALL()
2322 bRet = FALSE;
2325 return bRet;
2328 // Attach a CMimeMessage
2329 // pMsg - pointer to the Msg object
2330 inline BOOL AttachMessage(CMimeMessage* pMsg) throw( ... )
2332 if (!pMsg)
2333 return FALSE;
2335 _ATLTRY
2337 CAutoPtr<CMimeBodyPart> spMsg(pMsg->Copy());
2338 if (!m_BodyParts.AddTail(spMsg))
2339 return FALSE;
2341 _ATLCATCHALL()
2343 return FALSE;
2346 return TRUE;
2349 protected:
2350 // Make the MIME header
2351 virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) throw()
2353 _ATLTRY
2355 if (!*szBoundary)
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);
2364 return TRUE;
2366 _ATLCATCHALL()
2368 return FALSE;
2372 // Make the MIME boundary
2373 inline BOOL MakeBoundary(__out_ecount_z(nBufLen) LPSTR szBoundary, __in int nBufLen)
2375 ATLENSURE(szBoundary != NULL);
2377 if(nBufLen < 1)
2379 return FALSE;
2382 if (m_BodyParts.GetCount() < 2)
2384 *szBoundary = '\0';
2386 else
2388 int ret = sprintf_s(szBoundary, nBufLen, "------=_Next_Part_%.10u.%.3u", GetTickCount(), rand()%1000);
2389 if (ret == -1 || ret >= nBufLen)
2390 return FALSE;
2392 return TRUE;
2395 }; // class CMimeMessage
2397 } // namespace ATL
2398 #pragma pack(pop)
2400 #ifndef _CPPUNWIND
2401 #pragma warning (pop)
2402 #endif //_CPPUNWIND
2404 #pragma warning(pop)
2406 #endif // __ATLMIME_H__