nspi: use PropertyTagArray_r for minimal ids.
[jelmer/openchange.git] / mapiproxy / servers / default / nspi / emsabp.c
1 /*
2    OpenChange Server implementation.
3
4    EMSABP: Address Book Provider implementation
5
6    Copyright (C) Julien Kerihuel 2006-2011.
7    Copyright (C) Pauline Khun 2006.
8
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.
13    
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.
18    
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/>.
21  */
22
23 /**
24    \file emsabp.c
25
26    \brief Address Book Provider implementation
27  */
28
29 #include "mapiproxy/dcesrv_mapiproxy.h"
30 #include "dcesrv_exchange_nsp.h"
31
32 /**
33    \details Initialize the EMSABP context and open connections to
34    Samba databases.
35
36    \param lp_ctx pointer to the loadparm context
37    \param tdb_ctx pointer to the EMSABP TDB context
38
39    \return Allocated emsabp_context on success, otherwise NULL
40  */
41 _PUBLIC_ struct emsabp_context *emsabp_init(struct loadparm_context *lp_ctx,
42                                             TDB_CONTEXT *tdb_ctx)
43 {
44         TALLOC_CTX              *mem_ctx;
45         struct emsabp_context   *emsabp_ctx;
46         struct tevent_context   *ev;
47
48         /* Sanity checks */
49         if (!lp_ctx) return NULL;
50
51         mem_ctx = talloc_named(NULL, 0, "emsabp_init");
52         
53         emsabp_ctx = talloc_zero(mem_ctx, struct emsabp_context);
54         if (!emsabp_ctx) {
55                 talloc_free(mem_ctx);
56                 return NULL;
57         }
58
59         emsabp_ctx->mem_ctx = mem_ctx;
60
61         ev = tevent_context_init(mem_ctx);
62         if (!ev) {
63                 talloc_free(mem_ctx);
64                 return NULL;
65         }
66
67         /* Save a pointer to the loadparm context */
68         emsabp_ctx->lp_ctx = lp_ctx;
69
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) {
73                 talloc_free(mem_ctx);
74                 DEBUG(0, ("[%s:%d]: Connection to \"sam.ldb\" failed\n", __FUNCTION__, __LINE__));
75                 return NULL;
76         }
77
78         /* Reference the global TDB context to the current emsabp context */
79         emsabp_ctx->tdb_ctx = tdb_ctx;
80
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");
86         }
87
88         return emsabp_ctx;
89 }
90
91
92 _PUBLIC_ bool emsabp_destructor(void *data)
93 {
94         struct emsabp_context   *emsabp_ctx = (struct emsabp_context *)data;
95
96         if (emsabp_ctx) {
97                 if (emsabp_ctx->ttdb_ctx) {
98                         tdb_close(emsabp_ctx->ttdb_ctx);
99                 }
100
101                 talloc_free(emsabp_ctx->mem_ctx);
102                 return true;
103         }
104
105         return false;
106 }
107
108 /**
109    \details Get AD record for Mail-enabled account
110
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
115
116    \return MAPI_E_SUCCESS on success, otherwise
117    MAPI_E_NOT_ENOUGH_RESOURCES, MAPI_E_NOT_FOUND or
118    MAPI_E_CORRUPT_STORE
119  */
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)
124 {
125         int                     ret;
126         int                     msExchUserAccountControl;
127         struct ldb_result       *res = NULL;
128         const char * const      recipient_attrs[] = { "*", NULL };
129
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",
133                          username);
134         OPENCHANGE_RETVAL_IF((ret != LDB_SUCCESS || !res->count), MAPI_E_NOT_FOUND, NULL);
135
136         /* Check if more than one record was found */
137         OPENCHANGE_RETVAL_IF(res->count != 1, MAPI_E_CORRUPT_STORE, NULL);
138
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;
143         case 0:
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;
151         }
152         
153         /* We should never get here anyway */
154         return MAPI_E_CORRUPT_STORE;
155 }
156
157 /**
158    \details Check if the authenticated user belongs to the Exchange
159    organization
160
161    \param dce_call pointer to the session context
162    \param emsabp_ctx pointer to the EMSABP context
163
164    \return true on success, otherwise false
165  */
166 _PUBLIC_ bool emsabp_verify_user(struct dcesrv_call_state *dce_call,
167                                  struct emsabp_context *emsabp_ctx)
168 {
169         int                     ret;
170         TALLOC_CTX              *mem_ctx;
171         const char              *username = NULL;
172         struct ldb_message      *ldb_msg = NULL;
173
174         username = dcesrv_call_account_name(dce_call);
175
176         mem_ctx = talloc_named(emsabp_ctx->mem_ctx, 0, "emsabp_verify_user");
177
178         ret = emsabp_get_account_info(mem_ctx, emsabp_ctx, username, &ldb_msg);
179         
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);
183         }
184
185         talloc_free(mem_ctx);
186         return MAPI_STATUS_IS_OK(ret);
187 }
188
189
190 /**
191    \details Check if the provided codepage is correct
192
193    \param emsabp_ctx pointer to the EMSABP context
194    \param CodePage the codepage identifier
195
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.
199
200    \return true on success, otherwise false
201  */
202 _PUBLIC_ bool emsabp_verify_codepage(struct emsabp_context *emsabp_ctx,
203                                      uint32_t CodePage)
204 {
205         return mapi_verify_cpid(CodePage);
206 }
207
208
209 /**
210    \details Build an EphemeralEntryID structure
211
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
216    function
217
218    \return MAPI_E_SUCCESS on success, otherwise
219    MAPI_E_NOT_ENOUGH_RESOURCES or MAPI_E_CORRUPT_STORE
220  */
221 _PUBLIC_ enum MAPISTATUS emsabp_set_EphemeralEntryID(struct emsabp_context *emsabp_ctx,
222                                                      uint32_t DisplayType, uint32_t MId,
223                                                      struct EphemeralEntryID *ephEntryID)
224 {
225         struct GUID     *guid = (struct GUID *) NULL;
226
227         /* Sanity checks */
228         OPENCHANGE_RETVAL_IF(!ephEntryID, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
229
230         guid = (struct GUID *) samdb_ntds_objectGUID(emsabp_ctx->samdb_ctx);
231         OPENCHANGE_RETVAL_IF(!guid, MAPI_E_CORRUPT_STORE, NULL);
232
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;
250
251         talloc_free(guid);
252
253         return MAPI_E_SUCCESS;
254 }
255
256
257 /**
258    \details Map an EphemeralEntryID structure into a Binary_r structure
259
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
263
264    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_INVALID_PARAMETER
265  */
266 _PUBLIC_ enum MAPISTATUS emsabp_EphemeralEntryID_to_Binary_r(TALLOC_CTX *mem_ctx,
267                                                              struct EphemeralEntryID *ephEntryID,
268                                                              struct Binary_r *bin)
269 {
270         /* Sanity checks */
271         OPENCHANGE_RETVAL_IF(!ephEntryID, MAPI_E_INVALID_PARAMETER, NULL);
272         OPENCHANGE_RETVAL_IF(!bin, MAPI_E_INVALID_PARAMETER, NULL);
273
274         bin->cb = sizeof (*ephEntryID);
275         bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
276
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);
296
297         return MAPI_E_SUCCESS;
298 }
299
300
301 /**
302    \details Build a PermanentEntryID structure
303
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
308    function
309
310    \note This function only covers DT_CONTAINER AddressBook
311    objects. It should be extended in the future to support more
312    containers.
313
314    \return MAPI_E_SUCCESS on success, otherwise
315    MAPI_E_NOT_ENOUGH_RESOURCES or MAPI_E_CORRUPT_STORE
316  */
317 _PUBLIC_ enum MAPISTATUS emsabp_set_PermanentEntryID(struct emsabp_context *emsabp_ctx, 
318                                                      uint32_t DisplayType, struct ldb_message *msg, 
319                                                      struct PermanentEntryID *permEntryID)
320 {
321         struct GUID             *guid = (struct GUID *) NULL;
322         const struct ldb_val    *ldb_value = NULL;
323         const char              *dn_str;
324
325         /* Sanity checks */
326         OPENCHANGE_RETVAL_IF(!permEntryID, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
327         
328
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;
336
337         if (!msg) {
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);
342
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,
348                                                   guid->clock_seq[0],
349                                                   guid->clock_seq[1],
350                                                   guid->node[0], guid->node[1],
351                                                   guid->node[2], guid->node[3],
352                                                   guid->node[4], guid->node[5]);
353                 talloc_free(guid);
354
355         }  else {
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);
359         }
360
361         return MAPI_E_SUCCESS;
362 }
363
364
365 /**
366    \details Map a PermanentEntryID structure into a Binary_r
367    structure (for PR_ENTRYID and PR_EMS_AB_PARENT_ENTRYID properties)
368
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
372
373    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_INVALID_PARAMETER
374  */
375 _PUBLIC_ enum MAPISTATUS emsabp_PermanentEntryID_to_Binary_r(TALLOC_CTX *mem_ctx,
376                                                              struct PermanentEntryID *permEntryID,
377                                                              struct Binary_r *bin)
378 {
379         /* Sanity checks */
380         OPENCHANGE_RETVAL_IF(!permEntryID, MAPI_E_INVALID_PARAMETER, NULL);
381         OPENCHANGE_RETVAL_IF(!bin, MAPI_E_INVALID_PARAMETER, NULL);
382
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);
386
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);
403
404         return MAPI_E_SUCCESS;
405 }
406
407
408 /**
409    \details Find the attribute matching the specified property tag and
410    return the associated data.
411
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
420
421
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.
425
426    \return Valid generic pointer on success, otherwise NULL
427  */
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,
430                             uint32_t dwFlags)
431 {
432         enum MAPISTATUS                 retval;
433         void                            *data = (void *) NULL;
434         const char                      *attribute;
435         const char                      *ref_attribute;
436         const char                      *ldb_string = NULL;
437         char                            *tmp_str;
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;
444         int                             ret;
445         const char                      *dn = NULL;
446         uint32_t                        i;
447
448         /* Step 1. Fill attributes not in AD but created using EMSABP databases */
449         switch (ulPropTag) {
450         case PR_ADDRTYPE:
451         case PR_ADDRTYPE_UNICODE:
452                 data = (void *) talloc_strdup(mem_ctx, EMSABP_ADDRTYPE);
453                 return data;
454         case PR_OBJECT_TYPE:
455                 data = talloc_zero(mem_ctx, uint32_t);
456                 *((uint32_t *)data) = MAPI_MAILUSER;
457                 return data;
458         case PR_DISPLAY_TYPE:
459                 data = talloc_zero(mem_ctx, uint32_t);
460                 *((uint32_t *)data) = DT_MAILUSER;
461                 return data;
462         case PR_ENTRYID:
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);
467                 } else {
468                         retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_MAILUSER, msg, &permEntryID);
469                         retval = emsabp_PermanentEntryID_to_Binary_r(mem_ctx, &permEntryID, bin);
470                 }
471                 return bin;
472         case PR_SEARCH_KEY:
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);
483                 return bin;
484         case PR_INSTANCE_KEY:
485                 bin = talloc_zero(mem_ctx, struct Binary_r);
486                 bin->cb = 4;
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;
493                 return bin;
494         default:
495                 break;
496         }
497
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;
501
502         ret = emsabp_property_is_ref(ulPropTag);
503         if (ret == 1) {
504                 ref_attribute = emsabp_property_get_ref_attr(ulPropTag);
505                 if (ref_attribute) {
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) {
509                                 return NULL;
510                         }
511                         attribute = ref_attribute;
512                 }
513         } else {
514                 msg2 = msg;
515         }
516
517         /* Step 3. Retrieve data associated to the attribute/tag */
518         switch (ulPropTag & 0xFFFF) {
519         case PT_STRING8:
520         case PT_UNICODE:
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);
524                 break;
525         case PT_MV_STRING8:
526         case PT_MV_UNICODE:
527                 ldb_element = ldb_msg_find_element(msg2, attribute);
528                 if (!ldb_element) return NULL;
529
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);
535                 }
536                 data = (void *) mvszA;
537                 break;
538         default:
539                 DEBUG(3, ("[%s:%d]: Unsupported property type: 0x%x\n", __FUNCTION__, __LINE__,
540                           (ulPropTag & 0xFFFF)));
541                 break;
542         }
543
544         return data;
545 }
546
547
548 /**
549    \details Build the SRow array entry for the specified ldb_message.
550
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
558
559    \return MAPI_E_SUCCESS on success, otherwise MAPI error
560  */
561 _PUBLIC_ enum MAPISTATUS emsabp_fetch_attrs_from_msg(TALLOC_CTX *mem_ctx,
562                                                      struct emsabp_context *emsabp_ctx,
563                                                      struct SRow *aRow,
564                                                      struct ldb_message *ldb_msg,
565                                                      uint32_t MId, uint32_t dwFlags,
566                                                      struct SPropTagArray *pPropTags)
567 {
568         enum MAPISTATUS retval;
569         const char      *dn;
570         void            *data;
571         uint32_t        ulPropTag;
572         uint32_t        i;
573
574         /* Step 0. Create MId if necessary */
575         if (MId == 0) {
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);
579                 if (retval) {
580                         retval = emsabp_tdb_insert(emsabp_ctx->ttdb_ctx, dn);
581                         OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
582
583                         retval = emsabp_tdb_fetch_MId(emsabp_ctx->ttdb_ctx, dn, &MId);
584                         OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
585                 }
586         }
587
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);
592
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);
596                 if (!data) {
597                         ulPropTag = (ulPropTag & 0xFFFF0000) | PT_ERROR;
598                 }
599                 
600                 aRow->lpProps[i].ulPropTag = (enum MAPITAGS) ulPropTag;
601                 aRow->lpProps[i].dwAlignPad = 0x0;
602                 set_SPropValue(&(aRow->lpProps[i]), data);
603         }
604
605         return MAPI_E_SUCCESS;
606 }
607
608 /**
609    \details Builds the SRow array entry for the specified MId.
610
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
614    this record.
615
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
619    stored
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
625
626    \note We currently assume records are users.ldb
627
628    \return MAPI_E_SUCCESS on success, otherwise MAPI error
629  */
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)
633 {
634         enum MAPISTATUS         retval;
635         char                    *dn;
636         const char * const      recipient_attrs[] = { "*", NULL };
637         struct ldb_result       *res = NULL;
638         struct ldb_dn           *ldb_dn = NULL;
639         int                     ret;
640         uint32_t                ulPropTag;
641         void                    *data;
642         uint32_t                i;
643
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);
649         }
650         OPENCHANGE_RETVAL_IF(retval, MAPI_E_INVALID_BOOKMARK, NULL);
651
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);
655
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);
659
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);
664
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);
668                 if (!data) {
669                         ulPropTag &= 0xFFFF0000;
670                         ulPropTag += PT_ERROR;
671                 }
672
673                 aRow->lpProps[i].ulPropTag = (enum MAPITAGS) ulPropTag;
674                 aRow->lpProps[i].dwAlignPad = 0x0;
675                 set_SPropValue(&(aRow->lpProps[i]), data);
676         }
677
678
679         return MAPI_E_SUCCESS;
680 }
681
682
683 /**
684    \details Builds the SRow array entry for the specified table
685    record.
686
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
690    stored
691    \param dwFlags flags controlling whether strings should be unicode
692    encoded or not
693    \param permEntryID pointer to the current record Permanent
694    EntryID
695    \param parentPermEntryID pointer to the parent record Permanent
696    EntryID
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
700
701    \return MAPI_E_SUCCESS on success, otherwise MAPI error
702  */
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)
708 {
709         enum MAPISTATUS                 retval;
710         struct SPropTagArray            *SPropTagArray;
711         struct SPropValue               lpProps;
712         int                             proptag;
713         uint32_t                        i;
714         uint32_t                        containerID = 0;
715         const char                      *dn = NULL;
716
717         /* Step 1. Build the array of properties to fetch and map */
718         if (child == false) {
719                 SPropTagArray = set_SPropTagArray(mem_ctx, 0x6,
720                                                   PR_ENTRYID,
721                                                   PR_CONTAINER_FLAGS,
722                                                   PR_DEPTH,
723                                                   PR_EMS_AB_CONTAINERID,
724                                                   ((dwFlags & NspiUnicodeStrings) ? PR_DISPLAY_NAME_UNICODE : PR_DISPLAY_NAME),
725                                                   PR_EMS_AB_IS_MASTER);
726         } else {
727                 SPropTagArray = set_SPropTagArray(mem_ctx, 0x7,
728                                                   PR_ENTRYID,
729                                                   PR_CONTAINER_FLAGS,
730                                                   PR_DEPTH,
731                                                   PR_EMS_AB_CONTAINERID,
732                                                   ((dwFlags & NspiUnicodeStrings) ? PR_DISPLAY_NAME_UNICODE : PR_DISPLAY_NAME),
733                                                   PR_EMS_AB_IS_MASTER,
734                                                   PR_EMS_AB_PARENT_ENTRYID);
735         }
736
737         /* Step 2. Allocate SPropValue array and update SRow cValues field */
738         aRow->ulAdrEntryPad = 0x0;
739         aRow->cValues = 0x0;
740         aRow->lpProps = talloc_zero(mem_ctx, struct SPropValue);
741
742         /* Step 3. Global Address List or real container */
743         if (!msg) {
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;
748
749                         switch (SPropTagArray->aulPropTag[i]) {
750                         case PR_ENTRYID:
751                                 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, permEntryID, &(lpProps.value.bin));
752                                 break;
753                         case PR_CONTAINER_FLAGS:
754                                 lpProps.value.l =  AB_RECIPIENTS | AB_UNMODIFIABLE;
755                                 break;
756                         case PR_DEPTH:
757                                 lpProps.value.l = 0x0;
758                                 break;
759                         case PR_EMS_AB_CONTAINERID:
760                                 lpProps.value.l = 0x0;
761                                 break;
762                         case PR_DISPLAY_NAME:
763                                 lpProps.value.lpszA = NULL;
764                                 break;
765                         case PR_DISPLAY_NAME_UNICODE:
766                                 lpProps.value.lpszW = NULL;
767                                 break;
768                         case PR_EMS_AB_IS_MASTER:
769                                 lpProps.value.b = false;
770                                 break;
771                         default:
772                                 break;
773                         }
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;
780                         }
781                 }
782         } else {
783                 for (i = 0; i < SPropTagArray->cValues; i++) {
784                         lpProps.ulPropTag = SPropTagArray->aulPropTag[i];
785                         lpProps.dwAlignPad = 0x0;
786
787                         switch (SPropTagArray->aulPropTag[i]) {
788                         case PR_ENTRYID:
789                                 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, permEntryID, &(lpProps.value.bin));
790                                 break;
791                         case PR_CONTAINER_FLAGS:
792                                 switch (child) {
793                                 case true:
794                                         lpProps.value.l = AB_RECIPIENTS | AB_SUBCONTAINERS | AB_UNMODIFIABLE;
795                                         break;
796                                 case false:
797                                         lpProps.value.l = AB_RECIPIENTS | AB_UNMODIFIABLE;
798                                 }
799                                 break;
800                         case PR_DEPTH:
801                                 switch (child) {
802                                 case true:
803                                         lpProps.value.l = 0x1;
804                                         break;
805                                 case false:
806                                         lpProps.value.l = 0x0;
807                                         break;
808                                 }
809                                 break;
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);
813                                 if (retval) {
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);
818                                 }
819                                 lpProps.value.l = containerID;
820                                 break;
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;
826                                         proptag += PT_ERROR;
827                                         lpProps.ulPropTag = (enum MAPITAGS) proptag;
828                                 }
829                                 break;
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;
835                                         proptag += PT_ERROR;
836                                         lpProps.ulPropTag = (enum MAPITAGS) proptag;
837                                 }
838                                 break;
839                         case PR_EMS_AB_IS_MASTER:
840                                 /* FIXME: harcoded value - no load balancing */
841                                 lpProps.value.l = 0x0;
842                                 break;
843                         case PR_EMS_AB_PARENT_ENTRYID:
844                                 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, parentPermEntryID, &lpProps.value.bin);
845                                 break;
846                         default:
847                                 break;
848                         }
849                         SRow_addprop(aRow, lpProps);
850                 }
851         }
852
853         return MAPI_E_SUCCESS;
854 }
855
856
857 /**
858    \details Retrieve and build the HierarchyTable requested by
859    GetSpecialTable NSPI call
860
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
864    or not
865    \param SRowSet pointer on pointer to the output SRowSet array
866
867    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_CORRUPT_STORE
868  */
869 _PUBLIC_ enum MAPISTATUS emsabp_get_HierarchyTable(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
870                                                    uint32_t dwFlags, struct SRowSet **SRowSet)
871 {
872         enum MAPISTATUS                 retval;
873         struct SRow                     *aRow;
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;
885         int                             ret;
886         uint32_t                        aRow_idx;
887         uint32_t                        i;
888
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);
892         aRow_idx = 0;
893
894         retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, NULL, &gal);
895         OPENCHANGE_RETVAL_IF(retval, retval, aRow);
896
897         retval = emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &gal, NULL, NULL, false);
898         aRow_idx++;
899
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);
905
906         addressBookRoots = ldb_msg_find_attr_as_string(res->msgs[0], "addressBookRoots", NULL);
907         OPENCHANGE_RETVAL_IF(!addressBookRoots, MAPI_E_CORRUPT_STORE, aRow);
908
909         ldb_dn = ldb_dn_new(emsabp_ctx->mem_ctx, emsabp_ctx->samdb_ctx, addressBookRoots);
910         talloc_free(res);
911         OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, aRow);
912
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);
917
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);
921         aRow_idx++;
922         talloc_free(res);
923
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);
927
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);
932
933         if (ret != LDB_SUCCESS) {
934                 talloc_free(res);
935                 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS, MAPI_E_CORRUPT_STORE, aRow);
936         }
937
938         ret = ldb_request(emsabp_ctx->samdb_ctx, req);
939         if (ret == LDB_SUCCESS) {
940                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
941         }
942         talloc_free(req);
943         
944         if (ret != LDB_SUCCESS || !res->count) {
945                 talloc_free(res);
946                 OPENCHANGE_RETVAL_IF(1, MAPI_E_CORRUPT_STORE, aRow);
947         }
948
949         aRow = talloc_realloc(mem_ctx, aRow, struct SRow, aRow_idx + res->count + 1);
950
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));
956                 aRow_idx++;
957         }
958         talloc_free(res);
959         talloc_free(parentPermEntryID.dn);
960
961         /* Step 4. Build output SRowSet */
962         SRowSet[0]->cRows = aRow_idx;
963         SRowSet[0]->aRow = aRow;
964
965         return MAPI_E_SUCCESS;
966 }
967
968
969 /**
970    \details Retrieve and build the CreationTemplates Table requested
971    by GetSpecialTable NSPI call
972
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
976    or not
977    \param SRowSet pointer on pointer to the output SRowSet array
978
979    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_CORRUPT_STORE 
980  */
981 _PUBLIC_ enum MAPISTATUS emsabp_get_CreationTemplatesTable(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
982                                                            uint32_t dwFlags, struct SRowSet **SRowSet)
983 {
984         return MAPI_E_SUCCESS;
985 }
986
987
988 /**
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.
992
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
997    search
998    \param pStat pointer the STAT structure associated to the search
999    \param limit the limit number of results the function can return
1000
1001    \note SortTypePhoneticDisplayName sort type is currently not supported.
1002
1003    \return MAPI_E_SUCCESS on success, otherwise MAPI error
1004  */
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)
1008 {
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 };
1014         int                             ret;
1015         uint32_t                        i;
1016         const char                      *dn;
1017
1018         /* Step 0. Sanity Checks (MS-NSPI Server Processing Rules) */
1019         if (pStat->SortType == SortTypePhoneticDisplayName) {
1020                 return MAPI_E_CALL_FAILED;
1021         }
1022
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;
1026         }
1027
1028         if (restriction && (pStat->SortType != SortTypeDisplayName) && 
1029             (pStat->SortType != SortTypePhoneticDisplayName)) {
1030                 return MAPI_E_CALL_FAILED;
1031         }
1032
1033         /* Step 1. Apply restriction and retrieve results from AD */
1034         if (restriction) {
1035                 /* FIXME: We only support RES_PROPERTY restriction */
1036                 if ((uint32_t)restriction->rt != RES_PROPERTY) {
1037                         return MAPI_E_TOO_COMPLEX;
1038                 }
1039
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;
1044                 }
1045                 
1046                 recipient = (res_prop->ulPropTag == PR_ANR) ?
1047                         res_prop->lpProp->value.lpszA :
1048                         res_prop->lpProp->value.lpszW;
1049
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)))",
1054                                  recipient);
1055
1056                 if (ret != LDB_SUCCESS) {
1057                         return MAPI_E_NOT_FOUND;
1058                 }
1059                 if (res == NULL) {
1060                         return MAPI_E_INVALID_OBJECT;
1061                 }
1062                 if (!res->count) {
1063                         return MAPI_E_NOT_FOUND;
1064                 }
1065         } else {
1066                 /* FIXME Check restriction == NULL */
1067                 return MAPI_E_INVALID_OBJECT;
1068         }
1069
1070         if (limit && res->count > limit) {
1071                 return MAPI_E_TABLE_TOO_BIG;
1072         }
1073
1074         MIds->aulPropTag = talloc_array(emsabp_ctx->mem_ctx, uint32_t, res->count);
1075         MIds->cValues = res->count;
1076
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]));
1081                 if (retval) {
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);
1086                 }
1087         }
1088
1089         return MAPI_E_SUCCESS;
1090 }
1091
1092
1093 /**
1094    \details Search for a given DN within AD and return the associated
1095    LDB message.
1096
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
1100    the function
1101
1102    \return MAPI_E_SUCCESS on success, otherwise MAPI error
1103  */
1104 _PUBLIC_ enum MAPISTATUS emsabp_search_dn(struct emsabp_context *emsabp_ctx, const char *dn, 
1105                                           struct ldb_message **ldb_res)
1106 {
1107         struct ldb_dn           *ldb_dn = NULL;
1108         struct ldb_result       *res = NULL;
1109         const char * const      recipient_attrs[] = { "*", NULL };
1110         int                     ret;
1111
1112         /* Sanity Checks */
1113         OPENCHANGE_RETVAL_IF(!dn, MAPI_E_INVALID_PARAMETER, NULL);
1114         OPENCHANGE_RETVAL_IF(!ldb_res, MAPI_E_INVALID_PARAMETER, NULL);
1115
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);
1118
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);
1122
1123         *ldb_res = res->msgs[0];
1124
1125         return MAPI_E_SUCCESS;
1126 }
1127
1128
1129 /**
1130    \details Search for a given AD record given its legacyDN parameter
1131    and return the associated LDB message.
1132
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 
1136    the function
1137    \param pbUseConfPartition pointer on boolean specifying whether the
1138    legacyExchangeDN was retrieved from the Configuration parition or
1139    not
1140
1141    \return MAPI_E_SUCCESS on success, otherwise MAPI error
1142  */
1143 _PUBLIC_ enum MAPISTATUS emsabp_search_legacyExchangeDN(struct emsabp_context *emsabp_ctx, const char *legacyDN,
1144                                                         struct ldb_message **ldb_res, bool *pbUseConfPartition)
1145 {
1146         const char * const      recipient_attrs[] = { "*", NULL };
1147         int                     ret;
1148         struct ldb_result       *res = NULL;
1149
1150         /* Sanity Checks */
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);
1154
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)",
1159                          legacyDN);
1160
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)",
1166                                  legacyDN);
1167         }
1168         OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count, MAPI_E_NOT_FOUND, NULL);
1169
1170         *ldb_res = res->msgs[0];
1171
1172         return MAPI_E_SUCCESS;
1173 }
1174
1175
1176 /**
1177    \details Fetch Address Book container record for given ContainerID
1178
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
1183    the function
1184
1185    \return MAPI_E_SUCCESS on success, otherwise MAPI_ERROR
1186  */
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)
1191 {
1192         int                     ret;
1193         char                    *dn;
1194         const char * const      recipient_attrs[] = { "globalAddressList", NULL };
1195         struct ldb_result       *res = NULL;
1196
1197         if (!ContainerID) {
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);
1203
1204                 /* TODO: If more than one GAL, determine the most appropriate */
1205
1206                 dn = (char *) ldb_msg_find_attr_as_string(res->msgs[0], "globalAddressList", NULL);
1207                 OPENCHANGE_RETVAL_IF(!dn, MAPI_E_CORRUPT_STORE, NULL);
1208         } else {
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);
1212         }
1213
1214         ret = emsabp_search_dn(emsabp_ctx, dn, ldb_msg);
1215         OPENCHANGE_RETVAL_IF(!MAPI_STATUS_IS_OK(ret), MAPI_E_CORRUPT_STORE, NULL);
1216
1217         return MAPI_E_SUCCESS;
1218 }
1219
1220
1221 /**
1222    \details Enumerate AB container entries
1223
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
1228    function
1229
1230    \return MAPI_E_SUCCESS on success, otherwise MAPI error
1231  */
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)
1236 {
1237         enum MAPISTATUS         retval;
1238         int                     ldb_ret;
1239         struct ldb_message      *ldb_msg_ab;
1240         const char              *purportedSearch;
1241         const char * const      recipient_attrs[] = { "*", NULL };
1242
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);
1246
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;
1251         }
1252         OPENCHANGE_RETVAL_IF(!purportedSearch, MAPI_E_INVALID_BOOKMARK, NULL);
1253
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);
1259
1260         return (ldb_ret != LDB_SUCCESS) ? MAPI_E_NOT_FOUND : MAPI_E_SUCCESS;
1261 }