libsyncml  0.5.4
sml_session.c
1 /*
2  * libsyncml - A syncml protocol implementation
3  * Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
4  * Copyright (C) 2007-2008 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 "syncml.h"
23 
24 #include "syncml_internals.h"
25 #include "sml_error_internals.h"
26 #include "sml_queue_internals.h"
27 #include "sml_session_internals.h"
28 #include "sml_command_internals.h"
29 #include "sml_elements_internals.h"
30 #include "sml_parse_internals.h"
31 #include "sml_transport_internals.h"
32 #include "objects/sml_auth_internals.h"
33 
41 
42 typedef enum SmlSessionCommandType {
43  SML_SESSION_COMMAND_START,
44  SML_SESSION_COMMAND_END,
45  SML_SESSION_STATUS,
46  SML_SESSION_FLUSH
47 } SmlSessionCommandType;
48 
49 typedef struct SmlSessionCommand {
50  SmlSessionCommandType type;
51  SmlCommand *cmd;
52  SmlCommand *parent;
53  SmlStatus *status;
54  SmlStatusReplyCb callback;
55  void *callbackUserdata;
56  SmlBool final;
57  SmlBool end;
59 
60 void smlSessionDispatchEvent(SmlSession *session, SmlSessionEventType type, SmlCommand *cmd, SmlCommand *parent, SmlStatus *headerreply, SmlError *error)
61 {
62  smlTrace(TRACE_ENTRY, "%s(%p, %i, %p, %p, %p, %p)", __func__, session, type, cmd, parent, headerreply, error);
63  smlAssert(session);
64  smlAssert(session->eventCallback);
65 
66  session->eventCallback(session, type, cmd, parent, headerreply, error, session->eventCallbackUserdata);
67 
68  smlTrace(TRACE_EXIT, "%s", __func__);
69 }
70 
71 static void _smlSessionFreePendingReplies(SmlSession *session)
72 {
73  while (session->pendingReplies) {
74  smlSafeFree((gpointer *)&(session->pendingReplies->data));
75  session->pendingReplies = g_list_delete_link(session->pendingReplies, session->pendingReplies);
76  }
77 }
78 
93 static SmlBool _smlSessionFlushInternal(SmlSession *session, SmlBool final, SmlError **error)
94 {
95  smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, session, final, error);
96  CHECK_ERROR_REF
97  smlAssert(session);
98  smlAssert(session->dataCallback);
99 
100  char *data = NULL;
101  unsigned int size = 0;
102  SmlBool end = FALSE;
103 
104  /* Add the latest header */
105  if (!smlAssemblerAddHeader(session->assembler, session, error))
106  goto error;
107 
108  /* Get the data that is waiting in the assembler */
109  if (!smlAssemblerRun(session->assembler, &data, &size, &end, final, error))
110  goto error;
111  if (session->authenticate)
112  end = FALSE;
113 
114  /* Flush and reset the command ID counter */
115  if (session->lastCommandID > 1)
116  {
117  smlAssemblerFlush(session->assembler);
118  } else {
119  session->lastCommandID = smlAssemblerFlush(session->assembler) - 1;
120  }
121  smlTrace(TRACE_INTERNAL, "%s: flushed assembler - last command id is %i now",
122  __func__, session->lastCommandID);
123 
124  SmlTransportData *tspdata = smlTransportDataNew(data, size, session->type, TRUE, error);
125  if (!tspdata)
126  goto error_free_data;
127  data = NULL;
128 
129  if (end && session->sessionType == SML_SESSION_TYPE_SERVER)
130  tspdata->needsAnswer = FALSE;
131 
132  /* Send the flush event */
133  if (!session->authenticate)
134  smlSessionDispatchEvent(session, SML_SESSION_EVENT_FLUSH, NULL, NULL, NULL, NULL);
135 
136  /* Send the data over the transport */
137  session->dataCallback(session, tspdata, session->dataCallbackUserdata);
138 
139  smlTransportDataDeref(tspdata);
140 
141  session->waiting = TRUE;
142 
143  if (final)
144  session->sending = FALSE;
145 
146  if (end && session->sessionType == SML_SESSION_TYPE_SERVER) {
147  smlTrace(TRACE_INTERNAL, "%s: Ending session now", __func__);
148  session->end = TRUE;
149  smlSessionRestoreTargetURI(session);
150  g_mutex_lock(session->reportEnd);
151  if (!session->reportedEnd) {
152  /* The end should only be reported once. */
153  session->reportedEnd = TRUE;
154  smlSessionDispatchEvent(session, SML_SESSION_EVENT_END,
155  NULL, NULL, NULL, NULL);
156  }
157  g_mutex_unlock(session->reportEnd);
158  }
159 
160  smlTrace(TRACE_INTERNAL, "%s: incrementing lastMessageID %d (old)",
161  __func__, session->lastMessageID);
162  session->lastMessageID++;
163 
164  session->hasCommand = FALSE;
165 
166  smlTrace(TRACE_EXIT, "%s", __func__);
167  return TRUE;
168 
169 error_free_data:
170  smlSafeCFree(&data);
171 error:
172  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
173  return FALSE;
174 }
175 
176 static void _check_reply(SmlSession *session, SmlStatus *status, void *userdata)
177 {
178  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
179 
180  smlTrace(TRACE_EXIT, "%s", __func__);
181 }
182 
183 static void _smlSessionFragmentStatus(SmlSession *session, SmlStatus *status, void *userdata)
184 {
185  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, userdata);
186  //SmlError *error = NULL;
187 
188  smlTrace(TRACE_INTERNAL, "%s: Got a status reply %i", __func__, status->type);
189 
190  if (smlStatusGetCode(status) != SML_CHUNK_ACCEPTED) {
191  /* We need to send a error reply for the original command */
192 
193  smlCommandUnref(session->frag_command);
194  session->frag_command = NULL;
195  }
196 
197  smlTrace(TRACE_EXIT, "%s", __func__);
198  return;
199 
200 /*error:
201  smlSessionDispatchEvent(session, SML_SESSION_EVENT_ERROR, NULL, NULL, NULL, error);
202  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
203  smlErrorDeref(&error);
204  return;*/
205 }
206 
207 static SmlCommand *_smlSessionFragmentSend(SmlSession *session, SmlCommand *orig_cmd, SmlCommand *parent, unsigned int space, unsigned int start, unsigned int complete_size, SmlError **error)
208 {
209  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %i, %i, %i, %p)", __func__, session, orig_cmd, parent, space, start, complete_size, error);
210  CHECK_ERROR_REF
211  /* This library must not send more that one item per change
212  * command. This is compliant with all specification and only a
213  * self limitation.
214  */
215  smlAssert(g_list_length(orig_cmd->private.change.items) == 1);
216  SmlCommand *frag_cmd = NULL;
217 
218  char *data = NULL;
219  unsigned int size = 0;
220  SmlItem *orig_item = g_list_nth_data(orig_cmd->private.change.items, 0);
221  if (!smlItemGetData(orig_item, &data, &size, error))
222  goto error;
223 
224  // FIXME: should we add an assertion here to guarantee
225  // FIXME: that size and complete_size are equal
226 
227  if (space < size - start) {
228  /* We need to create a new command. But we only send as much data as space
229  * is available */
230  frag_cmd = smlCommandNew(orig_cmd->type, error);
231  if (!frag_cmd)
232  goto error;
233 
234  SmlItem *frag_item = smlItemNewForData(data + start, space, error);
235  if (!frag_item)
236  goto error_free_cmd;
237  frag_cmd->private.change.items = g_list_append (NULL, frag_item);
238  if (!frag_cmd->private.change.items)
239  {
240  smlItemUnref(frag_item);
241  goto error_free_cmd;
242  }
243 
244  frag_item->moreData = TRUE;
245 
246  frag_item->target = orig_item->target;
247  if (frag_item->target)
248  smlLocationRef(frag_item->target);
249 
250  frag_item->source = orig_item->source;
251  if (frag_item->source)
252  smlLocationRef(frag_item->source);
253 
254  frag_item->contenttype = g_strdup(orig_item->contenttype);
255 
256  /* If this is the first sent part of the fragmented command
257  * then this command must include the size of the complete
258  * command.
259  */
260  if (start == 0)
261  frag_cmd->size = complete_size;
262 
263  session->frag_size += space;
264  } else {
265  /* We have enough room to send the original command.
266  * Nevertheless the original item must be replaced
267  * with a new item which contains the rest of the data.
268  * The original item must be freed.
269  */
270 
271  /* get original item and cleanup item list */
272  smlAssert(g_list_length(orig_cmd->private.change.items) == 1);
273  smlAssert(g_list_remove(orig_cmd->private.change.items, orig_item) == NULL);
274  orig_cmd->private.change.items = NULL;
275 
276  /* setup new item */
277  SmlItem *frag_item = smlItemNewForData(data + start, size - start, error);
278  if (!frag_item) {
279  smlItemUnref(orig_item);
280  goto error;
281  }
282  orig_cmd->private.change.items = g_list_append(NULL, frag_item);
283  if (!orig_cmd->private.change.items)
284  {
285  smlItemUnref(orig_item);
286  smlItemUnref(frag_item);
287  goto error;
288  }
289 
290  /* configure new item */
291  frag_item->contenttype = g_strdup(orig_item->contenttype);
292  frag_item->target = orig_item->target;
293  if (frag_item->target)
294  smlLocationRef(frag_item->target);
295  frag_item->source = orig_item->source;
296  if (frag_item->source)
297  smlLocationRef(frag_item->source);
298 
299  /* cleanup original item */
300  smlItemUnref(orig_item);
301 
302  /* setup fragmentation command */
303  frag_cmd = orig_cmd;
304  session->frag_size += size - start;
305  }
306 
307 
308  smlTrace(TRACE_EXIT, "%s: %p", __func__, frag_cmd);
309  return frag_cmd;
310 
311 error_free_cmd:
312  smlCommandUnref(frag_cmd);
313 error:
314  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
315  return NULL;
316 }
317 
318 static SmlAssemblerResult _smlSessionStartCommandInternal(SmlSession *session, SmlCommand *cmd, SmlCommand *parent, SmlStatusReplyCb callback, void *userdata, SmlBool addToCommandStack, SmlError **error)
319 {
320  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p, %i, %p)", __func__, session, cmd, parent, callback, userdata, addToCommandStack, error);
321  smlAssert(session);
322  smlAssert(cmd);
323  CHECK_ERROR_REF
324  SmlPendingStatus *pending = NULL;
325  SmlBool fragmented = FALSE;
326 
327  if (parent && !session->hasCommand) {
328  GList *c = NULL;
329  SmlCommand *oldcmd = NULL;
330  SmlCommand *oldparent = NULL;
331  GList * cmds = g_list_copy(session->commandStack);
332  for (c = cmds; c; c = c->next) {
333  oldcmd = c->data;
334 
335  if (_smlSessionStartCommandInternal(session, oldcmd, oldparent, _check_reply, NULL, FALSE, error) != SML_ASSEMBLER_RESULT_OK)
336  goto error;
337  oldparent = oldcmd;
338  }
339  g_list_free(cmds);
340  }
341 
342  /* Large object handling. Only possible of the session has a limit and
343  * we dont have a 1.0 session */
344  if ((cmd->type == SML_COMMAND_TYPE_ADD || cmd->type == SML_COMMAND_TYPE_REPLACE) &&
345  smlAssemblerGetRemoteMaxMsgSize(session->assembler) > 0 && session->version != SML_VERSION_10) {
346 
347  smlTrace(TRACE_INTERNAL, "%s: Checking if command needs to be fragmented", __func__);
348  /* This library must not send more that one item per change
349  * command. This is compliant with all specification and only a
350  * self limitation.
351  */
352  smlAssert(g_list_length(cmd->private.change.items) == 1);
353 
354  char *data = NULL;
355  unsigned int size = 0;
356  SmlItem *item = g_list_nth_data(cmd->private.change.items, 0);
357  if (!smlItemGetData(item, &data, &size, error))
358  goto error;
359 
360  /* If max obj size is not unlimited (0), and the size of the item is larger
361  * than the max obj size, we have to return an error */
362  int sendingmaxobjsize = smlSessionGetRemoteMaxObjSize(session);
363  if ((sendingmaxobjsize > 0) &&
364  (size > (unsigned int)sendingmaxobjsize))
365  {
366  smlErrorSet(error, SML_ERROR_SIZE_MISMATCH, "Item (%ub) is larger than the limit (%db)", size, smlSessionGetRemoteMaxObjSize(session));
367  goto error;
368  }
369 
370  if (!session->frag_command)
371  cmd->size = size;
372  item->moreData = TRUE;
373  int space = 0;
374  if (!smlAssemblerGetSpace(session->assembler, &space, parent, cmd, error))
375  goto error;
376  cmd->size = 0;
377  item->moreData = FALSE;
378 
379  /* Check if item data fits into the current message */
380  if (session->frag_command ||
381  (space > 0 &&
382  (unsigned int) space < size - session->frag_size))
383  {
384  smlTrace(TRACE_INTERNAL, "%s: Space %i, size %i. Fragmenting. Already added: %i", __func__, space, size - session->frag_size, session->frag_size);
385  /* We need to fragment */
386  /* Store the command for processing */
387 
388  if (!session->frag_command) {
389  session->frag_size = 0;
390 
391  session->frag_command = cmd;
392  smlCommandRef(cmd);
393 
394  session->frag_callback = callback;
395  session->frag_userdata = userdata;
396  }
397 
398  if (!(cmd = _smlSessionFragmentSend(session, session->frag_command, parent, space, session->frag_size, size, error)))
399  goto error;
400 
401  callback = _smlSessionFragmentStatus;
402  userdata = NULL;
403  fragmented = TRUE;
404 
405  /* Sometime cmd is frag_command. So theoretically it can
406  * happen that the item is replaced. Therefore the item
407  * reference must be determined again.
408  */
409  item = g_list_nth_data(cmd->private.change.items, 0);
410  if (item->moreData == FALSE) {
411  smlTrace(TRACE_INTERNAL, "%s: This is the last chunk", __func__);
412 
413  callback = session->frag_callback;
414  userdata = session->frag_userdata;
415  fragmented = FALSE;
416 
417  session->frag_size = 0;
418 
419  smlCommandUnref(session->frag_command);
420  session->frag_command = NULL;
421  }
422 
423  smlTrace(TRACE_INTERNAL, "%s: Fragmented. Added %i already", __func__, session->frag_size);
424  }
425  }
426 
427  /* We now increment the session ID */
428  session->lastCommandID++;
429  cmd->cmdID = session->lastCommandID;
430  smlTrace(TRACE_INTERNAL, "%s: last command id is %i", __func__, session->lastCommandID);
431 
432  /* Now we can try to add the command to the assembler */
433  switch (smlAssemblerStartCommand(session->assembler, parent, cmd, error)) {
434  case SML_ASSEMBLER_RESULT_OK:
435  /* We successfully added the command */
436  cmd->pushedBack = FALSE;
437  break;
438  case SML_ASSEMBLER_RESULT_MISMATCH:
439  if (cmd->pushedBack) {
440  smlErrorSet(error, SML_ERROR_GENERIC, "Command is too large");
441  goto error;
442  }
443  cmd->pushedBack = TRUE;
444 
445  /* We werent able to add the command. So we have to flush the already
446  * added statuses/commands and then add it again later */
447  if (!_smlSessionFlushInternal(session, FALSE, error)) {
448  session->lastCommandID--;
449  goto error;
450  }
451 
452  smlTrace(TRACE_EXIT, "%s: Mismatch", __func__);
453  return SML_ASSEMBLER_RESULT_MISMATCH;
454  case SML_ASSEMBLER_RESULT_ERROR:
455  session->lastCommandID--;
456  goto error;
457  }
458 
459  /* If the user requested a reply to the command we generate
460  * the pending status so that we can dispatch the incoming status
461  */
462  if (callback) {
463  pending = smlTryMalloc0(sizeof(SmlPendingStatus), error);
464  if (!pending) {
465  session->lastCommandID--;
466  goto error;
467  }
468 
469  pending->callback = callback;
470  pending->userdata = userdata;
471  pending->cmdID = cmd->cmdID;
472  pending->msgID = session->lastMessageID;
473 
474  smlTrace(TRACE_INTERNAL, "%s: Appending pending status with cmdID %i and msgID %i", __func__, pending->cmdID, pending->msgID);
475  session->pendingReplies = g_list_append(session->pendingReplies, pending);
476  } else
477  cmd->noResp = TRUE;
478 
479  /* We will get a status and a result to a get command
480  * so we register a second callback for the result */
481  if (cmd->type == SML_COMMAND_TYPE_GET) {
482  pending = smlTryMalloc0(sizeof(SmlPendingStatus), error);
483  if (!pending) {
484  session->lastCommandID--;
485  goto error;
486  }
487 
488  pending->callback = callback;
489  pending->userdata = userdata;
490  pending->cmdID = cmd->cmdID;
491  pending->msgID = session->lastMessageID;
492 
493  smlTrace(TRACE_INTERNAL, "%s: Appending pending status for a result with cmdID %i and msgID %i", __func__, pending->cmdID, pending->msgID);
494  session->pendingReplies = g_list_append(session->pendingReplies, pending);
495  }
496 
497  if (fragmented) {
498  /* We werent able to add the command. So we have to flush the already
499  * added statuses/commands and then add it again later */
500  if (!_smlSessionFlushInternal(session, FALSE, error)) {
501  session->lastCommandID--;
502  goto error;
503  }
504 
505  smlCommandUnref(cmd);
506 
507  smlTrace(TRACE_EXIT, "%s: Mismatch but fragmented", __func__);
508  return SML_ASSEMBLER_RESULT_MISMATCH;
509  }
510 
511  if (addToCommandStack)
512  session->commandStack = g_list_append(session->commandStack, cmd);
513  session->hasCommand = TRUE;
514 
515  smlTrace(TRACE_EXIT, "%s", __func__);
516  return SML_ASSEMBLER_RESULT_OK;
517 
518 error:
519  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
520  return SML_ASSEMBLER_RESULT_ERROR;
521 }
522 
523 static SmlBool _smlSessionEndCommandInternal(SmlSession *session, SmlCommand *parent, SmlError **error)
524 {
525  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, parent, error);
526  smlAssert(session);
527  CHECK_ERROR_REF
528 
529  if (!smlAssemblerEndCommand(session->assembler, parent, error))
530  goto error;
531 
532  session->commandStack = g_list_delete_link(session->commandStack, g_list_last(session->commandStack));
533 
534  smlTrace(TRACE_EXIT, "%s", __func__);
535  return TRUE;
536 
537 error:
538  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
539  return FALSE;
540 }
541 
542 static void _smlSessionCommandFree(SmlSessionCommand *sesscmd)
543 {
544  if (sesscmd->cmd)
545  smlCommandUnref(sesscmd->cmd);
546 
547  if (sesscmd->parent)
548  smlCommandUnref(sesscmd->parent);
549 
550  if (sesscmd->status)
551  smlStatusUnref(sesscmd->status);
552 
553  smlSafeFree((gpointer *)&sesscmd);
554 }
555 
556 static void _smlSessionCommandHandler(void *message, void *userdata)
557 {
558  smlTrace(TRACE_ENTRY, "%s", __func__);
559  smlAssert(message);
560  smlAssert(userdata);
561  SmlSessionCommand *sesscmd = message;
562  SmlSession *session = userdata;
563  SmlError *error = NULL;
564  //SmlPendingStatus *pending = NULL;
565  smlTrace(TRACE_INTERNAL, "%s: session cmd type %i", __func__, sesscmd->type);
566 
567  /* check if the header is ready */
568  if (!session->assmHasHeader)
569  {
570  if (!smlAssemblerAddHeader(session->assembler, session, &error))
571  goto error;
572  session->assmHasHeader = TRUE;
573  }
574 
575  SmlCommand *parent = sesscmd->parent;
576  SmlCommand *cmd = sesscmd->cmd;
577  SmlStatus *status = sesscmd->status;
578 
579  switch (sesscmd->type) {
580  case SML_SESSION_FLUSH:
581  if (sesscmd->end) {
582  session->end = TRUE;
583  smlTrace(TRACE_INTERNAL, "%s: End on flush was set", __func__);
584  }
585  if (!_smlSessionFlushInternal(session, sesscmd->final, &error))
586  goto error;
587  break;
588  case SML_SESSION_COMMAND_END:
589  if (!_smlSessionEndCommandInternal(session, parent, &error))
590  goto error;
591  break;
592  case SML_SESSION_COMMAND_START:
593  switch (_smlSessionStartCommandInternal(session, cmd, parent, sesscmd->callback, sesscmd->callbackUserdata, TRUE, &error)) {
594  case SML_ASSEMBLER_RESULT_OK:
595  /* We successfully added the command */
596  break;
597  case SML_ASSEMBLER_RESULT_MISMATCH:
598  smlQueuePushHead(session->command_queue, message);
599  return;
600  case SML_ASSEMBLER_RESULT_ERROR:
601  goto error;
602  }
603  break;
604  case SML_SESSION_STATUS:;
605  unsigned int cmdRef, msgRef = 0;
606  if (!smlAssemblerGetNextCmdRef(session->assembler, &cmdRef, &msgRef)) {
607  smlErrorSet(&error, SML_ERROR_GENERIC, "No more statuses were needed");
608  goto error;
609  }
610 
611  if (status->cmdRef != cmdRef || status->msgRef != msgRef) {
612  //Put it on the end of the queue
613  smlQueueSendPrio(session->command_queue, message);
614  smlTrace(TRACE_EXIT,
615  "%s - next needed command status %d for message %d is not this one (cmd %d, msg %d)",
616  __func__, cmdRef, msgRef, status->cmdRef, status->msgRef);
617  return;
618  }
619 
620  /* We ignore if the added status violates the size limitation if
621  * a incoming buffer for a large object is open. the problem is that
622  * the status for the chunk has to go into the next message AND the status
623  * MUST be in the same order as the original commands. so the only solution is
624  * to send all statuses and ignore the size. */
625 
626  /* Now we can try to add the status to the assembler */
627  smlTrace(TRACE_INTERNAL, "%s - try to add status", __func__);
628  switch (smlAssemblerAddStatusFull(session->assembler, status, session->incomingBuffer ? TRUE : FALSE, &error)) {
629  case SML_ASSEMBLER_RESULT_OK:
630  /* We successfully added the status */
631  session->active = TRUE;
632  break;
633  case SML_ASSEMBLER_RESULT_MISMATCH:
634  /* We werent able to add the command. So we have to flush the already
635  * added statuses/commands and then add it again later */
636  if (!_smlSessionFlushInternal(session, FALSE, &error)) {
637  session->lastCommandID--;
638  goto error;
639  }
640 
641  /* Push the message back into the queue */
642  smlQueuePushHeadPrio(session->command_queue, message);
643  smlTrace(TRACE_EXIT, "%s - status cannot be added, so push back", __func__);
644  return;
645  break;
646  case SML_ASSEMBLER_RESULT_ERROR:
647  goto error;
648  }
649  break;
650  }
651 
652  _smlSessionCommandFree(sesscmd);
653  smlTrace(TRACE_EXIT, "%s", __func__);
654 
655  return;
656 
657 error:
658  smlSessionDispatchEvent(session, SML_SESSION_EVENT_ERROR, NULL, NULL, NULL, error);
659  _smlSessionCommandFree(sesscmd);
660  return;
661 }
662 
663 SmlBool smlSessionReceiveHeader(SmlSession *session, SmlHeader *header, SmlError **error)
664 {
665  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, header, error);
666  smlAssert(session);
667  smlAssert(header);
668  CHECK_ERROR_REF
669 
670  /* check and set the counters */
671  if (header->messageID <= session->lastReceivedMessageID) {
672  smlErrorSet(error, SML_ERROR_GENERIC, "Message ID not incremented");
673  goto error;
674  }
675  session->lastReceivedMessageID = header->messageID;
676  session->lastCommandID++;
677 
678  /* check and set the MaxMsgSize */
679  if (0 < header->maxmsgsize) {
680  /* If MaxMsgSize is 0 then it is not set. Therefore
681  * a MaxMsgSize of 0 is ignored to not override an
682  * already configured MaxMsgSize from an earlier
683  * message.
684  */
685  smlAssemblerSetRemoteMaxMsgSize(session->assembler, header->maxmsgsize);
686  }
687  if (0 < header->maxmsgsize && header->maxmsgsize < session->localMaxMsgSize)
688  {
689  /* The localMaxMsgSize is only set if it is already
690  * configured. If there is no local limit then it is not
691  * necessary to configure it only because the remote
692  * peer has a limit.
693  */
694  smlSessionSetLocalMaxMsgSize(session, header->maxmsgsize);
695  }
696 
697  /* Reserve the status for header reply. It will always be cmdRef 0
698  * and it will always be the first command in the message */
699  if (!smlAssemblerReserveStatus(session->assembler, 0, header->messageID, 1, error))
700  goto error;
701 
702  /* If RespURI is send in the header
703  * then the URI in the transport layer must be adjusted. */
704  if (header->responseURI) {
705  if (!session->orgTarget) {
706  /* If the original target is already set
707  * then it MUST NOT be set again
708  * because this would destroy the real original
709  * target and an earlier RespURI would be used
710  * as original target.
711  */
712  session->orgTarget = session->target;
713  session->target = NULL;
714  }
715 
716  if (session->target) {
717  /* This is not the original target.
718  * Perhaps this is from an earlier message.
719  * Nevertheless only the most actual target
720  * is required.
721  */
722  smlLocationUnref(session->target);
723  }
724 
725  /* build the new target and publish it*/
726  session->target = smlLocationNew(
727  header->responseURI,
728  smlLocationGetName(session->orgTarget),
729  error);
730  if (!session->target)
731  goto error;
732  smlSessionDispatchEvent(session, SML_SESSION_EVENT_RESPONSE_URI, NULL, NULL, NULL, NULL);
733  smlTrace(TRACE_INTERNAL, "%s: RespURI: %s, Target: %s", __func__,
734  VA_STRING(smlLocationGetURI(session->target)),
735  VA_STRING(smlLocationGetURI(session->orgTarget)));
736  } else {
737  /* Sometimes a client changes the source reference
738  * after the SAN was sent because the SAN does not
739  * include the correct IMEI.
740  *
741  * If the header includes a new SourceRef/LocURI
742  * then we have to change the TargetRef/LocURI
743  * of the session.
744  */
745  const char *header_source = smlLocationGetURI(header->source);
746  const char *session_target = smlLocationGetURI(session->target);
747  if (header_source &&
748  session_target &&
749  strcmp(header_source, session_target))
750  {
751  /* session target needs an update */
752  smlTrace(TRACE_INTERNAL,
753  "%s: update session target to header source %s",
754  __func__, header_source);
755  smlLocationSetURI(session->target, header_source);
756  }
757  }
758  smlTrace(TRACE_EXIT, "%s", __func__);
759  return TRUE;
760 
761 error:
762  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
763  return FALSE;
764 }
765 
766 SmlBool smlSessionDispatchStatus(SmlSession *session, SmlStatus *status, SmlError **error)
767 {
768  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, error);
769  smlAssert(status);
770  CHECK_ERROR_REF
771 
772  if (status->cmdRef == 0) {
773  smlAssert(status->type == SML_COMMAND_TYPE_HEADER);
774  smlAssert(status->data);
775  SmlErrorType errorCode = atoi(status->data);
776  if (session->sessionType == SML_SESSION_TYPE_SERVER)
777  {
778  /* If this is an OMA DS server then the session
779  * authorization is done by the header callback.
780  * This is usually handled by functions named
781  * smlAuth*.
782  *
783  * If the OMA DS client code would be active
784  * then the remote client only have to send an
785  * error code 212 to force the server to accept
786  * the connection. So this is highly critical.
787  */
788  SmlCommand *cmd = smlCommandNew(SML_COMMAND_TYPE_HEADER, error);
789  smlSessionDispatchEvent(session, SML_SESSION_EVENT_HEADER_REPLY, cmd, NULL, status, NULL);
790  smlCommandUnref(cmd);
791  }
792  else if (errorCode == SML_AUTH_ACCEPTED)
793  {
794  /* authentication succeeded
795  * we can remove the credentials from this session
796  * we do it now for better security
797  */
798  smlTrace(TRACE_INTERNAL, "%s - authentication succeeded", __func__);
799  if (session->cred != NULL)
800  {
801  smlCredUnref(session->cred);
802  session->cred = NULL;
803  }
804  if (!session->established)
805  {
806  session->established = TRUE;
807  smlSessionDispatchEvent(
808  session, SML_SESSION_EVENT_ESTABLISHED,
809  NULL, NULL, NULL, NULL);
810  }
811  }
812  else if (errorCode == SML_ERROR_AUTH_REQUIRED)
813  {
814  /* If the requested authentication method violates the
815  * local security policy then the authentication must
816  * fail. Otherwise the password of the user can be
817  * compromized.
818  */
819  smlTrace(TRACE_INTERNAL, "%s - authentication required", __func__);
820  session->authenticate = TRUE;
821  session->established = FALSE;
822  smlAssemblerRestoreCommands(session->assembler);
823  smlTrace(TRACE_INTERNAL, "%s - restored commands from previous message", __func__);
824  smlAssert(status->chal);
825  smlAssert(session->cred);
826  if (session->cred->type == SML_AUTH_TYPE_MD5 &&
827  status->chal->type != SML_AUTH_TYPE_MD5)
828  {
829  smlErrorSet(error, SML_ERROR_AUTH_REJECTED, "The remote peer tries to enforce an authentication method which violates the local security policy (syncml:auth-md5 is required).");
830  goto error;
831  }
832  smlTrace(TRACE_INTERNAL, "%s - authentication type conforms to security policy", __func__);
833 
834  /* build the authentication string */
835  session->cred->data = smlAuthGetCredString(
836  status->chal->type,
837  session->cred->username,
838  session->cred->password,
839  status->chal->nonce_b64,
840  error);
841  if (!session->cred->data)
842  goto error;
843  smlTrace(TRACE_INTERNAL, "%s - credential string set", __func__);
844  } else {
845  if (!session->established)
846  {
847  session->established = TRUE;
848  smlSessionDispatchEvent(
849  session, SML_SESSION_EVENT_ESTABLISHED,
850  NULL, NULL, NULL, NULL);
851  }
852  SmlCommand *cmd = smlCommandNew(SML_COMMAND_TYPE_HEADER, error);
853  smlSessionDispatchEvent(session, SML_SESSION_EVENT_HEADER_REPLY, cmd, NULL, status, NULL);
854  smlCommandUnref(cmd);
855  }
856  goto out;
857  }
858  smlTrace(TRACE_INTERNAL, "%s - no header status", __func__);
859 
860  /* Check if a handler for this object at this path has been installed.
861  * Please note that sometimes (e.g. GET command) a second handler
862  * was installed. So we cannot abort at the first found pending reply.
863  * We have to check every handler.
864  */
865  GList *o = NULL;
866  SmlBool foundPending = FALSE;
867  for (o = session->pendingReplies; o; o = o ? o->next : NULL ) {
868  SmlPendingStatus *pending = o->data;
869  smlTrace(TRACE_INTERNAL, "%s: check cmd %i of msg %i", __func__, pending->cmdID, pending->msgID);
870  if (pending->cmdID == status->cmdRef && pending->msgID == status->msgRef) {
871  smlTrace(TRACE_INTERNAL, "%s - Found pending status %s of command %d in message %d",
872  __func__, VA_STRING(status->data), status->cmdRef, status->msgRef);
873  if (session->authenticate)
874  {
875  /* modify the pending reply
876  * If there is a required authentication
877  * then all commands must be send again.
878  * Please note that the simple change of the
879  * message ID only works because we never touch
880  * command ID.
881  */
882  smlTrace(TRACE_INTERNAL, "%s - update message ID to %d",
883  __func__, session->lastMessageID);
884  pending->msgID++;
885  } else {
886  /* normal handling of the first pendingReply (the status one) */
887  smlTrace(TRACE_INTERNAL, "%s - normal status", __func__);
888  session->pendingReplies = g_list_remove(session->pendingReplies, pending);
889 
890  smlAssert(pending->callback);
891  pending->callback(session, status, pending->userdata);
892 
893  smlSafeFree((gpointer *)&pending);
894 
895  /* We have to abort the for loop here
896  * because we MUST NOT delete a potential
897  * second pending reply. The GET command
898  * registers to pending replies - one for
899  * the status and one for the results.
900  */
901  o = NULL;
902  }
903 
904  foundPending = TRUE;
905  }
906  }
907  if (foundPending)
908  goto out;
909  smlTrace(TRACE_INTERNAL, "%s - no pending reply found", __func__);
910 
911  /* We have to catch at this point if the status reply is the reply
912  * to our server alerted sync. Since this initial alert is not sent
913  * over a session, the status can of course not be wanted */
914  if (status->type == SML_COMMAND_TYPE_ALERT && !status->anchor) {
915  smlTrace(TRACE_INTERNAL, "%s: Handling status for server alerted sync", __func__);
916  SmlErrorType errorCode = atoi(status->data);
917  if ((199 < errorCode && errorCode < 300) ||
918  errorCode == 508)
919  {
920  /* If the status is 2xx or 508 then it is no error. */
921  goto out;
922  } else {
923  /* There is an error and this must be signaled.
924  * If there is no callback available
925  * then this is a normal error.
926  */
927  smlErrorSet(error, errorCode, "The SAN alert was rejected.");
928  goto error;
929  }
930  }
931 
932  smlErrorSet(error, SML_ERROR_GENERIC,
933  "Received unwanted status %s of command %d in message %d",
934  status->data, status->cmdRef, status->msgRef);
935  goto error;
936 out:
937  smlTrace(TRACE_EXIT, "%s", __func__);
938  return TRUE;
939 error:
940  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
941  return FALSE;
942 }
943 
944 SmlBool smlSessionCheck(SmlSession *session)
945 {
946  smlAssert(session);
947 
948  if (session->waiting)
949  {
950  smlTrace(TRACE_INTERNAL, "%s: waiting", __func__);
951  return FALSE;
952  }
953 
954  SmlBool result;
955  if (!smlAssemblerIsStatusMissing(session->assembler))
956  {
957  result = smlQueueCheck(session->command_queue);
958  smlTrace(TRACE_INTERNAL, "%s: no status is missing - %i", __func__, result);
959  }
960  else
961  {
962  result = smlQueueCheckPrio(session->command_queue);
963  smlTrace(TRACE_INTERNAL, "%s: status is missing - %i", __func__, result);
964  }
965  return result;
966 }
967 
968 SmlBool smlSessionTryLock(SmlSession *session)
969 {
970  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, session);
971  smlAssert(session);
972  smlAssert(session->dispatch_mutex);
973  SmlBool ret = g_mutex_trylock(session->dispatch_mutex);
974  smlTrace(TRACE_EXIT, "%s - %d", __func__, ret);
975  return ret;
976 }
977 
978 void smlSessionLock(SmlSession *session)
979 {
980  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, session);
981  smlAssert(session);
982  smlAssert(session->dispatch_mutex);
983  g_mutex_lock(session->dispatch_mutex);
984  smlTrace(TRACE_EXIT, "%s", __func__);
985 }
986 
987 void smlSessionUnlock(SmlSession *session)
988 {
989  smlTrace(TRACE_ENTRY, "%s", __func__);
990  smlAssert(session);
991  smlAssert(session->dispatch_mutex);
992  g_mutex_unlock(session->dispatch_mutex);
993  smlTrace(TRACE_EXIT, "%s", __func__);
994 }
995 
996 void smlSessionDispatch(SmlSession *session)
997 {
998  smlTrace(TRACE_ENTRY, "%s", __func__);
999  smlAssert(session);
1000 
1001  if (session->waiting)
1002  {
1003  smlTrace(TRACE_EXIT, "%s - session is waiting", __func__);
1004  return;
1005  }
1006 
1007  /* If we are still missing statuses, we just dispatch the statuses
1008  * If we are complete on the status side, we dispatch the commands
1009  */
1010  if (!smlAssemblerIsStatusMissing(session->assembler))
1011  {
1012  smlTrace(TRACE_INTERNAL, "%s: dispatch commands", __func__);
1013  smlQueueDispatch(session->command_queue);
1014  }
1015  else
1016  {
1017  smlTrace(TRACE_INTERNAL, "%s: dispatch status", __func__);
1018  smlQueueDispatchPrio(session->command_queue);
1019  }
1020  smlTrace(TRACE_EXIT, "%s - done", __func__);
1021 }
1022 
1023 SmlBool smlSessionReceiveBody(SmlSession *session, SmlParser *parser, SmlError **error)
1024 {
1025  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, parser, error);
1026  smlAssert(session);
1027  smlAssert(parser);
1028  CHECK_ERROR_REF
1029  SmlStatus *status = NULL;
1030  SmlCommand *cmd = NULL;
1031  SmlParserResult result = SML_PARSER_RESULT_ERROR;
1032  SmlBool isSan = TRUE;
1033 
1034  if (session->sessionType == SML_SESSION_TYPE_SERVER)
1035  isSan = FALSE;
1036 
1037  /* The session received some stuff and so it is no longer waiting.
1038  * It is important to set the state here because if it is set later
1039  * then there is the risk that some dispatched command sends the
1040  * next message via a direct flush. In this case a late state change
1041  * could lead to a race condition because the correct state WAITING
1042  * would be overwritten with the old value.
1043  */
1044  session->waiting = FALSE;
1045  session->sending = FALSE;
1046 
1047 parse_status:
1048  /* Now lets go through the available statuses (if any) */
1049  while (1) {
1050  if (!smlParserGetStatus(parser, &status, error))
1051  goto error_free_status;
1052 
1053  if (!status)
1054  break;
1055 
1056  /* There is at minimum one status. This means that
1057  * this is not the first message and there is an
1058  * header reply.
1059  */
1060  isSan = FALSE;
1061 
1062  /* If there is a status then it is necessary to check
1063  * the session environment before the user has a chance
1064  * to do something with the received information.
1065  *
1066  * The background is that libsyncml does not handle the
1067  * SANs in a SmlSession. Therefore the SmlSession is not
1068  * aware of an already used MsgRef 1.
1069  *
1070  * If a status is received and lastMessageID is still 1
1071  * then a reply to a SAN is assumed and the lastMessageID
1072  * is fixed accordingly.
1073  */
1074  if (session->lastMessageID == 1)
1075  {
1076  /* This should be an answer to a SAN. */
1077  smlTrace(TRACE_INTERNAL, "%s - reply to SAN detected", __func__);
1078  session->lastMessageID = 2;
1079  }
1080 
1081  /* Call the callback*/
1082  if (!smlSessionDispatchStatus(session, status, error))
1083  goto error_free_status;
1084 
1085  smlStatusUnref(status);
1086  }
1087 
1088  /* Go through the remaining commands */
1089  while (1) {
1090  result = smlParserGetCommand(parser, &cmd, error);
1091  if (result == SML_PARSER_RESULT_ERROR)
1092  goto error_free_cmd;
1093  if (result == SML_PARSER_RESULT_OTHER)
1094  break;
1095 
1096  if (cmd)
1097  cmd->msgID = session->lastReceivedMessageID;
1098 
1099  /* If the handler of the header signals that an authentication
1100  * is still required then we must send status 407 to all
1101  * commands. The client must send all commands with the
1102  * next message again.
1103  */
1104  if (!session->established && !isSan) {
1105  session->lastCommandID++;
1106 
1107  /* Reserve the place for the status in the assembler */
1108  if (!smlAssemblerReserveStatus(session->assembler, cmd->cmdID, session->lastReceivedMessageID, session->lastCommandID, error))
1109  goto error_free_cmd;
1110 
1111  /* send status 407 */
1112  SmlStatus *reply = smlCommandNewReply(
1113  cmd, SML_ERROR_AUTH_REQUIRED, error);
1114  if (!reply)
1115  goto error_free_cmd;
1116  if (!smlSessionSendReply(session, reply, error))
1117  goto error_free_cmd;
1118  smlStatusUnref(reply);
1119  smlCommandUnref(cmd);
1120  continue;
1121  }
1122 
1123  switch (result) {
1124  case SML_PARSER_RESULT_OPEN:
1125  session->lastCommandID++;
1126  /* Reserve the place for the status in the assembler */
1127  if (!smlAssemblerReserveStatus(session->assembler, cmd->cmdID, session->lastReceivedMessageID, session->lastCommandID, error))
1128  goto error_free_cmd;
1129 
1130  /* Store the parent */
1131  if (session->parentCommand) {
1132  smlErrorSet(error, SML_ERROR_GENERIC, "Already had a parent");
1133  goto error_free_cmd;
1134  }
1135 
1136  /* If we are a server, we just mimick the behaviour of the client regarding
1137  * large object handling and support of number of changes. This way we dont
1138  * need to parse the devinf */
1139  if (cmd->type == SML_COMMAND_TYPE_SYNC) {
1140  smlTrace(TRACE_INTERNAL, "%s: maxObjSize: %i, numbOfChanges: %i, %i",
1141  __func__,
1142  cmd->private.sync.maxObjSize,
1143  cmd->private.sync.hasNumChanged,
1144  cmd->private.sync.numChanged);
1145  /* If the sync has the maxObjSize set (>= 0), we set the requested max obj size.
1146  * If the max obj size was set before (with the alert, we overwrite it */
1147  if (cmd->private.sync.maxObjSize >= 0)
1148  smlSessionSetRemoteMaxObjSize(session, cmd->private.sync.maxObjSize);
1149  /* If the DevInf of the client includes SupportNumberOfChanges
1150  * but the client does not send NumberOfChanges
1151  * then the server still MUST send NumberOfChanges.
1152  * The client only MAY send NumberOfChanges.
1153  *
1154  * So even if NumberOfChanges are missing then never touch
1155  * the requested and configured NumberOfChanges property.
1156  *
1157  * Please see OMA DS Protocol v1.2
1158  * Section 9 Two-Way Sync
1159  * Section 9.1 Client Modifications to Server
1160  * Section 9.2 Server Modifications to Client
1161  */
1162  }
1163 
1164  session->parentCommand = cmd;
1165  cmd->msgID = session->lastReceivedMessageID;
1166 
1167  if (!session->end) {
1168  smlSessionDispatchEvent(session, SML_SESSION_EVENT_COMMAND_START, cmd, NULL, NULL, NULL);
1169  } else {
1170  smlTrace(TRACE_INTERNAL, "%s: Replying with 407 since the session has ended",
1171  __func__);
1172  SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_AUTH_REQUIRED, error);
1173  if (!reply)
1174  goto error_free_cmd;
1175 
1176  if (!smlSessionSendReply(session, reply, error))
1177  goto error_free_cmd;
1178 
1179  smlStatusUnref(reply);
1180  }
1181  break;
1182  case SML_PARSER_RESULT_NORMAL:
1183  session->lastCommandID++;
1184  /* Reserve the place for the status in the assembler */
1185  if (!smlAssemblerReserveStatus(session->assembler, cmd->cmdID, session->lastReceivedMessageID, session->lastCommandID, error))
1186  goto error_free_cmd;
1187 
1188  /* Here we catch Alerts for the next message */
1189  if (cmd->type == SML_COMMAND_TYPE_ALERT) {
1190  if (cmd->private.alert.type == SML_ALERT_NEXT_MESSAGE) {
1191  SmlStatus *reply = smlCommandNewReply(cmd, SML_NO_ERROR, error);
1192  if (!reply)
1193  goto error_free_cmd;
1194 
1195  if (!smlSessionSendReply(session, reply, error))
1196  goto error_free_cmd;
1197 
1198  smlStatusUnref(reply);
1199 
1200  smlCommandUnref(cmd);
1201  break;
1202  } else {
1203  smlTrace(TRACE_INTERNAL, "%s: Alert maxObjSize: %i",
1204  __func__, cmd->private.alert.maxObjSize);
1205  if (cmd->private.alert.maxObjSize >= 0)
1206  smlAssemblerSetRemoteMaxObjSize(session->assembler, cmd->private.alert.maxObjSize);
1207  }
1208  }
1209 
1210  /* Here we catch changes with moreData set */
1211  if (cmd->type == SML_COMMAND_TYPE_ADD || cmd->type == SML_COMMAND_TYPE_REPLACE) {
1212 
1213  SmlItem *lastItem = g_list_nth_data(cmd->private.change.items,
1214  g_list_length(cmd->private.change.items) - 1);
1215 
1216  if (session->incomingBuffer) {
1217  /* There is a command in the incoming buffer.
1218  * This requires that all items of the received
1219  * command are merged into this incoming buffer.
1220  * Please remember that one command can contain
1221  * several item each with a large object inside.
1222  *
1223  * If the last item of the command has no moreData
1224  * flag then the command is dispatched.
1225  */
1226  smlTrace(TRACE_INTERNAL, "%s: Appending to incoming buffer", __func__);
1227 
1228  /* The size of an item must not be set in the
1229  * different chunks except the first one.
1230  */
1231  if (cmd->size) {
1232  smlErrorSet(error, SML_ERROR_GENERIC,
1233  "Only the first chunk can have the size set");
1234  goto error_free_cmd;
1235  }
1236 
1237  /* Copy the item data of the first new item.
1238  * This includes the moreData flag.
1239  * This is a chunk of the previous item in the buffer.
1240  */
1241  char *data = NULL;
1242  unsigned int size = 0;
1243  SmlItem *item = g_list_nth_data(cmd->private.change.items, 0);
1244  if (!item)
1245  goto error_free_cmd;
1246  if (!smlItemGetData(item, &data, &size, error))
1247  goto error_free_cmd;
1248  lastItem = g_list_nth_data(session->incomingBuffer->private.change.items, g_list_length(session->incomingBuffer->private.change.items) - 1);
1249  if (!smlItemAddData(lastItem, data, size, error))
1250  goto error_free_cmd;
1251  lastItem->moreData = item->moreData;
1252  smlTrace(TRACE_INTERNAL,
1253  "%s: Appended %i to buffer. Buffer size: %i. Required: %i",
1254  __func__, size,
1255  xmlBufferLength(lastItem->buffer),
1256  lastItem->size);
1257 
1258  /* move all other items from the new to the buffered command */
1259  guint i;
1260  for (i=1; i < g_list_length(cmd->private.change.items); i++)
1261  {
1262  item = g_list_nth_data(cmd->private.change.items, i);
1263  session->incomingBuffer->private.change.items =
1264  g_list_append(
1265  session->incomingBuffer->private.change.items,
1266  item);
1267  }
1268  smlItemUnref(g_list_nth_data(cmd->private.change.items, 0));
1269  g_list_free(cmd->private.change.items);
1270  cmd->private.change.items = NULL;
1271 
1272  /* If the last item of the command is complete
1273  * then the command must be prepared for dispatching.
1274  */
1275  lastItem = g_list_nth_data(session->incomingBuffer->private.change.items, g_list_length(session->incomingBuffer->private.change.items) - 1);
1276  if (!lastItem->moreData) {
1277  smlTrace(TRACE_INTERNAL,
1278  "%s: Command buffer complete. Dispatching.",
1279  __func__);
1280 
1281  /* check if the item size is correct */
1282  if (!smlItemCheck(lastItem)) {
1283  /* We have a size mismatch */
1284  SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_SIZE_MISMATCH, error);
1285  if (!reply)
1286  goto error_free_cmd;
1287  if (!smlSessionSendReply(session, reply, error))
1288  goto error_free_cmd;
1289  smlStatusUnref(reply);
1290  smlErrorSet(error, SML_ERROR_GENERIC,
1291  "Chunked item size mismatch");
1292  goto error_free_cmd;
1293  }
1294 
1295  /* The original command is no longer needed.
1296  * The incoming buffer cannot be used
1297  * because several status informations like
1298  * the message ID are required for correct
1299  * status replies.
1300  */
1301  cmd->private.change.items =
1302  session->incomingBuffer->private.change.items;
1303  session->incomingBuffer->private.change.items = NULL;
1304  smlCommandUnref(session->incomingBuffer);
1305  session->incomingBuffer = NULL;
1306  }
1307  } else if (lastItem->moreData) {
1308  /* There is no buffered command yet.
1309  * The new command is copied to the buffer.
1310  * This is done by reference copy and
1311  * an incrementation of the reference counter.
1312  */
1313 
1314  if (!cmd->size) {
1315  smlErrorSet(error, SML_ERROR_GENERIC, "First MoreData item didn't have size set");
1316  goto error_free_cmd;
1317  }
1318 
1319  session->incomingBuffer = cmd;
1320  smlCommandRef(cmd);
1321  smlTrace(TRACE_INTERNAL, "%s: New Buffer started. Buffered item size: %i. Required: %i",
1322  __func__,
1323  xmlBufferLength(lastItem->buffer),
1324  lastItem->size);
1325 
1326  char *bin = smlPrintBinary((char *) xmlBufferContent(lastItem->buffer), xmlBufferLength(lastItem->buffer));
1327  smlTrace(TRACE_INTERNAL, "%s: Content so far: %s\n",
1328  __func__, VA_STRING(bin));
1329  smlSafeCFree(&bin);
1330  }
1331 
1332  if (lastItem->moreData == TRUE) {
1333  smlTrace(TRACE_INTERNAL, "%s: Got item with moreData %i",
1334  __func__, cmd->msgID);
1335 
1336  SmlStatus *reply = smlCommandNewReply(cmd, SML_CHUNK_ACCEPTED, error);
1337  if (!reply)
1338  goto error_free_cmd;
1339 
1340  if (!smlSessionSendReply(session, reply, error))
1341  goto error_free_cmd;
1342 
1343  smlStatusUnref(reply);
1344 
1345  smlCommandUnref(cmd);
1346  break;
1347  }
1348  }
1349 
1350  /* if the command is a result, we treat it as a status */
1351  if (cmd->type == SML_COMMAND_TYPE_RESULTS) {
1352  /* Call the callback*/
1353  if (!smlSessionDispatchStatus(session, cmd->private.results.status, error))
1354  goto error_free_status;
1355  } else {
1356  /* Dispatch the command */
1357  cmd->msgID = session->lastReceivedMessageID;
1358  if (!session->end) {
1359  if (!session->parentCommand) {
1360  smlSessionDispatchEvent(session, SML_SESSION_EVENT_COMMAND_START, cmd, NULL, NULL, NULL);
1361  } else {
1362  smlSessionDispatchEvent(session, SML_SESSION_EVENT_CHILD_COMMAND, cmd, session->parentCommand, NULL, NULL);
1363  }
1364  } else {
1365  smlTrace(TRACE_INTERNAL, "%s: Replying with 407 since the session has ended2",
1366  __func__);
1367  SmlStatus *reply = smlCommandNewReply(cmd, SML_ERROR_AUTH_REQUIRED, error);
1368  if (!reply)
1369  goto error_free_cmd;
1370 
1371  if (!smlSessionSendReply(session, reply, error))
1372  goto error_free_cmd;
1373 
1374  smlStatusUnref(reply);
1375  }
1376  }
1377 
1378  smlCommandUnref(cmd);
1379  break;
1380  case SML_PARSER_RESULT_CLOSE:
1381  /* If it is a sync then it is necessary to signal it twice
1382  * because DsSession has an enabled write_lock.
1383  * If you add other command types here then please check
1384  * twice that the callback are ready to be called twice
1385  * (usually this requires some kind of statefulness).
1386  */
1387  if (session->parentCommand->type == SML_COMMAND_TYPE_SYNC) {
1388  smlSessionDispatchEvent(
1389  session,
1390  SML_SESSION_EVENT_COMMAND_END,
1391  session->parentCommand,
1392  NULL, NULL, NULL);
1393  }
1394  /* Remove the parent */
1395  smlCommandUnref(session->parentCommand);
1396  session->parentCommand = NULL;
1397  break;
1398  case SML_PARSER_RESULT_STATUS:
1399  goto parse_status;
1400  default:
1401  goto error_free_cmd;
1402  }
1403  }
1404 
1405  /* Check that the message closes correctly
1406  * A final tag is only send if the SyncML package is finished.
1407  * A package can consists of several messages if MaxMsgSize
1408  * is smaller then the exchange data.
1409  */
1410  SmlBool final = FALSE;
1411  SmlBool end = FALSE;
1412  if (!smlParserEnd(parser, &final, &end, error))
1413  goto error_free_cmd;
1414 
1415  /* Dispatch the final event before we enter the following loop. Otherwise if upper
1416  layers depend on the final event we might end in a deadlock. */
1417  if (final && !session->authenticate) {
1418  /* If the session is handling an authentication
1419  * then this must be transparent for the user.
1420  * This means that the user should not see
1421  * the FINAL event.
1422  *
1423  * Example: If the user flushs on FINAL then a FINAL
1424  * signal could crash the complete client
1425  * logic.
1426  */
1427  /* FIXME: Why do we activate session->sending here? */
1428  session->sending = TRUE;
1429  smlSessionDispatchEvent(session, SML_SESSION_EVENT_FINAL, NULL, NULL, NULL, NULL);
1430  }
1431 
1432  /* We have to wait at this point until no status are pending anymore
1433  * if there is an incoming buffer for large objs open,
1434  * the other side expects to see the status for the open buffer of course. so we have to
1435  * wait here for all open statuses, since they have to be added in the correct order and
1436  * the status for the open buffer is obviously last */
1437  while (1) {
1438  unsigned int cmdRef, msgRef;
1439  if (!smlAssemblerGetNextCmdRef(session->assembler, &cmdRef, &msgRef))
1440  break;
1441  smlTrace(TRACE_INTERNAL, "%s: command %d of message %d found",
1442  __func__, cmdRef, msgRef);
1443 
1444  /* yes, we do some kind busy wait here */
1445  usleep(10000);
1446 
1447  /* if there is nothing in the queue (num == 0) then this means
1448  * that all status and commands are dispatched but the result of one or
1449  * more triggered activities is still not available
1450  * (therefore smlAssemblerGetNextCmdRef returns a command).
1451  *
1452  * smlQueueLengthPrio returns missing status count
1453  */
1454  unsigned int num = smlQueueLengthPrio(session->command_queue);
1455  smlTrace(TRACE_INTERNAL, "%s: Now dispatching %i statuses. Session waiting: %i, prio: %i",
1456  __func__, num, session->waiting, smlQueueCheckPrio(session->command_queue));
1457 
1458  /* If the session does not wait for the remote peer (session->waiting)
1459  * and there is a missing status in the queue
1460  * then the message is dispatched again.
1461  * The message is dispatched at maximimum once per missing status (i < num).
1462  * If smlSessionDispatch blocks because the dispatch_mutex is already in use
1463  * and the situation changes (e.g. session->waiting is true)
1464  * then this is no problem because the function is safe against
1465  * mutliple calls (the SmlManager already requires this).
1466  */
1467  unsigned int i = 0;
1468  while (!session->waiting && smlQueueCheckPrio(session->command_queue) && i < num) {
1469  smlSessionDispatch(session);
1470  i++;
1471  }
1472  /* should we really call dispatch here or wait? */
1473  }
1474 
1475  /* A lot of checks are performed before the while loop is executed.
1476  * This is necessary because this while loop is only present for one
1477  * reason, a NEXT MESSAGE request from the remote peer if the original
1478  * message data does not fit into one message (MaxMsgSize to small).
1479  * The following stuff is checked:
1480  *
1481  * - the session must be in normal mode
1482  * - not sending or waiting
1483  * - no changed message counters
1484  * - there must be a waiting command (not a waiting status!)
1485  *
1486  * Usually this happens if an image is too large and must be splitted
1487  * (MaxMsgSize < MaxObjSize). Do not touch this stuff except that you
1488  * really test it with an image which must be send and it is correctly
1489  * splitted.
1490  */
1491  unsigned int receivedMsg = session->lastReceivedMessageID;
1492  unsigned int lastMsg = session->lastMessageID;
1493  while (!session->waiting && !session->sending &&
1494  smlQueueCheck(session->command_queue) &&
1495  smlQueueLength(session->command_queue) != smlQueueLengthPrio(session->command_queue) &&
1496  receivedMsg == session->lastReceivedMessageID &&
1497  lastMsg == session->lastMessageID)
1498  {
1499  /* If the session does not wait for the remote peer (session->waiting)
1500  * and there is a command in the queue
1501  * then the command is dispatched.
1502  * If the command is too large then the session is automatically
1503  * flushed. If all commands were dispatched then the queue check
1504  * fails and the normal mechanism for sending messages flushes
1505  * the session or do some other actions.
1506  */
1507  smlQueueDispatch(session->command_queue);
1508  }
1509 
1510  smlTrace(TRACE_INTERNAL, "%s: Final %i, waiting %i, sending %i",
1511  __func__, final, session->waiting, session->sending);
1512  if (session->authenticate) {
1513  /* Do nothing. Simply send the message again.
1514  * Please note that final and end MUST be ignored.
1515  */
1516  smlTrace(TRACE_INTERNAL, "%s: ongoing authentication", __func__);
1517  }
1518  else if (final) {
1519  /* Final means that the received message is the last message
1520  * of this SyncML package. When a final is received then the
1521  * library should not flush the session automatically because
1522  * the user of the library usually starts some special action.
1523  *
1524  * This can be for example a new package or some kind of
1525  * background action which results in informations expected by
1526  * the remote peer in the next message.
1527  * Example: OMA DS client (remote peer) sends a SYNC package
1528  * and expects that the OMA DS server sends the server
1529  * modifications at minimum after the last client
1530  * modification.
1531  *
1532  * So again the library user must flush the session (e.g. handle
1533  * the SML_MANAGER_SESSION_FINAL event).
1534  */
1535  if (session->pendingReplies) {
1536  /* If final is reached then there should be no
1537  * pendingReplies anymore. The problem is that some
1538  * commands (especially SYNC) results in new commands.
1539  * Nevertheless we have to check for old pendingReplies
1540  * and this we try to do here.
1541  *
1542  * Yes, this is some kind of bug detection.
1543  */
1544  GList *o = NULL;
1545  for (o = session->pendingReplies; o; o = o->next) {
1546  SmlPendingStatus *pending = o->data;
1547  /* This is an asynchronous library but the
1548  * message numbering and sending is a serial
1549  * operation. So pendingReplies should only
1550  * exist for the last message. Anything else
1551  * should be a bug.
1552  * The problem is that a race condition can
1553  * appear here. If the user of the library
1554  * gets the FINAL event and flushs the session
1555  * immediately then the flush function will
1556  * send the message and increment the counter
1557  * lastMessageID. So lastMessageID is in this
1558  * case one number too high. The solution is to
1559  * detect this event by the state of the
1560  * session via the status variable waiting.
1561  */
1562  if ((!session->waiting && pending->msgID < session->lastMessageID) ||
1563  (session->waiting && pending->msgID < session->lastMessageID - 1))
1564  {
1565  smlErrorSet(error, SML_ERROR_GENERIC, "Didn't receive a reply for pending status (actual message %i, received message %i, cmdID %i, msgID %i)", session->lastMessageID, session->lastReceivedMessageID, pending->cmdID, pending->msgID);
1566  goto error;
1567  }
1568  }
1569  }
1570 
1571  /* The incomingBuffer is used to store the last unhandled item.
1572  * The incoming buffer can be filled after a message is
1573  * completely handled if an object is too large for a message.
1574  * Usually this happens if you add images to your contacts ;)
1575  * The remote peer sends the moreData element in this case.
1576  *
1577  * Nevertheless if final is received then the package is
1578  * finished and so there cannot be any more data which is not
1579  * send by the client.
1580  *
1581  * Summary: there is a bug in this library or in the remote peer.
1582  */
1583  if (session->incomingBuffer) {
1584  smlErrorSet(error, SML_ERROR_GENERIC, "Incoming buffer was open when final arrived");
1585  smlCommandUnref(session->incomingBuffer);
1586  session->incomingBuffer = NULL;
1587  goto error;
1588  }
1589  } else if (!session->waiting && !session->sending) {
1590  /* If we don't wait already for an answer from the remote peer
1591  * (perhaps because a user of this library directly called
1592  * smlSessionFlush which causes a send operation of the
1593  * transport layer) and if we are waiting for or already
1594  * receiving a package (in terms of OMA DS specifications)
1595  * then we should send the collected stuff to the remote peer.
1596  */
1597  smlTrace(TRACE_INTERNAL, "%s: This is not the final message. Empty? %i",
1598  __func__, smlAssemblerIsEmpty(session->assembler));
1599  /* The received message was not a final message. So this is
1600  * only a reaction to a part from a package. Sometimes such
1601  * replies only contain status but a message must include a
1602  * command.
1603  */
1604  if (smlAssemblerIsEmpty(session->assembler)) {
1605  /* The assembler is empty. This means there is no
1606  * command included which is a requirement.
1607  * The solution is simple - an alert called next
1608  * message is added. this special alert does not need
1609  * a source or target because it only signals that the
1610  * remote peer should continue its work.
1611  */
1612  SmlCommand *alert = smlCommandNewAlert(SML_ALERT_NEXT_MESSAGE, NULL, NULL, NULL, NULL, NULL, error);
1613  if (!alert)
1614  goto error;
1615 
1616  if (_smlSessionStartCommandInternal(session, alert, NULL, NULL, NULL, TRUE, error) != SML_ASSEMBLER_RESULT_OK)
1617  goto error;
1618  if (!_smlSessionEndCommandInternal(session, NULL, error))
1619  goto error;
1620 
1621  smlCommandUnref(alert);
1622  }
1623  /* Anything is fine and nobody else want to send a reply
1624  * to the received message. So the library flushs now.
1625  */
1626  if (!_smlSessionFlushInternal(session, FALSE, error))
1627  goto error;
1628  }
1629 
1630  if (session->authenticate) {
1631  /* Do nothing. Simply send the message again. */
1632  smlTrace(TRACE_INTERNAL, "%s: ongoing authentication", __func__);
1633  /* session->end signals true because an authentication request
1634  * is signaled via status/error 407 as answer to all commands.
1635  */
1636  session->end = FALSE;
1637  smlTrace(TRACE_INTERNAL, "%s: flushing server finally",
1638  __func__);
1639  if (!_smlSessionFlushInternal(session, TRUE, error))
1640  goto error;
1641  /* reset authenticate only after flush
1642  * because we must surpress the FLUSH event.
1643  */
1644  session->authenticate = FALSE;
1645  } else if (end) {
1646  /* end means the last message does not contain a command which
1647  * is clearly illegal in SyncML except that the remote party
1648  * thinks that the device synchronization or management action
1649  * is successfully completed.
1650  *
1651  * The library can close the session now.
1652  */
1653  smlTrace(TRACE_INTERNAL, "%s: Ending session now", __func__);
1654  session->end = TRUE;
1655 
1656  /* If the OMA DS or DM session ends and a correct FINAL was
1657  * received then clients should simply end and servers should do
1658  * a final flush according to OMA DS spec.
1659  *
1660  * This only makes sense after a received FINAL because
1661  * otherwise the session was already flushed in some way. This
1662  * can happen if the user of the library takes action because of
1663  * an event.
1664  */
1665  if (final)
1666  {
1667  smlTrace(TRACE_INTERNAL, "%s: session end on final",
1668  __func__);
1669  if (session->sessionType == SML_SESSION_TYPE_SERVER)
1670  {
1671  smlTrace(TRACE_INTERNAL, "%s: flushing server finally",
1672  __func__);
1673  if (!_smlSessionFlushInternal(session, TRUE, error))
1674  goto error;
1675  }
1676  } else {
1677  if (!session->waiting)
1678  {
1679  /* The library does not send a message
1680  * after it received the last message
1681  * and neither the remote peer nor the
1682  * library send a final. This means
1683  * there is an active package. This is
1684  * an error of the library or the remote
1685  * peer.
1686  */
1687  smlErrorSet(error, SML_ERROR_BAD_REQUEST,
1688  "The message from the remote peer does not contain a command.");
1689  goto error;
1690  }
1691  }
1692  /* Send the library user the signal that the session ends now
1693  * and every use of this session will result in an undefined
1694  * behaviour.
1695  * If this is a server which received a final
1696  * then FlushInternal already signalled the session end.
1697  */
1698  if (!final || session->sessionType != SML_SESSION_TYPE_SERVER)
1699  {
1700  g_mutex_lock(session->reportEnd);
1701  if (!session->reportedEnd) {
1702  /* The end should only be reported once. */
1703  session->reportedEnd = TRUE;
1704  smlSessionRestoreTargetURI(session);
1705  smlSessionDispatchEvent(session, SML_SESSION_EVENT_END,
1706  NULL, NULL, NULL, NULL);
1707  }
1708  g_mutex_unlock(session->reportEnd);
1709  }
1710  }
1711 
1712  smlTrace(TRACE_EXIT, "%s", __func__);
1713  return TRUE;
1714 
1715 error_free_cmd:
1716  if (cmd)
1717  smlCommandUnref(cmd);
1718 error_free_status:
1719  if (status)
1720  smlStatusUnref(status);
1721 error:
1722  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1723  return FALSE;
1724 }
1725 
1726 void smlSessionSetEventCallback(SmlSession *session, SmlSessionEventCallback callback, void *userdata)
1727 {
1728  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, callback, userdata);
1729  smlAssert(session);
1730 
1731  session->eventCallback = callback;
1732  session->eventCallbackUserdata = userdata;
1733 
1734  smlTrace(TRACE_EXIT, "%s", __func__);
1735 }
1736 
1737 void smlSessionSetDataCallback(SmlSession *session, SmlSessionDataCallback callback, void *userdata)
1738 {
1739  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, callback, userdata);
1740  smlAssert(session);
1741 
1742  session->dataCallback = callback;
1743  session->dataCallbackUserdata = userdata;
1744 
1745  smlTrace(TRACE_EXIT, "%s", __func__);
1746 }
1747 
1748 const char *smlSessionGetSessionID(SmlSession *session)
1749 {
1750  smlAssert(session);
1751 
1752  return session->sessionID;
1753 }
1754 
1755 void smlSessionSetSessionID(SmlSession *session, const char *sessionID)
1756 {
1757  smlAssert(session);
1758 
1759  if (session->sessionID)
1760  smlSafeCFree(&(session->sessionID));
1761  session->sessionID = g_strdup(sessionID);
1762 }
1763 
1764 SmlProtocolVersion smlSessionGetVersion(SmlSession *session)
1765 {
1766  smlAssert(session);
1767 
1768  return session->version;
1769 }
1770 
1771 SmlLocation *smlSessionGetSource(SmlSession *session)
1772 {
1773  smlAssert(session);
1774  return session->source;
1775 }
1776 
1777 SmlLocation *smlSessionGetTarget(SmlSession *session)
1778 {
1779  smlAssert(session);
1780  return session->target;
1781 }
1782 
1783 void smlSessionRegisterCred(SmlSession *session, SmlCred *cred)
1784 {
1785  smlAssert(session);
1786  smlAssert(session->sessionType == SML_SESSION_TYPE_CLIENT);
1787  smlAssert(cred);
1788  if (session->cred)
1789  smlCredUnref(session->cred);
1790  session->cred = cred;
1791  if (cred->username)
1792  smlLocationSetName(session->source, cred->username);
1793  smlCredRef(session->cred);
1794 }
1795 
1807 
1813 
1832 SmlSession *smlSessionNew(SmlSessionType sessionType, SmlMimeType mimetype, SmlProtocolVersion version, SmlProtocolType protocol, SmlLocation *target, SmlLocation *source, const char *sessionID, unsigned int messageID, SmlError **error)
1833 {
1834  smlTrace(TRACE_ENTRY, "%s(%i, %i, %i, %i, %p, %p, %s, %i, %p)", __func__, sessionType, mimetype, version, protocol, target, source, VA_STRING(sessionID), messageID, error);
1835  CHECK_ERROR_REF
1836 
1837  /* Initialize GLib thread system. */
1838  if (!g_thread_supported ()) g_thread_init (NULL);
1839 
1840  /* Malloc the new session object */
1841  SmlSession *session = smlTryMalloc0(sizeof(SmlSession), error);
1842  if (!session)
1843  goto error;
1844 
1845  session->dispatch_mutex = g_mutex_new();
1846  session->refCount = 1;
1847  session->eventCallback = NULL;
1848  session->version = version;
1849  session->protocol = protocol;
1850  session->type = mimetype;
1851  session->sessionID = g_strdup(sessionID);
1852  if (messageID)
1853  session->lastMessageID = messageID;
1854  else
1855  session->lastMessageID = 1;
1856 
1857  session->end = FALSE;
1858  session->reportEnd = g_mutex_new();
1859  session->reportedEnd = FALSE;
1860  session->sessionType = sessionType;
1861  session->lastCommandID = 0;
1862 
1863  if (sessionType == SML_SESSION_TYPE_CLIENT)
1864  session->sending = TRUE;
1865  else
1866  session->sending = FALSE;
1867 
1868  session->source = source;
1869  smlLocationRef(source);
1870 
1871  session->target = target;
1872  smlLocationRef(target);
1873 
1874  session->cred = NULL;
1875  session->chal = NULL;
1876 
1877  /* Create the assembler that will parse messages send
1878  * from this session */
1879  session->assembler = smlAssemblerNew(mimetype, 0, error);
1880  if (!session->assembler)
1881  goto error_free_session;
1882  smlAssemblerSetOption(session->assembler, "USE_STRTABLE", "1");
1883 
1884  if (!smlAssemblerStart(session->assembler, session, error))
1885  goto error_free_session;
1886 
1887  /* Do not call smlAssemblerAddHeader here. It is nice to know
1888  * if the header is is too large for the maximum message size
1889  * but there is sometimes not enough information to pass all
1890  * sanity checks of the assembler at this time (e.g. missing
1891  * local MaxMsgSize).
1892  */
1893  session->assmHasHeader = FALSE;
1894 
1895  session->command_queue = smlQueueNew(error);
1896  if (!session->command_queue)
1897  goto error_free_session;
1898  smlQueueSetHandler(session->command_queue, (SmlQueueHandler)_smlSessionCommandHandler, session);
1899 
1900  smlTrace(TRACE_EXIT, "%s: %p", __func__, session);
1901  return session;
1902 
1903 error_free_session:
1904  smlSessionUnref(session);
1905 error:
1906  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1907  return NULL;
1908 }
1909 
1910 SmlSession *smlSessionRef(SmlSession *session)
1911 {
1912  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, session);
1913  smlAssert(session);
1914 
1915  g_atomic_int_inc(&(session->refCount));
1916 
1917  smlTrace(TRACE_EXIT, "%s: New refcount: %i", __func__, session->refCount);
1918  return session;
1919 }
1920 
1921 void smlSessionUnref(SmlSession *session)
1922 {
1923  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, session);
1924  smlAssert(session);
1925 
1926  if (g_atomic_int_dec_and_test(&(session->refCount))) {
1927  smlTrace(TRACE_INTERNAL, "%s: Refcount == 0!", __func__);
1928 
1929  if (session->target)
1930  smlLocationUnref(session->target);
1931 
1932  if (session->responseURI)
1933  smlSafeCFree(&(session->responseURI));
1934 
1935  if (session->source)
1936  smlLocationUnref(session->source);
1937 
1938  if (session->cred)
1939  smlCredUnref(session->cred);
1940 
1941  if (session->chal)
1942  smlChalUnref(session->chal);
1943 
1944  if (session->sessionID)
1945  smlSafeCFree(&(session->sessionID));
1946 
1947  if (session->command_queue) {
1948  SmlSessionCommand *sesscmd = NULL;
1949  while ((sesscmd = smlQueueTryPop(session->command_queue)))
1950  _smlSessionCommandFree(sesscmd);
1951 
1952  smlQueueFree(session->command_queue);
1953  }
1954 
1955  _smlSessionFreePendingReplies(session);
1956 
1957  if (session->assembler)
1958  smlAssemblerFree(session->assembler);
1959 
1960  if (session->incomingBuffer) {
1961  smlTrace(TRACE_INTERNAL, "%s: There is an unhandled incoming buffer.", __func__);
1962  smlCommandUnref(session->incomingBuffer);
1963  }
1964 
1965  if (session->parentCommand)
1966  smlCommandUnref(session->parentCommand);
1967 
1968  smlTrace(TRACE_INTERNAL, "%s: cleanup dispatch mutex", __func__);
1969  if (!g_mutex_trylock(session->dispatch_mutex))
1970  g_warning("%s: The session is still locked for dispatching - forcing unlock/free.",
1971  __func__);
1972  g_mutex_unlock(session->dispatch_mutex);
1973  g_mutex_free(session->dispatch_mutex);
1974  smlTrace(TRACE_INTERNAL, "%s: cleanup reportEnd mutex", __func__);
1975  /* It is no bug if reportEnd is not used. */
1976  if (!g_mutex_trylock(session->reportEnd))
1977  g_warning("%s: The reportEnd is still locked to set reportEnd - forcing unlock/free.",
1978  __func__);
1979  g_mutex_unlock(session->reportEnd);
1980  g_mutex_free(session->reportEnd);
1981 
1982  smlSafeFree((gpointer *)&session);
1983  }
1984 
1985  smlTrace(TRACE_EXIT, "%s", __func__);
1986 }
1987 
1995 
2010 SmlBool smlSessionFlush(SmlSession *session, SmlBool final, SmlError **error)
2011 {
2012  smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, session, final, error);
2013  smlAssert(session);
2014  CHECK_ERROR_REF
2015 
2016  SmlSessionCommand *sesscmd = smlTryMalloc0(sizeof(SmlSessionCommand), error);
2017  if (!sesscmd)
2018  goto error;
2019 
2020  sesscmd->type = SML_SESSION_FLUSH;
2021  sesscmd->final = final;
2022 
2023  smlTrace(TRACE_INTERNAL, "%s: sending flush (%i) command %p via queue %p",
2024  __func__, SML_SESSION_FLUSH, sesscmd, session->command_queue);
2025 
2026  smlQueueSend(session->command_queue, sesscmd);
2027 
2028  // session is still active even if the other side sent only status
2029  session->end = FALSE;
2030 
2031  smlTrace(TRACE_EXIT, "%s", __func__);
2032  return TRUE;
2033 
2034 error:
2035  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2036  return FALSE;
2037 }
2038 
2057 SmlBool smlSessionEnd(SmlSession *session, SmlError **error)
2058 {
2059  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, session, error);
2060  smlAssert(session);
2061  CHECK_ERROR_REF
2062 
2063  g_mutex_lock(session->reportEnd);
2064  if (session->reportedEnd)
2065  {
2066  /* If the end was already reported
2067  * then this function should not be called.
2068  * This can happen because of race conditions
2069  * during the event handling. So this event
2070  * is not necessarily a bug.
2071  */
2072  g_mutex_unlock(session->reportEnd);
2073  smlTrace(TRACE_EXIT, "%s - session end already signalled.", __func__);
2074  return TRUE;
2075  }
2076  session->reportedEnd = TRUE;
2077 
2078  /* If the session received all status and has no command ready
2079  * to send then it makes no sense to flush. Such situations can
2080  * appear if the user opens a connection to a SyncML server but
2081  * after the connect the user notices that there is nothing to
2082  * synchronize.
2083  *
2084  * The only exception is a server which received a map from the
2085  * client as answer for a sync command. The server must send an
2086  * empty message to the client. If the client sends no map
2087  * then this is handled by the function ReceiveBody.
2088  */
2089  if (smlSessionCheck(session) ||
2090  (!session->end && session->lastCommandID))
2091  {
2092  /* The session end is reported by the internal
2093  * flush function in this situation.
2094  */
2095  session->reportedEnd = FALSE;
2096 
2097  SmlSessionCommand *sesscmd = smlTryMalloc0(sizeof(SmlSessionCommand), error);
2098  if (!sesscmd)
2099  goto error;
2100 
2101  sesscmd->type = SML_SESSION_FLUSH;
2102  sesscmd->final = TRUE;
2103  sesscmd->end = TRUE;
2104  smlTrace(TRACE_INTERNAL, "%s: sending command %p", sesscmd);
2105 
2106  smlQueueSend(session->command_queue, sesscmd);
2107  } else {
2108  /* If there is nothing to synchronize
2109  * then simply signal the end.
2110  */
2111  smlTrace(TRACE_INTERNAL, "%s: no status/command ... only sending event", __func__);
2112  smlSessionRestoreTargetURI(session);
2113  smlSessionDispatchEvent(session, SML_SESSION_EVENT_END, NULL, NULL, NULL, NULL);
2114  }
2115 
2116  g_mutex_unlock(session->reportEnd);
2117  smlTrace(TRACE_EXIT, "%s", __func__);
2118  return TRUE;
2119 
2120 error:
2121  g_mutex_unlock(session->reportEnd);
2122  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2123  return FALSE;
2124 }
2125 
2126 /* This is for the client side of ResponseURI support. */
2127 void smlSessionRestoreTargetURI(SmlSession *session)
2128 {
2129  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, session);
2130  smlAssert(session);
2131 
2132  if (session->orgTarget) {
2133  /* Restore original target if RespURI was used.
2134  * This is unimportant because every new session
2135  * must use a new link (connection).
2136  */
2137  smlLocationUnref(session->target);
2138  session->target = session->orgTarget;
2139  session->orgTarget = NULL;
2140  /* Do not try to reset the ResponseURI via the session
2141  * event because the original Target URI is very often
2142  * no URL which causes an error in the transport layer.
2143  */
2144  }
2145 
2146  smlTrace(TRACE_EXIT, "%s", __func__);
2147 }
2148 
2149 /* This is for the server side of ResponseURI support. */
2150 SmlBool smlSessionSetResponseURI(
2151  SmlSession *session,
2152  const char *responseURI,
2153  SmlError **error)
2154 {
2155  smlTrace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, session, VA_STRING(responseURI), error);
2156  CHECK_ERROR_REF
2157 
2158  if (session->responseURI) {
2159  g_warning("The response URI (%s) of a session (%s) should never be overwritten (%s).",
2160  session->responseURI, session->sessionID, responseURI);
2161  smlSafeCFree(&(session->responseURI));
2162  }
2163 
2164  session->responseURI = g_strdup(responseURI);
2165 
2166  smlTrace(TRACE_EXIT, "%s", __func__);
2167  return TRUE;
2168 }
2169 
2170 void smlSessionUseStringTable(SmlSession *session, SmlBool useStringtable)
2171 {
2172  smlTrace(TRACE_ENTRY, "%s(%p, %i)", __func__, session, useStringtable);
2173  smlAssert(session);
2174 
2175  smlAssemblerSetOption(session->assembler, "USE_STRTABLE", useStringtable ? "1" : "0");
2176 
2177  smlTrace(TRACE_EXIT, "%s", __func__);
2178 }
2179 
2180 void smlSessionUseNumberOfChanges(SmlSession *session, SmlBool support)
2181 {
2182  smlTrace(TRACE_ENTRY, "%s(%p, %i)", __func__, session, support);
2183  smlAssert(session);
2184 
2185  smlAssemblerSetOption(session->assembler, "USE_NUMBEROFCHANGES", support ? "1" : "0");
2186 
2187  smlTrace(TRACE_EXIT, "%s", __func__);
2188 }
2189 
2190 void smlSessionUseLargeObjects(SmlSession *session, SmlBool support)
2191 {
2192  smlTrace(TRACE_ENTRY, "%s(%p, %i)", __func__, session, support);
2193  smlAssert(session);
2194 
2195  smlAssemblerSetOption(session->assembler, "USE_LARGEOBJECTS", support ? "1" : "0");
2196 
2197  smlTrace(TRACE_EXIT, "%s", __func__);
2198 }
2199 
2200 void smlSessionUseOnlyReplace(SmlSession *session, SmlBool onlyReplace)
2201 {
2202  smlTrace(TRACE_ENTRY, "%s(%p, %i)", __func__, session, onlyReplace);
2203  smlAssert(session);
2204 
2205  smlAssemblerSetOption(session->assembler, "ONLY_REPLACE", onlyReplace ? "1" : "0");
2206  session->onlyReplace = onlyReplace;
2207 
2208  smlTrace(TRACE_EXIT, "%s", __func__);
2209 }
2210 
2211 /* FIXME: DEPRECATED */
2212 void smlSessionSetSendingLimit(SmlSession *session, unsigned int limit)
2213 {
2214  smlSessionSetRemoteMaxMsgSize(session, limit);
2215 }
2216 
2217 /* FIXME: DEPRECATED */
2218 unsigned int smlSessionGetSendingLimit(SmlSession *session)
2219 {
2220  return smlSessionGetRemoteMaxMsgSize(session);
2221 }
2222 
2223 /* FIXME: DEPRECATED */
2224 void smlSessionSetReceivingLimit(SmlSession *session, unsigned int limit)
2225 {
2226  smlSessionSetLocalMaxMsgSize(session, limit);
2227 }
2228 
2229 /* FIXME: DEPRECATED */
2230 void smlSessionSetSendingMaxMsgSize(SmlSession *session, unsigned int size)
2231 {
2232  smlSessionSetRemoteMaxMsgSize(session, size);
2233 }
2234 
2235 /* FIXME: DEPRECATED */
2236 unsigned int smlSessionGetSendingMaxMsgSize(SmlSession *session)
2237 {
2238  return smlSessionGetRemoteMaxMsgSize(session);
2239 }
2240 
2241 /* FIXME: DEPRECATED */
2242 void smlSessionSetReceivingMaxMsgSize(SmlSession *session, unsigned int size)
2243 {
2244  smlSessionSetLocalMaxMsgSize(session, size);
2245 }
2246 
2247 void smlSessionSetRemoteMaxMsgSize(SmlSession *session, unsigned int size)
2248 {
2249  smlTrace(TRACE_ENTRY, "%s(%p, %u)", __func__, session, size);
2250  smlAssert(session);
2251 
2252  smlAssemblerSetRemoteMaxMsgSize(session->assembler, size);
2253 
2254  smlTrace(TRACE_EXIT, "%s", __func__);
2255 }
2256 
2257 unsigned int smlSessionGetRemoteMaxMsgSize(SmlSession *session)
2258 {
2259  smlAssert(session);
2260  return smlAssemblerGetRemoteMaxMsgSize(session->assembler);
2261 }
2262 
2263 void smlSessionSetLocalMaxMsgSize(SmlSession *session, unsigned int size)
2264 {
2265  smlTrace(TRACE_ENTRY, "%s(%p, %u)", __func__, session, size);
2266  smlAssert(session);
2267 
2268  if (! smlSessionGetRemoteMaxMsgSize(session) > 0 ||
2269  smlSessionGetRemoteMaxMsgSize(session) > size)
2270  {
2271  session->localMaxMsgSize = size;
2272  } else {
2273  session->localMaxMsgSize = smlSessionGetRemoteMaxMsgSize(session);
2274  }
2275 
2276  smlTrace(TRACE_EXIT, "%s", __func__);
2277 }
2278 
2279 unsigned int smlSessionGetLocalMaxMsgSize(SmlSession *session)
2280 {
2281  smlAssert(session);
2282  return session->localMaxMsgSize;
2283 }
2284 
2285 /* FIXME: DEPRECATED */
2286 void smlSessionSetSendingMaxObjSize(SmlSession *session, int limit)
2287 {
2288  smlSessionSetRemoteMaxObjSize(session, limit);
2289 }
2290 
2291 /* FIXME: DEPRECATED */
2292 void smlSessionSetRequestedMaxObjSize(SmlSession *session, int limit)
2293 {
2294  smlSessionSetRemoteMaxObjSize(session, limit);
2295 }
2296 
2297 /* FIXME: DEPRECATED */
2298 int smlSessionGetSendingMaxObjSize(SmlSession *session)
2299 {
2300  return smlSessionGetRemoteMaxObjSize(session);
2301 }
2302 
2303 /* FIXME: DEPRECATED */
2304 void smlSessionSetReceivingMaxObjSize(SmlSession *session, unsigned int limit)
2305 {
2306  smlSessionSetLocalMaxObjSize(session, limit);
2307 }
2308 
2309 /* FIXME: DEPRECATED */
2310 unsigned int smlSessionGetReceivingMaxObjSize(SmlSession *session)
2311 {
2312  return smlSessionGetLocalMaxObjSize(session);
2313 }
2314 
2325 void smlSessionSetRemoteMaxObjSize(SmlSession *session, unsigned int limit)
2326 {
2327  smlTrace(TRACE_ENTRY, "%s(%p, %i)", __func__, session, limit);
2328  smlAssert(session);
2329 
2330  smlAssemblerSetRemoteMaxObjSize(session->assembler, limit);
2331 
2332  smlTrace(TRACE_EXIT, "%s", __func__);
2333 }
2334 
2340 {
2341  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, session);
2342  smlAssert(session);
2343 
2344  int result = smlAssemblerGetRemoteMaxObjSize(session->assembler);
2345 
2346  smlTrace(TRACE_EXIT, "%s => %i", __func__, result);
2347  return result;
2348 }
2349 
2363 void smlSessionSetLocalMaxObjSize(SmlSession *session, unsigned int limit)
2364 {
2365  smlTrace(TRACE_ENTRY, "%s(%p, %u)", __func__, session, limit);
2366  smlAssert(session);
2367 
2368  int remoteLimit = smlSessionGetRemoteMaxObjSize(session);
2369 
2370  if (remoteLimit > 0)
2371  {
2372  /* The remote peer already sent MaxObjSize */
2373  if ((unsigned int)remoteLimit > limit && limit > 0)
2374  {
2375  /* If the limit of the remote peer is higher
2376  * than the local limit then the local limit
2377  * must be used.
2378  */
2379  session->localMaxObjSize = limit;
2380  } else {
2381  /* If the remote limit is not higher than
2382  * the local limit or of the local limit is
2383  * not set then the remote limit is used as
2384  * the local limit.
2385  */
2386  session->localMaxObjSize = remoteLimit;
2387  }
2388  } else {
2389  /* If there is no remote limit then the local limit is used. */
2390  session->localMaxObjSize = limit;
2391  }
2392 
2393  smlTrace(TRACE_EXIT, "%s => %d", __func__, session->localMaxObjSize);
2394 }
2395 
2396 unsigned int smlSessionGetLocalMaxObjSize(SmlSession *session)
2397 {
2398  smlAssert(session);
2399  return session->localMaxObjSize;
2400 }
2401 
2422 SmlBool smlSessionSendCommand(SmlSession *session, SmlCommand *cmd, SmlCommand *parent, SmlStatusReplyCb callback, void *userdata, SmlError **error)
2423 {
2424  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p, %p)", __func__, session, cmd, parent, callback, userdata, error);
2425  smlAssert(session);
2426  smlAssert(cmd);
2427  CHECK_ERROR_REF
2428 
2429  if (!smlSessionStartCommand(session, cmd, parent, callback, userdata, error))
2430  goto error;
2431 
2432  if (!smlSessionEndCommand(session, parent, error))
2433  goto error;
2434 
2435  smlTrace(TRACE_EXIT, "%s", __func__);
2436  return TRUE;
2437 
2438 error:
2439  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2440  return FALSE;
2441 }
2442 
2454 SmlBool smlSessionStartCommand(SmlSession *session, SmlCommand *cmd, SmlCommand *parent, SmlStatusReplyCb callback, void *userdata, SmlError **error)
2455 {
2456  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p, %p, %p)", __func__, session, cmd, parent, callback, userdata, error);
2457  smlAssert(session);
2458  smlAssert(cmd);
2459  CHECK_ERROR_REF
2460 
2461  SmlSessionCommand *sesscmd = smlTryMalloc0(sizeof(SmlSessionCommand), error);
2462  if (!sesscmd)
2463  goto error;
2464 
2465  sesscmd->type = SML_SESSION_COMMAND_START;
2466  sesscmd->cmd = cmd;
2467  smlCommandRef(cmd);
2468 
2469  if (parent) {
2470  sesscmd->parent = parent;
2471  smlCommandRef(parent);
2472  }
2473 
2474  sesscmd->callback = callback;
2475  sesscmd->callbackUserdata = userdata;
2476 
2477  smlQueueSend(session->command_queue, sesscmd);
2478 
2479  smlTrace(TRACE_EXIT, "%s", __func__);
2480  return TRUE;
2481 
2482 error:
2483  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2484  return FALSE;
2485 }
2486 
2495 SmlBool smlSessionEndCommand(SmlSession *session, SmlCommand *parent, SmlError **error)
2496 {
2497  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, parent, error);
2498  smlAssert(session);
2499  CHECK_ERROR_REF
2500 
2501  SmlSessionCommand *sesscmd = smlTryMalloc0(sizeof(SmlSessionCommand), error);
2502  if (!sesscmd)
2503  goto error;
2504 
2505  sesscmd->type = SML_SESSION_COMMAND_END;
2506 
2507  if (parent) {
2508  sesscmd->parent = parent;
2509  smlCommandRef(parent);
2510  }
2511 
2512  smlQueueSend(session->command_queue, sesscmd);
2513 
2514  smlTrace(TRACE_EXIT, "%s", __func__);
2515  return TRUE;
2516 
2517 error:
2518  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2519  return FALSE;
2520 }
2521 
2522 
2536 SmlBool smlSessionSendReply(SmlSession *session, SmlStatus *status, SmlError **error)
2537 {
2538  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, status, error);
2539  smlAssert(session);
2540  smlAssert(status);
2541  CHECK_ERROR_REF
2542 
2543  SmlSessionCommand *sesscmd = smlTryMalloc0(sizeof(SmlSessionCommand), error);
2544  if (!sesscmd)
2545  goto error;
2546 
2547  sesscmd->type = SML_SESSION_STATUS;
2548  sesscmd->status = status;
2549  smlStatusRef(status);
2550 
2551  smlQueueSendPrio(session->command_queue, sesscmd);
2552 
2553  smlTrace(TRACE_EXIT, "%s", __func__);
2554  return TRUE;
2555 
2556 error:
2557  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
2558  return FALSE;
2559 }
2560 
void smlAssemblerSetOption(SmlAssembler *assm, const char *optionname, const char *value)
Sets a option on the assembler.
Definition: sml_parse.c:896
SmlBool smlAssemblerAddHeader(SmlAssembler *assm, SmlSession *session, SmlError **error)
Assembles the header.
Definition: sml_parse.c:762
SmlBool smlSessionSendReply(SmlSession *session, SmlStatus *status, SmlError **error)
Sends a reply to a command.
Definition: sml_session.c:2536
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
SmlErrorType type
void smlSessionSetLocalMaxObjSize(SmlSession *session, unsigned int limit)
Definition: sml_session.c:2363
SmlBool smlSessionFlush(SmlSession *session, SmlBool final, SmlError **error)
Flushes a session.
Definition: sml_session.c:2010
SmlAssemblerResult smlAssemblerStartCommand(SmlAssembler *assm, SmlCommand *parent, SmlCommand *cmd, SmlError **error)
Starts a parent command.
Definition: sml_parse.c:659
SmlAssemblerResult smlAssemblerAddStatusFull(SmlAssembler *assm, SmlStatus *status, SmlBool force, SmlError **error)
Assembles a status.
Definition: sml_parse.c:839
SmlBool smlSessionEndCommand(SmlSession *session, SmlCommand *parent, SmlError **error)
ends a command
Definition: sml_session.c:2495
unsigned int smlSessionGetRemoteMaxObjSize(SmlSession *session)
Definition: sml_session.c:2339
void smlQueueSend(SmlQueue *queue, void *data)
Sends a message down a queue.
Definition: sml_queue.c:353
void smlQueueSetHandler(SmlQueue *queue, SmlQueueHandler handler, void *userdata)
Sets the message handler for a queue.
Definition: sml_queue.c:384
SmlAssemblerResult smlAssemblerReserveStatus(SmlAssembler *assm, unsigned int cmdRef, unsigned int msgRef, unsigned int cmdID, SmlError **error)
Reserves a place for a status with this assembler.
Definition: sml_parse.c:806
char * smlPrintBinary(const char *data, int len)
Used for printing binary data.
Definition: sml_support.c:252
void smlSessionSetRemoteMaxObjSize(SmlSession *session, unsigned int limit)
Definition: sml_session.c:2325
SmlQueue * smlQueueNew(SmlError **error)
Creates a new asynchronous queue.
Definition: sml_queue.c:81
SmlBool smlParserEnd(SmlParser *parser, SmlBool *final, SmlBool *end, SmlError **error)
Ends the parsing.
Definition: sml_parse.c:314
SmlBool smlAssemblerRun(SmlAssembler *assm, char **data, unsigned int *size, SmlBool *end, SmlBool final, SmlError **error)
Assembles the complete message and returns the result.
Definition: sml_parse.c:456
SmlBool smlSessionEnd(SmlSession *session, SmlError **error)
Ends the session.
Definition: sml_session.c:2057
unsigned int smlAssemblerFlush(SmlAssembler *assm)
Flushes the already parsed commands.
Definition: sml_parse.c:514
SmlBool smlAssemblerStart(SmlAssembler *assm, SmlSession *session, SmlError **error)
Starts a new message.
Definition: sml_parse.c:428
SmlBool smlAssemblerIsStatusMissing(SmlAssembler *assm)
Checks if there are reserved statuses missing.
Definition: sml_parse.c:1053
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 smlItemGetData(SmlItem *item, char **data, unsigned int *size, SmlError **error)
Definition: sml_elements.c:495
SmlBool smlItemCheck(SmlItem *item)
Definition: sml_elements.c:444
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
void smlAssemblerFree(SmlAssembler *assm)
Frees a assembler.
Definition: sml_parse.c:402
SmlBool smlAssemblerEndCommand(SmlAssembler *assm, SmlCommand *parent, SmlError **error)
Ends a parent command.
Definition: sml_parse.c:722
SmlBool smlSessionStartCommand(SmlSession *session, SmlCommand *cmd, SmlCommand *parent, SmlStatusReplyCb callback, void *userdata, SmlError **error)
starts a command with a parent
Definition: sml_session.c:2454
SmlBool smlParserGetStatus(SmlParser *parser, SmlStatus **status, SmlError **error)
Parses the next status.
Definition: sml_parse.c:284
SmlAssembler * smlAssemblerNew(SmlMimeType type, unsigned int limit, SmlError **error)
Creates a new assembler.
Definition: sml_parse.c:352
SmlSession * smlSessionNew(SmlSessionType sessionType, SmlMimeType mimetype, SmlProtocolVersion version, SmlProtocolType protocol, SmlLocation *target, SmlLocation *source, const char *sessionID, unsigned int messageID, SmlError **error)
Creates a new session.
Definition: sml_session.c:1832
SmlParserResult smlParserGetCommand(SmlParser *parser, SmlCommand **cmd, SmlError **error)
Parses the next command.
Definition: sml_parse.c:250
void * smlTryMalloc0(long n_bytes, SmlError **error)
Safely mallocs.
Definition: sml_support.c:335
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
SmlBool smlAssemblerGetSpace(SmlAssembler *assm, int *space, SmlCommand *parent, SmlCommand *cmd, SmlError **error)
Gets the available space in the assembler.
Definition: sml_parse.c:545
Represent an error.
void smlQueueSendPrio(SmlQueue *queue, void *data)
Sends a message down a queue.
Definition: sml_queue.c:323