data_sync_server.c
/*
* libsyncml - A syncml protocol implementation
* Copyright (C) 2008-2009 Michael Bell <michael.bell@opensync.org>
*
* 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) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "data_sync_server.h"
#include "data_sync_common.h"
#include "libsyncml/sml_error_internals.h"
#include "data_sync_callbacks.h"
#include "libsyncml/objects/sml_ds_server.h"
#include "data_sync_devinf.h"
#include "libsyncml/sml_support.h"
static SmlBool smlDataSyncServerAlertCallback(
SmlDsSession *dsession,
SmlAlertType recvType,
const char *last,
const char *next,
void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %s, %p)", __func__, dsession, recvType, VA_STRING(last), VA_STRING(next), userdata);
SmlDataSyncDatastore *datastore = userdata;
SmlDataSyncObject *dsObject = datastore->dsObject;
SmlBool ret = TRUE;
SmlError *error = NULL;
SmlAlertType sentType = recvType;
/* libsyncml only supports SML_ALERT_TWO_WAY and SML_ALERT_SLOW_SYNC
* but some old phones reply on a SAN alert 206 with a slow sync 201
* alert or a SAN alert 206 (insteed of a normal two way alert 200).
* Therefore it is necessary to check for TWO_WAY and TWO_WAY_BY_SERVER.
*/
if (recvType != SML_ALERT_TWO_WAY &&
recvType != SML_ALERT_SLOW_SYNC &&
recvType != SML_ALERT_TWO_WAY_BY_SERVER)
{
smlErrorSet(&error, SML_ERROR_NOT_IMPLEMENTED, "Unsupported alert type %d.", recvType);
goto error;
}
char *remote_key = g_strdup_printf("remoteanchor%s", smlDsSessionGetLocation(dsession));
datastore->remoteNext = g_strdup(next);
/* We return FALSE if we need a special return code as answer:
* SML_ERROR_REQUIRE_REFRESH 508
* This return code enforces a SLOW-SYNC.
*/
if (recvType == SML_ALERT_TWO_WAY || recvType == SML_ALERT_TWO_WAY_BY_SERVER)
{
if (!last)
{
smlTrace(TRACE_INTERNAL, "%s: TWO-WAY-SYNC but last is missing", __func__);
sentType = SML_ALERT_SLOW_SYNC;
ret = FALSE;
} else {
char *cached = NULL;
if (dsObject->getAnchorCallback)
cached = dsObject->getAnchorCallback(
dsObject,
remote_key,
dsObject->getAnchorUserdata,
&error);
if (!cached && error)
goto error;
if (!cached || strcmp(cached, last))
{
smlTrace(TRACE_INTERNAL,
"%s: TWO-WAY-SYNC but received LAST(%s) and cached LAST (%s) mismatch",
__func__, VA_STRING(last), VA_STRING(cached));
if (cached)
smlSafeCFree(&cached);
sentType = SML_ALERT_SLOW_SYNC;
ret = FALSE;
}
}
}
if (dsObject->getAlertTypeCallback)
{
SmlAlertType alertType;
alertType = dsObject->getAlertTypeCallback(
dsObject,
datastore->sourceUri,
sentType,
dsObject->getAlertTypeUserdata,
&error);
if (alertType == SML_ALERT_UNKNOWN || error)
goto error;
if (alertType == SML_ALERT_SLOW_SYNC &&
alertType != recvType)
ret = FALSE; /* REQUIRE REFRESH */
sentType = alertType;
}
smlSafeCFree(&remote_key);
/* If the getAnchorCallback and the getAlertTypeCallback
* return inconsistent data then this must be detected here.
* Inconsistent means that for example status 508 and alert
* type 200 are used together which is illegal.
*/
if (
( /* alert 200 => alert 200 + status 508 */
recvType != SML_ALERT_SLOW_SYNC &&
sentType != SML_ALERT_SLOW_SYNC &&
ret == FALSE
) ||
( /* alert 200 => alert 201 + status 200 */
recvType != SML_ALERT_SLOW_SYNC &&
sentType == SML_ALERT_SLOW_SYNC &&
ret != FALSE
) ||
( /* alert 201 => alert 200 */
recvType == SML_ALERT_SLOW_SYNC &&
sentType != SML_ALERT_SLOW_SYNC
) ||
( /* alert 201 => alert 201 + status 508 */
recvType == SML_ALERT_SLOW_SYNC &&
sentType == SML_ALERT_SLOW_SYNC &&
ret == FALSE
)
)
{
if (ret) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The library user tries to respond an alert %d " \
"with an alert %d and status 200 which is illegal.",
recvType, sentType);
} else {
smlErrorSet(&error, SML_ERROR_GENERIC,
"The library user tries to respond an alert %d " \
"with an alert %d and status 508 which is illegal.",
recvType, sentType);
}
goto error;
}
/* generate new timestamp for local anchors */
char *local_key = g_strdup_printf("localanchor%s", smlDsSessionGetLocation(dsession));
char *local_last = NULL;
if (dsObject->getAnchorCallback)
local_last = dsObject->getAnchorCallback(
dsObject,
local_key,
dsObject->getAnchorUserdata,
&error);
if (!local_last && error)
goto error;
if (datastore->localNext)
smlSafeCFree(&(datastore->localNext));
if (local_last == NULL || strlen(local_last) == 0)
{
/* this is the first sync
* let's respect the remote's anchor style
*/
if (smlDataSyncIsTimestamp(next, dsObject->useTimestampAnchor) != dsObject->useTimestampAnchor)
{
/* Many users are confused by warnings which can be ignored.
* Therefore the issue is only traced.
*/
smlTrace(TRACE_INTERNAL,
"%s: libsyncml uses different timestamp anchor modes.",
__func__);
}
}
datastore->localNext = smlDataSyncGetNextAnchor(datastore, local_last, &error);
if (!datastore->localNext)
goto error;
/* send alert */
if (!smlDsSessionSendAlert(
dsession, sentType,
local_last, datastore->localNext,
smlDataSyncAlertStatusCallback,
datastore, &error))
goto error;
/* free local anchor stuff */
smlSafeCFree(&local_key);
if (local_last)
smlSafeCFree(&local_last);
smlTrace(TRACE_EXIT, "%s: %i", __func__, ret);
return ret;
error:
smlErrorRef(&error);
smlDataSyncSendEvent(
dsObject, SML_DATA_SYNC_EVENT_ERROR,
dsObject->eventUserdata, error);
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
return FALSE;
}
SmlBool smlDataSyncServerInit(SmlDataSyncObject *dsObject, SmlError **error)
{
CHECK_ERROR_REF
/* The manager responsible for handling the other objects */
dsObject->manager = smlManagerNew(dsObject->tsp, error);
if (!dsObject->manager)
goto error;
smlManagerSetEventCallback(dsObject->manager, smlDataSyncEventCallback, dsObject);
smlManagerSetLocalMaxMsgSize(dsObject->manager, dsObject->maxMsgSize);
smlManagerSetLocalMaxObjSize(dsObject->manager, dsObject->maxObjSize);
/* set server specific callbacks */
dsObject->funcDatastoreAlert = smlDataSyncServerAlertCallback;
/* The authenticator */
dsObject->auth = smlAuthNew(error);
if (!dsObject->auth)
goto error;
smlAuthSetVerifyCallback(dsObject->auth, smlDataSyncVerifyUserCallback, dsObject);
if (!dsObject->username) {
smlAuthSetEnable(dsObject->auth, FALSE);
} else {
smlAuthSetEnable(dsObject->auth, TRUE);
smlAuthSetType(dsObject->auth, dsObject->authType);
}
if (!smlAuthRegister(dsObject->auth, dsObject->manager, error))
goto error;
/* prepare device info */
if (!smlDataSyncDevInfInit(dsObject, SML_DEVINF_DEVTYPE_SERVER, error))
goto error;
/* prepare datastore server */
GList *o = dsObject->datastores;
for (; o; o = o->next) {
SmlDataSyncDatastore *datastore = o->data;
/* We now create the ds server hat the given location */
SmlLocation *loc = smlLocationNew(datastore->sourceUri, NULL, error);
if (!loc)
goto error;
datastore->server = smlDsServerNew(datastore->contentType, loc, error);
if (!datastore->server) {
smlLocationUnref(loc);
goto error;
}
smlLocationUnref(loc);
if (!smlDsServerRegister(datastore->server, dsObject->manager, error))
goto error;
smlDsServerSetConnectCallback(
datastore->server,
smlDataSyncDatastoreConnectCallback,
datastore);
/* And we also add the devinfo to the devinf agent */
if (!smlDataSyncDevInfAddDatastore(dsObject->localDevInf, datastore, error))
goto error;
}
/* Run the manager */
if (!smlManagerStart(dsObject->manager, error))
goto error;
/* Initialize the Transport */
if (!smlTransportInitialize(dsObject->tsp, error))
goto error;
smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
SmlDataSyncDatastore
This object represents an OMA DS datastore.
Definition: data_sync.h:93
smlErrorPrint
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
SmlDataSyncObject
This is the central synchronization object.
Definition: data_sync.h:110
smlDsSessionSendAlert
SmlBool smlDsSessionSendAlert(SmlDsSession *dsession, SmlAlertType type, const char *last, const char *next, SmlStatusReplyCb callback, void *userdata, SmlError **error)
Sends the alert to the remote side.
Definition: sml_ds_server.c:972
smlDsServerSetConnectCallback
void smlDsServerSetConnectCallback(SmlDsServer *server, SmlDsSessionConnectCb callback, void *userdata)
Registers a callback that will get called once a client connects.
Definition: sml_ds_server.c:279
SmlDsSession
Definition: sml_ds_server_internals.h:45
smlTransportInitialize
SmlBool smlTransportInitialize(SmlTransport *tsp, SmlError **error)
Initializes the transport with the given config.
Definition: sml_transport.c:969
smlTrace
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
SmlLocation
Definition: sml_elements_internals.h:40
smlErrorSet
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
SmlError
Represent an error.
Definition: sml_error_internals.h:29