- Add a very preliminary and light libmapistore implementation.
authorJulien Kerihuel <j.kerihuel@openchange.org>
Mon, 9 Feb 2009 17:13:52 +0000 (17:13 +0000)
committerJulien Kerihuel <j.kerihuel@openchange.org>
Mon, 9 Feb 2009 17:13:52 +0000 (17:13 +0000)
mapistore only supplies init/release and add/del backend contexts.
This commit also includes a sqlite3 backend skeleton (open/close sqlite db)
- A temporary mapistore testing tool has been added locally for implementation checks.

13 files changed:
Makefile
config.mk.in
configure.ac
mapiproxy/libmapistore.pc.in [new file with mode: 0644]
mapiproxy/libmapistore/backends/mapistore_sqlite3.c [new file with mode: 0644]
mapiproxy/libmapistore/backends/mapistore_sqlite3.h [new file with mode: 0644]
mapiproxy/libmapistore/mapistore.h [new file with mode: 0644]
mapiproxy/libmapistore/mapistore_backend.c [new file with mode: 0644]
mapiproxy/libmapistore/mapistore_errors.h [new file with mode: 0644]
mapiproxy/libmapistore/mapistore_interface.c [new file with mode: 0644]
mapiproxy/libmapistore/mapistore_private.h [new file with mode: 0644]
mapiproxy/libmapistore/mapistore_processing.c [new file with mode: 0644]
mapiproxy/libmapistore/tests/mapistore_test.c [new file with mode: 0644]

index ac4b7645097957457375ebd95335680e609c1561..ae33f7b709584554a90993acb82104dcbd5870b7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -708,23 +708,37 @@ LIBMAPISERVER_SO_VERSION = 0
 
 .PHONY: mapiproxy
 
-mapiproxy:     idl                                                     \
-               libmapiproxy                                            \
-               libmapiserver                                           \
-               mapiproxy/dcesrv_mapiproxy.$(SHLIBEXT)                  \
-               mapiproxy-modules                                       \
-               mapiproxy-servers
-
-mapiproxy-install: mapiproxy mapiproxy-modules-install mapiproxy-servers-install libmapiproxy-install libmapiserver-install
+mapiproxy:             idl                                     \
+                       libmapiproxy                            \
+                       libmapiserver                           \
+                       libmapistore                            \
+                       mapiproxy/dcesrv_mapiproxy.$(SHLIBEXT)  \
+                       mapiproxy-modules                       \
+                       mapiproxy-servers
+
+mapiproxy-install:     mapiproxy                               \
+                       mapiproxy-modules-install               \
+                       mapiproxy-servers-install               \
+                       libmapiproxy-install                    \
+                       libmapiserver-install                   \
+                       libmapistore-install
        $(INSTALL) -d $(DESTDIR)$(SERVER_MODULESDIR)
        $(INSTALL) -m 0755 mapiproxy/dcesrv_mapiproxy.$(SHLIBEXT) $(DESTDIR)$(SERVER_MODULESDIR)
 
-mapiproxy-uninstall: mapiproxy-modules-uninstall mapiproxy-servers-uninstall libmapiproxy-uninstall libmapiserver-uninstall
+mapiproxy-uninstall:   mapiproxy-modules-uninstall             \
+                       mapiproxy-servers-uninstall             \
+                       libmapiproxy-uninstall                  \
+                       libmapiserver-uninstall                 \
+                       libmapistore-uninstall
        rm -f $(DESTDIR)$(SERVER_MODULESDIR)/dcesrv_mapiproxy.*
        rm -f $(DESTDIR)$(libdir)/libmapiproxy.*
        rm -f $(DESTDIR)$(includedir)/libmapiproxy.h
 
-mapiproxy-clean:: mapiproxy-modules-clean mapiproxy-servers-clean libmapiproxy-clean libmapiserver-clean
+mapiproxy-clean::      mapiproxy-modules-clean                 \
+                       mapiproxy-servers-clean                 \
+                       libmapiproxy-clean                      \
+                       libmapiserver-clean                     \
+                       libmapistore-clean
        rm -f mapiproxy/*.o mapiproxy/*.po
        rm -f mapiproxy/dcesrv_mapiproxy.$(SHLIBEXT)
 
@@ -817,6 +831,89 @@ mapiproxy/libmapiserver.$(SHLIBEXT).$(LIBMAPISERVER_SO_VERSION): libmapiserver.$
        ln -fs $< $@
 
 
+################
+# libmapistore
+################
+
+libmapistore:  mapiproxy/libmapistore.$(SHLIBEXT).$(PACKAGE_VERSION)   \
+               $(OC_MAPISTORE)                                         \
+               mapistore_test
+
+libmapistore-install:  $(OC_MAPISTORE_INSTALL)
+       $(INSTALL) -m 0755 mapiproxy/libmapistore.$(SHLIBEXT).$(PACKAGE_VERSION) $(DESTDIR)$(libdir)
+       ln -sf libmapistore.$(SHLIBEXT).$(PACKAGE_VERSION) $(DESTDIR)$(libdir)/libmapistore.$(SHLIBEXT)
+       $(INSTALL) -d $(DESTDIR)$(includedir)/mapistore
+       $(INSTALL) -m 0644 mapiproxy/libmapistore/mapistore.h $(DESTDIR)$(includedir)/mapistore/
+       $(INSTALL) -m 0644 mapiproxy/libmapistore/mapistore_errors.h $(DESTDIR)$(includedir)/mapistore/
+       $(INSTALL) -m 0644 mapiproxy/libmapiserver.pc $(DESTDIR)$(libdir)/pkgconfig
+
+libmapistore-clean:    $(OC_MAPISTORE_CLEAN)
+       rm -f mapiproxy/libmapistore/*.po mapiproxy/libmapistore/*.o
+       rm -f mapiproxy/libmapistore.$(SHLIBEXT).$(PACKAGE_VERSION)
+       rm -f mapiproxy/libmapistore.$(SHLIBEXT).$(LIBMAPISTORE_SO_VERSION)
+
+libmapistore-uninstall:        $(OC_MAPISTORE_UNINSTALL)
+       rm -f $(DESTDIR)$(libdir)/libmapistore.*
+       rm -rf $(DESTDIR)$(includedir)/mapistore
+       rm -f $(DESTDIR)$(libdir)/pkgconfig/libmapistore.pc
+
+libmapistore-distclean: libmapistore-clean
+       rm -f mapiproxy/libmapistore.pc
+
+distclean:: libmapistore-distclean
+
+mapiproxy/libmapistore.$(SHLIBEXT).$(PACKAGE_VERSION):         mapiproxy/libmapistore/mapistore_interface.po   \
+                                                       mapiproxy/libmapistore/mapistore_processing.po  \
+                                                       mapiproxy/libmapistore/mapistore_backend.po
+       @$(CC) -o $@ $(DSOOPT) -Wl,-soname,libmapistore.$(SHLIBEXT).$(LIBMAPISTORE_SO_VERSION) $^ -L. $(LIBS)
+
+mapiproxy/libmapistore.$(SHLIBEXT).$(LIBMAPISTORE_SO_VERSION): libmapistore.$(SHLIBEXT).$(PACKAGE_VERSION)
+
+#####################
+# mapistore backends
+#####################
+
+mapistore_sqlite3: mapiproxy/libmapistore/backends/mapistore_sqlite3.$(SHLIBEXT)
+
+mapistore_sqlite3-install:
+       $(INSTALL) -d $(DESTDIR)$(libdir)/mapistore_backends
+       $(INSTALL) -m 0755 mapiproxy/libmapistore/backends/mapistore_sqlite3.$(SHLIBEXT) $(DESTDIR)$(libdir)/mapistore_backends/
+
+mapistore_sqlite3-uninstall:
+       rm -rf $(DESTDIR)$(libdir)/mapistore_backends
+
+mapistore_sqlite3-clean:
+       rm -f mapiproxy/libmapistore/backends/mapistore_sqlite3.o
+       rm -f mapiproxy/libmapistore/backends/mapistore_sqlite3.po
+
+clean:: mapistore_sqlite3-clean
+
+mapistore_sqlite3-distclean: mapistore_sqlite3-clean
+       rm -f mapiproxy/libmapistore/backends/mapistore_sqlite3.so
+
+distclean:: mapistore_sqlite3-distclean
+
+mapiproxy/libmapistore/backends/mapistore_sqlite3.$(SHLIBEXT): mapiproxy/libmapistore/backends/mapistore_sqlite3.po
+       @echo "Linking mapistore module $@"
+       @$(CC) $(SQLITE_CFLAGS) -o $@ $(DSOOPT) $^ -L. $(LIBS) $(SQLITE_LIBS)
+
+#######################
+# mapistore test tools
+#######################
+
+mapistore_test: bin/mapistore_test
+
+bin/mapistore_test:    mapiproxy/libmapistore/tests/mapistore_test.o           \
+                       mapiproxy/libmapistore.$(SHLIBEXT).$(PACKAGE_VERSION)
+       @echo "Linking $@"
+       @$(CC) -o $@ $^ $(LIBS) -lpopt
+
+mapistore_clean:
+       rm -f mapiproxy/libmapistore/tests/*.o
+       rm -f bin/mapistore_test
+
+clean:: mapistore_clean
+
 ####################
 # mapiproxy modules
 ####################
index 2f8e37bc973840d570a0639f26248b327acf588d..72de2628a546f775bf835093c931db4b97aa74dd 100644 (file)
@@ -29,8 +29,10 @@ pythondir=@pythondir@
 sambaprefix=@sambaprefix@
 
 DSOOPT=-shared -fPIC
-CFLAGS=@CFLAGS@ -I. -Wall -Wmissing-prototypes -Wstrict-prototypes -g3 \
-          -DDEFAULT_LDIF=\"$(datadir)/setup/profiles\" 
+CFLAGS=@CFLAGS@ -I. -Wall -Wmissing-prototypes -Wstrict-prototypes -g3         \
+          -DDEFAULT_LDIF=\"$(datadir)/setup/profiles\"                         \
+          -DMAPISTORE_BACKEND_INSTALLDIR=\"$(libdir)/mapistore_backends\"      \
+          -DMAPISTORE_MAPPING_PATH=\"$(prefix)/private/mapistore\"     
 
 # This value should be determined by configure at some point
 SHLIBEXT=so
@@ -78,6 +80,14 @@ OC_SERVER=@OC_SERVER@
 OC_SERVER_INSTALL=@OC_SERVER_INSTALL@
 OC_SERVER_UNINSTALL=@OC_SERVER_UNINSTALL@
 
+# MAPISTORE BACKENDS
+OC_MAPISTORE=@OC_MAPISTORE@
+OC_MAPISTORE_CLEAN=@OC_MAPISTORE_CLEAN@
+OC_MAPISTORE_INSTALL=@OC_MAPISTORE_INSTALL@
+OC_MAPISTORE_UNINSTALL=@OC_MAPISTORE_UNINSTALL@
+
+SQLITE_CFLAGS=@SQLITE_CFLAGS@
+SQLITE_LIBS=@SQLITE_LIBS@
 
 # SWIG
 SWIGDIRS-ALL=@SWIGDIRSALL@
index ebdfbf3cbbe6a1f905d4aca9ce2be7f7e5b6c962..798c37861913eb4cfe3e1af0c151a5bb84ccf90e 100644 (file)
@@ -92,14 +92,16 @@ if test "x$1_set" != "xset"; then
                OC_$2_INSTALL+=" $1-install"
                OC_$2_UNINSTALL+=" $1-uninstall"
        ;;
-       SERVER)
+       SERVER|MAPISTORE)
                OC_$2+=" $1"
+               OC_$2_CLEAN+="$1-clean"
                OC_$2_INSTALL+=" $1-install"
                OC_$2_UNINSTALL+=" $1-uninstall"
        ;;
    esac
 
    AC_SUBST(OC_$2)
+   AC_SUBST(OC_$2_CLEAN)
    AC_SUBST(OC_$2_INSTALL)
    AC_SUBST(OC_$2_UNINSTALL)
 
@@ -357,6 +359,21 @@ if test x"$enable_libmagic" = x"yes"; then
 fi
 
 
+dnl ##########################################################################
+dnl libmapistore backends dependencies
+dnl ##########################################################################
+
+dnl --------------------------------------------------------------------------
+dnl Check for sqlite3
+dnl --------------------------------------------------------------------------
+PKG_CHECK_MODULES(SQLITE, sqlite3, SQLITEFOUND=yes, [SQLITEFOUND=no])
+AC_SUBST(SQLITE_CFLAGS)
+AC_SUBST(SQLITE_LIBS)
+
+if test x"$SQLITEFOUND" = x"yes"; then
+   OC_RULE_ADD(mapistore_sqlite3, MAPISTORE)
+fi
+
 
 dnl ##########################################################################
 dnl torture dependencies
@@ -448,8 +465,9 @@ dnl ***********************
 dnl Makefiles 
 dnl ***********************
 AC_CONFIG_FILES([config.mk libmapi.pc libmapiadmin.pc libocpf.pc mapiproxy/libmapiproxy.pc
-                mapiproxy/libmapiserver.pc Doxyfile libmapi++/Doxyfile libocpf/Doxyfile 
-                libmapiadmin/Doxyfile libmapi/Doxyfile mapiproxy/Doxyfile utils/mapitest/Doxyfile])
+                mapiproxy/libmapiserver.pc mapiproxy/libmapistore.pc Doxyfile libmapi++/Doxyfile 
+                libocpf/Doxyfile libmapiadmin/Doxyfile libmapi/Doxyfile mapiproxy/Doxyfile 
+                utils/mapitest/Doxyfile])
 AC_OUTPUT
 
 
@@ -495,6 +513,9 @@ OpenChange Configuration (Please review)
           * OpenChange Server:
             - mapiproxy:               $enable_mapiproxy
 
+          * OpenChange mapistore backends:
+            - sqlite3:                 $enable_mapistore_sqlite3
+
           * OpenChange Tools:
             - openchangeclient:        $enable_openchangeclient
             - mapiprofile:             $enable_mapiprofile
diff --git a/mapiproxy/libmapistore.pc.in b/mapiproxy/libmapistore.pc.in
new file mode 100644 (file)
index 0000000..af14e93
--- /dev/null
@@ -0,0 +1,14 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+datarootdir=@prefix@/share
+datadir=@datadir@
+
+Name: MAPISTORE
+Description: MAPI Storage Abstraction Layer library
+Version: @PACKAGE_VERSION@ 
+Libs: -L${libdir} -lmapistore
+Libs.private: @LIBS@
+Cflags: -I${includedir}
+Requires: talloc tdb
diff --git a/mapiproxy/libmapistore/backends/mapistore_sqlite3.c b/mapiproxy/libmapistore/backends/mapistore_sqlite3.c
new file mode 100644 (file)
index 0000000..2b3a369
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+   OpenChange Storage Abstraction Layer library
+   MAPIStore SQLite backend
+
+   OpenChange Project
+
+   Copyright (C) Julien Kerihuel 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/>.
+ */
+
+#include "mapistore_sqlite3.h"
+
+
+/**
+   \details Initialize sqlite3 mapistore backend
+
+   \return MAPISTORE_SUCCESS on success
+ */
+static int sqlite3_init(void)
+{
+       DEBUG(0, ("sqlite3 backend initialized\n"));
+
+       return MAPISTORE_SUCCESS;
+}
+
+
+/**
+   \details Create a connection context to the sqlite3 backend
+
+   \param mem_ctx pointer to the memory context
+   \param uri pointer to the database path
+   \param context_id pointer to the context identifier the function
+   returns
+
+   \return MAPISTORE_SUCCESS on success
+ */
+static int sqlite3_create_context(TALLOC_CTX *mem_ctx, const char *uri, void **private_data)
+{
+       struct sqlite3_context          *sqlite_ctx;
+       sqlite3                         *db;
+       int                             ret;
+
+       DEBUG(0, ("[%s:%d]\n", __FUNCTION__, __LINE__));
+
+       ret = sqlite3_open(uri, &db);
+       if (ret) {
+               DEBUG(3, ("[%s:%d]: %s\n", __FUNCTION__, __LINE__,
+                         sqlite3_errmsg(db)));
+               sqlite3_close(db);
+               return -1;
+       }
+
+       sqlite_ctx = talloc_zero(mem_ctx, struct sqlite3_context);
+       sqlite_ctx->db = db;
+       sqlite_ctx->private_data = NULL;
+
+       *private_data = (void *)sqlite_ctx;
+
+       return MAPISTORE_SUCCESS;
+}
+
+
+/**
+   \details Delete a connection context from the sqlite3 backend
+
+   \return MAPISTORE_SUCCESS on success
+ */
+static int sqlite3_delete_context(void *private_data)
+{
+       struct sqlite3_context  *sqlite_ctx = (struct sqlite3_context *)private_data;
+       int                     ret;
+
+       DEBUG(5, ("[%s:%d]\n", __FUNCTION__, __LINE__));
+
+       if (!private_data) {
+               return MAPISTORE_SUCCESS;
+       }
+
+       ret = sqlite3_close(sqlite_ctx->db);
+       if (ret) return MAPISTORE_ERROR;
+
+       return MAPISTORE_SUCCESS;
+}
+
+
+/**
+   \details Entry point for mapistore SQLite backend
+
+   \return MAPISTORE_SUCCESS on success, otherwise -1
+ */
+int mapistore_init_backend(void)
+{
+       struct mapistore_backend        backend;
+       int                             ret;
+
+       /* Fill in our name */
+       backend.name = "sqlite3";
+       backend.description = "mapistore sqlite3 backend";
+       backend.namespace = "sqlite://";
+
+       /* Fill in all the operations */
+       backend.init = sqlite3_init;
+       backend.create_context = sqlite3_create_context;
+       backend.delete_context = sqlite3_delete_context;
+
+       /* Register ourselves with the MAPIPROXY subsystem */
+       ret = mapistore_backend_register(&backend);
+       if (ret != MAPISTORE_SUCCESS) {
+               DEBUG(0, ("Failed to register the '%s' mapistore backend!\n", backend.name));
+               return ret;
+       }
+
+       return MAPISTORE_SUCCESS;
+}
diff --git a/mapiproxy/libmapistore/backends/mapistore_sqlite3.h b/mapiproxy/libmapistore/backends/mapistore_sqlite3.h
new file mode 100644 (file)
index 0000000..9363cbc
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+   OpenChange Storage Abstraction Layer library
+   MAPIStore SQLite backend
+
+   OpenChange Project
+
+   Copyright (C) Julien Kerihuel 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/>.
+ */
+
+#ifndef        __MAPISTORE_SQLITE3_H
+#define        __MAPISTORE_SQLITE3_H
+
+#include <mapiproxy/libmapistore/mapistore.h>
+#include <mapiproxy/libmapistore/mapistore_errors.h>
+#include <libmapi/dlinklist.h>
+#include <sqlite3.h>
+
+struct sqlite3_context {
+       sqlite3         *db;
+       void            *private_data;
+};
+
+
+__BEGIN_DECLS
+
+int    mapistore_init_backend(void);
+
+__END_DECLS
+
+#endif /* ! __MAPISTORE_SQLITE3_H */
diff --git a/mapiproxy/libmapistore/mapistore.h b/mapiproxy/libmapistore/mapistore.h
new file mode 100644 (file)
index 0000000..cd62e52
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+   OpenChange Storage Abstraction Layer library
+
+   OpenChange Project
+
+   Copyright (C) Julien Kerihuel 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/>.
+ */
+
+#ifndef        __MAPISTORE_H
+#define        __MAPISTORE_H
+
+#ifndef        _GNU_SOURCE
+#define        _GNU_SOURCE
+#endif
+
+#ifndef        _PUBLIC_
+#define        _PUBLIC_
+#endif
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <tdb.h>
+#include <talloc.h>
+#include <util/debug.h>
+
+#define        MAPISTORE_SUCCESS       0
+
+typedef        int (*init_backend_fn) (void);
+
+#define        MAPISTORE_INIT_MODULE   "mapistore_init_backend"
+
+struct mapistore_backend {
+       const char      *name;
+       const char      *description;
+       const char      *namespace;
+
+       int (*init)(void);
+       int (*create_context)(TALLOC_CTX *, const char *, void **);
+       int (*delete_context)(void *);
+};
+
+struct backend_context {
+       const struct mapistore_backend  *backend;
+       void                            *private_data;
+       uint32_t                        context_id;
+};
+
+struct backend_context_list {
+       struct backend_context          *ctx;
+       struct backend_context_list     *prev;
+       struct backend_context_list     *next;
+};
+
+struct processing_context;
+
+struct mapistore_context {
+       struct processing_context       *processing_ctx;
+       struct backend_context_list     *context_list;
+};
+
+#ifndef __BEGIN_DECLS
+#ifdef __cplusplus
+#define __BEGIN_DECLS          extern "C" {
+#define __END_DECLS            }
+#else
+#define __BEGIN_DECLS
+#define __END_DECLS
+#endif
+#endif
+
+__BEGIN_DECLS
+
+/* definitions from mapistore_interface.c */
+struct mapistore_context *mapistore_init(TALLOC_CTX *, const char *);
+int mapistore_release(struct mapistore_context *);
+int mapistore_add_context(struct mapistore_context *, const char *uri, uint32_t *);
+int mapistore_del_context(struct mapistore_context *, uint32_t);
+const char *mapistore_errstr(int);
+
+/* definitions from mapistore_processing.c */
+int mapistore_set_mapping_path(const char *);
+
+/* definitions from mapistore_backend.c */
+extern int     mapistore_backend_register(const void *);
+const char     *mapistore_backend_get_installdir(void);
+init_backend_fn        *mapistore_backend_load(TALLOC_CTX *, const char *);
+
+bool           mapistore_backend_run_init(init_backend_fn *);
+
+__END_DECLS
+
+#endif /* ! __MAPISTORE_H */
diff --git a/mapiproxy/libmapistore/mapistore_backend.c b/mapiproxy/libmapistore/mapistore_backend.c
new file mode 100644 (file)
index 0000000..2428316
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+   OpenChange Storage Abstraction Layer library
+
+   OpenChange Project
+
+   Note: init and load functions have been copied from
+   samba4/source4/param/util.c initially wrote by Jelmer.
+
+   Copyright (C) Jelmer Vernooij 2005-2007
+   Copyright (C) Julien Kerihuel 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/>.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <dirent.h>
+
+#include "mapistore.h"
+#include "mapistore_errors.h"
+#include "mapistore_private.h"
+#include <libmapi/dlinklist.h>
+
+#include <util.h>
+#include <util/debug.h>
+
+/**
+   \file mapistore_backend.c
+
+   \brief mapistore backends management API
+ */
+
+
+static struct mstore_backend {
+       struct mapistore_backend        *backend;
+} *backends = NULL;
+
+int                                    num_backends;
+
+
+/**
+   \details Register mapistore backends
+
+   \param _backend pointer to the mapistore backend to register
+
+   \return MAPISTORE_SUCCESS on success
+ */
+_PUBLIC_ extern int mapistore_backend_register(const void *_backend)
+{
+       const struct mapistore_backend  *backend = _backend;
+
+       backends = realloc_p(backends, struct mstore_backend, num_backends + 1);
+       if (!backends) {
+               smb_panic("out of memory in mapistore_backend_register");
+       }
+
+       backends[num_backends].backend = smb_xmemdup(backend, sizeof (*backend));
+       backends[num_backends].backend->name = smb_xstrdup(backend->name);
+
+       num_backends++;
+
+       DEBUG(3, ("MAPISTORE backend '%s' registered\n", backend->name));
+
+       return MAPISTORE_SUCCESS;
+}
+
+
+/**
+   \details Return the full path where mapistore backends are
+   installed.
+
+   \return Pointer to the full path where backends are installed.
+ */
+_PUBLIC_ const char *mapistore_backend_get_installdir(void)
+{
+       return MAPISTORE_BACKEND_INSTALLDIR;
+}
+
+
+/**
+   \details Obtain the backend init function from a shared library
+   file
+
+   \param path full path to the backend shared library
+
+   \return Pointer to the initialization function on success,
+   otherwise NULL.
+ */
+static init_backend_fn load_backend(const char *path)
+{
+       void    *handle;
+       void    *init_fn;
+
+       handle = dlopen(path, RTLD_NOW);
+       if (handle == NULL) {
+               DEBUG(0, ("Unable to open %s: %s\n", path, dlerror()));
+               return NULL;
+       }
+
+       init_fn = dlsym(handle, MAPISTORE_INIT_MODULE);
+
+       if (init_fn == NULL) {
+               DEBUG(0, ("Unable to find %s() in %s: %s\n",
+                         MAPISTORE_INIT_MODULE, path, dlerror()));
+               DEBUG(1, ("Loading mapistore backend '%s' failed\n", path));
+               dlclose(handle);
+               return NULL;
+       }
+
+       return (init_backend_fn) init_fn;
+}
+
+
+/**
+   \details Load backends from specified directory
+
+   \param mem_ctx pointer to the memory context
+   \param pointer to the backends shared library folder
+
+   \return allocated array of functions pointers to initialization
+   functions on success, otherwise NULL.
+ */
+static init_backend_fn *load_backends(TALLOC_CTX *mem_ctx, const char *path)
+{
+       DIR             *dir;
+       struct dirent   *entry;
+       char            *filename;
+       int             success = 0;
+       init_backend_fn *ret;
+
+       ret = talloc_array(mem_ctx, init_backend_fn, 2);
+       ret[0] = NULL;
+
+       dir = opendir(path);
+       if (dir == NULL) {
+               talloc_free(ret);
+               return NULL;
+       }
+
+       while ((entry = readdir(dir))) {
+               if (ISDOT(entry->d_name) || ISDOTDOT(entry->d_name)) {
+                       continue;
+               }
+               
+               filename = talloc_asprintf(mem_ctx, "%s/%s", path, entry->d_name);
+               ret[success] = load_backend(filename);
+               if (ret[success]) {
+                       ret = talloc_realloc(mem_ctx, ret, init_backend_fn, success + 2);
+                       success++;
+                       ret[success] = NULL;
+               }
+
+               talloc_free(filename);
+       }
+
+       closedir(dir);
+
+       return ret;
+}
+
+
+/**
+   \details Load the initialization functions from backends DSO
+
+   \param mem_ctx pointer to the memory context
+   \param path pointer to the backend's DSO folder
+
+   \return allocated array of functions pointers to initialization
+   functions on success, otherwise NULL.
+ */
+_PUBLIC_ init_backend_fn *mapistore_backend_load(TALLOC_CTX *mem_ctx, const char *path)
+{
+       if (!path) {
+               path = mapistore_backend_get_installdir();
+       }
+
+       return load_backends(mem_ctx, path);
+}
+
+
+/**
+   \details Run specified initialization functions.
+
+   \param fns pointer to an array of mapistore backends initialization
+   functions
+
+   \return true on success, otherwise false
+ */
+_PUBLIC_ bool mapistore_backend_run_init(init_backend_fn *fns)
+{
+       int                             i;
+       bool                            ret = true;
+
+       if (fns == NULL) {
+               return true;
+       }
+
+       for (i = 0; fns[i]; i++) {
+               ret &= (bool)fns[i]();
+       }
+
+       return ret;
+}
+
+
+/**
+   \details Initialize mapistore backends
+
+   \param mem_ctx pointer to the memory context
+   \param path pointer to folder where mapistore backends are
+   installed
+
+   \return MAPISTORE_SUCCESS on success, otherwise
+   MAPISTORE_ERR_BACKEND_INIT
+ */
+int mapistore_backend_init(TALLOC_CTX *mem_ctx, const char *path)
+{
+       init_backend_fn                 *ret;
+       bool                            status;
+       int                             retval;
+       int                             i;
+
+       ret = mapistore_backend_load(mem_ctx, path);
+       status = mapistore_backend_run_init(ret);
+       talloc_free(ret);
+
+       for (i = 0; i < num_backends; i++) {
+               if (backends[i].backend) {
+                       DEBUG(3, ("MAPISTORE backend '%s' loaded\n", backends[i].backend->name));
+                       retval = backends[i].backend->init();
+               }
+       }
+
+       return (status != true) ? MAPISTORE_SUCCESS : MAPISTORE_ERR_BACKEND_INIT;
+}
+
+
+/**
+   \details Create backend context
+
+   \param mem_ctx pointer to the memory context
+   \param namespace the backend namespace
+   \param uri the backend parameters which can be passes inline
+
+   \return a valid backend_context pointer on success, otherwise NULL
+ */
+struct backend_context *mapistore_backend_create_context(TALLOC_CTX *mem_ctx, const char *namespace, 
+                                                        const char *uri)
+{
+       struct backend_context          *context;
+       int                             retval;
+       bool                            found;
+       void                            *private_data = NULL;
+       int                             i;
+
+       DEBUG(0, ("namespace is %s and backend_uri is '%s'\n", namespace, uri));
+       for (i = 0; i < num_backends; i++) {
+               if (backends[i].backend->namespace && 
+                   !strcmp(namespace, backends[i].backend->namespace)) {
+                       found = true;
+                       retval = backends[i].backend->create_context(mem_ctx, uri, &private_data);
+                       if (retval != MAPISTORE_SUCCESS) {
+                               return NULL;
+                       }
+
+                       break;
+               }
+       }
+       if (found == false) {
+               DEBUG(0, ("MAPISTORE: no backend with namespace '%s' is available\n", namespace));
+               return NULL;
+       }
+
+       context = talloc_zero(mem_ctx, struct backend_context);
+       context->backend = backends[i].backend;
+       context->private_data = private_data;
+       talloc_steal(context, context->private_data);
+
+       return context;
+}
+
+
+/**
+   \details Delete a context from the specified backend
+
+   \param bctx pointer to the backend context
+
+   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
+ */
+_PUBLIC_ int mapistore_backend_delete_context(struct backend_context *bctx)
+{
+       if (!bctx->backend->delete_context) return MAPISTORE_ERROR;
+
+       return bctx->backend->delete_context(bctx->private_data);
+}
diff --git a/mapiproxy/libmapistore/mapistore_errors.h b/mapiproxy/libmapistore/mapistore_errors.h
new file mode 100644 (file)
index 0000000..97b21e7
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+   OpenChange Storage Abstraction Layer library
+
+   OpenChange Project
+
+   Copyright (C) Julien Kerihuel 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 mapistore_errors.h
+
+   \brief Thie header providers a set of result codes for MAPISTORE
+   function calls.
+ */
+
+#ifndef        __MAPISTORE_ERRORS_H
+#define        __MAPISTORE_ERRORS_H
+
+/**
+   The function call succeeded.
+ */
+#define        MAPISTORE_SUCCESS                       0
+
+/**
+   The function call failed for some non-specific reason.
+ */
+#define        MAPISTORE_ERROR                         1
+
+/**
+   The function call failed because it was unable to allocate the
+   memory required by underlying operations.
+ */
+#define        MAPISTORE_ERR_NO_MEMORY                 2
+
+/**
+   The function call failed because underlying context has already
+   been initialized
+ */
+#define        MAPISTORE_ERR_ALREADY_INITIALIZED       3
+
+/**
+   The function call failed because context has not been initialized.
+ */
+#define        MAPISTORE_ERR_NOT_INITIALIZED           4
+
+/**
+   The function call failed because an internal mapistore storage
+   component has corrupted data.
+ */
+#define        MAPISTORE_ERR_CORRUPTED                 5
+
+/**
+   The function call failed because one of the function parameters is
+   invalid
+ */
+#define        MAPISTORE_ERR_INVALID_PARAMETER         6
+
+/**
+   The function call failed because the directory doesn't exist
+ */
+#define        MAPISTORE_ERR_NO_DIRECTORY              7
+
+/**
+   The function call failed because the underlying function couldn't
+   open a database.
+ */
+#define        MAPISTORE_ERR_DATABASE_INIT             8
+
+/**
+   The function call failed because the underlying function didn't run
+   a database operation successfully.
+ */
+#define        MAPISTORE_ERR_DATABASE_OPS              9
+
+/**
+   The function failed to register a storage backend
+ */
+#define        MAPISTORE_ERR_BACKEND_REGISTER          10
+
+/**
+   One of more storage backend initialization functions failed to
+   complete successfully.
+ */
+#define        MAPISTORE_ERR_BACKEND_INIT              11
+
+/**
+   The function failed because mapistore failed to create a context
+ */
+#define        MAPISTORE_ERR_CONTEXT_FAILED            12
+
+/**
+   The function failed because the provided namespace is invalid
+ */
+#define        MAPISTORE_ERR_INVALID_NAMESPACE         13
+
+#endif /* ! __MAPISTORE_ERRORS_H */
diff --git a/mapiproxy/libmapistore/mapistore_interface.c b/mapiproxy/libmapistore/mapistore_interface.c
new file mode 100644 (file)
index 0000000..c2a87c5
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+   OpenChange Storage Abstraction Layer library
+
+   OpenChange Project
+
+   Copyright (C) Julien Kerihuel 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/>.
+ */
+
+#include "mapistore.h"
+#include "mapistore_errors.h"
+#include "mapistore_private.h"
+#include <libmapi/dlinklist.h>
+
+#include <string.h>
+
+/**
+   \details Initialize the mapistore context
+
+   \param mem_ctx pointer to the memory context
+
+   \return allocate mapistore context on success, otherwise NULL
+ */
+_PUBLIC_ struct mapistore_context *mapistore_init(TALLOC_CTX *mem_ctx, const char *path)
+{
+       int                             retval;
+       struct mapistore_context        *mstore_ctx;
+
+       mstore_ctx = talloc_zero(mem_ctx, struct mapistore_context);
+       if (!mstore_ctx) {
+               return NULL;
+       }
+
+       mstore_ctx->processing_ctx = talloc_zero(mstore_ctx, struct processing_context);
+       retval = mapistore_init_mapping_context(mstore_ctx->processing_ctx);
+       if (retval != MAPISTORE_SUCCESS) {
+               DEBUG(5, ("[%s:%d]: %s\n", __FUNCTION__, __LINE__, mapistore_errstr(retval)));
+               talloc_free(mstore_ctx);
+               return NULL;
+       }
+
+       retval = mapistore_backend_init(mem_ctx, path);
+       if (retval != MAPISTORE_SUCCESS) {
+               DEBUG(5, ("[%s:%d]: %s\n", __FUNCTION__, __LINE__, mapistore_errstr(retval)));
+               talloc_free(mstore_ctx);
+               return NULL;
+       }
+
+       mstore_ctx->context_list = NULL;
+
+       return mstore_ctx;
+}
+
+
+/**
+   \details Release the mapistore context and destroy any data
+   associated
+
+   \param mstore_ctx pointer to the mapistore context
+
+   \note The function needs to rely on talloc destructors which is not
+   implemented in code yet.
+
+   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
+ */
+_PUBLIC_ int mapistore_release(struct mapistore_context *mstore_ctx)
+{
+       if (!mstore_ctx) return MAPISTORE_ERR_NOT_INITIALIZED;
+
+       talloc_free(mstore_ctx->processing_ctx);
+       talloc_free(mstore_ctx->context_list);
+       talloc_free(mstore_ctx);
+
+       return MAPISTORE_SUCCESS;
+}
+
+
+/**
+   \details Add a new connection context to mapistore
+
+   \param mstore_ctx pointer to the mapistore context
+   \param uri the connection context URI
+   \param pointer to the context identifier the function returns
+
+   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
+ */
+_PUBLIC_ int mapistore_add_context(struct mapistore_context *mstore_ctx, 
+                                  const char *uri, uint32_t *context_id)
+{
+       TALLOC_CTX                              *mem_ctx;
+       int                                     retval;
+       struct backend_context                  *backend_ctx;
+       struct backend_context_list             *backend_list;
+       char                                    *namespace;
+       char                                    *namespace_start;
+       char                                    *backend_uri;
+
+       /* Step 1. Perform Sanity Checks on URI */
+       if (!uri || strlen(uri) < 4) {
+               return MAPISTORE_ERR_INVALID_NAMESPACE;
+       }
+
+       mem_ctx = talloc_named(NULL, 0, "mapistore_add_context");
+       namespace = talloc_strdup(mem_ctx, uri);
+       namespace_start = namespace;
+       namespace = strchr(namespace, ':');
+       if (!namespace) {
+               DEBUG(0, ("[%s:%d]: Error - Invalid namespace '%s'\n", __FUNCTION__, __LINE__, namespace_start));
+               talloc_free(mem_ctx);
+               return MAPISTORE_ERR_INVALID_NAMESPACE;
+       }
+
+       if (namespace[1] && namespace[1] == '/' &&
+           namespace[2] && namespace[2] == '/' &&
+           namespace[3]) {
+               backend_uri = talloc_strdup(mem_ctx, &namespace[3]);
+               namespace[3] = '\0';
+               backend_ctx = mapistore_backend_create_context((TALLOC_CTX *)mstore_ctx, namespace_start, backend_uri);
+               if (!backend_ctx) {
+                       return MAPISTORE_ERR_CONTEXT_FAILED;
+               }
+
+               backend_list = talloc_zero((TALLOC_CTX *) mstore_ctx, struct backend_context_list);
+               talloc_steal(backend_list, backend_ctx);
+               backend_list->ctx = backend_ctx;
+               retval = mapistore_get_context_id(mstore_ctx->processing_ctx, &backend_list->ctx->context_id);
+               if (retval != MAPISTORE_SUCCESS) {
+                       talloc_free(mem_ctx);
+                       return MAPISTORE_ERR_CONTEXT_FAILED;
+               }
+               *context_id = backend_list->ctx->context_id;
+               DLIST_ADD_END(mstore_ctx->context_list, backend_list, struct backend_context_list *);
+
+       } else {
+               DEBUG(0, ("[%s:%d]: Error - Invalid URI '%s'\n", __FUNCTION__, __LINE__, uri));
+               talloc_free(mem_ctx);
+               return MAPISTORE_ERR_INVALID_NAMESPACE;
+       }
+
+       talloc_free(mem_ctx);
+       return MAPISTORE_SUCCESS;
+}
+
+
+/**
+   \details Delete an existing connection context from mapistore
+
+   \param mstore_ctx pointer to the mapistore context
+   \param context_id the context identifier referencing the context to
+   delete
+
+   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
+ */
+_PUBLIC_ int mapistore_del_context(struct mapistore_context *mstore_ctx, 
+                                  uint32_t context_id)
+{
+       struct backend_context_list     *el;
+       int                             retval;
+       bool                            found = false;
+
+       /* Sanity checks */
+       if (!mstore_ctx) return MAPISTORE_ERR_NOT_INITIALIZED;
+       if (!mstore_ctx->processing_ctx) return MAPISTORE_ERR_NOT_INITIALIZED;
+       if (!mstore_ctx->context_list) return MAPISTORE_ERR_NOT_INITIALIZED;
+
+       /* Step 0. Ensure the context exists */
+       for (el = mstore_ctx->context_list; el; el = el->next) {
+               if (el->ctx && el->ctx->context_id == context_id) {
+                       found = true;
+                       break;
+               }
+       }
+       if (found == false) return MAPISTORE_ERR_INVALID_PARAMETER;
+
+       /* Step 1. Delete the context within backend */
+       retval = mapistore_backend_delete_context(el->ctx);
+       if (retval) return retval;
+
+       /* Step 2. Delete the context from the processing layer */
+
+       /* Step 2. Add the free'd context id to the free list */
+       retval = mapistore_free_context_id(mstore_ctx->processing_ctx, context_id);
+       return retval;
+}
+
+
+/**
+   \details return a string explaining what a mapistore error constant
+   means.
+
+   \param mapistore_err the mapistore error constant
+
+   \return constant string
+ */
+_PUBLIC_ const char *mapistore_errstr(int mapistore_err)
+{
+       switch (mapistore_err) {
+       case MAPISTORE_SUCCESS:
+               return "Success";
+       case MAPISTORE_ERROR:
+               return "Non-specific error";
+       case MAPISTORE_ERR_NO_MEMORY:
+               return "No memory available";
+       case MAPISTORE_ERR_ALREADY_INITIALIZED:
+               return "Already initialized";
+       case MAPISTORE_ERR_NOT_INITIALIZED:
+               return "Not initialized";
+       case MAPISTORE_ERR_NO_DIRECTORY:
+               return "No such file or directory";
+       case MAPISTORE_ERR_DATABASE_INIT:
+               return "Database initialization failed";
+       case MAPISTORE_ERR_DATABASE_OPS:
+               return "database operation failed";
+       case MAPISTORE_ERR_BACKEND_REGISTER:
+               return "storage backend registration failed";
+       case MAPISTORE_ERR_BACKEND_INIT:
+               return "storage backend initialization failed";
+       }
+
+       return "Unknown error";
+}
diff --git a/mapiproxy/libmapistore/mapistore_private.h b/mapiproxy/libmapistore/mapistore_private.h
new file mode 100644 (file)
index 0000000..c401ab2
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+   OpenChange Storage Abstraction Layer library
+
+   OpenChange Project
+
+   Copyright (C) Julien Kerihuel 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/>.
+ */
+
+#ifndef        __MAPISTORE_PRIVATE_H__
+#define        __MAPISTORE_PRIVATE_H__
+
+#ifndef        ISDOT
+#define ISDOT(path) ( \
+                       *((const char *)(path)) == '.' && \
+                       *(((const char *)(path)) + 1) == '\0' \
+                   )
+#endif
+
+#ifndef        ISDOTDOT
+#define ISDOTDOT(path) ( \
+                           *((const char *)(path)) == '.' && \
+                           *(((const char *)(path)) + 1) == '.' && \
+                           *(((const char *)(path)) + 2) == '\0' \
+                       )
+#endif
+
+
+/**
+   Identifier mapping context.
+
+   This structure stores PR_MID and PR_FID identifiers to backend
+   identifiers mapping. It points on 2 databases, one with "in use"
+   identifiers and another one with a list of "free identifiers" which
+   are added when an object is deleted, moved, etc.
+
+   The last_id structure member references the last identifier value
+   which got created. There is no identifier available with a value
+   higher than last_id.
+ */
+struct id_mapping_context {
+       TDB_CONTEXT             *used_ctx;
+       TDB_CONTEXT             *free_ctx;
+       uint64_t                last_id;
+};
+
+
+/**
+   Free context identifier list
+
+   This structure is a double chained list storing unused context
+   identifiers.
+ */
+struct context_id_list {
+       uint32_t                context_id;
+       struct context_id_list  *prev;
+       struct context_id_list  *next;
+};
+
+
+struct processing_context {
+       struct id_mapping_context       *mapping_ctx;
+       struct context_id_list          *free_ctx;
+       uint32_t                        last_context_id;
+       uint64_t                        dflt_start_id;
+};
+
+
+/**
+   The database name where in use ID mappings are stored
+ */
+#define        MAPISTORE_DB_LAST_ID_KEY        "mapistore_last_id"
+#define        MAPISTORE_DB_LAST_ID_VAL        0x15000
+
+#define        MAPISTORE_DB_NAME_USED_ID       "mapistore_id_mapping_used.tdb"
+#define        MAPISTORE_DB_NAME_FREE_ID       "mapistore_id_mapping_free.tdb"
+
+__BEGIN_DECLS
+
+/* definitions from mapistore_processing.c */
+const char *mapistore_get_mapping_path(void);
+int mapistore_init_mapping_context(struct processing_context *);
+int mapistore_get_context_id(struct processing_context *, uint32_t *);
+int mapistore_free_context_id(struct processing_context *, uint32_t);
+
+
+/* definitions from mapistore_backend.c */
+int mapistore_backend_init(TALLOC_CTX *, const char *);
+struct backend_context *mapistore_backend_create_context(TALLOC_CTX *, const char *, const char *);
+int mapistore_backend_delete_context(struct backend_context *);
+
+__END_DECLS
+
+#endif /* ! __MAPISTORE_PRIVATE_H__ */
diff --git a/mapiproxy/libmapistore/mapistore_processing.c b/mapiproxy/libmapistore/mapistore_processing.c
new file mode 100644 (file)
index 0000000..c1c18f7
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+   OpenChange Storage Abstraction Layer library
+
+   OpenChange Project
+
+   Copyright (C) Julien Kerihuel 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/>.
+ */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+#include "mapistore.h"
+#include "mapistore_errors.h"
+#include "mapistore_private.h"
+#include <libmapi/dlinklist.h>
+
+#include <tdb.h>
+
+static struct id_mapping_context *mapping_ctx = NULL;
+char *mapping_path = NULL;
+
+
+/**
+   \details Set the mapping path
+
+   \param path pointer to the mapping path
+
+   \note The mapping path can be set unless id_mapping_context is
+   initialized. If path is NULL and mapping path is not yet
+   initialized, then mapping_path will be reset to its default value
+   when the initialization routine is called.
+
+   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
+ */
+_PUBLIC_ int mapistore_set_mapping_path(const char *path)
+{
+       TALLOC_CTX      *mem_ctx;
+       DIR             *dir;
+
+       /* Sanity checks */
+       if (mapping_ctx) {
+               return MAPISTORE_ERR_ALREADY_INITIALIZED;
+       }
+
+       /* Case 1. Path is set to NULL */
+       if (!path) {
+               if (mapping_path) {
+                       talloc_free(mapping_path);
+               }
+               mapping_path = NULL;
+               return MAPISTORE_SUCCESS;
+       }
+
+       if (mapping_path) {
+               talloc_free(mapping_path);
+       }
+
+       /* Case 2. path is initialized */
+
+       /* Step 1. Check if path is valid path */
+       dir = opendir(path);
+       if (!dir) {
+               return MAPISTORE_ERR_NO_DIRECTORY;
+       }
+
+       /* Step 2. TODO: Check for write permissions */
+       
+       mem_ctx = talloc_autofree_context();
+       mapping_path = talloc_strdup(mem_ctx, path);
+       return MAPISTORE_SUCCESS;
+}
+
+/**
+   \details Return the current mapping path
+
+   \return pointer to the mapping path.
+ */
+const char *mapistore_get_mapping_path(void)
+{
+       return (!mapping_path) ? MAPISTORE_MAPPING_PATH : (const char *)mapping_path;
+}
+
+
+/**
+   \details Initialize the ID mapping context or return the existing
+   one if already initialized.
+
+   \param pctx pointer to the processing context
+
+   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
+ */
+int mapistore_init_mapping_context(struct processing_context *pctx)
+{
+       TDB_DATA        key;
+       TDB_DATA        dbuf;
+       TALLOC_CTX      *mem_ctx;
+       char            *dbpath;
+       uint64_t        last_id;
+       char            *tmp_buf;
+       int             ret;
+
+       if (!pctx) return MAPISTORE_ERR_NOT_INITIALIZED;
+
+       /* Step 0. return existing mapping context if already initialized */
+       if (mapping_ctx) {
+               pctx->mapping_ctx = mapping_ctx;
+               return MAPISTORE_SUCCESS;
+       } else {
+               mapping_ctx = talloc_named(NULL, 0, "mapping_context");
+               if (!mapping_ctx) return MAPISTORE_ERR_NO_MEMORY;
+       }
+
+       mem_ctx = talloc_named(NULL, 0, "mapistore_init_mapping_context");
+
+       /* Step 1. Open/Create the used ID database */
+       if (!mapping_ctx->used_ctx) {
+               dbpath = talloc_asprintf(mem_ctx, "%s/%s", mapistore_get_mapping_path(), MAPISTORE_DB_NAME_USED_ID);
+               mapping_ctx->used_ctx = tdb_open(dbpath, 0, 0, O_RDWR|O_CREAT, 0600);
+               talloc_free(dbpath);
+               if (!mapping_ctx->used_ctx) {
+                       DEBUG(3, ("[%s:%d]: %s\n", __FUNCTION__, __LINE__, strerror(errno)));
+                       talloc_free(mem_ctx);
+                       talloc_free(mapping_ctx);
+                       return MAPISTORE_ERR_DATABASE_INIT;
+               }
+       }
+
+       /* Step 2. Open/Create the free ID database */
+       if (!mapping_ctx->free_ctx) {
+               dbpath = talloc_asprintf(mem_ctx, "%s/%s", mapistore_get_mapping_path(), MAPISTORE_DB_NAME_FREE_ID);
+               mapping_ctx->free_ctx = tdb_open(dbpath, 0, 0, O_RDWR|O_CREAT, 0600);
+               talloc_free(dbpath);
+               if (!mapping_ctx->free_ctx) {
+                       DEBUG(3, ("[%s:%d]: %s\n", __FUNCTION__, __LINE__, strerror(errno)));
+                       talloc_free(mem_ctx);
+                       talloc_free(mapping_ctx);
+                       return MAPISTORE_ERR_DATABASE_INIT;
+               }
+       }
+
+       /* Step 3. Retrieve the last ID value */
+       key.dptr = (unsigned char *) MAPISTORE_DB_LAST_ID_KEY;
+       key.dsize = strlen(MAPISTORE_DB_LAST_ID_KEY);
+
+       dbuf = tdb_fetch(mapping_ctx->used_ctx, key);
+
+       /* If the record doesn't exist, insert it */
+       if (!dbuf.dptr || !dbuf.dsize) {
+               dbuf.dptr = (unsigned char *) talloc_asprintf(mem_ctx, "0x%llx", (uint64_t) MAPISTORE_DB_LAST_ID_VAL);
+               dbuf.dsize = strlen((const char *) dbuf.dptr);
+               last_id = MAPISTORE_DB_LAST_ID_VAL;
+
+               ret = tdb_store(mapping_ctx->used_ctx, key, dbuf, TDB_INSERT);
+               talloc_free(dbuf.dptr);
+               if (ret == -1) {
+                       DEBUG(3, ("[%s:%d]: Unable to create %s record: %s\n", __FUNCTION__, __LINE__,
+                                 MAPISTORE_DB_LAST_ID_KEY, tdb_errorstr(mapping_ctx->used_ctx)));
+                       talloc_free(mem_ctx);
+                       talloc_free(mapping_ctx);
+
+                       return MAPISTORE_ERR_DATABASE_OPS;
+               }
+
+       } else {
+               tmp_buf = talloc_strndup(mem_ctx, (char *)dbuf.dptr, dbuf.dsize);
+               free(dbuf.dptr);
+               last_id = strtoull(tmp_buf, NULL, 16);
+               talloc_free(tmp_buf);
+       }
+
+       mapping_ctx->last_id = last_id;
+
+       pctx->mapping_ctx = mapping_ctx;
+       talloc_free(mem_ctx);
+
+       return MAPISTORE_SUCCESS;
+}
+
+
+/**
+   \details Return an unused or new context identifier
+
+   \param pctx pointer to the processing context
+   \param context_id pointer to the context identifier the function
+   returns
+
+   \return a non zero context identifier on success, otherwise 0.
+ */
+int mapistore_get_context_id(struct processing_context *pctx, uint32_t *context_id)
+{
+       struct context_id_list  *el;
+
+       /* Sanity checks */
+       if (!pctx) return MAPISTORE_ERR_NOT_INITIALIZED;
+
+       /* Step 1. The free context list doesn't exist yet */
+       if (!pctx->free_ctx) {
+               pctx->last_context_id++;
+               *context_id = pctx->last_context_id;
+       }
+
+       /* Step 2. We have a free list */
+       for (el = pctx->free_ctx; el; el = el->next) {
+               if (el->context_id) {
+                       *context_id = el->context_id;
+                       DLIST_REMOVE(pctx->free_ctx, el);
+                       break;
+               }
+       }
+
+       return MAPISTORE_SUCCESS;
+}
+
+
+/**
+   \details Add a context identifier to the list
+
+   \param pctx pointer to the processing context
+   \param context_id the identifier referencing the context to free
+
+   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
+ */
+int mapistore_free_context_id(struct processing_context *pctx, uint32_t context_id)
+{
+       struct context_id_list  *el;
+
+       /* Sanity checks */
+       if (!pctx) return MAPISTORE_ERR_NOT_INITIALIZED;
+
+       /* Step 1. Ensure the list is not corrupted */
+       for (el = pctx->free_ctx; el; el = el->next) {
+               if (el->context_id == context_id) {
+                       return MAPISTORE_ERR_CORRUPTED;
+               }
+       }
+
+       /* Step 2. Create the element and add it to the list */
+       el = talloc_zero((TALLOC_CTX *)pctx, struct context_id_list);
+       el->context_id = context_id;
+       DLIST_ADD_END(pctx->free_ctx, el, struct context_id_list *);
+
+       return MAPISTORE_SUCCESS;
+}
diff --git a/mapiproxy/libmapistore/tests/mapistore_test.c b/mapiproxy/libmapistore/tests/mapistore_test.c
new file mode 100644 (file)
index 0000000..7745d19
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+   OpenChange Storage Abstraction Layer library test tool
+
+   OpenChange Project
+
+   Copyright (C) Julien Kerihuel 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/>.
+ */
+
+#include "mapiproxy/libmapistore/mapistore.h"
+#include <talloc.h>
+#include <core/ntstatus.h>
+#include <samba/popt.h>
+#include <param.h>
+#include <util/debug.h>
+
+/**
+   \file mapistore_test.c
+
+   \brief Test mapistore implementation
+ */
+
+
+int main(int argc, const char *argv[])
+{
+       TALLOC_CTX                      *mem_ctx;
+       int                             retval;
+       struct mapistore_context        *mstore_ctx;
+       struct loadparm_context         *lp_ctx;
+       poptContext                     pc;
+       int                             opt;
+       const char                      *opt_debug = NULL;
+       uint32_t                        context_id = 0;
+       uint32_t                        context_id2 = 0;
+
+       enum { OPT_DEBUG=1000 };
+
+       struct poptOption long_options[] = {
+               POPT_AUTOHELP
+               { "debuglevel", 'd', POPT_ARG_STRING, NULL, OPT_DEBUG,  "set the debug level", NULL },
+               { NULL, 0, 0, NULL, 0, NULL, NULL }
+       };
+
+       mem_ctx = talloc_named(NULL, 0, "mapistore_test");
+       lp_ctx = loadparm_init(mem_ctx);
+       lp_load_default(lp_ctx);
+       setup_logging(NULL, DEBUG_STDOUT);
+       
+       pc = poptGetContext("mapistore_test", argc, argv, long_options, 0);
+       while ((opt = poptGetNextOpt(pc)) != -1) {
+               switch (opt) {
+               case OPT_DEBUG:
+                       opt_debug = poptGetOptArg(pc);
+                       break;
+               }
+       }
+
+       poptFreeContext(pc);
+
+       if (opt_debug) {
+               lp_set_cmdline(lp_ctx, "log level", opt_debug);
+       }
+       
+       retval = mapistore_set_mapping_path("/tmp");
+       if (retval != MAPISTORE_SUCCESS) {
+               DEBUG(0, ("%s\n", mapistore_errstr(retval)));
+               exit (1);
+       }
+
+       mstore_ctx = mapistore_init(mem_ctx, NULL);
+       if (!mstore_ctx) {
+               DEBUG(0, ("%s\n", mapistore_errstr(retval)));
+               exit (1);
+       }
+
+       retval = mapistore_add_context(mstore_ctx, "sqlite:///tmp/test.db", &context_id);
+       if (retval != MAPISTORE_SUCCESS) {
+               DEBUG(0, ("%s\n", mapistore_errstr(retval)));
+               exit (1);
+       }
+
+       retval = mapistore_add_context(mstore_ctx, "sqlite:///tmp/test2.db", &context_id2);
+       if (retval != MAPISTORE_SUCCESS) {
+               DEBUG(0, ("%s\n", mapistore_errstr(retval)));
+               exit (1);
+       }
+
+       DEBUG(0, ("Context ID: [1] = %d and [2] = %d\n", context_id, context_id2));
+
+       retval = mapistore_del_context(mstore_ctx, context_id);
+       retval = mapistore_del_context(mstore_ctx, context_id2);
+
+       retval = mapistore_release(mstore_ctx);
+       if (retval != MAPISTORE_SUCCESS) {
+               DEBUG(0, ("%s\n", mapistore_errstr(retval)));
+               exit (1);
+       }
+
+       return 0;
+}