2 OpenChange Server implementation.
4 EMSABP: Address Book Provider implementation
6 Copyright (C) Julien Kerihuel 2006-2011.
7 Copyright (C) Pauline Khun 2006.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 3 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 \brief Address Book Provider implementation
29 #include "mapiproxy/dcesrv_mapiproxy.h"
30 #include "dcesrv_exchange_nsp.h"
33 \details Initialize the EMSABP context and open connections to
36 \param lp_ctx pointer to the loadparm context
37 \param tdb_ctx pointer to the EMSABP TDB context
39 \return Allocated emsabp_context on success, otherwise NULL
41 _PUBLIC_ struct emsabp_context *emsabp_init(struct loadparm_context *lp_ctx,
45 struct emsabp_context *emsabp_ctx;
46 struct tevent_context *ev;
49 if (!lp_ctx) return NULL;
51 mem_ctx = talloc_named(NULL, 0, "emsabp_init");
53 emsabp_ctx = talloc_zero(mem_ctx, struct emsabp_context);
59 emsabp_ctx->mem_ctx = mem_ctx;
61 ev = tevent_context_init(mem_ctx);
67 /* Save a pointer to the loadparm context */
68 emsabp_ctx->lp_ctx = lp_ctx;
70 /* Return an opaque context pointer on samDB database */
71 emsabp_ctx->samdb_ctx = samdb_connect(mem_ctx, ev, lp_ctx, system_session(lp_ctx), 0);
72 if (!emsabp_ctx->samdb_ctx) {
74 DEBUG(0, ("[%s:%d]: Connection to \"sam.ldb\" failed\n", __FUNCTION__, __LINE__));
78 /* Reference the global TDB context to the current emsabp context */
79 emsabp_ctx->tdb_ctx = tdb_ctx;
81 /* Initialize a temporary (on-memory) TDB database to store
82 * temporary MId used within EMSABP */
83 emsabp_ctx->ttdb_ctx = emsabp_tdb_init_tmp(emsabp_ctx->mem_ctx);
84 if (!emsabp_ctx->ttdb_ctx) {
85 smb_panic("unable to create on-memory TDB database");
92 _PUBLIC_ bool emsabp_destructor(void *data)
94 struct emsabp_context *emsabp_ctx = (struct emsabp_context *)data;
97 if (emsabp_ctx->ttdb_ctx) {
98 tdb_close(emsabp_ctx->ttdb_ctx);
101 talloc_free(emsabp_ctx->mem_ctx);
109 \details Get AD record for Mail-enabled account
111 \param mem_ctx pointer to the session context
112 \param emsabp_ctx pointer to the EMSABP context
113 \param username User common name
114 \param ldb_msg Pointer on pointer to ldb_message to return result to
116 \return MAPI_E_SUCCESS on success, otherwise
117 MAPI_E_NOT_ENOUGH_RESOURCES, MAPI_E_NOT_FOUND or
120 _PUBLIC_ enum MAPISTATUS emsabp_get_account_info(TALLOC_CTX *mem_ctx,
121 struct emsabp_context *emsabp_ctx,
122 const char *username,
123 struct ldb_message **ldb_msg)
126 int msExchUserAccountControl;
127 struct ldb_result *res = NULL;
128 const char * const recipient_attrs[] = { "*", NULL };
130 ret = ldb_search(emsabp_ctx->samdb_ctx, mem_ctx, &res,
131 ldb_get_default_basedn(emsabp_ctx->samdb_ctx),
132 LDB_SCOPE_SUBTREE, recipient_attrs, "CN=%s",
134 OPENCHANGE_RETVAL_IF((ret != LDB_SUCCESS || !res->count), MAPI_E_NOT_FOUND, NULL);
136 /* Check if more than one record was found */
137 OPENCHANGE_RETVAL_IF(res->count != 1, MAPI_E_CORRUPT_STORE, NULL);
139 msExchUserAccountControl = ldb_msg_find_attr_as_int(res->msgs[0], "msExchUserAccountControl", -1);
140 switch (msExchUserAccountControl) {
141 case -1: /* msExchUserAccountControl attribute is not found */
142 return MAPI_E_NOT_FOUND;
144 *ldb_msg = res->msgs[0];
145 return MAPI_E_SUCCESS;
146 case 2: /* Account is disabled */
147 *ldb_msg = res->msgs[0];
148 return MAPI_E_ACCOUNT_DISABLED;
149 default: /* Unknown value for msExchUserAccountControl attribute */
150 return MAPI_E_CORRUPT_STORE;
153 /* We should never get here anyway */
154 return MAPI_E_CORRUPT_STORE;
158 \details Check if the authenticated user belongs to the Exchange
161 \param dce_call pointer to the session context
162 \param emsabp_ctx pointer to the EMSABP context
164 \return true on success, otherwise false
166 _PUBLIC_ bool emsabp_verify_user(struct dcesrv_call_state *dce_call,
167 struct emsabp_context *emsabp_ctx)
171 const char *username = NULL;
172 struct ldb_message *ldb_msg = NULL;
174 username = dcesrv_call_account_name(dce_call);
176 mem_ctx = talloc_named(emsabp_ctx->mem_ctx, 0, "emsabp_verify_user");
178 ret = emsabp_get_account_info(mem_ctx, emsabp_ctx, username, &ldb_msg);
180 /* cache account_name upon success */
181 if (MAPI_STATUS_IS_OK(ret)) {
182 emsabp_ctx->account_name = talloc_strdup(emsabp_ctx->mem_ctx, username);
185 talloc_free(mem_ctx);
186 return MAPI_STATUS_IS_OK(ret);
191 \details Check if the provided codepage is correct
193 \param emsabp_ctx pointer to the EMSABP context
194 \param CodePage the codepage identifier
196 \note The prototype is currently incorrect, but we are looking for
197 a better way to check codepage, maybe within AD. At the moment this
198 function is just a wrapper over libmapi valid_codepage function.
200 \return true on success, otherwise false
202 _PUBLIC_ bool emsabp_verify_codepage(struct emsabp_context *emsabp_ctx,
205 return mapi_verify_cpid(CodePage);
210 \details Build an EphemeralEntryID structure
212 \param emsabp_ctx pointer to the EMSABP context
213 \param DisplayType the AB object display type
214 \param MId the MId value
215 \param ephEntryID pointer to the EphemeralEntryID returned by the
218 \return MAPI_E_SUCCESS on success, otherwise
219 MAPI_E_NOT_ENOUGH_RESOURCES or MAPI_E_CORRUPT_STORE
221 _PUBLIC_ enum MAPISTATUS emsabp_set_EphemeralEntryID(struct emsabp_context *emsabp_ctx,
222 uint32_t DisplayType, uint32_t MId,
223 struct EphemeralEntryID *ephEntryID)
225 struct GUID *guid = (struct GUID *) NULL;
228 OPENCHANGE_RETVAL_IF(!ephEntryID, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
230 guid = (struct GUID *) samdb_ntds_objectGUID(emsabp_ctx->samdb_ctx);
231 OPENCHANGE_RETVAL_IF(!guid, MAPI_E_CORRUPT_STORE, NULL);
233 ephEntryID->ID_type = 0x87;
234 ephEntryID->R1 = 0x0;
235 ephEntryID->R2 = 0x0;
236 ephEntryID->R3 = 0x0;
237 ephEntryID->ProviderUID.ab[0] = (guid->time_low & 0xFF);
238 ephEntryID->ProviderUID.ab[1] = ((guid->time_low >> 8) & 0xFF);
239 ephEntryID->ProviderUID.ab[2] = ((guid->time_low >> 16) & 0xFF);
240 ephEntryID->ProviderUID.ab[3] = ((guid->time_low >> 24) & 0xFF);
241 ephEntryID->ProviderUID.ab[4] = (guid->time_mid & 0xFF);
242 ephEntryID->ProviderUID.ab[5] = ((guid->time_mid >> 8) & 0xFF);
243 ephEntryID->ProviderUID.ab[6] = (guid->time_hi_and_version & 0xFF);
244 ephEntryID->ProviderUID.ab[7] = ((guid->time_hi_and_version >> 8) & 0xFF);
245 memcpy(ephEntryID->ProviderUID.ab + 8, guid->clock_seq, sizeof (uint8_t) * 2);
246 memcpy(ephEntryID->ProviderUID.ab + 10, guid->node, sizeof (uint8_t) * 6);
247 ephEntryID->R4 = 0x1;
248 ephEntryID->DisplayType = DisplayType;
249 ephEntryID->MId = MId;
253 return MAPI_E_SUCCESS;
258 \details Map an EphemeralEntryID structure into a Binary_r structure
260 \param mem_ctx pointer to the memory context
261 \param ephEntryID pointer to the Ephemeral EntryID structure
262 \param bin pointer to the Binary_r structure the server will return
264 \return MAPI_E_SUCCESS on success, otherwise MAPI_E_INVALID_PARAMETER
266 _PUBLIC_ enum MAPISTATUS emsabp_EphemeralEntryID_to_Binary_r(TALLOC_CTX *mem_ctx,
267 struct EphemeralEntryID *ephEntryID,
268 struct Binary_r *bin)
271 OPENCHANGE_RETVAL_IF(!ephEntryID, MAPI_E_INVALID_PARAMETER, NULL);
272 OPENCHANGE_RETVAL_IF(!bin, MAPI_E_INVALID_PARAMETER, NULL);
274 bin->cb = sizeof (*ephEntryID);
275 bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
277 /* Copy EphemeralEntryID into bin->lpb */
278 memset(bin->lpb, 0, bin->cb);
279 bin->lpb[0] = ephEntryID->ID_type;
280 bin->lpb[1] = ephEntryID->R1;
281 bin->lpb[2] = ephEntryID->R2;
282 bin->lpb[3] = ephEntryID->R3;
283 memcpy(bin->lpb + 4, ephEntryID->ProviderUID.ab, 16);
284 bin->lpb[20] = (ephEntryID->R4 & 0xFF);
285 bin->lpb[21] = ((ephEntryID->R4 >> 8) & 0xFF);
286 bin->lpb[22] = ((ephEntryID->R4 >> 16) & 0xFF);
287 bin->lpb[23] = ((ephEntryID->R4 >> 24) & 0xFF);
288 bin->lpb[24] = (ephEntryID->DisplayType & 0xFF);
289 bin->lpb[25] = ((ephEntryID->DisplayType >> 8) & 0xFF);
290 bin->lpb[26] = ((ephEntryID->DisplayType >> 16) & 0xFF);
291 bin->lpb[27] = ((ephEntryID->DisplayType >> 24) & 0xFF);
292 bin->lpb[28] = (ephEntryID->MId & 0xFF);
293 bin->lpb[29] = ((ephEntryID->MId >> 8) & 0xFF);
294 bin->lpb[30] = ((ephEntryID->MId >> 16) & 0xFF);
295 bin->lpb[31] = ((ephEntryID->MId >> 24) & 0xFF);
297 return MAPI_E_SUCCESS;
302 \details Build a PermanentEntryID structure
304 \param emsabp_ctx pointer to the EMSABP context
305 \param DisplayType the AB object display type
306 \param msg pointer to the LDB message
307 \param permEntryID pointer to the PermanentEntryID returned by the
310 \note This function only covers DT_CONTAINER AddressBook
311 objects. It should be extended in the future to support more
314 \return MAPI_E_SUCCESS on success, otherwise
315 MAPI_E_NOT_ENOUGH_RESOURCES or MAPI_E_CORRUPT_STORE
317 _PUBLIC_ enum MAPISTATUS emsabp_set_PermanentEntryID(struct emsabp_context *emsabp_ctx,
318 uint32_t DisplayType, struct ldb_message *msg,
319 struct PermanentEntryID *permEntryID)
321 struct GUID *guid = (struct GUID *) NULL;
322 const struct ldb_val *ldb_value = NULL;
326 OPENCHANGE_RETVAL_IF(!permEntryID, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
329 permEntryID->ID_type = 0x0;
330 permEntryID->R1 = 0x0;
331 permEntryID->R2 = 0x0;
332 permEntryID->R3 = 0x0;
333 memcpy(permEntryID->ProviderUID.ab, GUID_NSPI, 16);
334 permEntryID->R4 = 0x1;
335 permEntryID->DisplayType = DisplayType;
338 permEntryID->dn = talloc_strdup(emsabp_ctx->mem_ctx, "/");
339 } else if (DisplayType == DT_CONTAINER) {
340 ldb_value = ldb_msg_find_ldb_val(msg, "objectGUID");
341 OPENCHANGE_RETVAL_IF(!ldb_value, MAPI_E_CORRUPT_STORE, NULL);
343 guid = talloc_zero(emsabp_ctx->mem_ctx, struct GUID);
344 GUID_from_data_blob(ldb_value, guid);
345 permEntryID->dn = talloc_asprintf(emsabp_ctx->mem_ctx, EMSABP_DN,
346 guid->time_low, guid->time_mid,
347 guid->time_hi_and_version,
350 guid->node[0], guid->node[1],
351 guid->node[2], guid->node[3],
352 guid->node[4], guid->node[5]);
356 dn_str = ldb_msg_find_attr_as_string(msg, "legacyExchangeDN", NULL);
357 OPENCHANGE_RETVAL_IF(!dn_str, MAPI_E_CORRUPT_STORE, NULL);
358 permEntryID->dn = talloc_strdup(emsabp_ctx->mem_ctx, dn_str);
361 return MAPI_E_SUCCESS;
366 \details Map a PermanentEntryID structure into a Binary_r
367 structure (for PR_ENTRYID and PR_EMS_AB_PARENT_ENTRYID properties)
369 \param mem_ctx pointer to the memory context
370 \param permEntryID pointer to the Permanent EntryID structure
371 \param bin pointer to the Binary_r structure the server will return
373 \return MAPI_E_SUCCESS on success, otherwise MAPI_E_INVALID_PARAMETER
375 _PUBLIC_ enum MAPISTATUS emsabp_PermanentEntryID_to_Binary_r(TALLOC_CTX *mem_ctx,
376 struct PermanentEntryID *permEntryID,
377 struct Binary_r *bin)
380 OPENCHANGE_RETVAL_IF(!permEntryID, MAPI_E_INVALID_PARAMETER, NULL);
381 OPENCHANGE_RETVAL_IF(!bin, MAPI_E_INVALID_PARAMETER, NULL);
383 /* Remove const char * size and replace it with effective dn string length */
384 bin->cb = sizeof (*permEntryID) - 4 + strlen(permEntryID->dn) + 1;
385 bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
387 /* Copy PermanantEntryID intro bin->lpb */
388 memset(bin->lpb, 0, bin->cb);
389 bin->lpb[0] = permEntryID->ID_type;
390 bin->lpb[1] = permEntryID->R1;
391 bin->lpb[2] = permEntryID->R2;
392 bin->lpb[3] = permEntryID->R3;
393 memcpy(bin->lpb + 4, permEntryID->ProviderUID.ab, 16);
394 bin->lpb[20] = (permEntryID->R4 & 0xFF);
395 bin->lpb[21] = ((permEntryID->R4 >> 8) & 0xFF);
396 bin->lpb[22] = ((permEntryID->R4 >> 16) & 0xFF);
397 bin->lpb[23] = ((permEntryID->R4 >> 24) & 0xFF);
398 bin->lpb[24] = (permEntryID->DisplayType & 0xFF);
399 bin->lpb[25] = ((permEntryID->DisplayType >> 8) & 0xFF);
400 bin->lpb[26] = ((permEntryID->DisplayType >> 16) & 0xFF);
401 bin->lpb[27] = ((permEntryID->DisplayType >> 24) & 0xFF);
402 memcpy(bin->lpb + 28, permEntryID->dn, strlen(permEntryID->dn) + 1);
404 return MAPI_E_SUCCESS;
409 \details Find the attribute matching the specified property tag and
410 return the associated data.
412 \param mem_ctx pointer to the memory context
413 \param emsabp_ctx pointer to the EMSABP context
414 \param msg pointer to the LDB message
415 \param ulPropTag the property tag to lookup
416 \param MId Minimal Entry ID associated to the current message
417 \param dwFlags bit flags specifying whether or not the server must
418 return the values of the property PidTagEntryId in the Ephemeral
419 or Permanent Entry ID format
422 \note This implementation is at the moment limited to MAILUSER,
423 which means we arbitrary set PR_OBJECT_TYPE and PR_DISPLAY_TYPE
424 while we should have a generic method to fill these properties.
426 \return Valid generic pointer on success, otherwise NULL
428 _PUBLIC_ void *emsabp_query(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
429 struct ldb_message *msg, uint32_t ulPropTag, uint32_t MId,
432 enum MAPISTATUS retval;
433 void *data = (void *) NULL;
434 const char *attribute;
435 const char *ref_attribute;
436 const char *ldb_string = NULL;
438 struct Binary_r *bin;
439 struct StringArray_r *mvszA;
440 struct EphemeralEntryID ephEntryID;
441 struct PermanentEntryID permEntryID;
442 struct ldb_message *msg2 = NULL;
443 struct ldb_message_element *ldb_element;
445 const char *dn = NULL;
448 /* Step 1. Fill attributes not in AD but created using EMSABP databases */
451 case PR_ADDRTYPE_UNICODE:
452 data = (void *) talloc_strdup(mem_ctx, EMSABP_ADDRTYPE);
455 data = talloc_zero(mem_ctx, uint32_t);
456 *((uint32_t *)data) = MAPI_MAILUSER;
458 case PR_DISPLAY_TYPE:
459 data = talloc_zero(mem_ctx, uint32_t);
460 *((uint32_t *)data) = DT_MAILUSER;
463 bin = talloc(mem_ctx, struct Binary_r);
464 if (dwFlags & fEphID) {
465 retval = emsabp_set_EphemeralEntryID(emsabp_ctx, DT_MAILUSER, MId, &ephEntryID);
466 retval = emsabp_EphemeralEntryID_to_Binary_r(mem_ctx, &ephEntryID, bin);
468 retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_MAILUSER, msg, &permEntryID);
469 retval = emsabp_PermanentEntryID_to_Binary_r(mem_ctx, &permEntryID, bin);
473 /* retrieve email address attribute, i.e. legacyExchangeDN */
474 ldb_string = ldb_msg_find_attr_as_string(msg, emsabp_property_get_attribute(PR_EMAIL_ADDRESS), NULL);
475 if (!ldb_string) return NULL;
476 tmp_str = talloc_strdup_upper(mem_ctx, ldb_string);
477 if (!tmp_str) return NULL;
478 /* make binary for PR_SEARCH_KEY */
479 bin = talloc(mem_ctx, struct Binary_r);
480 bin->lpb = (uint8_t *)talloc_asprintf(mem_ctx, "EX:%s", tmp_str);
481 bin->cb = strlen((const char *)bin->lpb) + 1;
482 talloc_free(tmp_str);
484 case PR_INSTANCE_KEY:
485 bin = talloc_zero(mem_ctx, struct Binary_r);
487 bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
488 memset(bin->lpb, 0, bin->cb);
489 bin->lpb[0] = MId & 0xFF;
490 bin->lpb[1] = (MId >> 8) & 0xFF;
491 bin->lpb[2] = (MId >> 16) & 0xFF;
492 bin->lpb[3] = (MId >> 24) & 0xFF;
498 /* Step 2. Retrieve the attribute name associated to ulPropTag and handle the ref case */
499 attribute = emsabp_property_get_attribute(ulPropTag);
500 if (!attribute) return NULL;
502 ret = emsabp_property_is_ref(ulPropTag);
504 ref_attribute = emsabp_property_get_ref_attr(ulPropTag);
506 dn = ldb_msg_find_attr_as_string(msg, attribute, NULL);
507 retval = emsabp_search_dn(emsabp_ctx, dn, &msg2);
508 if (retval != MAPI_E_SUCCESS) {
511 attribute = ref_attribute;
517 /* Step 3. Retrieve data associated to the attribute/tag */
518 switch (ulPropTag & 0xFFFF) {
521 ldb_string = ldb_msg_find_attr_as_string(msg2, attribute, NULL);
522 if (!ldb_string) return NULL;
523 data = talloc_strdup(mem_ctx, ldb_string);
527 ldb_element = ldb_msg_find_element(msg2, attribute);
528 if (!ldb_element) return NULL;
530 mvszA = talloc(mem_ctx, struct StringArray_r);
531 mvszA->cValues = ldb_element[0].num_values & 0xFFFFFFFF;
532 mvszA->lppszA = talloc_array(mem_ctx, const char *, mvszA->cValues);
533 for (i = 0; i < mvszA->cValues; i++) {
534 mvszA->lppszA[i] = talloc_strdup(mem_ctx, (char *)ldb_element->values[i].data);
536 data = (void *) mvszA;
539 DEBUG(3, ("[%s:%d]: Unsupported property type: 0x%x\n", __FUNCTION__, __LINE__,
540 (ulPropTag & 0xFFFF)));
549 \details Build the SRow array entry for the specified ldb_message.
551 \param mem_ctx pointer to the memory context
552 \param emsabp_ctx pointer to the EMSABP context
553 \param aRow pointer to the SRow structure where results will be stored
554 \param ldb_msg ldb_message record to fetch results from
555 \param MId MId of the entry to fetch (may be 0)
556 \param dwFlags input call flags (default to 0)
557 \param pPropTags pointer to the property tags array
559 \return MAPI_E_SUCCESS on success, otherwise MAPI error
561 _PUBLIC_ enum MAPISTATUS emsabp_fetch_attrs_from_msg(TALLOC_CTX *mem_ctx,
562 struct emsabp_context *emsabp_ctx,
564 struct ldb_message *ldb_msg,
565 uint32_t MId, uint32_t dwFlags,
566 struct SPropTagArray *pPropTags)
568 enum MAPISTATUS retval;
574 /* Step 0. Create MId if necessary */
576 dn = ldb_msg_find_attr_as_string(ldb_msg, "distinguishedName", NULL);
577 OPENCHANGE_RETVAL_IF(!dn, MAPI_E_CORRUPT_DATA, NULL);
578 retval = emsabp_tdb_fetch_MId(emsabp_ctx->ttdb_ctx, dn, &MId);
580 retval = emsabp_tdb_insert(emsabp_ctx->ttdb_ctx, dn);
581 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
583 retval = emsabp_tdb_fetch_MId(emsabp_ctx->ttdb_ctx, dn, &MId);
584 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
588 /* Step 1. Retrieve property values and build aRow */
589 aRow->ulAdrEntryPad = 0x0;
590 aRow->cValues = pPropTags->cValues;
591 aRow->lpProps = talloc_array(mem_ctx, struct SPropValue, aRow->cValues);
593 for (i = 0; i < aRow->cValues; i++) {
594 ulPropTag = pPropTags->aulPropTag[i];
595 data = emsabp_query(mem_ctx, emsabp_ctx, ldb_msg, ulPropTag, MId, dwFlags);
597 ulPropTag = (ulPropTag & 0xFFFF0000) | PT_ERROR;
600 aRow->lpProps[i].ulPropTag = (enum MAPITAGS) ulPropTag;
601 aRow->lpProps[i].dwAlignPad = 0x0;
602 set_SPropValue(&(aRow->lpProps[i]), data);
605 return MAPI_E_SUCCESS;
609 \details Builds the SRow array entry for the specified MId.
611 The function retrieves the DN associated to the specified MId
612 within its on-memory TDB database. It next fetches the LDB record
613 matching the DN and finally retrieve the requested properties for
616 \param mem_ctx pointer to the memory context
617 \param emsabp_ctx pointer to the EMSABP context
618 \param aRow pointer to the SRow structure where results will be
620 \param MId MId to fetch properties for
621 \param dwFlags bit flags specifying whether or not the server must
622 return the values of the property PidTagEntryId in the Ephemeral
623 or Permanent Entry ID format
624 \param pPropTags pointer to the property tags array
626 \note We currently assume records are users.ldb
628 \return MAPI_E_SUCCESS on success, otherwise MAPI error
630 _PUBLIC_ enum MAPISTATUS emsabp_fetch_attrs(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
631 struct SRow *aRow, uint32_t MId, uint32_t dwFlags,
632 struct SPropTagArray *pPropTags)
634 enum MAPISTATUS retval;
636 const char * const recipient_attrs[] = { "*", NULL };
637 struct ldb_result *res = NULL;
638 struct ldb_dn *ldb_dn = NULL;
644 /* Step 0. Try to Retrieve the dn associated to the MId first from temp TDB (users) */
645 retval = emsabp_tdb_fetch_dn_from_MId(mem_ctx, emsabp_ctx->ttdb_ctx, MId, &dn);
646 if (!MAPI_STATUS_IS_OK(retval)) {
647 /* If it fails try to retrieve it from the on-disk TDB database (conf) */
648 retval = emsabp_tdb_fetch_dn_from_MId(mem_ctx, emsabp_ctx->tdb_ctx, MId, &dn);
650 OPENCHANGE_RETVAL_IF(retval, MAPI_E_INVALID_BOOKMARK, NULL);
652 /* Step 1. Fetch the LDB record */
653 ldb_dn = ldb_dn_new(mem_ctx, emsabp_ctx->samdb_ctx, dn);
654 OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, NULL);
656 ret = ldb_search(emsabp_ctx->samdb_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn, LDB_SCOPE_BASE,
657 recipient_attrs, NULL);
658 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count || res->count != 1, MAPI_E_CORRUPT_STORE, NULL);
660 /* Step 2. Retrieve property values and build aRow */
661 aRow->ulAdrEntryPad = 0x0;
662 aRow->cValues = pPropTags->cValues;
663 aRow->lpProps = talloc_array(mem_ctx, struct SPropValue, aRow->cValues);
665 for (i = 0; i < aRow->cValues; i++) {
666 ulPropTag = pPropTags->aulPropTag[i];
667 data = emsabp_query(mem_ctx, emsabp_ctx, res->msgs[0], ulPropTag, MId, dwFlags);
669 ulPropTag &= 0xFFFF0000;
670 ulPropTag += PT_ERROR;
673 aRow->lpProps[i].ulPropTag = (enum MAPITAGS) ulPropTag;
674 aRow->lpProps[i].dwAlignPad = 0x0;
675 set_SPropValue(&(aRow->lpProps[i]), data);
679 return MAPI_E_SUCCESS;
684 \details Builds the SRow array entry for the specified table
687 \param mem_ctx pointer to the memory context
688 \param emsabp_ctx pointer to the EMSABP context
689 \param aRow pointer to the SRow structure where results will be
691 \param dwFlags flags controlling whether strings should be unicode
693 \param permEntryID pointer to the current record Permanent
695 \param parentPermEntryID pointer to the parent record Permanent
697 \param msg pointer to the LDB message for current record
698 \param child boolean value specifying whether current record is
699 root or child within containers hierarchy
701 \return MAPI_E_SUCCESS on success, otherwise MAPI error
703 _PUBLIC_ enum MAPISTATUS emsabp_table_fetch_attrs(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
704 struct SRow *aRow, uint32_t dwFlags,
705 struct PermanentEntryID *permEntryID,
706 struct PermanentEntryID *parentPermEntryID,
707 struct ldb_message *msg, bool child)
709 enum MAPISTATUS retval;
710 struct SPropTagArray *SPropTagArray;
711 struct SPropValue lpProps;
714 uint32_t containerID = 0;
715 const char *dn = NULL;
717 /* Step 1. Build the array of properties to fetch and map */
718 if (child == false) {
719 SPropTagArray = set_SPropTagArray(mem_ctx, 0x6,
723 PR_EMS_AB_CONTAINERID,
724 ((dwFlags & NspiUnicodeStrings) ? PR_DISPLAY_NAME_UNICODE : PR_DISPLAY_NAME),
725 PR_EMS_AB_IS_MASTER);
727 SPropTagArray = set_SPropTagArray(mem_ctx, 0x7,
731 PR_EMS_AB_CONTAINERID,
732 ((dwFlags & NspiUnicodeStrings) ? PR_DISPLAY_NAME_UNICODE : PR_DISPLAY_NAME),
734 PR_EMS_AB_PARENT_ENTRYID);
737 /* Step 2. Allocate SPropValue array and update SRow cValues field */
738 aRow->ulAdrEntryPad = 0x0;
740 aRow->lpProps = talloc_zero(mem_ctx, struct SPropValue);
742 /* Step 3. Global Address List or real container */
744 /* Global Address List record is constant */
745 for (i = 0; i < SPropTagArray->cValues; i++) {
746 lpProps.ulPropTag = SPropTagArray->aulPropTag[i];
747 lpProps.dwAlignPad = 0x0;
749 switch (SPropTagArray->aulPropTag[i]) {
751 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, permEntryID, &(lpProps.value.bin));
753 case PR_CONTAINER_FLAGS:
754 lpProps.value.l = AB_RECIPIENTS | AB_UNMODIFIABLE;
757 lpProps.value.l = 0x0;
759 case PR_EMS_AB_CONTAINERID:
760 lpProps.value.l = 0x0;
762 case PR_DISPLAY_NAME:
763 lpProps.value.lpszA = NULL;
765 case PR_DISPLAY_NAME_UNICODE:
766 lpProps.value.lpszW = NULL;
768 case PR_EMS_AB_IS_MASTER:
769 lpProps.value.b = false;
774 SRow_addprop(aRow, lpProps);
775 /* SRow_addprop internals overwrite with MAPI_E_NOT_FOUND when data is NULL */
776 if (SPropTagArray->aulPropTag[i] == PR_DISPLAY_NAME ||
777 SPropTagArray->aulPropTag[i] == PR_DISPLAY_NAME_UNICODE) {
778 aRow->lpProps[aRow->cValues - 1].value.lpszA = NULL;
779 aRow->lpProps[aRow->cValues - 1].value.lpszW = NULL;
783 for (i = 0; i < SPropTagArray->cValues; i++) {
784 lpProps.ulPropTag = SPropTagArray->aulPropTag[i];
785 lpProps.dwAlignPad = 0x0;
787 switch (SPropTagArray->aulPropTag[i]) {
789 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, permEntryID, &(lpProps.value.bin));
791 case PR_CONTAINER_FLAGS:
794 lpProps.value.l = AB_RECIPIENTS | AB_SUBCONTAINERS | AB_UNMODIFIABLE;
797 lpProps.value.l = AB_RECIPIENTS | AB_UNMODIFIABLE;
803 lpProps.value.l = 0x1;
806 lpProps.value.l = 0x0;
810 case PR_EMS_AB_CONTAINERID:
811 dn = ldb_msg_find_attr_as_string(msg, "distinguishedName", NULL);
812 retval = emsabp_tdb_fetch_MId(emsabp_ctx->tdb_ctx, dn, &containerID);
814 retval = emsabp_tdb_insert(emsabp_ctx->tdb_ctx, dn);
815 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
816 retval = emsabp_tdb_fetch_MId(emsabp_ctx->tdb_ctx, dn, &containerID);
817 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
819 lpProps.value.l = containerID;
821 case PR_DISPLAY_NAME:
822 lpProps.value.lpszA = talloc_strdup(mem_ctx, ldb_msg_find_attr_as_string(msg, "displayName", NULL));
823 if (!lpProps.value.lpszA) {
824 proptag = (int) lpProps.ulPropTag;
825 proptag &= 0xFFFF0000;
827 lpProps.ulPropTag = (enum MAPITAGS) proptag;
830 case PR_DISPLAY_NAME_UNICODE:
831 lpProps.value.lpszW = talloc_strdup(mem_ctx, ldb_msg_find_attr_as_string(msg, "displayName", NULL));
832 if (!lpProps.value.lpszW) {
833 proptag = (int) lpProps.ulPropTag;
834 proptag &= 0xFFFF0000;
836 lpProps.ulPropTag = (enum MAPITAGS) proptag;
839 case PR_EMS_AB_IS_MASTER:
840 /* FIXME: harcoded value - no load balancing */
841 lpProps.value.l = 0x0;
843 case PR_EMS_AB_PARENT_ENTRYID:
844 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, parentPermEntryID, &lpProps.value.bin);
849 SRow_addprop(aRow, lpProps);
853 return MAPI_E_SUCCESS;
858 \details Retrieve and build the HierarchyTable requested by
859 GetSpecialTable NSPI call
861 \param mem_ctx pointer to the memory context
862 \param emsabp_ctx pointer to the EMSABP context
863 \param dwFlags flags controlling whether strings should be UNICODE
865 \param SRowSet pointer on pointer to the output SRowSet array
867 \return MAPI_E_SUCCESS on success, otherwise MAPI_E_CORRUPT_STORE
869 _PUBLIC_ enum MAPISTATUS emsabp_get_HierarchyTable(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
870 uint32_t dwFlags, struct SRowSet **SRowSet)
872 enum MAPISTATUS retval;
874 struct PermanentEntryID gal;
875 struct PermanentEntryID parentPermEntryID;
876 struct PermanentEntryID permEntryID;
877 enum ldb_scope scope = LDB_SCOPE_SUBTREE;
878 struct ldb_request *req;
879 struct ldb_result *res = NULL;
880 struct ldb_dn *ldb_dn = NULL;
881 struct ldb_control **controls;
882 const char * const recipient_attrs[] = { "*", NULL };
883 const char *control_strings[2] = { "server_sort:0:0:displayName", NULL };
884 const char *addressBookRoots;
889 /* Step 1. Build the 'Global Address List' object using PermanentEntryID */
890 aRow = talloc_zero(mem_ctx, struct SRow);
891 OPENCHANGE_RETVAL_IF(!aRow, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
894 retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, NULL, &gal);
895 OPENCHANGE_RETVAL_IF(retval, retval, aRow);
897 retval = emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &gal, NULL, NULL, false);
900 /* Step 2. Retrieve the object pointed by addressBookRoots attribute: 'All Address Lists' */
901 ret = ldb_search(emsabp_ctx->samdb_ctx, emsabp_ctx->mem_ctx, &res,
902 ldb_get_config_basedn(emsabp_ctx->samdb_ctx),
903 scope, recipient_attrs, "(addressBookRoots=*)");
904 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count, MAPI_E_CORRUPT_STORE, aRow);
906 addressBookRoots = ldb_msg_find_attr_as_string(res->msgs[0], "addressBookRoots", NULL);
907 OPENCHANGE_RETVAL_IF(!addressBookRoots, MAPI_E_CORRUPT_STORE, aRow);
909 ldb_dn = ldb_dn_new(emsabp_ctx->mem_ctx, emsabp_ctx->samdb_ctx, addressBookRoots);
911 OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, aRow);
913 scope = LDB_SCOPE_BASE;
914 ret = ldb_search(emsabp_ctx->samdb_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn,
915 scope, recipient_attrs, NULL);
916 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count || res->count != 1, MAPI_E_CORRUPT_STORE, aRow);
918 aRow = talloc_realloc(mem_ctx, aRow, struct SRow, aRow_idx + 1);
919 retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, res->msgs[0], &parentPermEntryID);
920 emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &parentPermEntryID, NULL, res->msgs[0], false);
924 /* Step 3. Retrieve 'All Address Lists' subcontainers */
925 res = talloc_zero(mem_ctx, struct ldb_result);
926 OPENCHANGE_RETVAL_IF(!res, MAPI_E_NOT_ENOUGH_RESOURCES, aRow);
928 controls = ldb_parse_control_strings(emsabp_ctx->samdb_ctx, emsabp_ctx->mem_ctx, control_strings);
929 ret = ldb_build_search_req(&req, emsabp_ctx->samdb_ctx, emsabp_ctx->mem_ctx,
930 ldb_dn, LDB_SCOPE_SUBTREE, "(purportedSearch=*)",
931 recipient_attrs, controls, res, ldb_search_default_callback, NULL);
933 if (ret != LDB_SUCCESS) {
935 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS, MAPI_E_CORRUPT_STORE, aRow);
938 ret = ldb_request(emsabp_ctx->samdb_ctx, req);
939 if (ret == LDB_SUCCESS) {
940 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
944 if (ret != LDB_SUCCESS || !res->count) {
946 OPENCHANGE_RETVAL_IF(1, MAPI_E_CORRUPT_STORE, aRow);
949 aRow = talloc_realloc(mem_ctx, aRow, struct SRow, aRow_idx + res->count + 1);
951 for (i = 0; res->msgs[i]; i++) {
952 retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, res->msgs[i], &permEntryID);
953 emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &permEntryID, &parentPermEntryID, res->msgs[i], true);
954 talloc_free(permEntryID.dn);
955 memset(&permEntryID, 0, sizeof (permEntryID));
959 talloc_free(parentPermEntryID.dn);
961 /* Step 4. Build output SRowSet */
962 SRowSet[0]->cRows = aRow_idx;
963 SRowSet[0]->aRow = aRow;
965 return MAPI_E_SUCCESS;
970 \details Retrieve and build the CreationTemplates Table requested
971 by GetSpecialTable NSPI call
973 \param mem_ctx pointer to the memory context
974 \param emsabp_ctx pointer to the EMSABP context
975 \param dwFlags flags controlling whether strings should be UNICODE
977 \param SRowSet pointer on pointer to the output SRowSet array
979 \return MAPI_E_SUCCESS on success, otherwise MAPI_E_CORRUPT_STORE
981 _PUBLIC_ enum MAPISTATUS emsabp_get_CreationTemplatesTable(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
982 uint32_t dwFlags, struct SRowSet **SRowSet)
984 return MAPI_E_SUCCESS;
989 \details Search Active Directory given input search criterias. The
990 function associates for each records returned by the search a
991 unique session Minimal Entry ID and a LDB message.
993 \param mem_ctx pointer to the memory context
994 \param emsabp_ctx pointer to the EMSABP context
995 \param MIds pointer to the list of MIds the function returns
996 \param restriction pointer to restriction rules to apply to the
998 \param pStat pointer the STAT structure associated to the search
999 \param limit the limit number of results the function can return
1001 \note SortTypePhoneticDisplayName sort type is currently not supported.
1003 \return MAPI_E_SUCCESS on success, otherwise MAPI error
1005 _PUBLIC_ enum MAPISTATUS emsabp_search(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
1006 struct PropertyTagArray_r *MIds, struct Restriction_r *restriction,
1007 struct STAT *pStat, uint32_t limit)
1009 enum MAPISTATUS retval;
1010 struct ldb_result *res = NULL;
1011 struct PropertyRestriction_r *res_prop = NULL;
1012 const char *recipient = NULL;
1013 const char * const recipient_attrs[] = { "*", NULL };
1018 /* Step 0. Sanity Checks (MS-NSPI Server Processing Rules) */
1019 if (pStat->SortType == SortTypePhoneticDisplayName) {
1020 return MAPI_E_CALL_FAILED;
1023 if (((pStat->SortType == SortTypeDisplayName) || (pStat->SortType == SortTypePhoneticDisplayName)) &&
1024 (pStat->ContainerID && (emsabp_tdb_lookup_MId(emsabp_ctx->tdb_ctx, pStat->ContainerID) == false))) {
1025 return MAPI_E_INVALID_BOOKMARK;
1028 if (restriction && (pStat->SortType != SortTypeDisplayName) &&
1029 (pStat->SortType != SortTypePhoneticDisplayName)) {
1030 return MAPI_E_CALL_FAILED;
1033 /* Step 1. Apply restriction and retrieve results from AD */
1035 /* FIXME: We only support RES_PROPERTY restriction */
1036 if ((uint32_t)restriction->rt != RES_PROPERTY) {
1037 return MAPI_E_TOO_COMPLEX;
1040 /* FIXME: We only support PR_ANR */
1041 res_prop = (struct PropertyRestriction_r *)&(restriction->res.resProperty);
1042 if ((res_prop->ulPropTag != PR_ANR) && (res_prop->ulPropTag != PR_ANR_UNICODE)) {
1043 return MAPI_E_NO_SUPPORT;
1046 recipient = (res_prop->ulPropTag == PR_ANR) ?
1047 res_prop->lpProp->value.lpszA :
1048 res_prop->lpProp->value.lpszW;
1050 ret = ldb_search(emsabp_ctx->samdb_ctx, emsabp_ctx->mem_ctx, &res,
1051 ldb_get_default_basedn(emsabp_ctx->samdb_ctx),
1052 LDB_SCOPE_SUBTREE, recipient_attrs,
1053 "(&(objectClass=user)(sAMAccountName=*%s*)(!(objectClass=computer)))",
1056 if (ret != LDB_SUCCESS) {
1057 return MAPI_E_NOT_FOUND;
1060 return MAPI_E_INVALID_OBJECT;
1063 return MAPI_E_NOT_FOUND;
1066 /* FIXME Check restriction == NULL */
1067 return MAPI_E_INVALID_OBJECT;
1070 if (limit && res->count > limit) {
1071 return MAPI_E_TABLE_TOO_BIG;
1074 MIds->aulPropTag = talloc_array(emsabp_ctx->mem_ctx, uint32_t, res->count);
1075 MIds->cValues = res->count;
1077 /* Step 2. Create session MId for all fetched records */
1078 for (i = 0; i < res->count; i++) {
1079 dn = ldb_msg_find_attr_as_string(res->msgs[i], "distinguishedName", NULL);
1080 retval = emsabp_tdb_fetch_MId(emsabp_ctx->ttdb_ctx, dn, (uint32_t *)&(MIds->aulPropTag[i]));
1082 retval = emsabp_tdb_insert(emsabp_ctx->ttdb_ctx, dn);
1083 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
1084 retval = emsabp_tdb_fetch_MId(emsabp_ctx->ttdb_ctx, dn, (uint32_t *) &(MIds->aulPropTag[i]));
1085 OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
1089 return MAPI_E_SUCCESS;
1094 \details Search for a given DN within AD and return the associated
1097 \param emsabp_ctx pointer to the EMSABP context
1098 \param dn pointer to the DN string to search for
1099 \param ldb_res pointer on pointer to the LDB message returned by
1102 \return MAPI_E_SUCCESS on success, otherwise MAPI error
1104 _PUBLIC_ enum MAPISTATUS emsabp_search_dn(struct emsabp_context *emsabp_ctx, const char *dn,
1105 struct ldb_message **ldb_res)
1107 struct ldb_dn *ldb_dn = NULL;
1108 struct ldb_result *res = NULL;
1109 const char * const recipient_attrs[] = { "*", NULL };
1113 OPENCHANGE_RETVAL_IF(!dn, MAPI_E_INVALID_PARAMETER, NULL);
1114 OPENCHANGE_RETVAL_IF(!ldb_res, MAPI_E_INVALID_PARAMETER, NULL);
1116 ldb_dn = ldb_dn_new(emsabp_ctx->mem_ctx, emsabp_ctx->samdb_ctx, dn);
1117 OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, NULL);
1119 ret = ldb_search(emsabp_ctx->samdb_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn,
1120 LDB_SCOPE_BASE, recipient_attrs, NULL);
1121 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count || res->count != 1, MAPI_E_CORRUPT_STORE, NULL);
1123 *ldb_res = res->msgs[0];
1125 return MAPI_E_SUCCESS;
1130 \details Search for a given AD record given its legacyDN parameter
1131 and return the associated LDB message.
1133 \param emsabp_ctx pointer to the EMSABP context
1134 \param legacyDN pointer to the legacyDN attribute value to lookup
1135 \param ldb_res pointer on pointer to the LDB message returned by
1137 \param pbUseConfPartition pointer on boolean specifying whether the
1138 legacyExchangeDN was retrieved from the Configuration parition or
1141 \return MAPI_E_SUCCESS on success, otherwise MAPI error
1143 _PUBLIC_ enum MAPISTATUS emsabp_search_legacyExchangeDN(struct emsabp_context *emsabp_ctx, const char *legacyDN,
1144 struct ldb_message **ldb_res, bool *pbUseConfPartition)
1146 const char * const recipient_attrs[] = { "*", NULL };
1148 struct ldb_result *res = NULL;
1151 OPENCHANGE_RETVAL_IF(!legacyDN, MAPI_E_INVALID_PARAMETER, NULL);
1152 OPENCHANGE_RETVAL_IF(!ldb_res, MAPI_E_INVALID_PARAMETER, NULL);
1153 OPENCHANGE_RETVAL_IF(!pbUseConfPartition, MAPI_E_INVALID_PARAMETER, NULL);
1155 *pbUseConfPartition = true;
1156 ret = ldb_search(emsabp_ctx->samdb_ctx, emsabp_ctx->mem_ctx, &res,
1157 ldb_get_config_basedn(emsabp_ctx->samdb_ctx),
1158 LDB_SCOPE_SUBTREE, recipient_attrs, "(legacyExchangeDN=%s)",
1161 if (ret != LDB_SUCCESS || res->count == 0) {
1162 *pbUseConfPartition = false;
1163 ret = ldb_search(emsabp_ctx->samdb_ctx, emsabp_ctx->mem_ctx, &res,
1164 ldb_get_default_basedn(emsabp_ctx->samdb_ctx),
1165 LDB_SCOPE_SUBTREE, recipient_attrs, "(legacyExchangeDN=%s)",
1168 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count, MAPI_E_NOT_FOUND, NULL);
1170 *ldb_res = res->msgs[0];
1172 return MAPI_E_SUCCESS;
1177 \details Fetch Address Book container record for given ContainerID
1179 \param mem_ctx memory context for allocation
1180 \param emsabp_ctx pointer to the EMSABP context
1181 \param ContainerID id of the container to fetch
1182 \param ldb_msg pointer on pointer to the LDB message returned by
1185 \return MAPI_E_SUCCESS on success, otherwise MAPI_ERROR
1187 _PUBLIC_ enum MAPISTATUS emsabp_ab_container_by_id(TALLOC_CTX *mem_ctx,
1188 struct emsabp_context *emsabp_ctx,
1189 uint32_t ContainerID,
1190 struct ldb_message **ldb_msg)
1194 const char * const recipient_attrs[] = { "globalAddressList", NULL };
1195 struct ldb_result *res = NULL;
1198 /* if GAL is requested */
1199 ret = ldb_search(emsabp_ctx->samdb_ctx, mem_ctx, &res,
1200 ldb_get_config_basedn(emsabp_ctx->samdb_ctx),
1201 LDB_SCOPE_SUBTREE, recipient_attrs, "(globalAddressList=*)");
1202 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count, MAPI_E_CORRUPT_STORE, NULL);
1204 /* TODO: If more than one GAL, determine the most appropriate */
1206 dn = (char *) ldb_msg_find_attr_as_string(res->msgs[0], "globalAddressList", NULL);
1207 OPENCHANGE_RETVAL_IF(!dn, MAPI_E_CORRUPT_STORE, NULL);
1209 /* fetch a container we have already recorded */
1210 ret = emsabp_tdb_fetch_dn_from_MId(mem_ctx, emsabp_ctx->tdb_ctx, ContainerID, &dn);
1211 OPENCHANGE_RETVAL_IF(!MAPI_STATUS_IS_OK(ret), MAPI_E_INVALID_BOOKMARK, NULL);
1214 ret = emsabp_search_dn(emsabp_ctx, dn, ldb_msg);
1215 OPENCHANGE_RETVAL_IF(!MAPI_STATUS_IS_OK(ret), MAPI_E_CORRUPT_STORE, NULL);
1217 return MAPI_E_SUCCESS;
1222 \details Enumerate AB container entries
1224 \param mem_ctx pointer to the memory context
1225 \param emsabp_ctx pointer to the EMSABP context
1226 \param ContainerID id of the container to fetch
1227 \param ldb_res pointer on pointer to the LDB result returned by the
1230 \return MAPI_E_SUCCESS on success, otherwise MAPI error
1232 _PUBLIC_ enum MAPISTATUS emsabp_ab_container_enum(TALLOC_CTX *mem_ctx,
1233 struct emsabp_context *emsabp_ctx,
1234 uint32_t ContainerID,
1235 struct ldb_result **ldb_res)
1237 enum MAPISTATUS retval;
1239 struct ldb_message *ldb_msg_ab;
1240 const char *purportedSearch;
1241 const char * const recipient_attrs[] = { "*", NULL };
1243 /* Fetch AB container record */
1244 retval = emsabp_ab_container_by_id(mem_ctx, emsabp_ctx, ContainerID, &ldb_msg_ab);
1245 OPENCHANGE_RETVAL_IF(!MAPI_STATUS_IS_OK(retval), MAPI_E_INVALID_BOOKMARK, NULL);
1247 purportedSearch = ldb_msg_find_attr_as_string(ldb_msg_ab, "purportedSearch", NULL);
1248 if (!purportedSearch) {
1249 *ldb_res = talloc_zero(mem_ctx, struct ldb_result);
1250 return MAPI_E_SUCCESS;
1252 OPENCHANGE_RETVAL_IF(!purportedSearch, MAPI_E_INVALID_BOOKMARK, NULL);
1254 /* Search AD with purportedSearch filter */
1255 ldb_ret = ldb_search(emsabp_ctx->samdb_ctx, mem_ctx, ldb_res,
1256 ldb_get_default_basedn(emsabp_ctx->samdb_ctx),
1257 LDB_SCOPE_SUBTREE, recipient_attrs,
1258 "%s", purportedSearch);
1260 return (ldb_ret != LDB_SUCCESS) ? MAPI_E_NOT_FOUND : MAPI_E_SUCCESS;