CalDavUtil.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  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/platform.h>
  18. #include <memory>
  19. #include <utility>
  20. #include <kopano/ECRestriction.h>
  21. #include "CalDavUtil.h"
  22. #include <kopano/EMSAbTag.h>
  23. #include <kopano/charset/convert.h>
  24. #include <kopano/mapi_ptr.h>
  25. #include <kopano/memory.hpp>
  26. using namespace std;
  27. using namespace KCHL;
  28. /**
  29. * Open MAPI session
  30. *
  31. * @param[in] strUser User's login name
  32. * @param[in] strPass User's password
  33. * @param[in] strPath Kopano server's path
  34. * @param[out] lppSession IMAPISession object if login is successful
  35. * @return HRESULT
  36. * @retval MAPI_E_LOGON_FAILED Unable to login with the specified user-name and password
  37. */
  38. HRESULT HrAuthenticate(const std::string &appVersion,
  39. const std::string &appMisc, const std::wstring &wstrUser,
  40. const std::wstring &wstrPass, const std::string &strPath,
  41. IMAPISession **lppSession)
  42. {
  43. // @todo: if login with utf8 username is not possible, lookup user from addressbook? but how?
  44. return HrOpenECSession(lppSession, appVersion.c_str(), appMisc.c_str(),
  45. wstrUser.c_str(), wstrPass.c_str(), strPath.c_str(),
  46. 0, NULL, NULL, NULL);
  47. }
  48. /**
  49. * Add Property to the folder or message
  50. *
  51. * @param[in] lpMapiProp IMAPIProp object pointer to which property is to be added
  52. * @param[in] ulPropTag Property Tag of the property to be added
  53. * @param[in] bFldId Boolean to state if the property to be added is FolderID or not
  54. * @param[in,out] wstrProperty String value of the property, if empty then GUID is created and added
  55. *
  56. * @return HRESULT
  57. */
  58. // @todo rewrite usage of this function, and remove it
  59. HRESULT HrAddProperty(IMAPIProp *lpMapiProp, ULONG ulPropTag, bool bFldId, std::wstring *wstrProperty)
  60. {
  61. HRESULT hr = hrSuccess;
  62. SPropValue sPropVal;
  63. memory_ptr<SPropValue> lpMsgProp;
  64. GUID sGuid;
  65. if(wstrProperty->empty())
  66. {
  67. CoCreateGuid(&sGuid);
  68. wstrProperty->assign(convert_to<wstring>(bin2hex(sizeof(GUID), (LPBYTE)&sGuid)));
  69. }
  70. assert(PROP_TYPE(ulPropTag) == PT_UNICODE);
  71. sPropVal.ulPropTag = ulPropTag;
  72. sPropVal.Value.lpszW = (LPWSTR)wstrProperty->c_str();
  73. hr = HrGetOneProp(lpMapiProp, sPropVal.ulPropTag, &~lpMsgProp);
  74. if (hr == MAPI_E_NOT_FOUND) {
  75. hr = HrSetOneProp(lpMapiProp, &sPropVal);
  76. if (hr == E_ACCESSDENIED && bFldId)
  77. {
  78. hr = HrGetOneProp(lpMapiProp, PR_ENTRYID, &~lpMsgProp);
  79. if(hr != hrSuccess)
  80. return hr;
  81. wstrProperty->assign(convert_to<wstring>(bin2hex(lpMsgProp->Value.bin.cb, lpMsgProp->Value.bin.lpb)));
  82. }
  83. } else if (hr == hrSuccess)
  84. wstrProperty->assign(lpMsgProp->Value.lpszW);
  85. return hr;
  86. }
  87. /**
  88. * Add Property to the folder or message
  89. *
  90. * @param[in] lpMsgStore Pointer to store of the user
  91. * @param[in] sbEid EntryID of the folder to which property has to be added
  92. * @param[in] ulPropertyId Property Tag of the property to be added
  93. * @param[in] bIsFldID Boolean to state if the property to be added is FolderID or not
  94. * @param[in,out] lpwstrProperty String value of the property, if empty then GUID is created and added
  95. *
  96. * @return HRESULT
  97. */
  98. HRESULT HrAddProperty(IMsgStore *lpMsgStore, SBinary sbEid, ULONG ulPropertyId, bool bIsFldID, std::wstring *lpwstrProperty )
  99. {
  100. object_ptr<IMAPIFolder> lpUsrFld;
  101. ULONG ulObjType = 0;
  102. HRESULT hr = lpMsgStore->OpenEntry(sbEid.cb, reinterpret_cast<ENTRYID *>(sbEid.lpb),
  103. nullptr, MAPI_BEST_ACCESS, &ulObjType, &~lpUsrFld);
  104. if(hr != hrSuccess)
  105. return hr;
  106. hr = HrAddProperty(lpUsrFld, ulPropertyId, bIsFldID, lpwstrProperty);
  107. if(hr != hrSuccess)
  108. return hr;
  109. return lpUsrFld->SaveChanges(KEEP_OPEN_READWRITE);
  110. }
  111. /**
  112. * Finds the folder in the store by using the its Folder ID (if
  113. * prefixed with FOLDER_PREFIX), or the folder name.
  114. *
  115. * Folder-id is could be either named property (dispidFldID) or PR_ENTRYID
  116. * and I'm not sure why, but I'll leave this for now.
  117. *
  118. * @param[in] lpMsgStore Pointer to users store (used for inbox/outbox)
  119. * @param[in] lpRootFolder Pointer to users root container (starting point in hierarchy, IPMSubtree most likely)
  120. * @param[in] lpNamedProps Named property tag array
  121. * @param[in] wstrFldId Folder-id of the folder to be searched
  122. * @param[out] lppUsrFld Return pointer for the folder found
  123. *
  124. * @return mapi error codes
  125. * @retval MAPI_E_NOT_FOUND Folder refrenced by folder-id not found
  126. *
  127. * @todo add some check to remove the dirty >50 length check
  128. */
  129. HRESULT HrFindFolder(IMsgStore *lpMsgStore, IMAPIFolder *lpRootFolder,
  130. SPropTagArray *lpNamedProps, const std::wstring &wstrFldIdOrig,
  131. IMAPIFolder **lppUsrFld)
  132. {
  133. HRESULT hr = hrSuccess;
  134. std::string strBinEid;
  135. object_ptr<IMAPITable> lpHichyTable;
  136. SPropValue sPropFolderID;
  137. SPropValue sPropFolderName;
  138. ULONG ulPropTagFldId = 0;
  139. rowset_ptr lpRows;
  140. SBinary sbEid = {0,0};
  141. IMAPIFolder *lpUsrFld = NULL;
  142. ULONG ulObjType = 0;
  143. convert_context converter;
  144. ULONG cbEntryID = 0;
  145. LPENTRYID lpEntryID = NULL;
  146. LPSPropValue lpOutbox = NULL;
  147. static constexpr const SizedSPropTagArray(1, sPropTagArr) = {1, {PR_ENTRYID}};
  148. auto wstrFldId = wstrFldIdOrig;
  149. // wstrFldId can be:
  150. // FOLDER_PREFIX + hexed named folder id
  151. // FOLDER_PREFIX + hexed entry id
  152. // folder name
  153. if (wstrFldId.find(FOLDER_PREFIX) == 0)
  154. wstrFldId.erase(0, wcslen(FOLDER_PREFIX));
  155. // Hack Alert #47 -- get Inbox and Outbox as special folders
  156. if (wstrFldId.compare(L"Inbox") == 0) {
  157. hr = lpMsgStore->GetReceiveFolder(const_cast<TCHAR *>(_T("IPM")), fMapiUnicode, &cbEntryID, &lpEntryID, NULL);
  158. if (hr != hrSuccess) {
  159. ec_log_err("Cannot open Inbox Folder, no Receive Folder EntryID: 0x%08X", hr);
  160. goto exit;
  161. }
  162. } else if (wstrFldId.compare(L"Outbox") == 0) {
  163. hr = HrGetOneProp(lpMsgStore, PR_IPM_OUTBOX_ENTRYID, &lpOutbox);
  164. if (hr != hrSuccess) {
  165. ec_log_err("Cannot open Outbox Folder, no PR_IPM_OUTBOX_ENTRYID: 0x%08X", hr);
  166. goto exit;
  167. }
  168. cbEntryID = lpOutbox->Value.bin.cb;
  169. lpEntryID = (LPENTRYID)lpOutbox->Value.bin.lpb;
  170. }
  171. if (cbEntryID && lpEntryID) {
  172. hr = lpMsgStore->OpenEntry(cbEntryID, lpEntryID, &IID_IMAPIFolder, MAPI_BEST_ACCESS, &ulObjType, (LPUNKNOWN*)lppUsrFld);
  173. if (hr != hrSuccess)
  174. ec_log_err("Cannot open %ls Folder: 0x%08X", wstrFldId.c_str(), hr);
  175. // we're done either way
  176. goto exit;
  177. }
  178. hr = lpRootFolder->GetHierarchyTable(CONVENIENT_DEPTH, &~lpHichyTable);
  179. if(hr != hrSuccess)
  180. goto exit;
  181. //When ENTRY_ID is use For Read Only Calendars assuming the folder id string
  182. //not larger than lenght 50
  183. //FIXME: include some Entry-id identifier
  184. if(wstrFldId.size()> 50)
  185. {
  186. ulPropTagFldId = PR_ENTRYID;
  187. strBinEid = hex2bin(wstrFldId);
  188. sPropFolderID.Value.bin.cb = strBinEid.size();
  189. sPropFolderID.Value.bin.lpb = (LPBYTE)strBinEid.c_str();
  190. }
  191. else
  192. {
  193. // note: this is a custom kopano named property, defined in libicalmapi/names.*
  194. ulPropTagFldId = CHANGE_PROP_TYPE(lpNamedProps->aulPropTag[PROP_FLDID], PT_UNICODE);
  195. sPropFolderID.Value.lpszW = (LPWSTR)wstrFldId.c_str();
  196. }
  197. sPropFolderID.ulPropTag = ulPropTagFldId;
  198. sPropFolderName.ulPropTag = PR_DISPLAY_NAME_W;
  199. sPropFolderName.Value.lpszW = (WCHAR*)wstrFldId.c_str();
  200. // @note, this will find the first folder using this name (1 level, eg 'Calendar', no subfolders in caldav)
  201. // so if you have Calendar and subfolder/Calender, the latter will not be able to open using names, but must use IDs.
  202. hr = ECOrRestriction(
  203. ECPropertyRestriction(RELOP_EQ, ulPropTagFldId, &sPropFolderID, ECRestriction::Cheap) +
  204. ECContentRestriction(FL_IGNORECASE, PR_DISPLAY_NAME_W, &sPropFolderName, ECRestriction::Cheap)
  205. ).RestrictTable(lpHichyTable);
  206. if (hr != hrSuccess)
  207. goto exit;
  208. hr = lpHichyTable->SetColumns(sPropTagArr, 0);
  209. if(hr != hrSuccess)
  210. goto exit;
  211. hr = lpHichyTable->QueryRows(1, 0, &~lpRows);
  212. if (hr != hrSuccess)
  213. goto exit;
  214. if (lpRows->cRows != 1) {
  215. hr = MAPI_E_NOT_FOUND;
  216. goto exit;
  217. }
  218. sbEid = lpRows->aRow[0].lpProps[0].Value.bin;
  219. hr = lpMsgStore->OpenEntry(sbEid.cb, (LPENTRYID)sbEid.lpb,NULL, MAPI_BEST_ACCESS, &ulObjType, (LPUNKNOWN *) &lpUsrFld);
  220. if(hr != hrSuccess)
  221. goto exit;
  222. *lppUsrFld = lpUsrFld;
  223. exit:
  224. // Free either one. lpEntryID may point to lpOutbox memory.
  225. if (lpOutbox)
  226. MAPIFreeBuffer(lpOutbox);
  227. else if (lpEntryID)
  228. MAPIFreeBuffer(lpEntryID);
  229. return hr;
  230. }
  231. /**
  232. * Set supported-report-set properties, used by mac ical.app client
  233. *
  234. * @param[in,out] lpsProperty Pointer to WEBDAVPROPERTY structure to store the supported-report-set properties
  235. *
  236. * @return HRESULT Always set to hrSuccess
  237. */
  238. HRESULT HrBuildReportSet(WEBDAVPROPERTY *lpsProperty)
  239. {
  240. HRESULT hr = hrSuccess;
  241. int ulDepth = 0 ;
  242. WEBDAVITEM sDavItem;
  243. sDavItem.sDavValue.sPropName.strPropname = "supported-report";
  244. sDavItem.sDavValue.sPropName.strNS = WEBDAVNS;
  245. sDavItem.ulDepth = ulDepth ;
  246. lpsProperty->lstItems.push_back(sDavItem);
  247. sDavItem.sDavValue.sPropName.strPropname = "report";
  248. sDavItem.ulDepth = ulDepth + 1;
  249. lpsProperty->lstItems.push_back(sDavItem);
  250. sDavItem.sDavValue.sPropName.strPropname = "acl-principal-prop-set";
  251. sDavItem.ulDepth = ulDepth + 2 ;
  252. lpsProperty->lstItems.push_back(sDavItem);
  253. sDavItem.sDavValue.sPropName.strPropname = "supported-report";
  254. sDavItem.sDavValue.sPropName.strNS = WEBDAVNS;
  255. sDavItem.ulDepth = ulDepth ;
  256. lpsProperty->lstItems.push_back(sDavItem);
  257. sDavItem.sDavValue.sPropName.strPropname = "report";
  258. sDavItem.ulDepth = ulDepth + 1;
  259. lpsProperty->lstItems.push_back(sDavItem);
  260. sDavItem.sDavValue.sPropName.strPropname = "principal-match";
  261. sDavItem.ulDepth = ulDepth + 2 ;
  262. lpsProperty->lstItems.push_back(sDavItem);
  263. sDavItem.sDavValue.sPropName.strPropname = "supported-report";
  264. sDavItem.sDavValue.sPropName.strNS = WEBDAVNS;
  265. sDavItem.ulDepth = ulDepth ;
  266. lpsProperty->lstItems.push_back(sDavItem);
  267. sDavItem.sDavValue.sPropName.strPropname = "report";
  268. sDavItem.ulDepth = ulDepth + 1;
  269. lpsProperty->lstItems.push_back(sDavItem);
  270. sDavItem.sDavValue.sPropName.strPropname = "principal-property-search";
  271. sDavItem.ulDepth = ulDepth + 2 ;
  272. lpsProperty->lstItems.push_back(sDavItem);
  273. sDavItem.sDavValue.sPropName.strPropname = "supported-report";
  274. sDavItem.sDavValue.sPropName.strNS = WEBDAVNS;
  275. sDavItem.ulDepth = ulDepth ;
  276. lpsProperty->lstItems.push_back(sDavItem);
  277. sDavItem.sDavValue.sPropName.strPropname = "report";
  278. sDavItem.ulDepth = ulDepth + 1;
  279. lpsProperty->lstItems.push_back(sDavItem);
  280. sDavItem.sDavValue.sPropName.strPropname = "expand-property";
  281. sDavItem.ulDepth = ulDepth + 2 ;
  282. lpsProperty->lstItems.push_back(sDavItem);
  283. return hr;
  284. }
  285. /**
  286. * Set Acl properties, used by mac ical.app client
  287. *
  288. * @param[in,out] lpsProperty Pointer to WEBDAVPROPERTY structure to store the acl properties
  289. *
  290. * @return HRESULT Always set to hrSuccess
  291. */
  292. HRESULT HrBuildACL(WEBDAVPROPERTY *lpsProperty)
  293. {
  294. HRESULT hr = hrSuccess;
  295. int ulDepth = 0 ;
  296. WEBDAVITEM sDavItem;
  297. sDavItem.sDavValue.sPropName.strPropname = "privilege";
  298. sDavItem.sDavValue.sPropName.strNS = WEBDAVNS;
  299. sDavItem.ulDepth = ulDepth ;
  300. lpsProperty->lstItems.push_back(sDavItem);
  301. sDavItem.sDavValue.sPropName.strPropname = "all";
  302. sDavItem.ulDepth = ulDepth + 1;
  303. lpsProperty->lstItems.push_back(sDavItem);
  304. sDavItem.sDavValue.sPropName.strPropname = "privilege";
  305. sDavItem.ulDepth = ulDepth ;
  306. lpsProperty->lstItems.push_back(sDavItem);
  307. sDavItem.sDavValue.sPropName.strPropname = "read-current-user-privilege-set";
  308. sDavItem.ulDepth = ulDepth + 1;
  309. lpsProperty->lstItems.push_back(sDavItem);
  310. sDavItem.sDavValue.sPropName.strPropname = "privilege";
  311. sDavItem.ulDepth = ulDepth ;
  312. lpsProperty->lstItems.push_back(sDavItem);
  313. sDavItem.sDavValue.sPropName.strPropname = "read";
  314. sDavItem.ulDepth = ulDepth + 1;
  315. lpsProperty->lstItems.push_back(sDavItem);
  316. sDavItem.sDavValue.sPropName.strPropname = "privilege";
  317. sDavItem.ulDepth = ulDepth ;
  318. lpsProperty->lstItems.push_back(sDavItem);
  319. sDavItem.sDavValue.sPropName.strPropname = "write";
  320. sDavItem.ulDepth = ulDepth + 1;
  321. lpsProperty->lstItems.push_back(sDavItem);
  322. sDavItem.sDavValue.sPropName.strPropname = "privilege";
  323. sDavItem.ulDepth = ulDepth;
  324. lpsProperty->lstItems.push_back(sDavItem);
  325. sDavItem.sDavValue.sPropName.strPropname = "write-content";
  326. sDavItem.ulDepth = ulDepth + 1;
  327. lpsProperty->lstItems.push_back(sDavItem);
  328. sDavItem.sDavValue.sPropName.strPropname = "privilege";
  329. sDavItem.ulDepth = ulDepth;
  330. lpsProperty->lstItems.push_back(sDavItem);
  331. sDavItem.sDavValue.sPropName.strPropname = "write-properties";
  332. sDavItem.ulDepth = ulDepth + 1;
  333. lpsProperty->lstItems.push_back(sDavItem);
  334. sDavItem.sDavValue.sPropName.strPropname = "privilege";
  335. sDavItem.ulDepth = ulDepth;
  336. lpsProperty->lstItems.push_back(sDavItem);
  337. sDavItem.sDavValue.sPropName.strPropname = "unlock";
  338. sDavItem.ulDepth = ulDepth + 1;
  339. lpsProperty->lstItems.push_back(sDavItem);
  340. sDavItem.sDavValue.sPropName.strPropname = "privilege";
  341. sDavItem.ulDepth = ulDepth;
  342. lpsProperty->lstItems.push_back(sDavItem);
  343. sDavItem.sDavValue.sPropName.strPropname = "read-acl";
  344. sDavItem.ulDepth = ulDepth + 1;
  345. lpsProperty->lstItems.push_back(sDavItem);
  346. sDavItem.sDavValue.sPropName.strPropname = "privilege";
  347. sDavItem.ulDepth = ulDepth;
  348. lpsProperty->lstItems.push_back(sDavItem);
  349. sDavItem.sDavValue.sPropName.strPropname = "bind";
  350. sDavItem.ulDepth = ulDepth + 1;
  351. lpsProperty->lstItems.push_back(sDavItem);
  352. sDavItem.sDavValue.sPropName.strPropname = "privilege";
  353. sDavItem.ulDepth = ulDepth;
  354. lpsProperty->lstItems.push_back(sDavItem);
  355. sDavItem.sDavValue.sPropName.strPropname = "unbind";
  356. sDavItem.ulDepth = ulDepth + 1;
  357. lpsProperty->lstItems.push_back(sDavItem);
  358. return hr;
  359. }
  360. /**
  361. * Return the GUID value from the input string
  362. *
  363. * Input string is of format '/caldav/Calendar name/asbxjk3-3980434-xn49cn4930.ics',
  364. * function returns 'asbxjk3-3980434-xn49cn4930'
  365. *
  366. * @param[in] strInput Input string contaning guid
  367. * @return string string countaing guid
  368. */
  369. std::string StripGuid(const std::string &strInput)
  370. {
  371. size_t ulFound = -1;
  372. size_t ulFoundSlash = -1;
  373. std::string strRetVal;
  374. ulFoundSlash = strInput.rfind('/');
  375. if(ulFoundSlash == string::npos)
  376. ulFoundSlash = 0;
  377. else
  378. ++ulFoundSlash;
  379. ulFound = strInput.rfind(".ics");
  380. if(ulFound != wstring::npos)
  381. strRetVal.assign(strInput.begin() + ulFoundSlash, strInput.begin() + ulFound);
  382. return strRetVal;
  383. }
  384. /**
  385. * Get owner of given store
  386. *
  387. * @param[in] lpSession IMAPISession object pointer of the user
  388. * @param[in] lpDefStore Default store to get ownership info from
  389. * @param[out] lppImailUser IMailUser Object pointer of the owner
  390. *
  391. * @return HRESULT
  392. */
  393. HRESULT HrGetOwner(IMAPISession *lpSession, IMsgStore *lpDefStore, IMailUser **lppImailUser)
  394. {
  395. SPropValuePtr ptrSProp;
  396. object_ptr<IMailUser> lpMailUser;
  397. ULONG ulObjType = 0;
  398. HRESULT hr = HrGetOneProp(lpDefStore, PR_MAILBOX_OWNER_ENTRYID, &~ptrSProp);
  399. if(hr != hrSuccess)
  400. return hr;
  401. hr = lpSession->OpenEntry(ptrSProp->Value.bin.cb, reinterpret_cast<ENTRYID *>(ptrSProp->Value.bin.lpb), nullptr, MAPI_BEST_ACCESS, &ulObjType, &~lpMailUser);
  402. if(hr != hrSuccess)
  403. return hr;
  404. *lppImailUser = lpMailUser.release();
  405. return hrSuccess;
  406. }
  407. /**
  408. * Get all calendar folder of a specified folder, also includes all sub folders
  409. *
  410. * @param[in] lpSession IMAPISession object of the user
  411. * @param[in] lpFolder IMAPIFolder object for which all calendar are to returned(Optional, can be set to NULL if lpsbEid != NULL)
  412. * @param[in] lpsbEid EntryID of the Folder for which all calendar are to be returned(can be NULL if lpFolderIn != NULL)
  413. * @param[out] lppTable IMAPITable of the sub calendar of the folder
  414. *
  415. * @return HRESULT
  416. */
  417. HRESULT HrGetSubCalendars(IMAPISession *lpSession, IMAPIFolder *lpFolder,
  418. SBinary *lpsbEid, IMAPITable **lppTable)
  419. {
  420. HRESULT hr = hrSuccess;
  421. object_ptr<IMAPIFolder> local_fld;
  422. ULONG ulObjType = 0;
  423. IMAPITable *lpTable = NULL;
  424. SPropValue sPropVal;
  425. ECOrRestriction rst;
  426. if (lpFolder == nullptr) {
  427. hr = lpSession->OpenEntry(lpsbEid->cb, reinterpret_cast<ENTRYID *>(lpsbEid->lpb),
  428. nullptr, MAPI_BEST_ACCESS, &ulObjType, &~local_fld);
  429. if(hr != hrSuccess)
  430. return hr;
  431. lpFolder = local_fld.get();
  432. }
  433. hr = lpFolder->GetHierarchyTable(CONVENIENT_DEPTH,&lpTable);
  434. if(hr != hrSuccess)
  435. return hr;
  436. sPropVal.ulPropTag = PR_CONTAINER_CLASS_A;
  437. sPropVal.Value.lpszA = const_cast<char *>("IPF.Appointment");
  438. rst += ECContentRestriction(FL_IGNORECASE, sPropVal.ulPropTag, &sPropVal, ECRestriction::Shallow);
  439. sPropVal.Value.lpszA = const_cast<char *>("IPF.Task");
  440. rst += ECContentRestriction(FL_IGNORECASE, sPropVal.ulPropTag, &sPropVal, ECRestriction::Shallow);
  441. hr = rst.RestrictTable(lpTable);
  442. if (hr != hrSuccess)
  443. return hr;
  444. *lppTable = lpTable;
  445. return hrSuccess;
  446. }
  447. /**
  448. * Check for delegate permissions on the store
  449. * @param[in] lpDefStore The users default store
  450. * @param[in] lpSharedStore The store of the other user
  451. *
  452. * @return bool True if permissions are set or false
  453. */
  454. bool HasDelegatePerm(IMsgStore *lpDefStore, IMsgStore *lpSharedStore)
  455. {
  456. object_ptr<IMessage> lpFbMessage;
  457. memory_ptr<SPropValue> lpProp, lpMailBoxEid;
  458. object_ptr<IMAPIContainer> lpRootCont;
  459. ULONG ulType = 0;
  460. ULONG ulPos = 0;
  461. SBinary sbEid = {0,0};
  462. bool blFound = false;
  463. HRESULT hr = HrGetOneProp(lpDefStore, PR_MAILBOX_OWNER_ENTRYID, &~lpMailBoxEid);
  464. if (hr != hrSuccess)
  465. return false;
  466. hr = lpSharedStore->OpenEntry(0, nullptr, nullptr, 0, &ulType, &~lpRootCont);
  467. if (hr != hrSuccess)
  468. return false;
  469. hr = HrGetOneProp(lpRootCont, PR_FREEBUSY_ENTRYIDS, &~lpProp);
  470. if (hr != hrSuccess)
  471. return false;
  472. if (lpProp->Value.MVbin.cValues > 1 && lpProp->Value.MVbin.lpbin[1].cb != 0)
  473. sbEid = lpProp->Value.MVbin.lpbin[1];
  474. else
  475. return false;
  476. hr = lpSharedStore->OpenEntry(sbEid.cb, reinterpret_cast<ENTRYID *>(sbEid.lpb), nullptr, MAPI_BEST_ACCESS, &ulType, &~lpFbMessage);
  477. if (hr != hrSuccess)
  478. return false;
  479. hr = HrGetOneProp(lpFbMessage, PR_SCHDINFO_DELEGATE_ENTRYIDS, &~lpProp);
  480. if (hr != hrSuccess)
  481. return false;
  482. for (ULONG i = 0; i < lpProp->Value.MVbin.cValues; ++i) {
  483. if (lpProp->Value.MVbin.lpbin[i].cb == lpMailBoxEid->Value.bin.cb &&
  484. memcmp(lpProp->Value.MVbin.lpbin[i].lpb, lpMailBoxEid->Value.bin.lpb, lpMailBoxEid->Value.bin.cb) == 0)
  485. {
  486. blFound = true;
  487. ulPos = i;
  488. break;
  489. }
  490. }
  491. if (!blFound)
  492. return false;
  493. hr = HrGetOneProp(lpFbMessage, PR_DELEGATE_FLAGS, &~lpProp);
  494. if (hr != hrSuccess)
  495. return false;
  496. return lpProp->Value.MVl.cValues >= ulPos && lpProp->Value.MVl.lpl[ulPos];
  497. }
  498. /**
  499. * Check if the message is a private item
  500. *
  501. * @param[in] lpMessage message to be checked
  502. * @param[in] ulPropIDPrivate named property tag
  503. * @return bool
  504. */
  505. bool IsPrivate(LPMESSAGE lpMessage, ULONG ulPropIDPrivate)
  506. {
  507. memory_ptr<SPropValue> lpPropPrivate;
  508. return HrGetOneProp(lpMessage, ulPropIDPrivate, &~lpPropPrivate) == hrSuccess &&
  509. lpPropPrivate->Value.b == TRUE;
  510. }
  511. /**
  512. * Creates restriction to find calendar entries refrenced by strGuid.
  513. *
  514. * @param[in] strGuid Guid string of calendar entry requested by caldav client, in url-base64 mode
  515. * @param[in] lpNamedProps Named property tag array
  516. * @param[out] lpsRectrict Pointer to the restriction created
  517. *
  518. * @return HRESULT
  519. * @retval MAPI_E_INVALID_PARAMETER null parameter is passed in lpsRectrict
  520. */
  521. HRESULT HrMakeRestriction(const std::string &strGuid, LPSPropTagArray lpNamedProps, LPSRestriction *lpsRectrict)
  522. {
  523. HRESULT hr = hrSuccess;
  524. LPSRestriction lpsRoot = NULL;
  525. std::string strBinGuid;
  526. std::string strBinOtherUID;
  527. SPropValue sSpropVal = {0};
  528. ECOrRestriction rst;
  529. if (lpsRectrict == NULL) {
  530. hr = MAPI_E_INVALID_PARAMETER;
  531. goto exit;
  532. }
  533. // convert guid to outlook format
  534. if (IsOutlookUid(strGuid))
  535. strBinGuid = hex2bin(strGuid);
  536. else
  537. HrMakeBinUidFromICalUid(strGuid, &strBinGuid);
  538. sSpropVal.Value.bin.cb = (ULONG)strBinGuid.size();
  539. sSpropVal.Value.bin.lpb = (LPBYTE)strBinGuid.c_str();
  540. sSpropVal.ulPropTag = CHANGE_PROP_TYPE(lpNamedProps->aulPropTag[PROP_GOID], PT_BINARY);
  541. rst += ECPropertyRestriction(RELOP_EQ, sSpropVal.ulPropTag, &sSpropVal, ECRestriction::Shallow);
  542. // converting guid to hex
  543. strBinOtherUID = hex2bin(strGuid);
  544. sSpropVal.ulPropTag = PR_ENTRYID;
  545. sSpropVal.Value.bin.cb = (ULONG)strBinOtherUID.size();
  546. sSpropVal.Value.bin.lpb = (LPBYTE)strBinOtherUID.c_str();
  547. // When CreateAndGetGuid() fails PR_ENTRYID is used as guid.
  548. rst += ECPropertyRestriction(RELOP_EQ, PR_ENTRYID, &sSpropVal, ECRestriction::Shallow);
  549. // z-push iphone UIDs are not in Outlook format
  550. sSpropVal.ulPropTag = CHANGE_PROP_TYPE(lpNamedProps->aulPropTag[PROP_GOID], PT_BINARY);
  551. rst += ECPropertyRestriction(RELOP_EQ, sSpropVal.ulPropTag, &sSpropVal, ECRestriction::Shallow);
  552. // PUT url [guid].ics part, (eg. Evolution UIDs)
  553. sSpropVal.ulPropTag = CHANGE_PROP_TYPE(lpNamedProps->aulPropTag[PROP_APPTTSREF], PT_STRING8);
  554. sSpropVal.Value.lpszA = (char*)strGuid.c_str();
  555. rst += ECPropertyRestriction(RELOP_EQ, sSpropVal.ulPropTag, &sSpropVal, ECRestriction::Shallow);
  556. hr = rst.CreateMAPIRestriction(&lpsRoot, ECRestriction::Full);
  557. exit:
  558. if (lpsRoot && lpsRectrict)
  559. *lpsRectrict = std::move(lpsRoot);
  560. return hr;
  561. }
  562. /**
  563. * Finds mapi message in the folder using the UID string.
  564. * UID string can be PR_ENTRYID/GlobalOjectId of the message.
  565. *
  566. * @param[in] strGuid Guid value of the message to be searched
  567. * @param[in] lpUsrFld Mapi folder in which the message has to be searched
  568. * @param[in] lpNamedProps Named property tag array
  569. * @param[out] lppMessage if found the mapi message is returned
  570. * @return HRESULT
  571. * @retval MAPI_E_NOT_FOUND No message found containing the guid value.
  572. */
  573. HRESULT HrFindAndGetMessage(std::string strGuid, IMAPIFolder *lpUsrFld, LPSPropTagArray lpNamedProps, IMessage **lppMessage)
  574. {
  575. SBinary sbEid = {0,0};
  576. memory_ptr<SRestriction> lpsRoot;
  577. rowset_ptr lpValRows;
  578. object_ptr<IMAPITable> lpTable;
  579. object_ptr<IMessage> lpMessage;
  580. ULONG ulObjType = 0;
  581. static constexpr const SizedSPropTagArray(1, sPropTagArr) = {1, {PR_ENTRYID}};
  582. HRESULT hr = HrMakeRestriction(strGuid, lpNamedProps, &~lpsRoot);
  583. if (hr != hrSuccess)
  584. return hr;
  585. hr = lpUsrFld->GetContentsTable(0, &~lpTable);
  586. if(hr != hrSuccess)
  587. return hr;
  588. hr = lpTable->SetColumns(sPropTagArr, 0);
  589. if(hr != hrSuccess)
  590. return hr;
  591. hr = lpTable->Restrict(lpsRoot, TBL_BATCH);
  592. if(hr != hrSuccess)
  593. return hr;
  594. hr = lpTable->QueryRows(1, 0, &~lpValRows);
  595. if (hr != hrSuccess)
  596. return hr;
  597. if (lpValRows->cRows != 1)
  598. return MAPI_E_NOT_FOUND;
  599. if (PROP_TYPE(lpValRows->aRow[0].lpProps[0].ulPropTag) != PT_BINARY)
  600. return MAPI_E_NOT_FOUND;
  601. sbEid = lpValRows->aRow[0].lpProps[0].Value.bin;
  602. hr = lpUsrFld->OpenEntry(sbEid.cb, reinterpret_cast<ENTRYID *>(sbEid.lpb), nullptr, MAPI_MODIFY, &ulObjType, &~lpMessage);
  603. if (hr != hrSuccess)
  604. return hr;
  605. *lppMessage = lpMessage.release();
  606. return hrSuccess;
  607. }
  608. /**
  609. * Retrieves freebusy information of attendees and converts it to ical data
  610. *
  611. * @param[in] lpMapiToIcal Mapi to ical conversion object
  612. * @param[in] lpFBSupport IFreebusySupport object used to retrive freebusy information of attendee
  613. * @param[in] lpAddrBook Addressbook used for user lookups
  614. * @param[in] lplstUsers List of attendees whose freebusy is requested
  615. * @param[out] lpFbInfo Structure which stores the retrieved ical data for the attendees
  616. *
  617. * @return MAPI error code
  618. * @retval hrSuccess valid users have freebusy blocks, invalid users have empty string in ical data. (see xml converter for request-status code)
  619. */
  620. HRESULT HrGetFreebusy(MapiToICal *lpMapiToIcal, IFreeBusySupport* lpFBSupport, IAddrBook *lpAddrBook, std::list<std::string> *lplstUsers, WEBDAVFBINFO *lpFbInfo)
  621. {
  622. memory_ptr<FBUser> lpUsers;
  623. IFreeBusyData **lppFBData = NULL;
  624. memory_ptr<FBBlock_1> lpsFBblks;
  625. std::string strMethod;
  626. std::string strIcal;
  627. ULONG cUsers = 0;
  628. ULONG cFBData = 0;
  629. FILETIME ftStart = {0,0};
  630. FILETIME ftEnd = {0,0};
  631. LONG lMaxblks = 100;
  632. LONG lblkFetched = 0;
  633. WEBDAVFBUSERINFO sWebFbUserInfo;
  634. std::list<std::string>::const_iterator itUsers;
  635. adrlist_ptr lpAdrList;
  636. FlagListPtr ptrFlagList;
  637. EntryIdPtr ptrEntryId;
  638. ULONG cbEntryId = 0;
  639. ULONG ulObj = 0;
  640. ABContainerPtr ptrABDir;
  641. HRESULT hr = lpAddrBook->GetDefaultDir(&cbEntryId, &~ptrEntryId);
  642. if (hr != hrSuccess)
  643. goto exit;
  644. hr = lpAddrBook->OpenEntry(cbEntryId, ptrEntryId, nullptr, 0, &ulObj, &~ptrABDir);
  645. if (hr != hrSuccess)
  646. goto exit;
  647. cUsers = lplstUsers->size();
  648. hr = MAPIAllocateBuffer(CbNewADRLIST(cUsers), &~lpAdrList);
  649. if(hr != hrSuccess)
  650. goto exit;
  651. lpAdrList->cEntries = cUsers;
  652. hr = MAPIAllocateBuffer(CbNewFlagList(cUsers), &~ptrFlagList);
  653. if (hr != hrSuccess)
  654. goto exit;
  655. ptrFlagList->cFlags = cUsers;
  656. cUsers = 0;
  657. for (const auto &user : *lplstUsers) {
  658. lpAdrList->aEntries[cUsers].cValues = 1;
  659. hr = MAPIAllocateBuffer(sizeof(SPropValue), (void **)&lpAdrList->aEntries[cUsers].rgPropVals);
  660. if(hr != hrSuccess)
  661. goto exit;
  662. lpAdrList->aEntries[cUsers].rgPropVals[0].ulPropTag = PR_DISPLAY_NAME_A;
  663. lpAdrList->aEntries[cUsers].rgPropVals[0].Value.lpszA = const_cast<char *>(user.c_str());
  664. ptrFlagList->ulFlag[cUsers] = MAPI_UNRESOLVED;
  665. ++cUsers;
  666. }
  667. // NULL or sptaAddrListProps containing just PR_ENTRYID?
  668. hr = ptrABDir->ResolveNames(NULL, EMS_AB_ADDRESS_LOOKUP, lpAdrList, ptrFlagList);
  669. if (hr != hrSuccess)
  670. goto exit;
  671. hr = MAPIAllocateBuffer(sizeof(FBUser)*cUsers, &~lpUsers);
  672. if (hr != hrSuccess)
  673. goto exit;
  674. // Get the user entryids
  675. for (cUsers = 0; cUsers < lpAdrList->cEntries; ++cUsers) {
  676. const SPropValue *lpEntryID = nullptr;
  677. if (ptrFlagList->ulFlag[cUsers] == MAPI_RESOLVED)
  678. lpEntryID = PpropFindProp(lpAdrList->aEntries[cUsers].rgPropVals, lpAdrList->aEntries[cUsers].cValues, PR_ENTRYID);
  679. if (lpEntryID == nullptr) {
  680. lpUsers[cUsers].m_cbEid = 0;
  681. lpUsers[cUsers].m_lpEid = NULL;
  682. continue;
  683. }
  684. lpUsers[cUsers].m_cbEid = lpEntryID->Value.bin.cb;
  685. hr = MAPIAllocateMore(lpEntryID->Value.bin.cb, lpUsers, (void**)&lpUsers[cUsers].m_lpEid);
  686. if (hr != hrSuccess)
  687. goto exit;
  688. memcpy(lpUsers[cUsers].m_lpEid, lpEntryID->Value.bin.lpb, lpEntryID->Value.bin.cb);
  689. }
  690. hr = MAPIAllocateBuffer(sizeof(IFreeBusyData*)*cUsers, (void **)&lppFBData);
  691. if (hr != hrSuccess)
  692. goto exit;
  693. // retrieve freebusy for the attendees
  694. hr = lpFBSupport->LoadFreeBusyData(cUsers, lpUsers, lppFBData, NULL, &cFBData);
  695. if (hr != hrSuccess)
  696. goto exit;
  697. UnixTimeToFileTime(lpFbInfo->tStart, &ftStart);
  698. UnixTimeToFileTime(lpFbInfo->tEnd, &ftEnd);
  699. itUsers = lplstUsers->cbegin();
  700. // iterate through all users
  701. for (ULONG i = 0; i < cUsers; ++i) {
  702. object_ptr<IEnumFBBlock> lpEnumBlock;
  703. strIcal.clear();
  704. if (!lppFBData[i])
  705. goto next;
  706. hr = lppFBData[i]->EnumBlocks(&~lpEnumBlock, ftStart, ftEnd);
  707. if (hr != hrSuccess)
  708. goto next;
  709. hr = MAPIAllocateBuffer(sizeof(FBBlock_1)*lMaxblks, &~lpsFBblks);
  710. if (hr != hrSuccess)
  711. goto next;
  712. // restrict the freebusy blocks
  713. hr = lpEnumBlock->Restrict(ftStart, ftEnd);
  714. if (hr != hrSuccess)
  715. goto next;
  716. // ignore error
  717. lpEnumBlock->Next(lMaxblks, lpsFBblks, &lblkFetched);
  718. // add freebusy blocks to ical data
  719. if (lblkFetched == 0)
  720. hr = lpMapiToIcal->AddBlocks(NULL, lblkFetched, lpFbInfo->tStart, lpFbInfo->tEnd, lpFbInfo->strOrganiser, *itUsers, lpFbInfo->strUID);
  721. else
  722. hr = lpMapiToIcal->AddBlocks(lpsFBblks, lblkFetched, lpFbInfo->tStart, lpFbInfo->tEnd, lpFbInfo->strOrganiser, *itUsers, lpFbInfo->strUID);
  723. if (hr != hrSuccess)
  724. goto next;
  725. // retrieve VFREEBUSY ical data
  726. lpMapiToIcal->Finalize(M2IC_NO_VTIMEZONE, &strMethod, &strIcal);
  727. next:
  728. sWebFbUserInfo.strUser = *itUsers;
  729. sWebFbUserInfo.strIcal = strIcal;
  730. lpFbInfo->lstFbUserInfo.push_back(sWebFbUserInfo);
  731. ++itUsers;
  732. lpMapiToIcal->ResetObject();
  733. lblkFetched = 0;
  734. }
  735. // ignoring ical data for unknown users.
  736. hr = hrSuccess;
  737. exit:
  738. if (lppFBData) {
  739. for (ULONG i = 0; i < cUsers; ++i)
  740. if (lppFBData[i])
  741. lppFBData[i]->Release();
  742. MAPIFreeBuffer(lppFBData);
  743. }
  744. return hr;
  745. }