/* MAPIStoreSOGo.m - this file is part of SOGo
 *
 * Copyright (C) 2010-2012 Inverse inc.
 *
 * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
 *
 * This file 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, or (at your option)
 * any later version.
 *
 * This file 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/* OpenChange SOGo storage backend */

#import <Foundation/NSAutoreleasePool.h>
#import <Foundation/NSDebug.h>
#import <Foundation/NSFileHandle.h>
#import <Foundation/NSProcessInfo.h>
#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSThread.h>
#import <NGObjWeb/SoProductRegistry.h>
#import <NGExtensions/NSObject+Logs.h>
#import <SOGo/SOGoCache.h>
#import <SOGo/SOGoProductLoader.h>
#import <SOGo/SOGoSystemDefaults.h>

#import "MAPIApplication.h"
#import "MAPIStoreAttachment.h"
#import "MAPIStoreAttachmentTable.h"
#import "MAPIStoreContext.h"
#import "MAPIStoreEmbeddedMessage.h"
#import "MAPIStoreFolder.h"
#import "MAPIStoreMessage.h"
#import "MAPIStoreMailVolatileMessage.h"
#import "MAPIStoreObject.h"
#import "MAPIStoreTable.h"
#import "NSObject+MAPIStore.h"
#import "NSString+MAPIStore.h"

#include <libmapi/libmapi.h>
#include <mapistore/mapistore.h>
#include <mapistore/mapistore_errors.h>
#include <execinfo.h>

static Class MAPIStoreContextK = Nil;
static BOOL leakDebugging = NO;
static BOOL initialization_done = NO;

#define NS_CURRENT_THREAD_REGISTER() \
    BOOL __nsrct_thread_registered = GSRegisterCurrentThread(); \
    if (!initialization_done) { \
        OC_DEBUG(5, "[SOGo] You should call sogo_backend_init() first. Current thread: %p, pid: %d\n", \
                 GSCurrentThread(), getpid());                         \
    } \
    OC_DEBUG(5, "[SOGo] --->");

#define NS_CURRENT_THREAD_TRY_UNREGISTER(rc) \
    if (__nsrct_thread_registered) { \
        GSUnregisterCurrentThread(); \
    } \
    OC_DEBUG(6, "[SOGo] <--- [%s]", mapistore_errstr (rc));

#define TRYCATCH_START @try {
#define TRYCATCH_END(pool)  \
          } @catch (NSException * e) { \
            enum mapistore_error ret = sogo_backend_handle_objc_exception(e, __PRETTY_FUNCTION__, __LINE__); \
            mapiapp_cleanup(); \
            [pool release]; \
            NS_CURRENT_THREAD_TRY_UNREGISTER(ret); \
            return ret; \
          } \
        mapiapp_cleanup();


static enum mapistore_error
sogo_backend_unexpected_error()
{
  NSLog (@"  UNEXPECTED WEIRDNESS: RECEIVED NO OBJECT");
  abort();
  return MAPISTORE_SUCCESS;
}

static enum mapistore_error
sogo_backend_handle_objc_exception(NSException *e, const char *fn_name, const int line_no)
{
  NSString *callStackSymbols = nil;
  if ([e respondsToSelector:@selector(callStackSymbols)])
    {
      callStackSymbols = [[e callStackSymbols] componentsJoinedByString:@"\n\t"];
    }
  NSLog(@"[SOGo: %s:%d] - EXCEPTION: %@, reason: %@, backtrace: \n\t%@\n",
        fn_name, line_no, [e name], [e reason], callStackSymbols);

  // Another point of view on the stack trace
  {
    void *frames[128];
    int i, len = backtrace(frames, 128);
    char **symbols = backtrace_symbols(frames, len);
    NSLog(@"Backtrace using execinfo.h:\n");
    for (i = 0; i < len; ++i)
      NSLog(@"\t%s", symbols[i]);
    free(symbols);
  }

  if ([[e name] isEqual:@"NotImplementedException"])
    {
      return MAPISTORE_ERR_NOT_IMPLEMENTED;
    }
  return MAPISTORE_ERROR;
}

static void
sogo_backend_atexit (void)
{
  NSAutoreleasePool *pool;

  GSRegisterCurrentThread ();
  pool = [NSAutoreleasePool new];
  //NSLog (@"allocated classes:\n%s", GSDebugAllocationList (YES));
  [pool release];
  GSUnregisterCurrentThread ();
}


/**
   \details Initialize sogo mapistore backend

   \return MAPISTORE_SUCCESS on success
*/
static enum mapistore_error
sogo_backend_init (void)
{
  NSAutoreleasePool *pool;
  SOGoProductLoader *loader;
  Class MAPIApplicationK;
  NSUserDefaults *ud;
  SoProductRegistry *registry;
  char *argv[] = { SAMBA_PREFIX "/sbin/samba", NULL };
  NSString *debugLevel;
  uint8_t parentLogLevel;

  GSRegisterCurrentThread();

  if (initialization_done) {
      OC_DEBUG(5, "[SOGo] SOGo backend already initialized.\n");
      return MAPISTORE_SUCCESS;
  }

  pool = [NSAutoreleasePool new];

  /* Here we work around a bug in GNUstep which decodes XML user
     defaults using the system encoding rather than honouring
     the encoding specified in the file. */
  putenv ("GNUSTEP_STRING_ENCODING=NSUTF8StringEncoding");
  //putenv ("NSZombieEnabled=YES");

  [NSProcessInfo initializeWithArguments: argv
                                   count: 1
                             environment: environ];

  [SOGoSystemDefaults sharedSystemDefaults];

  /* We force the plugin to base its configuration on the SOGo tree. */
  ud = [NSUserDefaults standardUserDefaults];

  /* Ensure imap4Connection calls raise Exception if
    IMAP connection is not established. See NGImap4Connection.m */
  [ud setBool: YES forKey: @"SoIMAP4ExceptionsEnabled"];

  if (!leakDebugging && [ud boolForKey: @"SOGoDebugLeaks"])
    {
      NSLog (@"  leak debugging on");
      GSDebugAllocationActive (YES);
      atexit (sogo_backend_atexit);
      leakDebugging = YES;
    }

  /* Set debug level according to samba */
  parentLogLevel = DEBUGLEVEL_CLASS[DBGC_ALL]; // FIXME: samba logger specific code
  if (parentLogLevel >= 4)
    debugLevel = @"DEBUG";
  else if (parentLogLevel >= 3)
    debugLevel = @"INFO";
  else if (parentLogLevel >= 2)
    debugLevel = @"WARN";
  else if (parentLogLevel >= 1)
    debugLevel = @"ERROR";
  else
    debugLevel = @"FATAL";
  OC_DEBUG(3, "[SOGo] Setting log level to %s", [debugLevel UTF8String]);
  [ud setObject: debugLevel forKey: @"NGLogDefaultLogLevel"];
  [ud synchronize];

  registry = [SoProductRegistry sharedProductRegistry];
  [registry scanForProductsInDirectory: SOGO_BUNDLES_DIR];

  loader = [SOGoProductLoader productLoader];
  [loader loadProducts: [NSArray arrayWithObject: BACKEND_BUNDLE_NAME]];

  MAPIApplicationK = NSClassFromString (@"MAPIApplication");
  if (MAPIApplicationK)
    [[MAPIApplicationK new] activateApplication];

  [[SOGoCache sharedCache] disableRequestsCache];
  [[SOGoCache sharedCache] disableLocalCache];

  MAPIStoreContextK = NSClassFromString (@"MAPIStoreContext");

  [pool release];

  OC_DEBUG(5, "[SOGo] backend init SUCCESS. Current thread: %p, pid: %d\n", GSCurrentThread(), getpid());
  initialization_done = YES;

  return MAPISTORE_SUCCESS;
}

/**
   \details Cleanup operation to execute after an action has been performed
   so there won't be any conflicts with future calls.
   In practice this will deactivate the current user context set on MAPIApp
   (which is the current WOApplication), this means two things: (1) set nil
   as current user context on MAPIApp and (2) remove woContext from current
   thread dictionary (this is used on WOContext.m).
*/
static void mapiapp_cleanup(void)
{
  Class MAPIApplicationK;
  MAPIApplicationK = NSClassFromString (@"MAPIApplication");
  if (MAPIApplicationK)
    [[MAPIApplicationK application] cleanup];
}

/**
   \details Create a connection context to the sogo backend

   \param mem_ctx pointer to the memory context
   \param conn_info pointer to the connection information available for this context
                    (database connection, connected user, replica server info)
   \param indexing pointer to the indexing database connection
   \param uri pointer to the sogo path
   \param private_data pointer to the private backend context

   \note the developer must free allocated private_data
*/

static enum mapistore_error
sogo_backend_create_context(TALLOC_CTX *mem_ctx,
                            struct mapistore_connection_info *conn_info,
                            struct indexing_context *indexing,
                            const char *uri, void **context_object)
{
  NSAutoreleasePool *pool;
  MAPIStoreContext *context;
  enum mapistore_error rc;

  NS_CURRENT_THREAD_REGISTER();
  pool = [NSAutoreleasePool new];

  if (MAPIStoreContextK)
    {
      TRYCATCH_START
      rc = [MAPIStoreContextK openContext: &context
                                  withURI: uri
                           connectionInfo: conn_info
                           andTDBIndexing: indexing];
      if (rc == MAPISTORE_SUCCESS)
        *context_object = [context tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)
    }
  else
    rc = MAPISTORE_ERROR;

  [pool release];
  NS_CURRENT_THREAD_TRY_UNREGISTER(rc);

  return rc;
}

static enum mapistore_error
sogo_backend_create_root_folder (const char *username,
                                 enum mapistore_context_role role,
                                 uint64_t fid, const char *name,
                                 // struct indexing_context *indexing,
                                 TALLOC_CTX *mem_ctx, char **mapistore_urip)
{
  NSAutoreleasePool *pool;
  NSString *userName, *folderName;
  NSString *mapistoreUri;
  enum mapistore_error rc;

  NS_CURRENT_THREAD_REGISTER();
  pool = [NSAutoreleasePool new];

  if (MAPIStoreContextK)
    {
      TRYCATCH_START
      userName = [NSString stringWithUTF8String: username];
      folderName = [NSString stringWithUTF8String: name];
      rc = [MAPIStoreContextK createRootFolder: &mapistoreUri
                                       withFID: fid
                                       andName: folderName
                                       forUser: userName
                                      withRole: role];
      if (rc == MAPISTORE_SUCCESS)
        *mapistore_urip = [mapistoreUri asUnicodeInMemCtx: mem_ctx];
      TRYCATCH_END(pool)
    }
  else
    rc = MAPISTORE_ERROR;

  [pool release];
  NS_CURRENT_THREAD_TRY_UNREGISTER(rc);

  return rc;
}

static enum mapistore_error
sogo_backend_list_contexts(const char *username, struct indexing_context *indexing,
                           TALLOC_CTX *mem_ctx,
                           struct mapistore_contexts_list **contexts_listp)
{
  NSAutoreleasePool *pool;
  NSString *userName;
  enum mapistore_error rc;

  NS_CURRENT_THREAD_REGISTER();
  pool = [NSAutoreleasePool new];

  if (MAPIStoreContextK)
    {
      TRYCATCH_START
      userName = [NSString stringWithUTF8String: username];
      *contexts_listp = [MAPIStoreContextK listAllContextsForUser: userName
                                                  withIndexing: indexing
                                                         inMemCtx: mem_ctx];
      rc = MAPISTORE_SUCCESS;
      TRYCATCH_END(pool)
    }
  else
    rc = MAPISTORE_ERROR;

  [pool release];
  NS_CURRENT_THREAD_TRY_UNREGISTER(rc);

  return rc;
}

// andFID: fid
// uint64_t fid,
//   void **private_data)

/**
   \details return the mapistore path associated to a given message or
   folder ID

   \param private_data pointer to the current sogo context
   \param fmid the folder/message ID to lookup
   \param type whether it is a folder or message
   \param path pointer on pointer to the path to return

   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
*/
static enum mapistore_error
sogo_context_get_path(void *backend_object, TALLOC_CTX *mem_ctx,
                      uint64_t fmid, char **path)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreContext *context;
  enum mapistore_error rc;

  if (backend_object)
    {
      wrapper = backend_object;
      context = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [context getPath: path ofFMID: fmid inMemCtx: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_context_get_root_folder(void *backend_object, TALLOC_CTX *mem_ctx,
                             uint64_t fid, void **folder_object)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreContext *context;
  MAPIStoreFolder *folder;
  enum mapistore_error rc;

  if (backend_object)
    {
      wrapper = backend_object;
      context = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [context getRootFolder: &folder withFID: fid];
      if (rc == MAPISTORE_SUCCESS)
        *folder_object = [folder tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

/**
   \details Open a folder from the sogo backend

   \param private_data pointer to the current sogo context
   \param parent_fid the parent folder identifier
   \param fid the identifier of the colder to open

   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE_ERROR
*/
static enum mapistore_error
sogo_folder_open_folder(void *folder_object, TALLOC_CTX *mem_ctx, uint64_t fid, void **childfolder_object)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder, *childFolder;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder openFolder: &childFolder withFID: fid];
      if (rc == MAPISTORE_SUCCESS)
        *childfolder_object = [childFolder tallocWrapper: mem_ctx];
      // [context tearDownRequest];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

/**
   \details Create a folder in the sogo backend

   \param private_data pointer to the current sogo context

   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE_ERROR
*/
static enum mapistore_error
sogo_folder_create_folder(void *folder_object, TALLOC_CTX *mem_ctx,
                          uint64_t fid, struct SRow *aRow,
                          void **childfolder_object)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder, *childFolder;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder createFolder: &childFolder withRow: aRow andFID: fid];
      if (rc == MAPISTORE_SUCCESS)
        *childfolder_object = [childFolder tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

/**
   \details Delete a folder from the sogo backend

   \param private_data pointer to the current sogo context
   \param parent_fid the FID for the parent of the folder to delete
   \param fid the FID for the folder to delete

   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE_ERROR
*/
static enum mapistore_error
sogo_folder_delete(void *folder_object)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder deleteFolder];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
       rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_get_child_count(void *folder_object, enum mapistore_table_type table_type, uint32_t *child_count)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder getChildCount: child_count ofTableType: table_type];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_open_message(void *folder_object,
                         TALLOC_CTX *mem_ctx,
                         uint64_t mid, bool write_access,
                         void **message_object)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder;
  MAPIStoreMessage *message;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder openMessage: &message
                       withMID: mid
                    forWriting: write_access
                      inMemCtx: mem_ctx];
      if (rc == MAPISTORE_SUCCESS)
        *message_object = [message tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_create_message(void *folder_object,
                           TALLOC_CTX *mem_ctx,
                           uint64_t mid,
                           uint8_t associated,
                           void **message_object)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder;
  MAPIStoreMessage *message;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder createMessage: &message
                         withMID: mid
                    isAssociated: associated];
      if (rc == MAPISTORE_SUCCESS)
        *message_object = [message tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_delete_message(void *folder_object, uint64_t mid, uint8_t flags)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder deleteMessageWithMID: mid andFlags: flags];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_move_copy_messages(void *folder_object,
                               void *source_folder_object,
                               TALLOC_CTX *mem_ctx,
                               uint32_t mid_count,
                               uint64_t *src_mids, uint64_t *t_mids,
                               struct Binary_r **target_change_keys,
                               struct Binary_r **target_predecessor_change_lists,
                               uint8_t want_copy)
{
  MAPIStoreFolder *sourceFolder, *targetFolder;
  NSAutoreleasePool *pool;
  struct MAPIStoreTallocWrapper *wrapper;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      targetFolder = wrapper->instance;

      wrapper = source_folder_object;
      sourceFolder = wrapper->instance;

      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [targetFolder moveCopyMessagesWithMIDs: src_mids
                                         andCount: mid_count
                                       fromFolder: sourceFolder
                                         withMIDs: t_mids
                                    andChangeKeys: target_change_keys
                        andPredecessorChangeLists: target_predecessor_change_lists
                                         wantCopy: want_copy
                                         inMemCtx: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_move_folder(void *folder_object, void *target_folder_object,
                        TALLOC_CTX *mem_ctx, const char *new_folder_name)
{
  NSAutoreleasePool *pool;
  MAPIStoreFolder *moveFolder, *targetFolder;
  NSString *newFolderName;
  struct MAPIStoreTallocWrapper *wrapper;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      moveFolder = wrapper->instance;

      wrapper = target_folder_object;
      if (wrapper)
        targetFolder = wrapper->instance;
      else
        targetFolder = nil;

      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      if (new_folder_name)
        newFolderName = [NSString stringWithUTF8String: new_folder_name];
      else
        newFolderName = nil;

      TRYCATCH_START
      rc = [moveFolder moveCopyToFolder: targetFolder
                            withNewName: newFolderName
                                 isMove: YES
                            isRecursive: YES
                               inMemCtx: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_copy_folder(void *folder_object, void *target_folder_object, TALLOC_CTX *mem_ctx,
                        bool recursive, const char *new_folder_name)
{
  NSAutoreleasePool *pool;
  MAPIStoreFolder *copyFolder, *targetFolder;
  NSString *newFolderName;
  struct MAPIStoreTallocWrapper *wrapper;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      copyFolder = wrapper->instance;

      wrapper = target_folder_object;
      targetFolder = wrapper->instance;

      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      newFolderName = [NSString stringWithUTF8String: new_folder_name];

      TRYCATCH_START
      rc = [copyFolder moveCopyToFolder: targetFolder
                            withNewName: newFolderName
                                 isMove: NO
                            isRecursive: recursive
                               inMemCtx: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_get_deleted_fmids(void *folder_object, TALLOC_CTX *mem_ctx,
                              enum mapistore_table_type table_type, uint64_t change_num,
                              struct UI8Array_r **fmidsp, uint64_t *cnp)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder getDeletedFMIDs: fmidsp
                             andCN: cnp
                  fromChangeNumber: change_num
                       inTableType: table_type
                          inMemCtx: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_open_table(void *folder_object, TALLOC_CTX *mem_ctx,
                       enum mapistore_table_type table_type, uint32_t handle_id,
                       void **table_object, uint32_t *row_count)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder;
  MAPIStoreTable *table;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder getTable: &table
                andRowCount: row_count
                  tableType: table_type
                andHandleId: handle_id];
      if (rc == MAPISTORE_SUCCESS)
        *table_object = [table tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_modify_permissions(void *folder_object, uint8_t flags,
                               uint16_t pcount,
                               struct PermissionData *permissions)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder modifyPermissions: permissions
                           withCount: pcount
                            andFlags: flags];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_folder_preload_message_bodies(void *folder_object, enum mapistore_table_type table_type, const struct UI8Array_r *mids)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreFolder *folder;
  enum mapistore_error rc;

  if (folder_object)
    {
      wrapper = folder_object;
      folder = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [folder preloadMessageBodiesWithMIDs: mids
                                    ofTableType: table_type];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_message_get_message_data(void *message_object,
                              TALLOC_CTX *mem_ctx,
                              struct mapistore_message **msg_dataP)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreMessage *message;
  enum mapistore_error rc;

  if (message_object)
    {
      wrapper = message_object;
      message = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      [message getMessageData: msg_dataP
                     inMemCtx: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      rc = MAPISTORE_SUCCESS;
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_message_create_attachment (void *message_object, TALLOC_CTX *mem_ctx, void **attachment_object, uint32_t *aidp)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreMessage *message;
  MAPIStoreAttachment *attachment;
  enum mapistore_error rc;

  if (message_object)
    {
      wrapper = message_object;
      message = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [message createAttachment: &attachment inAID: aidp];
      if (rc == MAPISTORE_SUCCESS)
        *attachment_object = [attachment tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
       rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_message_open_attachment (void *message_object, TALLOC_CTX *mem_ctx,
                              uint32_t aid, void **attachment_object)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreMessage *message;
  MAPIStoreAttachment *attachment;
  enum mapistore_error rc;

  if (message_object)
    {
      wrapper = message_object;
      message = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [message getAttachment: &attachment withAID: aid];
      if (rc == MAPISTORE_SUCCESS)
        *attachment_object = [attachment tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_message_get_attachment_table (void *message_object, TALLOC_CTX *mem_ctx, void **table_object, uint32_t *row_count)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreMessage *message;
  MAPIStoreAttachmentTable *table;
  enum mapistore_error rc;

  if (message_object)
    {
      wrapper = message_object;
      message = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [message getAttachmentTable: &table
                           andRowCount: row_count];
      if (rc == MAPISTORE_SUCCESS)
        *table_object = [table tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_message_modify_recipients (void *message_object,
                                struct SPropTagArray *columns,
                                uint16_t count,
                                struct mapistore_message_recipient *recipients)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreMessage *message;
  enum mapistore_error rc;

  if (message_object)
    {
      wrapper = message_object;
      message = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [message modifyRecipientsWithRecipients: recipients
                                          andCount: count
                                        andColumns: columns];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_message_set_read_flag (void *message_object, uint8_t flag)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreMessage *message;
  enum mapistore_error rc;

  if (message_object)
    {
      wrapper = message_object;
      message = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [message setReadFlag: flag];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_message_save (void *message_object, TALLOC_CTX *mem_ctx)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreMessage *message;
  enum mapistore_error rc;

  if (message_object)
    {
      wrapper = message_object;
      message = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [message saveMessage: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_message_submit (void *message_object, enum SubmitFlags flags)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreMailVolatileMessage *message;
  enum mapistore_error rc;

  if (message_object)
    {
      wrapper = message_object;
      message = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [message submitWithFlags: flags];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_message_attachment_open_embedded_message (void *attachment_object,
                                               TALLOC_CTX *mem_ctx,
                                               void **message_object,
                                               uint64_t *midP,
                                               struct mapistore_message **msg)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreAttachment *attachment;
  MAPIStoreEmbeddedMessage *message;
  enum mapistore_error rc;

  if (attachment_object)
    {
      wrapper = attachment_object;
      attachment = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [attachment openEmbeddedMessage: &message
                                   withMID: midP
                          withMAPIStoreMsg: msg
                                  inMemCtx: mem_ctx];
      if (rc == MAPISTORE_SUCCESS)
        *message_object = [message tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_message_attachment_create_embedded_message (void *attachment_object,
                                                 TALLOC_CTX *mem_ctx,
                                                 void **message_object,
                                                 struct mapistore_message **msg)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreAttachment *attachment;
  MAPIStoreEmbeddedMessage *message;
  enum mapistore_error rc;

  if (attachment_object)
    {
      wrapper = attachment_object;
      attachment = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
          rc = [attachment createEmbeddedMessage: &message
                                withMAPIStoreMsg: msg
                                        inMemCtx: mem_ctx];
          if (rc == MAPISTORE_SUCCESS)
            *message_object = [message tallocWrapper: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error sogo_table_get_available_properties(void *table_object,
                                               TALLOC_CTX *mem_ctx, struct SPropTagArray **propertiesP)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreTable *table;
  enum mapistore_error rc;

  if (table_object)
    {
      wrapper = table_object;
      table = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [table getAvailableProperties: propertiesP inMemCtx: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_table_set_columns (void *table_object, uint16_t count, enum MAPITAGS *properties)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreTable *table;
  enum mapistore_error rc;

  if (table_object)
    {
      wrapper = table_object;
      table = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [table setColumns: properties
                   withCount: count];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_table_set_restrictions (void *table_object, struct mapi_SRestriction *restrictions, uint8_t *table_status)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreTable *table;
  enum mapistore_error rc;

  if (table_object)
    {
      wrapper = table_object;
      table = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      [table setRestrictions: restrictions];
      //[table cleanupCaches];
      rc = MAPISTORE_SUCCESS;
      *table_status = TBLSTAT_COMPLETE;
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_table_set_sort_order (void *table_object, struct SSortOrderSet *sort_order, uint8_t *table_status)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreTable *table;
  enum mapistore_error rc;

  if (table_object)
    {
      wrapper = table_object;
      table = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      [table setSortOrder: sort_order];
      [table cleanupCaches];
      rc = MAPISTORE_SUCCESS;
      *table_status = TBLSTAT_COMPLETE;
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_table_get_row (void *table_object, TALLOC_CTX *mem_ctx,
                    enum mapistore_query_type query_type, uint32_t row_id,
                    struct mapistore_property_data **data)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreTable *table;
  enum mapistore_error rc;

  if (table_object)
    {
      wrapper = table_object;
      table = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [table getRow: data withRowID: row_id andQueryType: query_type
                inMemCtx: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_table_get_row_count (void *table_object,
                          enum mapistore_query_type query_type,
                          uint32_t *row_countp)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreTable *table;
  enum mapistore_error rc;

  if (table_object)
    {
      wrapper = table_object;
      table = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [table getRowCount: row_countp
                withQueryType: query_type];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_table_handle_destructor (void *table_object, uint32_t handle_id)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreTable *table;
  enum mapistore_error rc;

  if (table_object)
    {
      wrapper = table_object;
      table = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      [table destroyHandle: handle_id];
      TRYCATCH_END(pool)

      [pool release];
      rc = MAPISTORE_SUCCESS;
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error sogo_properties_get_available_properties(void *object,
                                                    TALLOC_CTX *mem_ctx,
                                                    struct SPropTagArray **propertiesP)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreObject *propObject;
  enum mapistore_error rc;

  if (object)
    {
      wrapper = object;
      propObject = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [propObject getAvailableProperties: propertiesP inMemCtx: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_properties_get_properties (void *object,
                                TALLOC_CTX *mem_ctx,
                                uint16_t count, enum MAPITAGS *properties,
                                struct mapistore_property_data *data)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreObject *propObject;
  enum mapistore_error rc;

  if (object)
    {
      wrapper = object;
      propObject = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [propObject getProperties: data withTags: properties
                            andCount: count
                            inMemCtx: mem_ctx];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_properties_set_properties (void *object, struct SRow *aRow)
{
  struct MAPIStoreTallocWrapper *wrapper;
  NSAutoreleasePool *pool;
  MAPIStoreObject *propObject;
  enum mapistore_error rc;

  if (object)
    {
      wrapper = object;
      propObject = wrapper->instance;
      NS_CURRENT_THREAD_REGISTER();
      pool = [NSAutoreleasePool new];

      TRYCATCH_START
      rc = [propObject addPropertiesFromRow: aRow];
      TRYCATCH_END(pool)

      [pool release];
      NS_CURRENT_THREAD_TRY_UNREGISTER(rc);
    }
  else
    {
      rc = sogo_backend_unexpected_error();
    }

  return rc;
}

static enum mapistore_error
sogo_manager_generate_uri (TALLOC_CTX *mem_ctx,
                           const char *user,
                           const char *folder,
                           const char *message,
                           const char *rootURI,
                           char **uri)
{
  NSAutoreleasePool *pool;
  NSString *partialURLString, *username, *directory;

  if (uri == NULL) return MAPISTORE_ERR_INVALID_PARAMETER;

  /* This fixes a crash occurring during the instantiation of the
     NSAutoreleasePool below. */
  NS_CURRENT_THREAD_REGISTER();
  pool = [NSAutoreleasePool new];

  TRYCATCH_START
  // printf("rootURI = %s\n", rootURI);
  if (rootURI)
    partialURLString = [NSString stringWithUTF8String: rootURI];
  else
    {
      /* sogo uri are of type: sogo://[username]:[password]@[folder type]/folder/id */
      username = [NSString stringWithUTF8String: (user ? user : "*")];
      /* Do proper directory lookup here */
      directory = [NSString stringWithUTF8String: (folder ? folder : "*")];
      partialURLString = [NSString stringWithFormat: @"sogo://%@:*@%@",
                                   [username stringByReplacingOccurrencesOfString: @"@"
                                             withString: @"%40"],
                                   directory];
    }
  if (![partialURLString hasSuffix: @"/"])
    partialURLString = [partialURLString stringByAppendingString: @"/"];

  if (message)
    partialURLString = [partialURLString stringByAppendingFormat: @"%s.eml", message];

  // printf("uri = %s\n", [partialURLString UTF8String]);
  *uri = talloc_strdup (mem_ctx, [partialURLString UTF8String]);
  TRYCATCH_END(pool)

  [pool release];
  NS_CURRENT_THREAD_TRY_UNREGISTER(MAPISTORE_SUCCESS);

  return MAPISTORE_SUCCESS;
}

/**
   \details Entry point for mapistore SOGO backend

   \return MAPISTORE_SUCCESS on success, otherwise MAPISTORE error
*/
enum mapistore_error mapistore_init_backend(void)
{
  struct mapistore_backend backend;
  enum mapistore_error ret;
  static BOOL registered = NO;

  if (registered)
    ret = MAPISTORE_SUCCESS;
  else
    {
      registered = YES;

      backend.backend.name = "SOGo";
      backend.backend.description = "mapistore SOGo backend";
      backend.backend.namespace = "sogo://";
      backend.backend.init = sogo_backend_init;
      backend.backend.create_context = sogo_backend_create_context;
      backend.backend.create_root_folder = sogo_backend_create_root_folder;
      backend.backend.list_contexts = sogo_backend_list_contexts;
      backend.context.get_path = sogo_context_get_path;
      backend.context.get_root_folder = sogo_context_get_root_folder;
      backend.folder.open_folder = sogo_folder_open_folder;
      backend.folder.create_folder = sogo_folder_create_folder;
      backend.folder.delete = sogo_folder_delete;
      backend.folder.open_message = sogo_folder_open_message;
      backend.folder.create_message = sogo_folder_create_message;
      backend.folder.delete_message = sogo_folder_delete_message;
      backend.folder.move_copy_messages = sogo_folder_move_copy_messages;
      backend.folder.move_folder = sogo_folder_move_folder;
      backend.folder.copy_folder = sogo_folder_copy_folder;
      backend.folder.get_deleted_fmids = sogo_folder_get_deleted_fmids;
      backend.folder.get_child_count = sogo_folder_get_child_count;
      backend.folder.open_table = sogo_folder_open_table;
      backend.folder.modify_permissions = sogo_folder_modify_permissions;
      backend.folder.preload_message_bodies = sogo_folder_preload_message_bodies;
      backend.message.create_attachment = sogo_message_create_attachment;
      backend.message.get_attachment_table = sogo_message_get_attachment_table;
      backend.message.open_attachment = sogo_message_open_attachment;
      backend.message.open_embedded_message = sogo_message_attachment_open_embedded_message;
      backend.message.create_embedded_message = sogo_message_attachment_create_embedded_message;
      backend.message.get_message_data = sogo_message_get_message_data;
      backend.message.modify_recipients = sogo_message_modify_recipients;
      backend.message.set_read_flag = sogo_message_set_read_flag;
      backend.message.save = sogo_message_save;
      backend.message.submit = sogo_message_submit;
      backend.table.get_available_properties = sogo_table_get_available_properties;
      backend.table.set_restrictions = sogo_table_set_restrictions;
      backend.table.set_sort_order = sogo_table_set_sort_order;
      backend.table.set_columns = sogo_table_set_columns;
      backend.table.get_row = sogo_table_get_row;
      backend.table.get_row_count = sogo_table_get_row_count;
      backend.table.handle_destructor = sogo_table_handle_destructor;
      backend.properties.get_available_properties = sogo_properties_get_available_properties;
      backend.properties.get_properties = sogo_properties_get_properties;
      backend.properties.set_properties = sogo_properties_set_properties;
      backend.manager.generate_uri = sogo_manager_generate_uri;

      /* Register ourselves with the MAPISTORE subsystem */
      ret = mapistore_backend_register (&backend);
      if (ret != MAPISTORE_SUCCESS)
        OC_DEBUG(0, "[SOGo] Failed to register the '%s' mapistore backend!\n", backend.backend.name);
    }

  return ret;
}
