- Preliminary NspiGetSpecialTable implementation added to NSPI
authorJulien Kerihuel <j.kerihuel@openchange.org>
Sat, 10 Jan 2009 19:34:32 +0000 (19:34 +0000)
committerJulien Kerihuel <j.kerihuel@openchange.org>
Sat, 10 Jan 2009 19:34:32 +0000 (19:34 +0000)
  server/EMSABP provider: Hierarchy Table supported (required during
  profile creation)
- add PT_BINARY support for mapidump_SPropValue
- fix a bug when PT_STRING8 or PT_UNICODE pointer is set to
  MAPI_E_NOT_FOUND

Makefile
libmapi/mapidump.c
mapiproxy/dcesrv_mapiproxy_server.c
mapiproxy/libmapiproxy.h
mapiproxy/servers/default/nspi/dcesrv_exchange_nsp.c
mapiproxy/servers/default/nspi/dcesrv_exchange_nsp.h
mapiproxy/servers/default/nspi/emsabp.c
mapiproxy/servers/default/nspi/emsabp_tdb.c [new file with mode: 0644]

index 55b36a5f30f20c97fddd8d23b8316ee90628c5be..2c97ced2052cfcc1ec14e68d37ce29b2eb94e9e6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -876,7 +876,8 @@ mapiproxy-servers-clean::
 clean:: mapiproxy-servers-clean
 
 mapiproxy/servers/exchange_nsp.$(SHLIBEXT):    mapiproxy/servers/default/nspi/dcesrv_exchange_nsp.po   \
-                                               mapiproxy/servers/default/nspi/emsabp.po
+                                               mapiproxy/servers/default/nspi/emsabp.po                \
+                                               mapiproxy/servers/default/nspi/emsabp_tdb.po            
        @echo "Linking $@"
        @$(CC) -o $@ $(DSOOPT) $^ -L. $(LIBS) -Lmapiproxy mapiproxy/libmapiproxy.$(SHLIBEXT).$(PACKAGE_VERSION)  libmapi.$(SHLIBEXT).$(PACKAGE_VERSION)
 
index 242cc70a502419d5ec525e97f7fc0bc00620c0a6..096bf6746e611c5a3578623c6b1c3967e051eb8f 100644 (file)
@@ -56,7 +56,7 @@ _PUBLIC_ void mapidump_SPropValue(struct SPropValue lpProp, const char *sep)
        case PT_STRING8:
        case PT_UNICODE:
                data = get_SPropValue_data(&lpProp);
-               printf("%s%s: %s\n", sep?sep:"", proptag, (const char *)data);
+               printf("%s%s: %s\n", sep?sep:"", proptag, (data && (uint32_t)data != MAPI_E_NOT_FOUND) ? (const char *)data : "NULL");
                break;
        case PT_SYSTIME:
                mapidump_date_SPropValue(lpProp, proptag);
@@ -69,6 +69,11 @@ _PUBLIC_ void mapidump_SPropValue(struct SPropValue lpProp, const char *sep)
                data = get_SPropValue_data(&lpProp);
                printf("%s%s: %u\n", sep?sep:"", proptag, (*(const uint32_t *)data));
                break;
+       case PT_BINARY:
+               data = get_SPropValue_data(&lpProp);
+               printf("%s%s\n", sep?sep:"", proptag);
+               dump_data(0, ((const struct Binary_r *)data)->lpb, ((const struct Binary_r *)data)->cb);
+               break;
        case PT_MV_STRING8:
                StringArray_r = (const struct StringArray_r *) get_SPropValue_data(&lpProp);
                printf("%s%s: ", sep?sep:"", proptag);
index 1b29986c8f4d4028efa284b2703fbc1c171d1b51..a916f41ed10ff369c47678d411559d2a413372fe 100644 (file)
@@ -36,6 +36,8 @@ static struct server_module {
 int                                    num_server_modules;
 static struct mapiproxy_module_list    *server_list = NULL;
 
+static TDB_CONTEXT                     *emsabp_tdb_ctx = NULL;
+
 
 NTSTATUS mapiproxy_server_dispatch(struct dcesrv_call_state *dce_call,
                                   TALLOC_CTX *mem_ctx, void *r,
@@ -266,3 +268,42 @@ const struct mapiproxy_module *mapiproxy_server_byname(const char *name)
 
        return NULL;
 }
+
+
+/**
+   \details Initialize an EMSABP TDB context available to all
+   instances when Samba is not run in single mode.
+
+   \param mem_ctx pointer to the memory context
+   \param lp_ctx pointer to the loadparm context
+
+   \note TDB database can't be opened twice with O_RDWR flags. We
+   ensure here we have a general context initialized, which we'll
+   reopen within forked instances
+
+   return Allocated TDB context on success, otherwise NULL
+ */
+_PUBLIC_ TDB_CONTEXT *mapiproxy_server_emsabp_tdb_init(struct loadparm_context *lp_ctx)
+{
+       char                    *tdb_path;
+       TALLOC_CTX              *mem_ctx;
+
+       if (emsabp_tdb_ctx) return emsabp_tdb_ctx;
+
+       mem_ctx = talloc_init("mapiproxy_server_emsabp_tdb_init");
+       if (!mem_ctx) return NULL;
+
+       /* Step 0. Retrieve a TDB context pointer on the emsabp_tdb database */
+       tdb_path = talloc_asprintf(mem_ctx, "%s/%s", lp_private_dir(lp_ctx), EMSABP_TDB_NAME);
+       emsabp_tdb_ctx = tdb_open(tdb_path, 0, 0, O_RDWR|O_CREAT, 0600);
+       talloc_free(tdb_path);
+       if (!emsabp_tdb_ctx) {
+               DEBUG(3, ("[%s:%d]: %s\n", __FUNCTION__, __LINE__, strerror(errno)));
+               talloc_free(mem_ctx);
+               return NULL;
+       }
+
+       talloc_free(mem_ctx);
+
+       return emsabp_tdb_ctx;
+}
index 0efa80c9ba2de8c5c6c8878cc8d7d3a7b2ca6513..ed048c4a99f98b9f3294d03522863a7722d102da 100644 (file)
 #include <dcerpc_server.h>
 #include <talloc.h>
 #include <tevent.h>
+#include <tdb.h>
 #include <libmapi/dlinklist.h>
+#include <fcntl.h>
+#include <errno.h>
 
 struct mapiproxy {
        bool                    norelay;
@@ -105,6 +108,12 @@ struct auth_serversupplied_info
 };
 
 
+/**
+   EMSABP server defines
+ */
+#define        EMSABP_TDB_NAME         "emsabp_tdb.tdb"
+
+
 #define        NTLM_AUTH_IS_OK(dce_call) \
 (dce_call->conn->auth_state.session_info->server_info->authenticated == true)
 
@@ -142,6 +151,7 @@ bool mapiproxy_server_loaded(const char *);
 const struct mapiproxy_module *mapiproxy_server_bystatus(const char *, enum mapiproxy_status);
 const struct mapiproxy_module *mapiproxy_server_byname(const char *);
 
+TDB_CONTEXT *mapiproxy_server_emsabp_tdb_init(struct loadparm_context *);
 
 /* definitions from dcesrv_mapiproxy_session. c */
 struct mpm_session *mpm_session_new(TALLOC_CTX *, struct server_id, uint32_t);
index b80e4a22d4d05f378c36a9793cf8d9c046644110..e53e3c52e37c7d3ce79af512be4341e0495c4ca8 100644 (file)
@@ -30,6 +30,7 @@
 #include "dcesrv_exchange_nsp.h"
 
 struct exchange_nsp_session    *nsp_session = NULL;
+TDB_CONTEXT                    *emsabp_tdb_ctx = NULL;
 
 /**
    \details exchange_nsp NspiBind (0x0) function, Initiates a NSPI
@@ -72,8 +73,11 @@ static enum MAPISTATUS dcesrv_NspiBind(struct dcesrv_call_state *dce_call,
        }
        
        /* Step 1. Initialize the emsabp context */
-       emsabp_ctx = emsabp_init(dce_call->conn->dce_ctx->lp_ctx);
-       OPENCHANGE_RETVAL_IF(!emsabp_ctx, MAPI_E_FAILONEPROVIDER, NULL);
+       emsabp_ctx = emsabp_init(dce_call->conn->dce_ctx->lp_ctx, emsabp_tdb_ctx);
+       if (!emsabp_ctx) {
+               smb_panic("unable to initialize emsabp context");
+               OPENCHANGE_RETVAL_IF(!emsabp_ctx, MAPI_E_FAILONEPROVIDER, NULL);
+       }
 
        /* Step 2. Check if incoming user belongs to the Exchange organization */
        if (emsabp_verify_user(dce_call, emsabp_ctx) == false) {
@@ -360,6 +364,10 @@ static enum MAPISTATUS dcesrv_NspiModProps(struct dcesrv_call_state *dce_call,
    \param mem_ctx pointer to the memory context
    \param r pointer to the NspiGetSpecialTable request data
 
+   \note MS-NSPI specifies lpVersion "holds the value of the version
+   number of the hierarchy table that the client has." We will ignore
+   this for the moment.
+
    \return MAPI_E_SUCCESS on success
 
  */
@@ -367,8 +375,48 @@ static enum MAPISTATUS dcesrv_NspiGetSpecialTable(struct dcesrv_call_state *dce_
                                                  TALLOC_CTX *mem_ctx,
                                                  struct NspiGetSpecialTable *r)
 {
-       DEBUG(3, ("exchange_nsp: NspiGetSpecialTable (0xC) not implemented\n"));
-       DCESRV_FAULT(DCERPC_FAULT_OP_RNG_ERROR);
+       struct dcesrv_handle            *h;
+       struct emsabp_context           *emsabp_ctx;
+
+       DEBUG(3, ("exchange_nsp: NspiGetSpecialTable (0xC)\n"));
+
+       /* Step 0. Ensure incoming user is authenticated */
+       if (!NTLM_AUTH_IS_OK(dce_call)) {
+               DEBUG(1, ("No challenge requested by client, cannot authenticate\n"));
+               return MAPI_E_LOGON_FAILED;
+       }
+
+       h = dcesrv_handle_fetch(dce_call->context, r->in.handle, DCESRV_HANDLE_ANY);
+       emsabp_ctx = (struct emsabp_context *) h->data;
+
+       /* Step 1. (FIXME) We arbitrary set lpVersion to 0x1 */
+       r->out.lpVersion = talloc_zero(mem_ctx, uint32_t);
+       *r->out.lpVersion = 0x1;
+
+       /* Step 2. Allocate output SRowSet and call associated emsabp function */
+       r->out.ppRows = talloc_zero(mem_ctx, struct SRowSet *);
+       OPENCHANGE_RETVAL_IF(!r->out.ppRows, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
+       r->out.ppRows[0] = talloc_zero(mem_ctx, struct SRowSet);
+       OPENCHANGE_RETVAL_IF(!r->out.ppRows[0], MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
+
+       switch (r->in.dwFlags) {
+       case NspiAddressCreationTemplates:
+       case NspiAddressCreationTemplates|NspiUnicodeStrings:
+               DEBUG(0, ("CreationTemplates Table requested\n"));
+               r->out.result = emsabp_get_CreationTemplatesTable(mem_ctx, emsabp_ctx, r->in.dwFlags, r->out.ppRows);
+               break;
+       case NspiUnicodeStrings:
+       case 0x0:
+               DEBUG(0, ("Hierarchy Table requested\n"));
+               r->out.result = emsabp_get_HierarchyTable(mem_ctx, emsabp_ctx, r->in.dwFlags, r->out.ppRows);
+               break;
+       default:
+               talloc_free(r->out.ppRows);
+               talloc_free(r->out.ppRows[0]);
+               return MAPI_E_NO_SUPPORT;
+       }
+
+       return r->out.result;
 }
 
 
@@ -635,6 +683,12 @@ static NTSTATUS dcesrv_exchange_nsp_init(struct dcesrv_context *dce_ctx)
        if (!nsp_session) return NT_STATUS_NO_MEMORY;
        nsp_session->session = NULL;
 
+       /* Open a read-write pointer on the EMSABP TDB database */
+       emsabp_tdb_ctx = emsabp_tdb_init((TALLOC_CTX *)dce_ctx, dce_ctx->lp_ctx);
+       if (!emsabp_tdb_ctx) {
+               smb_panic("unable to initialize EMSABP context");
+       }
+
        return NT_STATUS_OK;
 }
 
index 6f1fb276d7216740d12935a7ee873080eb393c7b..f2c49668482999366e66e1d75510769dc3283f54 100644 (file)
 
 #include <libmapi/libmapi.h>
 #include <libmapi/proto_private.h>
+#include <mapiproxy/libmapiproxy.h>
+#include <ldb.h>
+#include <ldb_errors.h>
+#include <fcntl.h>
 #include <util/debug.h>
 
 #ifndef        __BEGIN_DECLS
@@ -40,6 +44,7 @@ struct emsabp_context {
        void            *conf_ctx;
        void            *users_ctx;
        void            *ldb_ctx;
+       TDB_CONTEXT     *tdb_ctx;
        TALLOC_CTX      *mem_ctx;
 };
 
@@ -51,17 +56,81 @@ struct exchange_nsp_session {
 };
 
 
+/**
+   Represents the NSPI Protocol in Permanent Entry IDs.
+ */
+static const uint8_t GUID_NSPI[] = {
+0xDC, 0xA7, 0x40, 0xC8, 0xC0, 0x42, 0x10, 0x1A, 0xB4, 0xB9,
+0x08, 0x00, 0x2B, 0x2F, 0xE1, 0x82
+};
+
+
+/**
+   PermanentEntryID structure 
+ */
+struct PermanentEntryID {
+       uint8_t                 ID_type;        /* constant: 0x0        */
+       uint8_t                 R1;             /* reserved: 0x0        */
+       uint8_t                 R2;             /* reserved: 0x0        */
+       uint8_t                 R3;             /* reserved: 0x0        */
+       struct FlatUID_r        ProviderUID;    /* constant: GUID_NSPI  */
+       uint32_t                R4;             /* constant: 0x1        */
+       uint32_t                DisplayType;    /* must match one of the existing Display Type value */
+       char                    *dn;            /* DN string representing the object GUID */
+};
+
+
+/**
+   EphemeralEntryID structure
+ */
+struct EphemeralEntryID {
+       uint8_t                 ID_type;        /* constant: 0x87       */
+       uint8_t                 R1;             /* reserved: 0x0        */
+       uint8_t                 R2;             /* reserved: 0x0        */
+       uint8_t                 R3;             /* reserved: 0x0        */
+       struct FlatUID_r        ProviderUID;    /* NSPI server GUID     */
+       uint32_t                R4;             /* constant: 0x1        */
+       uint32_t                DisplayType;    /* must match one of the existing Display Type value */
+       uint32_t                MId;            /* MId of this object   */
+};
+
+#define        EMSABP_DN       "/guid=%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X"
+
+/**
+   NSPI PR_CONTAINER_FLAGS values
+ */
+#define        AB_RECIPIENTS           0x1
+#define        AB_SUBCONTAINERS        0x2
+#define        AB_UNMODIFIABLE         0x8
+
+#define        EMSABP_TDB_MID_START    0x1b28
+#define        EMSABP_TDB_DATA_REC     "MId_index"
+
 __BEGIN_DECLS
 
 NTSTATUS       samba_init_module(void);
 
 /* definitions from emsabp.c */
-struct emsabp_context  *emsabp_init(struct loadparm_context *);
+struct emsabp_context  *emsabp_init(struct loadparm_context *, TDB_CONTEXT *);
 bool                   emsabp_destructor(void *);
 bool                   emsabp_verify_user(struct dcesrv_call_state *, struct emsabp_context *);
 bool                   emsabp_verify_codepage(struct loadparm_context *, struct emsabp_context *, uint32_t);
 bool                   emsabp_verify_lcid(struct loadparm_context *, struct emsabp_context *, uint32_t);
 struct GUID            *emsabp_get_server_GUID(struct loadparm_context *, struct emsabp_context *);
+enum MAPISTATUS                emsabp_set_EphemeralEntryID(struct loadparm_context *, struct emsabp_context *, uint32_t, uint32_t, struct EphemeralEntryID *);
+enum MAPISTATUS                emsabp_set_PermanentEntryID(struct emsabp_context *, uint32_t, struct ldb_message *, struct PermanentEntryID *);
+enum MAPISTATUS                emsabp_PermanentEntryID_to_Binary_r(TALLOC_CTX *, struct PermanentEntryID *, struct Binary_r *);
+enum MAPISTATUS                emsabp_get_HierarchyTable(TALLOC_CTX *, struct emsabp_context *, uint32_t, struct SRowSet **);
+enum MAPISTATUS                emsabp_get_CreationTemplatesTable(TALLOC_CTX *, struct emsabp_context *, uint32_t, struct SRowSet **);
+enum MAPISTATUS                emsabp_table_fetch_attrs(TALLOC_CTX *, struct emsabp_context *, struct SRow *, uint32_t, struct PermanentEntryID *, 
+                                                struct PermanentEntryID *, struct ldb_message *, bool);
+
+/* definitiosn from emsabp_tdb.c */
+TDB_CONTEXT            *emsabp_tdb_init(TALLOC_CTX *, struct loadparm_context *);
+enum MAPISTATUS                emsabp_tdb_close(TDB_CONTEXT *);
+enum MAPISTATUS                emsabp_tdb_fetch(TDB_CONTEXT *, const char *, TDB_DATA *);
+enum MAPISTATUS                emsabp_tdb_insert(TDB_CONTEXT *, const char *);
+enum MAPISTATUS                emsabp_tdb_fetch_MId(TDB_CONTEXT *, const char *, uint32_t *);
 
 __END_DECLS
 
index 1db0d363ac47c08d7c6935b450d34a9a0d4c5604..903510fbc4a8a7551b7385c3f8f52da6dc25f507 100644 (file)
    Samba databases.
 
    \param lp_ctx pointer to the loadparm context
+   \param tdb_ctx pointer to the EMSABP TDB context
 
    \return Allocated emsabp_context on success, otherwise NULL
  */
-_PUBLIC_ struct emsabp_context *emsabp_init(struct loadparm_context *lp_ctx)
+_PUBLIC_ struct emsabp_context *emsabp_init(struct loadparm_context *lp_ctx,
+                                           TDB_CONTEXT *tdb_ctx)
 {
        TALLOC_CTX              *mem_ctx;
        struct emsabp_context   *emsabp_ctx;
@@ -103,6 +105,9 @@ _PUBLIC_ struct emsabp_context *emsabp_init(struct loadparm_context *lp_ctx)
                return NULL;
        }
 
+       /* Reference the global TDB context to the current emsabp context */
+       emsabp_ctx->tdb_ctx = tdb_ctx;
+
        return emsabp_ctx;
 }
 
@@ -257,3 +262,452 @@ _PUBLIC_ struct GUID *emsabp_get_server_GUID(struct loadparm_context *lp_ctx,
        
        return guid;
 }
+
+
+/**
+   \details Build an EphemeralEntryID structure
+
+   \param lp_ctx pointer to the loadparm context
+   \param emsabp_ctx pointer to the EMSABP context
+   \param DisplayType the AB object display type
+   \param MId the MId value
+   \param ephEntryID pointer to the EphemeralEntryID returned by the
+   function
+
+   \return MAPI_E_SUCCESS on success, otherwise
+   MAPI_E_NOT_ENOUGH_RESOURCES or MAPI_E_CORRUPT_STORE
+ */
+_PUBLIC_ enum MAPISTATUS emsabp_set_EphemeralEntryID(struct loadparm_context *lp_ctx, 
+                                                    struct emsabp_context *emsabp_ctx,
+                                                    uint32_t DisplayType, uint32_t MId,
+                                                    struct EphemeralEntryID *ephEntryID)
+{
+       struct GUID     *guid = (struct GUID *) NULL;
+
+       /* Sanity checks */
+       OPENCHANGE_RETVAL_IF(!ephEntryID, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
+
+       guid = emsabp_get_server_GUID(lp_ctx, emsabp_ctx);
+       OPENCHANGE_RETVAL_IF(!guid, MAPI_E_CORRUPT_STORE, NULL);
+
+       ephEntryID->ID_type = 0x87;
+       ephEntryID->R1 = 0x0;
+       ephEntryID->R2 = 0x0;
+       ephEntryID->R3 = 0x0;
+       ephEntryID->ProviderUID.ab[0] = (guid->time_low & 0xFF);
+       ephEntryID->ProviderUID.ab[1] = ((guid->time_low >> 8)  & 0xFF);
+       ephEntryID->ProviderUID.ab[2] = ((guid->time_low >> 16) & 0xFF);
+       ephEntryID->ProviderUID.ab[3] = ((guid->time_low >> 24) & 0xFF);
+       ephEntryID->ProviderUID.ab[4] = (guid->time_mid & 0xFF);
+       ephEntryID->ProviderUID.ab[5] = ((guid->time_mid >> 8)  & 0xFF);
+       ephEntryID->ProviderUID.ab[6] = (guid->time_hi_and_version & 0xFF);
+       ephEntryID->ProviderUID.ab[7] = ((guid->time_hi_and_version >> 8) & 0xFF);
+       memcpy(ephEntryID->ProviderUID.ab + 8,  guid->clock_seq, sizeof (uint8_t) * 2);
+       memcpy(ephEntryID->ProviderUID.ab + 10, guid->node, sizeof (uint8_t) * 6);
+       ephEntryID->R4 = 0x1;
+       ephEntryID->DisplayType = DisplayType;
+       ephEntryID->MId = MId;
+
+       talloc_free(guid);
+
+       return MAPI_E_SUCCESS;
+}
+
+
+/**
+   \details Build a PermanentEntryID structure
+
+   \param emsabp_ctx pointer to the EMSABP context
+   \param DisplayType the AB object display type
+   \param ldb_recipient pointer on the LDB message
+   \param permEntryID pointer to the PermanentEntryID returned by the
+   function
+
+   \return MAPI_E_SUCCESS on success, otherwise
+   MAPI_E_NOT_ENOUGH_RESOURCES or MAPI_E_CORRUPT_STORE
+ */
+_PUBLIC_ enum MAPISTATUS emsabp_set_PermanentEntryID(struct emsabp_context *emsabp_ctx, 
+                                                    uint32_t DisplayType, struct ldb_message *msg, 
+                                                    struct PermanentEntryID *permEntryID)
+{
+       struct GUID     *guid = (struct GUID *) NULL;
+       const char      *guid_str;
+
+       /* Sanity checks */
+       OPENCHANGE_RETVAL_IF(!permEntryID, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
+       
+
+       permEntryID->ID_type = 0x0;
+       permEntryID->R1 = 0x0;
+       permEntryID->R2 = 0x0;
+       permEntryID->R3 = 0x0;
+       memcpy(permEntryID->ProviderUID.ab, GUID_NSPI, 16);
+       permEntryID->R4 = 0x1;
+       permEntryID->DisplayType = DisplayType;
+
+       if (!msg) {
+               permEntryID->dn = talloc_strdup(emsabp_ctx->mem_ctx, "/");
+       } else {
+               guid_str = ldb_msg_find_attr_as_string(msg, "objectGUID", NULL);
+               OPENCHANGE_RETVAL_IF(!guid_str, MAPI_E_CORRUPT_STORE, NULL);
+               guid = talloc_zero(emsabp_ctx->mem_ctx, struct GUID);
+               GUID_from_string(guid_str, guid);
+               permEntryID->dn = talloc_asprintf(emsabp_ctx->mem_ctx, EMSABP_DN, 
+                                                 guid->time_low, guid->time_mid,
+                                                 guid->time_hi_and_version,
+                                                 guid->clock_seq[0],
+                                                 guid->clock_seq[1],
+                                                 guid->node[0], guid->node[1],
+                                                 guid->node[2], guid->node[3],
+                                                 guid->node[4], guid->node[5]);
+               talloc_free(guid);
+       }
+
+       return MAPI_E_SUCCESS;
+}
+
+
+/**
+   \details Map a PermanentEntryID structure into a SBinary_r
+   structure (for PR_ENTRYID and PR_EMS_AB_PARENT_ENTRYID properties)
+
+   \param mem_ctx pointer to the memory context
+   \param permEntryID pointer to the Permanent EntryID structure
+   \param pointer to the Binary_r structure the server will return
+
+   \return MAPI_E_SUCCESS on success, otherwise MAPI_E_INVALID_PARAMETER
+ */
+_PUBLIC_ enum MAPISTATUS emsabp_PermanentEntryID_to_Binary_r(TALLOC_CTX *mem_ctx,
+                                                            struct PermanentEntryID *permEntryID,
+                                                            struct Binary_r *bin)
+{
+       /* Sanity checks */
+       OPENCHANGE_RETVAL_IF(!permEntryID, MAPI_E_INVALID_PARAMETER, NULL);
+       OPENCHANGE_RETVAL_IF(!bin, MAPI_E_INVALID_PARAMETER, NULL);
+
+       /* Remove const char * size and replace it with effective dn string length */
+       bin->cb = sizeof (*permEntryID) - 4 + strlen(permEntryID->dn) + 1;
+       bin->lpb = talloc_array(mem_ctx, uint8_t, bin->cb);
+
+       /* Copy PermanantEntryID intro bin->lpb */
+       memset(bin->lpb, 0, bin->cb);
+       bin->lpb[0] = permEntryID->ID_type;
+       bin->lpb[1] = permEntryID->R1;
+       bin->lpb[2] = permEntryID->R2;
+       bin->lpb[3] = permEntryID->R3;
+       memcpy(bin->lpb + 4, permEntryID->ProviderUID.ab, 16);
+       bin->lpb[20] = (permEntryID->R4 & 0xFF);
+       bin->lpb[21] = ((permEntryID->R4 >> 8)  & 0xFF);
+       bin->lpb[22] = ((permEntryID->R4 >> 16) & 0xFF);
+       bin->lpb[23] = ((permEntryID->R4 >> 24) & 0xFF);
+       bin->lpb[24] = (permEntryID->DisplayType & 0xFF);
+       bin->lpb[25] = ((permEntryID->DisplayType >> 8)  & 0xFF);
+       bin->lpb[26] = ((permEntryID->DisplayType >> 16) & 0xFF);
+       bin->lpb[27] = ((permEntryID->DisplayType >> 24) & 0xFF);
+       memcpy(bin->lpb + 28, permEntryID->dn, strlen(permEntryID->dn) + 1);
+
+       return MAPI_E_SUCCESS;
+}
+
+
+/**
+   \details Builds the SRow array entry for the specified table
+   record.
+
+   \param mem_ctx pointer to the memory context
+   \param emsabp_ctx pointer to the EMSABP context
+   \param aRow pointer to the SRow structure where results will be
+   stored
+   \param dwFlags flags controlling whether strings should be unicode
+   encoded or not
+   \param permEntryID pointer to the current record Permanent
+   EntryID
+   \param parentPermEntryID pointer to the parent record Permanent
+   EntryID
+   \param msg pointer to the LDB message for current record
+   \param child boolean value specifying whether current record is
+   root or child within containers hierarchy
+
+   \return MAPI_E_SUCCESS on success, otherwise MAPI error
+ */
+_PUBLIC_ enum MAPISTATUS emsabp_table_fetch_attrs(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
+                                                 struct SRow *aRow, uint32_t dwFlags,
+                                                 struct PermanentEntryID *permEntryID,
+                                                 struct PermanentEntryID *parentPermEntryID,
+                                                 struct ldb_message *msg, bool child)
+{
+       enum MAPISTATUS                 retval;
+       struct SPropTagArray            *SPropTagArray;
+       struct SPropValue               lpProps;
+       uint32_t                        i;
+       uint32_t                        containerID = 0;
+       const char                      *dn = NULL;
+
+       /* Step 1. Build the array of properties to fetch and map */
+       if (child == false) {
+               SPropTagArray = set_SPropTagArray(mem_ctx, 0x6,
+                                                 PR_ENTRYID,
+                                                 PR_CONTAINER_FLAGS,
+                                                 PR_DEPTH,
+                                                 PR_EMS_AB_CONTAINERID,
+                                                 ((dwFlags & NspiUnicodeStrings) ? PR_DISPLAY_NAME_UNICODE : PR_DISPLAY_NAME),
+                                                 PR_EMS_AB_IS_MASTER);
+       } else {
+               SPropTagArray = set_SPropTagArray(mem_ctx, 0x7,
+                                                 PR_ENTRYID,
+                                                 PR_CONTAINER_FLAGS,
+                                                 PR_DEPTH,
+                                                 PR_EMS_AB_CONTAINERID,
+                                                 ((dwFlags & NspiUnicodeStrings) ? PR_DISPLAY_NAME_UNICODE : PR_DISPLAY_NAME),
+                                                 PR_EMS_AB_IS_MASTER,
+                                                 PR_EMS_AB_PARENT_ENTRYID);
+       }
+
+       /* Step 2. Allocate SPropValue array and update SRow cValues field */
+       aRow->ulAdrEntryPad = 0x0;
+       aRow->cValues = 0x0;
+       aRow->lpProps = talloc_zero(mem_ctx, struct SPropValue);
+
+       /* Step 3. Global Address List or real container */
+       if (!msg) {
+               /* Global Address List record is constant */
+               for (i = 0; i < SPropTagArray->cValues; i++) {
+                       lpProps.ulPropTag = SPropTagArray->aulPropTag[i];
+                       lpProps.dwAlignPad = 0x0;
+
+                       switch (SPropTagArray->aulPropTag[i]) {
+                       case PR_ENTRYID:
+                               emsabp_PermanentEntryID_to_Binary_r(mem_ctx, permEntryID, &(lpProps.value.bin));
+                               break;
+                       case PR_CONTAINER_FLAGS:
+                               lpProps.value.l =  AB_RECIPIENTS | AB_UNMODIFIABLE;
+                               break;
+                       case PR_DEPTH:
+                               lpProps.value.l = 0x0;
+                               break;
+                       case PR_EMS_AB_CONTAINERID:
+                               lpProps.value.l = 0x0;
+                               break;
+                       case PR_DISPLAY_NAME:
+                               lpProps.value.lpszA = NULL;
+                               break;
+                       case PR_DISPLAY_NAME_UNICODE:
+                               lpProps.value.lpszW = NULL;
+                               break;
+                       case PR_EMS_AB_IS_MASTER:
+                               lpProps.value.b = false;
+                               break;
+                       default:
+                               break;
+                       }
+                       SRow_addprop(aRow, lpProps);
+                       /* SRow_addprop internals overwrite with MAPI_E_NOT_FOUND when data is NULL */
+                       if (SPropTagArray->aulPropTag[i] == PR_DISPLAY_NAME || 
+                           SPropTagArray->aulPropTag[i] == PR_DISPLAY_NAME_UNICODE) {
+                               aRow->lpProps[aRow->cValues - 1].value.lpszA = NULL;
+                               aRow->lpProps[aRow->cValues - 1].value.lpszW = NULL;
+                       }
+               }
+       } else {
+               for (i = 0; i < SPropTagArray->cValues; i++) {
+                       lpProps.ulPropTag = SPropTagArray->aulPropTag[i];
+                       lpProps.dwAlignPad = 0x0;
+
+                       switch (SPropTagArray->aulPropTag[i]) {
+                       case PR_ENTRYID:
+                               emsabp_PermanentEntryID_to_Binary_r(mem_ctx, permEntryID, &(lpProps.value.bin));
+                               break;
+                       case PR_CONTAINER_FLAGS:
+                               switch (child) {
+                               case true:
+                                       lpProps.value.l = AB_RECIPIENTS | AB_SUBCONTAINERS | AB_UNMODIFIABLE;
+                                       break;
+                               case false:
+                                       lpProps.value.l = AB_RECIPIENTS | AB_UNMODIFIABLE;
+                               }
+                               break;
+                       case PR_DEPTH:
+                               switch (child) {
+                               case true:
+                                       lpProps.value.l = 0x1;
+                                       break;
+                               case false:
+                                       lpProps.value.l = 0x0;
+                                       break;
+                               }
+                               break;
+                       case PR_EMS_AB_CONTAINERID:
+                               dn = ldb_msg_find_attr_as_string(msg, "distinguishedName", NULL);
+                               retval = emsabp_tdb_fetch_MId(emsabp_ctx->tdb_ctx, dn, &containerID);
+                               if (retval) {
+                                       retval = emsabp_tdb_insert(emsabp_ctx->tdb_ctx, dn);
+                                       OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
+                                       retval = emsabp_tdb_fetch_MId(emsabp_ctx->tdb_ctx, dn, &containerID);
+                                       OPENCHANGE_RETVAL_IF(retval, MAPI_E_CORRUPT_STORE, NULL);
+                               }
+                               lpProps.value.l = containerID;
+                               break;
+                       case PR_DISPLAY_NAME:
+                               lpProps.value.lpszA = talloc_strdup(mem_ctx, ldb_msg_find_attr_as_string(msg, "displayName", NULL));
+                               if (!lpProps.value.lpszA) {
+                                       lpProps.ulPropTag &= 0xFFFF0000;
+                                       lpProps.ulPropTag += PT_ERROR;
+                               }
+                               break;
+                       case PR_DISPLAY_NAME_UNICODE:
+                               lpProps.value.lpszW = talloc_strdup(mem_ctx, ldb_msg_find_attr_as_string(msg, "displayName", NULL));
+                               if (!lpProps.value.lpszW) {
+                                       lpProps.ulPropTag &= 0xFFFF0000;
+                                       lpProps.ulPropTag += PT_ERROR;
+                               }
+                               break;
+                       case PR_EMS_AB_IS_MASTER:
+                               /* FIXME: harcoded value - no load balancing */
+                               lpProps.value.l = 0x0;
+                               break;
+                       case PR_EMS_AB_PARENT_ENTRYID:
+                               emsabp_PermanentEntryID_to_Binary_r(mem_ctx, parentPermEntryID, &lpProps.value.bin);
+                               break;
+                       default:
+                               break;
+                       }
+                       SRow_addprop(aRow, lpProps);
+               }
+       }
+
+       return MAPI_E_SUCCESS;
+}
+
+
+/**
+   \details Retrieve and build the HierarchyTable requested by
+   GetSpecialTable NSPI call
+
+   \param mem_ctx pointer to the memory context
+   \param emsabp_ctx pointer to the EMSABP context
+   \param dwFlags flags controlling whether strings should be UNICODE
+   or not
+   \param SRowSet pointer on pointer to the output SRowSet array
+
+   \return MAPI_E_SUCCESS on success, otherwise MAPI_E_CORRUPT_STORE
+ */
+_PUBLIC_ enum MAPISTATUS emsabp_get_HierarchyTable(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
+                                                  uint32_t dwFlags, struct SRowSet **SRowSet)
+{
+       enum MAPISTATUS                 retval;
+       struct SRow                     *aRow;
+       struct PermanentEntryID         gal;
+       struct PermanentEntryID         parentPermEntryID;
+       struct PermanentEntryID         permEntryID;
+       enum ldb_scope                  scope = LDB_SCOPE_SUBTREE;
+       struct ldb_request              *req;
+       struct ldb_result               *res = NULL;
+       char                            *ldb_filter;
+       struct ldb_dn                   *ldb_dn = NULL;
+       struct ldb_control              **controls;
+       const char * const              recipient_attrs[] = { "*", NULL };
+       const char                      *control_strings[2] = { "server_sort:0:0:displayName", NULL };
+       const char                      *addressBookRoots;
+       int                             ret;
+       uint32_t                        aRow_idx;
+       uint32_t                        i;
+
+       /* Step 1. Build the 'Global Address List' object using PermanentEntryID */
+       aRow = talloc_zero(mem_ctx, struct SRow);
+       OPENCHANGE_RETVAL_IF(!aRow, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
+       aRow_idx = 0;
+
+       retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, NULL, &gal);
+       OPENCHANGE_RETVAL_IF(retval, retval, aRow);
+
+       retval = emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &gal, NULL, NULL, false);
+       aRow_idx++;
+
+       /* Step 2. Retrieve the object pointed by addressBookRoots attribute: 'All Address Lists' */
+       ldb_filter = talloc_strdup(emsabp_ctx->mem_ctx, "(addressBookRoots=*)");
+       ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res,
+                        ldb_get_default_basedn(emsabp_ctx->conf_ctx),
+                        scope, recipient_attrs, ldb_filter);
+       talloc_free(ldb_filter);
+       OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count, MAPI_E_CORRUPT_STORE, aRow);
+
+       addressBookRoots = ldb_msg_find_attr_as_string(res->msgs[0], "addressBookRoots", NULL);
+       OPENCHANGE_RETVAL_IF(!addressBookRoots, MAPI_E_CORRUPT_STORE, aRow);
+       talloc_free(res);
+
+       ldb_dn = ldb_dn_new(emsabp_ctx->mem_ctx, emsabp_ctx->conf_ctx, addressBookRoots);
+       OPENCHANGE_RETVAL_IF(!ldb_dn_validate(ldb_dn), MAPI_E_CORRUPT_STORE, aRow);
+
+       scope = LDB_SCOPE_BASE;
+       ret = ldb_search(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, &res, ldb_dn, 
+                        scope, recipient_attrs, NULL);
+       OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS || !res->count || res->count != 1, MAPI_E_CORRUPT_STORE, aRow);
+
+       aRow = talloc_realloc(mem_ctx, aRow, struct SRow, aRow_idx + 1);
+       retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, res->msgs[0], &parentPermEntryID);
+       emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &parentPermEntryID, NULL, res->msgs[0], false);
+       aRow_idx++;
+       talloc_free(res);
+
+       /* Step 3. Retrieve 'All Address Lists' subcontainers */
+       res = talloc_zero(mem_ctx, struct ldb_result);
+       OPENCHANGE_RETVAL_IF(!res, MAPI_E_NOT_ENOUGH_RESOURCES, aRow);
+
+       controls = ldb_parse_control_strings(emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx, control_strings);
+       ret = ldb_build_search_req(&req, emsabp_ctx->conf_ctx, emsabp_ctx->mem_ctx,
+                                  ldb_dn, LDB_SCOPE_SUBTREE, "(purportedSearch=*)",
+                                  recipient_attrs, controls, res, ldb_search_default_callback, NULL);
+
+       if (ret != LDB_SUCCESS) {
+               talloc_free(res);
+               OPENCHANGE_RETVAL_IF(ret != LDB_SUCCESS, MAPI_E_CORRUPT_STORE, aRow);
+       }
+
+       ret = ldb_request(emsabp_ctx->conf_ctx, req);
+       if (ret == LDB_SUCCESS) {
+               ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+       }
+       talloc_free(req);
+       
+       if (ret != LDB_SUCCESS || !res->count) {
+               talloc_free(res);
+               OPENCHANGE_RETVAL_IF(1, MAPI_E_CORRUPT_STORE, aRow);
+       }
+
+       aRow = talloc_realloc(mem_ctx, aRow, struct SRow, aRow_idx + res->count + 1);
+
+       for (i = 0; res->msgs[i]; i++) {
+               retval = emsabp_set_PermanentEntryID(emsabp_ctx, DT_CONTAINER, res->msgs[i], &permEntryID);
+               emsabp_table_fetch_attrs(mem_ctx, emsabp_ctx, &aRow[aRow_idx], dwFlags, &permEntryID, &parentPermEntryID, res->msgs[i], true);
+               talloc_free(permEntryID.dn);
+               memset(&permEntryID, 0, sizeof (permEntryID));
+               aRow_idx++;
+       }
+       talloc_free(res);
+       talloc_free(parentPermEntryID.dn);
+
+       /* Step 4. Build output SRowSet */
+       SRowSet[0]->cRows = aRow_idx;
+       SRowSet[0]->aRow = aRow;
+
+       return MAPI_E_SUCCESS;
+}
+
+
+/**
+   \details Retrieve and build the CreationTemplates Table requested
+   by GetSpecialTable NSPI call
+
+   \param mem_ctx pointer to the memory context
+   \param emsabp_ctx pointer to the EMSABP context
+   \param dwFlags flags controlling whether strings should be UNICODE
+   or not
+   \param SRowSet pointer on pointer to the output SRowSet array
+
+   \return MAPI_E_SUCCESS on success, otherwise MAPI_E_CORRUPT_STORE 
+ */
+_PUBLIC_ enum MAPISTATUS emsabp_get_CreationTemplatesTable(TALLOC_CTX *mem_ctx, struct emsabp_context *emsabp_ctx,
+                                                          uint32_t dwFlags, struct SRowSet **SRowSet)
+{
+       return MAPI_E_SUCCESS;
+}
diff --git a/mapiproxy/servers/default/nspi/emsabp_tdb.c b/mapiproxy/servers/default/nspi/emsabp_tdb.c
new file mode 100644 (file)
index 0000000..38e2e9c
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+   OpenChange Server implementation.
+
+   EMSABP: Address Book Provider implementation
+
+   Copyright (C) Julien Kerihuel 2006-2009.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+   \file emsabp_tdb.c
+
+   \brief EMSABP TDB database API
+*/
+
+#include "mapiproxy/dcesrv_mapiproxy.h"
+#include "mapiproxy/libmapiproxy.h"
+#include "dcesrv_exchange_nsp.h"
+#include <util/debug.h>
+
+/* This hack only works for single mode - need to move the context
+ * somewhere else. Possible libmapiproxy and use tdb_reopen */
+/* static TDB_CONTEXT  *tdb_ctx = NULL; */
+
+/**
+   \details Open EMSABP TDB database
+
+   \param mem_ctx pointer to the memory context
+   \param lp_ctx pointer to the loadparm context
+   \param ev pointer to the event context
+
+   \return MAPI_E_SUCCESS on success, otherwise MAPI error
+ */
+_PUBLIC_ TDB_CONTEXT *emsabp_tdb_init(TALLOC_CTX *mem_ctx, 
+                                     struct loadparm_context *lp_ctx)
+{
+       enum MAPISTATUS                 retval;
+       TDB_CONTEXT                     *tdb_ctx;
+       TDB_DATA                        key;
+       TDB_DATA                        dbuf;
+       int                             ret;
+
+       /* Sanity checks */
+       if (!lp_ctx) return NULL;
+
+       /* Step 0. Retrieve a TDB context pointer on the emsabp_tdb database */
+       tdb_ctx = mapiproxy_server_emsabp_tdb_init(lp_ctx);
+       if (!tdb_ctx) return NULL;
+
+       /* Step 1. If EMSABP_TDB_DATA_REC doesn't exist, create it */
+       retval = emsabp_tdb_fetch(tdb_ctx, EMSABP_TDB_DATA_REC, &dbuf);
+       if (retval == MAPI_E_NOT_FOUND) {
+               key.dptr = (unsigned char *) EMSABP_TDB_DATA_REC;
+               key.dsize = strlen(EMSABP_TDB_DATA_REC);
+
+               dbuf.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%x", EMSABP_TDB_MID_START);
+               dbuf.dsize = strlen((const char *)dbuf.dptr);
+
+               ret = tdb_store(tdb_ctx, key, dbuf, TDB_INSERT);
+               if (ret == -1) {
+                       DEBUG(3, ("[%s:%d]: Unable to create %s record: %s\n", __FUNCTION__, __LINE__,
+                                 EMSABP_TDB_DATA_REC, tdb_errorstr(tdb_ctx)));
+                       tdb_close(tdb_ctx);
+                       return NULL;
+               }
+       } else {
+               free (dbuf.dptr);
+       }
+
+       return tdb_ctx;
+}
+
+
+/**
+   \details Close EMSABP TDB database
+
+   \return MAPI_E_SUCCESS on success, otherwise
+   MAPI_E_INVALID_PARAMETER
+ */
+_PUBLIC_ enum MAPISTATUS emsabp_tdb_close(TDB_CONTEXT *tdb_ctx)
+{
+       /* Sanity checks */
+       OPENCHANGE_RETVAL_IF(!tdb_ctx, MAPI_E_INVALID_PARAMETER, NULL);
+
+       tdb_close(tdb_ctx);
+       DEBUG(0, ("TDB database closed\n"));
+
+       return MAPI_E_SUCCESS;
+}
+
+
+/**
+   \details Fetch an element within a TDB database given its key
+
+   \param keyname pointer to the TDB key to fetch
+   \param result pointer on TDB results
+
+   \return MAPI_E_SUCCESS on success, otherwise MAPI_E_NOT_FOUND
+ */
+_PUBLIC_ enum MAPISTATUS emsabp_tdb_fetch(TDB_CONTEXT *tdb_ctx,
+                                         const char *keyname,
+                                         TDB_DATA *result)
+{
+       TDB_DATA        key;
+       TDB_DATA        dbuf;
+       size_t          keylen;
+
+       /* Sanity checks */
+       OPENCHANGE_RETVAL_IF(!tdb_ctx, MAPI_E_NOT_INITIALIZED, NULL);
+       OPENCHANGE_RETVAL_IF(!keyname, MAPI_E_INVALID_PARAMETER, NULL);
+
+       keylen = strlen(keyname);
+       OPENCHANGE_RETVAL_IF(!keylen, MAPI_E_INVALID_PARAMETER, NULL);
+
+       key.dptr = (unsigned char *)keyname;
+       key.dsize = keylen;
+
+       dbuf = tdb_fetch(tdb_ctx, key);
+       OPENCHANGE_RETVAL_IF(!dbuf.dptr, MAPI_E_NOT_FOUND, NULL);
+       OPENCHANGE_RETVAL_IF(!dbuf.dsize, MAPI_E_NOT_FOUND, NULL);
+
+       if (!result) {
+               free (dbuf.dptr);
+       } else {
+               *result = dbuf;
+       }
+
+       return MAPI_E_SUCCESS;
+}
+
+
+/**
+   \details Retrieve the Minimal EntryID associated to a given DN
+
+   \param tdb_ctx pointer to the EMSABP TDB context
+   \param keyname pointer to the TDB key to search for
+   \param MId pointer on the integer the function returns
+
+   \return MAPI_E_SUCCESS on success, otherwise MAPI error
+ */
+_PUBLIC_ enum MAPISTATUS emsabp_tdb_fetch_MId(TDB_CONTEXT *tdb_ctx,
+                                             const char *keyname,
+                                             uint32_t *MId)
+{
+       TDB_DATA        key;
+       TDB_DATA        dbuf;
+
+       /* Sanity checks */
+       OPENCHANGE_RETVAL_IF(!tdb_ctx, MAPI_E_NOT_INITIALIZED, NULL);
+       OPENCHANGE_RETVAL_IF(!keyname, MAPI_E_INVALID_PARAMETER, NULL);
+       OPENCHANGE_RETVAL_IF(!MId, MAPI_E_INVALID_PARAMETER, NULL);
+
+       key.dptr = (unsigned char *) keyname;
+       key.dsize = strlen(keyname);
+
+       dbuf = tdb_fetch(tdb_ctx, key);
+       OPENCHANGE_RETVAL_IF(!dbuf.dptr, MAPI_E_NOT_FOUND, NULL);
+       OPENCHANGE_RETVAL_IF(!dbuf.dsize, MAPI_E_NOT_FOUND, NULL);
+
+       *MId = strtol((const char *)dbuf.dptr, NULL, 16);
+       free(dbuf.dptr);
+
+       return MAPI_E_SUCCESS;
+}
+
+
+/**
+   \details Insert an element into TDB database
+
+   \param tdb_ctx pointer to the EMSABP TDB context
+   \param keyname pointer to the TDB key name string
+
+   \return MAPI_E_SUCCESS on success, otherwise MAPI error
+ */
+_PUBLIC_ enum MAPISTATUS emsabp_tdb_insert(TDB_CONTEXT *tdb_ctx,
+                                          const char *keyname)
+{
+       enum MAPISTATUS retval;
+       TALLOC_CTX      *mem_ctx;
+       TDB_DATA        key;
+       TDB_DATA        dbuf;
+       int             index;
+       int             ret;
+
+       /* Sanity checks */
+       OPENCHANGE_RETVAL_IF(!tdb_ctx, MAPI_E_NOT_INITIALIZED, NULL);
+       OPENCHANGE_RETVAL_IF(!keyname, MAPI_E_INVALID_PARAMETER, NULL);
+
+       mem_ctx = talloc_init("emsabp_tdb_insert");
+       OPENCHANGE_RETVAL_IF(!mem_ctx, MAPI_E_NOT_ENOUGH_RESOURCES, NULL);
+
+       /* Step 1. Check if the record already exists */
+       retval = emsabp_tdb_fetch(tdb_ctx, keyname, &dbuf);
+       OPENCHANGE_RETVAL_IF(!retval, ecExiting, mem_ctx);
+
+       /* Step 2. Retrieve the latest TDB data value inserted */
+       retval = emsabp_tdb_fetch(tdb_ctx, EMSABP_TDB_DATA_REC, &dbuf);
+       OPENCHANGE_RETVAL_IF(retval, retval, mem_ctx);
+
+       index = strtol((const char *)dbuf.dptr, NULL, 16);
+       index += 1;
+
+       free(dbuf.dptr);
+
+       dbuf.dptr = (unsigned char *)talloc_asprintf(mem_ctx, "0x%x", index);
+       dbuf.dsize = strlen((const char *)dbuf.dptr);
+
+       /* Step 3. Insert the new record */
+       key.dptr = (unsigned char *)keyname;
+       key.dsize = strlen(keyname);
+       
+       ret = tdb_store(tdb_ctx, key, dbuf, TDB_INSERT);
+       OPENCHANGE_RETVAL_IF(ret == -1, MAPI_E_CORRUPT_STORE, mem_ctx);
+
+       /* Step 4. Update Data record */
+       talloc_free(key.dptr);
+       key.dptr = (unsigned char *) EMSABP_TDB_DATA_REC;
+       key.dsize = strlen((const char *)key.dptr);
+
+       ret = tdb_store(tdb_ctx, key, dbuf, TDB_MODIFY);
+       OPENCHANGE_RETVAL_IF(ret == -1, MAPI_E_CORRUPT_STORE, mem_ctx);
+
+       talloc_free(mem_ctx);
+
+       return MAPI_E_SUCCESS;
+}