1 #ifdef _WIN32
2 
3 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
4 //
5 // File: MAPIMessage.cpp
6 // Description: MAPI Message class wrapper
7 //
8 // Copyright (C) 2005-2011, Noel Dillabough
9 //
10 // This source code is free to use and modify provided this notice remains intact and that any enhancements
11 // or bug fixes are posted to the CodeProject page hosting this class for the community to benefit.
12 //
13 // Usage: see the CodeProject article at http://www.codeproject.com/internet/CMapiEx.asp
14 //
15 ////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 
17 // Ported to U++ Framework by Koldo. See License.txt file
18 
19 #include "MAPIEx.h"
20 
21 const GUID CLSID_MailMessage = {0x00020D0B, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46};
22 
23 #ifndef _WIN32_WCE
24 #define INITGUID
25 #define USES_IID_IMessage
26 #include <InitGuid.h>
27 #include <MAPIGuid.h>
28 #endif
29 
30 
31 /////////////////////////////////////////////////////////////
32 // MAPIMessage
33 
MAPIMessage()34 MAPIMessage::MAPIMessage() {
35 	m_pRecipients = NULL;
36 }
37 
~MAPIMessage()38 MAPIMessage::~MAPIMessage() {
39 	Close();
40 }
41 
Open(MAPIEx * pMAPI,SBinary entryID)42 bool MAPIMessage::Open(MAPIEx* pMAPI, SBinary entryID) {
43 	if(!MAPIObject::Open(pMAPI,entryID))
44 		return false;
45 
46 	m_strSenderName = GetPropertyString(PR_SENDER_NAME);
47 	FillSenderEmail();
48 	m_strSubject = GetPropertyString(PR_SUBJECT);
49 
50 	return true;
51 }
52 
Close()53 void MAPIMessage::Close() {
54 	RELEASE(m_pRecipients);
55 	MAPIObject::Close();
56 }
57 
GetHeader()58 String MAPIMessage::GetHeader() {
59 	return GetPropertyString(PR_TRANSPORT_MESSAGE_HEADERS);
60 }
61 
FillSenderEmail()62 void MAPIMessage::FillSenderEmail() {
63 	String strAddrType = GetPropertyString(PR_SENDER_ADDRTYPE);
64 	m_strSenderEmail = GetPropertyString(PR_SENDER_EMAIL_ADDRESS);
65 
66 	// for Microsoft Exchange server internal mails we want to try to resolve the SMTP email address
67 	if(strAddrType == "EX") {
68 		LPSPropValue pProp;
69 		if(GetProperty(PR_SENDER_ENTRYID, pProp)==S_OK) {
70 			if(m_pMAPI)
71 				m_pMAPI->GetExEmail(pProp->Value.bin, m_strSenderEmail);
72 			MAPIFreeBuffer(pProp);
73 		}
74 	}
75 }
76 
GetTime(ULONG property)77 Time MAPIMessage::GetTime(ULONG property) {
78 	SYSTEMTIME st;
79 	LPSPropValue pProp;
80 	if(GetProperty(property, pProp) == S_OK) {
81 		FILETIME tmLocal;
82 		FileTimeToLocalFileTime(&pProp->Value.ft, &tmLocal);
83 		FileTimeToSystemTime(&tmLocal, &st);
84 		MAPIFreeBuffer(pProp);
85 		return MAPIEx::GetSystemTime(st);
86 	}
87 	return Null;
88 }
89 
GetTo()90 String MAPIMessage::GetTo() {
91 	return GetPropertyString(PR_DISPLAY_TO);
92 }
93 
GetCC()94 String MAPIMessage::GetCC() {
95 	return GetPropertyString(PR_DISPLAY_CC);
96 }
97 
GetBCC()98 String MAPIMessage::GetBCC() {
99 	return GetPropertyString(PR_DISPLAY_BCC);
100 }
101 
GetMessageStatus()102 int MAPIMessage::GetMessageStatus() {
103 	return GetPropertyValue(PR_MSG_STATUS, 0);
104 }
105 
GetPriority()106 int MAPIMessage::GetPriority() {
107 	return GetPropertyValue(PR_PRIORITY, PRIO_NONURGENT);
108 }
109 
GetSize()110 DWORD MAPIMessage::GetSize() {
111 	return GetPropertyValue(PR_MESSAGE_SIZE, 0);
112 }
113 
GetRecipients()114 bool MAPIMessage::GetRecipients() {
115 	if(!Message())
116 		return false;
117 	RELEASE(m_pRecipients);
118 
119 	if(Message()->GetRecipientTable(MAPIEx::cm_nMAPICode, &m_pRecipients) != S_OK)
120 		return false;
121 
122 	const int nProperties = RECIPIENT_COLS;
123 	SizedSPropTagArray(nProperties, Columns)={nProperties,{PR_RECIPIENT_TYPE, PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ADDRTYPE, PR_ENTRYID }};
124 	return (m_pRecipients->SetColumns((LPSPropTagArray)&Columns, 0) == S_OK);
125 }
126 
GetNextRecipient(String & strName,String & strEmail,int & nType)127 bool MAPIMessage::GetNextRecipient(String& strName, String& strEmail, int &nType) {
128 	if(!m_pRecipients)
129 		return false;
130 
131 	LPSRowSet pRows = NULL;
132 	bool bResult = false;
133 	if(m_pRecipients->QueryRows(1, 0, &pRows) == S_OK) {
134 		if(pRows->cRows) {
135 			nType = pRows->aRow[0].lpProps[PROP_RECIPIENT_TYPE].Value.ul;
136 			strName = MAPIEx::GetValidString(pRows->aRow[0].lpProps[PROP_RECIPIENT_NAME]);
137 
138 			// for Microsoft Exchange server internal mails we want to try to resolve the SMTP email address
139 			String strAddrType = MAPIEx::GetValidString(pRows->aRow[0].lpProps[PROP_ADDR_TYPE]);
140 			if(strAddrType == "EX") {
141 				if(m_pMAPI)
142 					m_pMAPI->GetExEmail(pRows->aRow[0].lpProps[PROP_ENTRYID].Value.bin, strEmail);
143 			} else
144 				strEmail = MAPIEx::GetValidString(pRows->aRow[0].lpProps[PROP_RECIPIENT_EMAIL]);
145 			bResult = true;
146 		}
147 		MAPIEx::FreeProws(pRows);
148 	}
149 	return bResult;
150 }
151 
GetReplyTo()152 String MAPIMessage::GetReplyTo() {
153 	String strEmail;
154 	bool bResult = false;
155 	LPSPropValue prop;
156 	if(GetProperty(PR_REPLY_RECIPIENT_ENTRIES, prop) == S_OK) {
157 		LPFLATENTRYLIST pReplyEntryList = (LPFLATENTRYLIST)prop->Value.bin.lpb;
158 		if(pReplyEntryList->cEntries > 0) {
159 			LPFLATENTRY pReplyEntry = (LPFLATENTRY)pReplyEntryList->abEntries;
160 
161 			SBinary entryID;
162 			entryID.cb = pReplyEntry->cb;
163 			entryID.lpb = pReplyEntry->abEntry;
164 			bResult = m_pMAPI->GetExEmail(entryID, strEmail);
165 		}
166 		MAPIFreeBuffer(prop);
167 	}
168 	if (bResult)
169 		return strEmail;
170 	else
171 		return String();
172 }
173 
174 // nPriority defaults to IMPORTANCE_NORMAL, IMPORTANCE_HIGH or IMPORTANCE_LOW also valid
Create(MAPIEx & mapi,MAPIFolder & folder,int nPriority,bool bSaveToSentFolder)175 bool MAPIMessage::Create(MAPIEx &mapi, MAPIFolder &folder, int nPriority, bool bSaveToSentFolder) {
176 	if(!MAPIObject::Create(mapi, folder))
177 		return false;
178 
179 	SPropValue prop;
180 	SetMessageFlags(MSGFLAG_UNSENT | MSGFLAG_FROMME);
181 
182 	LPSPropValue pProp = NULL;
183 	ULONG cValues = 0;
184 	ULONG rgTags[] = { 1, PR_IPM_SENTMAIL_ENTRYID };
185 
186 	if(m_pMAPI->GetMessageStore()->GetProps((LPSPropTagArray) rgTags, MAPIEx::cm_nMAPICode, &cValues, &pProp)==S_OK) {
187 		if(bSaveToSentFolder) {
188 			prop.ulPropTag = PR_SENTMAIL_ENTRYID;
189 			prop.Value.bin = pProp[0].Value.bin;
190 		} else {
191 			prop.ulPropTag = PR_DELETE_AFTER_SUBMIT;
192 			prop.Value.b = true;
193 		}
194 		Message()->SetProps(1, &prop, NULL);
195 		SetEntryID(&(pProp[0].Value.bin));
196 		MAPIFreeBuffer(pProp);
197 	}
198 
199 	if(nPriority != IMPORTANCE_NORMAL) {
200 		prop.ulPropTag = PR_IMPORTANCE;
201 		prop.Value.l = nPriority;
202 		Message()->SetProps(1, &prop, NULL);
203 	}
204 	SetMessageClass("IPM.Note");
205 
206 #ifdef _WIN32_WCE
207 	prop.ulPropTag = PR_MSG_STATUS;
208 	prop.Value.ul = MSGSTATUS_RECTYPE_SMTP;
209 	Message()->SetProps(1, &prop, NULL);
210 #endif
211 
212 	return true;
213 }
214 
IsUnread()215 bool MAPIMessage::IsUnread() {
216 	return (!(GetMessageFlags()&MSGFLAG_READ));
217 }
218 
MarkAsRead(bool bRead)219 bool MAPIMessage::MarkAsRead(bool bRead) {
220 #ifdef _WIN32_WCE
221 	int ulMessageFlags = GetMessageFlags();
222 	if(bRead) ulMessageFlags |= MSGFLAG_READ;
223 	else ulMessageFlags &= ~MSGFLAG_READ;
224 	return SetMessageFlags(ulMessageFlags);
225 #else
226 	return (Message()->SetReadFlag(bRead ? MSGFLAG_READ : CLEAR_READ_FLAG)==S_OK);
227 #endif
228 }
229 
AddRecipients(LPADRLIST pAddressList)230 bool MAPIMessage::AddRecipients(LPADRLIST pAddressList) {
231 	HRESULT hr = E_INVALIDARG;
232 #ifdef _WIN32_WCE
233 	hr = Message()->ModifyRecipients(MODRECIP_ADD, pAddressList);
234 #else
235 	LPADRBOOK pAddressBook;
236 	if(m_pMAPI->GetSession()->OpenAddressBook(0, NULL, AB_NO_DIALOG, &pAddressBook) != S_OK)
237 		return false;
238 
239 	if(pAddressBook->ResolveName(0, MAPIEx::cm_nMAPICode, NULL, pAddressList) == S_OK)
240 		hr = Message()->ModifyRecipients(MODRECIP_ADD, pAddressList);
241 	RELEASE(pAddressBook);
242 #endif
243 	return (hr == S_OK);
244 }
245 
246 // AddrType only needed by Windows CE, use SMTP, or SMS etc, default NULL will not set PR_ADDRTYPE
AddRecipient(const String & email,int nType,const char * szAddrType)247 bool MAPIMessage::AddRecipient(const String &email, int nType, const char* szAddrType) {
248 	if(!Message() || !m_pMAPI)
249 		return false;
250 
251 	int nBufSize=CbNewADRLIST(1);
252 	LPADRLIST pAddressList = NULL;
253 	MAPIAllocateBuffer(nBufSize, (LPVOID FAR*)&pAddressList);
254 	memset(pAddressList, 0, nBufSize);
255 
256 	int nProperties = 3;
257 	if(szAddrType==NULL)
258 		nProperties--;
259 	pAddressList->cEntries=1;
260 
261 	pAddressList->aEntries[0].ulReserved1 = 0;
262 	pAddressList->aEntries[0].cValues=nProperties;
263 
264 	MAPIAllocateBuffer(sizeof(SPropValue)*nProperties, (LPVOID FAR*)&pAddressList->aEntries[0].rgPropVals);
265 	memset(pAddressList->aEntries[0].rgPropVals, 0, sizeof(SPropValue)*nProperties);
266 
267 	pAddressList->aEntries[0].rgPropVals[0].ulPropTag = PR_RECIPIENT_TYPE;
268 	pAddressList->aEntries[0].rgPropVals[0].Value.ul = nType;
269 
270 #ifdef _WIN32_WCE
271 	pAddressList->aEntries[0].rgPropVals[1].ulPropTag = PR_EMAIL_ADDRESS;
272 	pAddressList->aEntries[0].rgPropVals[1].Value.LPSZ = (TCHAR*)szEmail;
273 #else
274 	pAddressList->aEntries[0].rgPropVals[1].ulPropTag = PR_DISPLAY_NAME;
275 	pAddressList->aEntries[0].rgPropVals[1].Value.LPSZ = (TCHAR*)(email.Begin());
276 #endif
277 
278 	if(szAddrType != NULL) {
279 		pAddressList->aEntries[0].rgPropVals[2].ulPropTag = PR_ADDRTYPE;
280 		pAddressList->aEntries[0].rgPropVals[2].Value.LPSZ = (TCHAR*)szAddrType;
281 	}
282 
283 	bool bResult = AddRecipients(pAddressList);
284 	MAPIEx::ReleaseAddressList(pAddressList);
285 	return bResult;
286 }
287 
SetSubject(const String & subject)288 void MAPIMessage::SetSubject(const String &subject) {
289 	m_strSubject = subject;
290 	SetPropertyString(PR_SUBJECT, subject);
291 }
292 
SetSender(const String & senderName,const String & senderEmail)293 void MAPIMessage::SetSender(const String &senderName, const String &senderEmail) {
294 	m_strSenderName = senderName;
295 	m_strSenderEmail = senderEmail;
296 	LPTSTR szAddrType = (TCHAR*)"SMTP";
297 	if(m_strSenderName.GetLength() && m_strSenderEmail.GetLength()) {
298 		SetPropertyString(PR_SENDER_NAME, senderName);
299 		SetPropertyString(PR_SENDER_EMAIL_ADDRESS, senderEmail);
300 
301 #ifndef _WIN32_WCE
302 		LPADRBOOK pAddressBook;
303 		if(m_pMAPI->GetSession()->OpenAddressBook(0, NULL, AB_NO_DIALOG, &pAddressBook) == S_OK) {
304 			SPropValue prop;
305 			if(pAddressBook->CreateOneOff((LPTSTR)(senderName.Begin()), szAddrType,
306 										(LPTSTR)(senderEmail.Begin()), 0, &prop.Value.bin.cb,
307 										(LPENTRYID*)&prop.Value.bin.lpb) == S_OK) {
308 				prop.ulPropTag=PR_SENT_REPRESENTING_ENTRYID;
309 				Message()->SetProps(1, &prop, NULL);
310 
311 				SetPropertyString(PR_SENT_REPRESENTING_NAME, senderName);
312 				SetPropertyString(PR_SENT_REPRESENTING_EMAIL_ADDRESS, senderEmail);
313 				SetPropertyString(PR_SENT_REPRESENTING_ADDRTYPE, szAddrType);
314 			}
315 		}
316 		RELEASE(pAddressBook);
317 #endif
318 	}
319 }
320 
SetReceivedTime(const Time & tm)321 bool MAPIMessage::SetReceivedTime(const Time &tm) {
322 	SYSTEMTIME tmReceived;
323 	MAPIEx::SetSystemTime(tmReceived, tm);
324 
325 	SPropValue prop;
326 	prop.ulPropTag = PR_MESSAGE_DELIVERY_TIME;
327 #ifndef _WIN32_WCE
328 //	if(bLocalTime) TzSpecificLocalTimeToSystemTime(NULL, &tmReceived, &tmReceived);
329 #endif
330 	SystemTimeToFileTime(&tmReceived, &prop.Value.ft);
331 	return (Message() && Message()->SetProps(1, &prop, NULL) == S_OK);
332 }
333 
SetSubmitTime(const Time & tm)334 bool MAPIMessage::SetSubmitTime(const Time &tm) {
335 	SYSTEMTIME tmSubmit;
336 	MAPIEx::SetSystemTime(tmSubmit, tm);
337 
338 	SPropValue prop;
339 	prop.ulPropTag = PR_CLIENT_SUBMIT_TIME;
340 #ifndef _WIN32_WCE
341 //	if(bLocalTime) TzSpecificLocalTimeToSystemTime(NULL, &tmSubmit, &tmSubmit);
342 #endif
343 	SystemTimeToFileTime(&tmSubmit, &prop.Value.ft);
344 	return (Message() && Message()->SetProps(1, &prop, NULL) == S_OK);
345 }
346 
347 // request a Read Receipt sent to szReceiverEmail
SetReadReceipt(bool bSet,String szReceiverEmail)348 bool MAPIMessage::SetReadReceipt(bool bSet, String szReceiverEmail) {
349 	if(!Message())
350 		return false;
351 
352 	SPropValue prop;
353 	prop.ulPropTag = PR_READ_RECEIPT_REQUESTED;
354 	prop.Value.b = (unsigned short)bSet;
355 	if(Message()->SetProps(1, &prop, NULL) != S_OK)
356 		return false;
357 
358 	if(bSet && !IsNull(szReceiverEmail) && szReceiverEmail.GetLength() > 0)
359 		SetPropertyString(PR_READ_RECEIPT_REQUESTED, szReceiverEmail);
360 	return true;
361 }
362 
SetDeliveryReceipt(bool bSet)363 bool MAPIMessage::SetDeliveryReceipt(bool bSet) {
364 	if(!Message())
365 		return false;
366 
367 	SPropValue prop;
368 	prop.ulPropTag = PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED;
369 	prop.Value.b = (unsigned short)bSet;
370 	return (Message()->SetProps(1, &prop, NULL) != S_OK);
371 }
372 
373 // limited compare, compares entry IDs and subject to determine if two emails are equal
operator ==(MAPIMessage & message)374 bool MAPIMessage::operator==(MAPIMessage& message) {
375 	if(!m_entryID.cb || !message.m_entryID.cb || m_entryID.cb!=message.m_entryID.cb)
376 		return false;
377 	if(memcmp(m_entryID.lpb,message.m_entryID.lpb, m_entryID.cb))
378 		return false;
379 	return (!m_strSubject.Compare(message.m_strSubject));
380 }
381 
382 // Novell GroupWise customization by jcadmin
383 #ifndef GROUPWISE
MarkAsPrivate()384 bool MAPIMessage::MarkAsPrivate() { return false; }
385 #else
386 #include GWMAPI.h
387 //(from Novell Developer Kit)
388 #define SEND_OPTIONS_MARK_PRIVATE 0x00080000L
389 
MarkAsPrivate()390 void MAPIMessage::MarkAsPrivate()  {
391 	SPropValue prop;
392 	prop.ulPropTag = PR_NGW_SEND_OPTIONS;
393 	prop.Value.l = NGW_SEND_OPTIONS_MARK_PRIVATE;
394 	return (Message()->SetProps(1, &prop, NULL) == S_OK);
395 }
396 #endif
397 
398 // In WinCE, use MSGSTATUS_RECTYPE_SMS for an SMS, MSGSTATUS_RECTYPE_SMTP for an email
SetMessageStatus(int nMessageStatus)399 bool MAPIMessage::SetMessageStatus(int nMessageStatus) {
400 	SPropValue prop;
401 	prop.ulPropTag = PR_MSG_STATUS;
402 	prop.Value.ul = nMessageStatus;
403 	return (Message()->SetProps(1, &prop, NULL) == S_OK);
404 }
405 
406 // Shows the default MAPI form for IMessage, returns false on failure, IDOK on success or close existing messages
407 // and IDCANCEL on close new messages
ShowForm(MAPIEx * pMAPI,MAPIFolder & folder)408 int MAPIMessage::ShowForm(MAPIEx* pMAPI, MAPIFolder &folder) {
409 	//MAPIFolder* pFolder=pMAPI->GetFolder();
410 	IMAPISession* pSession = pMAPI->GetSession();
411 	ULONG_PTR2 ulMessageToken;
412 
413 	if(folder.IsOpened() && pSession && pSession->PrepareForm(NULL, Message(), &ulMessageToken) == S_OK) {
414 		ULONG ulMessageStatus=GetPropertyValue(PR_MSG_STATUS, 0);
415 		ULONG ulMessageFlags=GetMessageFlags();
416 		ULONG ulAccess=GetPropertyValue(PR_ACCESS, 0);
417 
418 		LPSPropValue pProp;
419 		if(GetProperty(PR_MESSAGE_CLASS, pProp) == S_OK) {
420 			char* szMessageClass = pProp->Value.LPSZ;
421 			HRESULT hr = pSession->ShowForm(0, pMAPI->GetMessageStore(), folder.Folder(),
422 							NULL, ulMessageToken, NULL, 0, ulMessageStatus,ulMessageFlags,
423 							ulAccess, szMessageClass);
424 			MAPIFreeBuffer(pProp);
425 			if(hr == S_OK)
426 				return IDOK;
427 			if(hr == MAPI_E_USER_CANCEL)
428 				return IDCANCEL;
429 		}
430 	}
431 	return false;
432 }
433 
Send()434 bool MAPIMessage::Send() {
435 	if(Message()) {
436 		HRESULT ret = Message()->SubmitMessage(0);
437 		if (ret == S_OK) {
438 			Close();
439 			return true;
440 		} else
441 			return false;
442 	}
443 	return false;
444 }
445 
SaveToFile(const String & fileName)446 bool MAPIMessage::SaveToFile(const String &fileName) {
447 	if (!RealizePath(fileName))
448 		return false;
449 
450 #ifdef _WIN32_WCE
451 	return false;
452 #else
453 	LPSTORAGE pStorage;
454 	DWORD dwFlags = STGM_READWRITE | STGM_TRANSACTED | STGM_CREATE;
455 	if(StgCreateDocfile(fileName.ToWString(), dwFlags, 0, &pStorage) != S_OK)
456 		return false;
457 	LPMSGSESS pMsgSession;
458 	LPMALLOC pMalloc = MF().MAPIGetDefaultMalloc();
459 	if(MF().OpenIMsgSession(pMalloc, 0, &pMsgSession) == S_OK) {
460 		LPMESSAGE pIMsg;
461 		if(MF().OpenIMsgOnIStg(pMsgSession, MAPIAllocateBuffer, MAPIAllocateMore, MAPIFreeBuffer,
462 										pMalloc, NULL, pStorage, NULL, 0, 0, &pIMsg) == S_OK) {
463 			// client must support CLSID_MailMessage as the compound document
464 			if(WriteClassStg(pStorage, CLSID_MailMessage) == S_OK) {
465 				// exclude these properties from the copy operation to speed things up
466 				SizedSPropTagArray (7, excludeTags);
467 				excludeTags.cValues = 7;
468 				excludeTags.aulPropTag[0] = PR_ACCESS;
469 				excludeTags.aulPropTag[1] = PR_BODY;
470 				excludeTags.aulPropTag[2] = PR_RTF_SYNC_BODY_COUNT;
471 				excludeTags.aulPropTag[3] = PR_RTF_SYNC_BODY_CRC;
472 				excludeTags.aulPropTag[4] = PR_RTF_SYNC_BODY_TAG;
473 				excludeTags.aulPropTag[5] = PR_RTF_SYNC_PREFIX_COUNT;
474 				excludeTags.aulPropTag[6] = PR_RTF_SYNC_TRAILING_COUNT;
475 
476 				if(Message()->CopyTo(0, NULL, (LPSPropTagArray)&excludeTags, 0, NULL,
477 											(LPIID)&IID_IMessage, pIMsg, 0, NULL ) == S_OK) {
478 					pIMsg->SaveChanges(KEEP_OPEN_READWRITE);
479 					pStorage->Commit(STGC_DEFAULT);
480 				}
481 			}
482 			RELEASE(pIMsg);
483 		}
484 		MF().CloseIMsgSession(pMsgSession);
485 	}
486 	RELEASE(pStorage);
487 	return true;
488 #endif
489 }
490 
491 #endif