data_sync_client.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 smlDataSyncClientAlertCallback(
SmlDsSession *dsession,
SmlAlertType type,
const char *last,
const char *next,
void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %s, %p)", __func__, dsession, type, VA_STRING(last), VA_STRING(next), userdata);
SmlDataSyncDatastore *datastore = userdata;
SmlDataSyncObject *dsObject = datastore->dsObject;
SmlBool ret = TRUE;
SmlError *error = NULL;
/* 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 (type != SML_ALERT_TWO_WAY &&
type != SML_ALERT_SLOW_SYNC &&
type != SML_ALERT_TWO_WAY_BY_SERVER)
{
smlErrorSet(&error, SML_ERROR_NOT_IMPLEMENTED, "Unsupported alert type %d.", type);
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 (type == SML_ALERT_TWO_WAY || type == SML_ALERT_TWO_WAY_BY_SERVER)
{
if (!last)
{
smlTrace(TRACE_INTERNAL, "%s: TWO-WAY-SYNC is requested but there is no LAST anchor.", __func__);
type = 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 is requested but the cached LAST anchor (%s) does not match the presented LAST anchor from the remote peer (%s).",
__func__, last, cached);
if (cached)
smlSafeCFree(&cached);
type = SML_ALERT_SLOW_SYNC;
ret = FALSE;
}
}
}
if (dsObject->getAlertTypeCallback)
{
SmlAlertType h = dsObject->getAlertTypeCallback(
dsObject,
datastore->sourceUri,
type,
dsObject->getAlertTypeUserdata,
&error);
if (h == SML_ALERT_UNKNOWN || error)
goto error;
/* There is only one situation where the callback can return
* something different than type. If the client requested an
* alert type 200, the server requested an alert type 200 too
* and after this there is a problem detected on the client
* then the returned type can be 201 SLOW-SYNC.
*
* If this happens then a new alert 201 and a status 508 must
* be send.
*/
if (h != type) {
if (h != SML_ALERT_SLOW_SYNC) {
smlErrorSet(&error, SML_ERROR_GENERIC,
"It is not possible to change the alert type after an OMA DS client received the alerts from the OMA DS server.");
goto error;
} else {
/* send alert 201 */
SmlLocation *source = NULL;
source = smlLocationNew(datastore->sourceUri, NULL, &error);
if (!source)
goto error;
SmlCommand *alert = NULL;
alert = smlCommandNewAlert(
SML_ALERT_SLOW_SYNC,
smlDsSessionGetTarget(datastore->session),
source,
NULL, datastore->localNext,
NULL, &error);
if (!alert) {
smlLocationUnref(source);
goto error;
}
smlLocationUnref(source);
if (!smlSessionSendCommand(dsObject->session, alert, NULL, NULL, NULL, &error)) {
smlCommandUnref(alert);
goto error;
}
smlCommandUnref(alert);
/* send status 508 */
ret = FALSE;
}
}
}
smlSafeCFree(&remote_key);
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 smlDataSyncClientSendAlert(
SmlDataSyncDatastore *datastore,
SmlAlertType type,
SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %d, %p)", __func__, datastore, type, error);
CHECK_ERROR_REF
SmlDataSyncObject *dsObject = datastore->dsObject;
/* initialize the last anchor */
SmlAlertType alertType = type;
char *local_last = NULL; // perhaps NULL is better
if (dsObject->getAlertTypeCallback)
{
alertType = dsObject->getAlertTypeCallback(
dsObject,
datastore->sourceUri,
type,
dsObject->getAlertTypeUserdata,
error);
if (*error)
goto error;
}
if (alertType == SML_ALERT_UNKNOWN)
{
smlTrace(TRACE_INTERNAL, "%s: no alert type => slow-sync", __func__);
alertType = SML_ALERT_SLOW_SYNC;
}
if (alertType != SML_ALERT_SLOW_SYNC)
{
/* this must be a two-way-sync */
alertType = SML_ALERT_TWO_WAY;
char *local_key = g_strdup_printf("localanchor%s", smlDsServerGetLocation(datastore->server));
if (dsObject->getAnchorCallback)
local_last = dsObject->getAnchorCallback(
dsObject,
local_key,
dsObject->getAnchorUserdata,
error);
smlSafeCFree(&local_key);
}
/* initialize the next anchor */
if (datastore->localNext)
smlSafeCFree(&(datastore->localNext));
datastore->localNext = smlDataSyncGetNextAnchor(datastore, local_last, error);
/* The session is required here. So if the session is missing
* then the function has to block here. This is no real problem
* because the SESSION_EVENT_NEW must already be in the queue of
* the manager object.
*/
while (!dsObject->session) {
smlManagerDispatch(dsObject->manager);
}
/* The OMA DS client starts the synchronization so there should be no
* DsSession (datastore session) present.
*/
datastore->session = smlDsServerSendAlert(
datastore->server,
dsObject->session,
alertType,
local_last, datastore->localNext,
smlDataSyncAlertStatusCallback, datastore,
error);
if (local_last)
smlSafeCFree(&local_last);
if (!datastore->session)
goto error;
/* Only the alert callback is registered here because the changes should
* be managed after the function ds_client_get_changeinfo was called.
*/
smlDsSessionGetAlert(datastore->session, smlDataSyncClientAlertCallback, datastore);
if (dsObject->changeCallback) {
smlDsSessionGetSync(datastore->session,
smlDataSyncSyncCallback, datastore);
smlDsSessionGetChanges(datastore->session,
smlDataSyncChangeCallback, datastore);
}
/* If a slow-sync alert is initiated and the remote peer
* did not request it then the function must return false
* to trigger a status 508 (REFRESH_REQUIRED).
* If the requested alert type is not known then there
* is no need for a status and the function returns true.
* This can happen if the remote peer did not trigger
* the client via a SAN (e.g. OMA DS client over HTTP).
*/
SmlBool ret = TRUE;
if (alertType == SML_ALERT_SLOW_SYNC &&
alertType != type &&
type != SML_ALERT_UNKNOWN) {
ret = FALSE;
}
smlTrace(TRACE_EXIT, "%s: %i", __func__, ret);
return ret;
error:
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
static SmlErrorType smlDataSyncSanCallback(
SmlDsServer *server,
SmlSession *session,
SmlAlertType type,
void *userdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %d, %p)", __func__, server, session, type, userdata);
SmlDataSyncDatastore *datastore = userdata;
SmlDataSyncObject *dsObject = datastore->dsObject;
SmlError *error = NULL;
SmlErrorType ret = SML_NO_ERROR;
/* soemtimes the event manager thread is too slow */
if (!dsObject->session) {
dsObject->session = session;
smlSessionRef(session);
}
if (!smlDataSyncClientSendAlert(datastore, type, &error)) {
if (!error)
ret = SML_ERROR_REQUIRE_REFRESH;
else
goto error;
}
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 SML_ERROR_GENERIC;
}
SmlBool smlDataSyncClientInit(SmlDataSyncObject *dsObject, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, dsObject, 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 = smlDataSyncClientAlertCallback;
/* prepare device info */
if (!smlDataSyncDevInfInit(dsObject, SML_DEVINF_DEVTYPE_WORKSTATION, error))
goto error;
/* prepare datastore server */
GList *o = dsObject->datastores;
for (; o; o = o->next) {
SmlDataSyncDatastore *datastore = o->data;
smlTrace(TRACE_INTERNAL, "preparing DsServer (datastore) %s", datastore->sourceUri);
/* We now create the ds server at the given location. */
SmlLocation *loc = smlLocationNew(datastore->sourceUri, NULL, error);
if (!loc)
goto error;
datastore->server = smlDsClientNew(datastore->contentType, loc, loc, error);
smlLocationUnref(loc);
if (!datastore->server)
goto error;
if (!smlDsServerRegister(datastore->server, dsObject->manager, error))
goto error;
/* this is a client and not a server
* but the callback initializes only database->session (DsSession)
*/
smlDsServerSetConnectCallback(
datastore->server,
smlDataSyncDatastoreConnectCallback,
datastore);
smlDsServerSetSanSessionCallback(
datastore->server,
smlDataSyncSanCallback,
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;
}
smlDsSessionGetChanges
void smlDsSessionGetChanges(SmlDsSession *dsession, SmlDsSessionChangesCb chgCallback, void *userdata)
Gets a already received sync command.
Definition: sml_ds_server.c:1022
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
smlDsSessionGetAlert
void smlDsSessionGetAlert(SmlDsSession *dsession, SmlDsSessionAlertCb callback, void *userdata)
Gets a already received alert.
Definition: sml_ds_server.c:946
SmlDsServer
Definition: sml_ds_server_internals.h:101
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
SmlSession
Definition: sml_session_internals.h:27
SmlCommand
Definition: sml_command_internals.h:73
smlSessionSendCommand
SmlBool smlSessionSendCommand(SmlSession *session, SmlCommand *cmd, SmlCommand *parent, SmlStatusReplyCb callback, void *userdata, SmlError **error)
Sends a command with a parent.
Definition: sml_session.c:2422
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