/*
 * This file implements the Synthesis DB API and maps it to
 * EvolutionSyncSources. It was derived from the Synthesis
 * sync_dbapi_demo.c file.
 *
 * The external API of this file are the globally visible
 * C functions defined by sync_dbapidef.h.
 * 
 * Copyright (c) 2004-2008 by Synthesis AG
 * Copyright (C) 2009 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) version 3.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include <synthesis/sync_include.h>   /* include general SDK definitions */
#include <synthesis/sync_dbapidef.h>  /* include the interface file and utilities */
#include <synthesis/SDK_util.h>       /* include SDK utilities */

using namespace sysync;

#include <syncevo/SyncContext.h>
#include <syncevo/SyncSource.h>
#include <syncevo/IdentityProvider.h>

#include <sstream>

#include <syncevo/declarations.h>
SE_BEGIN_CXX

#define BuildNumber  0  /* User defined build number, can be 0..255 */
#define MyDB   "SyncEvolution" /* example debug name */
#define MY_ID       42  /* example datastore context */

#define STRLEN      80  /* Max length of local string copies */

/* -- MODULE -------------------------------------------------------------------- */
/* <mContext> will be casted to the SyncSource * structure */
static SyncSource *MoC(CContext mContext) { return (SyncSource *)mContext; }

/**
 * looks up datasource and uses pointer to it as context
 *
 * @param mContextName   name of previously instantiated SyncSource, "" when used as session module
 * @retval mContext      the corresponding SyncSource
 */
extern "C"
TSyError SyncEvolution_Module_CreateContext( CContext *mContext, cAppCharP   moduleName,
                                             cAppCharP      subName,
                                             cAppCharP mContextName,
                                             DB_Callback mCB )
{
    TSyError err = LOCERR_WRONGUSAGE;
    if (!mContextName[0]) {
        *mContext = nullptr;
        err = LOCERR_OK;
    } else {
        SyncSource *source = SyncContext::findSource(mContextName);
        if (source) {
            source->pushSynthesisAPI(mCB);
            *mContext = (CContext)source;
            err = LOCERR_OK;
        }
    }

    SE_LOG_DEBUG(NULL, "CreateContext %s/%s/%s => %d",
                 moduleName, subName, mContextName, err);
    return err;
}



/**
 * @TODO: introduce and return some kind of SyncEvolution build number
 */
extern "C"
CVersion SyncEvolution_Module_Version(CContext mContext) 
{
    CVersion v = Plugin_Version(BuildNumber);
  
    if (mContext) {
        SE_LOG_DEBUG(NULL, "Module_Version = %08lx", (long)v);
    }
    return v;
}



/* Get the plug-in's capabilities */
extern "C"
TSyError SyncEvolution_Module_Capabilities( CContext mContext, appCharP *mCapabilities )
{
    SyncSource *source  = MoC(mContext);

    std::stringstream s;
    s << MyPlatform() << "\n"
      << DLL_Info << "\n"
      << CA_MinVersion << ":V1.0.6.0\n" /* must not be changed */
      << CA_Manufacturer << ":SyncEvolution\n"
      << CA_Description << ":SyncEvolution Synthesis DB Plugin\n"
      << Plugin_DS_Data_Str << ":no\n"
      << Plugin_DS_Data_Key << ":yes\n"
      << CA_ItemAsKey << ":yes\n"
      << Plugin_DS_Blob <<
        ((source && source->getOperations().m_readBlob) ?
         ":yes\n" :
         ":no\n");

    if (source && source->getOperations().m_loadAdminData) {
        s << Plugin_DS_Admin << ":yes\n";
        if (source && !source->getOperations().m_readNextMapItem) {
            s << CA_ResumeSupported << ":no\n";
        }
    }

    *mCapabilities= StrAlloc(s.str().c_str());
    SE_LOG_DEBUG(NULL, "Module_Capabilities:\n%s", *mCapabilities);
    return LOCERR_OK;
} /* Module_Capabilities */



extern "C"
TSyError SyncEvolution_Module_PluginParams( CContext mContext,
                                            cAppCharP mConfigParams, CVersion engineVersion )
{
    SyncSource *source  = MoC(mContext);
    SE_LOG_DEBUG(source ? source->getDisplayName() : "", "Module_PluginParams\n Engine=%08lX\n %s",
                 (long)engineVersion, mConfigParams);
    /*return LOCERR_CFGPARSE;*/ /* if there are unsupported params */
    return LOCERR_OK;
} /* Module_PluginParams */



/* Dispose the memory of the module context */
extern "C"
void SyncEvolution_Module_DisposeObj( CContext mContext, void* memory )
{
    StrDispose(memory);
}

extern "C"
TSyError SyncEvolution_Module_DeleteContext( CContext mContext )
{
    SyncSource  *source = MoC(mContext);
    SE_LOG_DEBUG(NULL, "Module_DeleteContext %s",
                 source ? source->getName().c_str() : "'session'");
    if (source) {
        source->popSynthesisAPI();
    }
    return LOCERR_OK;
}


/* <sContext> will be casted to the SyncContext* structure */
static SyncContext* SeC( CContext sContext ) { return (SyncContext*)sContext; }



/* Create a context for a new session. Maps to the existing SyncContext. */ 
extern "C"
TSyError SyncEvolution_Session_CreateContext( CContext *sContext, cAppCharP sessionName, DB_Callback sCB )
{ 
    *sContext = (CContext)SyncContext::findContext(sessionName);
    SE_LOG_DEBUG(NULL, "Session_CreateContext '%s' %s", 
                 sessionName, *sContext ? "found" : "not found");
    if (*sContext) {
        return LOCERR_OK;
    } else {
        return DB_NotFound;
    }
} /* Session_CreateContext */



/* ----- "script-like" ADAPT --------- */
extern "C"
TSyError SyncEvolution_Session_AdaptItem( CContext sContext, appCharP *sItemData1, 
                                          appCharP *sItemData2,
                                          appCharP *sLocalVars, 
                                          uInt32  sIdentifier ) 
{ 
    /**** CAN BE ADAPTED BY USER ****/ 
    // SyncContext* sc= SeC( sContext );
    SE_LOG_DEBUG(NULL, "Session_AdaptItem '%s' '%s' '%s' id=%d", 
                 *sItemData1,*sItemData2,*sLocalVars, sIdentifier);
    return LOCERR_OK;
} /* Session_AdaptItem */



/* Check the database entry of <deviceID> and return its nonce string */
extern "C"
TSyError SyncEvolution_Session_CheckDevice( CContext sContext,
                                            cAppCharP aDeviceID, appCharP *sDevKey,
                                            appCharP *nonce )
{
    SyncContext *sc = SeC(sContext);
    if (!sc) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = LOCERR_OK;

    sc->setSyncDeviceID(aDeviceID);
    string id = sc->getRemoteDevID();
    if (id.empty()) {
        sc->setRemoteDevID(aDeviceID);
        sc->flush();
    } else if (id != aDeviceID) {
        // We are using the wrong configuration?! Refuse to continue.
        SE_LOG_ERROR(NULL, "remote device ID '%s' in config does not match the one from the peer '%s' - incorrect configuration?!",
                     id.c_str(), aDeviceID);
        res = DB_Forbidden;
    }

    *sDevKey= StrAlloc(aDeviceID);
    *nonce = StrAlloc(sc->getNonce().c_str());
    SE_LOG_DEBUG(NULL, "Session_CheckDevice dev='%s' nonce='%s' res=%d",
                 *sDevKey, *nonce, res);
    return res;
} /* Session_CheckDevice */



/* Get a new nonce from the database. If this returns an error, the SyncML engine
 * will create its own nonce.
 */
extern "C"
TSyError SyncEvolution_Session_GetNonce( CContext sContext, appCharP *nonce )
{
    return DB_NotFound;
} /* Session_GetNonce */

 
 
/* Save the new nonce (which will be expected to be returned 
 * in the next session for this device
 */
extern "C"
TSyError SyncEvolution_Session_SaveNonce( CContext sContext, cAppCharP nonce )
{
    SyncContext *sc = SeC(sContext);
    if (!sc) {
        return LOCERR_WRONGUSAGE;
    }
    SE_LOG_DEBUG(NULL, "Session_SaveNonce nonce='%s'",
                 nonce);
    sc->setNonce(nonce);
    sc->flush();
    return LOCERR_OK;
} /* Session_SaveNonce */



/* Save the device info of <sContext> */
extern "C"
TSyError SyncEvolution_Session_SaveDeviceInfo( CContext sContext, cAppCharP aDeviceInfo )
{
    SyncContext *sc = SeC(sContext);
    if (!sc) {
        return LOCERR_WRONGUSAGE;
    }
    SE_LOG_DEBUG(NULL, "Session_SaveDeviceInfo info='%s'",
                 aDeviceInfo );
    sc->setDeviceData(aDeviceInfo);
    sc->flush();
    return LOCERR_OK;
} /* Session_SaveDeviceInfo */



/* Get the plugin's DB time */
extern "C"
TSyError SyncEvolution_Session_GetDBTime( CContext sContext, appCharP *currentDBTime )
{ 
    return DB_NotFound;
} /* Session_GetDBTime */



/* Return: Password_ClrText_IN    'SessionLogin' will get    clear text password
 *         Password_ClrText_OUT         "        must return clear text password
 *         Password_MD5_OUT             "        must return MD5 coded  password
 *         Password_MD5_Nonce_IN        "        will get    MD5B64(MD5B64(user:pwd):nonce)
 */
extern "C"
sInt32 SyncEvolution_Session_PasswordMode( CContext sContext )
{
    return Password_ClrText_OUT;
} /* Session_PasswordMode */



/* Make login */
extern "C"
TSyError SyncEvolution_Session_Login( CContext sContext, cAppCharP sUsername, appCharP *sPassword,
                                      appCharP *sUsrKey )
{ 
    SyncContext *sc = SeC(sContext);
    if (!sc) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = DB_Forbidden;
    UserIdentity id = sc->getSyncUser();
    Credentials cred = IdentityProviderCredentials(id, sc->getSyncPassword());
    const std::string &user = cred.m_username;
    const std::string &password = cred.m_password;

    if (user.empty() && password.empty()) {
        // nothing to check, accept peer
        res = LOCERR_OK;
    } else if (user == sUsername) {
        *sPassword=StrAlloc(password.c_str());
        res = LOCERR_OK;
    }

    SE_LOG_DEBUG(NULL, "Session_Login usr='%s' expected user='%s' res=%d",
                 sUsername, user.c_str(), res);
    return res;
} /* Session_Login */



/* Make logout */
extern "C"
TSyError SyncEvolution_Session_Logout( CContext sContext )
{
    return LOCERR_OK;
} /* Session_Logout */



extern "C"
void SyncEvolution_Session_DisposeObj( CContext sContext, void* memory )
{
  StrDispose              ( memory );
} /* Session_DisposeObj */



/* Can be implemented empty, if no action is required */
extern "C"
void SyncEvolution_Session_ThreadMayChangeNow( CContext sContext )
{
} /* Session_ThreadMayChangeNow */



/* This routine is implemented for debug purposes only and will NOT BE CALLED by the
 * SyncML engine. Can be implemented empty, if not needed
 */
extern "C"
void SyncEvolution_Session_DispItems( CContext sContext, bool allFields, cAppCharP specificItem )
{
} /* Session_DispItems */



/* Delete a session context */
extern "C"
TSyError SyncEvolution_Session_DeleteContext( CContext sContext ) 
{ 
    return LOCERR_OK;
} /* Session_DeleteContext */




/* ----------------------------------------------------------------- */
/* This is an example, how a context could be structured */
/* <aContext> will be casted to the SyncSource structure */
static SyncSource *DBC( CContext aContext ) { return (SyncSource *)aContext; }



/* -- OPEN ----------------------------------------------------------------------- */
/**
 * looks up datasource and uses pointer to it as context
 *
 * @param aContextName   name of previously instantiated SyncSource
 * @retval aContext      the corresponding SyncSource
 */
extern "C"
TSyError SyncEvolution_CreateContext( CContext *aContext, cAppCharP aContextName, DB_Callback aCB,
                                      cAppCharP sDevKey,
                                      cAppCharP sUsrKey )
{
    TSyError err = LOCERR_WRONGUSAGE;
    SyncSource *source = SyncContext::findSource(aContextName);
    if (source) {
        source->pushSynthesisAPI(aCB);
        *aContext = (CContext)source;
        err = LOCERR_OK;
    }
    SE_LOG_DEBUG(source ? source->getDisplayName() : "", "'%s' dev='%s' usr='%s' err=%d",
                 aContextName, sDevKey, sUsrKey, err);
    return err;
}



extern "C"
uInt32 SyncEvolution_ContextSupport( CContext aContext, cAppCharP aContextRules ) 
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    SE_LOG_DEBUG(source->getDisplayName(), "ContextSupport %s", aContextRules);
    return 0;
}



extern "C"
uInt32 SyncEvolution_FilterSupport( CContext aContext, cAppCharP aFilterRules )
{
  /**** CAN BE ADAPTED BY USER ****/ 
  SyncSource *source = DBC( aContext );
  if (!source) {
      return LOCERR_WRONGUSAGE;
  }
  SE_LOG_DEBUG(source->getDisplayName(), "FilterSupport %s", aFilterRules);
  return 0;
} /* FilterSupport */



/* -- ADMINISTRATION ------------------------------------------------------------ */
extern "C"
TSyError SyncEvolution_LoadAdminData( CContext aContext, cAppCharP aLocDB,
                                      cAppCharP aRemDB, appCharP *adminData )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_loadAdminData(aLocDB, aRemDB, adminData);
    SE_LOG_DEBUG(source->getDisplayName(), "LoadAdminData '%s' '%s', '%s' res=%d",
                 aLocDB, aRemDB, *adminData ? *adminData : "", res);
    return res;
} /* LoadAdminData */



extern "C"
TSyError SyncEvolution_SaveAdminData( CContext aContext, cAppCharP adminData )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_saveAdminData(adminData);
    SE_LOG_DEBUG(source->getDisplayName(), "SaveAdminData '%s' res=%d", adminData, res);
    return res;
} /* SaveAdminData */



extern "C"
bool SyncEvolution_ReadNextMapItem( CContext aContext, MapID mID, bool aFirst )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return false;
    }
    bool res = false;
    try {
        // always reset mID, just in case that caller expects it or
        // operation doesn't do it correctly
        mID->localID = nullptr;
        mID->remoteID = nullptr;
        mID->ident = 0;
        mID->flags = 0;
        if (source->getOperations().m_readNextMapItem) {
            res = source->getOperations().m_readNextMapItem(mID, aFirst);
        }
    } catch (...) {
        res = source->handleException();
    }

    SE_LOG_DEBUG(source->getDisplayName(), "ReadNextMapItem '%s' + %x = '%s' + %d first=%s res=%d",
                 res ? NullPtrCheck(mID->localID) : "(none)",
                 res ? mID->ident : 0,
                 res ? NullPtrCheck(mID->remoteID) : "(none)",
                 res ? mID->flags : 0,
                 aFirst ? "yes" : "no",
                 res);
    return res;
} /* ReadNextMapItem */



extern "C"
TSyError SyncEvolution_InsertMapItem( CContext aContext, cMapID mID )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_insertMapItem(mID);
    SE_LOG_DEBUG(source->getDisplayName(), "InsertMapItem '%s' + %x = '%s' + %x res=%d", 
                 NullPtrCheck(mID->localID), mID->ident,
                 NullPtrCheck(mID->remoteID), mID->flags,
                 res);
    return res;
} /* InsertMapItem */



extern "C"
TSyError SyncEvolution_UpdateMapItem( CContext aContext, cMapID mID )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_updateMapItem(mID);
    SE_LOG_DEBUG(source->getDisplayName(), "UpdateMapItem '%s' + %x = '%s' + %x, res=%d", 
                 mID->localID, mID->ident,
                 mID->remoteID, mID->flags,
                 res);

    return res;
} /* UpdateMapItem */



extern "C"
TSyError SyncEvolution_DeleteMapItem( CContext aContext, cMapID mID )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_deleteMapItem(mID);
    SE_LOG_DEBUG(source->getDisplayName(), "DeleteMapItem '%s' + %x = '%s' + %x res=%d",
                 mID->localID, mID->ident,
                 mID->remoteID, mID->flags,
                 res);
    return res;
} /* DeleteMapItem */




/* -- GENERAL -------------------------------------------------------------------- */
extern "C"
void SyncEvolution_DisposeObj( CContext aContext, void* memory )
{
  free( memory );
} /* DisposeObj */



extern "C"
void SyncEvolution_ThreadMayChangeNow( CContext aContext )
{
} /* ThreadMayChangeNow */



extern "C"
void SyncEvolution_WriteLogData( CContext aContext, cAppCharP logData )
{
} /* WriteLogData */



/* This routine is implemented for debug purposes only and will NOT BE CALLED by the
 * SyncML engine. Can be implemented empty, if not needed
 */
extern "C"
void SyncEvolution_DispItems( CContext aContext, bool allFields, cAppCharP specificItem )
{
} /* DispItems */



/* ----- "script-like" ADAPT --------- */
extern "C"
TSyError SyncEvolution_AdaptItem( CContext aContext, appCharP *aItemData1,
                                  appCharP *aItemData2,
                                  appCharP *aLocalVars, 
                                  uInt32  aIdentifier ) 
{ 
  /**** CAN BE ADAPTED BY USER ****/ 
  SyncSource *source = DBC( aContext );
  if (!source) {
      return LOCERR_WRONGUSAGE;
  }
  SE_LOG_DEBUG(source->getDisplayName(), "AdaptItem '%s' '%s' '%s' id=%d", 
               *aItemData1, *aItemData2, *aLocalVars, aIdentifier);
  return LOCERR_OK;
} /* AdaptItem */



/* -- READ ---------------------------------------------------------------------- */
/**
 * Start data access here and complete it in SyncEvolution_EndDataWrite().
 */
extern "C"
TSyError SyncEvolution_StartDataRead( CContext aContext, cAppCharP   lastToken,
                                      cAppCharP resumeToken )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_startDataRead(lastToken, resumeToken);
    SE_LOG_DEBUG(source->getDisplayName(), "StartDataRead last='%s' resume='%s' res=%d",
                 lastToken, resumeToken, res);
    return res;
}


extern "C"
TSyError SyncEvolution_ReadNextItemAsKey( CContext aContext, ItemID aID, KeyH aItemKey,
                                          sInt32*    aStatus, bool aFirst )
{
    /**** CAN BE ADAPTED BY USER ****/
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    *aStatus = 0;
    memset(aID, 0, sizeof(*aID));
    TSyError res = source->getOperations().m_readNextItem(aID, aStatus, aFirst);
    SE_LOG_DEBUG(source->getDisplayName(), "ReadNextItemAsKey aStatus=%d aID=(%s,%s) res=%d",
                 *aStatus, aID->item, aID->parent, res);
    return res;
}

extern "C"
TSyError SyncEvolution_ReadItemAsKey( CContext aContext, cItemID aID, KeyH aItemKey )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_readItemAsKey(aID, aItemKey);
    SE_LOG_DEBUG(source->getDisplayName(), "ReadItemAsKey aID=(%s,%s) res=%d",
                 aID->item, aID->parent, res);
    return res;
}

extern "C"
sysync::TSyError SyncEvolution_ReadBlob(CContext aContext, cItemID  aID,  cAppCharP  aBlobID,
                                        appPointer *aBlkPtr, memSize *aBlkSize, 
                                        memSize *aTotSize,
                                        bool  aFirst,    bool *aLast)
{
  SyncSource *source = DBC( aContext );
  if (!source) {
      return LOCERR_WRONGUSAGE;
  }

  TSyError res;
  if (source->getOperations().m_readBlob) {
      try {
            size_t blksize = aBlkSize ? static_cast<size_t>(*aBlkSize) : 0,
                totsize = aTotSize ? static_cast<size_t>(*aTotSize) : 0;
	    /* Another conversion between memSize and size_t to make s390 happy */
            res = source->getOperations().m_readBlob(aID, aBlobID, (void **)aBlkPtr,
						     aBlkSize ? &blksize : nullptr,
						     aTotSize ? &totsize : nullptr,
						     aFirst, aLast);
	    if (aBlkSize) {
	        *aBlkSize = blksize;
	    }
            if (aTotSize) {
                *aTotSize = totsize;
            }

      } catch (...) {
          res = source->handleException();
      }
  } else {
      res = LOCERR_NOTIMP;
  }

  SE_LOG_DEBUG(source->getDisplayName(), "ReadBlob aID=(%s,%s) aBlobID=(%s) aBlkPtr=%p aBlkSize=%lu aTotSize=%lu aFirst=%s aLast=%s res=%d",
               aID->item,aID->parent, aBlobID, aBlkPtr,
               aBlkSize ? (unsigned long)*aBlkSize : 0,
               aTotSize ? (unsigned long)*aTotSize : 0,
               aFirst? "true" : "false", *aLast ? "true" : "false", res);
  return res;
} /* ReadBlob */


extern "C"
TSyError SyncEvolution_EndDataRead( CContext aContext )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_endDataRead();
    SE_LOG_DEBUG(source->getDisplayName(), "EndDataRead res=%d", res);
    return res;
}




/* -- WRITE --------------------------------------------------------------------- */
extern "C"
TSyError SyncEvolution_StartDataWrite( CContext aContext )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    SE_LOG_DEBUG(source->getDisplayName(), "StartDataWrite");
    return LOCERR_OK;
}

extern "C"
TSyError SyncEvolution_InsertItemAsKey( CContext aContext, KeyH aItemKey, ItemID newID )
{
    /**** CAN BE ADAPTED BY USER ****/
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_insertItemAsKey(aItemKey, newID);
    SE_LOG_DEBUG(source->getDisplayName(), "InsertItemAsKey res=%d\n", res);
    return res;
}


extern "C"
TSyError SyncEvolution_UpdateItemAsKey( CContext aContext, KeyH aItemKey, cItemID   aID, 
                                        ItemID updID )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_updateItemAsKey(aItemKey, aID, updID);
    SE_LOG_DEBUG(source->getDisplayName(), "aID=(%s,%s) res=%d",
                 aID->item,aID->parent, res);
    return res;
}



extern "C"
TSyError SyncEvolution_MoveItem( CContext aContext, cItemID aID, cAppCharP newParID )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    SE_LOG_DEBUG(source->getDisplayName(), "MoveItem aID=(%s,%s) => (%s,%s)",
                 aID->item,aID->parent, aID->item,newParID);
    return LOCERR_NOTIMP;
}



extern "C"
TSyError SyncEvolution_DeleteItem( CContext aContext, cItemID aID )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_deleteItem(aID);
    SE_LOG_DEBUG(source->getDisplayName(), "DeleteItem aID=(%s,%s) res=%d",
                 aID->item, aID->parent, res);
    return res;
}



extern "C"
TSyError SyncEvolution_FinalizeLocalID( CContext aContext, cItemID aID, ItemID updID )
{
    return LOCERR_NOTIMP;
}



extern "C"
TSyError SyncEvolution_DeleteSyncSet( CContext aContext )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    SE_LOG_DEBUG(source->getDisplayName(), "DeleteSyncSet not implemented");
    return LOCERR_NOTIMP;
}


extern "C"
TSyError SyncEvolution_WriteBlob(CContext aContext, cItemID aID,  cAppCharP aBlobID,
                                 appPointer aBlkPtr, memSize aBlkSize, 
                                 memSize aTotSize,
                                 bool aFirst,    bool aLast)
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }

    TSyError res;
    if (source->getOperations().m_writeBlob) {
        try {
            res = source->getOperations().m_writeBlob(aID, aBlobID, aBlkPtr, aBlkSize,
                                                      aTotSize, aFirst, aLast);
        } catch (...) {
            res = source->handleException();
        }
    } else {
        res = LOCERR_NOTIMP;
    }
    
    SE_LOG_DEBUG(source->getDisplayName(), "WriteBlob aID=(%s,%s) aBlobID=(%s) aBlkPtr=%p aBlkSize=%lu aTotSize=%lu aFirst=%s aLast=%s res=%d",
                 aID->item,aID->parent, aBlobID, aBlkPtr, (unsigned long)aBlkSize, (unsigned long)aTotSize, 
                 aFirst ? "true" : "false", aLast ? "true" : "false", res);
    return res;
} /* WriteBlob */


extern "C"
TSyError SyncEvolution_DeleteBlob( CContext aContext, cItemID aID, cAppCharP aBlobID )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }

    TSyError res = source->getOperations().m_deleteBlob(aID, aBlobID);
    SE_LOG_DEBUG(source->getDisplayName(), "DeleteBlob aID=(%s,%s) aBlobID=(%s) res=%d",
                 aID->item,aID->parent, aBlobID, res);
    return res;
} /* DeleteBlob */

extern "C"
TSyError SyncEvolution_EndDataWrite( CContext aContext, bool success, appCharP *newToken )
{
    SyncSource *source = DBC( aContext );
    if (!source) {
        return LOCERR_WRONGUSAGE;
    }
    TSyError res = source->getOperations().m_endDataWrite(success, newToken);
    SE_LOG_DEBUG(source->getDisplayName(), "EndDataWrite %s '%s' res=%d", 
                 success ? "COMMIT":"ROLLBACK", *newToken, res);
    return res;
}

/* ----------------------------------- */
extern "C"
TSyError SyncEvolution_DeleteContext( CContext aContext )
{
  /**** CAN BE ADAPTED BY USER ****/ 
  SyncSource *source = DBC( aContext );
  if (!source) {
      return LOCERR_WRONGUSAGE;
  }
  SE_LOG_DEBUG(source->getDisplayName(), "DeleteContext");
  source->popSynthesisAPI();
  return LOCERR_OK;
}

SE_END_CXX
