2407 lines
61 KiB
C++
2407 lines
61 KiB
C++
// This is a part of the Active Template Library.
|
|
// Copyright (C) Microsoft Corporation
|
|
// All rights reserved.
|
|
//
|
|
// This source code is only intended as a supplement to the
|
|
// Active Template Library Reference and related
|
|
// electronic documentation provided with the library.
|
|
// See these sources for detailed information regarding the
|
|
// Active Template Library product.
|
|
|
|
#ifndef __ATLMIME_H__
|
|
#define __ATLMIME_H__
|
|
|
|
#pragma once
|
|
|
|
#include <tchar.h>
|
|
#include <time.h>
|
|
#include <atlbase.h>
|
|
#include <mlang.h>
|
|
#include <atlfile.h>
|
|
#include <atlcoll.h>
|
|
#include <atlstr.h>
|
|
#include <atlsmtputil.h>
|
|
#include <atlenc.h>
|
|
#include <atlspriv.h>
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
|
|
#pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
|
|
|
|
#ifndef _CPPUNWIND
|
|
#pragma warning (push)
|
|
#pragma warning(disable: 4702) // unreachable code
|
|
#endif // _CPPUNWIND
|
|
|
|
#pragma pack(push,_ATL_PACKING)
|
|
namespace ATL {
|
|
|
|
#ifndef ATLMIME_SEPARATOR
|
|
#define ATLMIME_SEPARATOR "\r\n\r\n--"
|
|
#endif//ATLMIME_SEPARATOR
|
|
|
|
#ifndef ATLMIME_VERSION
|
|
#define ATLMIME_VERSION "MIME-Version: 1.0"
|
|
#endif//ATLMIME_VERSION
|
|
|
|
#ifndef ATLMIME_EMAIL
|
|
#define ATLMIME_EMAIL "email"
|
|
#endif//ATLMIME_EMAIL
|
|
|
|
extern __declspec(selectany) const DWORD ATL_MIME_BOUNDARYLEN = 32;
|
|
extern __declspec(selectany) const DWORD ATL_MIME_DATE_LEN = 64;
|
|
|
|
// Called when message is sent - sets the "Date:" field
|
|
inline size_t SetRfc822Time(__out_ecount_part_z_opt(dwLen, return) LPSTR szDate, __in size_t dwLen) throw()
|
|
{
|
|
// Max buffer size required(including NULL) - 38
|
|
const size_t s_dwMaxBufferLen = 38;
|
|
if (szDate == NULL)
|
|
{
|
|
return s_dwMaxBufferLen;
|
|
}
|
|
|
|
if (dwLen < 38)
|
|
{
|
|
return 0;
|
|
}
|
|
static const LPCSTR s_months[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
|
|
static const LPCSTR s_days[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
|
|
|
SYSTEMTIME st;
|
|
DWORD dwTimeZoneId=TIME_ZONE_ID_UNKNOWN;
|
|
CHAR cDiff;
|
|
LONG ltzBias=0;
|
|
LONG ltzHour;
|
|
LONG ltzMinute;
|
|
TIME_ZONE_INFORMATION tzi;
|
|
|
|
GetLocalTime(&st);
|
|
|
|
// Gets TIME_ZONE_INFORMATION
|
|
memset(&tzi, 0, sizeof(tzi));
|
|
dwTimeZoneId = GetTimeZoneInformation(&tzi);
|
|
switch (dwTimeZoneId)
|
|
{
|
|
case TIME_ZONE_ID_STANDARD:
|
|
ltzBias = tzi.Bias + tzi.StandardBias;
|
|
break;
|
|
|
|
case TIME_ZONE_ID_DAYLIGHT:
|
|
ltzBias = tzi.Bias + tzi.DaylightBias;
|
|
break;
|
|
|
|
case TIME_ZONE_ID_UNKNOWN:
|
|
default:
|
|
ltzBias = tzi.Bias;
|
|
break;
|
|
}
|
|
|
|
// Set Hour Minutes and time zone dif
|
|
ltzHour = ltzBias / 60;
|
|
ltzMinute = ltzBias % 60;
|
|
cDiff = (ltzHour < 0) ? '+' : '-';
|
|
|
|
int nDay = (st.wDayOfWeek > 6) ? 0 : st.wDayOfWeek;
|
|
int nMonth = st.wMonth = (WORD)((st.wMonth < 1 || st.wMonth > 12) ? 0 : st.wMonth - 1);
|
|
|
|
|
|
// Constructs RFC 822 format: "ddd, dd mmm yyyy hh:mm:ss +/- hhmm\0"
|
|
sprintf_s(szDate, dwLen, "Date: %3s, %d %3s %4d %02d:%02d:%02d %c%02d%02d",
|
|
s_days[nDay], // "ddd"
|
|
st.wDay, // "dd"
|
|
s_months[nMonth], // "mmm"
|
|
st.wYear, // "yyyy"
|
|
st.wHour, // "hh"
|
|
st.wMinute, // "mm"
|
|
st.wSecond, // "ss"
|
|
cDiff, // "+" / "-"
|
|
abs (ltzHour), // "hh"
|
|
abs (ltzMinute)); // "mm"
|
|
return s_dwMaxBufferLen;
|
|
}
|
|
|
|
inline DWORD GetContentTypeFromFileName(LPCTSTR szFileName, CSimpleString& strContentType) throw()
|
|
{
|
|
if (szFileName == NULL)
|
|
{
|
|
return ERROR_INVALID_DATA;
|
|
}
|
|
|
|
DWORD dwErr = ERROR_PATH_NOT_FOUND;
|
|
_ATLTRY
|
|
{
|
|
// get the file extension
|
|
TCHAR szExt[_MAX_EXT];
|
|
Checked::tsplitpath_s(szFileName, NULL, 0, NULL, 0, NULL, 0, szExt, _countof(szExt));
|
|
if (*szExt)
|
|
{
|
|
// Query the content type from the registry
|
|
CRegKey rkContentType;
|
|
dwErr = rkContentType.Open(HKEY_CLASSES_ROOT, szExt, KEY_READ);
|
|
if (dwErr == ERROR_SUCCESS)
|
|
{
|
|
ULONG nChars=0;
|
|
dwErr = rkContentType.QueryStringValue(_T("Content Type"), NULL, &nChars);
|
|
if (dwErr == ERROR_SUCCESS)
|
|
{
|
|
LPTSTR szBuf = strContentType.GetBuffer(nChars);
|
|
dwErr = rkContentType.QueryStringValue(_T("Content Type"), szBuf, &nChars);
|
|
strContentType.ReleaseBuffer(nChars);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwErr != ERROR_SUCCESS)
|
|
{
|
|
// default to application/octet-stream
|
|
strContentType.SetString(_T("application/octet-stream"), sizeof("application/octet-stream")-1);
|
|
}
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
dwErr = ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
// CMimeBodyPart is an abstract base class for the body parts
|
|
// CMimeAttachment, CMimeText, CMimeHeader.
|
|
class CMimeBodyPart
|
|
{
|
|
public:
|
|
|
|
virtual ~CMimeBodyPart() = 0 {}
|
|
|
|
// WriteData - pure virtual method to dump the data for a body part.
|
|
virtual BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary, DWORD dwFlags = 0) = 0;
|
|
|
|
// GetContentType - pure virtual method to get the content of a body part
|
|
virtual LPCSTR GetContentType() = 0;
|
|
|
|
// GetCharset - virtual method to get the character set of a body part
|
|
// (defaults to ATLSMTP_DEFAULT_CSET).
|
|
virtual LPCSTR GetCharset()
|
|
{
|
|
return ATLSMTP_DEFAULT_CSET;
|
|
}
|
|
|
|
virtual CMimeBodyPart* Copy() = 0;
|
|
|
|
protected:
|
|
|
|
// MakeMimeHeader - pure virutal method to create a MIME header for a
|
|
// body part.
|
|
virtual BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) = 0;
|
|
}; // class CMimeBodyPart
|
|
|
|
|
|
// This enum is used with the X-Priority part of the message header
|
|
enum ATL_MIME_PRIORITY
|
|
{
|
|
ATL_MIME_HIGH_PRIORITY = 1,
|
|
ATL_MIME_NORMAL_PRIORITY = 3,
|
|
ATL_MIME_LOW_PRIORITY = 5,
|
|
ATL_MIME_PRIORITY_ERROR = 0
|
|
};
|
|
|
|
|
|
// CMimeHeader describes the basic RFC 822 message header.
|
|
// It also serves as the base class for the CMimeMessage object.
|
|
class CMimeHeader : public CMimeBodyPart
|
|
{
|
|
protected:
|
|
|
|
// Pointer to MLANG's IMultiLanguage interface.
|
|
// This is used in doing conversion from code pages
|
|
// to MIME-compatible character sets.
|
|
CComPtr<IMultiLanguage> m_spMultiLanguage;
|
|
|
|
//Basic Header Parts
|
|
CStringA m_strFrom;
|
|
CStringA m_strTo;
|
|
CStringA m_strCc;
|
|
CStringA m_strBcc;
|
|
CStringA m_strSubject;
|
|
|
|
//Extended Header Parts
|
|
ATL_MIME_PRIORITY m_nPriority;
|
|
CStringA m_XHeader;
|
|
|
|
//Display Names
|
|
CStringA m_strSenderName;
|
|
|
|
//MIME Character Sets
|
|
char m_szSubjectCharset[ATL_MAX_ENC_CHARSET_LENGTH];
|
|
char m_szSenderCharset[ATL_MAX_ENC_CHARSET_LENGTH];
|
|
|
|
//Recipient and CC charsets are encoded in the Add methods
|
|
|
|
public:
|
|
|
|
CMimeHeader() throw()
|
|
:m_nPriority(ATL_MIME_NORMAL_PRIORITY)
|
|
{
|
|
m_szSubjectCharset[0] = '\0';
|
|
m_szSenderCharset[0] = '\0';
|
|
}
|
|
|
|
~CMimeHeader() throw()
|
|
{
|
|
}
|
|
|
|
// Initialize MLang for multilanguage support
|
|
inline BOOL Initialize(IMultiLanguage* pMultiLanguage = NULL) throw()
|
|
{
|
|
if (pMultiLanguage != NULL)
|
|
{
|
|
m_spMultiLanguage = pMultiLanguage;
|
|
}
|
|
else
|
|
{
|
|
HRESULT hr = m_spMultiLanguage.CoCreateInstance(__uuidof(CMultiLanguage), NULL, CLSCTX_INPROC_SERVER);
|
|
if (hr != S_OK)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Get the content type
|
|
virtual inline LPCSTR GetContentType() throw()
|
|
{
|
|
return "text/plain";
|
|
}
|
|
|
|
// Get the character set
|
|
virtual inline LPCSTR GetCharset() throw()
|
|
{
|
|
return "iso-8859-1";
|
|
}
|
|
|
|
virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
|
|
{
|
|
CAutoPtr<CMimeHeader> pNewHeader;
|
|
ATLTRY(pNewHeader.Attach(new CMimeHeader));
|
|
if (pNewHeader)
|
|
*pNewHeader = *this;
|
|
|
|
return pNewHeader.Detach();
|
|
}
|
|
|
|
const CMimeHeader& operator=(const CMimeHeader& that) throw( ... )
|
|
{
|
|
if (this != &that)
|
|
{
|
|
m_spMultiLanguage = that.m_spMultiLanguage;
|
|
m_strFrom = that.m_strFrom;
|
|
m_strTo = that.m_strTo;
|
|
m_strCc = that.m_strCc;
|
|
m_strSubject = that.m_strSubject;
|
|
|
|
m_nPriority = that.m_nPriority;
|
|
m_XHeader = that.m_XHeader;
|
|
|
|
m_strSenderName = that.m_strSenderName;
|
|
|
|
Checked::strcpy_s(m_szSubjectCharset, ATL_MAX_ENC_CHARSET_LENGTH, that.m_szSubjectCharset);
|
|
Checked::strcpy_s(m_szSenderCharset, ATL_MAX_ENC_CHARSET_LENGTH, that.m_szSenderCharset);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
// Set the priority of the message
|
|
inline BOOL SetPriority(ATL_MIME_PRIORITY nPriority) throw()
|
|
{
|
|
if (nPriority < 0)
|
|
return FALSE;
|
|
m_nPriority = nPriority;
|
|
return TRUE;
|
|
}
|
|
|
|
// Get the priority of the message
|
|
inline ATL_MIME_PRIORITY GetPriority() throw()
|
|
{
|
|
return m_nPriority;
|
|
}
|
|
|
|
// Set the display (friendly) name for the header
|
|
inline BOOL SetSenderName(LPCTSTR szName, UINT uiCodePage = 0) throw()
|
|
{
|
|
if (szName == NULL)
|
|
return FALSE;
|
|
|
|
CHeapPtr<char> szNamePtr;
|
|
UINT nLen(0);
|
|
|
|
BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szName, &szNamePtr, &nLen);
|
|
if (bRet)
|
|
{
|
|
_ATLTRY
|
|
{
|
|
m_strSenderName.Empty();
|
|
m_strSenderName.Append(szNamePtr, (int) nLen);
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
bRet = AtlMimeCharsetFromCodePage(m_szSenderCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// Get the display (friendly) name for the sender
|
|
inline LPCSTR GetSenderName() throw()
|
|
{
|
|
return m_strSenderName;
|
|
}
|
|
|
|
// Append a user defined header (should not contain CRLF)
|
|
inline BOOL AppendUserDefinedHeader(LPCTSTR szHeaderName, LPCTSTR szHeader, UINT uiCodePage = 0) throw()
|
|
{
|
|
if ((szHeader == NULL) || (szHeaderName == NULL))
|
|
return FALSE;
|
|
|
|
_ATLTRY
|
|
{
|
|
CHeapPtr<char> szName;
|
|
UINT nLen(0);
|
|
|
|
BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szHeader, &szName, &nLen);
|
|
if (bRet)
|
|
{
|
|
// get the charset
|
|
char szCharset[ATL_MAX_ENC_CHARSET_LENGTH];
|
|
bRet = AtlMimeCharsetFromCodePage(szCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
|
|
|
|
if (bRet)
|
|
{
|
|
CStringA str;
|
|
str.Append(szName, (int)nLen);
|
|
|
|
// encode the string
|
|
CHeapPtr<char> szBuf;
|
|
DWORD dwReqLen = QEncodeGetRequiredLength(str.GetLength(),
|
|
ATL_MAX_ENC_CHARSET_LENGTH);
|
|
|
|
if (szBuf.Allocate(dwReqLen) == false)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD dwLength(0);
|
|
BOOL bEncoded = FALSE;
|
|
if (!GetEncodedString(str, szCharset, szBuf, dwReqLen, dwLength, bEncoded))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// add to m_XHeader
|
|
m_XHeader += CT2CA(szHeaderName);
|
|
m_XHeader.Append(": ", 2);
|
|
m_XHeader.Append(szBuf, dwLength);
|
|
m_XHeader.Append("\r\n", 2);
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Add a recipient ("To:" line)
|
|
inline BOOL AddRecipient(LPCTSTR szAddress, LPCTSTR szName = NULL, UINT uiCodePage = 0) throw()
|
|
{
|
|
return AddRecipientHelper(m_strTo, szAddress, szName, uiCodePage);
|
|
}
|
|
|
|
// Get the recipients string ("To:" line)
|
|
inline LPCSTR GetRecipients() throw()
|
|
{
|
|
return m_strTo;
|
|
}
|
|
|
|
// Clear all recipients ("To:" line)
|
|
inline BOOL ClearRecipients() throw()
|
|
{
|
|
m_strTo.Empty();
|
|
return TRUE;
|
|
}
|
|
|
|
// Add a recipient ("CC:" line)
|
|
inline BOOL AddCc(LPCTSTR szAddress, LPCTSTR szName = NULL, UINT uiCodePage = 0) throw()
|
|
{
|
|
return AddRecipientHelper(m_strCc, szAddress, szName, uiCodePage);
|
|
}
|
|
|
|
// Get the recipients string ("CC:" line)
|
|
inline LPCSTR GetCc() throw()
|
|
{
|
|
return m_strCc;
|
|
}
|
|
|
|
// Clear the recipients string ("CC:" line)
|
|
inline BOOL ClearCc() throw()
|
|
{
|
|
m_strCc.Empty();
|
|
return TRUE;
|
|
}
|
|
|
|
// Add a Bcc recipient (not output as part of message)
|
|
inline BOOL AddBcc(LPCTSTR szAddress) throw()
|
|
{
|
|
if (szAddress == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
_ATLTRY
|
|
{
|
|
CStringA str = m_strBcc;
|
|
|
|
if (m_strBcc.GetLength() > 0)
|
|
str.Append(",", 1);
|
|
|
|
str += CT2CA(szAddress);
|
|
|
|
m_strBcc = str;
|
|
|
|
return TRUE;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Get the recipients string (Bcc part)
|
|
inline LPCSTR GetBcc() throw()
|
|
{
|
|
return m_strBcc;
|
|
}
|
|
|
|
// Clear the recipients string (Bcc part)
|
|
inline BOOL ClearBcc() throw()
|
|
{
|
|
m_strBcc.Empty();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
inline DWORD GetRequiredRecipientsStringLength() throw()
|
|
{
|
|
DWORD dwRet = m_strTo.GetLength();
|
|
if (m_strCc.GetLength())
|
|
{
|
|
dwRet += dwRet ? 1 : 0;
|
|
dwRet += m_strCc.GetLength();
|
|
}
|
|
if (m_strBcc.GetLength())
|
|
{
|
|
dwRet += dwRet ? 1 : 0;
|
|
dwRet += m_strBcc.GetLength();
|
|
}
|
|
dwRet++;
|
|
return dwRet;
|
|
}
|
|
|
|
// returns the recipients string to be (addresses only, in comma separated format)
|
|
ATL_NOINLINE BOOL GetRecipientsString(__out_ecount_part_z(*pdwLen, *pdwLen) LPSTR szRecip, __inout LPDWORD pdwLen) throw()
|
|
{
|
|
if ( (szRecip == NULL) || (pdwLen == NULL) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if ( *pdwLen < GetRequiredRecipientsStringLength())
|
|
{
|
|
*pdwLen = GetRequiredRecipientsStringLength();
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD dwMaxLen = *pdwLen;
|
|
*pdwLen = 0;
|
|
|
|
DWORD dwLen = 0;
|
|
DWORD dwTotalLen = 0;
|
|
if (m_strTo.GetLength() > 0)
|
|
{
|
|
dwLen = *pdwLen - dwTotalLen;
|
|
if (AtlMimeMakeRecipientsString(m_strTo, szRecip, &dwLen) != TRUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
szRecip+= dwLen;
|
|
dwTotalLen = dwLen;
|
|
}
|
|
|
|
if (m_strCc.GetLength() > 0)
|
|
{
|
|
if (dwTotalLen)
|
|
{
|
|
*szRecip++ = ',';
|
|
dwTotalLen++;
|
|
}
|
|
dwLen = *pdwLen - dwTotalLen;
|
|
if (AtlMimeMakeRecipientsString(m_strCc, szRecip, &dwLen) != TRUE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
szRecip+= dwLen;
|
|
dwTotalLen+= dwLen;
|
|
}
|
|
|
|
if (m_strBcc.GetLength() > 0)
|
|
{
|
|
dwLen = m_strBcc.GetLength();
|
|
if (dwTotalLen)
|
|
{
|
|
*szRecip++ = ',';
|
|
dwTotalLen++;
|
|
}
|
|
dwLen = *pdwLen - dwTotalLen;
|
|
Checked::memcpy_s(szRecip, dwMaxLen-dwTotalLen, m_strBcc, dwLen);
|
|
szRecip+= dwLen;
|
|
dwTotalLen+= dwLen;
|
|
}
|
|
|
|
*szRecip = '\0';
|
|
*pdwLen = dwTotalLen;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Get the sender
|
|
inline LPCSTR GetSender() throw()
|
|
{
|
|
return m_strFrom;
|
|
}
|
|
|
|
// Set the sender
|
|
inline BOOL SetSender(LPCTSTR szSender) throw()
|
|
{
|
|
if (szSender == NULL)
|
|
return FALSE;
|
|
|
|
_ATLTRY
|
|
{
|
|
m_strFrom = CT2CA(szSender);
|
|
return TRUE;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Set the subject
|
|
inline BOOL SetSubject(LPCTSTR szSubject, UINT uiCodePage = 0) throw()
|
|
{
|
|
if (szSubject == NULL)
|
|
return FALSE;
|
|
|
|
_ATLTRY
|
|
{
|
|
CHeapPtr<char> szName;
|
|
UINT nLen(0);
|
|
|
|
BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szSubject, &szName, &nLen);
|
|
if (bRet)
|
|
{
|
|
m_strSubject.Empty();
|
|
m_strSubject.Append(szName, (int)nLen);
|
|
bRet = AtlMimeCharsetFromCodePage(m_szSubjectCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Get the subject
|
|
inline LPCSTR GetSubject() throw()
|
|
{
|
|
return (LPCSTR)m_strSubject;
|
|
}
|
|
|
|
// Dump the header to hFile
|
|
virtual inline BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR /*szBoundary*/, DWORD dwFlags = 0) throw()
|
|
{
|
|
if (pOverlapped == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int nMaxSendLen = GetRequiredBufferSize(ATLSMTP_MAX_LINE_LENGTH-4);
|
|
CHeapPtr<char> spSendBuffer;
|
|
if (!spSendBuffer.Allocate(nMaxSendLen))
|
|
return FALSE;
|
|
|
|
// choose QEncode here, because the max QEncodeGetRequiredLength will always
|
|
// return a value greater than BEncodeGetRequiredLength
|
|
int nBufLen = __max(QEncodeGetRequiredLength(m_strSubject.GetLength(),
|
|
ATL_MAX_ENC_CHARSET_LENGTH),
|
|
QEncodeGetRequiredLength(m_strSenderName.GetLength(),
|
|
ATL_MAX_ENC_CHARSET_LENGTH)+m_strFrom.GetLength()+2);
|
|
|
|
CHeapPtr<char> spBuf;
|
|
if (!spBuf.Allocate(nBufLen))
|
|
return FALSE;
|
|
|
|
int nMaxLen = nBufLen;
|
|
DWORD dwOffset = 0;
|
|
|
|
char szDate[ATL_MIME_DATE_LEN];
|
|
|
|
SetRfc822Time(szDate, ATL_MIME_DATE_LEN);
|
|
char *pSendBuffer = spSendBuffer;
|
|
|
|
DWORD dwLength = (DWORD) strlen(szDate);
|
|
|
|
if(dwLength > ATLSMTP_MAX_LINE_LENGTH -2 -dwOffset )
|
|
return FALSE;
|
|
|
|
Checked::memcpy_s(pSendBuffer+dwOffset, nMaxSendLen-dwOffset, szDate, dwLength);
|
|
dwOffset += dwLength;
|
|
*(pSendBuffer+dwOffset++) = '\r';
|
|
*(pSendBuffer+dwOffset++) = '\n';
|
|
|
|
int dwHeaderPartLength = 0;
|
|
*spBuf = '\0';
|
|
|
|
// Get the sender name
|
|
BOOL bRet = TRUE;
|
|
BOOL bEncoded = FALSE;
|
|
if (m_strSenderName.GetLength() > 0)
|
|
{
|
|
bRet = GetEncodedString(m_strSenderName, m_szSenderCharset, spBuf, nBufLen, dwLength, bEncoded);
|
|
dwHeaderPartLength += dwLength;
|
|
}
|
|
|
|
// Get the sender email address
|
|
if (bRet && m_strFrom.GetLength() > 0)
|
|
{
|
|
|
|
if (dwHeaderPartLength != 0)
|
|
{
|
|
if(dwHeaderPartLength + 1 > nBufLen)
|
|
return FALSE;
|
|
|
|
*(spBuf+dwHeaderPartLength++) = ' ';
|
|
}
|
|
|
|
if(dwHeaderPartLength + m_strFrom.GetLength() + 2 > nBufLen)
|
|
return FALSE;
|
|
|
|
*(spBuf+dwHeaderPartLength++) = '<';
|
|
if (dwHeaderPartLength < 0 || dwHeaderPartLength > nMaxLen)
|
|
{
|
|
return FALSE;
|
|
}
|
|
Checked::memcpy_s(spBuf+dwHeaderPartLength, nMaxLen-dwHeaderPartLength, (LPCSTR)m_strFrom, m_strFrom.GetLength());
|
|
dwHeaderPartLength+= m_strFrom.GetLength();
|
|
*(spBuf+dwHeaderPartLength++) = '>';
|
|
}
|
|
|
|
// Output the "From: " line
|
|
if (bRet && dwHeaderPartLength != 0)
|
|
{
|
|
const char szFrom[] = "From: ";
|
|
if(sizeof(szFrom)/sizeof(szFrom[0])-1 > ATLSMTP_MAX_LINE_LENGTH -2 -dwOffset )
|
|
return FALSE;
|
|
if (dwOffset > static_cast<DWORD>(nMaxSendLen))
|
|
{
|
|
return FALSE;
|
|
}
|
|
Checked::memcpy_s(pSendBuffer+dwOffset, nMaxSendLen-dwOffset, szFrom, _countof(szFrom)-1);
|
|
dwOffset+= (sizeof(szFrom)/sizeof(szFrom[0])-1) ;
|
|
DWORD dwWritten = ATLSMTP_MAX_LINE_LENGTH - 2 - dwOffset;
|
|
bRet = FormatField((LPBYTE)(char*)spBuf, dwHeaderPartLength, (LPBYTE)(pSendBuffer+dwOffset), &dwWritten, dwFlags);
|
|
dwOffset += dwWritten;
|
|
*(pSendBuffer+dwOffset++) = '\r';
|
|
*(pSendBuffer+dwOffset++) = '\n';
|
|
}
|
|
|
|
// Output the subject
|
|
if (bRet && m_strSubject.GetLength() > 0)
|
|
{
|
|
dwLength = 0;
|
|
bRet = GetEncodedString(m_strSubject, m_szSubjectCharset, spBuf, nBufLen, dwLength, bEncoded);
|
|
if (bRet && dwLength != 0)
|
|
{
|
|
const char szSubject[] = "Subject: ";
|
|
if(sizeof(szSubject)/sizeof(szSubject[0])-1 > ATLSMTP_MAX_LINE_LENGTH -2 -dwOffset )
|
|
return FALSE;
|
|
if (dwOffset > static_cast<DWORD>(nMaxSendLen))
|
|
{
|
|
return FALSE;
|
|
}
|
|
Checked::memcpy_s(pSendBuffer+dwOffset, nMaxSendLen-dwOffset, szSubject, _countof(szSubject)-1);
|
|
dwOffset+= (sizeof(szSubject)/sizeof(szSubject[0])-1);
|
|
DWORD dwWritten = ATLSMTP_MAX_LINE_LENGTH - 2 - dwOffset;
|
|
bRet = FormatField((LPBYTE)(char*)spBuf, dwLength, (LPBYTE)(pSendBuffer+dwOffset), &dwWritten, dwFlags);
|
|
dwOffset += dwWritten;
|
|
*(pSendBuffer+dwOffset++) = '\r';
|
|
*(pSendBuffer+dwOffset++) = '\n';
|
|
}
|
|
}
|
|
|
|
// Output the "To:" line
|
|
if (bRet && m_strTo.GetLength() > 0)
|
|
{
|
|
const char szTo[] = "To: ";
|
|
if(sizeof(szTo)/sizeof(szTo[0])-1 > ATLSMTP_MAX_LINE_LENGTH -2 -dwOffset )
|
|
return FALSE;
|
|
if (dwOffset > static_cast<DWORD>(nMaxSendLen))
|
|
{
|
|
return FALSE;
|
|
}
|
|
Checked::memcpy_s(pSendBuffer+dwOffset, nMaxSendLen-dwOffset, szTo, _countof(szTo)-1);
|
|
dwOffset+= (sizeof(szTo)/sizeof(szTo[0]) -1);
|
|
DWORD dwWritten = ATLSMTP_MAX_LINE_LENGTH - 2 - dwOffset;
|
|
bRet = FormatRecipients((LPBYTE)((LPCSTR)m_strTo), m_strTo.GetLength(), (LPBYTE)(pSendBuffer+dwOffset), &dwWritten);
|
|
dwOffset+= dwWritten;
|
|
*(pSendBuffer+dwOffset++) = '\r';
|
|
*(pSendBuffer+dwOffset++) = '\n';
|
|
}
|
|
|
|
// Output the "CC:" line
|
|
if (bRet && m_strCc.GetLength() > 0)
|
|
{
|
|
const char szCC[] = "CC: ";
|
|
if(sizeof(szCC)/sizeof(szCC[0])-1 > ATLSMTP_MAX_LINE_LENGTH -2 -dwOffset )
|
|
return FALSE;
|
|
if (dwOffset > static_cast<DWORD>(nMaxSendLen))
|
|
{
|
|
return FALSE;
|
|
}
|
|
Checked::memcpy_s(pSendBuffer+dwOffset, nMaxSendLen-dwOffset, szCC, _countof(szCC)-1);
|
|
dwOffset+= (sizeof(szCC)/sizeof(szCC[0]) -1);
|
|
DWORD dwWritten = ATLSMTP_MAX_LINE_LENGTH - 2 - dwOffset;
|
|
bRet = FormatRecipients((LPBYTE)((LPCSTR)m_strCc), m_strCc.GetLength(), (LPBYTE)(pSendBuffer+dwOffset), &dwWritten);
|
|
dwOffset+= dwWritten;
|
|
*(pSendBuffer+dwOffset++) = '\r';
|
|
*(pSendBuffer+dwOffset++) = '\n';
|
|
}
|
|
|
|
// Send the header
|
|
if (bRet && dwOffset)
|
|
bRet = AtlSmtpSendAndWait(hFile, pSendBuffer, dwOffset, pOverlapped);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
protected:
|
|
|
|
// Make the mime header
|
|
virtual inline BOOL MakeMimeHeader(CStringA& /*header*/, LPCSTR /*szBoundary*/) throw()
|
|
{
|
|
// The message header does not have its own MIME header
|
|
ATLASSERT(FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
// Get an encoded string for a header field
|
|
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()
|
|
{
|
|
// BOOL bEncoded = FALSE;
|
|
bEncoded = FALSE;
|
|
if (m_spMultiLanguage.p)
|
|
{
|
|
// only encode if there are 8bit characters
|
|
int nExtendedChars = GetExtendedChars(headerString, headerString.GetLength());
|
|
if (nExtendedChars)
|
|
{
|
|
// choose smallest encoding
|
|
if (((nExtendedChars*100)/headerString.GetLength()) < 17)
|
|
{
|
|
int nEncCnt = 0;
|
|
if (!QEncode((LPBYTE)((LPCSTR)headerString), headerString.GetLength(), szBuf, &nBufLen, szCharset, &nEncCnt))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//if no unsafe characters were encountered, just output it
|
|
if (nEncCnt != 0)
|
|
{
|
|
bEncoded = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!BEncode((LPBYTE)((LPCSTR)headerString), headerString.GetLength(), szBuf, &nBufLen, szCharset))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
bEncoded = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bEncoded)
|
|
{
|
|
// there was no encoding
|
|
dwLength = (DWORD) headerString.GetLength();
|
|
if(dwLength > DWORD(nBufLen))
|
|
return FALSE;
|
|
Checked::memcpy_s(szBuf, nBufLen, headerString, dwLength);
|
|
}
|
|
else
|
|
{
|
|
dwLength = nBufLen;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Helper function for adding recipients
|
|
inline BOOL AddRecipientHelper(CStringA& str, LPCTSTR szAddress, LPCTSTR szName = NULL, UINT uiCodePage = 0) throw()
|
|
{
|
|
if ((szAddress == NULL) && (szName == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
_ATLTRY
|
|
{
|
|
if (szName)
|
|
{
|
|
CHeapPtr<char> szNamePtr;
|
|
UINT nLen(0);
|
|
|
|
BOOL bRet = AtlMimeConvertString(m_spMultiLanguage, uiCodePage, szName, &szNamePtr, &nLen);
|
|
if (bRet)
|
|
{
|
|
CStringA Name(szNamePtr, (int)nLen);
|
|
|
|
char szCharset[ATL_MAX_ENC_CHARSET_LENGTH];
|
|
|
|
if (!AtlMimeCharsetFromCodePage(szCharset, uiCodePage, m_spMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CFixedStringT<CStringA, 256> strBuf;
|
|
|
|
int nBufLen = QEncodeGetRequiredLength(Name.GetLength(),
|
|
ATL_MAX_ENC_CHARSET_LENGTH)+1;
|
|
|
|
char * szBuf = strBuf.GetBuffer(nBufLen);
|
|
if (szBuf == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD dwLength = 0;
|
|
BOOL bEncoded = FALSE;
|
|
if (!GetEncodedString(Name, szCharset, szBuf, nBufLen, dwLength, bEncoded))
|
|
{
|
|
strBuf.ReleaseBuffer();
|
|
return FALSE;
|
|
}
|
|
|
|
strBuf.ReleaseBuffer(dwLength);
|
|
|
|
// append comma if there are existing recipients
|
|
if (str.GetLength() != 0)
|
|
{
|
|
str.Append(", ", 2);
|
|
}
|
|
|
|
if (bEncoded == FALSE)
|
|
{
|
|
// need to escape the string if no encoding
|
|
strBuf.Replace("\\", "\\\\");
|
|
strBuf.Replace("\"", "\\\"");
|
|
|
|
// wrap the unescaped name in quotes
|
|
str.Append("\"", 1);
|
|
}
|
|
str += strBuf;
|
|
if (bEncoded == FALSE)
|
|
{
|
|
// close quote
|
|
str.Append("\"", 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return bRet;
|
|
}
|
|
}
|
|
|
|
if (szAddress)
|
|
{
|
|
if (szName)
|
|
{
|
|
str.Append(" ", 1);
|
|
}
|
|
else
|
|
{
|
|
// append comma if there are existing recipients
|
|
if (str.GetLength() != 0)
|
|
{
|
|
str.Append(", ", 2);
|
|
}
|
|
}
|
|
str.Append("<", 1);
|
|
str += CT2CA(szAddress);
|
|
str.Append(">", 1);
|
|
}
|
|
return TRUE;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Get the formatted header information
|
|
inline BOOL FormatField(LPBYTE pbSrcData, int nSrcLen, LPBYTE pbDest,
|
|
DWORD* pnBufLen, DWORD dwFlags = 0) throw()
|
|
{
|
|
if(pnBufLen == NULL)
|
|
return FALSE;
|
|
|
|
int nRead = 0;
|
|
|
|
// 9 is the length of the maximum field name : "Subject :"
|
|
// we set that here for simplicity
|
|
int nLineLen = 9;
|
|
DWORD nWritten = 0;
|
|
|
|
//subtract 2 from these because it's easier for when we have
|
|
//to break lines with a CRLF (and tab if necessary)
|
|
int nMaxLineLength = ATLSMTP_MAX_LINE_LENGTH-3;
|
|
while (nRead < nSrcLen)
|
|
{
|
|
//if we're at the end of the line, break it
|
|
if (nLineLen == nMaxLineLength)
|
|
{
|
|
if( nWritten + 2 > *pnBufLen)
|
|
return FALSE;
|
|
|
|
*pbDest++ = '\r';
|
|
*pbDest++ = '\n';
|
|
nWritten+= 2;
|
|
nLineLen = -1;
|
|
|
|
if ((dwFlags & ATLSMTP_FORMAT_SMTP))
|
|
{
|
|
if(nWritten + 1 > *pnBufLen)
|
|
return FALSE;
|
|
|
|
*pbDest++ = '\t';
|
|
nWritten++;
|
|
nLineLen++;
|
|
}
|
|
}
|
|
|
|
//if we hit a CRLF, reset nLineLen
|
|
if (*pbSrcData == '\n' && nRead > 0 && *(pbSrcData-1) == '\r')
|
|
{
|
|
nLineLen = -1;
|
|
}
|
|
|
|
if(nWritten + 1 > *pnBufLen)
|
|
return FALSE;
|
|
|
|
*pbDest++ = *pbSrcData++;
|
|
nRead++;
|
|
nWritten++;
|
|
nLineLen++;
|
|
}
|
|
|
|
*pnBufLen = (DWORD)nWritten;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Get the formatted recipient information
|
|
inline BOOL FormatRecipients(LPBYTE pbSrcData, int nSrcLen, LPBYTE pbDest,
|
|
DWORD* pnBufLen) throw()
|
|
{
|
|
|
|
if(pnBufLen == NULL)
|
|
return FALSE;
|
|
|
|
int nRead = 0;
|
|
DWORD nWritten = 0;
|
|
|
|
while (nRead < nSrcLen)
|
|
{
|
|
if (*pbSrcData == ',')
|
|
{
|
|
if(nWritten + 4 > *pnBufLen)
|
|
return FALSE;
|
|
|
|
*pbDest++ = *pbSrcData++;
|
|
nRead++;
|
|
if (nRead+1 <= nSrcLen && *pbSrcData == ' ')
|
|
{
|
|
pbSrcData++;
|
|
nRead++;
|
|
}
|
|
*pbDest++ = '\r';
|
|
*pbDest++ = '\n';
|
|
*pbDest++ = '\t';
|
|
nWritten+= 4;
|
|
|
|
continue;
|
|
}
|
|
|
|
if(nWritten + 1 > *pnBufLen)
|
|
return FALSE;
|
|
|
|
*pbDest++ = *pbSrcData++;
|
|
nRead++;
|
|
nWritten++;
|
|
}
|
|
|
|
*pnBufLen = nWritten;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Get the required buffer size for the header
|
|
inline int GetRequiredBufferSize(int nMaxLineLength) throw()
|
|
{
|
|
const static DWORD DATELINE = 27;
|
|
const static DWORD FROMLINE = 10;
|
|
const static DWORD TOLINE = 6;
|
|
const static DWORD CCLINE = 6;
|
|
const static DWORD SUBJECTLINE = 11;
|
|
|
|
//data lengths (QEncoding potentially takes up more space than BEncoding,
|
|
//so default to it)
|
|
int nRequiredLength = QEncodeGetRequiredLength(m_strSenderName.GetLength(), ATL_MAX_ENC_CHARSET_LENGTH)
|
|
+QEncodeGetRequiredLength(m_strSubject.GetLength(), ATL_MAX_ENC_CHARSET_LENGTH);
|
|
nRequiredLength += m_strFrom.GetLength()+m_strTo.GetLength()+m_strCc.GetLength();
|
|
|
|
//Add space for date
|
|
nRequiredLength += DATELINE;
|
|
|
|
//Add space for From: line
|
|
nRequiredLength += FROMLINE;
|
|
|
|
//Add space for To: line
|
|
nRequiredLength += TOLINE;
|
|
|
|
//Add space for Cc: line
|
|
nRequiredLength += CCLINE;
|
|
|
|
//Add space for Subject: line
|
|
nRequiredLength += SUBJECTLINE;
|
|
|
|
//Add space for line breaks and tabs
|
|
nRequiredLength += 3*(nRequiredLength/nMaxLineLength);
|
|
|
|
//Trailing CRLF
|
|
nRequiredLength += 2;
|
|
|
|
return nRequiredLength;
|
|
}
|
|
|
|
}; // class CMimeHeader
|
|
|
|
|
|
// CMimeAttachment is an abstract base class for MIME message attachments.
|
|
// It serves as a base class for CMimeFileAttachment and CMimeRawAttachment
|
|
class CMimeAttachment : public CMimeBodyPart
|
|
{
|
|
protected:
|
|
|
|
// the encoding scheme (ATLSMTP_BASE64_ENCODE, ATLSMTP_UUENCODE, ATLSMTP_QP_ENCODE)
|
|
int m_nEncodingScheme;
|
|
|
|
// the content type of the attachment
|
|
CStringA m_ContentType;
|
|
|
|
// the character set
|
|
char m_szCharset[ATL_MAX_ENC_CHARSET_LENGTH];
|
|
|
|
// the encode string ("base64", "quoted-printable", "uuencode")
|
|
char *m_pszEncodeString;
|
|
|
|
// the display name of the attachment
|
|
TCHAR m_szDisplayName[_MAX_FNAME];
|
|
|
|
public:
|
|
CMimeAttachment() throw()
|
|
:m_nEncodingScheme(ATLSMTP_BASE64_ENCODE), m_pszEncodeString(NULL)
|
|
{
|
|
m_szCharset[0] = 0;
|
|
m_szDisplayName[0] = 0;
|
|
}
|
|
|
|
virtual ~CMimeAttachment() throw()
|
|
{
|
|
}
|
|
|
|
// CMimeFileAttachment and CMimeRawAttachment have to handle their own dumping
|
|
virtual inline BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary, DWORD dwFlags = 0) = 0;
|
|
|
|
// Set the encoding scheme of the attachment
|
|
inline BOOL SetEncodingScheme(int nScheme) throw()
|
|
{
|
|
if (nScheme != ATLSMTP_BASE64_ENCODE && nScheme != ATLSMTP_UUENCODE && nScheme != ATLSMTP_QP_ENCODE)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
m_nEncodingScheme = nScheme;
|
|
return TRUE;
|
|
}
|
|
|
|
// Set the Content-Type of the attachment
|
|
inline BOOL SetContentType(LPCTSTR szContent) throw()
|
|
{
|
|
_ATLTRY
|
|
{
|
|
m_ContentType = CT2CA(szContent);
|
|
return TRUE;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Get the content type of the attachment
|
|
virtual inline LPCSTR GetContentType() throw()
|
|
{
|
|
return m_ContentType;
|
|
}
|
|
|
|
// Get the character set of the attachment
|
|
virtual inline LPCSTR GetCharset() throw()
|
|
{
|
|
return m_szCharset;
|
|
}
|
|
|
|
virtual ATL_NOINLINE CMimeBodyPart* Copy() = 0;
|
|
|
|
const CMimeAttachment& operator=(const CMimeAttachment& that) throw( ... )
|
|
{
|
|
if (this != &that)
|
|
{
|
|
m_nEncodingScheme = that.m_nEncodingScheme;
|
|
m_ContentType = that.m_ContentType;
|
|
Checked::strcpy_s(m_szCharset, ATL_MAX_ENC_CHARSET_LENGTH, that.m_szCharset);
|
|
m_pszEncodeString = that.m_pszEncodeString;
|
|
Checked::tcscpy_s(m_szDisplayName, _countof(m_szDisplayName), that.m_szDisplayName);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
protected:
|
|
|
|
// Make the MIME header for the attachment
|
|
virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) throw()
|
|
{
|
|
// if no display name is specified, default to "rawdata"
|
|
return MakeMimeHeader(header, szBoundary, _T("rawdata"));
|
|
}
|
|
|
|
// Make the MIME header with the specified filename
|
|
virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary, LPCTSTR szFileName)
|
|
{
|
|
ATLENSURE(szBoundary != NULL);
|
|
ATLASSERT(szFileName != NULL);
|
|
ATLASSUME(m_pszEncodeString != NULL);
|
|
|
|
char szBegin[256];
|
|
if (*szBoundary)
|
|
{
|
|
// this is not the only body part
|
|
Checked::memcpy_s(szBegin, 256, ATLMIME_SEPARATOR, sizeof(ATLMIME_SEPARATOR));
|
|
Checked::memcpy_s(szBegin+6, 250, szBoundary, ATL_MIME_BOUNDARYLEN);
|
|
*(szBegin+(ATL_MIME_BOUNDARYLEN+6)) = '\0';
|
|
}
|
|
else
|
|
{
|
|
// this is the only body part, so output the MIME header
|
|
Checked::memcpy_s(szBegin, 256, ATLMIME_VERSION, sizeof(ATLMIME_VERSION));
|
|
}
|
|
|
|
// Get file name with the path stripped out
|
|
TCHAR szFile[MAX_PATH+1];
|
|
TCHAR szExt[_MAX_EXT+1];
|
|
Checked::tsplitpath_s(szFileName, NULL, 0, NULL, 0, szFile, _countof(szFile), szExt, _countof(szExt));
|
|
Checked::tcscat_s(szFile, _countof(szFile), szExt);
|
|
|
|
_ATLTRY
|
|
{
|
|
CT2CAEX<MAX_PATH+1> szFileNameA(szFile);
|
|
|
|
CStringA szDisplayName(szFile);
|
|
if (m_szDisplayName[0] != '\0')
|
|
{
|
|
szDisplayName = CT2CAEX<_MAX_FNAME+1>(m_szDisplayName);
|
|
}
|
|
|
|
header.Format("%s\r\nContent-Type: %s;\r\n\tcharset=\"%s\"\r\n\tname=\"%s\"\r\n"
|
|
"Content-Transfer-Encoding: %s\r\nContent-Disposition: attachment;\r\n\tfilename=\"%s\"\r\n\r\n",
|
|
szBegin, (LPCSTR) m_ContentType, m_szCharset, (LPCSTR) szDisplayName, m_pszEncodeString, (LPCSTR) szFileNameA);
|
|
return TRUE;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Get encoding information
|
|
inline BOOL GetEncodingInformation(int* pnRequiredLength, int* pnLineLength)
|
|
{
|
|
ATLENSURE(pnRequiredLength != NULL);
|
|
ATLENSURE(pnLineLength != NULL);
|
|
|
|
switch(m_nEncodingScheme)
|
|
{
|
|
case ATLSMTP_BASE64_ENCODE:
|
|
m_pszEncodeString = "base64";
|
|
*pnLineLength = ATLSMTP_MAX_BASE64_LINE_LENGTH;
|
|
*pnRequiredLength = Base64EncodeGetRequiredLength(ATLSMTP_MAX_BASE64_LINE_LENGTH);
|
|
break;
|
|
case ATLSMTP_UUENCODE:
|
|
m_pszEncodeString ="uuencode";
|
|
*pnLineLength = ATLSMTP_MAX_UUENCODE_LINE_LENGTH;
|
|
*pnRequiredLength = UUEncodeGetRequiredLength(ATLSMTP_MAX_UUENCODE_LINE_LENGTH);
|
|
break;
|
|
case ATLSMTP_QP_ENCODE:
|
|
m_pszEncodeString = "quoted-printable";
|
|
*pnLineLength = ATLSMTP_MAX_QP_LINE_LENGTH;
|
|
*pnRequiredLength = QPEncodeGetRequiredLength(ATLSMTP_MAX_QP_LINE_LENGTH);
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
}; // class CMimeAttachment
|
|
|
|
|
|
// CMimeFileAttachment represents a MIME file attachment body part
|
|
class CMimeFileAttachment : public CMimeAttachment
|
|
{
|
|
|
|
protected:
|
|
// The filename
|
|
TCHAR m_szFileName[MAX_PATH+1];
|
|
|
|
public:
|
|
CMimeFileAttachment() throw()
|
|
{
|
|
m_szFileName[0] = 0;
|
|
}
|
|
|
|
virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
|
|
{
|
|
CAutoPtr<CMimeFileAttachment> pNewAttachment;
|
|
ATLTRY(pNewAttachment.Attach(new CMimeFileAttachment));
|
|
if (pNewAttachment)
|
|
*pNewAttachment = *this;
|
|
|
|
return pNewAttachment.Detach();
|
|
}
|
|
|
|
const CMimeFileAttachment& operator=(const CMimeFileAttachment& that) throw( ... )
|
|
{
|
|
if (this != &that)
|
|
{
|
|
CMimeAttachment::operator=(that);
|
|
Checked::tcscpy_s(m_szFileName, _countof(m_szFileName), that.m_szFileName);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
// Initialize the file attachment
|
|
// szFileName - the actual file name
|
|
// szDisplayName - the display name for the file (optional)
|
|
// pMultiLanguage - the IMulitLanguage pointer for codepage to charset conversion (optional)
|
|
// uiCodePage - the code page (optional)
|
|
inline BOOL Initialize(LPCTSTR szFileName, LPCTSTR szDisplayName = NULL, IMultiLanguage* pMultiLanguage = NULL, UINT uiCodePage = 0) throw()
|
|
{
|
|
if (!AtlMimeCharsetFromCodePage(m_szCharset, uiCodePage, pMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH))
|
|
return FALSE;
|
|
|
|
if( _tcslen(szFileName) > MAX_PATH )
|
|
{
|
|
return FALSE;
|
|
}
|
|
Checked::tcscpy_s(m_szFileName, _countof(m_szFileName), szFileName);
|
|
|
|
if (szDisplayName)
|
|
{
|
|
// use the user-specified display name
|
|
size_t nLen = _tcslen(szDisplayName)+1;
|
|
if (nLen <= _countof(m_szDisplayName))
|
|
{
|
|
Checked::tcscpy_s(m_szDisplayName, _countof(m_szDisplayName), szDisplayName);
|
|
}
|
|
else
|
|
{
|
|
Checked::tcsncpy_s(m_szDisplayName, _countof(m_szDisplayName), szDisplayName, _countof(m_szDisplayName) - 4);
|
|
Checked::tcscpy_s(m_szDisplayName + _countof(m_szDisplayName) - 4, 4, _T("..."));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// otherwise there is no display name
|
|
*m_szDisplayName = '\0';
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Dump the data for the file attachment
|
|
virtual inline BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary, DWORD dwFlags = 0) throw()
|
|
{
|
|
if ((pOverlapped == NULL) || (szBoundary == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int nLineLength = 0;
|
|
int nRequiredLength = 0;
|
|
|
|
if (!GetEncodingInformation(&nRequiredLength, &nLineLength))
|
|
return FALSE;
|
|
|
|
//Try to open the file that is being attached
|
|
CAtlFile readFile;
|
|
if (FAILED(readFile.Create(m_szFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)))
|
|
return FALSE;
|
|
|
|
//Make the mime header
|
|
CStringA header;
|
|
if (!MakeMimeHeader(header, szBoundary, m_szFileName))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//Try to send the mime header
|
|
if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)header), header.GetLength(), pOverlapped))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int nGetLines = ATLSMTP_GET_LINES;
|
|
|
|
nRequiredLength *= nGetLines;
|
|
|
|
//dwToGet is the total number of characters to attempt to get
|
|
DWORD dwToGet = (DWORD)nGetLines*nLineLength;
|
|
|
|
//allocate the data array
|
|
CHeapPtr<BYTE> spData;
|
|
if (!spData.Allocate(dwToGet+1))
|
|
return FALSE;
|
|
|
|
// if double buffering is defined, create two buffers
|
|
#ifdef ATLSMTP_DOUBLE_BUFFERED
|
|
CHeapPtr<char> buffer1;
|
|
if (!buffer1.Allocate(nRequiredLength+3))
|
|
return FALSE;
|
|
|
|
CHeapPtr<char> buffer2;
|
|
if (!buffer2.Allocate(nRequiredLength+3))
|
|
return FALSE;
|
|
|
|
char* currBuffer = buffer1;
|
|
char* prevBuffer = NULL;
|
|
int nCurrBuffer = 0;
|
|
DWORD dwPrevLength = 0;
|
|
#else
|
|
CHeapPtr<char> currBuffer;
|
|
if (!currBuffer.Allocate(nRequiredLength+3))
|
|
return FALSE;
|
|
|
|
#endif // ATLSMTP_DOUBLE_BUFFERED
|
|
|
|
int nEncodedLength = nRequiredLength;
|
|
BOOL bRet = FALSE;
|
|
DWORD dwRead = 0;
|
|
DWORD dwTotalRead = 0;
|
|
DWORD dwCurrRead = 0;
|
|
|
|
do
|
|
{
|
|
do
|
|
{
|
|
//Read a chunk of data from the file increment buffer offsets and amount to read
|
|
//based on what's already been read in this iteration of the loop
|
|
HRESULT hr = readFile.Read(((LPBYTE)spData)+dwCurrRead, dwToGet-dwCurrRead, dwRead);
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr != AtlHresultFromWin32(ERROR_MORE_DATA))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
dwCurrRead += dwRead;
|
|
|
|
} while (dwRead != 0 && dwCurrRead < dwToGet);
|
|
|
|
//reset nEncodedLength
|
|
nEncodedLength = nRequiredLength;
|
|
switch (m_nEncodingScheme)
|
|
{
|
|
case ATLSMTP_BASE64_ENCODE:
|
|
//if we are at the end of input (dwCurrRead < dwToGet), output the trailing padding if necessary
|
|
//(ATL_FLAG_NONE)
|
|
bRet = Base64Encode(spData, dwCurrRead, currBuffer, &nEncodedLength,
|
|
(dwCurrRead < dwToGet ? ATL_BASE64_FLAG_NONE: ATL_BASE64_FLAG_NOPAD));
|
|
//Base64Encoding needs explicit CRLF added
|
|
if (dwCurrRead < dwToGet)
|
|
{
|
|
currBuffer[nEncodedLength++] = '\r';
|
|
currBuffer[nEncodedLength++] = '\n';
|
|
}
|
|
break;
|
|
case ATLSMTP_UUENCODE:
|
|
//if we are at the beginning of the input, output the header (ATL_UUENCODE_HEADER)
|
|
//if we are the end of input (dwCurrRead < dwToGet), output the 'end'
|
|
//we are encoding for purposes of sending mail, so stuff dots (ATL_UUENCODE_DOT)
|
|
bRet = UUEncode(spData, dwCurrRead, currBuffer, &nEncodedLength, m_szFileName,
|
|
(dwTotalRead > 0 ? 0 : ATLSMTP_UUENCODE_HEADER) |
|
|
(dwCurrRead < dwToGet ? ATLSMTP_UUENCODE_END : 0) |
|
|
((dwFlags & ATLSMTP_FORMAT_SMTP) ? ATLSMTP_UUENCODE_DOT : 0));
|
|
break;
|
|
case ATLSMTP_QP_ENCODE:
|
|
//we are encoding for purposes of sending mail, so stuff dots
|
|
bRet = QPEncode(spData, dwCurrRead, currBuffer, &nEncodedLength,
|
|
((dwFlags & ATLSMTP_FORMAT_SMTP) ? ATLSMTP_QPENCODE_DOT : 0) |
|
|
(dwCurrRead < dwToGet ? 0 : ATLSMTP_QPENCODE_TRAILING_SOFT));
|
|
break;
|
|
}
|
|
//try to send the encoded data
|
|
#ifdef ATLSMTP_DOUBLE_BUFFERED
|
|
if (bRet)
|
|
{
|
|
bRet = AtlSmtpSendOverlapped(hFile, currBuffer, nEncodedLength,
|
|
prevBuffer, dwPrevLength, pOverlapped);
|
|
}
|
|
|
|
//swap the buffers
|
|
dwPrevLength = nEncodedLength;
|
|
prevBuffer = currBuffer;
|
|
currBuffer = (nCurrBuffer == 0 ? buffer2 : buffer1);
|
|
nCurrBuffer = (nCurrBuffer == 0 ? 1 : 0);
|
|
#else
|
|
if (bRet)
|
|
{
|
|
bRet = AtlSmtpSendAndWait(hFile, currBuffer, nEncodedLength, pOverlapped);
|
|
}
|
|
#endif // ATLSMTP_DOUBLE_BUFFERED
|
|
|
|
dwTotalRead += dwCurrRead;
|
|
if (dwRead != 0)
|
|
dwCurrRead = 0;
|
|
|
|
nEncodedLength = nRequiredLength;
|
|
|
|
} while (dwRead != 0 && bRet);
|
|
|
|
//ensure that the last Send sent all the data
|
|
#ifdef ATLSMTP_DOUBLE_BUFFERED
|
|
DWORD dwWritten = 0, dwErr = 0;
|
|
if (!GetOverlappedResult(hFile, pOverlapped, &dwWritten, TRUE))
|
|
{
|
|
if ((dwErr = GetLastError()) != ERROR_IO_PENDING && dwErr != ERROR_IO_INCOMPLETE)
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
else if (dwWritten < dwPrevLength)
|
|
{
|
|
bRet = AtlSmtpSendAndWait(hFile, prevBuffer+dwWritten,
|
|
dwPrevLength-dwWritten, pOverlapped);
|
|
}
|
|
}
|
|
#endif // ATLSMTP_DOUBLE_BUFFERED
|
|
|
|
//for uuencoding, if the last chunk read was of size dwToGet, but it was also the end of the file,
|
|
//the "end" keyword will not get encoded, so a check is necessary
|
|
if (m_nEncodingScheme == ATLSMTP_UUENCODE && dwCurrRead == dwToGet)
|
|
{
|
|
bRet = UUEncode(spData, 0, currBuffer, &nEncodedLength, m_szFileName,
|
|
(dwFlags & ATLSMTP_FORMAT_SMTP ? ATLSMTP_UUENCODE_DOT : 0) |
|
|
ATLSMTP_UUENCODE_END);
|
|
if (bRet)
|
|
{
|
|
bRet = AtlSmtpSendAndWait(hFile, currBuffer, nEncodedLength, pOverlapped);
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
}; // class CMimeFileAttachment
|
|
|
|
// CMimeRawAttachment represents a file attachment MIME body part.
|
|
// The data provided is not a file, but a blob of raw data.
|
|
class CMimeRawAttachment : public CMimeAttachment
|
|
{
|
|
protected:
|
|
|
|
//the raw data
|
|
void* m_pvRaw;
|
|
|
|
//the length
|
|
DWORD m_dwLength;
|
|
|
|
//whether or not we own it
|
|
bool m_bShared;
|
|
|
|
public:
|
|
CMimeRawAttachment() throw()
|
|
:m_dwLength(0), m_bShared(false), m_pvRaw(NULL)
|
|
{
|
|
}
|
|
|
|
~CMimeRawAttachment() throw()
|
|
{
|
|
//If we own the raw data, free it
|
|
if (!m_bShared && m_pvRaw)
|
|
free(m_pvRaw);
|
|
}
|
|
|
|
virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
|
|
{
|
|
CAutoPtr<CMimeRawAttachment> pNewAttachment;
|
|
ATLTRY(pNewAttachment.Attach(new CMimeRawAttachment));
|
|
if (pNewAttachment)
|
|
*pNewAttachment = *this;
|
|
|
|
return pNewAttachment.Detach();
|
|
}
|
|
|
|
const CMimeRawAttachment& operator=(const CMimeRawAttachment& that) throw( ... )
|
|
{
|
|
if (this != &that)
|
|
{
|
|
CMimeAttachment::operator=(that);
|
|
if (!m_bShared && m_pvRaw)
|
|
free(m_pvRaw);
|
|
|
|
m_bShared = that.m_bShared;
|
|
m_dwLength = that.m_dwLength;
|
|
|
|
if (m_bShared)
|
|
{
|
|
m_pvRaw = that.m_pvRaw;
|
|
}
|
|
else
|
|
{
|
|
m_pvRaw = malloc(m_dwLength);
|
|
if (m_pvRaw)
|
|
{
|
|
Checked::memcpy_s(m_pvRaw, m_dwLength, that.m_pvRaw, m_dwLength);
|
|
}
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
// Initialize the attachment
|
|
// pData - the data
|
|
// nDataLength - the size of pData in BYTEs
|
|
// bCopyData - flag specifying whether CMimeRawAttachment should make a copy of the data (optional)
|
|
// pMultiLanguage - the IMultiLanguage pointer for codepage to character set conversion (optional)
|
|
// uiCodePage - the codepage (optional)
|
|
inline BOOL Initialize(void* pData, DWORD nDataLength, BOOL bCopyData = TRUE, LPCTSTR szDisplayName = NULL,
|
|
IMultiLanguage* pMultiLanguage = NULL, UINT uiCodePage = 0) throw()
|
|
{
|
|
// if we're already attached to some data, and it's not shared, free it
|
|
if (m_pvRaw && !m_bShared)
|
|
free(m_pvRaw);
|
|
m_pvRaw = NULL;
|
|
|
|
m_dwLength = nDataLength;
|
|
if (bCopyData)
|
|
{
|
|
m_pvRaw = calloc(sizeof(BYTE),m_dwLength);
|
|
if (!m_pvRaw)
|
|
{
|
|
return FALSE;
|
|
}
|
|
Checked::memcpy_s(m_pvRaw, m_dwLength, pData, m_dwLength);
|
|
m_bShared = false;
|
|
}
|
|
else
|
|
{
|
|
m_pvRaw = pData;
|
|
m_bShared = true;
|
|
}
|
|
|
|
if (!AtlMimeCharsetFromCodePage(m_szCharset, uiCodePage, pMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH))
|
|
return FALSE;
|
|
|
|
if (szDisplayName)
|
|
{
|
|
// use the user-specified display name
|
|
Checked::tcscpy_s(m_szDisplayName, _countof(m_szDisplayName), szDisplayName);
|
|
m_szDisplayName[_countof(m_szDisplayName)-1] = 0;
|
|
}
|
|
else
|
|
{
|
|
// no display name
|
|
*m_szDisplayName = '\0';
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Output the data--similar to CFileAttachment::WriteData
|
|
// See CFileAttachment::WriteData for comments
|
|
virtual inline BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary, DWORD dwFlags = 0) throw()
|
|
{
|
|
if ((pOverlapped == NULL) || (szBoundary == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!m_pvRaw)
|
|
return FALSE;
|
|
|
|
int nLineLength = 0, nRequiredLength = 0;
|
|
if (!GetEncodingInformation(&nRequiredLength, &nLineLength))
|
|
return FALSE;
|
|
|
|
CStringA header;
|
|
|
|
if (!MakeMimeHeader(header, szBoundary))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)header), header.GetLength(), pOverlapped))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
int nGetLines = ATLSMTP_GET_LINES;
|
|
DWORD dwCurrChunk = 0;
|
|
nRequiredLength *= nGetLines;
|
|
DWORD dwToGet = (DWORD)nGetLines*nLineLength;
|
|
int nDestLen = nRequiredLength;
|
|
BOOL bRet = FALSE;
|
|
DWORD dwRead = 0;
|
|
#ifdef ATLSMTP_DOUBLE_BUFFERED
|
|
CHeapPtr<char> buffer1;
|
|
if (!buffer1.Allocate(nRequiredLength+3))
|
|
return FALSE;
|
|
|
|
CHeapPtr<char> buffer2;
|
|
if (!buffer2.Allocate(nRequiredLength+3))
|
|
return FALSE;
|
|
|
|
char* currBuffer = buffer1;
|
|
char* prevBuffer = NULL;
|
|
int nCurrBuffer = 0;
|
|
DWORD dwPrevLength = 0;
|
|
#else
|
|
CHeapPtr<char> currBuffer;
|
|
if (!currBuffer.Allocate(nRequiredLength+3))
|
|
return FALSE;
|
|
#endif // ATLSMTP_DOUBLE_BUFFERED
|
|
|
|
do
|
|
{
|
|
if ((m_dwLength-dwRead) <= dwToGet)
|
|
dwCurrChunk = m_dwLength-dwRead;
|
|
else
|
|
dwCurrChunk = dwToGet;
|
|
switch(m_nEncodingScheme)
|
|
{
|
|
case ATLSMTP_BASE64_ENCODE:
|
|
bRet = Base64Encode(((LPBYTE)(m_pvRaw))+dwRead, dwCurrChunk, currBuffer, &nDestLen,
|
|
(dwRead < m_dwLength) ? ATL_BASE64_FLAG_NONE : ATL_BASE64_FLAG_NOPAD);
|
|
if (dwRead+dwCurrChunk == m_dwLength)
|
|
{
|
|
currBuffer[nDestLen++] = '\r';
|
|
currBuffer[nDestLen++] = '\n';
|
|
}
|
|
break;
|
|
case ATLSMTP_UUENCODE:
|
|
bRet = UUEncode(((LPBYTE)(m_pvRaw))+dwRead, dwCurrChunk, currBuffer, &nDestLen, _T("rawdata"),
|
|
(dwRead > 0 ? 0 : ATLSMTP_UUENCODE_HEADER) |
|
|
(dwRead+dwCurrChunk == m_dwLength ? ATLSMTP_UUENCODE_END : 0) |
|
|
((dwFlags & ATLSMTP_FORMAT_SMTP) ? ATLSMTP_UUENCODE_DOT : 0));
|
|
break;
|
|
case ATLSMTP_QP_ENCODE:
|
|
bRet = QPEncode(((LPBYTE)(m_pvRaw))+dwRead, dwCurrChunk, currBuffer, &nDestLen,
|
|
((dwFlags & ATLSMTP_FORMAT_SMTP) ? ATLSMTP_QPENCODE_DOT : 0) |
|
|
(dwRead+dwCurrChunk == m_dwLength ? 0 : ATLSMTP_QPENCODE_TRAILING_SOFT));
|
|
break;
|
|
}
|
|
if (!bRet)
|
|
break;
|
|
#ifdef ATLSMTP_DOUBLE_BUFFERED
|
|
bRet = AtlSmtpSendOverlapped(hFile, currBuffer, nDestLen, prevBuffer, dwPrevLength, pOverlapped);
|
|
dwPrevLength = (DWORD)nDestLen;
|
|
prevBuffer = currBuffer;
|
|
currBuffer = (nCurrBuffer == 0 ? buffer2 : buffer1);
|
|
nCurrBuffer = (nCurrBuffer == 0 ? 1 : 0);
|
|
#else
|
|
bRet = AtlSmtpSendAndWait(hFile, currBuffer, nDestLen, pOverlapped);
|
|
#endif // ATLSMTP_DOUBLE_BUFFERED
|
|
|
|
nDestLen = nRequiredLength;
|
|
dwRead += dwCurrChunk;
|
|
} while (bRet && (dwRead < m_dwLength));
|
|
|
|
//ensure all data is sent from prevBuffer
|
|
#ifdef ATLSMTP_DOUBLE_BUFFERED
|
|
DWORD dwWritten = 0, dwErr = 0;
|
|
if (!GetOverlappedResult(hFile, pOverlapped, &dwWritten, TRUE))
|
|
{
|
|
if ((dwErr = GetLastError()) != ERROR_IO_PENDING && dwErr != ERROR_IO_INCOMPLETE)
|
|
bRet = FALSE;
|
|
else if (dwWritten < dwPrevLength)
|
|
bRet = AtlSmtpSendAndWait(hFile, prevBuffer+dwWritten, dwPrevLength-dwWritten, pOverlapped);
|
|
}
|
|
#endif // ATLSMTP_DOUBLE_BUFFERED
|
|
|
|
return bRet;
|
|
}
|
|
}; // class CMimeRawAttachment
|
|
|
|
|
|
// CMimeText - represents a text body part in MIME body
|
|
class CMimeText : public CMimeBodyPart
|
|
{
|
|
protected:
|
|
|
|
// the text
|
|
CHeapPtr<char> m_szText;
|
|
|
|
// the character set
|
|
char m_szCharset[ATL_MAX_ENC_CHARSET_LENGTH];
|
|
|
|
// the text length
|
|
int m_nTextLen;
|
|
|
|
public:
|
|
CMimeText() throw()
|
|
:m_nTextLen(0)
|
|
{
|
|
Checked::strcpy_s(m_szCharset, ATL_MAX_ENC_CHARSET_LENGTH, ATLSMTP_DEFAULT_CSET);
|
|
}
|
|
|
|
virtual ~CMimeText() throw()
|
|
{
|
|
}
|
|
|
|
// Get the content type
|
|
virtual inline LPCSTR GetContentType() throw()
|
|
{
|
|
return "text/plain";
|
|
}
|
|
|
|
// Get the character set
|
|
virtual inline LPCSTR GetCharset() throw()
|
|
{
|
|
return m_szCharset;
|
|
}
|
|
|
|
virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
|
|
{
|
|
CAutoPtr<CMimeText> pNewText;
|
|
ATLTRY(pNewText.Attach(new CMimeText));
|
|
if (pNewText)
|
|
*pNewText = *this;
|
|
|
|
return pNewText.Detach();
|
|
}
|
|
|
|
const CMimeText& operator=(const CMimeText& that) throw( ... )
|
|
{
|
|
if (this != &that)
|
|
{
|
|
m_nTextLen = that.m_nTextLen;
|
|
Checked::strcpy_s(m_szCharset, ATL_MAX_ENC_CHARSET_LENGTH, that.m_szCharset);
|
|
m_szText.Free();
|
|
if (m_szText.AllocateBytes(m_nTextLen) != false)
|
|
{
|
|
Checked::memcpy_s((char *)m_szText, m_nTextLen, (char *)that.m_szText, m_nTextLen);
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
// Initialize the body part
|
|
// szText - the text (required)
|
|
// nTextLen - the text length in bytes (optional--if not specified a _tcslen will be done)
|
|
// pMultiLanguage - the IMultiLanguagte pointer for converting codepages to MIME character sets (optional)
|
|
// uiCodePage - the codepage
|
|
inline BOOL Initialize(LPCTSTR szText, int nTextLen = -1, IMultiLanguage* pMultiLanguage = NULL, UINT uiCodePage = 0) throw()
|
|
{
|
|
BOOL bRet = TRUE;
|
|
|
|
// if IMultiLanguage is there, respect the codepage
|
|
if (pMultiLanguage)
|
|
{
|
|
CHeapPtr<char> szTextPtr;
|
|
UINT nLen(0);
|
|
|
|
bRet = AtlMimeConvertString(pMultiLanguage, uiCodePage, szText, &szTextPtr, &nLen);
|
|
if (bRet)
|
|
{
|
|
m_szText.Free();
|
|
m_szText.Attach(szTextPtr.Detach());
|
|
m_nTextLen = nLen;
|
|
}
|
|
}
|
|
else // no multilanguage support
|
|
{
|
|
if (nTextLen < 0)
|
|
{
|
|
nTextLen = (int) _tcslen(szText);
|
|
nTextLen*= sizeof(TCHAR);
|
|
}
|
|
|
|
m_szText.Free();
|
|
if (m_szText.AllocateBytes(nTextLen) != false)
|
|
{
|
|
Checked::memcpy_s((char *)m_szText, nTextLen, szText, nTextLen);
|
|
m_nTextLen = nTextLen;
|
|
}
|
|
}
|
|
|
|
if (bRet)
|
|
{
|
|
bRet = AtlMimeCharsetFromCodePage(m_szCharset, uiCodePage, pMultiLanguage, ATL_MAX_ENC_CHARSET_LENGTH);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// Dump the data to hFile
|
|
virtual inline BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary, DWORD dwFlags = 0) throw()
|
|
{
|
|
if ((pOverlapped == NULL) || (szBoundary == NULL))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CStringA strHeader;
|
|
char sendBuffer[ATLSMTP_READBUFFER_SIZE];
|
|
LPSTR pSendBuffer = sendBuffer;
|
|
LPSTR szText = m_szText;
|
|
|
|
if (!MakeMimeHeader(strHeader, szBoundary))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//copy the header into the sendbuffer
|
|
int nWritten = strHeader.GetLength();
|
|
if(nWritten > ATLSMTP_READBUFFER_SIZE)
|
|
return FALSE;
|
|
|
|
Checked::memcpy_s(pSendBuffer, ATLSMTP_READBUFFER_SIZE, (LPCSTR)strHeader, nWritten);
|
|
pSendBuffer+= nWritten;
|
|
int nRead = 0;
|
|
int nLineLen = 0;
|
|
|
|
//subtract 2 from these because it's easier for when we have
|
|
//to break lines with a CRLF
|
|
int nMaxLineLength = ATLSMTP_MAX_LINE_LENGTH-2;
|
|
int nMaxBufferSize = ATLSMTP_READBUFFER_SIZE-2;
|
|
while (nRead <= m_nTextLen)
|
|
{
|
|
//if the buffer is full or we've reached the end of the text,
|
|
//send it
|
|
if (nWritten >= nMaxBufferSize || nRead == m_nTextLen)
|
|
{
|
|
if (!AtlSmtpSendAndWait(hFile, sendBuffer, nWritten, pOverlapped))
|
|
return FALSE;
|
|
nWritten = 0;
|
|
pSendBuffer = sendBuffer;
|
|
if (nRead == m_nTextLen)
|
|
{
|
|
break; // job done, no need to run the code below
|
|
}
|
|
}
|
|
|
|
//if we're at the end of the line, break it
|
|
if (nLineLen == nMaxLineLength)
|
|
{
|
|
if(nWritten + 2 > ATLSMTP_READBUFFER_SIZE)
|
|
return FALSE;
|
|
*pSendBuffer++ = '\r';
|
|
*pSendBuffer++ = '\n';
|
|
nWritten+= 2;
|
|
nLineLen = -1;
|
|
continue;
|
|
}
|
|
|
|
//stuff dots at the start of the line
|
|
if (nLineLen == 0 && (dwFlags & ATLSMTP_FORMAT_SMTP) && *szText == '.')
|
|
{
|
|
if(nWritten + 1 > ATLSMTP_READBUFFER_SIZE)
|
|
return FALSE;
|
|
*pSendBuffer++ = '.';
|
|
nWritten++;
|
|
nLineLen++;
|
|
continue;
|
|
}
|
|
|
|
//if we hit a CRLF, reset nLineLen
|
|
if (*szText == '\n' && nRead > 0 && *(szText-1) == '\r')
|
|
nLineLen = -1;
|
|
|
|
if(nWritten + 1 > ATLSMTP_READBUFFER_SIZE)
|
|
return FALSE;
|
|
*pSendBuffer++ = (*szText++);
|
|
nRead++;
|
|
nWritten++;
|
|
nLineLen++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
protected:
|
|
|
|
// Make the MIME header
|
|
virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) throw()
|
|
{
|
|
char szBegin[256];
|
|
if (*szBoundary)
|
|
{
|
|
// this is not the only body part
|
|
Checked::memcpy_s(szBegin, sizeof(szBegin), ATLMIME_SEPARATOR, sizeof(ATLMIME_SEPARATOR));
|
|
Checked::memcpy_s(szBegin+6, sizeof(szBegin)-6, szBoundary, ATL_MIME_BOUNDARYLEN);
|
|
*(szBegin+(ATL_MIME_BOUNDARYLEN+6)) = '\0';
|
|
}
|
|
else
|
|
{
|
|
// this is the only body part, so output the full MIME header
|
|
Checked::memcpy_s(szBegin, sizeof(szBegin), ATLMIME_VERSION, sizeof(ATLMIME_VERSION));
|
|
}
|
|
|
|
_ATLTRY
|
|
{
|
|
header.Format("%s\r\nContent-Type: text/plain;\r\n\tcharset=\"%s\"\r\nContent-Transfer-Encoding: 8bit\r\n\r\n",
|
|
szBegin, m_szCharset);
|
|
return TRUE;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}; // class CMimeText
|
|
|
|
|
|
// CMimeMessage - the MIME message class. Represents a full MIME message
|
|
class CMimeMessage : public CMimeHeader
|
|
{
|
|
protected:
|
|
|
|
// The list of the MIME body parts
|
|
CAutoPtrList<CMimeBodyPart> m_BodyParts;
|
|
|
|
// The display name of the message
|
|
char m_szDisplayName[MAX_PATH+1];
|
|
|
|
public:
|
|
CMimeMessage(IMultiLanguage *pMultiLanguage = NULL) throw()
|
|
{
|
|
Initialize(pMultiLanguage);
|
|
Checked::memcpy_s(m_szDisplayName, MAX_PATH+1, ATLMIME_EMAIL, sizeof(ATLMIME_EMAIL));
|
|
}
|
|
|
|
virtual ~CMimeMessage() throw()
|
|
{
|
|
RemoveParts();
|
|
}
|
|
|
|
void RemoveParts() throw()
|
|
{
|
|
m_BodyParts.RemoveAll();
|
|
}
|
|
|
|
|
|
virtual ATL_NOINLINE CMimeBodyPart* Copy() throw( ... )
|
|
{
|
|
CAutoPtr<CMimeMessage> pNewMessage;
|
|
ATLTRY(pNewMessage.Attach(new CMimeMessage));
|
|
if (pNewMessage)
|
|
*pNewMessage = *this;
|
|
|
|
return pNewMessage.Detach();
|
|
}
|
|
|
|
|
|
const CMimeMessage& operator=(const CMimeMessage& that) throw( ... )
|
|
{
|
|
if (this != &that)
|
|
{
|
|
CMimeHeader::operator=(that);
|
|
Checked::strcpy_s(m_szDisplayName, MAX_PATH+1, that.m_szDisplayName);
|
|
|
|
RemoveParts();
|
|
POSITION pos = that.m_BodyParts.GetHeadPosition();
|
|
while (pos != NULL)
|
|
{
|
|
CAutoPtr<CMimeBodyPart> pCopy(that.m_BodyParts.GetNext(pos)->Copy());
|
|
if (pCopy)
|
|
{
|
|
m_BodyParts.AddTail(pCopy);
|
|
}
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
// Set the display name of the message
|
|
inline BOOL SetDisplayName(LPCTSTR szDisplayName) throw()
|
|
{
|
|
if (szDisplayName == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
_ATLTRY
|
|
{
|
|
CT2CA szDisplayNameA(szDisplayName);
|
|
if (szDisplayNameA == NULL || strlen(szDisplayNameA) > MAX_PATH)
|
|
return FALSE;
|
|
Checked::strcpy_s(m_szDisplayName, MAX_PATH+1, szDisplayNameA);
|
|
return TRUE;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Add some text to the message at position nPos in the body parts list
|
|
// szText - the text
|
|
// nTextLen - the size of the text in bytes (optional - if not specified a _tcslen will be done)
|
|
// nPos - the position in the message at which to insert the text (optional)
|
|
// uiCodePage - the codepage (optional)
|
|
inline BOOL AddText(LPCTSTR szText, int nTextLen = -1, int nPos = 1, UINT uiCodePage = 0) throw()
|
|
{
|
|
if (szText == NULL)
|
|
return FALSE;
|
|
|
|
if (nPos < 1)
|
|
{
|
|
nPos = 1;
|
|
}
|
|
|
|
CAutoPtr<CMimeBodyPart> spNewText;
|
|
CMimeText *pNewText = NULL;
|
|
ATLTRY(spNewText.Attach(pNewText = new CMimeText()));
|
|
if (!spNewText || !pNewText)
|
|
return FALSE;
|
|
|
|
BOOL bRet = pNewText->Initialize(szText, nTextLen, m_spMultiLanguage, uiCodePage);
|
|
if (bRet)
|
|
{
|
|
_ATLTRY
|
|
{
|
|
POSITION currPos = m_BodyParts.FindIndex(nPos-1);
|
|
|
|
if (!currPos)
|
|
{
|
|
if (!m_BodyParts.AddTail(spNewText))
|
|
bRet = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (!m_BodyParts.InsertBefore(currPos, spNewText))
|
|
bRet = FALSE;
|
|
}
|
|
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// Dump the data
|
|
virtual BOOL WriteData(HANDLE hFile, LPOVERLAPPED pOverlapped, LPCSTR szBoundary=NULL, DWORD dwFlags = 0) throw()
|
|
{
|
|
if (pOverlapped == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Make the MIME boundary for this message
|
|
char szBoundaryBuf[ATL_MIME_BOUNDARYLEN+1];
|
|
if(MakeBoundary(szBoundaryBuf,ATL_MIME_BOUNDARYLEN+1) == FALSE)
|
|
return FALSE;
|
|
|
|
// if the passed boundary is valid, this is an attached message
|
|
if (szBoundary && *szBoundary != '\0')
|
|
{
|
|
_ATLTRY
|
|
{
|
|
// output the MIME header for a message attachment
|
|
CStringA strHeader;
|
|
strHeader.Format("\r\n\r\n--%s\r\nContent-Type: message/rfc822\r\n\tname=\"%s\"\r\nContent-Transfer-Encoding: 8bit\r\n"
|
|
"Content-Disposition: attachment;\r\n\tfilename=\"%s\"\r\n\r\n",
|
|
szBoundary, m_szDisplayName, m_szDisplayName);
|
|
|
|
if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)strHeader), strHeader.GetLength(), pOverlapped))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (!CMimeHeader::WriteData(hFile, pOverlapped, szBoundaryBuf, dwFlags))
|
|
return FALSE;
|
|
|
|
// Create and output the header
|
|
CStringA strHeader;
|
|
|
|
if (!MakeMimeHeader(strHeader, szBoundaryBuf))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!AtlSmtpSendAndWait(hFile, ((LPCSTR)strHeader), strHeader.GetLength(), pOverlapped))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CMimeBodyPart* pCurrPart;
|
|
POSITION currPos = m_BodyParts.GetHeadPosition();
|
|
|
|
//Dump the body parts
|
|
while (currPos != NULL)
|
|
{
|
|
pCurrPart = m_BodyParts.GetAt(currPos);
|
|
if (!pCurrPart->WriteData(hFile, pOverlapped, szBoundaryBuf, dwFlags))
|
|
{
|
|
return FALSE;
|
|
}
|
|
m_BodyParts.GetNext(currPos);
|
|
}
|
|
|
|
char szBuf[ATL_MIME_BOUNDARYLEN+(sizeof("\r\n\r\n--%s--\r\n"))];
|
|
//output a trailing boundary
|
|
if (*szBoundaryBuf)
|
|
{
|
|
int nBufLen = sprintf_s(szBuf, ATL_MIME_BOUNDARYLEN+(sizeof("\r\n\r\n--%s--\r\n")),
|
|
"\r\n\r\n--%s--\r\n", szBoundaryBuf);
|
|
if ((nBufLen < 0) || (!AtlSmtpSendAndWait(hFile, szBuf, nBufLen, pOverlapped)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// Attach a file.
|
|
// szFileName - the filename
|
|
// szDisplayName - the display name (optional)
|
|
// szContentType - the content type (optional - defaults to NULL -- lookup will be attempted, otherwise default to application/octet-stream)
|
|
// nEncodingScheme - the encoding scheme to use for the attachment (optional - defaults to base64
|
|
// uiCodePage - the codepage (optional)
|
|
inline BOOL AttachFile(LPCTSTR szFileName, LPCTSTR szDisplayName = NULL, LPCTSTR szContentType = NULL,
|
|
int nEncodingScheme = ATLSMTP_BASE64_ENCODE, UINT uiCodepage = 0)
|
|
{
|
|
if (szFileName == NULL)
|
|
return FALSE;
|
|
|
|
CAutoPtr<CMimeBodyPart> spFileAttach;
|
|
CMimeFileAttachment* pFileAttach = NULL;
|
|
ATLTRY(spFileAttach.Attach(pFileAttach = new CMimeFileAttachment()));
|
|
if (!spFileAttach || !pFileAttach)
|
|
return FALSE;
|
|
|
|
BOOL bRet = pFileAttach->Initialize(szFileName, szDisplayName, m_spMultiLanguage, uiCodepage);
|
|
|
|
if (bRet)
|
|
bRet = pFileAttach->SetEncodingScheme(nEncodingScheme);
|
|
|
|
CString strContentType;
|
|
if (bRet && (szContentType == NULL))
|
|
{
|
|
if (GetContentTypeFromFileName(szFileName, strContentType) != ERROR_OUTOFMEMORY)
|
|
{
|
|
szContentType = strContentType;
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
|
|
_ATLTRY
|
|
{
|
|
if (bRet)
|
|
{
|
|
bRet = pFileAttach->SetContentType(szContentType);
|
|
if (bRet)
|
|
{
|
|
if (!m_BodyParts.AddTail(spFileAttach))
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// Attach some raw data
|
|
// pRawData - the data
|
|
// nDataLength - the size of the data in bytes
|
|
// nEncodingScheme - the encoding scheme to use for the attachment (optional - defaults to base64
|
|
// uiCodePage - the codepage (optional)
|
|
inline BOOL AttachRaw(void* pRawData, DWORD dwDataLength, int nEncodingScheme = ATLSMTP_BASE64_ENCODE, BOOL bCopyData = TRUE,
|
|
LPCTSTR szDisplayName = NULL, LPCTSTR szContentType = _T("application/octet-stream"), UINT uiCodepage = 0)
|
|
{
|
|
if (!pRawData)
|
|
return FALSE;
|
|
|
|
CAutoPtr<CMimeBodyPart> spRawAttach;
|
|
CMimeRawAttachment* pRawAttach;
|
|
ATLTRY(spRawAttach.Attach(pRawAttach = new CMimeRawAttachment()));
|
|
if (!spRawAttach)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL bRet = pRawAttach->Initialize(pRawData, dwDataLength, bCopyData, szDisplayName, m_spMultiLanguage, uiCodepage);
|
|
|
|
if (bRet)
|
|
bRet = pRawAttach->SetEncodingScheme(nEncodingScheme);
|
|
if (bRet)
|
|
bRet = pRawAttach->SetContentType(szContentType);
|
|
|
|
_ATLTRY
|
|
{
|
|
if (bRet)
|
|
if(!m_BodyParts.AddTail(spRawAttach))
|
|
bRet = FALSE;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
// Attach a CMimeMessage
|
|
// pMsg - pointer to the Msg object
|
|
inline BOOL AttachMessage(CMimeMessage* pMsg) throw( ... )
|
|
{
|
|
if (!pMsg)
|
|
return FALSE;
|
|
|
|
_ATLTRY
|
|
{
|
|
CAutoPtr<CMimeBodyPart> spMsg(pMsg->Copy());
|
|
if (!m_BodyParts.AddTail(spMsg))
|
|
return FALSE;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
protected:
|
|
// Make the MIME header
|
|
virtual inline BOOL MakeMimeHeader(CStringA& header, LPCSTR szBoundary) throw()
|
|
{
|
|
_ATLTRY
|
|
{
|
|
if (!*szBoundary)
|
|
{
|
|
header.Format("X-Priority: %d\r\n%s", m_nPriority, (LPCSTR) m_XHeader);
|
|
}
|
|
else if (m_BodyParts.GetCount() > 1)
|
|
{
|
|
header.Format("X-Priority: %d\r\n%sMIME-Version: 1.0\r\nContent-Type: multipart/mixed;\r\n\tboundary=\"%s\"\r\n",
|
|
m_nPriority, (LPCSTR) m_XHeader, szBoundary);
|
|
}
|
|
return TRUE;
|
|
}
|
|
_ATLCATCHALL()
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Make the MIME boundary
|
|
inline BOOL MakeBoundary(__out_ecount_z(nBufLen) LPSTR szBoundary, __in int nBufLen)
|
|
{
|
|
ATLENSURE(szBoundary != NULL);
|
|
|
|
if(nBufLen < 1)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (m_BodyParts.GetCount() < 2)
|
|
{
|
|
*szBoundary = '\0';
|
|
}
|
|
else
|
|
{
|
|
int ret = sprintf_s(szBoundary, nBufLen, "------=_Next_Part_%.10u.%.3u", GetTickCount(), rand()%1000);
|
|
if (ret == -1 || ret >= nBufLen)
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
}; // class CMimeMessage
|
|
|
|
} // namespace ATL
|
|
#pragma pack(pop)
|
|
|
|
#ifndef _CPPUNWIND
|
|
#pragma warning (pop)
|
|
#endif //_CPPUNWIND
|
|
|
|
#pragma warning(pop)
|
|
|
|
#endif // __ATLMIME_H__
|