libsyncml  0.5.4
sml_ds_server.c
1 /*
2  * libsyncml - A syncml protocol implementation
3  * Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
4  * Copyright (C) 2007-2009 Michael Bell <michael.bell@opensync.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include <libsyncml/syncml.h>
23 #include "sml_ds_server.h"
24 
25 #include <libsyncml/syncml_internals.h>
26 #include "sml_ds_server_internals.h"
27 #include <libsyncml/sml_error_internals.h>
28 #include <libsyncml/sml_manager_internals.h>
29 #include <libsyncml/sml_session_internals.h>
30 #include <libsyncml/sml_command_internals.h>
31 #include <libsyncml/sml_elements_internals.h>
32 
33 static SmlWriteContext *_write_context_find(SmlDsSession *dsession, const char *uid, SmlChangeType type)
34 {
35  smlTrace(TRACE_ENTRY, "%s(%p, %s, %i)", __func__, dsession, VA_STRING(uid), type);
36 
37  g_mutex_lock(dsession->pendingMapsLock);
38  GList *c = NULL;
39  for (c = dsession->pendingMaps; c; c = c->next) {
40  SmlWriteContext *ctx = c->data;
41  if (!strcmp(uid, ctx->uid) && ctx->type == type) {
42  g_mutex_unlock(dsession->pendingMapsLock);
43  smlTrace(TRACE_EXIT, "%s: %p", __func__, ctx);
44  return ctx;
45  }
46  }
47  g_mutex_unlock(dsession->pendingMapsLock);
48 
49  smlTrace(TRACE_EXIT_ERROR, "%s: Not found", __func__);
50  return NULL;
51 }
52 
53 static void _write_context_free(SmlWriteContext *ctx)
54 {
55  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, ctx);
56 
57  if (ctx->status)
58  smlStatusUnref(ctx->status);
59 
60  if (ctx->uid)
61  smlSafeCFree(&(ctx->uid));
62 
63  if (ctx->newuid)
64  smlSafeCFree(&(ctx->newuid));
65 
66  smlSafeFree((gpointer *)&ctx);
67 
68  smlTrace(TRACE_EXIT, "%s", __func__);
69 }
70 
71 static void _write_context_dispatch(SmlDsSession *dsession, SmlWriteContext *ctx)
72 {
73  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, dsession, ctx);
74 
75  if (!ctx->status) {
76  smlTrace(TRACE_EXIT, "%s: No status yet", __func__);
77  return;
78  }
79 
80  smlTrace(TRACE_INTERNAL, "%s: Dispatching: uid %s, Type %i, newuid %s, result %i", __func__, VA_STRING(ctx->uid), ctx->type, VA_STRING(ctx->newuid), smlStatusGetCode(ctx->status));
81 
82  if (ctx->type == SML_CHANGE_ADD &&
83  smlStatusGetClass(ctx->status) == SML_ERRORCLASS_SUCCESS &&
84  !dsession->recvMappingCallback &&
85  !ctx->newuid &&
86  dsession->server->servertype == SML_DS_SERVER)
87  {
88  smlTrace(TRACE_EXIT, "%s: No mapitem yet", __func__);
89  return;
90  }
91 
92  ctx->callback(dsession, ctx->status, ctx->newuid, ctx->userdata);
93 
94  g_mutex_lock(dsession->pendingMapsLock);
95  dsession->pendingMaps = g_list_remove(dsession->pendingMaps, ctx);
96  g_mutex_unlock(dsession->pendingMapsLock);
97 
98  if (ctx->type == SML_CHANGE_ADD &&
99  smlStatusGetClass(ctx->status) == SML_ERRORCLASS_SUCCESS &&
100  dsession->recvEventCallback &&
101  !dsession->pendingMaps)
102  {
103  /* FIXME: I'm sure that this event is buggy.
104  * FIXME: What is the semantic of this event?
105  * Now there are no more MapItems ...
106  * do some fancy callback. Some applications will love it ;)
107  */
108  dsession->recvEventCallback(dsession, SML_DS_EVENT_COMMITEDCHANGES, dsession->recvEventCallbackUserdata);
109  smlTrace(TRACE_INTERNAL, "%s: recvEventCallback commited changes callback", __func__);
110  }
111 
112  _write_context_free(ctx);
113 
114  smlTrace(TRACE_EXIT, "%s", __func__);
115 }
116 
117 static void _alert_reply(SmlSession *session, SmlStatus *status, void *userdata)
118 {
119  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
120  SmlDsSession *dsession = userdata;
121 
122  if (dsession->sentAlertCallback)
123  dsession->sentAlertCallback(session, status, dsession->sentAlertCallbackUserdata);
124 
125  dsession->sentAlertCallback = NULL;
126  dsession->sentAlertCallbackUserdata = NULL;
127 
128  smlTrace(TRACE_EXIT, "%s", __func__);
129 }
130 
131 static void _sync_reply(SmlSession *session, SmlStatus *status, void *userdata)
132 {
133  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
134  SmlDsSession *dsession = userdata;
135 
136  if (dsession->sentSyncCallback)
137  dsession->sentSyncCallback(session, status, dsession->sentSyncCallbackUserdata);
138 
139  dsession->sentSyncCallback = NULL;
140  dsession->sentSyncCallbackUserdata = NULL;
141 
142  // Now there are no more MapItems ... do some fancy callback. Some applications will love it ;)
143  if (dsession->recvEventCallback && !dsession->pendingMaps) {
144  dsession->recvEventCallback(dsession, SML_DS_EVENT_COMMITEDCHANGES, dsession->recvEventCallbackUserdata);
145  smlTrace(TRACE_INTERNAL, "%s: recvEventCallback commited changes callback", __func__);
146  }
147 
148  smlTrace(TRACE_EXIT, "%s", __func__);
149 }
150 
151 static void _change_reply(SmlSession *session, SmlStatus *status, void *userdata)
152 {
153  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
154  smlAssert(session);
155  smlAssert(status);
156  SmlWriteContext *ctx = userdata;
157  SmlDsSession *dsession = ctx->session;
158 
159  /* Please see smlChangeAssemble in sml_xml_parser.c for
160  * a more detailed description on the checked behaviour.
161  * If the received command is an Add or the remote peer
162  * is an OMA DS client then the remote peer must send a
163  * Source. Otherwise a Target must be sent by the remote
164  * peer. We only check the required behaviour and not
165  * the optional one.
166  */
167  if (status->type == SML_COMMAND_TYPE_ADD ||
168  session->sessionType == SML_SESSION_TYPE_CLIENT) {
169  /* FIXME: How do we handle remote peers which are clients
170  * FIXME: and only support replace? The assembler will
171  * FIXME: send a replace command with a target instead of
172  * FIXME: an add command with a source.
173  */
174  if (!status->sourceRef) {
175  smlTrace(TRACE_EXIT_ERROR, "%s: Received add status or change reply from an OMA DS server without sourceRef", __func__);
176  return;
177  }
178  } else {
179  if (!status->targetRef) {
180  smlTrace(TRACE_EXIT_ERROR, "%s: Received delete or modify status or change reply from an OMA DS client without targetRef", __func__);
181  return;
182  }
183  }
184 
185  ctx->status = status;
186  smlStatusRef(status);
187  _write_context_dispatch(dsession, ctx);
188 
189  smlTrace(TRACE_EXIT, "%s", __func__);
190 }
191 
192 SmlDsServer *smlDsServerNew(const char *type, SmlLocation *location, SmlError **error)
193 {
194  smlTrace(TRACE_ENTRY, "%s(%s, %p, %p)", __func__, VA_STRING(type), location, error);
195  smlAssert(location);
196  CHECK_ERROR_REF
197 
198  SmlDsServer *server = smlTryMalloc0(sizeof(SmlDsServer), error);
199  if (!server)
200  goto error;
201 
202  server->location = location;
203  smlLocationRef(location);
204 
205  server->contenttype = g_strdup(type);
206  server->servertype = SML_DS_SERVER;
207 
208  smlTrace(TRACE_EXIT, "%s: %p", __func__, server);
209  return server;
210 
211 error:
212  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
213  return NULL;
214 }
215 
216 SmlDsServer *smlDsClientNew(const char *type, SmlLocation *location, SmlLocation *target, SmlError **error)
217 {
218  smlTrace(TRACE_ENTRY, "%s(%s, %p, %p, %p)", __func__, VA_STRING(type), location, target, error);
219  smlAssert(location);
220  smlAssert(target);
221  CHECK_ERROR_REF
222 
223  SmlDsServer *server = smlTryMalloc0(sizeof(SmlDsServer), error);
224  if (!server)
225  goto error;
226 
227  server->location = location;
228  smlLocationRef(location);
229 
230  server->target = target;
231  smlLocationRef(target);
232 
233  server->contenttype = g_strdup(type);
234  server->servertype = SML_DS_CLIENT;
235 
236  smlTrace(TRACE_EXIT, "%s: %p", __func__, server);
237  return server;
238 
239 error:
240  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
241  return NULL;
242 }
243 
244 SmlDsServerType smlDsServerGetServerType(SmlDsServer *server)
245 {
246  smlAssert(server);
247  return server->servertype;
248 }
249 
250 void smlDsServerFree(SmlDsServer *server)
251 {
252  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, server);
253  smlAssert(server);
254 
255  if (server->location)
256  smlLocationUnref(server->location);
257 
258  if (server->target)
259  smlLocationUnref(server->target);
260 
261  if (server->contenttype)
262  smlSafeCFree(&(server->contenttype));
263 
264  smlSafeFree((gpointer *)&server);
265 
266  smlTrace(TRACE_EXIT, "%s", __func__);
267 }
268 
279 void smlDsServerSetConnectCallback(SmlDsServer *server, SmlDsSessionConnectCb callback, void *userdata)
280 {
281  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, callback, userdata);
282  smlAssert(server);
283 
284  server->connectCallback = callback;
285  server->connectCallbackUserdata = userdata;
286 
287  smlTrace(TRACE_EXIT, "%s", __func__);
288 }
289 
290 void smlDsServerSetSanCallback(SmlDsServer *server, SmlDsServerSanCb callback, void *userdata)
291 {
292  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, callback, userdata);
293  smlAssert(server);
294  smlAssert(server->servertype == SML_DS_CLIENT);
295 
296  server->sanCallback = callback;
297  server->sanCallbackUserdata = userdata;
298 
299  smlTrace(TRACE_EXIT, "%s", __func__);
300 }
301 
302 void smlDsServerSetSanSessionCallback(SmlDsServer *server, SmlDsServerSanSessionCb callback, void *userdata)
303 {
304  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, callback, userdata);
305  smlAssert(server);
306  smlAssert(server->servertype == SML_DS_CLIENT);
307 
308  server->sanSessionCallback = callback;
309  server->sanSessionCallbackUserdata = userdata;
310 
311  smlTrace(TRACE_EXIT, "%s", __func__);
312 }
313 
314 const char *smlDsServerGetLocation(SmlDsServer *server)
315 {
316  smlAssert(server);
317  if (server->location)
318  return server->location->locURI;
319  return NULL;
320 }
321 
322 const char *smlDsServerGetContentType(SmlDsServer *server)
323 {
324  smlAssert(server);
325  return server->contenttype;
326 }
327 
328 SmlBool smlDsServerAddSan(SmlDsServer *server, SmlNotification *san, SmlError **error)
329 {
330  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, san, error);
331  smlAssert(server);
332  smlAssert(san);
333  CHECK_ERROR_REF
334 
335  if (!smlNotificationNewAlert(san, SML_ALERT_TWO_WAY_BY_SERVER, server->contenttype, smlLocationGetURI(server->location), error))
336  goto error;
337 
338  smlTrace(TRACE_EXIT, "%s", __func__);
339  return TRUE;
340 
341 error:
342  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
343  return FALSE;
344 }
345 
346 SmlDsSession *smlDsServerRecvAlert(SmlDsServer *server, SmlSession *session, SmlCommand *cmd)
347 {
348  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, session, cmd);
349  SmlError *error = NULL;
350 
351  SmlDsSession *dsession = smlDsSessionNew(server, session, &error);
352  if (!dsession)
353  goto error;
354 
355  smlDsSessionRecvAlert(session, cmd, dsession);
356 
357  if (server->connectCallback)
358  server->connectCallback(dsession, server->connectCallbackUserdata);
359 
360  smlDsSessionUnref(dsession);
361 
362  smlTrace(TRACE_EXIT, "%s", __func__);
363  return dsession;
364 
365 error:
366  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
367  smlErrorDeref(&error);
368  return NULL;
369 }
370 
371 SmlDsSession *smlDsServerSendAlert(SmlDsServer *server, SmlSession *session, SmlAlertType type, const char *last, const char *next, SmlStatusReplyCb callback, void *userdata, SmlError **error)
372 {
373  smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %s, %s, %p)", __func__, server, session, type, VA_STRING(last), VA_STRING(next), error);
374  smlAssert(server);
375  smlAssert(session);
376  CHECK_ERROR_REF
377 
378  SmlDsSession *dsession = smlDsSessionNew(server, session, error);
379  if (!dsession)
380  goto error;
381 
382  if (server->manager) {
383  if (!smlManagerObjectRegister(server->manager, SML_COMMAND_TYPE_SYNC, session, server->location, NULL, NULL, smlDsSessionRecvSync, smlDsSessionRecvChange, dsession, error))
384  goto error_free_dsession;
385 
386  if (!smlManagerObjectRegister(server->manager, SML_COMMAND_TYPE_MAP, session, server->location, NULL, NULL, smlDsSessionRecvMap, NULL, dsession, error))
387  goto error_free_dsession;
388 
389  if (!smlManagerObjectRegister(server->manager, SML_COMMAND_TYPE_ALERT, session, server->location, NULL, NULL, smlDsSessionRecvAlert, NULL, dsession, error))
390  goto error_free_dsession;
391  }
392 
393  if (!smlDsSessionSendAlert(dsession, type, last, next, callback, userdata, error))
394  goto error_free_dsession;
395 
396  smlTrace(TRACE_EXIT, "%s", __func__);
397  return dsession;
398 
399 error_free_dsession:
400  smlSafeFree((gpointer *)&dsession);
401 error:
402  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
403  return NULL;
404 }
405 
406 SmlDsSession *smlDsSessionNew(SmlDsServer *server, SmlSession *session, SmlError **error)
407 {
408  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, session, error);
409  smlAssert(server);
410  CHECK_ERROR_REF
411 
412  SmlDsSession *dsession = smlTryMalloc0(sizeof(SmlDsSession), error);
413  if (!dsession)
414  goto error;
415 
416  dsession->server = server;
417  dsession->session = session;
418  dsession->write_lock = g_mutex_new();
419  dsession->lock = g_mutex_new();
420  dsession->syncReply = SML_ERROR_UNKNOWN;
421  dsession->refCount = 1;
422 
423  if (server->servertype == SML_DS_CLIENT) {
424  dsession->target = server->target;
425  smlLocationRef(dsession->target);
426  }
427 
428  dsession->location = server->location;
429  smlLocationRef(dsession->location);
430 
431  dsession->pendingMapsLock = g_mutex_new();
432 
433  smlTrace(TRACE_EXIT, "%s: %p", __func__, dsession);
434  return dsession;
435 
436 error:
437  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
438  return NULL;
439 }
440 
441 SmlDsSession *smlDsSessionRef(SmlDsSession *dsession)
442 {
443  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, dsession);
444  smlAssert(dsession);
445 
446  g_atomic_int_inc(&(dsession->refCount));
447 
448  smlTrace(TRACE_EXIT, "%s: New refcount: %i", __func__, dsession->refCount);
449  return dsession;
450 }
451 
452 void smlDsSessionUnref(SmlDsSession *dsession)
453 {
454  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, dsession);
455  smlAssert(dsession);
456 
457  if (g_atomic_int_dec_and_test(&(dsession->refCount))) {
458  smlTrace(TRACE_INTERNAL, "%s: Refcount == 0!", __func__);
459 
460  if (dsession->target)
461  smlLocationUnref(dsession->target);
462 
463  if (dsession->location)
464  smlLocationUnref(dsession->location);
465 
466  if (dsession->alertCommand)
467  smlCommandUnref(dsession->alertCommand);
468 
469  while (dsession->recvSync) {
470  SmlCommand *cmd = dsession->recvSync->data;
471  smlCommandUnref(cmd);
472  dsession->recvSync = g_list_delete_link(dsession->recvSync, dsession->recvSync);
473  }
474 
475  while (dsession->recvChanges) {
476  SmlCommand *cmd = dsession->recvChanges->data;
477  smlCommandUnref(cmd);
478  dsession->recvChanges = g_list_delete_link(dsession->recvChanges, dsession->recvChanges);
479  }
480 
481  if (dsession->syncCommand)
482  smlCommandUnref(dsession->syncCommand);
483 
484  if (!g_mutex_trylock(dsession->pendingMapsLock)) {
485  smlTrace(TRACE_ERROR,
486  "%s: somebody still uses this object", __func__);
487  g_mutex_lock(dsession->pendingMapsLock);
488  }
489  while (dsession->pendingMaps) {
490  SmlWriteContext *ctx = dsession->pendingMaps->data;
491  _write_context_free(ctx);
492  dsession->pendingMaps = g_list_delete_link(dsession->pendingMaps, dsession->pendingMaps);
493  }
494  g_mutex_unlock(dsession->pendingMapsLock);
495  g_mutex_free(dsession->pendingMapsLock);
496 
497  while (dsession->mapItems) {
498  SmlMapItem *item = dsession->mapItems->data;
499  smlMapItemUnref(item);
500  dsession->mapItems = g_list_delete_link(dsession->mapItems, dsession->mapItems);
501  }
502 
503  if (dsession->lock)
504  g_mutex_free(dsession->lock);
505  if (dsession->write_lock)
506  g_mutex_free(dsession->write_lock);
507 
508  smlSafeFree((gpointer *)&dsession);
509  }
510 
511  smlTrace(TRACE_EXIT, "%s", __func__);
512 }
513 
514 void smlDsSessionDispatch(SmlDsSession *dsession)
515 {
516  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, dsession);
517  SmlError *error = NULL;
518  SmlStatus *reply = NULL;
519 
520  /* A dispatch function should never block on a
521  * locked resource. This is like a busy wait
522  * because the thread is not available for this
523  * time which can block the whole system if two
524  * dispatchers use the same thread.
525  */
526  if (!g_mutex_trylock(dsession->lock))
527  {
528  smlTrace(TRACE_EXIT, "%s - DsSession is already locked", __func__);
529  return;
530  }
531  smlTrace(TRACE_INTERNAL, "%s - locked DsSession successfully", __func__);
532 
533  if (dsession->alertCommand && dsession->recvAlertCallback) {
534  smlTrace(TRACE_INTERNAL, "%s: Dispatching alert", __func__);
535 
536  SmlErrorType type = SML_NO_ERROR;
537  if (!dsession->recvAlertCallback(dsession, dsession->alertCommand->private.alert.type, dsession->alertCommand->private.alert.anchor->last, dsession->alertCommand->private.alert.anchor->next, dsession->recvAlertCallbackUserdata))
538  type = SML_ERROR_REQUIRE_REFRESH;
539  dsession->recvAlertCallback = NULL;
540 
541  reply = smlCommandNewReply(dsession->alertCommand, type, &error);
542  if (!reply)
543  goto error;
544 
545  if (!smlSessionSendReply(dsession->session, reply, &error))
546  goto error;
547 
548 
549  smlStatusUnref(reply);
550 
551  smlCommandUnref(dsession->alertCommand);
552  dsession->alertCommand = NULL;
553 
554  /* unlock the final event at the manager
555  *
556  * If this is called from a test case then the datastore
557  * is sometimes not registered at a SmlManager.
558  */
559  if (dsession->server->manager && dsession->finalLock)
560  {
561  dsession->finalLock = FALSE;
562  smlManagerSessionFinalLockUnref(dsession->server->manager, dsession->session);
563  }
564 
565  } else if (dsession->recvSync && dsession->recvSyncCallback) {
566  smlTrace(TRACE_INTERNAL, "%s: Dispatching sync", __func__);
567 
568  dsession->recvSyncCallback(dsession, ((SmlCommand *)(dsession->recvSync->data))->private.sync.numChanged, dsession->recvSyncCallbackUserdata);
569  dsession->recvSyncCallback = NULL;
570 
571  while (dsession->recvSync) {
572  SmlCommand *cmd = dsession->recvSync->data;
573 
574  smlTrace(TRACE_INTERNAL, "%s: answering sync command with cmdRef %i and msgRef %i", __func__, cmd->cmdID, cmd->msgID);
575  reply = smlCommandNewReply(cmd, SML_NO_ERROR, &error);
576  if (!reply)
577  goto error;
578 
579  if (!smlSessionSendReply(dsession->session, reply, &error))
580  goto error;
581 
582  smlStatusUnref(reply);
583 
584  smlCommandUnref(cmd);
585  dsession->recvSync = g_list_delete_link(dsession->recvSync, dsession->recvSync);
586  }
587 
588  dsession->syncReply = SML_NO_ERROR;
589 
590  if (!dsession->recvChanges) {
591  /* There are no changes. */
592 
593  /* ... do some fancy callback. Some applications will love it ;) */
594  if (dsession->recvEventCallback) {
595  dsession->recvEventCallback(dsession, SML_DS_EVENT_GOTCHANGES, dsession->recvEventCallbackUserdata);
596  smlTrace(TRACE_INTERNAL, "%s: recvEventCallback no changes in recvSync callback", __func__);
597  }
598  /* unlock the final event at the manager
599  *
600  * If this is called from a test case then the datastore
601  * is sometimes not registered at a SmlManager.
602  */
603  if (dsession->server->manager && dsession->finalLock)
604  {
605  dsession->finalLock = FALSE;
606  smlManagerSessionFinalLockUnref(dsession->server->manager, dsession->session);
607  }
608  smlTrace(TRACE_INTERNAL, "%s: final handling done", __func__);
609  }
610  } else if (dsession->recvChanges && dsession->changesCallback) {
611  smlTrace(TRACE_INTERNAL, "%s: Dispatching changes", __func__);
612 
613  while (dsession->recvChanges) {
614 
615  SmlCommand *cmd = dsession->recvChanges->data;
616 
617  if (cmd) {
618  if (!cmd->private.change.items ||
619  !g_list_length(cmd->private.change.items))
620  {
621  smlErrorSet(&error, SML_ERROR_GENERIC, "No items found in command.");
622  goto error;
623  }
624 
625  /* All items handled at once because libsyncml
626  * only supports one status code for all items.
627  * So one failed item means that the complete
628  * command is aborted.
629  */
630 
631  guint i;
632  for (i=0; i < g_list_length(cmd->private.change.items); i++)
633  {
634  SmlItem *item = g_list_nth_data(cmd->private.change.items, i);
635  if (!item)
636  {
637  smlErrorSet(&error, SML_ERROR_GENERIC,
638  "Item %i of the command's item list is NULL.", i);
639  goto error;
640  }
641  /* We'd rather use the target of the change since it already the mapped
642  * uid (if we are a client for example). If it is not given we use the
643  * source uri. This has then to be translated by the sync engine of course */
644  if ((!item->source && !item->target)) {
645  smlErrorSet(&error, SML_ERROR_GENERIC,
646  "Cannot determine UID because source an target are missing on item %i.", i);
647  goto error;
648  }
649 
650  char *data = NULL;
651  unsigned int size = 0;
652  if (!smlItemStealData(item, &data, &size, &error))
653  goto error;
654 
655  if (!dsession->changesCallback(dsession, cmd->private.change.type, item->target ? item->target->locURI : item->source->locURI, data, size, item->contenttype, dsession->changesCallbackUserdata, &error))
656  goto error;
657  }
658 
659  if (cmd->private.change.type == SML_CHANGE_ADD)
660  reply = smlCommandNewReply(cmd, SML_ITEM_ADDED, &error);
661  else
662  reply = smlCommandNewReply(cmd, SML_NO_ERROR, &error);
663 
664  if (!reply)
665  goto error;
666 
667  if (!smlSessionSendReply(dsession->session, reply, &error))
668  goto error;
669 
670  smlStatusUnref(reply);
671 
672  smlCommandUnref(cmd);
673  }
674 
675  dsession->recvChanges = g_list_delete_link(dsession->recvChanges, dsession->recvChanges);
676  }
677 
678  // Now there are no more changes... do some fancy callback. Some applications will love it ;)
679  if (dsession->recvEventCallback) {
680  dsession->recvEventCallback(dsession, SML_DS_EVENT_GOTCHANGES, dsession->recvEventCallbackUserdata);
681  smlTrace(TRACE_INTERNAL, "%s: recvEventCallback all changes sent in recvChanges callback", __func__);
682  }
683  /* unlock the final event at the manager
684  *
685  * If this is called from a test case then the datastore
686  * is sometimes not registered at a SmlManager.
687  */
688  if (dsession->server->manager)
689  {
690  dsession->finalLock = FALSE;
691  smlManagerSessionFinalLockUnref(dsession->server->manager, dsession->session);
692  }
693  else
694  smlTrace(TRACE_INTERNAL, "%s - no manager so should be a test", __func__);
695  } else {
696 
697  smlTrace(TRACE_INTERNAL, "%s: recvChanges: %p changesCallback: %p", __func__, dsession->recvChanges, dsession->changesCallback);
698  }
699 
700  smlTrace(TRACE_EXIT, "%s()", __func__);
701  g_mutex_unlock(dsession->lock);
702 
703  return;
704 
705 error:
706  if (reply)
707  smlStatusUnref(reply);
708  g_mutex_unlock(dsession->lock);
709  smlTrace(TRACE_EXIT_ERROR, "%s: Unable to dispatch: %s", __func__, smlErrorPrint(&error));
710  smlErrorDeref(&error);
711 }
712 
713 SmlBool smlDsSessionCheck(SmlDsSession *dsession)
714 {
715  if ((dsession->alertCommand && dsession->recvAlertCallback) || \
716  (dsession->recvSync && dsession->recvSyncCallback) || \
717  (dsession->recvChanges && dsession->changesCallback))
718  return TRUE;
719  return FALSE;
720 }
721 
722 void smlDsSessionRecvAlert(SmlSession *session, SmlCommand *cmd, void *userdata)
723 {
724  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
725  SmlDsSession *dsession = userdata;
726  SmlError *error = NULL;
727  smlAssert(dsession->location);
728 
729  g_mutex_lock(dsession->lock);
730 
731  if (!cmd->target || !cmd->source) {
732  SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_BAD_REQUEST, &error);
733  if (!reply)
734  goto error;
735 
736  if (!smlSessionSendReply(session, reply, &error)) {
737  smlStatusUnref(reply);
738  goto error;
739  }
740 
741  smlStatusUnref(reply);
742 
743  smlTrace(TRACE_EXIT, "%s: Alert had no target or source", __func__);
744  return;
745  }
746 
747  if (!smlLocationCompare(NULL, dsession->location, NULL, cmd->target)) {
748  SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_NOT_FOUND, &error);
749  if (!reply)
750  goto error;
751 
752  if (!smlSessionSendReply(session, reply, &error)) {
753  smlStatusUnref(reply);
754  goto error;
755  }
756 
757  smlStatusUnref(reply);
758 
759  smlTrace(TRACE_EXIT, "%s: Alert does not match our location", __func__);
760  return;
761  }
762 
763  smlCommandRef(cmd);
764 
765  if (!dsession->target) {
766  dsession->target = cmd->source;
767  smlLocationRef(cmd->source);
768  }
769 
770  dsession->alertCommand = cmd;
771 
772  /* Lock the final event at the manager.
773  *
774  * If this is called from a test case then the datastore
775  * is sometimes not registered at a SmlManager.
776  */
777  if (dsession->server->manager)
778  {
779  smlManagerSessionFinalLockRef(dsession->server->manager, session);
780  dsession->finalLock = TRUE;
781  }
782  else
783  smlTrace(TRACE_INTERNAL, "%s - no manager so should be a test", __func__);
784 
785  g_mutex_unlock(dsession->lock);
786 
787  smlTrace(TRACE_EXIT, "%s", __func__);
788  return;
789 
790 error:
791  g_mutex_unlock(dsession->lock);
792  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
793  smlErrorDeref(&error);
794  return;
795 }
796 
797 void smlDsSessionRecvSync(SmlSession *session, SmlCommand *cmd, void *userdata)
798 {
799  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
800  SmlDsSession *dsession = userdata;
801  SmlError *error = NULL;
802 
803  /* Create a write lock on the complete DsSession object to avoid race
804  * conditions. If the write lock is already present then this is the
805  * end of the sync block and the write lock must only be removed.
806  */
807  if (g_mutex_trylock(dsession->write_lock))
808  {
809  /* write lock succeeded so lock the complete DsSession */
810  g_mutex_lock(dsession->lock);
811  smlTrace(TRACE_INTERNAL, "%s - write lock enabled", __func__);
812  /* Lock the final event at the manager.
813  *
814  * If this is called from a test case then the datastore
815  * is sometimes not registered at a SmlManager.
816  */
817  if (dsession->server->manager)
818  {
819  smlManagerSessionFinalLockRef(dsession->server->manager, session);
820  dsession->finalLock = TRUE;
821  dsession->emptySync = TRUE;
822  }
823  else
824  smlTrace(TRACE_INTERNAL, "%s - no manager so should be a test", __func__);
825  } else {
826  /* detected an old write lock so this is the closing sync call */
827  if (dsession->finalLock && dsession->emptySync)
828  {
829  smlTrace(TRACE_INTERNAL, "%s - empty sync of moreData ?", __func__);
830  dsession->finalLock = FALSE;
831  smlManagerSessionFinalLockUnref(dsession->server->manager, session);
832  }
833  g_mutex_unlock(dsession->lock);
834  g_mutex_unlock(dsession->write_lock);
835  smlTrace(TRACE_EXIT, "%s - removed write lock", __func__);
836  return;
837  }
838 
839  if (dsession->syncReply == SML_ERROR_UNKNOWN) {
840  smlTrace(TRACE_INTERNAL, "%s: Storing sync command with cmdRef %i and msgRef %i", __func__, cmd->cmdID, cmd->msgID);
841  smlCommandRef(cmd);
842  dsession->recvSync = g_list_append(dsession->recvSync, cmd);
843  } else {
844  smlTrace(TRACE_INTERNAL, "%s: Using stored sync reply on cmd with cmdRef %i and msgRef %i", __func__, cmd->cmdID, cmd->msgID);
845  SmlStatus *reply = smlCommandNewReply(cmd, dsession->syncReply, &error);
846  if (!reply)
847  goto error;
848 
849  if (!smlSessionSendReply(dsession->session, reply, &error))
850  goto error;
851 
852  smlStatusUnref(reply);
853  }
854 
855  smlTrace(TRACE_EXIT, "%s", __func__);
856  return;
857 
858 error:
859  g_mutex_unlock(dsession->lock);
860  g_mutex_unlock(dsession->write_lock);
861  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
862  smlErrorDeref(&error);
863 }
864 
865 void smlDsSessionRecvChange(SmlSession *session, SmlCommand *cmd, void *userdata)
866 {
867  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
868  SmlDsSession *dsession = userdata;
869 
870  /* It is not necessary to lock the DsSesion object
871  * because RecvSync already enabled a global write_lock.
872  */
873  dsession->recvChanges = g_list_append(dsession->recvChanges, cmd);
874  smlCommandRef(cmd);
875 
876  /* This is important for fast and consistent final signalling. */
877  dsession->emptySync = FALSE;
878 
879  smlTrace(TRACE_EXIT, "%s", __func__);
880 }
881 
882 void smlDsSessionRecvMap(SmlSession *session, SmlCommand *cmd, void *userdata)
883 {
884  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
885  SmlDsSession *dsession = userdata;
886  SmlError *error = NULL;
887 
888  g_mutex_lock(dsession->lock);
889 
890  SmlStatus *reply = smlCommandNewReply(cmd, SML_NO_ERROR, &error);
891  if (!reply)
892  goto error;
893 
894  if (!smlSessionSendReply(session, reply, &error))
895  goto error;
896 
897  smlStatusUnref(reply);
898 
899  GList *m = NULL;
900  for (m = cmd->private.map.items; m; m = m->next) {
901  SmlMapItem *item = m->data;
902 
903  if (dsession->recvMappingCallback) {
904  dsession->recvMappingCallback(
905  dsession,
906  item->target,
907  item->source,
908  dsession->recvMappingCallbackUserdata);
909  } else {
910  SmlWriteContext *ctx = _write_context_find(dsession, item->target->locURI, SML_CHANGE_ADD);
911  if (ctx) {
912  ctx->newuid = g_strdup(item->source->locURI);
913  _write_context_dispatch(dsession, ctx);
914  } else {
915  smlTrace(TRACE_ERROR, "%s: Unknown map ... %s => %s",
916  __func__,
917  VA_STRING(item->target->locURI),
918  VA_STRING(item->source->locURI));
919  }
920  }
921  }
922 
923  g_mutex_unlock(dsession->lock);
924 
925  smlTrace(TRACE_EXIT, "%s", __func__);
926  return;
927 
928 error:
929  g_mutex_unlock(dsession->lock);
930  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
931  smlErrorDeref(&error);
932 }
933 
946 void smlDsSessionGetAlert(SmlDsSession *dsession, SmlDsSessionAlertCb callback, void *userdata)
947 {
948  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, dsession, callback, userdata);
949  smlAssert(dsession);
950  smlAssert(callback);
951 
952  dsession->recvAlertCallback = callback;
953  dsession->recvAlertCallbackUserdata = userdata;
954 
955  smlDsSessionDispatch(dsession);
956 
957  smlTrace(TRACE_EXIT, "%s", __func__);
958 }
959 
972 SmlBool smlDsSessionSendAlert(SmlDsSession *dsession, SmlAlertType type, const char *last, const char *next, SmlStatusReplyCb callback, void *userdata, SmlError **error)
973 {
974  smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %s, %p)", __func__, dsession, type, VA_STRING(last), VA_STRING(next), error);
975  smlAssert(dsession);
976  CHECK_ERROR_REF
977 
978  SmlCommand *alert = smlCommandNewAlert(type, dsession->target, dsession->location, next, last, NULL, error);
979  if (!alert)
980  goto error;
981 
982  dsession->sentAlertCallback = callback;
983  dsession->sentAlertCallbackUserdata = userdata;
984 
985  if (!smlSessionSendCommand(dsession->session, alert, NULL, _alert_reply, dsession, error))
986  goto error;
987 
988  smlCommandUnref(alert);
989 
990  smlTrace(TRACE_EXIT, "%s", __func__);
991  return TRUE;
992 
993 error:
994  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
995  return FALSE;
996 }
997 
998 void smlDsSessionGetMapping(
999  SmlDsSession *dsession,
1000  SmlDsSessionMapCb mapCallback,
1001  void *userdata)
1002 {
1003  smlTrace(TRACE_ENTRY, "%s", __func__);
1004  dsession->recvMappingCallback = mapCallback;
1005  dsession->recvMappingCallbackUserdata = userdata;
1006  smlTrace(TRACE_EXIT, "%s", __func__);
1007 }
1008 
1022 void smlDsSessionGetChanges(SmlDsSession *dsession, SmlDsSessionChangesCb chgCallback, void *userdata)
1023 {
1024  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, dsession, chgCallback, userdata);
1025  smlAssert(dsession);
1026  smlAssert(chgCallback);
1027 
1028  dsession->changesCallback = chgCallback;
1029  dsession->changesCallbackUserdata = userdata;
1030 
1031  smlDsSessionDispatch(dsession);
1032 
1033  smlTrace(TRACE_EXIT, "%s", __func__);
1034 }
1035 
1036 void smlDsSessionGetSync(SmlDsSession *dsession, SmlDsSessionSyncCb syncCallback, void *userdata)
1037 {
1038  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, dsession, syncCallback, userdata);
1039  smlAssert(dsession);
1040  smlAssert(syncCallback);
1041 
1042  dsession->recvSyncCallback = syncCallback;
1043  dsession->recvSyncCallbackUserdata = userdata;
1044 
1045  smlDsSessionDispatch(dsession);
1046 
1047  smlTrace(TRACE_EXIT, "%s", __func__);
1048 }
1049 
1050 void smlDsSessionGetEvent(SmlDsSession *dsession, SmlDsSessionEventCb eventCallback, void *userdata)
1051 {
1052  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, dsession, eventCallback, userdata);
1053  smlAssert(dsession);
1054  smlAssert(eventCallback);
1055 
1056  dsession->recvEventCallback = eventCallback;
1057  dsession->recvEventCallbackUserdata = userdata;
1058 
1059  smlDsSessionDispatch(dsession);
1060 
1061  smlTrace(TRACE_EXIT, "%s", __func__);
1062 }
1063 
1064 
1077 SmlBool smlDsSessionSendSync(SmlDsSession *dsession, unsigned int num_changes, SmlStatusReplyCb callback, void *userdata, SmlError **error)
1078 {
1079  smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p)", __func__, dsession, num_changes, callback, userdata, error);
1080  smlAssert(dsession);
1081  CHECK_ERROR_REF
1082 
1083  if (dsession->syncCommand) {
1084  smlErrorSet(error, SML_ERROR_GENERIC, "There already was a sync command started");
1085  goto error;
1086  }
1087 
1088  dsession->sentSyncCallback = callback;
1089  dsession->sentSyncCallbackUserdata = userdata;
1090 
1091  dsession->syncCommand = smlCommandNewSync(dsession->target, dsession->location, num_changes, error);
1092  if (!dsession->syncCommand)
1093  goto error;
1094 
1095  if (!smlSessionStartCommand(dsession->session, dsession->syncCommand, NULL, _sync_reply, dsession, error))
1096  goto error;
1097 
1098  smlTrace(TRACE_EXIT, "%s", __func__);
1099  return TRUE;
1100 
1101 error:
1102  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1103  return FALSE;
1104 }
1105 
1106 //Send a change to the remote side
1107 SmlBool smlDsSessionQueueChange(SmlDsSession *dsession, SmlChangeType type, const char *uid, const char *data, unsigned int size, const char *contenttype, SmlDsSessionWriteCb callback, void *userdata, SmlError **error)
1108 {
1109  smlTrace(TRACE_ENTRY, "%s(%p, %i, %s, %p, %i, %s, %p, %p, %p)", __func__, dsession, type, VA_STRING(uid), data, size, VA_STRING(contenttype), callback, userdata, error);
1110  smlAssert(dsession);
1111  CHECK_ERROR_REF
1112 
1113  if (!dsession->syncCommand) {
1114  smlErrorSet(error, SML_ERROR_GENERIC, "You have to start a sync command first");
1115  goto error;
1116  }
1117 
1118  SmlCommand *cmd = smlCommandNewChange(type, uid, data, size, contenttype, error);
1119  if (!cmd)
1120  goto error;
1121 
1122  SmlWriteContext *ctx = smlTryMalloc0(sizeof(SmlWriteContext), error);
1123  if (!ctx)
1124  goto error_free_cmd;
1125 
1126  ctx->callback = callback;
1127  ctx->userdata = userdata;
1128  ctx->uid = g_strdup(uid);
1129  ctx->type = type;
1130  ctx->session = dsession;
1131 
1132  g_mutex_lock(dsession->pendingMapsLock);
1133  dsession->pendingMaps = g_list_append(dsession->pendingMaps, ctx);
1134  g_mutex_unlock(dsession->pendingMapsLock);
1135 
1136  if (!smlSessionSendCommand(dsession->session, cmd, dsession->syncCommand, _change_reply, ctx, error))
1137  goto error_free_ctx;
1138 
1139  smlCommandUnref(cmd);
1140 
1141  smlTrace(TRACE_EXIT, "%s", __func__);
1142  return TRUE;
1143 
1144 error_free_ctx:
1145  smlSafeCFree(&(ctx->uid));
1146  smlSafeFree((gpointer *)&ctx);
1147 error_free_cmd:
1148  smlCommandUnref(cmd);
1149 error:
1150  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1151  return FALSE;
1152 }
1153 
1160 SmlBool smlDsSessionCloseSync(SmlDsSession *dsession, SmlError **error)
1161 {
1162  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, dsession, error);
1163  smlAssert(dsession);
1164  CHECK_ERROR_REF
1165 
1166  if (!dsession->syncCommand) {
1167  smlErrorSet(error, SML_ERROR_GENERIC, "There already was a sync command started");
1168  goto error;
1169  }
1170 
1171  if (!smlSessionEndCommand(dsession->session, NULL, error))
1172  goto error;
1173 
1174  smlCommandUnref(dsession->syncCommand);
1175  dsession->syncCommand = NULL;
1176 
1177  smlTrace(TRACE_EXIT, "%s", __func__);
1178  return TRUE;
1179 
1180 error:
1181  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1182  return FALSE;
1183 }
1184 
1185 SmlBool smlDsSessionQueueMap(SmlDsSession *dsession, const char *uid, const char *newuid, SmlError **error)
1186 {
1187  smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %p)", __func__, dsession, VA_STRING(uid), VA_STRING(newuid), error);
1188  smlAssert(dsession);
1189  CHECK_ERROR_REF
1190 
1191  SmlMapItem *item = smlMapItemNew(uid, newuid, error);
1192  if (!item)
1193  goto error;
1194 
1195  dsession->mapItems = g_list_append(dsession->mapItems, item);
1196 
1197  smlTrace(TRACE_EXIT, "%s", __func__);
1198  return TRUE;
1199 
1200 error:
1201  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1202  return FALSE;
1203 }
1204 
1214 SmlBool smlDsSessionCloseMap(SmlDsSession *dsession, SmlStatusReplyCb callback, void *userdata, SmlError **error)
1215 {
1216  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, dsession, callback, userdata, error);
1217  smlAssert(dsession);
1218  CHECK_ERROR_REF
1219 
1220  if (!dsession->mapItems) {
1221  smlTrace(TRACE_EXIT, "%s: No mapitems", __func__);
1222  return TRUE;
1223  }
1224 
1225  SmlCommand *cmd = smlCommandNewMap(dsession->server->target, dsession->server->location, error);
1226  if (!cmd)
1227  goto error;
1228 
1229  while (dsession->mapItems) {
1230  SmlMapItem *item = dsession->mapItems->data;
1231  if (!smlCommandAddMapItem(cmd, item, error))
1232  goto error_free_cmd;
1233  smlMapItemUnref(item);
1234 
1235  dsession->mapItems = g_list_remove(dsession->mapItems, item);
1236  }
1237 
1238  if (!smlSessionSendCommand(dsession->session, cmd, NULL, callback, userdata, error))
1239  goto error_free_cmd;
1240 
1241  smlCommandUnref(cmd);
1242 
1243  smlTrace(TRACE_EXIT, "%s", __func__);
1244  return TRUE;
1245 
1246 error_free_cmd:
1247  smlCommandUnref(cmd);
1248 error:
1249  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1250  return FALSE;
1251 }
1252 
1253 const char *smlDsSessionGetLocation(SmlDsSession *dsession)
1254 {
1255  smlAssert(dsession);
1256  return smlDsServerGetLocation(dsession->server);
1257 }
1258 
1259 SmlDsServer *smlDsSessionGetServer(SmlDsSession *dsession)
1260 {
1261  smlAssert(dsession);
1262  return dsession->server;
1263 }
1264 
1265 const char *smlDsSessionGetContentType(SmlDsSession *dsession)
1266 {
1267  smlAssert(dsession);
1268  return smlDsServerGetContentType(dsession->server);
1269 }
1270 
1271 static void _recv_manager_alert(SmlSession *session, SmlCommand *cmd, void *userdata)
1272 {
1273  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
1274  SmlDsServer *server = userdata;
1275  SmlError *error = NULL;
1276 
1277  SmlDsSession *dsession = smlDsServerRecvAlert(server, session, cmd);
1278  if (!dsession)
1279  goto error;
1280 
1281  if (!smlManagerObjectRegister(server->manager, SML_COMMAND_TYPE_SYNC, session, server->location, NULL, NULL, smlDsSessionRecvSync, smlDsSessionRecvChange, dsession, &error))
1282  goto error_free_dsession;
1283 
1284  if (!smlManagerObjectRegister(server->manager, SML_COMMAND_TYPE_MAP, session, server->location, NULL, NULL, smlDsSessionRecvMap, NULL, dsession, &error))
1285  goto error_free_dsession;
1286 
1287  smlTrace(TRACE_EXIT, "%s", __func__);
1288  return;
1289 
1290 error_free_dsession:
1291  smlSafeFree((gpointer *)&dsession);
1292 error:
1293  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
1294  if (error)
1295  smlErrorDeref(&error);
1296 }
1297 
1298 static void _recv_manager_san(SmlSession *session, SmlCommand *cmd, void *userdata)
1299 {
1300  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, cmd, userdata);
1301  SmlDsServer *server = userdata;
1302  SmlError *error = NULL;
1303 
1304  /* ensure that the session is in client mode */
1305  session->sessionType = SML_SESSION_TYPE_CLIENT;
1306 
1307  /* fix target */
1308  smlLocationUnref(server->target);
1309  server->target = cmd->source;
1310  smlLocationRef(server->target);
1311 
1312  /* handle the SAN */
1313  if (server->sanSessionCallback) {
1314  SmlErrorType type = server->sanSessionCallback(
1315  server,
1316  session,
1317  cmd->private.alert.type,
1318  server->sanSessionCallbackUserdata);
1319 
1320  SmlStatus *reply = smlCommandNewReply(cmd, type, &error);
1321  if (!reply)
1322  goto error;
1323 
1324  if (!smlSessionSendReply(session, reply, &error))
1325  goto error;
1326 
1327  smlStatusUnref(reply);
1328  } else if (server->sanCallback) {
1329  SmlErrorType type = server->sanCallback(server, cmd->private.alert.type, server->sanCallbackUserdata);
1330 
1331  SmlStatus *reply = smlCommandNewReply(cmd, type, &error);
1332  if (!reply)
1333  goto error;
1334 
1335  if (!smlSessionSendReply(session, reply, &error))
1336  goto error;
1337 
1338  smlStatusUnref(reply);
1339  } else {
1340  smlTrace(TRACE_INTERNAL, "%s: SAN ignored", __func__);
1341 
1342  SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_NOT_IMPLEMENTED, &error);
1343  if (!reply)
1344  goto error;
1345 
1346  if (!smlSessionSendReply(session, reply, &error))
1347  goto error;
1348 
1349  smlStatusUnref(reply);
1350  }
1351 
1352  smlTrace(TRACE_EXIT, "%s", __func__);
1353  return;
1354 
1355 error:
1356  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
1357  if (error)
1358  smlErrorDeref(&error);
1359 }
1360 
1361 SmlBool smlDsServerRegister(SmlDsServer *server, SmlManager *manager, SmlError **error)
1362 {
1363  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, server, manager, error);
1364  smlAssert(server);
1365  smlAssert(manager);
1366  CHECK_ERROR_REF
1367 
1368  if (!smlManagerObjectRegister(manager, SML_COMMAND_TYPE_ALERT, NULL, server->location, NULL, NULL, _recv_manager_alert, NULL, server, error))
1369  goto error;
1370 
1371  if (!smlManagerObjectRegister(manager, SML_COMMAND_TYPE_ALERT, NULL, NULL, NULL,server->contenttype, _recv_manager_san, NULL, server, error))
1372  goto error;
1373 
1374  server->manager = manager;
1375 
1376  smlTrace(TRACE_EXIT, "%s", __func__);
1377  return TRUE;
1378 
1379 error:
1380  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1381  return FALSE;
1382 }
1383 
1384 SmlLocation *smlDsSessionGetTarget(SmlDsSession *dsession)
1385 {
1386  smlAssert(dsession);
1387  return dsession->target;
1388 }
1389