ZCABContainer.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116
  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 <new>
  19. #include "ZCABContainer.h"
  20. #include "ZCMAPIProp.h"
  21. #include <kopano/Trace.h>
  22. #include <mapiutil.h>
  23. #include <kopano/ECMemTable.h>
  24. #include <kopano/ECGuid.h>
  25. #include <kopano/ECInterfaceDefs.h>
  26. #include <kopano/ECDebug.h>
  27. #include <kopano/CommonUtil.h>
  28. #include <kopano/mapiext.h>
  29. #include <kopano/mapiguidext.h>
  30. #include <kopano/memory.hpp>
  31. #include <kopano/namedprops.h>
  32. #include <kopano/charset/convert.h>
  33. #include <kopano/mapi_ptr.h>
  34. #include <kopano/ECGetText.h>
  35. #include <kopano/EMSAbTag.h>
  36. #include <kopano/ECRestriction.h>
  37. #include <iostream>
  38. #include <kopano/Util.h>
  39. #include <kopano/stringutil.h>
  40. using namespace std;
  41. using namespace KCHL;
  42. ZCABContainer::ZCABContainer(std::vector<zcabFolderEntry> *lpFolders,
  43. IMAPIFolder *lpContacts, LPMAPISUP lpMAPISup, void *lpProvider,
  44. const char *szClassName) :
  45. ECUnknown(szClassName), m_lpFolders(lpFolders),
  46. m_lpContactFolder(lpContacts), m_lpMAPISup(lpMAPISup),
  47. m_lpProvider(lpProvider)
  48. {
  49. assert(!(lpFolders != NULL && lpContacts != NULL));
  50. if (m_lpMAPISup)
  51. m_lpMAPISup->AddRef();
  52. if (m_lpContactFolder)
  53. m_lpContactFolder->AddRef();
  54. }
  55. ZCABContainer::~ZCABContainer()
  56. {
  57. if (m_lpMAPISup)
  58. m_lpMAPISup->Release();
  59. if (m_lpContactFolder)
  60. m_lpContactFolder->Release();
  61. if (m_lpDistList)
  62. m_lpDistList->Release();
  63. }
  64. HRESULT ZCABContainer::QueryInterface(REFIID refiid, void **lppInterface)
  65. {
  66. if (m_lpDistList == NULL)
  67. REGISTER_INTERFACE2(ZCABContainer, this);
  68. else
  69. REGISTER_INTERFACE(IID_ZCDistList, this);
  70. REGISTER_INTERFACE2(ECUnknown, this);
  71. if (m_lpDistList == NULL)
  72. REGISTER_INTERFACE2(IABContainer, &this->m_xABContainer);
  73. else
  74. REGISTER_INTERFACE(IID_IDistList, &this->m_xABContainer);
  75. REGISTER_INTERFACE2(IMAPIProp, &this->m_xABContainer);
  76. REGISTER_INTERFACE2(IUnknown, &this->m_xABContainer);
  77. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  78. }
  79. /**
  80. * Create a ZCABContainer as either the top level (lpFolders is set) or
  81. * as a subfolder (lpContacts is set).
  82. *
  83. * @param[in] lpFolders Only the top container has the list to the wanted Kopano Contacts Folders, NULL otherwise.
  84. * @param[in] lpContacts Create this object as wrapper for the lpContacts folder, NULL if
  85. * @param[in] lpMAPISup
  86. * @param[in] lpProvider
  87. * @param[out] lppABContainer The newly created ZCABContainer class
  88. *
  89. * @return
  90. */
  91. HRESULT ZCABContainer::Create(std::vector<zcabFolderEntry> *lpFolders, IMAPIFolder *lpContacts, LPMAPISUP lpMAPISup, void* lpProvider, ZCABContainer **lppABContainer)
  92. {
  93. auto lpABContainer = new(std::nothrow) ZCABContainer(lpFolders, lpContacts, lpMAPISup, lpProvider, "IABContainer");
  94. if (lpABContainer == nullptr)
  95. return MAPI_E_NOT_ENOUGH_MEMORY;
  96. auto ret = lpABContainer->QueryInterface(IID_ZCABContainer,
  97. reinterpret_cast<void **>(lppABContainer));
  98. if (ret != hrSuccess)
  99. delete lpABContainer;
  100. return ret;
  101. }
  102. HRESULT ZCABContainer::Create(IMessage *lpContact, ULONG cbEntryID, LPENTRYID lpEntryID, LPMAPISUP lpMAPISup, ZCABContainer **lppABContainer)
  103. {
  104. HRESULT hr = hrSuccess;
  105. object_ptr<ZCMAPIProp> lpDistList;
  106. auto lpABContainer = new(std::nothrow) ZCABContainer(NULL, NULL, lpMAPISup, NULL, "IABContainer");
  107. if (lpABContainer == nullptr) {
  108. hr = MAPI_E_NOT_ENOUGH_MEMORY;
  109. goto exit;
  110. }
  111. hr = ZCMAPIProp::Create(lpContact, cbEntryID, lpEntryID, &~lpDistList);
  112. if (hr != hrSuccess)
  113. goto exit;
  114. hr = lpDistList->QueryInterface(IID_IMAPIProp, (void **)&lpABContainer->m_lpDistList);
  115. if (hr != hrSuccess)
  116. goto exit;
  117. hr = lpABContainer->QueryInterface(IID_ZCDistList, (void **)lppABContainer);
  118. exit:
  119. if (hr != hrSuccess)
  120. delete lpABContainer;
  121. return hr;
  122. }
  123. // IMAPIContainer
  124. HRESULT ZCABContainer::MakeWrappedEntryID(ULONG cbEntryID, LPENTRYID lpEntryID, ULONG ulObjType, ULONG ulOffset, ULONG *lpcbEntryID, LPENTRYID *lppEntryID)
  125. {
  126. cabEntryID *lpWrapped = NULL;
  127. ULONG cbWrapped = CbNewCABENTRYID(cbEntryID);
  128. HRESULT hr = MAPIAllocateBuffer(cbWrapped,
  129. reinterpret_cast<void **>(&lpWrapped));
  130. if (hr != hrSuccess)
  131. return hr;
  132. memset(lpWrapped, 0, cbWrapped);
  133. memcpy(&lpWrapped->muid, &MUIDZCSAB, sizeof(MAPIUID));
  134. lpWrapped->ulObjType = ulObjType;
  135. lpWrapped->ulOffset = ulOffset;
  136. memcpy(lpWrapped->origEntryID, lpEntryID, cbEntryID);
  137. *lpcbEntryID = cbWrapped;
  138. *lppEntryID = (LPENTRYID)lpWrapped;
  139. return hrSuccess;
  140. }
  141. HRESULT ZCABContainer::GetFolderContentsTable(ULONG ulFlags, LPMAPITABLE *lppTable)
  142. {
  143. HRESULT hr = hrSuccess;
  144. MAPITablePtr ptrContents;
  145. SRowSetPtr ptrRows;
  146. object_ptr<ECMemTable> lpTable;
  147. object_ptr<ECMemTableView> lpTableView;
  148. ULONG i, j = 0;
  149. ECAndRestriction resAnd;
  150. SPropValue sRestrictProp;
  151. #define I_NCOLS 11
  152. // data from the contact
  153. static constexpr const SizedSPropTagArray(I_NCOLS, inputCols) =
  154. {I_NCOLS, {PR_DISPLAY_NAME, PR_ADDRTYPE, PR_EMAIL_ADDRESS,
  155. PR_NORMALIZED_SUBJECT, PR_ENTRYID, PR_MESSAGE_CLASS,
  156. PR_ORIGINAL_DISPLAY_NAME, PR_PARENT_ENTRYID, PR_SOURCE_KEY,
  157. PR_PARENT_SOURCE_KEY, PR_CHANGE_KEY}};
  158. // I_MV_INDEX is dispidABPEmailList from mnNamedProps
  159. enum {I_DISPLAY_NAME = 0, I_ADDRTYPE, I_EMAIL_ADDRESS, I_NORMALIZED_SUBJECT, I_ENTRYID, I_MESSAGE_CLASS, I_ORIGINAL_DISPLAY_NAME,
  160. I_PARENT_ENTRYID, I_SOURCE_KEY, I_PARENT_SOURCE_KEY, I_CHANGE_KEY, I_MV_INDEX, I_NAMEDSTART};
  161. SPropTagArrayPtr ptrInputCols;
  162. #define O_NCOLS 21
  163. // data for the table
  164. static constexpr const SizedSPropTagArray(O_NCOLS, outputCols) =
  165. {O_NCOLS, {PR_DISPLAY_NAME, PR_ADDRTYPE, PR_EMAIL_ADDRESS,
  166. PR_NORMALIZED_SUBJECT, PR_ENTRYID, PR_DISPLAY_TYPE,
  167. PR_OBJECT_TYPE, PR_ORIGINAL_DISPLAY_NAME,
  168. PR_ZC_ORIGINAL_ENTRYID, PR_ZC_ORIGINAL_PARENT_ENTRYID,
  169. PR_ZC_ORIGINAL_SOURCE_KEY, PR_ZC_ORIGINAL_PARENT_SOURCE_KEY,
  170. PR_ZC_ORIGINAL_CHANGE_KEY, PR_SEARCH_KEY, PR_INSTANCE_KEY,
  171. PR_ROWID}};
  172. enum {O_DISPLAY_NAME = 0, O_ADDRTYPE, O_EMAIL_ADDRESS, O_NORMALIZED_SUBJECT, O_ENTRYID, O_DISPLAY_TYPE, O_OBJECT_TYPE, O_ORIGINAL_DISPLAY_NAME,
  173. O_ZC_ORIGINAL_ENTRYID, O_ZC_ORIGINAL_PARENT_ENTRYID, O_ZC_ORIGINAL_SOURCE_KEY, O_ZC_ORIGINAL_PARENT_SOURCE_KEY, O_ZC_ORIGINAL_CHANGE_KEY,
  174. O_SEARCH_KEY, O_INSTANCE_KEY, O_ROWID};
  175. SPropTagArrayPtr ptrOutputCols;
  176. SPropTagArrayPtr ptrContactCols;
  177. // named properties
  178. SPropTagArrayPtr ptrNameTags;
  179. KCHL::memory_ptr<MAPINAMEID *> lppNames;
  180. ULONG ulNames = (6 * 5) + 2;
  181. ULONG ulType = (ulFlags & MAPI_UNICODE) ? PT_UNICODE : PT_STRING8;
  182. MAPINAMEID mnNamedProps[(6 * 5) + 2] = {
  183. // index with MVI_FLAG
  184. {(LPGUID)&PSETID_Address, MNID_ID, {dispidABPEmailList}},
  185. // MVI offset 0: email1 set
  186. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail1DisplayName}},
  187. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail1AddressType}},
  188. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail1Address}},
  189. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail1OriginalDisplayName}},
  190. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail1OriginalEntryID}},
  191. // MVI offset 1: email2 set
  192. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail2DisplayName}},
  193. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail2AddressType}},
  194. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail2Address}},
  195. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail2OriginalDisplayName}},
  196. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail2OriginalEntryID}},
  197. // MVI offset 2: email3 set
  198. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail3DisplayName}},
  199. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail3AddressType}},
  200. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail3Address}},
  201. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail3OriginalDisplayName}},
  202. {(LPGUID)&PSETID_Address, MNID_ID, {dispidEmail3OriginalEntryID}},
  203. // MVI offset 3: business fax (fax2) set
  204. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax2DisplayName}},
  205. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax2AddressType}},
  206. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax2Address}},
  207. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax2OriginalDisplayName}},
  208. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax2OriginalEntryID}},
  209. // MVI offset 4: home fax (fax3) set
  210. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax3DisplayName}},
  211. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax3AddressType}},
  212. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax3Address}},
  213. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax3OriginalDisplayName}},
  214. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax3OriginalEntryID}},
  215. // MVI offset 5: primary fax (fax1) set
  216. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax1DisplayName}},
  217. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax1AddressType}},
  218. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax1Address}},
  219. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax1OriginalDisplayName}},
  220. {(LPGUID)&PSETID_Address, MNID_ID, {dispidFax1OriginalEntryID}},
  221. // restriction
  222. {(LPGUID)&PSETID_Address, MNID_ID, {dispidABPArrayType}},
  223. };
  224. ulFlags = ulFlags & MAPI_UNICODE;
  225. hr = Util::HrCopyUnicodePropTagArray(ulFlags, inputCols, &~ptrInputCols);
  226. if (hr != hrSuccess)
  227. return hr;
  228. hr = Util::HrCopyUnicodePropTagArray(ulFlags, outputCols, &~ptrOutputCols);
  229. if (hr != hrSuccess)
  230. return hr;
  231. hr = ECMemTable::Create(ptrOutputCols, PR_ROWID, &~lpTable);
  232. if(hr != hrSuccess)
  233. return hr;
  234. // root container has no contents, on hierarchy entries
  235. if (m_lpContactFolder == NULL)
  236. goto done;
  237. hr = m_lpContactFolder->GetContentsTable(ulFlags | MAPI_DEFERRED_ERRORS, &~ptrContents);
  238. if (hr != hrSuccess)
  239. return hr;
  240. hr = MAPIAllocateBuffer(sizeof(LPMAPINAMEID) * ulNames, &~lppNames);
  241. if (hr != hrSuccess)
  242. return hr;
  243. for (i = 0; i < ulNames; ++i)
  244. lppNames[i] = &mnNamedProps[i];
  245. hr = m_lpContactFolder->GetIDsFromNames(ulNames, lppNames, MAPI_CREATE, &~ptrNameTags);
  246. if (FAILED(hr))
  247. return hr;
  248. // fix types
  249. ptrNameTags->aulPropTag[0] = CHANGE_PROP_TYPE(ptrNameTags->aulPropTag[0], PT_MV_LONG | MV_INSTANCE);
  250. for (i = 0; i < (ulNames - 2) / 5; ++i) {
  251. ptrNameTags->aulPropTag[1+ (i*5) + 0] = CHANGE_PROP_TYPE(ptrNameTags->aulPropTag[1+ (i*5) + 0], ulType);
  252. ptrNameTags->aulPropTag[1+ (i*5) + 1] = CHANGE_PROP_TYPE(ptrNameTags->aulPropTag[1+ (i*5) + 1], ulType);
  253. ptrNameTags->aulPropTag[1+ (i*5) + 2] = CHANGE_PROP_TYPE(ptrNameTags->aulPropTag[1+ (i*5) + 2], ulType);
  254. ptrNameTags->aulPropTag[1+ (i*5) + 3] = CHANGE_PROP_TYPE(ptrNameTags->aulPropTag[1+ (i*5) + 3], ulType);
  255. ptrNameTags->aulPropTag[1+ (i*5) + 4] = CHANGE_PROP_TYPE(ptrNameTags->aulPropTag[1+ (i*5) + 4], PT_BINARY);
  256. }
  257. ptrNameTags->aulPropTag[ulNames-1] = CHANGE_PROP_TYPE(ptrNameTags->aulPropTag[ulNames-1], PT_LONG);
  258. // add func HrCombinePropTagArrays(part1, part2, dest);
  259. hr = MAPIAllocateBuffer(CbNewSPropTagArray(ptrInputCols->cValues + ptrNameTags->cValues), &~ptrContactCols);
  260. if (hr != hrSuccess)
  261. return hr;
  262. j = 0;
  263. for (i = 0; i < ptrInputCols->cValues; ++i)
  264. ptrContactCols->aulPropTag[j++] = ptrInputCols->aulPropTag[i];
  265. for (i = 0; i < ptrNameTags->cValues; ++i)
  266. ptrContactCols->aulPropTag[j++] = ptrNameTags->aulPropTag[i];
  267. ptrContactCols->cValues = j;
  268. // the exists is extra compared to the outlook restriction
  269. // restrict: ( distlist || ( contact && exist(abparraytype) && abparraytype != 0 ) )
  270. sRestrictProp.ulPropTag = PR_MESSAGE_CLASS_A;
  271. sRestrictProp.Value.lpszA = const_cast<char *>("IPM.Contact");
  272. resAnd += ECContentRestriction(FL_PREFIX|FL_IGNORECASE, PR_MESSAGE_CLASS_A, &sRestrictProp, ECRestriction::Shallow);
  273. sRestrictProp.ulPropTag = ptrNameTags->aulPropTag[ulNames-1];
  274. sRestrictProp.Value.ul = 0;
  275. resAnd += ECExistRestriction(sRestrictProp.ulPropTag);
  276. resAnd += ECPropertyRestriction(RELOP_NE, sRestrictProp.ulPropTag, &sRestrictProp, ECRestriction::Shallow);
  277. sRestrictProp.ulPropTag = PR_MESSAGE_CLASS_A;
  278. sRestrictProp.Value.lpszA = const_cast<char *>("IPM.DistList");
  279. hr = ECOrRestriction(
  280. ECContentRestriction(FL_PREFIX | FL_IGNORECASE, PR_MESSAGE_CLASS_A, &sRestrictProp, ECRestriction::Cheap) +
  281. resAnd
  282. ).RestrictTable(ptrContents, TBL_BATCH);
  283. if (hr != hrSuccess)
  284. return hr;
  285. // set columns
  286. hr = ptrContents->SetColumns(ptrContactCols, TBL_BATCH);
  287. if (hr != hrSuccess)
  288. return hr;
  289. j = 0;
  290. while (true) {
  291. hr = ptrContents->QueryRows(256, 0, &ptrRows);
  292. if (hr != hrSuccess)
  293. return hr;
  294. if (ptrRows.empty())
  295. break;
  296. for (i = 0; i < ptrRows.size(); ++i) {
  297. ULONG ulOffset = 0;
  298. std::string strSearchKey;
  299. SPropValue lpColData[O_NCOLS];
  300. memset(lpColData, 0, sizeof(lpColData));
  301. if (ptrRows[i].lpProps[I_MV_INDEX].ulPropTag == (ptrNameTags->aulPropTag[0] & ~MVI_FLAG)) {
  302. // do not index outside named properties
  303. if (ptrRows[i].lpProps[I_MV_INDEX].Value.ul > 5)
  304. continue;
  305. ulOffset = ptrRows[i].lpProps[I_MV_INDEX].Value.ul * 5;
  306. }
  307. if (PROP_TYPE(ptrRows[i].lpProps[I_MESSAGE_CLASS].ulPropTag) == PT_ERROR)
  308. // no PR_MESSAGE_CLASS, unusable
  309. continue;
  310. if (
  311. ((ulFlags & MAPI_UNICODE) && wcscasecmp(ptrRows[i].lpProps[I_MESSAGE_CLASS].Value.lpszW, L"IPM.Contact") == 0) ||
  312. ((ulFlags & MAPI_UNICODE) == 0 && strcasecmp(ptrRows[i].lpProps[I_MESSAGE_CLASS].Value.lpszA, "IPM.Contact") == 0)
  313. )
  314. {
  315. lpColData[O_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE;
  316. lpColData[O_DISPLAY_TYPE].Value.ul = DT_MAILUSER;
  317. lpColData[O_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE;
  318. lpColData[O_OBJECT_TYPE].Value.ul = MAPI_MAILUSER;
  319. lpColData[O_ADDRTYPE].ulPropTag = CHANGE_PROP_TYPE(ptrOutputCols->aulPropTag[O_ADDRTYPE], PROP_TYPE(ptrRows[i].lpProps[I_NAMEDSTART + ulOffset + 1].ulPropTag));
  320. lpColData[O_ADDRTYPE].Value = ptrRows[i].lpProps[I_NAMEDSTART + ulOffset + 1].Value;
  321. } else if (
  322. ((ulFlags & MAPI_UNICODE) && wcscasecmp(ptrRows[i].lpProps[I_MESSAGE_CLASS].Value.lpszW, L"IPM.DistList") == 0) ||
  323. ((ulFlags & MAPI_UNICODE) == 0 && strcasecmp(ptrRows[i].lpProps[I_MESSAGE_CLASS].Value.lpszA, "IPM.DistList") == 0)
  324. )
  325. {
  326. lpColData[O_DISPLAY_TYPE].ulPropTag = PR_DISPLAY_TYPE;
  327. lpColData[O_DISPLAY_TYPE].Value.ul = DT_PRIVATE_DISTLIST;
  328. lpColData[O_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE;
  329. lpColData[O_OBJECT_TYPE].Value.ul = MAPI_DISTLIST;
  330. lpColData[O_ADDRTYPE].ulPropTag = PR_ADDRTYPE_W;
  331. lpColData[O_ADDRTYPE].Value.lpszW = const_cast<wchar_t *>(L"MAPIPDL");
  332. } else {
  333. continue;
  334. }
  335. // devide by 5 since a block of properties on a contact is a set of 5 (see mnNamedProps above)
  336. memory_ptr<ENTRYID> wrapped_eid;
  337. hr = MakeWrappedEntryID(ptrRows[i].lpProps[I_ENTRYID].Value.bin.cb, (LPENTRYID)ptrRows[i].lpProps[I_ENTRYID].Value.bin.lpb,
  338. lpColData[O_OBJECT_TYPE].Value.ul, ulOffset/5,
  339. &lpColData[O_ENTRYID].Value.bin.cb, &~wrapped_eid);
  340. if (hr != hrSuccess)
  341. return hr;
  342. lpColData[O_ENTRYID].Value.bin.lpb = reinterpret_cast<BYTE *>(wrapped_eid.get());
  343. lpColData[O_ENTRYID].ulPropTag = PR_ENTRYID;
  344. ulOffset += I_NAMEDSTART;
  345. lpColData[O_DISPLAY_NAME].ulPropTag = CHANGE_PROP_TYPE(ptrOutputCols->aulPropTag[O_DISPLAY_NAME], PROP_TYPE(ptrRows[i].lpProps[ulOffset + 0].ulPropTag));
  346. if (PROP_TYPE(lpColData[O_DISPLAY_NAME].ulPropTag) == PT_ERROR)
  347. // Email#Display not available, fallback to normal PR_DISPLAY_NAME
  348. lpColData[O_DISPLAY_NAME] = ptrRows[i].lpProps[I_DISPLAY_NAME];
  349. else
  350. lpColData[O_DISPLAY_NAME].Value = ptrRows[i].lpProps[ulOffset + 0].Value;
  351. lpColData[O_EMAIL_ADDRESS].ulPropTag = CHANGE_PROP_TYPE(ptrOutputCols->aulPropTag[O_EMAIL_ADDRESS], PROP_TYPE(ptrRows[i].lpProps[ulOffset + 2].ulPropTag));
  352. if (PROP_TYPE(lpColData[O_EMAIL_ADDRESS].ulPropTag) == PT_ERROR)
  353. // Email#Address not available, fallback to normal PR_EMAIL_ADDRESS
  354. lpColData[O_EMAIL_ADDRESS] = ptrRows[i].lpProps[I_EMAIL_ADDRESS];
  355. else
  356. lpColData[O_EMAIL_ADDRESS].Value = ptrRows[i].lpProps[ulOffset + 2].Value;
  357. lpColData[O_NORMALIZED_SUBJECT].ulPropTag = CHANGE_PROP_TYPE(ptrOutputCols->aulPropTag[O_NORMALIZED_SUBJECT], PROP_TYPE(ptrRows[i].lpProps[ulOffset + 3].ulPropTag));
  358. if (PROP_TYPE(lpColData[O_NORMALIZED_SUBJECT].ulPropTag) == PT_ERROR)
  359. // Email#OriginalDisplayName not available, fallback to normal PR_NORMALIZED_SUBJECT
  360. lpColData[O_NORMALIZED_SUBJECT] = ptrRows[i].lpProps[I_NORMALIZED_SUBJECT];
  361. else
  362. lpColData[O_NORMALIZED_SUBJECT].Value = ptrRows[i].lpProps[ulOffset + 3].Value;
  363. lpColData[O_ORIGINAL_DISPLAY_NAME].ulPropTag = CHANGE_PROP_TYPE(ptrOutputCols->aulPropTag[O_ORIGINAL_DISPLAY_NAME], PROP_TYPE(ptrRows[i].lpProps[I_DISPLAY_NAME].ulPropTag));
  364. lpColData[O_ORIGINAL_DISPLAY_NAME].Value = ptrRows[i].lpProps[I_DISPLAY_NAME].Value;
  365. lpColData[O_ZC_ORIGINAL_ENTRYID].ulPropTag = CHANGE_PROP_TYPE(ptrOutputCols->aulPropTag[O_ZC_ORIGINAL_ENTRYID], PROP_TYPE(ptrRows[i].lpProps[I_ENTRYID].ulPropTag));
  366. lpColData[O_ZC_ORIGINAL_ENTRYID].Value = ptrRows[i].lpProps[I_ENTRYID].Value;
  367. lpColData[O_ZC_ORIGINAL_PARENT_ENTRYID].ulPropTag = CHANGE_PROP_TYPE(ptrOutputCols->aulPropTag[O_ZC_ORIGINAL_PARENT_ENTRYID], PROP_TYPE(ptrRows[i].lpProps[I_PARENT_ENTRYID].ulPropTag));
  368. lpColData[O_ZC_ORIGINAL_PARENT_ENTRYID].Value = ptrRows[i].lpProps[I_PARENT_ENTRYID].Value;
  369. lpColData[O_ZC_ORIGINAL_SOURCE_KEY].ulPropTag = CHANGE_PROP_TYPE(ptrOutputCols->aulPropTag[O_ZC_ORIGINAL_SOURCE_KEY], PROP_TYPE(ptrRows[i].lpProps[I_SOURCE_KEY].ulPropTag));
  370. lpColData[O_ZC_ORIGINAL_SOURCE_KEY].Value = ptrRows[i].lpProps[I_SOURCE_KEY].Value;
  371. lpColData[O_ZC_ORIGINAL_PARENT_SOURCE_KEY].ulPropTag = CHANGE_PROP_TYPE(ptrOutputCols->aulPropTag[O_ZC_ORIGINAL_PARENT_SOURCE_KEY], PROP_TYPE(ptrRows[i].lpProps[I_PARENT_SOURCE_KEY].ulPropTag));
  372. lpColData[O_ZC_ORIGINAL_PARENT_SOURCE_KEY].Value = ptrRows[i].lpProps[I_PARENT_SOURCE_KEY].Value;
  373. lpColData[O_ZC_ORIGINAL_CHANGE_KEY].ulPropTag = CHANGE_PROP_TYPE(ptrOutputCols->aulPropTag[O_ZC_ORIGINAL_CHANGE_KEY], PROP_TYPE(ptrRows[i].lpProps[I_CHANGE_KEY].ulPropTag));
  374. lpColData[O_ZC_ORIGINAL_CHANGE_KEY].Value = ptrRows[i].lpProps[I_CHANGE_KEY].Value;
  375. // @note, outlook seems to set the gab original search key (if possible, otherwise SMTP). The IMessage contact in the folder contains some unusable binary blob.
  376. if (PROP_TYPE(lpColData[O_ADDRTYPE].ulPropTag) == PT_STRING8 &&
  377. PROP_TYPE(lpColData[O_EMAIL_ADDRESS].ulPropTag) == PT_STRING8)
  378. strSearchKey = strToUpper(std::string(lpColData[O_ADDRTYPE].Value.lpszA) + ":" + lpColData[O_EMAIL_ADDRESS].Value.lpszA);
  379. else if (PROP_TYPE(lpColData[O_ADDRTYPE].ulPropTag) == PT_UNICODE &&
  380. PROP_TYPE(lpColData[O_EMAIL_ADDRESS].ulPropTag) == PT_UNICODE)
  381. strSearchKey = strToUpper(convert_to<std::string>(std::wstring(lpColData[O_ADDRTYPE].Value.lpszW) + L":" + lpColData[O_EMAIL_ADDRESS].Value.lpszW));
  382. else
  383. // eg. distlists
  384. hr = MAPI_E_NOT_FOUND;
  385. if (hr == hrSuccess) {
  386. lpColData[O_SEARCH_KEY].ulPropTag = PR_SEARCH_KEY;
  387. lpColData[O_SEARCH_KEY].Value.bin.cb = strSearchKey.length()+1;
  388. lpColData[O_SEARCH_KEY].Value.bin.lpb = (BYTE*)strSearchKey.data();
  389. } else {
  390. lpColData[O_SEARCH_KEY].ulPropTag = CHANGE_PROP_TYPE(PR_SEARCH_KEY, PT_ERROR);
  391. lpColData[O_SEARCH_KEY].Value.ul = MAPI_E_NOT_FOUND;
  392. }
  393. lpColData[O_INSTANCE_KEY].ulPropTag = PR_INSTANCE_KEY;
  394. lpColData[O_INSTANCE_KEY].Value.bin.cb = sizeof(ULONG);
  395. lpColData[O_INSTANCE_KEY].Value.bin.lpb = (LPBYTE)&j;
  396. lpColData[O_ROWID].ulPropTag = PR_ROWID;
  397. lpColData[O_ROWID].Value.ul = j++;
  398. hr = lpTable->HrModifyRow(ECKeyTable::TABLE_ROW_ADD, NULL, lpColData, O_NCOLS);
  399. if (hr != hrSuccess)
  400. return hr;
  401. }
  402. }
  403. done:
  404. AddChild(lpTable);
  405. hr = lpTable->HrGetView(createLocaleFromName(nullptr), ulFlags, &~lpTableView);
  406. if(hr != hrSuccess)
  407. return hr;
  408. return lpTableView->QueryInterface(IID_IMAPITable,
  409. reinterpret_cast<void **>(lppTable));
  410. #undef TCOLS
  411. }
  412. HRESULT ZCABContainer::GetDistListContentsTable(ULONG ulFlags, LPMAPITABLE *lppTable)
  413. {
  414. HRESULT hr = hrSuccess;
  415. static constexpr const SizedSPropTagArray(13, sptaCols) =
  416. {13, {PR_NULL /* reserve for PR_ROWID */, PR_ADDRTYPE,
  417. PR_DISPLAY_NAME, PR_DISPLAY_TYPE, PR_EMAIL_ADDRESS, PR_ENTRYID,
  418. PR_INSTANCE_KEY, PR_OBJECT_TYPE, PR_RECORD_KEY, PR_SEARCH_KEY,
  419. PR_SEND_INTERNET_ENCODING, PR_SEND_RICH_INFO,
  420. PR_TRANSMITABLE_DISPLAY_NAME}};
  421. SPropTagArrayPtr ptrCols;
  422. object_ptr<ECMemTable> lpTable;
  423. object_ptr<ECMemTableView> lpTableView;
  424. SPropValuePtr ptrEntries;
  425. MAPIPropPtr ptrUser;
  426. ULONG ulObjType;
  427. ULONG cValues;
  428. SPropArrayPtr ptrProps;
  429. SPropValue sKey;
  430. KCHL::object_ptr<ZCMAPIProp> ptrZCMAPIProp;
  431. hr = Util::HrCopyUnicodePropTagArray(ulFlags, sptaCols, &~ptrCols);
  432. if (hr != hrSuccess)
  433. return hr;
  434. hr = ECMemTable::Create(ptrCols, PR_ROWID, &~lpTable);
  435. if(hr != hrSuccess)
  436. return hr;
  437. // getprops, open real contacts, make table
  438. hr = HrGetOneProp(m_lpDistList, 0x81051102, &~ptrEntries); // Members "entryids" named property, see data layout below
  439. if (hr != hrSuccess)
  440. return hr;
  441. sKey.ulPropTag = PR_ROWID;
  442. sKey.Value.ul = 0;
  443. for (ULONG i = 0; i < ptrEntries->Value.MVbin.cValues; ++i) {
  444. ULONG ulOffset = 0;
  445. BYTE cType = 0;
  446. // Wrapped entryids:
  447. // Flags: (ULONG) 0
  448. // Provider: (GUID) 0xC091ADD3519DCF11A4A900AA0047FAA4
  449. // Type: (BYTE) <value>, describes wrapped entryid
  450. // lower 4 bits:
  451. // 0x00 = OneOff (use addressbook)
  452. // 0x03 = Contact (use folder / session?)
  453. // 0x04 = PDL (use folder / session?)
  454. // 0x05 = GAB IMailUser (use addressbook)
  455. // 0x06 = GAB IDistList (use addressbook)
  456. // next 3 bits: if lower is 0x03
  457. // 0x00 = business fax, or oneoff entryid
  458. // 0x10 = home fax
  459. // 0x20 = primary fax
  460. // 0x30 = no contact data
  461. // 0x40 = email 1
  462. // 0x50 = email 2
  463. // 0x60 = email 3
  464. // top bit:
  465. // 0x80 default on, except for oneoff entryids
  466. // either WAB_GUID or ONE_OFF_MUID
  467. if (memcmp(ptrEntries->Value.MVbin.lpbin[i].lpb + sizeof(ULONG), (void*)&WAB_GUID, sizeof(GUID)) == 0) {
  468. // handle wrapped entryids
  469. ulOffset = sizeof(ULONG) + sizeof(GUID) + sizeof(BYTE);
  470. cType = ptrEntries->Value.MVbin.lpbin[i].lpb[sizeof(ULONG) + sizeof(GUID)];
  471. }
  472. hr = m_lpMAPISup->OpenEntry(ptrEntries->Value.MVbin.lpbin[i].cb - ulOffset, reinterpret_cast<ENTRYID *>(ptrEntries->Value.MVbin.lpbin[i].lpb + ulOffset), nullptr, 0, &ulObjType, &~ptrUser);
  473. if (hr != hrSuccess)
  474. continue;
  475. if ((cType & 0x80) && (cType & 0x0F) < 5 && (cType & 0x0F) > 0) {
  476. ULONG cbEntryID;
  477. EntryIdPtr ptrEntryID;
  478. SPropValuePtr ptrPropEntryID;
  479. ULONG ulObjOffset = 0;
  480. ULONG ulObjType = 0;
  481. hr = HrGetOneProp(ptrUser, PR_ENTRYID, &~ptrPropEntryID);
  482. if (hr != hrSuccess)
  483. return hr;
  484. if ((cType & 0x0F) == 3) {
  485. ulObjType = MAPI_MAILUSER;
  486. ulObjOffset = cType >> 4;
  487. } else
  488. ulObjType = MAPI_DISTLIST;
  489. hr = MakeWrappedEntryID(ptrPropEntryID->Value.bin.cb, (LPENTRYID)ptrPropEntryID->Value.bin.lpb, ulObjType, ulObjOffset, &cbEntryID, &~ptrEntryID);
  490. if (hr != hrSuccess)
  491. return hr;
  492. hr = ZCMAPIProp::Create(ptrUser, cbEntryID, ptrEntryID, &~ptrZCMAPIProp);
  493. if (hr != hrSuccess)
  494. return hr;
  495. hr = ptrZCMAPIProp->GetProps(ptrCols, 0, &cValues, &~ptrProps);
  496. if (FAILED(hr))
  497. continue;
  498. } else {
  499. hr = ptrUser->GetProps(ptrCols, 0, &cValues, &~ptrProps);
  500. if (FAILED(hr))
  501. continue;
  502. }
  503. ptrProps[0] = sKey;
  504. hr = lpTable->HrModifyRow(ECKeyTable::TABLE_ROW_ADD, NULL, ptrProps.get(), cValues);
  505. if (hr != hrSuccess)
  506. return hr;
  507. ++sKey.Value.ul;
  508. }
  509. AddChild(lpTable);
  510. hr = lpTable->HrGetView(createLocaleFromName(nullptr), ulFlags, &~lpTableView);
  511. if(hr != hrSuccess)
  512. return hr;
  513. return lpTableView->QueryInterface(IID_IMAPITable,
  514. reinterpret_cast<void **>(lppTable));
  515. }
  516. /**
  517. * Returns an addressbook contents table of the IPM.Contacts folder in m_lpContactFolder.
  518. *
  519. * @param[in] ulFlags MAPI_UNICODE for default unicode columns
  520. * @param[out] lppTable contents table of all items found in folder
  521. *
  522. * @return
  523. */
  524. HRESULT ZCABContainer::GetContentsTable(ULONG ulFlags, LPMAPITABLE *lppTable)
  525. {
  526. if (m_lpDistList)
  527. return GetDistListContentsTable(ulFlags, lppTable);
  528. return GetFolderContentsTable(ulFlags, lppTable);
  529. }
  530. /**
  531. * Can return 3 kinds of tables:
  532. * 1. Root Container, contains one entry: the provider container
  533. * 2. Provider Container, contains user folders
  534. * 3. CONVENIENT_DEPTH: 1 + 2
  535. *
  536. * @param[in] ulFlags MAPI_UNICODE | CONVENIENT_DEPTH
  537. * @param[out] lppTable root container table
  538. *
  539. * @return MAPI Error code
  540. */
  541. HRESULT ZCABContainer::GetHierarchyTable(ULONG ulFlags, LPMAPITABLE *lppTable)
  542. {
  543. HRESULT hr = hrSuccess;
  544. object_ptr<ECMemTable> lpTable;
  545. object_ptr<ECMemTableView> lpTableView;
  546. #define TCOLS 9
  547. SizedSPropTagArray(TCOLS, sptaCols) = {TCOLS, {PR_ENTRYID, PR_STORE_ENTRYID, PR_DISPLAY_NAME_W, PR_OBJECT_TYPE, PR_CONTAINER_FLAGS, PR_DISPLAY_TYPE, PR_AB_PROVIDER_ID, PR_DEPTH, PR_INSTANCE_KEY}};
  548. enum {XENTRYID = 0, STORE_ENTRYID, DISPLAY_NAME, OBJECT_TYPE, CONTAINER_FLAGS, DISPLAY_TYPE, AB_PROVIDER_ID, DEPTH, INSTANCE_KEY, ROWID};
  549. ULONG ulInstance = 0;
  550. SPropValue sProps[TCOLS + 1];
  551. convert_context converter;
  552. if ((ulFlags & MAPI_UNICODE) == 0)
  553. sptaCols.aulPropTag[DISPLAY_NAME] = PR_DISPLAY_NAME_A;
  554. hr = ECMemTable::Create(sptaCols, PR_ROWID, &~lpTable);
  555. if(hr != hrSuccess)
  556. return hr;
  557. /*
  558. 1. root container : m_lpFolders = NULL, m_lpContactFolder = NULL, m_lpDistList = NULL, m_lpProvider = ZCABLogon (one entry: provider)
  559. 2. provider container : m_lpFolders = data, m_lpContactFolder = NULL, m_lpDistList = NULL, m_lpProvider = ZCABLogon (N entries: folders)
  560. 3. contact folder : m_lpFolders = NULL, m_lpContactFolder = data, m_lpDistList = NULL, m_lpProvider = ZCABContainer from (2), (no hierarchy entries)
  561. 4. distlist : m_lpFolders = NULL, m_lpContactFolder = NULL, m_lpDistList = data, m_lpProvider = ZCABContainer from (3), (no hierarchy entries)
  562. when ulFlags contains CONVENIENT_DEPTH, (1) also contains (2)
  563. so we open (2) through the provider, which gives the m_lpFolders
  564. */
  565. if (m_lpFolders) {
  566. // create hierarchy with folders from user stores
  567. for (const auto &folder : *m_lpFolders) {
  568. std::string strName;
  569. KCHL::memory_ptr<cabEntryID> lpEntryID;
  570. ULONG cbEntryID = CbNewCABENTRYID(folder.cbFolder);
  571. hr = MAPIAllocateBuffer(cbEntryID, &~lpEntryID);
  572. if (hr != hrSuccess)
  573. return hr;
  574. memset(lpEntryID, 0, cbEntryID);
  575. memcpy(&lpEntryID->muid, &MUIDZCSAB, sizeof(MAPIUID));
  576. lpEntryID->ulObjType = MAPI_ABCONT;
  577. lpEntryID->ulOffset = 0;
  578. memcpy(lpEntryID->origEntryID, folder.lpFolder, folder.cbFolder);
  579. sProps[XENTRYID].ulPropTag = sptaCols.aulPropTag[XENTRYID];
  580. sProps[XENTRYID].Value.bin.cb = cbEntryID;
  581. sProps[XENTRYID].Value.bin.lpb = reinterpret_cast<BYTE *>(lpEntryID.get());
  582. sProps[STORE_ENTRYID].ulPropTag = CHANGE_PROP_TYPE(sptaCols.aulPropTag[STORE_ENTRYID], PT_ERROR);
  583. sProps[STORE_ENTRYID].Value.err = MAPI_E_NOT_FOUND;
  584. sProps[DISPLAY_NAME].ulPropTag = sptaCols.aulPropTag[DISPLAY_NAME];
  585. if ((ulFlags & MAPI_UNICODE) == 0) {
  586. sProps[DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME_A;
  587. strName = converter.convert_to<std::string>(folder.strwDisplayName);
  588. sProps[DISPLAY_NAME].Value.lpszA = (char*)strName.c_str();
  589. } else {
  590. sProps[DISPLAY_NAME].Value.lpszW = const_cast<wchar_t *>(folder.strwDisplayName.c_str());
  591. }
  592. sProps[OBJECT_TYPE].ulPropTag = sptaCols.aulPropTag[OBJECT_TYPE];
  593. sProps[OBJECT_TYPE].Value.ul = MAPI_ABCONT;
  594. sProps[CONTAINER_FLAGS].ulPropTag = sptaCols.aulPropTag[CONTAINER_FLAGS];
  595. sProps[CONTAINER_FLAGS].Value.ul = AB_RECIPIENTS | AB_UNMODIFIABLE | AB_UNICODE_OK;
  596. sProps[DISPLAY_TYPE].ulPropTag = sptaCols.aulPropTag[DISPLAY_TYPE];
  597. sProps[DISPLAY_TYPE].Value.ul = DT_NOT_SPECIFIC;
  598. sProps[AB_PROVIDER_ID].ulPropTag = sptaCols.aulPropTag[AB_PROVIDER_ID];
  599. sProps[AB_PROVIDER_ID].Value.bin.cb = sizeof(GUID);
  600. sProps[AB_PROVIDER_ID].Value.bin.lpb = (BYTE*)&MUIDZCSAB;
  601. sProps[DEPTH].ulPropTag = PR_DEPTH;
  602. sProps[DEPTH].Value.ul = (ulFlags & CONVENIENT_DEPTH) ? 1 : 0;
  603. sProps[INSTANCE_KEY].ulPropTag = PR_INSTANCE_KEY;
  604. sProps[INSTANCE_KEY].Value.bin.cb = sizeof(ULONG);
  605. sProps[INSTANCE_KEY].Value.bin.lpb = (LPBYTE)&ulInstance;
  606. sProps[ROWID].ulPropTag = PR_ROWID;
  607. sProps[ROWID].Value.ul = ulInstance;
  608. hr = lpTable->HrModifyRow(ECKeyTable::TABLE_ROW_ADD, NULL, sProps, TCOLS + 1);
  609. if (hr != hrSuccess)
  610. return hr;
  611. ++ulInstance;
  612. }
  613. } else if (!m_lpContactFolder) {
  614. // only if not using a contacts folder, which should make the contents table. so this would return an empty hierarchy table, which is true.
  615. // create toplevel hierarchy. one entry: "Kopano Contacts Folders"
  616. BYTE sEntryID[4 + sizeof(GUID)]; // minimum sized entryid. no extra info needed
  617. memset(sEntryID, 0, sizeof(sEntryID));
  618. memcpy(sEntryID + 4, &MUIDZCSAB, sizeof(GUID));
  619. sProps[XENTRYID].ulPropTag = sptaCols.aulPropTag[XENTRYID];
  620. sProps[XENTRYID].Value.bin.cb = sizeof(sEntryID);
  621. sProps[XENTRYID].Value.bin.lpb = sEntryID;
  622. sProps[STORE_ENTRYID].ulPropTag = CHANGE_PROP_TYPE(sptaCols.aulPropTag[STORE_ENTRYID], PT_ERROR);
  623. sProps[STORE_ENTRYID].Value.err = MAPI_E_NOT_FOUND;
  624. sProps[DISPLAY_NAME].ulPropTag = sptaCols.aulPropTag[DISPLAY_NAME];
  625. if ((ulFlags & MAPI_UNICODE) == 0) {
  626. sProps[DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME_A;
  627. sProps[DISPLAY_NAME].Value.lpszA = _A("Kopano Contacts Folders");
  628. } else {
  629. sProps[DISPLAY_NAME].Value.lpszW = _W("Kopano Contacts Folders");
  630. }
  631. sProps[OBJECT_TYPE].ulPropTag = sptaCols.aulPropTag[OBJECT_TYPE];
  632. sProps[OBJECT_TYPE].Value.ul = MAPI_ABCONT;
  633. // AB_SUBCONTAINERS flag causes this folder to be skipped in the IAddrBook::GetSearchPath()
  634. sProps[CONTAINER_FLAGS].ulPropTag = sptaCols.aulPropTag[CONTAINER_FLAGS];
  635. sProps[CONTAINER_FLAGS].Value.ul = AB_SUBCONTAINERS | AB_UNMODIFIABLE | AB_UNICODE_OK;
  636. sProps[DISPLAY_TYPE].ulPropTag = sptaCols.aulPropTag[DISPLAY_TYPE];
  637. sProps[DISPLAY_TYPE].Value.ul = DT_NOT_SPECIFIC;
  638. sProps[AB_PROVIDER_ID].ulPropTag = sptaCols.aulPropTag[AB_PROVIDER_ID];
  639. sProps[AB_PROVIDER_ID].Value.bin.cb = sizeof(GUID);
  640. sProps[AB_PROVIDER_ID].Value.bin.lpb = (BYTE*)&MUIDZCSAB;
  641. sProps[DEPTH].ulPropTag = PR_DEPTH;
  642. sProps[DEPTH].Value.ul = 0;
  643. sProps[INSTANCE_KEY].ulPropTag = PR_INSTANCE_KEY;
  644. sProps[INSTANCE_KEY].Value.bin.cb = sizeof(ULONG);
  645. sProps[INSTANCE_KEY].Value.bin.lpb = (LPBYTE)&ulInstance;
  646. sProps[ROWID].ulPropTag = PR_ROWID;
  647. sProps[ROWID].Value.ul = ulInstance++;
  648. hr = lpTable->HrModifyRow(ECKeyTable::TABLE_ROW_ADD, NULL, sProps, TCOLS + 1);
  649. if (hr != hrSuccess)
  650. return hr;
  651. if (ulFlags & CONVENIENT_DEPTH) {
  652. ABContainerPtr ptrContainer;
  653. ULONG ulObjType;
  654. MAPITablePtr ptrTable;
  655. SRowSetPtr ptrRows;
  656. hr = ((ZCABLogon*)m_lpProvider)->OpenEntry(sizeof(sEntryID), reinterpret_cast<ENTRYID *>(sEntryID), nullptr, 0, &ulObjType, &~ptrContainer);
  657. if (hr != hrSuccess)
  658. return hr;
  659. hr = ptrContainer->GetHierarchyTable(ulFlags, &~ptrTable);
  660. if (hr != hrSuccess)
  661. return hr;
  662. hr = ptrTable->QueryRows(-1, 0, &ptrRows);
  663. if (hr != hrSuccess)
  664. return hr;
  665. for (SRowSetPtr::size_type i = 0; i < ptrRows.size(); ++i) {
  666. // use PR_STORE_ENTRYID field to set instance key, since that is always MAPI_E_NOT_FOUND (see above)
  667. auto lpProp = PpropFindProp(ptrRows[i].lpProps, ptrRows[i].cValues, CHANGE_PROP_TYPE(PR_STORE_ENTRYID, PT_ERROR));
  668. if (lpProp == nullptr)
  669. continue;
  670. lpProp->ulPropTag = PR_ROWID;
  671. lpProp->Value.ul = ulInstance++;
  672. hr = lpTable->HrModifyRow(ECKeyTable::TABLE_ROW_ADD, NULL, ptrRows[i].lpProps, ptrRows[i].cValues);
  673. if (hr != hrSuccess)
  674. return hr;
  675. }
  676. }
  677. }
  678. AddChild(lpTable);
  679. hr = lpTable->HrGetView(createLocaleFromName(nullptr), ulFlags, &~lpTableView);
  680. if(hr != hrSuccess)
  681. return hr;
  682. return lpTableView->QueryInterface(IID_IMAPITable,
  683. reinterpret_cast<void **>(lppTable));
  684. #undef TCOLS
  685. }
  686. /**
  687. * Opens the contact from any given contact folder, and makes a ZCMAPIProp object for that contact.
  688. *
  689. * @param[in] cbEntryID wrapped contact entryid bytes
  690. * @param[in] lpEntryID
  691. * @param[in] lpInterface requested interface
  692. * @param[in] ulFlags unicode flags
  693. * @param[out] lpulObjType returned object type
  694. * @param[out] lppUnk returned object
  695. *
  696. * @return MAPI Error code
  697. */
  698. HRESULT ZCABContainer::OpenEntry(ULONG cbEntryID, LPENTRYID lpEntryID, LPCIID lpInterface, ULONG ulFlags, ULONG *lpulObjType, LPUNKNOWN *lppUnk)
  699. {
  700. HRESULT hr = hrSuccess;
  701. auto lpCABEntryID = reinterpret_cast<cabEntryID *>(lpEntryID);
  702. ULONG cbNewCABEntryID = CbNewCABENTRYID(0);
  703. ULONG cbFolder = 0;
  704. LPENTRYID lpFolder = NULL;
  705. ULONG ulObjType = 0;
  706. MAPIFolderPtr ptrContactFolder;
  707. object_ptr<ZCABContainer> lpZCABContacts;
  708. MessagePtr ptrContact;
  709. object_ptr<ZCMAPIProp> lpZCMAPIProp;
  710. if (cbEntryID < cbNewCABEntryID ||
  711. memcmp(&lpCABEntryID->muid, &MUIDZCSAB, sizeof(MAPIUID)) != 0)
  712. return MAPI_E_UNKNOWN_ENTRYID;
  713. if (m_lpDistList)
  714. // there is nothing to open from the distlist point of view
  715. // @todo, passthrough to IMAPISupport object?
  716. return MAPI_E_NO_SUPPORT;
  717. cbFolder = cbEntryID - cbNewCABEntryID;
  718. lpFolder = (LPENTRYID)((LPBYTE)lpEntryID + cbNewCABEntryID);
  719. if (lpCABEntryID->ulObjType == MAPI_ABCONT) {
  720. hr = m_lpMAPISup->OpenEntry(cbFolder, lpFolder, nullptr, 0, &ulObjType, &~ptrContactFolder);
  721. if (hr == MAPI_E_NOT_FOUND) {
  722. // the folder is most likely in a store that is not yet available through this MAPI session
  723. // try opening the store through the support object, and see if we can get it anyway
  724. MsgStorePtr ptrStore;
  725. MAPIGetSessionPtr ptrGetSession;
  726. MAPISessionPtr ptrSession;
  727. hr = m_lpMAPISup->QueryInterface(IID_IMAPIGetSession, &~ptrGetSession);
  728. if (hr != hrSuccess)
  729. return hr;
  730. hr = ptrGetSession->GetMAPISession(&~ptrSession);
  731. if (hr != hrSuccess)
  732. return hr;
  733. std::vector<zcabFolderEntry>::const_iterator i;
  734. // find the store of this folder
  735. for (i = m_lpFolders->cbegin();
  736. i != m_lpFolders->cend(); ++i) {
  737. ULONG res;
  738. if ((m_lpMAPISup->CompareEntryIDs(i->cbFolder, (LPENTRYID)i->lpFolder, cbFolder, lpFolder, 0, &res) == hrSuccess) && res == TRUE)
  739. break;
  740. }
  741. if (i == m_lpFolders->cend())
  742. return MAPI_E_NOT_FOUND;
  743. hr = ptrSession->OpenMsgStore(0, i->cbStore, reinterpret_cast<ENTRYID *>(i->lpStore), nullptr, 0, &~ptrStore);
  744. if (hr != hrSuccess)
  745. return hr;
  746. hr = ptrStore->OpenEntry(cbFolder, lpFolder, nullptr, 0, &ulObjType, &~ptrContactFolder);
  747. }
  748. if (hr != hrSuccess)
  749. return hr;
  750. hr = ZCABContainer::Create(nullptr, ptrContactFolder, m_lpMAPISup, m_lpProvider, &~lpZCABContacts);
  751. if (hr != hrSuccess)
  752. return hr;
  753. AddChild(lpZCABContacts);
  754. if (lpInterface)
  755. hr = lpZCABContacts->QueryInterface(*lpInterface, (void**)lppUnk);
  756. else
  757. hr = lpZCABContacts->QueryInterface(IID_IABContainer, (void**)lppUnk);
  758. } else if (lpCABEntryID->ulObjType == MAPI_DISTLIST) {
  759. // open the Original Message
  760. hr = m_lpMAPISup->OpenEntry(cbFolder, lpFolder, nullptr, 0, &ulObjType, &~ptrContact);
  761. if (hr != hrSuccess)
  762. return hr;
  763. hr = ZCABContainer::Create(ptrContact, cbEntryID, lpEntryID, m_lpMAPISup, &~lpZCABContacts);
  764. if (hr != hrSuccess)
  765. return hr;
  766. AddChild(lpZCABContacts);
  767. if (lpInterface)
  768. hr = lpZCABContacts->QueryInterface(*lpInterface, (void**)lppUnk);
  769. else
  770. hr = lpZCABContacts->QueryInterface(IID_IDistList, (void**)lppUnk);
  771. } else if (lpCABEntryID->ulObjType == MAPI_MAILUSER) {
  772. // open the Original Message
  773. hr = m_lpMAPISup->OpenEntry(cbFolder, lpFolder, nullptr, 0, &ulObjType, &~ptrContact);
  774. if (hr != hrSuccess)
  775. return hr;
  776. hr = ZCMAPIProp::Create(ptrContact, cbEntryID, lpEntryID, &~lpZCMAPIProp);
  777. if (hr != hrSuccess)
  778. return hr;
  779. AddChild(lpZCMAPIProp);
  780. if (lpInterface)
  781. hr = lpZCMAPIProp->QueryInterface(*lpInterface, (void**)lppUnk);
  782. else
  783. hr = lpZCMAPIProp->QueryInterface(IID_IMAPIProp, (void**)lppUnk);
  784. } else {
  785. return MAPI_E_UNKNOWN_ENTRYID;
  786. }
  787. *lpulObjType = lpCABEntryID->ulObjType;
  788. return hr;
  789. }
  790. HRESULT ZCABContainer::SetSearchCriteria(LPSRestriction lpRestriction, LPENTRYLIST lpContainerList, ULONG ulSearchFlags)
  791. {
  792. return MAPI_E_NO_SUPPORT;
  793. }
  794. HRESULT ZCABContainer::GetSearchCriteria(ULONG ulFlags, LPSRestriction *lppRestriction, LPENTRYLIST *lppContainerList, ULONG *lpulSearchState)
  795. {
  796. return MAPI_E_NO_SUPPORT;
  797. }
  798. // IABContainer
  799. HRESULT ZCABContainer::CreateEntry(ULONG cbEntryID, LPENTRYID lpEntryID, ULONG ulCreateFlags, LPMAPIPROP* lppMAPIPropEntry)
  800. {
  801. return MAPI_E_NO_SUPPORT;
  802. }
  803. HRESULT ZCABContainer::CopyEntries(LPENTRYLIST lpEntries, ULONG ulUIParam, LPMAPIPROGRESS lpProgress, ULONG ulFlags)
  804. {
  805. return MAPI_E_NO_SUPPORT;
  806. }
  807. HRESULT ZCABContainer::DeleteEntries(LPENTRYLIST lpEntries, ULONG ulFlags)
  808. {
  809. return MAPI_E_NO_SUPPORT;
  810. }
  811. /**
  812. * Resolve MAPI_UNRESOLVED items in lpAdrList and possebly add resolved
  813. *
  814. * @param[in] lpPropTagArray properties to be included in lpAdrList
  815. * @param[in] ulFlags EMS_AB_ADDRESS_LOOKUP | MAPI_UNICODE
  816. * @param[in,out] lpAdrList
  817. * @param[in,out] lpFlagList MAPI_AMBIGUOUS | MAPI_RESOLVED | MAPI_UNRESOLVED
  818. *
  819. * @return
  820. */
  821. HRESULT ZCABContainer::ResolveNames(const SPropTagArray *lpPropTagArray,
  822. ULONG ulFlags, LPADRLIST lpAdrList, LPFlagList lpFlagList)
  823. {
  824. HRESULT hr;
  825. // only columns we can set from our contents table
  826. static constexpr const SizedSPropTagArray(7, sptaDefault) =
  827. {7, {PR_ADDRTYPE_A, PR_DISPLAY_NAME_A, PR_DISPLAY_TYPE,
  828. PR_EMAIL_ADDRESS_A, PR_ENTRYID, PR_INSTANCE_KEY,
  829. PR_OBJECT_TYPE}};
  830. static constexpr const SizedSPropTagArray(7, sptaUnicode) =
  831. {7, {PR_ADDRTYPE_W, PR_DISPLAY_NAME_W, PR_DISPLAY_TYPE,
  832. PR_EMAIL_ADDRESS_W, PR_ENTRYID, PR_INSTANCE_KEY,
  833. PR_OBJECT_TYPE}};
  834. ULONG i;
  835. SRowSetPtr ptrRows;
  836. if (lpPropTagArray == NULL)
  837. lpPropTagArray = (ulFlags & MAPI_UNICODE) ? sptaUnicode : sptaDefault;
  838. // in this container table, find given PR_DISPLAY_NAME
  839. if (m_lpFolders) {
  840. // return MAPI_E_NO_SUPPORT ? since you should not query on this level
  841. // @todo search in all folders, loop over self, since we want this providers entry ids
  842. MAPITablePtr ptrHierarchy;
  843. if (m_lpFolders->empty())
  844. return hrSuccess;
  845. hr = this->GetHierarchyTable(0, &~ptrHierarchy);
  846. if (hr != hrSuccess)
  847. return hr;
  848. hr = ptrHierarchy->QueryRows(m_lpFolders->size(), 0, &ptrRows);
  849. if (hr != hrSuccess)
  850. return hr;
  851. for (i = 0; i < ptrRows.size(); ++i) {
  852. ABContainerPtr ptrContainer;
  853. auto lpEntryID = PCpropFindProp(ptrRows[i].lpProps, ptrRows[i].cValues, PR_ENTRYID);
  854. ULONG ulObjType;
  855. if (!lpEntryID)
  856. continue;
  857. // this? provider?
  858. hr = this->OpenEntry(lpEntryID->Value.bin.cb, reinterpret_cast<ENTRYID *>(lpEntryID->Value.bin.lpb), nullptr, 0, &ulObjType, &~ptrContainer);
  859. if (hr != hrSuccess)
  860. return hr;
  861. hr = ptrContainer->ResolveNames(lpPropTagArray, ulFlags, lpAdrList, lpFlagList);
  862. if (hr != hrSuccess)
  863. return hr;
  864. }
  865. } else if (m_lpContactFolder) {
  866. // only search within this folder and set entries in lpAdrList / lpFlagList
  867. MAPITablePtr ptrContents;
  868. std::set<ULONG> stProps;
  869. SPropTagArrayPtr ptrColumns;
  870. // make joint proptags
  871. std::copy(lpPropTagArray->aulPropTag, lpPropTagArray->aulPropTag + lpPropTagArray->cValues, std::inserter(stProps, stProps.begin()));
  872. for (i = 0; i < lpAdrList->aEntries[0].cValues; ++i)
  873. stProps.insert(lpAdrList->aEntries[0].rgPropVals[i].ulPropTag);
  874. hr = MAPIAllocateBuffer(CbNewSPropTagArray(stProps.size()), &~ptrColumns);
  875. if (hr != hrSuccess)
  876. return hr;
  877. ptrColumns->cValues = stProps.size();
  878. std::copy(stProps.begin(), stProps.end(), ptrColumns->aulPropTag);
  879. hr = this->GetContentsTable(ulFlags & MAPI_UNICODE, &~ptrContents);
  880. if (hr != hrSuccess)
  881. return hr;
  882. hr = ptrContents->SetColumns(ptrColumns, 0);
  883. if (hr != hrSuccess)
  884. return hr;
  885. for (i = 0; i < lpAdrList->cEntries; ++i) {
  886. auto lpDisplayNameA = PCpropFindProp(lpAdrList->aEntries[i].rgPropVals, lpAdrList->aEntries[i].cValues, PR_DISPLAY_NAME_A);
  887. auto lpDisplayNameW = PCpropFindProp(lpAdrList->aEntries[i].rgPropVals, lpAdrList->aEntries[i].cValues, PR_DISPLAY_NAME_W);
  888. if (!lpDisplayNameA && !lpDisplayNameW)
  889. continue;
  890. ULONG ulResFlag = (ulFlags & EMS_AB_ADDRESS_LOOKUP) ? FL_FULLSTRING : FL_PREFIX | FL_IGNORECASE;
  891. ULONG ulStringType = lpDisplayNameW ? PT_UNICODE : PT_STRING8;
  892. SPropValue sProp = lpDisplayNameW ? *lpDisplayNameW : *lpDisplayNameA;
  893. ECOrRestriction resFind;
  894. static const ULONG ulSearchTags[] = {PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_ORIGINAL_DISPLAY_NAME};
  895. for (size_t j = 0; j < ARRAY_SIZE(ulSearchTags); ++j) {
  896. sProp.ulPropTag = CHANGE_PROP_TYPE(ulSearchTags[j], ulStringType);
  897. resFind += ECContentRestriction(ulResFlag, CHANGE_PROP_TYPE(ulSearchTags[j], ulStringType), &sProp, ECRestriction::Cheap);
  898. }
  899. hr = resFind.RestrictTable(ptrContents, 0);
  900. if (hr != hrSuccess)
  901. return hr;
  902. hr = ptrContents->QueryRows(-1, MAPI_UNICODE, &ptrRows);
  903. if (hr != hrSuccess)
  904. return hr;
  905. if (ptrRows.size() == 1) {
  906. lpFlagList->ulFlag[i] = MAPI_RESOLVED;
  907. // release old row
  908. MAPIFreeBuffer(lpAdrList->aEntries[i].rgPropVals);
  909. lpAdrList->aEntries[i].rgPropVals = NULL;
  910. hr = Util::HrCopySRow((LPSRow)&lpAdrList->aEntries[i], &ptrRows[0], NULL);
  911. if (hr != hrSuccess)
  912. return hr;
  913. } else if (ptrRows.size() > 1) {
  914. lpFlagList->ulFlag[i] = MAPI_AMBIGUOUS;
  915. }
  916. }
  917. } else {
  918. // not supported within MAPI_DISTLIST container
  919. return MAPI_E_NO_SUPPORT;
  920. }
  921. return hrSuccess;
  922. }
  923. // IMAPIProp for m_lpDistList
  924. HRESULT ZCABContainer::GetProps(const SPropTagArray *lpPropTagArray,
  925. ULONG ulFlags, ULONG *lpcValues, SPropValue **lppPropArray)
  926. {
  927. if (m_lpDistList != NULL)
  928. return m_lpDistList->GetProps(lpPropTagArray, ulFlags, lpcValues, lppPropArray);
  929. return MAPI_E_NO_SUPPORT;
  930. }
  931. HRESULT ZCABContainer::GetPropList(ULONG ulFlags, LPSPropTagArray *lppPropTagArray)
  932. {
  933. if (m_lpDistList != NULL)
  934. return m_lpDistList->GetPropList(ulFlags, lppPropTagArray);
  935. return MAPI_E_NO_SUPPORT;
  936. }
  937. // Interface IUnknown
  938. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, QueryInterface, (REFIID, refiid), (void**, lppInterface))
  939. DEF_ULONGMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, AddRef, (void))
  940. DEF_ULONGMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, Release, (void))
  941. // Interface IABContainer
  942. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, CreateEntry, (ULONG, cbEntryID), (LPENTRYID, lpEntryID), (ULONG, ulCreateFlags), (LPMAPIPROP*, lppMAPIPropEntry))
  943. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, CopyEntries, (LPENTRYLIST, lpEntries), (ULONG, ulUIParam), (LPMAPIPROGRESS, lpProgress), (ULONG, ulFlags))
  944. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, DeleteEntries, (LPENTRYLIST, lpEntries), (ULONG, ulFlags))
  945. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, ResolveNames, (const SPropTagArray *, lpPropTagArray), (ULONG, ulFlags), (LPADRLIST, lpAdrList), (LPFlagList, lpFlagList))
  946. // Interface IMAPIContainer
  947. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, GetContentsTable, (ULONG, ulFlags), (LPMAPITABLE *, lppTable))
  948. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, GetHierarchyTable, (ULONG, ulFlags), (LPMAPITABLE *, lppTable))
  949. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, OpenEntry, (ULONG, cbEntryID), (LPENTRYID, lpEntryID), (LPCIID, lpInterface), (ULONG, ulFlags), (ULONG *, lpulObjType), (LPUNKNOWN *, lppUnk))
  950. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, SetSearchCriteria, (LPSRestriction, lpRestriction), (LPENTRYLIST, lpContainerList), (ULONG, ulSearchFlags))
  951. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, GetSearchCriteria, (ULONG, ulFlags), (LPSRestriction *, lppRestriction), (LPENTRYLIST *, lppContainerList), (ULONG *, lpulSearchState))
  952. // Interface IMAPIProp
  953. DEF_HRMETHOD_NOSUPPORT(TRACE_MAPI, ZCABContainer, ABContainer, GetLastError, (HRESULT, hError), (ULONG, ulFlags), (LPMAPIERROR *, lppMapiError))
  954. DEF_HRMETHOD_NOSUPPORT(TRACE_MAPI, ZCABContainer, ABContainer, SaveChanges, (ULONG, ulFlags))
  955. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, GetProps, (const SPropTagArray *, lpPropTagArray), (ULONG, ulFlags), (ULONG *, lpcValues), (SPropValue **, lppPropArray))
  956. DEF_HRMETHOD1(TRACE_MAPI, ZCABContainer, ABContainer, GetPropList, (ULONG, ulFlags), (LPSPropTagArray *, lppPropTagArray))
  957. DEF_HRMETHOD_NOSUPPORT(TRACE_MAPI, ZCABContainer, ABContainer, OpenProperty, (ULONG, ulPropTag), (LPCIID, lpiid), (ULONG, ulInterfaceOptions), (ULONG, ulFlags), (LPUNKNOWN *, lppUnk))
  958. DEF_HRMETHOD_NOSUPPORT(TRACE_MAPI, ZCABContainer, ABContainer, SetProps, (ULONG, cValues), (const SPropValue *, lpPropArray), (SPropProblemArray **, lppProblems))
  959. DEF_HRMETHOD_NOSUPPORT(TRACE_MAPI, ZCABContainer, ABContainer, DeleteProps, (const SPropTagArray *, lpPropTagArray), (SPropProblemArray **, lppProblems))
  960. DEF_HRMETHOD_NOSUPPORT(TRACE_MAPI, ZCABContainer, ABContainer, CopyTo, (ULONG, ciidExclude), (LPCIID, rgiidExclude), (const SPropTagArray *, lpExcludeProps), (ULONG, ulUIParam), (LPMAPIPROGRESS, lpProgress), (LPCIID, lpInterface), (void *, lpDestObj), (ULONG, ulFlags), (SPropProblemArray **, lppProblems))
  961. DEF_HRMETHOD_NOSUPPORT(TRACE_MAPI, ZCABContainer, ABContainer, CopyProps, (const SPropTagArray *, lpIncludeProps), (ULONG, ulUIParam), (LPMAPIPROGRESS, lpProgress), (LPCIID, lpInterface), (void *, lpDestObj), (ULONG, ulFlags), (SPropProblemArray **, lppProblems))
  962. DEF_HRMETHOD_NOSUPPORT(TRACE_MAPI, ZCABContainer, ABContainer, GetNamesFromIDs, (LPSPropTagArray *, pptaga), (LPGUID, lpguid), (ULONG, ulFlags), (ULONG *, pcNames), (LPMAPINAMEID **, pppNames))
  963. DEF_HRMETHOD_NOSUPPORT(TRACE_MAPI, ZCABContainer, ABContainer, GetIDsFromNames, (ULONG, cNames), (LPMAPINAMEID *, ppNames), (ULONG, ulFlags), (LPSPropTagArray *, pptaga))