libsyncml 0.5.4

- Main Page
- Modules
- Data Structures
- Files

- File List

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 */
344 smlDsServerSetConnectCallback(
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