ICalToMAPI.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <kopano/zcdefs.h>
  18. #include <memory>
  19. #include <new>
  20. #include <kopano/platform.h>
  21. #include <kopano/ECRestriction.h>
  22. #include <kopano/memory.hpp>
  23. #include "ICalToMAPI.h"
  24. #include "vconverter.h"
  25. #include "vtimezone.h"
  26. #include "vevent.h"
  27. #include "vtodo.h"
  28. #include "vfreebusy.h"
  29. #include "nameids.h"
  30. #include "icalrecurrence.h"
  31. #include <mapix.h>
  32. #include <mapiutil.h>
  33. #include <kopano/mapiext.h>
  34. #include <libical/ical.h>
  35. #include <algorithm>
  36. #include <vector>
  37. #include <kopano/stringutil.h>
  38. #include <kopano/charset/convert.h>
  39. #include <mapi.h>
  40. #include "icalmem.hpp"
  41. using namespace KCHL;
  42. namespace KC {
  43. class ICalToMapiImpl _kc_final : public ICalToMapi {
  44. public:
  45. /*
  46. - lpPropObj to lookup named properties
  47. - Addressbook (Global AddressBook for looking up users)
  48. */
  49. ICalToMapiImpl(IMAPIProp *lpPropObj, LPADRBOOK lpAdrBook, bool bNoRecipients);
  50. virtual ~ICalToMapiImpl();
  51. HRESULT ParseICal(const std::string& strIcal, const std::string& strCharset, const std::string& strServerTZ, IMailUser *lpMailUser, ULONG ulFlags) _kc_override;
  52. ULONG GetItemCount(void) _kc_override;
  53. HRESULT GetItemInfo(ULONG ulPosition, eIcalType *lpType, time_t *lptLastModified, SBinary *lpUid) _kc_override;
  54. HRESULT GetItem(ULONG ulPosition, ULONG ulFlags, LPMESSAGE lpMessage) _kc_override;
  55. HRESULT GetFreeBusyInfo(time_t *lptstart, time_t *lptend, std::string *lpstrUId, std::list<std::string> **lplstUsers) _kc_override;
  56. private:
  57. void Clean();
  58. HRESULT SaveAttendeesString(const std::list<icalrecip> *lplstRecip, LPMESSAGE lpMessage);
  59. HRESULT SaveProps(const std::list<SPropValue> *lpPropList, IMAPIProp *, unsigned int flags = 0);
  60. HRESULT SaveRecipList(const std::list<icalrecip> *lplstRecip, ULONG ulFlag, LPMESSAGE lpMessage);
  61. SPropTagArray *m_lpNamedProps = nullptr;
  62. ULONG m_ulErrorCount = 0;
  63. TIMEZONE_STRUCT ttServerTZ;
  64. std::string strServerTimeZone;
  65. /* Contains a list of messages after ParseICal
  66. * Use GetItem() to get one of these messages
  67. */
  68. std::vector<icalitem*> m_vMessages;
  69. // freebusy information
  70. bool m_bHaveFreeBusy = false;
  71. time_t m_tFbStart = 0;
  72. time_t m_tFbEnd = 0;
  73. std::string m_strUID;
  74. std::list<std::string> m_lstUsers;
  75. };
  76. /**
  77. * Create a class implementing the ICalToMapi "interface".
  78. *
  79. * @param[in] lpPropObj MAPI object used to find named properties
  80. * @param[in] lpAdrBook MAPI Addressbook
  81. * @param[in] bNoRecipients Skip recipients from ical. Used for DAgent, which uses the mail recipients
  82. * @param[out] lppICalToMapi The ICalToMapi class
  83. */
  84. HRESULT CreateICalToMapi(IMAPIProp *lpPropObj, LPADRBOOK lpAdrBook, bool bNoRecipients, ICalToMapi **lppICalToMapi)
  85. {
  86. if (lpPropObj == nullptr || lppICalToMapi == nullptr)
  87. return MAPI_E_INVALID_PARAMETER;
  88. *lppICalToMapi = new(std::nothrow) ICalToMapiImpl(lpPropObj, lpAdrBook, bNoRecipients);
  89. if (*lppICalToMapi == nullptr)
  90. return MAPI_E_NOT_ENOUGH_MEMORY;
  91. return hrSuccess;
  92. }
  93. /**
  94. * Init ICalToMapi class
  95. *
  96. * @param[in] lpPropObj passed to super class
  97. * @param[in] lpAdrBook passed to super class
  98. * @param[in] bNoRecipients passed to super class
  99. */
  100. ICalToMapiImpl::ICalToMapiImpl(IMAPIProp *lpPropObj, LPADRBOOK lpAdrBook, bool bNoRecipients) : ICalToMapi(lpPropObj, lpAdrBook, bNoRecipients)
  101. {
  102. memset(&ttServerTZ, 0, sizeof(TIMEZONE_STRUCT));
  103. }
  104. /**
  105. * Frees all used memory of the ICalToMapi class
  106. */
  107. ICalToMapiImpl::~ICalToMapiImpl()
  108. {
  109. Clean();
  110. MAPIFreeBuffer(m_lpNamedProps);
  111. }
  112. /**
  113. * Frees and resets all used memory of the ICalToMapi class. The class
  114. * can be reused for another conversion after this call. Named
  115. * properties are kept, since you cannot switch items from a store.
  116. */
  117. void ICalToMapiImpl::Clean()
  118. {
  119. m_ulErrorCount = 0;
  120. for (const auto i : m_vMessages) {
  121. if (i->lpRecurrence != NULL)
  122. delete i->lpRecurrence;
  123. MAPIFreeBuffer(i->base);
  124. delete i;
  125. }
  126. m_vMessages.clear();
  127. m_bHaveFreeBusy = false;
  128. m_lstUsers.clear();
  129. m_tFbStart = 0;
  130. m_tFbEnd = 0;
  131. m_strUID.clear();
  132. }
  133. /**
  134. * Parses an ICal string (with a certain charset) and converts the
  135. * data in memory. The real MAPI object can be retrieved using
  136. * GetItem().
  137. *
  138. * @param[in] strIcal The ICal data to parse
  139. * @param[in] strCharset The charset of strIcal (usually UTF-8)
  140. * @param[in] strServerTZparam ID of default timezone to use if ICal data didn't specify
  141. * @param[in] lpMailUser IMailUser object of the current user (CalDav: the user logged in, DAgent: the user being delivered for)
  142. * @param[in] ulFlags Conversion flags - currently unused
  143. *
  144. * @return MAPI error code
  145. */
  146. HRESULT ICalToMapiImpl::ParseICal(const std::string& strIcal, const std::string& strCharset, const std::string& strServerTZparam, IMailUser *lpMailUser, ULONG ulFlags)
  147. {
  148. HRESULT hr = hrSuccess;
  149. icalcomp_ptr lpicCalendar;
  150. icalcomponent *lpicComponent = NULL;
  151. TIMEZONE_STRUCT ttTimeZone = {0};
  152. timezone_map tzMap;
  153. std::string strTZID;
  154. icalitem *item = NULL;
  155. icalitem *previtem = NULL;
  156. Clean();
  157. if (m_lpNamedProps == NULL) {
  158. hr = HrLookupNames(m_lpPropObj, &m_lpNamedProps);
  159. if (hr != hrSuccess)
  160. return hr;
  161. }
  162. icalerror_clear_errno();
  163. lpicCalendar.reset(icalparser_parse_string(strIcal.c_str()));
  164. if (lpicCalendar == NULL || icalerrno != ICAL_NO_ERROR) {
  165. switch (icalerrno) {
  166. case ICAL_BADARG_ERROR:
  167. case ICAL_USAGE_ERROR:
  168. return MAPI_E_INVALID_PARAMETER;
  169. case ICAL_NEWFAILED_ERROR:
  170. case ICAL_ALLOCATION_ERROR:
  171. return MAPI_E_NOT_ENOUGH_MEMORY;
  172. case ICAL_MALFORMEDDATA_ERROR:
  173. return MAPI_E_CORRUPT_DATA;
  174. case ICAL_FILE_ERROR:
  175. return MAPI_E_DISK_ERROR;
  176. case ICAL_UNIMPLEMENTED_ERROR:
  177. return E_NOTIMPL;
  178. case ICAL_UNKNOWN_ERROR:
  179. case ICAL_PARSE_ERROR:
  180. case ICAL_INTERNAL_ERROR:
  181. case ICAL_NO_ERROR:
  182. return MAPI_E_CALL_FAILED;
  183. }
  184. return hrSuccess;
  185. }
  186. if (icalcomponent_isa(lpicCalendar.get()) != ICAL_VCALENDAR_COMPONENT &&
  187. icalcomponent_isa(lpicCalendar.get()) != ICAL_XROOT_COMPONENT)
  188. return MAPI_E_INVALID_OBJECT;
  189. m_ulErrorCount = icalcomponent_count_errors(lpicCalendar.get());
  190. // * find all timezone's, place in map
  191. lpicComponent = icalcomponent_get_first_component(lpicCalendar.get(), ICAL_VTIMEZONE_COMPONENT);
  192. while (lpicComponent) {
  193. hr = HrParseVTimeZone(lpicComponent, &strTZID, &ttTimeZone);
  194. if (hr != hrSuccess)
  195. /* log warning? */ ;
  196. else
  197. tzMap[strTZID] = ttTimeZone;
  198. lpicComponent = icalcomponent_get_next_component(lpicCalendar.get(), ICAL_VTIMEZONE_COMPONENT);
  199. }
  200. // ICal file did not send any timezone information
  201. if (tzMap.empty() && strServerTimeZone.empty() &&
  202. // find timezone from given server timezone
  203. HrGetTzStruct(strServerTZparam, &ttServerTZ) == hrSuccess) {
  204. strServerTimeZone = strServerTZparam;
  205. tzMap[strServerTZparam] = ttServerTZ;
  206. }
  207. // find all "messages" vevent, vtodo, vjournal, ...?
  208. lpicComponent = icalcomponent_get_first_component(lpicCalendar.get(), ICAL_ANY_COMPONENT);
  209. while (lpicComponent) {
  210. std::unique_ptr<VConverter> lpVEC;
  211. auto type = icalcomponent_isa(lpicComponent);
  212. switch (type) {
  213. case ICAL_VEVENT_COMPONENT:
  214. static_assert(std::is_polymorphic<VEventConverter>::value, "VEventConverter needs to be polymorphic for unique_ptr to work");
  215. lpVEC.reset(new VEventConverter(m_lpAdrBook, &tzMap, m_lpNamedProps, strCharset, false, m_bNoRecipients, lpMailUser));
  216. hr = hrSuccess;
  217. break;
  218. case ICAL_VTODO_COMPONENT:
  219. static_assert(std::is_polymorphic<VTodoConverter>::value, "VTodoConverter needs to be polymorphic for unique_ptr to work");
  220. lpVEC.reset(new VTodoConverter(m_lpAdrBook, &tzMap, m_lpNamedProps, strCharset, false, m_bNoRecipients, lpMailUser));
  221. hr = hrSuccess;
  222. break;
  223. case ICAL_VFREEBUSY_COMPONENT:
  224. hr = hrSuccess;
  225. break;
  226. case ICAL_VJOURNAL_COMPONENT:
  227. default:
  228. hr = MAPI_E_NO_SUPPORT;
  229. break;
  230. };
  231. if (hr != hrSuccess)
  232. goto next;
  233. switch (type) {
  234. case ICAL_VFREEBUSY_COMPONENT:
  235. hr = HrGetFbInfo(lpicComponent, &m_tFbStart, &m_tFbEnd, &m_strUID, &m_lstUsers);
  236. if (hr == hrSuccess)
  237. m_bHaveFreeBusy = true;
  238. break;
  239. case ICAL_VEVENT_COMPONENT:
  240. case ICAL_VTODO_COMPONENT:
  241. hr = lpVEC->HrICal2MAPI(lpicCalendar.get(), lpicComponent, previtem, &item);
  242. break;
  243. default:
  244. break;
  245. };
  246. if (hr == hrSuccess && item != nullptr && previtem != item) {
  247. // previtem is equal to item when item was only updated (eg. vevent exception)
  248. m_vMessages.push_back(item);
  249. previtem = item;
  250. }
  251. next:
  252. lpicComponent = icalcomponent_get_next_component(lpicCalendar.get(), ICAL_ANY_COMPONENT);
  253. }
  254. hr = hrSuccess;
  255. // TODO: sort m_vMessages on sBinGuid in icalitem struct, so caldav server can use optimized algorithm for finding the same items in MAPI
  256. // seems this happens quite fast .. don't know what's wrong with exchange's ical
  257. // if (m_ulErrorCount != 0)
  258. // hr = MAPI_W_ERRORS_RETURNED;
  259. return hr;
  260. }
  261. /**
  262. * Returns the number of items parsed
  263. *
  264. * @return Max number +1 to pass in GetItem and GetItemInfo ulPosition parameter.
  265. */
  266. ULONG ICalToMapiImpl::GetItemCount()
  267. {
  268. return m_vMessages.size();
  269. }
  270. /**
  271. * Get some information about an ical item at a certain position.
  272. *
  273. * @param[in] ulPosition The position of the ical item (flat list)
  274. * @param[out] lpType Type of the object, VEVENT or VTODO. (VJOURNAL currently unsupported), NULL if not intrested
  275. * @param[out] lptLastModified Last modification timestamp of the object, NULL if not intrested
  276. * @param[out] lpUid UID of the object, NULL if not intrested
  277. *
  278. * @return MAPI error code
  279. * @retval MAPI_E_INVALID_PARAMETER ulPosition out of range, or all return values NULL
  280. */
  281. HRESULT ICalToMapiImpl::GetItemInfo(ULONG ulPosition, eIcalType *lpType, time_t *lptLastModified, SBinary *lpUid)
  282. {
  283. if (ulPosition >= m_vMessages.size())
  284. return MAPI_E_INVALID_PARAMETER;
  285. if (lpType == NULL && lptLastModified == NULL && lpUid == NULL)
  286. return MAPI_E_INVALID_PARAMETER;
  287. if (lpType)
  288. *lpType = m_vMessages[ulPosition]->eType;
  289. if (lptLastModified)
  290. *lptLastModified = m_vMessages[ulPosition]->tLastModified;
  291. if (lpUid)
  292. *lpUid = m_vMessages[ulPosition]->sBinGuid.Value.bin;
  293. return hrSuccess;
  294. }
  295. /**
  296. * Get information of the freebusy data in the parsed ical. All parameters are optional.
  297. *
  298. * @param[out] lptstart The start time of the freebusy data
  299. * @param[out] lptend The end time of the freebusy data
  300. * @param[out] lpstrUID The UID of the freebusy data
  301. * @param[out] lplstUsers A list of the email addresses of users in freebusy data, @note: internal data returned!
  302. *
  303. * @return MAPI error code
  304. */
  305. HRESULT ICalToMapiImpl::GetFreeBusyInfo(time_t *lptstart, time_t *lptend, std::string *lpstrUID, std::list<std::string> **lplstUsers)
  306. {
  307. if (!m_bHaveFreeBusy)
  308. return MAPI_E_NOT_FOUND;
  309. if (lptend)
  310. *lptend = m_tFbEnd;
  311. if (lptstart != NULL)
  312. *lptstart = m_tFbStart;
  313. if (lpstrUID)
  314. *lpstrUID = m_strUID;
  315. if (lplstUsers)
  316. *lplstUsers = &m_lstUsers;
  317. return hrSuccess;
  318. }
  319. /**
  320. * Sets mapi properties in Imessage object from the icalitem.
  321. *
  322. * @param[in] ulPosition specifies the message that is to be retrieved
  323. * @param[in] ulFlags conversion flags
  324. * @arg @c IC2M_NO_RECIPIENTS skip recipients in conversion from ICal to MAPI
  325. * @arg @c IC2M_APPEND_ONLY do not delete properties in lpMessage that are not present in ICal, but possebly are in lpMessage
  326. * @param[in,out] lpMessage IMessage in which properties has to be set
  327. *
  328. * @return MAPI error code
  329. * @retval MAPI_E_INVALID_PARAMETER invalid position set in ulPosition or NULL IMessage parameter
  330. */
  331. HRESULT ICalToMapiImpl::GetItem(ULONG ulPosition, ULONG ulFlags, LPMESSAGE lpMessage)
  332. {
  333. HRESULT hr = hrSuccess;
  334. ICalRecurrence cRec;
  335. icalitem *lpItem = NULL;
  336. std::vector<icalitem *>::const_iterator iItem;
  337. ULONG ulANr = 0;
  338. memory_ptr<SPropTagArray> lpsPTA;
  339. object_ptr<IMAPITable> lpAttachTable;
  340. rowset_ptr lpRows;
  341. SPropValue sStart = {0};
  342. SPropValue sMethod = {0};
  343. if (ulPosition >= m_vMessages.size() || lpMessage == nullptr)
  344. return MAPI_E_INVALID_PARAMETER;
  345. iItem = m_vMessages.begin() + ulPosition;
  346. lpItem = *iItem;
  347. if ((ulFlags & IC2M_APPEND_ONLY) == 0 && !lpItem->lstDelPropTags.empty()) {
  348. hr = MAPIAllocateBuffer(CbNewSPropTagArray(lpItem->lstDelPropTags.size()), &~lpsPTA);
  349. if (hr != hrSuccess)
  350. return hr;
  351. std::copy(lpItem->lstDelPropTags.begin(), lpItem->lstDelPropTags.end(), lpsPTA->aulPropTag);
  352. lpsPTA->cValues = lpItem->lstDelPropTags.size();
  353. hr = lpMessage->DeleteProps(lpsPTA, NULL);
  354. if (hr != hrSuccess)
  355. return hr;
  356. }
  357. hr = SaveProps(&lpItem->lstMsgProps, lpMessage, ulFlags);
  358. if (hr != hrSuccess)
  359. return hr;
  360. if (!(ulFlags & IC2M_NO_RECIPIENTS))
  361. hr = SaveRecipList(&(lpItem->lstRecips), ulFlags, lpMessage);
  362. if (hr != hrSuccess)
  363. return hr;
  364. hr = SaveAttendeesString(&(lpItem->lstRecips), lpMessage);
  365. if (hr != hrSuccess)
  366. return hr;
  367. // remove all exception attachments from message, if any
  368. hr = lpMessage->GetAttachmentTable(0, &~lpAttachTable);
  369. if (hr != hrSuccess)
  370. goto next;
  371. sStart.ulPropTag = PR_EXCEPTION_STARTTIME;
  372. // 1-1-4501 00:00
  373. sStart.Value.ft.dwLowDateTime = 0xA3DD4000;
  374. sStart.Value.ft.dwHighDateTime = 0x0CB34557;
  375. sMethod.ulPropTag = PR_ATTACH_METHOD;
  376. sMethod.Value.ul = ATTACH_EMBEDDED_MSG;
  377. // restrict to only exception attachments
  378. // (((!pr_exception_starttime) or (pr_exception_starttime != 1-1-4501)) and (have method) and (method == 5))
  379. // (AND( OR( NOT(pr_exception_start) (pr_exception_start!=time)) (have method)(method==5)))
  380. hr = ECAndRestriction(
  381. ECOrRestriction(
  382. ECNotRestriction(ECExistRestriction(sStart.ulPropTag)) +
  383. ECPropertyRestriction(RELOP_NE, sStart.ulPropTag, &sStart, ECRestriction::Cheap)
  384. ) +
  385. ECExistRestriction(sMethod.ulPropTag) +
  386. ECPropertyRestriction(RELOP_EQ, sMethod.ulPropTag, &sMethod, ECRestriction::Cheap)
  387. ).RestrictTable(lpAttachTable, 0);
  388. if (hr != hrSuccess)
  389. return hr;
  390. hr = lpAttachTable->QueryRows(-1, 0, &~lpRows);
  391. if (hr != hrSuccess)
  392. return hr;
  393. for (ULONG i = 0; i < lpRows->cRows; ++i) {
  394. auto lpPropVal = PCpropFindProp(lpRows->aRow[i].lpProps, lpRows->aRow[i].cValues, PR_ATTACH_NUM);
  395. if (lpPropVal == NULL)
  396. continue;
  397. hr = lpMessage->DeleteAttach(lpPropVal->Value.ul, 0, NULL, 0);
  398. if (hr != hrSuccess)
  399. return hr;
  400. }
  401. next:
  402. // add recurring properties & add exceptions
  403. if (lpItem->lpRecurrence) {
  404. hr = cRec.HrMakeMAPIRecurrence(lpItem->lpRecurrence, m_lpNamedProps, lpMessage);
  405. // TODO: log error if any?
  406. // check if all exceptions are valid
  407. for (const auto &ex : lpItem->lstExceptionAttachments)
  408. if (!cRec.HrValidateOccurrence(lpItem, ex))
  409. return MAPI_E_INVALID_OBJECT;
  410. for (const auto &ex : lpItem->lstExceptionAttachments) {
  411. object_ptr<IAttach> lpAttach;
  412. object_ptr<IMessage> lpExMsg;
  413. hr = lpMessage->CreateAttach(nullptr, 0, &ulANr, &~lpAttach);
  414. if (hr != hrSuccess)
  415. return hr;
  416. hr = lpAttach->OpenProperty(PR_ATTACH_DATA_OBJ, &IID_IMessage, 0, MAPI_CREATE | MAPI_MODIFY, &~lpExMsg);
  417. if (hr != hrSuccess)
  418. return hr;
  419. if (!(ulFlags & IC2M_NO_RECIPIENTS))
  420. hr = SaveRecipList(&ex.lstRecips, ulFlags, lpExMsg);
  421. if (hr != hrSuccess)
  422. return hr;
  423. hr = SaveAttendeesString(&ex.lstRecips, lpExMsg);
  424. if (hr != hrSuccess)
  425. return hr;
  426. hr = SaveProps(&ex.lstMsgProps, lpExMsg);
  427. if (hr != hrSuccess)
  428. return hr;
  429. hr = SaveProps(&ex.lstAttachProps, lpAttach);
  430. if (hr != hrSuccess)
  431. return hr;
  432. hr = lpExMsg->SaveChanges(0);
  433. if (hr != hrSuccess)
  434. return hr;
  435. hr = lpAttach->SaveChanges(0);
  436. if (hr != hrSuccess)
  437. return hr;
  438. }
  439. }
  440. return hr;
  441. }
  442. /**
  443. * Helper function for GetItem. Saves all properties converted from
  444. * ICal to MAPI in the MAPI object. Does not call SaveChanges.
  445. *
  446. * @param[in] lpPropList list of properties to save in lpMapiProp
  447. * @param[in] lpMapiProp The MAPI object to save properties in
  448. *
  449. * @return MAPI error code
  450. */
  451. HRESULT ICalToMapiImpl::SaveProps(const std::list<SPropValue> *lpPropList,
  452. LPMAPIPROP lpMapiProp, unsigned int flags)
  453. {
  454. HRESULT hr = hrSuccess;
  455. memory_ptr<SPropValue> lpsPropVals;
  456. int i;
  457. // all props to message
  458. hr = MAPIAllocateBuffer(lpPropList->size() * sizeof(SPropValue), &~lpsPropVals);
  459. if (hr != hrSuccess)
  460. return hr;
  461. // @todo: add exclude list or something? might set props the caller doesn't want (see vevent::HrAddTimes())
  462. i = 0;
  463. for (const auto &prop : *lpPropList) {
  464. if (flags & IC2M_NO_BODY &&
  465. PROP_ID(prop.ulPropTag) == PROP_ID(PR_BODY))
  466. continue;
  467. lpsPropVals[i++] = prop;
  468. }
  469. return lpMapiProp->SetProps(i, lpsPropVals, NULL);
  470. }
  471. /**
  472. * Helper function for GetItem. Converts recipient list to MAPI
  473. * recipients. Always replaces the complete recipient list in
  474. * lpMessage.
  475. *
  476. * @param[in] lplstRecip List of parsed ical recipients from a certain item
  477. * @param[in] lpMessage MAPI message to modify recipient table for
  478. *
  479. * @return MAPI error code
  480. */
  481. HRESULT ICalToMapiImpl::SaveRecipList(const std::list<icalrecip> *lplstRecip,
  482. ULONG ulFlag, LPMESSAGE lpMessage)
  483. {
  484. HRESULT hr = hrSuccess;
  485. adrlist_ptr lpRecipients;
  486. std::string strSearch;
  487. ULONG i = 0;
  488. convert_context converter;
  489. hr = MAPIAllocateBuffer(CbNewADRLIST(lplstRecip->size()), &~lpRecipients);
  490. if (hr != hrSuccess)
  491. return hr;
  492. lpRecipients->cEntries = 0;
  493. for (const auto &recip : *lplstRecip) {
  494. // iRecip->ulRecipientType
  495. // strEmail
  496. // strName
  497. // cbEntryID, lpEntryID
  498. if ((ulFlag & IC2M_NO_ORGANIZER) && recip.ulRecipientType == MAPI_ORIG)
  499. continue;
  500. if ((hr = MAPIAllocateBuffer(sizeof(SPropValue)*10, (void**)&lpRecipients->aEntries[i].rgPropVals)) != hrSuccess)
  501. return hr;
  502. lpRecipients->aEntries[i].cValues = 10;
  503. lpRecipients->aEntries[i].rgPropVals[0].ulPropTag = PR_RECIPIENT_TYPE;
  504. lpRecipients->aEntries[i].rgPropVals[0].Value.ul = recip.ulRecipientType;
  505. lpRecipients->aEntries[i].rgPropVals[1].ulPropTag = PR_DISPLAY_NAME_W;
  506. lpRecipients->aEntries[i].rgPropVals[1].Value.lpszW = const_cast<wchar_t *>(recip.strName.c_str());
  507. lpRecipients->aEntries[i].rgPropVals[2].ulPropTag = PR_SMTP_ADDRESS_W;
  508. lpRecipients->aEntries[i].rgPropVals[2].Value.lpszW = const_cast<wchar_t *>(recip.strEmail.c_str());
  509. lpRecipients->aEntries[i].rgPropVals[3].ulPropTag = PR_ENTRYID;
  510. lpRecipients->aEntries[i].rgPropVals[3].Value.bin.cb = recip.cbEntryID;
  511. hr = MAPIAllocateMore(recip.cbEntryID,
  512. lpRecipients->aEntries[i].rgPropVals,
  513. reinterpret_cast<void **>(&lpRecipients->aEntries[i].rgPropVals[3].Value.bin.lpb));
  514. if (hr != hrSuccess)
  515. return hr;
  516. memcpy(lpRecipients->aEntries[i].rgPropVals[3].Value.bin.lpb, recip.lpEntryID, recip.cbEntryID);
  517. lpRecipients->aEntries[i].rgPropVals[4].ulPropTag = PR_ADDRTYPE_W;
  518. lpRecipients->aEntries[i].rgPropVals[4].Value.lpszW = const_cast<wchar_t *>(L"SMTP");
  519. strSearch = strToUpper("SMTP:" + converter.convert_to<std::string>(recip.strEmail));
  520. lpRecipients->aEntries[i].rgPropVals[5].ulPropTag = PR_SEARCH_KEY;
  521. lpRecipients->aEntries[i].rgPropVals[5].Value.bin.cb = strSearch.size() + 1;
  522. if ((hr = MAPIAllocateMore(strSearch.size()+1, lpRecipients->aEntries[i].rgPropVals, (void **)&lpRecipients->aEntries[i].rgPropVals[5].Value.bin.lpb)) != hrSuccess)
  523. return hr;
  524. memcpy(lpRecipients->aEntries[i].rgPropVals[5].Value.bin.lpb, strSearch.c_str(), strSearch.size()+1);
  525. lpRecipients->aEntries[i].rgPropVals[6].ulPropTag = PR_EMAIL_ADDRESS_W;
  526. lpRecipients->aEntries[i].rgPropVals[6].Value.lpszW = const_cast<wchar_t *>(recip.strEmail.c_str());
  527. lpRecipients->aEntries[i].rgPropVals[7].ulPropTag = PR_DISPLAY_TYPE;
  528. lpRecipients->aEntries[i].rgPropVals[7].Value.ul = DT_MAILUSER;
  529. lpRecipients->aEntries[i].rgPropVals[8].ulPropTag = PR_RECIPIENT_FLAGS;
  530. lpRecipients->aEntries[i].rgPropVals[8].Value.ul = recip.ulRecipientType == MAPI_ORIG? 3 : 1;
  531. lpRecipients->aEntries[i].rgPropVals[9].ulPropTag = PR_RECIPIENT_TRACKSTATUS;
  532. lpRecipients->aEntries[i].rgPropVals[9].Value.ul = recip.ulTrackStatus;
  533. ++lpRecipients->cEntries;
  534. ++i;
  535. }
  536. // flag 0: remove old recipient table, and add the new list
  537. return lpMessage->ModifyRecipients(0, lpRecipients);
  538. }
  539. /**
  540. * Save attendees strings in the message
  541. *
  542. * @param[in] lplstRecip Pointer to a recipient list.
  543. * @param[in,out] lpMessage Pointer to the new MAPI message.
  544. *
  545. * @return Returns always S_OK except when there is an error.
  546. *
  547. * @note The properties dispidNonSendableCC, dispidNonSendableBCC are not set
  548. *
  549. */
  550. HRESULT ICalToMapiImpl::SaveAttendeesString(const std::list<icalrecip> *lplstRecip, LPMESSAGE lpMessage)
  551. {
  552. HRESULT hr = hrSuccess;
  553. std::wstring strAllAttendees;
  554. std::wstring strToAttendees;
  555. std::wstring strCCAttendees;
  556. memory_ptr<SPropValue> lpsPropValue;
  557. hr = MAPIAllocateBuffer(sizeof(SPropValue) * 3, &~lpsPropValue);
  558. if (hr != hrSuccess)
  559. return hr;
  560. // Create attendees string
  561. for (const auto &recip : *lplstRecip) {
  562. if (recip.ulRecipientType == MAPI_ORIG)
  563. continue;
  564. if (recip.ulRecipientType == MAPI_TO) {
  565. if (!strToAttendees.empty())
  566. strToAttendees += L"; ";
  567. strToAttendees += recip.strName;
  568. } else if (recip.ulRecipientType == MAPI_CC) {
  569. if (!strCCAttendees.empty())
  570. strCCAttendees += L"; ";
  571. strCCAttendees += recip.strName;
  572. }
  573. if (!strAllAttendees.empty())
  574. strAllAttendees += L"; ";
  575. strAllAttendees += recip.strName;
  576. }
  577. lpsPropValue[0].ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_TOATTENDEESSTRING], PT_UNICODE);
  578. lpsPropValue[0].Value.lpszW = (WCHAR*)strToAttendees.c_str();
  579. lpsPropValue[1].ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_CCATTENDEESSTRING], PT_UNICODE);
  580. lpsPropValue[1].Value.lpszW = (WCHAR*)strCCAttendees.c_str();
  581. lpsPropValue[2].ulPropTag = CHANGE_PROP_TYPE(m_lpNamedProps->aulPropTag[PROP_ALLATTENDEESSTRING], PT_UNICODE);
  582. lpsPropValue[2].Value.lpszW = (WCHAR*)strAllAttendees.c_str();
  583. return lpMessage->SetProps(3, lpsPropValue, nullptr);
  584. }
  585. } /* namespace */