libsyncml 0.5.4

- Main Page
- Modules
- Data Structures
- Files

- File List

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;
58 } SmlSessionCommand;
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
2339 unsigned int smlSessionGetRemoteMaxObjSize(SmlSession *session)
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