libsyncml  0.5.4
data_sync_client.c
1 /*
2  * libsyncml - A syncml protocol implementation
3  * Copyright (C) 2008-2009 Michael Bell <michael.bell@opensync.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  */
20 
21 #include "data_sync_server.h"
22 #include "data_sync_common.h"
23 #include "libsyncml/sml_error_internals.h"
24 #include "data_sync_callbacks.h"
25 #include "libsyncml/objects/sml_ds_server.h"
26 #include "data_sync_devinf.h"
27 #include "libsyncml/sml_support.h"
28 
29 static SmlBool smlDataSyncClientAlertCallback(
30  SmlDsSession *dsession,
31  SmlAlertType type,
32  const char *last,
33  const char *next,
34  void *userdata)
35 {
36  smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %s, %p)", __func__, dsession, type, VA_STRING(last), VA_STRING(next), userdata);
37 
38  SmlDataSyncDatastore *datastore = userdata;
39  SmlDataSyncObject *dsObject = datastore->dsObject;
40  SmlBool ret = TRUE;
41  SmlError *error = NULL;
42 
43  /* libsyncml only supports SML_ALERT_TWO_WAY and SML_ALERT_SLOW_SYNC
44  * but some old phones reply on a SAN alert 206 with a slow sync 201
45  * alert or a SAN alert 206 (insteed of a normal two way alert 200).
46  * Therefore it is necessary to check for TWO_WAY and TWO_WAY_BY_SERVER.
47  */
48 
49  if (type != SML_ALERT_TWO_WAY &&
50  type != SML_ALERT_SLOW_SYNC &&
51  type != SML_ALERT_TWO_WAY_BY_SERVER)
52  {
53  smlErrorSet(&error, SML_ERROR_NOT_IMPLEMENTED, "Unsupported alert type %d.", type);
54  goto error;
55  }
56 
57  char *remote_key = g_strdup_printf("remoteanchor%s", smlDsSessionGetLocation(dsession));
58  datastore->remoteNext = g_strdup(next);
59 
60  /* We return FALSE if we need a special return code as answer:
61  * SML_ERROR_REQUIRE_REFRESH 508
62  * This return code enforces a SLOW-SYNC.
63  */
64  if (type == SML_ALERT_TWO_WAY || type == SML_ALERT_TWO_WAY_BY_SERVER)
65  {
66  if (!last)
67  {
68  smlTrace(TRACE_INTERNAL, "%s: TWO-WAY-SYNC is requested but there is no LAST anchor.", __func__);
69  type = SML_ALERT_SLOW_SYNC;
70  ret = FALSE;
71  } else {
72  char *cached = NULL;
73  if (dsObject->getAnchorCallback)
74  cached = dsObject->getAnchorCallback(
75  dsObject,
76  remote_key,
77  dsObject->getAnchorUserdata,
78  &error);
79  if (!cached && error)
80  goto error;
81  if (!cached || strcmp(cached, last))
82  {
83  smlTrace(TRACE_INTERNAL,
84  "%s: TWO-WAY-SYNC is requested but the cached LAST anchor (%s) does not match the presented LAST anchor from the remote peer (%s).",
85  __func__, last, cached);
86  if (cached)
87  smlSafeCFree(&cached);
88  type = SML_ALERT_SLOW_SYNC;
89  ret = FALSE;
90  }
91  }
92  }
93 
94  if (dsObject->getAlertTypeCallback)
95  {
96  SmlAlertType h = dsObject->getAlertTypeCallback(
97  dsObject,
98  datastore->sourceUri,
99  type,
100  dsObject->getAlertTypeUserdata,
101  &error);
102  if (h == SML_ALERT_UNKNOWN || error)
103  goto error;
104  /* There is only one situation where the callback can return
105  * something different than type. If the client requested an
106  * alert type 200, the server requested an alert type 200 too
107  * and after this there is a problem detected on the client
108  * then the returned type can be 201 SLOW-SYNC.
109  *
110  * If this happens then a new alert 201 and a status 508 must
111  * be send.
112  */
113  if (h != type) {
114  if (h != SML_ALERT_SLOW_SYNC) {
115  smlErrorSet(&error, SML_ERROR_GENERIC,
116  "It is not possible to change the alert type after an OMA DS client received the alerts from the OMA DS server.");
117  goto error;
118  } else {
119  /* send alert 201 */
120  SmlLocation *source = NULL;
121  source = smlLocationNew(datastore->sourceUri, NULL, &error);
122  if (!source)
123  goto error;
124  SmlCommand *alert = NULL;
125  alert = smlCommandNewAlert(
126  SML_ALERT_SLOW_SYNC,
127  smlDsSessionGetTarget(datastore->session),
128  source,
129  NULL, datastore->localNext,
130  NULL, &error);
131  if (!alert) {
132  smlLocationUnref(source);
133  goto error;
134  }
135  smlLocationUnref(source);
136  if (!smlSessionSendCommand(dsObject->session, alert, NULL, NULL, NULL, &error)) {
137  smlCommandUnref(alert);
138  goto error;
139  }
140  smlCommandUnref(alert);
141 
142  /* send status 508 */
143  ret = FALSE;
144  }
145  }
146  }
147 
148  smlSafeCFree(&remote_key);
149 
150  smlTrace(TRACE_EXIT, "%s: %i", __func__, ret);
151  return ret;
152 error:
153  smlErrorRef(&error);
154  smlDataSyncSendEvent(
155  dsObject, SML_DATA_SYNC_EVENT_ERROR,
156  dsObject->eventUserdata, error);
157  smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
158  smlErrorDeref(&error);
159  return FALSE;
160 }
161 
162 SmlBool smlDataSyncClientSendAlert(
163  SmlDataSyncDatastore *datastore,
164  SmlAlertType type,
165  SmlError **error)
166 {
167  smlTrace(TRACE_ENTRY, "%s(%p, %d, %p)", __func__, datastore, type, error);
168  CHECK_ERROR_REF
169  SmlDataSyncObject *dsObject = datastore->dsObject;
170 
171  /* initialize the last anchor */
172  SmlAlertType alertType = type;
173  char *local_last = NULL; // perhaps NULL is better
174  if (dsObject->getAlertTypeCallback)
175  {
176  alertType = dsObject->getAlertTypeCallback(
177  dsObject,
178  datastore->sourceUri,
179  type,
180  dsObject->getAlertTypeUserdata,
181  error);
182  if (*error)
183  goto error;
184  }
185  if (alertType == SML_ALERT_UNKNOWN)
186  {
187  smlTrace(TRACE_INTERNAL, "%s: no alert type => slow-sync", __func__);
188  alertType = SML_ALERT_SLOW_SYNC;
189  }
190  if (alertType != SML_ALERT_SLOW_SYNC)
191  {
192  /* this must be a two-way-sync */
193  alertType = SML_ALERT_TWO_WAY;
194  char *local_key = g_strdup_printf("localanchor%s", smlDsServerGetLocation(datastore->server));
195  if (dsObject->getAnchorCallback)
196  local_last = dsObject->getAnchorCallback(
197  dsObject,
198  local_key,
199  dsObject->getAnchorUserdata,
200  error);
201  smlSafeCFree(&local_key);
202  }
203 
204  /* initialize the next anchor */
205  if (datastore->localNext)
206  smlSafeCFree(&(datastore->localNext));
207  datastore->localNext = smlDataSyncGetNextAnchor(datastore, local_last, error);
208 
209  /* The session is required here. So if the session is missing
210  * then the function has to block here. This is no real problem
211  * because the SESSION_EVENT_NEW must already be in the queue of
212  * the manager object.
213  */
214  while (!dsObject->session) {
215  smlManagerDispatch(dsObject->manager);
216  }
217 
218  /* The OMA DS client starts the synchronization so there should be no
219  * DsSession (datastore session) present.
220  */
221  datastore->session = smlDsServerSendAlert(
222  datastore->server,
223  dsObject->session,
224  alertType,
225  local_last, datastore->localNext,
226  smlDataSyncAlertStatusCallback, datastore,
227  error);
228  if (local_last)
229  smlSafeCFree(&local_last);
230  if (!datastore->session)
231  goto error;
232 
233  /* Only the alert callback is registered here because the changes should
234  * be managed after the function ds_client_get_changeinfo was called.
235  */
236  smlDsSessionGetAlert(datastore->session, smlDataSyncClientAlertCallback, datastore);
237  if (dsObject->changeCallback) {
238  smlDsSessionGetSync(datastore->session,
239  smlDataSyncSyncCallback, datastore);
240  smlDsSessionGetChanges(datastore->session,
241  smlDataSyncChangeCallback, datastore);
242  }
243 
244  /* If a slow-sync alert is initiated and the remote peer
245  * did not request it then the function must return false
246  * to trigger a status 508 (REFRESH_REQUIRED).
247  * If the requested alert type is not known then there
248  * is no need for a status and the function returns true.
249  * This can happen if the remote peer did not trigger
250  * the client via a SAN (e.g. OMA DS client over HTTP).
251  */
252  SmlBool ret = TRUE;
253  if (alertType == SML_ALERT_SLOW_SYNC &&
254  alertType != type &&
255  type != SML_ALERT_UNKNOWN) {
256  ret = FALSE;
257  }
258 
259  smlTrace(TRACE_EXIT, "%s: %i", __func__, ret);
260  return ret;
261 error:
262  smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
263  return FALSE;
264 }
265 
266 static SmlErrorType smlDataSyncSanCallback(
267  SmlDsServer *server,
268  SmlSession *session,
269  SmlAlertType type,
270  void *userdata)
271 {
272  smlTrace(TRACE_ENTRY, "%s(%p, %p, %d, %p)", __func__, server, session, type, userdata);
273  SmlDataSyncDatastore *datastore = userdata;
274  SmlDataSyncObject *dsObject = datastore->dsObject;
275  SmlError *error = NULL;
276  SmlErrorType ret = SML_NO_ERROR;
277 
278  /* soemtimes the event manager thread is too slow */
279  if (!dsObject->session) {
280  dsObject->session = session;
281  smlSessionRef(session);
282  }
283 
284  if (!smlDataSyncClientSendAlert(datastore, type, &error)) {
285  if (!error)
286  ret = SML_ERROR_REQUIRE_REFRESH;
287  else
288  goto error;
289  }
290 
291  smlTrace(TRACE_EXIT, "%s: %i", __func__, ret);
292  return ret;
293 error:
294  smlErrorRef(&error);
295  smlDataSyncSendEvent(
296  dsObject, SML_DATA_SYNC_EVENT_ERROR,
297  dsObject->eventUserdata, error);
298  smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(&error));
299  smlErrorDeref(&error);
300  return SML_ERROR_GENERIC;
301 }
302 
303 SmlBool smlDataSyncClientInit(SmlDataSyncObject *dsObject, SmlError **error)
304 {
305  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, dsObject, error);
306  CHECK_ERROR_REF
307  /* The manager responsible for handling the other objects */
308  dsObject->manager = smlManagerNew(dsObject->tsp, error);
309  if (!dsObject->manager)
310  goto error;
311  smlManagerSetEventCallback(dsObject->manager, smlDataSyncEventCallback, dsObject);
312  smlManagerSetLocalMaxMsgSize(dsObject->manager, dsObject->maxMsgSize);
313  smlManagerSetLocalMaxObjSize(dsObject->manager, dsObject->maxObjSize);
314 
315  /* set server specific callbacks */
316  dsObject->funcDatastoreAlert = smlDataSyncClientAlertCallback;
317 
318  /* prepare device info */
319  if (!smlDataSyncDevInfInit(dsObject, SML_DEVINF_DEVTYPE_WORKSTATION, error))
320  goto error;
321 
322  /* prepare datastore server */
323  GList *o = dsObject->datastores;
324  for (; o; o = o->next) {
325  SmlDataSyncDatastore *datastore = o->data;
326  smlTrace(TRACE_INTERNAL, "preparing DsServer (datastore) %s", datastore->sourceUri);
327 
328  /* We now create the ds server at the given location. */
329  SmlLocation *loc = smlLocationNew(datastore->sourceUri, NULL, error);
330  if (!loc)
331  goto error;
332 
333  datastore->server = smlDsClientNew(datastore->contentType, loc, loc, error);
334  smlLocationUnref(loc);
335  if (!datastore->server)
336  goto error;
337 
338  if (!smlDsServerRegister(datastore->server, dsObject->manager, error))
339  goto error;
340 
341  /* this is a client and not a server
342  * but the callback initializes only database->session (DsSession)
343  */
345  datastore->server,
346  smlDataSyncDatastoreConnectCallback,
347  datastore);
348 
349  smlDsServerSetSanSessionCallback(
350  datastore->server,
351  smlDataSyncSanCallback,
352  datastore);
353 
354  /* And we also add the devinfo to the devinf agent */
355  if (!smlDataSyncDevInfAddDatastore(dsObject->localDevInf, datastore, error))
356  goto error;
357  }
358 
359  /* Run the manager */
360  if (!smlManagerStart(dsObject->manager, error))
361  goto error;
362 
363  /* Initialize the Transport */
364  if (!smlTransportInitialize(dsObject->tsp, error))
365  goto error;
366 
367  smlTrace(TRACE_EXIT, "%s - TRUE", __func__);
368  return TRUE;
369 
370 error:
371  smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
372  return FALSE;
373 }
374 
void smlDsSessionGetChanges(SmlDsSession *dsession, SmlDsSessionChangesCb chgCallback, void *userdata)
Gets a already received sync command.
This object represents an OMA DS datastore.
Definition: data_sync.h:93
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
This is the central synchronization object.
Definition: data_sync.h:110
void smlDsSessionGetAlert(SmlDsSession *dsession, SmlDsSessionAlertCb callback, void *userdata)
Gets a already received alert.
void smlDsServerSetConnectCallback(SmlDsServer *server, SmlDsSessionConnectCb callback, void *userdata)
Registers a callback that will get called once a client connects.
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
SmlBool smlTransportInitialize(SmlTransport *tsp, SmlError **error)
Initializes the transport with the given config.
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
Represent an error.