Replace deprecated talloc_init calls with talloc_named
[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-2009.
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 "mapiproxy/libmapiproxy.h"
31 #include "dcesrv_exchange_nsp.h"
32 #include <ldb.h>
33 #include <ldb_errors.h>
34 #include <util/debug.h>
35
36 /**
37    \details Initialize the EMSABP context and open connections to
38    Samba databases.
39
40    \param lp_ctx pointer to the loadparm context
41    \param tdb_ctx pointer to the EMSABP TDB context
42
43    \return Allocated emsabp_context on success, otherwise NULL
44  */
45 _PUBLIC_ struct emsabp_context *emsabp_init(struct loadparm_context *lp_ctx,
46                                             TDB_CONTEXT *tdb_ctx)
47 {
48         TALLOC_CTX              *mem_ctx;
49         struct emsabp_context   *emsabp_ctx;
50         struct tevent_context   *ev;
51         char                    *configuration = NULL;
52         char                    *users = NULL;
53         int                     ret;
54
55         /* Sanity checks */
56         if (!lp_ctx) return NULL;
57
58         mem_ctx = talloc_named(NULL, 0, "emsabp_init");
59         
60         emsabp_ctx = talloc_zero(mem_ctx, struct emsabp_context);
61         if (!emsabp_ctx) {
62                 talloc_free(mem_ctx);
63                 return NULL;
64         }
65
66         emsabp_ctx->mem_ctx = mem_ctx;
67
68         ev = tevent_context_init(mem_ctx);
69         if (!ev) {
70                 talloc_free(mem_ctx);
71                 return NULL;
72         }
73
74         /* Save a pointer to the loadparm context */
75         emsabp_ctx->lp_ctx = lp_ctx;
76
77         /* Return an opaque context pointer on the configuration database */
78         configuration = private_path(mem_ctx, lp_ctx, "configuration.ldb");
79         emsabp_ctx->conf_ctx = ldb_init(mem_ctx, ev);
80         if (!emsabp_ctx->conf_ctx) {
81                 talloc_free(configuration);
82                 talloc_free(mem_ctx);
83                 return NULL;
84         }
85
86         ret = ldb_connect(emsabp_ctx->conf_ctx, configuration, LDB_FLG_RDONLY, NULL);
87         talloc_free(configuration);
88         if (ret != LDB_SUCCESS) {
89                 DEBUG(0, ("[%s:%d]: Connection to \"configuration.ldb\" failed\n", __FUNCTION__, __LINE__));
90                 talloc_free(mem_ctx);
91                 return NULL;
92         }
93
94         /* Return an opaque context pointer to the users database */
95         users = private_path(mem_ctx, lp_ctx, "users.ldb");
96         emsabp_ctx->users_ctx = ldb_init(mem_ctx, ev);
97         if (!emsabp_ctx->users_ctx) {
98                 talloc_free(users);
99                 talloc_free(mem_ctx);
100                 return NULL;
101         }
102
103         ret = ldb_connect(emsabp_ctx->users_ctx, users, LDB_FLG_RDONLY, NULL);
104         talloc_free(users);
105         if (ret != LDB_SUCCESS) {
106                 DEBUG(0, ("[%s:%d]: Connection to \"users.ldb\" failed\n", __FUNCTION__, __LINE__));
107                 talloc_free(mem_ctx);
108                 return NULL;
109         }
110
111         /* Reference the global TDB context to the current emsabp context */
112         emsabp_ctx->tdb_ctx = tdb_ctx;
113
114         /* Initialize a temporary (on-memory) TDB database to store
115          * temporary MId used within EMSABP */
116         emsabp_ctx->ttdb_ctx = emsabp_tdb_init_tmp(emsabp_ctx->mem_ctx);
117         if (!emsabp_ctx->ttdb_ctx) {
118                 smb_panic("unable to create on-memory TDB database");
119         }
120
121         return emsabp_ctx;
122 }
123
124
125 _PUBLIC_ bool emsabp_destructor(void *data)
126 {
127         struct emsabp_context   *emsabp_ctx = (struct emsabp_context *)data;
128
129         if (emsabp_ctx) {
130                 if (emsabp_ctx->ttdb_ctx) {
131                         tdb_close(emsabp_ctx->ttdb_ctx);
132                 }
133
134                 talloc_free(emsabp_ctx->mem_ctx);
135                 return true;
136         }
137
138         return false;
139 }
140
141
142 /**
143    \details Check if the authenticated user belongs to the Exchange
144    organization
145
146    \param dce_call pointer to the session context
147    \param emsabp_ctx pointer to the EMSABP context
148
149    \return true on success, otherwise false
150  */
151 _PUBLIC_ bool emsabp_verify_user(struct dcesrv_call_state *dce_call,
152                                  struct emsabp_context *emsabp_ctx)
153 {
154         int                     ret;
155         const char              *username = NULL;
156         int                     msExchUserAccountControl;
157         enum ldb_scope          scope = LDB_SCOPE_SUBTREE;
158         struct ldb_result       *res = NULL;
159         char                    *ldb_filter;
160         const char * const      recipient_attrs[] = { "msExchUserAccountControl", NULL };
161
162         username = dce_call->context->conn->auth_state.session_info->server_info->account_name;
163
164         ldb_filter = talloc_asprintf(emsabp_ctx->mem_ctx, "CN=%s", username);
165         ret = ldb_search(emsabp_ctx->users_ctx, emsabp_ctx->mem_ctx, &res, 
166                          ldb_get_default_basedn(emsabp_ctx->users_ctx),
167                          scope, recipient_attrs, ldb_filter);
168         talloc_free(ldb_filter);
169
170         /* If the search failed */
171         if (ret != LDB_SUCCESS || !res->count) {
172                 return false;
173         }
174
175         /* If msExchUserAccountControl attribute is not found */
176         if (!res->msgs[0]->num_elements) {
177                 return false;
178         }
179
180         /* If the attribute exists check its value */
181         msExchUserAccountControl = ldb_msg_find_attr_as_int(res->msgs[0], "msExchUserAccountControl", 2);
182         if (msExchUserAccountControl == 2) {
183                 return false;
184         }
185
186         return true;
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 valid_codepage(CodePage);
206 }
207
208
209 /**
210    \details Retrieve the NSPI server GUID from the server object in
211    the configuration LDB database
212
213    \param emsabp_ctx pointer to the EMSABP context
214
215    \return An allocated GUID structure on success, otherwise NULL
216  */
217 _PUBLIC_ struct GUID *emsabp_get_server_GUID(struct emsabp_context *emsabp_ctx)
218 {
219         int                     ret;
220         struct loadparm_context *lp_ctx;
221         struct GUID             *guid = (struct GUID *) NULL;
222         const char              *netbiosname = NULL;
223         const char              *guid_str = NULL;
224         enum ldb_scope          scope = LDB_SCOPE_SUBTREE;
225         struct ldb_result       *res = NULL;
226         char                    *ldb_filter;
227         char                    *dn = NULL;
228         struct ldb_dn           *ldb_dn = NULL;
229         const char * const      recipient_attrs[] = { "*", NULL };
230         const char              *firstorgdn = NULL;
231
232         lp_ctx = emsabp_ctx->lp_ctx;
233
234         netbiosname = lp_netbios_name(lp_ctx);
235         if (!netbiosname) return NULL;
236
237         /* Step 1. Find the Exchange Organization */
238         ldb_filter = talloc_strdup(emsabp_ctx->mem_ctx, "(objectClass=msExchOrganizationContainer)");
239         ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res,
240                          ldb_get_default_basedn(emsabp_ctx->conf_ctx),
241                          scope, recipient_attrs, ldb_filter);
242         talloc_free(ldb_filter);
243
244         if (ret != LDB_SUCCESS || !res->count) {
245                 return NULL;
246         }
247
248         firstorgdn = ldb_msg_find_attr_as_string(res->msgs[0], "distinguishedName", NULL);
249         if (!firstorgdn) {
250                 return NULL;
251         }
252
253         /* Step 2. Find the OpenChange Server object */
254         dn = talloc_asprintf(emsabp_ctx->mem_ctx, "CN=Servers,CN=First Administrative Group,CN=Administrative Groups,%s",
255                              firstorgdn);
256         ldb_dn = ldb_dn_new(emsabp_ctx->mem_ctx, emsabp_ctx->conf_ctx, dn);
257         talloc_free(dn);
258         if (!ldb_dn_validate(ldb_dn)) {
259                 return NULL;
260         }
261
262         ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn, 
263                          scope, recipient_attrs, "(cn=%s)", netbiosname);
264         if (ret != LDB_SUCCESS || !res->count) {
265                 return NULL;
266         }
267
268         /* Step 3. Retrieve the objectGUID GUID */
269         guid_str = ldb_msg_find_attr_as_string(res->msgs[0], "objectGUID", NULL);
270         if (!guid_str) return NULL;
271
272         guid = talloc_zero(emsabp_ctx->mem_ctx, struct GUID);
273         GUID_from_string(guid_str, guid);
274         
275         return guid;
276 }
277
278
279 /**
280    \details Build an EphemeralEntryID structure
281
282    \param emsabp_ctx pointer to the EMSABP context
283    \param DisplayType the AB object display type
284    \param MId the MId value
285    \param ephEntryID pointer to the EphemeralEntryID returned by the
286    function
287
288    \return MAPI_E_SUCCESS on success, otherwise
289    MAPI_E_NOT_ENOUGH_RESOURCES or MAPI_E_CORRUPT_STORE
290  */
291 _PUBLIC_ enum MAPISTATUS emsabp_set_EphemeralEntryID(struct emsabp_context *emsabp_ctx,
292                                                      uint32_t DisplayType, uint32_t MId,
293                                                      struct EphemeralEntryID *ephEntryID)
294 {
295         struct GUID     *guid = (struct GUID *) NULL;
296
297         /* Sanity checks */
298         OPENCHANGE_RETVAL_IF(!ephEntryID, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
299
300         guid = emsabp_get_server_GUID(emsabp_ctx);
301         OPENCHANGE_RETVAL_IF(!guid, MAPI_E_CORRUPT_STORE, NULL);
302
303         ephEntryID->ID_type = 0x87;
304         ephEntryID->R1 = 0x0;
305         ephEntryID->R2 = 0x0;
306         ephEntryID->R3 = 0x0;
307         ephEntryID->ProviderUID.ab[0] = (guid->time_low & 0xFF);
308         ephEntryID->ProviderUID.ab[1] = ((guid->time_low >> 8)  & 0xFF);
309         ephEntryID->ProviderUID.ab[2] = ((guid->time_low >> 16) & 0xFF);
310         ephEntryID->ProviderUID.ab[3] = ((guid->time_low >> 24) & 0xFF);
311         ephEntryID->ProviderUID.ab[4] = (guid->time_mid & 0xFF);
312         ephEntryID->ProviderUID.ab[5] = ((guid->time_mid >> 8)  & 0xFF);
313         ephEntryID->ProviderUID.ab[6] = (guid->time_hi_and_version & 0xFF);
314         ephEntryID->ProviderUID.ab[7] = ((guid->time_hi_and_version >> 8) & 0xFF);
315         memcpy(ephEntryID->ProviderUID.ab + 8,  guid->clock_seq, sizeof (uint8_t) * 2);
316         memcpy(ephEntryID->ProviderUID.ab + 10, guid->node, sizeof (uint8_t) * 6);
317         ephEntryID->R4 = 0x1;
318         ephEntryID->DisplayType = DisplayType;
319         ephEntryID->MId = MId;
320
321         talloc_free(guid);
322
323         return MAPI_E_SUCCESS;
324 }
325
326
327 /**
328    \details Map an EphemeralEntryID structure into a Binary_r structure
329
330    \param mem_ctx pointer to the memory context
331    \param ephEntryID pointer to the Ephemeral EntryID structure
332    \param bin pointer to the Binary_r structure the server will return
333
334    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_INVALID_PARAMETER
335  */
336 _PUBLIC_ enum MAPISTATUS emsabp_EphemeralEntryID_to_Binary_r(TALLOC_CTX *mem_ctx,
337                                                              struct EphemeralEntryID *ephEntryID,
338                                                              struct Binary_r *bin)
339 {
340         /* Sanity checks */
341         OPENCHANGE_RETVAL_IF(!ephEntryID, MAPI_E_INVALID_PARAMETER, NULL);
342         OPENCHANGE_RETVAL_IF(!bin, MAPI_E_INVALID_PARAMETER, NULL);
343
344         bin->cb = sizeof (*ephEntryID);
345         bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
346
347         /* Copy EphemeralEntryID into bin->lpb */
348         memset(bin->lpb, 0, bin->cb);
349         bin->lpb[0] = ephEntryID->ID_type;
350         bin->lpb[1] = ephEntryID->R1;
351         bin->lpb[2] = ephEntryID->R2;
352         bin->lpb[3] = ephEntryID->R3;
353         memcpy(bin->lpb + 4, ephEntryID->ProviderUID.ab, 16);
354         bin->lpb[20] = (ephEntryID->R4 & 0xFF);
355         bin->lpb[21] = ((ephEntryID->R4 >> 8)  & 0xFF);
356         bin->lpb[22] = ((ephEntryID->R4 >> 16) & 0xFF);
357         bin->lpb[23] = ((ephEntryID->R4 >> 24) & 0xFF);
358         bin->lpb[24] = (ephEntryID->DisplayType & 0xFF);
359         bin->lpb[25] = ((ephEntryID->DisplayType >> 8)  & 0xFF);
360         bin->lpb[26] = ((ephEntryID->DisplayType >> 16) & 0xFF);
361         bin->lpb[27] = ((ephEntryID->DisplayType >> 24) & 0xFF);
362         bin->lpb[28] = (ephEntryID->MId & 0xFF);
363         bin->lpb[29] = ((ephEntryID->MId >> 8)  & 0xFF);
364         bin->lpb[30] = ((ephEntryID->MId >> 16) & 0xFF);
365         bin->lpb[31] = ((ephEntryID->MId >> 24) & 0xFF);
366
367         return MAPI_E_SUCCESS;
368 }
369
370
371 /**
372    \details Build a PermanentEntryID structure
373
374    \param emsabp_ctx pointer to the EMSABP context
375    \param DisplayType the AB object display type
376    \param ldb_recipient pointer on the LDB message
377    \param permEntryID pointer to the PermanentEntryID returned by the
378    function
379
380    \return MAPI_E_SUCCESS on success, otherwise
381    MAPI_E_NOT_ENOUGH_RESOURCES or MAPI_E_CORRUPT_STORE
382  */
383 _PUBLIC_ enum MAPISTATUS emsabp_set_PermanentEntryID(struct emsabp_context *emsabp_ctx, 
384                                                      uint32_t DisplayType, struct ldb_message *msg, 
385                                                      struct PermanentEntryID *permEntryID)
386 {
387         struct GUID     *guid = (struct GUID *) NULL;
388         const char      *guid_str;
389
390         /* Sanity checks */
391         OPENCHANGE_RETVAL_IF(!permEntryID, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
392         
393
394         permEntryID->ID_type = 0x0;
395         permEntryID->R1 = 0x0;
396         permEntryID->R2 = 0x0;
397         permEntryID->R3 = 0x0;
398         memcpy(permEntryID->ProviderUID.ab, GUID_NSPI, 16);
399         permEntryID->R4 = 0x1;
400         permEntryID->DisplayType = DisplayType;
401
402         if (!msg) {
403                 permEntryID->dn = talloc_strdup(emsabp_ctx->mem_ctx, "/");
404         } else {
405                 guid_str = ldb_msg_find_attr_as_string(msg, "objectGUID", NULL);
406                 OPENCHANGE_RETVAL_IF(!guid_str, MAPI_E_CORRUPT_STORE, NULL);
407                 guid = talloc_zero(emsabp_ctx->mem_ctx, struct GUID);
408                 GUID_from_string(guid_str, guid);
409                 permEntryID->dn = talloc_asprintf(emsabp_ctx->mem_ctx, EMSABP_DN, 
410                                                   guid->time_low, guid->time_mid,
411                                                   guid->time_hi_and_version,
412                                                   guid->clock_seq[0],
413                                                   guid->clock_seq[1],
414                                                   guid->node[0], guid->node[1],
415                                                   guid->node[2], guid->node[3],
416                                                   guid->node[4], guid->node[5]);
417                 talloc_free(guid);
418         }
419
420         return MAPI_E_SUCCESS;
421 }
422
423
424 /**
425    \details Map a PermanentEntryID structure into a Binary_r
426    structure (for PR_ENTRYID and PR_EMS_AB_PARENT_ENTRYID properties)
427
428    \param mem_ctx pointer to the memory context
429    \param permEntryID pointer to the Permanent EntryID structure
430    \param bin pointer to the Binary_r structure the server will return
431
432    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_INVALID_PARAMETER
433  */
434 _PUBLIC_ enum MAPISTATUS emsabp_PermanentEntryID_to_Binary_r(TALLOC_CTX *mem_ctx,
435                                                              struct PermanentEntryID *permEntryID,
436                                                              struct Binary_r *bin)
437 {
438         /* Sanity checks */
439         OPENCHANGE_RETVAL_IF(!permEntryID, MAPI_E_INVALID_PARAMETER, NULL);
440         OPENCHANGE_RETVAL_IF(!bin, MAPI_E_INVALID_PARAMETER, NULL);
441
442         /* Remove const char * size and replace it with effective dn string length */
443         bin->cb = sizeof (*permEntryID) - 4 + strlen(permEntryID->dn) + 1;
444         bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
445
446         /* Copy PermanantEntryID intro bin->lpb */
447         memset(bin->lpb, 0, bin->cb);
448         bin->lpb[0] = permEntryID->ID_type;
449         bin->lpb[1] = permEntryID->R1;
450         bin->lpb[2] = permEntryID->R2;
451         bin->lpb[3] = permEntryID->R3;
452         memcpy(bin->lpb + 4, permEntryID->ProviderUID.ab, 16);
453         bin->lpb[20] = (permEntryID->R4 & 0xFF);
454         bin->lpb[21] = ((permEntryID->R4 >> 8)  & 0xFF);
455         bin->lpb[22] = ((permEntryID->R4 >> 16) & 0xFF);
456         bin->lpb[23] = ((permEntryID->R4 >> 24) & 0xFF);
457         bin->lpb[24] = (permEntryID->DisplayType & 0xFF);
458         bin->lpb[25] = ((permEntryID->DisplayType >> 8)  & 0xFF);
459         bin->lpb[26] = ((permEntryID->DisplayType >> 16) & 0xFF);
460         bin->lpb[27] = ((permEntryID->DisplayType >> 24) & 0xFF);
461         memcpy(bin->lpb + 28, permEntryID->dn, strlen(permEntryID->dn) + 1);
462
463         return MAPI_E_SUCCESS;
464 }
465
466
467 /**
468    \details Find the attribute matching the specified property tag and
469    return the associated data.
470
471    \param mem_ctx pointer to the memory context
472    \param emsabp_ctx pointer to the EMSABP context
473    \param msg pointer to the LDB message
474    \param ulPropTag the property tag to lookup
475    \param MId Minimal Entry ID associated to the current message
476
477    \note This implementation is at the moment limited to MAILUSER,
478    which means we arbitrary set PR_OBJECT_TYPE and PR_DISPLAY_TYPE
479    while we should have a generic method to fill these properties.
480
481    \return Valid generic pointer on success, otherwise NULL
482  */
483 _PUBLIC_ void *emsabp_query(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
484                             struct ldb_message *msg, uint32_t ulPropTag, uint32_t MId)
485 {
486         enum MAPISTATUS                 retval;
487         void                            *data = (void *) NULL;
488         const char                      *attribute;
489         const char                      *ref_attribute;
490         const char                      *ldb_string = NULL;
491         struct Binary_r                 *bin;
492         struct StringArray_r            *mvszA;
493         struct EphemeralEntryID         ephEntryID;
494         struct ldb_message              *msg2 = NULL;
495         struct ldb_message_element      *ldb_element;
496         int                             ret;
497         const char                      *dn = NULL;
498         uint32_t                        i;
499
500         /* Step 1. Fill attributes not in AD but created using EMSABP databases */
501         switch (ulPropTag) {
502         case PR_ADDRTYPE:
503                 data = (void *) talloc_strdup(mem_ctx, EMSABP_ADDRTYPE);
504                 return data;
505         case PR_OBJECT_TYPE:
506                 data = talloc_zero(mem_ctx, uint32_t);
507                 *((uint32_t *)data) = MAPI_MAILUSER;
508                 return data;
509         case PR_DISPLAY_TYPE:
510                 data = talloc_zero(mem_ctx, uint32_t);
511                 *((uint32_t *)data) = DT_MAILUSER;
512                 return data;
513         case PR_ENTRYID:
514                 bin = talloc(mem_ctx, struct Binary_r);
515                 retval = emsabp_set_EphemeralEntryID(emsabp_ctx, DT_MAILUSER, MId, &ephEntryID);
516                 retval = emsabp_EphemeralEntryID_to_Binary_r(mem_ctx, &ephEntryID, bin);
517                 return bin;
518         case PR_INSTANCE_KEY:
519                 bin = talloc_zero(mem_ctx, struct Binary_r);
520                 bin->cb = 4;
521                 bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
522                 memset(bin->lpb, 0, bin->cb);
523                 bin->lpb[0] = MId & 0xFF;
524                 bin->lpb[1] = (MId >> 8)  & 0xFF;
525                 bin->lpb[2] = (MId >> 16) & 0xFF;
526                 bin->lpb[3] = (MId >> 24) & 0xFF;
527                 return bin;
528         default:
529                 break;
530         }
531
532         /* Step 2. Retrieve the attribute name associated to ulPropTag and handle the ref case */
533         attribute = emsabp_property_get_attribute(ulPropTag);
534         if (!attribute) return NULL;
535
536         ret = emsabp_property_is_ref(ulPropTag);
537         if (ret == 1) {
538                 ref_attribute = emsabp_property_get_ref_attr(ulPropTag);
539                 if (ref_attribute) {
540                         dn = ldb_msg_find_attr_as_string(msg, attribute, NULL);
541                         retval = emsabp_search_dn(emsabp_ctx, dn, &msg2);
542                         if (retval != MAPI_E_SUCCESS) {
543                                 return NULL;
544                         }
545                         attribute = ref_attribute;
546                 }
547         } else {
548                 msg2 = msg;
549         }
550
551         /* Step 3. Retrieve data associated to the attribute/tag */
552         switch (ulPropTag & 0xFFFF) {
553         case PT_STRING8:
554         case PT_UNICODE:
555                 ldb_string = ldb_msg_find_attr_as_string(msg2, attribute, NULL);
556                 if (!ldb_string) return NULL;
557                 data = talloc_strdup(mem_ctx, ldb_string);
558                 break;
559         case PT_MV_STRING8:
560                 ldb_element = ldb_msg_find_element(msg2, attribute);
561                 if (!ldb_element) return NULL;
562
563                 mvszA = talloc(mem_ctx, struct StringArray_r);
564                 mvszA->cValues = ldb_element[0].num_values & 0xFFFFFFFF;
565                 mvszA->lppszA = talloc_array(mem_ctx, const char *, mvszA->cValues);
566                 for (i = 0; i < mvszA->cValues; i++) {
567                         mvszA->lppszA[i] = talloc_strdup(mem_ctx, (char *)ldb_element->values[i].data);
568                 }
569                 data = (void *) mvszA;
570                 break;
571         default:
572                 DEBUG(3, ("[%s:%d]: Unsupported property type: 0x%x\n", __FUNCTION__, __LINE__,
573                           (ulPropTag & 0xFFFF)));
574                 break;
575         }
576
577         return data;
578 }
579
580
581 /**
582    \details Builds the SRow array entry for the specified MId.
583
584    The function retrieves the DN associated to the specified MId
585    within its on-memory TDB database. It next fetches the LDB record
586    matching the DN and finally retrieve the requested properties for
587    this record.
588
589    \param mem_ctx pointer to the memory context
590    \param emsabp_ctx pointer to the EMSABP context
591    \param aRow pointer to the SRow structure where results will be
592    stored
593    \param MId MId to fetch properties for
594    \param pPropTags pointer to the property tags array
595
596    \note We currently assume records are users.ldb
597
598    \return MAPI_E_SUCCESS on success, otherwise MAPI error
599  */
600 _PUBLIC_ enum MAPISTATUS emsabp_fetch_attrs(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
601                                             struct SRow *aRow, uint32_t MId, 
602                                             struct SPropTagArray *pPropTags)
603 {
604         enum MAPISTATUS         retval;
605         void                    *ldb_ctx;
606         char                    *dn;
607         const char * const      recipient_attrs[] = { "*", NULL };
608         struct ldb_result       *res = NULL;
609         struct ldb_dn           *ldb_dn = NULL;
610         int                     ret;
611         uint32_t                ulPropTag;
612         void                    *data;
613         int                     i;
614
615         /* Step 0. Try to Retrieve the dn associated to the MId first from temp TDB (users) */
616         ldb_ctx = emsabp_ctx->users_ctx;
617         retval = emsabp_tdb_fetch_dn_from_MId(mem_ctx, emsabp_ctx->ttdb_ctx, MId, &dn);
618         if (retval) {
619                 /* If it fails try to retrieve it from the on-disk TDB database (conf) */
620                 retval = emsabp_tdb_fetch_dn_from_MId(mem_ctx, emsabp_ctx->tdb_ctx, MId, &dn);
621                 ldb_ctx = emsabp_ctx->conf_ctx;
622         }
623         OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
624
625         /* Step 1. Fetch the LDB record */
626         ldb_dn = ldb_dn_new(mem_ctx, ldb_ctx, dn);
627         OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, NULL);
628
629         ret = ldb_search(ldb_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn, LDB_SCOPE_BASE,
630                          recipient_attrs, NULL);
631         OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count || res->count != 1, MAPI_E_CORRUPT_STORE, NULL);
632
633         /* Step 2. Retrieve property values and build aRow */
634         aRow->ulAdrEntryPad = 0x0;
635         aRow->cValues = pPropTags->cValues;
636         aRow->lpProps = talloc_array(mem_ctx, struct SPropValue, aRow->cValues);
637
638         for (i = 0; i < aRow->cValues; i++) {
639                 ulPropTag = pPropTags->aulPropTag[i];
640                 data = emsabp_query(mem_ctx, emsabp_ctx, res->msgs[0], ulPropTag, MId);
641                 if (!data) {
642                         ulPropTag &= 0xFFFF0000;
643                         ulPropTag += PT_ERROR;
644                 }
645
646                 aRow->lpProps[i].ulPropTag = ulPropTag;
647                 aRow->lpProps[i].dwAlignPad = 0x0;
648                 set_SPropValue(&(aRow->lpProps[i]), data);
649         }
650
651
652         return MAPI_E_SUCCESS;
653 }
654
655
656 /**
657    \details Builds the SRow array entry for the specified table
658    record.
659
660    \param mem_ctx pointer to the memory context
661    \param emsabp_ctx pointer to the EMSABP context
662    \param aRow pointer to the SRow structure where results will be
663    stored
664    \param dwFlags flags controlling whether strings should be unicode
665    encoded or not
666    \param permEntryID pointer to the current record Permanent
667    EntryID
668    \param parentPermEntryID pointer to the parent record Permanent
669    EntryID
670    \param msg pointer to the LDB message for current record
671    \param child boolean value specifying whether current record is
672    root or child within containers hierarchy
673
674    \return MAPI_E_SUCCESS on success, otherwise MAPI error
675  */
676 _PUBLIC_ enum MAPISTATUS emsabp_table_fetch_attrs(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
677                                                   struct SRow *aRow, uint32_t dwFlags,
678                                                   struct PermanentEntryID *permEntryID,
679                                                   struct PermanentEntryID *parentPermEntryID,
680                                                   struct ldb_message *msg, bool child)
681 {
682         enum MAPISTATUS                 retval;
683         struct SPropTagArray            *SPropTagArray;
684         struct SPropValue               lpProps;
685         uint32_t                        i;
686         uint32_t                        containerID = 0;
687         const char                      *dn = NULL;
688
689         /* Step 1. Build the array of properties to fetch and map */
690         if (child == false) {
691                 SPropTagArray = set_SPropTagArray(mem_ctx, 0x6,
692                                                   PR_ENTRYID,
693                                                   PR_CONTAINER_FLAGS,
694                                                   PR_DEPTH,
695                                                   PR_EMS_AB_CONTAINERID,
696                                                   ((dwFlags & NspiUnicodeStrings) ? PR_DISPLAY_NAME_UNICODE : PR_DISPLAY_NAME),
697                                                   PR_EMS_AB_IS_MASTER);
698         } else {
699                 SPropTagArray = set_SPropTagArray(mem_ctx, 0x7,
700                                                   PR_ENTRYID,
701                                                   PR_CONTAINER_FLAGS,
702                                                   PR_DEPTH,
703                                                   PR_EMS_AB_CONTAINERID,
704                                                   ((dwFlags & NspiUnicodeStrings) ? PR_DISPLAY_NAME_UNICODE : PR_DISPLAY_NAME),
705                                                   PR_EMS_AB_IS_MASTER,
706                                                   PR_EMS_AB_PARENT_ENTRYID);
707         }
708
709         /* Step 2. Allocate SPropValue array and update SRow cValues field */
710         aRow->ulAdrEntryPad = 0x0;
711         aRow->cValues = 0x0;
712         aRow->lpProps = talloc_zero(mem_ctx, struct SPropValue);
713
714         /* Step 3. Global Address List or real container */
715         if (!msg) {
716                 /* Global Address List record is constant */
717                 for (i = 0; i < SPropTagArray->cValues; i++) {
718                         lpProps.ulPropTag = SPropTagArray->aulPropTag[i];
719                         lpProps.dwAlignPad = 0x0;
720
721                         switch (SPropTagArray->aulPropTag[i]) {
722                         case PR_ENTRYID:
723                                 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, permEntryID, &(lpProps.value.bin));
724                                 break;
725                         case PR_CONTAINER_FLAGS:
726                                 lpProps.value.l =  AB_RECIPIENTS | AB_UNMODIFIABLE;
727                                 break;
728                         case PR_DEPTH:
729                                 lpProps.value.l = 0x0;
730                                 break;
731                         case PR_EMS_AB_CONTAINERID:
732                                 lpProps.value.l = 0x0;
733                                 break;
734                         case PR_DISPLAY_NAME:
735                                 lpProps.value.lpszA = NULL;
736                                 break;
737                         case PR_DISPLAY_NAME_UNICODE:
738                                 lpProps.value.lpszW = NULL;
739                                 break;
740                         case PR_EMS_AB_IS_MASTER:
741                                 lpProps.value.b = false;
742                                 break;
743                         default:
744                                 break;
745                         }
746                         SRow_addprop(aRow, lpProps);
747                         /* SRow_addprop internals overwrite with MAPI_E_NOT_FOUND when data is NULL */
748                         if (SPropTagArray->aulPropTag[i] == PR_DISPLAY_NAME || 
749                             SPropTagArray->aulPropTag[i] == PR_DISPLAY_NAME_UNICODE) {
750                                 aRow->lpProps[aRow->cValues - 1].value.lpszA = NULL;
751                                 aRow->lpProps[aRow->cValues - 1].value.lpszW = NULL;
752                         }
753                 }
754         } else {
755                 for (i = 0; i < SPropTagArray->cValues; i++) {
756                         lpProps.ulPropTag = SPropTagArray->aulPropTag[i];
757                         lpProps.dwAlignPad = 0x0;
758
759                         switch (SPropTagArray->aulPropTag[i]) {
760                         case PR_ENTRYID:
761                                 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, permEntryID, &(lpProps.value.bin));
762                                 break;
763                         case PR_CONTAINER_FLAGS:
764                                 switch (child) {
765                                 case true:
766                                         lpProps.value.l = AB_RECIPIENTS | AB_SUBCONTAINERS | AB_UNMODIFIABLE;
767                                         break;
768                                 case false:
769                                         lpProps.value.l = AB_RECIPIENTS | AB_UNMODIFIABLE;
770                                 }
771                                 break;
772                         case PR_DEPTH:
773                                 switch (child) {
774                                 case true:
775                                         lpProps.value.l = 0x1;
776                                         break;
777                                 case false:
778                                         lpProps.value.l = 0x0;
779                                         break;
780                                 }
781                                 break;
782                         case PR_EMS_AB_CONTAINERID:
783                                 dn = ldb_msg_find_attr_as_string(msg, "distinguishedName", NULL);
784                                 retval = emsabp_tdb_fetch_MId(emsabp_ctx->tdb_ctx, dn, &containerID);
785                                 if (retval) {
786                                         retval = emsabp_tdb_insert(emsabp_ctx->tdb_ctx, dn);
787                                         OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
788                                         retval = emsabp_tdb_fetch_MId(emsabp_ctx->tdb_ctx, dn, &containerID);
789                                         OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
790                                 }
791                                 lpProps.value.l = containerID;
792                                 break;
793                         case PR_DISPLAY_NAME:
794                                 lpProps.value.lpszA = talloc_strdup(mem_ctx, ldb_msg_find_attr_as_string(msg, "displayName", NULL));
795                                 if (!lpProps.value.lpszA) {
796                                         lpProps.ulPropTag &= 0xFFFF0000;
797                                         lpProps.ulPropTag += PT_ERROR;
798                                 }
799                                 break;
800                         case PR_DISPLAY_NAME_UNICODE:
801                                 lpProps.value.lpszW = talloc_strdup(mem_ctx, ldb_msg_find_attr_as_string(msg, "displayName", NULL));
802                                 if (!lpProps.value.lpszW) {
803                                         lpProps.ulPropTag &= 0xFFFF0000;
804                                         lpProps.ulPropTag += PT_ERROR;
805                                 }
806                                 break;
807                         case PR_EMS_AB_IS_MASTER:
808                                 /* FIXME: harcoded value - no load balancing */
809                                 lpProps.value.l = 0x0;
810                                 break;
811                         case PR_EMS_AB_PARENT_ENTRYID:
812                                 emsabp_PermanentEntryID_to_Binary_r(mem_ctx, parentPermEntryID, &lpProps.value.bin);
813                                 break;
814                         default:
815                                 break;
816                         }
817                         SRow_addprop(aRow, lpProps);
818                 }
819         }
820
821         return MAPI_E_SUCCESS;
822 }
823
824
825 /**
826    \details Retrieve and build the HierarchyTable requested by
827    GetSpecialTable NSPI call
828
829    \param mem_ctx pointer to the memory context
830    \param emsabp_ctx pointer to the EMSABP context
831    \param dwFlags flags controlling whether strings should be UNICODE
832    or not
833    \param SRowSet pointer on pointer to the output SRowSet array
834
835    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_CORRUPT_STORE
836  */
837 _PUBLIC_ enum MAPISTATUS emsabp_get_HierarchyTable(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
838                                                    uint32_t dwFlags, struct SRowSet **SRowSet)
839 {
840         enum MAPISTATUS                 retval;
841         struct SRow                     *aRow;
842         struct PermanentEntryID         gal;
843         struct PermanentEntryID         parentPermEntryID;
844         struct PermanentEntryID         permEntryID;
845         enum ldb_scope                  scope = LDB_SCOPE_SUBTREE;
846         struct ldb_request              *req;
847         struct ldb_result               *res = NULL;
848         char                            *ldb_filter;
849         struct ldb_dn                   *ldb_dn = NULL;
850         struct ldb_control              **controls;
851         const char * const              recipient_attrs[] = { "*", NULL };
852         const char                      *control_strings[2] = { "server_sort:0:0:displayName", NULL };
853         const char                      *addressBookRoots;
854         int                             ret;
855         uint32_t                        aRow_idx;
856         uint32_t                        i;
857
858         /* Step 1. Build the 'Global Address List' object using PermanentEntryID */
859         aRow = talloc_zero(mem_ctx, struct SRow);
860         OPENCHANGE_RETVAL_IF(!aRow, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
861         aRow_idx = 0;
862
863         retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, NULL, &gal);
864         OPENCHANGE_RETVAL_IF(retval, retval, aRow);
865
866         retval = emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &gal, NULL, NULL, false);
867         aRow_idx++;
868
869         /* Step 2. Retrieve the object pointed by addressBookRoots attribute: 'All Address Lists' */
870         ldb_filter = talloc_strdup(emsabp_ctx->mem_ctx, "(addressBookRoots=*)");
871         ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res,
872                          ldb_get_default_basedn(emsabp_ctx->conf_ctx),
873                          scope, recipient_attrs, ldb_filter);
874         talloc_free(ldb_filter);
875         OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count, MAPI_E_CORRUPT_STORE, aRow);
876
877         addressBookRoots = ldb_msg_find_attr_as_string(res->msgs[0], "addressBookRoots", NULL);
878         OPENCHANGE_RETVAL_IF(!addressBookRoots, MAPI_E_CORRUPT_STORE, aRow);
879         talloc_free(res);
880
881         ldb_dn = ldb_dn_new(emsabp_ctx->mem_ctx, emsabp_ctx->conf_ctx, addressBookRoots);
882         OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, aRow);
883
884         scope = LDB_SCOPE_BASE;
885         ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn, 
886                          scope, recipient_attrs, NULL);
887         OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count || res->count != 1, MAPI_E_CORRUPT_STORE, aRow);
888
889         aRow = talloc_realloc(mem_ctx, aRow, struct SRow, aRow_idx + 1);
890         retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, res->msgs[0], &parentPermEntryID);
891         emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &parentPermEntryID, NULL, res->msgs[0], false);
892         aRow_idx++;
893         talloc_free(res);
894
895         /* Step 3. Retrieve 'All Address Lists' subcontainers */
896         res = talloc_zero(mem_ctx, struct ldb_result);
897         OPENCHANGE_RETVAL_IF(!res, MAPI_E_NOT_ENOUGH_RESOURCES, aRow);
898
899         controls = ldb_parse_control_strings(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, control_strings);
900         ret = ldb_build_search_req(&req, emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx,
901                                    ldb_dn, LDB_SCOPE_SUBTREE, "(purportedSearch=*)",
902                                    recipient_attrs, controls, res, ldb_search_default_callback, NULL);
903
904         if (ret != LDB_SUCCESS) {
905                 talloc_free(res);
906                 OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS, MAPI_E_CORRUPT_STORE, aRow);
907         }
908
909         ret = ldb_request(emsabp_ctx->conf_ctx, req);
910         if (ret == LDB_SUCCESS) {
911                 ret = ldb_wait(req->handle, LDB_WAIT_ALL);
912         }
913         talloc_free(req);
914         
915         if (ret != LDB_SUCCESS || !res->count) {
916                 talloc_free(res);
917                 OPENCHANGE_RETVAL_IF(1, MAPI_E_CORRUPT_STORE, aRow);
918         }
919
920         aRow = talloc_realloc(mem_ctx, aRow, struct SRow, aRow_idx + res->count + 1);
921
922         for (i = 0; res->msgs[i]; i++) {
923                 retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, res->msgs[i], &permEntryID);
924                 emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &permEntryID, &parentPermEntryID, res->msgs[i], true);
925                 talloc_free(permEntryID.dn);
926                 memset(&permEntryID, 0, sizeof (permEntryID));
927                 aRow_idx++;
928         }
929         talloc_free(res);
930         talloc_free(parentPermEntryID.dn);
931
932         /* Step 4. Build output SRowSet */
933         SRowSet[0]->cRows = aRow_idx;
934         SRowSet[0]->aRow = aRow;
935
936         return MAPI_E_SUCCESS;
937 }
938
939
940 /**
941    \details Retrieve and build the CreationTemplates Table requested
942    by GetSpecialTable NSPI call
943
944    \param mem_ctx pointer to the memory context
945    \param emsabp_ctx pointer to the EMSABP context
946    \param dwFlags flags controlling whether strings should be UNICODE
947    or not
948    \param SRowSet pointer on pointer to the output SRowSet array
949
950    \return MAPI_E_SUCCESS on success, otherwise MAPI_E_CORRUPT_STORE 
951  */
952 _PUBLIC_ enum MAPISTATUS emsabp_get_CreationTemplatesTable(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
953                                                            uint32_t dwFlags, struct SRowSet **SRowSet)
954 {
955         return MAPI_E_SUCCESS;
956 }
957
958
959 /**
960    \details Search Active Directory given input search criterias. The
961    function associates for each records returned by the search a
962    unique session Minimal Entry ID and a LDB message.
963
964    \param mem_ctx pointer to the memory context
965    \param emsabp_ctx pointer to the EMSABP context
966    \param MIds pointer to the list of MIds the function returns
967    \param Restriction pointer to restriction rules to apply to the
968    search
969    \param pStat pointer the STAT structure associated to the search
970    param limit the limit number of results the function can return
971
972    \note SortTypePhoneticDisplayName sort type is currently not supported.
973
974    \return MAPI_E_SUCCESS on success, otherwise MAPI error
975  */
976 _PUBLIC_ enum MAPISTATUS emsabp_search(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
977                                        struct SPropTagArray *MIds, struct Restriction_r *restriction,
978                                        struct STAT *pStat, uint32_t limit)
979 {
980         enum MAPISTATUS                 retval;
981         struct ldb_result               *res = NULL;
982         struct PropertyRestriction_r    *res_prop = NULL;
983         const char                      *recipient = NULL;
984         const char * const              recipient_attrs[] = { "*", NULL };
985         char                            *ldb_filter;
986         int                             ret;
987         uint32_t                        i;
988         const char                      *dn;
989
990         /* Step 0. Sanity Checks (MS-NSPI Server Processing Rules) */
991         if (pStat->SortType == SortTypePhoneticDisplayName) {
992                 return MAPI_E_CALL_FAILED;
993         }
994
995         if (((pStat->SortType == SortTypeDisplayName) || (pStat->SortType == SortTypePhoneticDisplayName)) &&
996             (pStat->ContainerID && (emsabp_tdb_lookup_MId(emsabp_ctx->tdb_ctx, pStat->ContainerID) == false))) {
997                 return MAPI_E_INVALID_BOOKMARK;
998         }
999
1000         if (restriction && (pStat->SortType != SortTypeDisplayName) && 
1001             (pStat->SortType != SortTypePhoneticDisplayName)) {
1002                 return MAPI_E_CALL_FAILED;
1003         }
1004
1005         /* Step 1. Apply restriction and retrieve results from AD */
1006         if (restriction) {
1007                 /* FIXME: We only support RES_PROPERTY restriction */
1008                 if ((uint32_t)restriction->rt != RES_PROPERTY) {
1009                         return MAPI_E_TOO_COMPLEX;
1010                 }
1011
1012                 /* FIXME: We only support PR_ANR */
1013                 res_prop = (struct PropertyRestriction_r *)&(restriction->res);
1014                 if ((res_prop->ulPropTag != PR_ANR) && (res_prop->ulPropTag != PR_ANR_UNICODE)) {
1015                         return MAPI_E_NO_SUPPORT;
1016                 }
1017                 
1018                 recipient = (res_prop->ulPropTag == PR_ANR) ?
1019                         res_prop->lpProp->value.lpszA :
1020                         res_prop->lpProp->value.lpszW;
1021
1022                 ldb_filter = talloc_asprintf(emsabp_ctx->mem_ctx, "(&(objectClass=user)(sAMAccountName=*%s*)(!(objectClass=computer)))", recipient);
1023                 ret = ldb_search(emsabp_ctx->users_ctx, emsabp_ctx->mem_ctx, &res,
1024                                  ldb_get_default_basedn(emsabp_ctx->users_ctx),
1025                                  LDB_SCOPE_SUBTREE, recipient_attrs, ldb_filter);
1026                 talloc_free(ldb_filter);
1027
1028                 if (ret != LDB_SUCCESS || !res->count) {
1029                         return MAPI_E_NOT_FOUND;
1030                 }
1031         } else {
1032                 /* FIXME Check restriction == NULL */
1033         }
1034
1035         if (limit && res->count > limit) {
1036                 return MAPI_E_TABLE_TOO_BIG;
1037         }
1038
1039         MIds->aulPropTag = talloc_array(emsabp_ctx->mem_ctx, uint32_t, res->count);
1040         MIds->cValues = res->count;
1041
1042         /* Step 2. Create session MId for all fetched records */
1043         for (i = 0; i < res->count; i++) {
1044                 dn = ldb_msg_find_attr_as_string(res->msgs[i], "distinguishedName", NULL);
1045                 retval = emsabp_tdb_fetch_MId(emsabp_ctx->ttdb_ctx, dn, &MIds->aulPropTag[i]);
1046                 if (retval) {
1047                         retval = emsabp_tdb_insert(emsabp_ctx->ttdb_ctx, dn);
1048                         OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
1049                         retval = emsabp_tdb_fetch_MId(emsabp_ctx->ttdb_ctx, dn, &(MIds->aulPropTag[i]));
1050                         OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
1051                 }
1052         }
1053
1054         return MAPI_E_SUCCESS;
1055 }
1056
1057
1058 /**
1059    \details Search for a given DN within AD and return the associated
1060    LDB message.
1061
1062    \param emsabp_ctx pointer to the EMSABP context
1063    \param dn pointer to the DN string to search for
1064    \param ldb_res pointer on pointer to the LDB message returned by
1065    the function
1066
1067    \return MAPI_E_SUCCESS on success, otherwise MAPI error
1068  */
1069 _PUBLIC_ enum MAPISTATUS emsabp_search_dn(struct emsabp_context *emsabp_ctx, const char *dn, 
1070                                           struct ldb_message **ldb_res)
1071 {
1072         struct ldb_dn           *ldb_dn = NULL;
1073         struct ldb_result       *res = NULL;
1074         const char * const      recipient_attrs[] = { "*", NULL };
1075         int                     ret;
1076
1077         /* Sanity Checks */
1078         OPENCHANGE_RETVAL_IF(!dn, MAPI_E_INVALID_PARAMETER, NULL);
1079         OPENCHANGE_RETVAL_IF(!ldb_res, MAPI_E_INVALID_PARAMETER, NULL);
1080
1081         ldb_dn = ldb_dn_new(emsabp_ctx->mem_ctx, emsabp_ctx->conf_ctx, dn);
1082         OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, NULL);
1083
1084         ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn,
1085                          LDB_SCOPE_BASE, recipient_attrs, NULL);
1086         OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count || res->count != 1, MAPI_E_CORRUPT_STORE, NULL);
1087
1088         *ldb_res = res->msgs[0];
1089
1090         return MAPI_E_SUCCESS;
1091 }
1092
1093
1094 /**
1095    \details Search for a given AD record given its legacyDN parameter
1096    and return the associated LDB message.
1097
1098    \param emsabp_ctx pointer to the EMSABP context
1099    \param legacyDN pointer to the legacyDN attribute value to lookup
1100    \param ldb_res pointer on pointer to the LDB message returned by 
1101    the function
1102
1103    \return MAPI_E_SUCCESS on success, otherwise MAPI error
1104  */
1105 _PUBLIC_ enum MAPISTATUS emsabp_search_legacyExchangeDN(struct emsabp_context *emsabp_ctx, const char *legacyDN,
1106                                                         struct ldb_message **ldb_res)
1107 {
1108         char                    *ldb_filter;
1109         const char * const      recipient_attrs[] = { "*", NULL };
1110         int                     ret;
1111         struct ldb_result       *res = NULL;
1112
1113         /* Sanity Checks */
1114         OPENCHANGE_RETVAL_IF(!legacyDN, MAPI_E_INVALID_PARAMETER, NULL);
1115         OPENCHANGE_RETVAL_IF(!ldb_res, MAPI_E_INVALID_PARAMETER, NULL);
1116
1117         ldb_filter = talloc_asprintf(emsabp_ctx->mem_ctx, "(legacyExchangeDN=%s)", legacyDN);
1118         ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res,
1119                          ldb_get_default_basedn(emsabp_ctx->conf_ctx), 
1120                          LDB_SCOPE_SUBTREE, recipient_attrs, ldb_filter);
1121         talloc_free(ldb_filter);
1122
1123         OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count, MAPI_E_NOT_FOUND, NULL);
1124
1125         *ldb_res = res->msgs[0];
1126
1127         return MAPI_E_SUCCESS;
1128 }