libsyncml 0.5.4

- Main Page
- Modules
- Data Structures
- Files

- File List

sml_transport.c
1 /*
2 * libsyncml - A syncml protocol implementation
3 * Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
4 * Copyright (C) 2007-2009 Michael Bell <michael.bell@opensync.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22 #include "syncml.h"
23
24 #include "syncml_internals.h"
25 #include "sml_error_internals.h"
26 #include "sml_transport_internals.h"
27
28 #ifdef ENABLE_HTTP
29 #include "transports/http_client_internals.h"
30 #include "transports/http_server_internals.h"
31 #endif
32
33 #ifdef ENABLE_OBEX
34 #include "transports/obex_client_internals.h"
35 #include "transports/obex_server_internals.h"
36 #endif
37
45
46 static SmlBool smlTransportIsServer(SmlTransport *tsp)
47 {
48 smlAssert(tsp);
49 if (tsp->type == SML_TRANSPORT_HTTP_SERVER)
50 return TRUE;
51 if (tsp->type == SML_TRANSPORT_OBEX_SERVER)
52 return TRUE;
53 return FALSE;
54 }
55
56 void smlTransportSetEventCallback(SmlTransport *tsp, SmlTransportEventCb callback, void *userdata)
57 {
58 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, tsp, callback, userdata);
59 smlAssert(tsp);
60
61 tsp->event_callback = callback;
62 tsp->event_callback_userdata = userdata;
63
64 /* Do not return until all running callbacks finished.
65 * This is a busy wait and yes it can happen that new
66 * callbacks are started during sleep but the setter
67 * must get a warranty that the old callback is no
68 * longer in use.
69 *
70 * This is acceptable because the callback is only set
71 * two times. First it is set when the manager registers
72 * the transport and second it is set when the manager
73 * is freed. So this is a protection against sending an
74 * event to an already freed manager.
75 */
76 while (g_atomic_int_get(&(tsp->event_callback_ref_count)) > 0)
77 {
78 usleep(50);
79 }
80
81 smlTrace(TRACE_EXIT, "%s", __func__);
82 }
83
84 SmlBool smlTransportSend(SmlTransport *tsp, SmlLink *link_, SmlTransportData *data, SmlError **error)
85 {
86 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, tsp, link_, data, error);
87 CHECK_ERROR_REF
88 smlAssert(tsp);
89 smlAssert(data);
90
91 if (tsp->cached_error != NULL)
92 {
93 // the most parameters should be NULL because the cached error
94 // has nothing to do with the actual send request
95 smlTransportReceiveEvent(tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, NULL);
96 goto error;
97 }
98
99 SmlTransportCommand *cmd = smlTryMalloc0(sizeof(SmlTransportCommand), error);
100 if (!cmd) {
101 smlTransportReceiveEvent(tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, *error);
102 goto error;
103 }
104
105 cmd->type = SML_TRANSPORT_CMD_SEND;
106 cmd->data = data;
107 if (link_) {
108 cmd->link = link_;
109 smlLinkRef(cmd->link);
110 }
111
112 smlTransportDataRef(cmd->data);
113
114 //Call the fin function
115 smlQueueSend(tsp->command_queue, cmd);
116
117 smlTrace(TRACE_EXIT, "%s", __func__);
118 return TRUE;
119
120 error:
121 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
122 return FALSE;
123 }
124
125 void smlTransportWorkerHandler(void *message, void *userdata)
126 {
127 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, message, userdata);
128 smlAssert(message);
129 smlAssert(userdata);
130 SmlTransportCommand *cmd = message;
131 SmlTransport *tsp = userdata;
132
133 switch (cmd->type) {
134 case SML_TRANSPORT_CMD_SEND:
135 tsp->functions.send(tsp->transport_data, cmd->link ? cmd->link->link_data : NULL, cmd->data, cmd->error);
136 break;
137 case SML_TRANSPORT_CMD_CONNECT:
138 if (!tsp->functions.connect) {
139 smlTransportReceiveEvent(tsp, NULL, SML_TRANSPORT_EVENT_CONNECT_DONE, NULL, NULL);
140 smlTrace(TRACE_INTERNAL, "%s: No connect function", __func__);
141 break;
142 }
143 tsp->functions.connect(tsp->transport_data);
144 break;
145 case SML_TRANSPORT_CMD_DISCONNECT:
146 if (!tsp->functions.disconnect) {
147 smlTransportReceiveEvent(tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
148 smlTrace(TRACE_INTERNAL, "%s: No disconnect function", __func__);
149 break;
150 }
151 /* If there is a disconnect request
152 * but the connection is already down
153 * then an earlier disconnect happened
154 * (perhaps initiated by the remote peer)
155 * and we can ignore the disconnect.
156 */
157 if (smlTransportIsServer(tsp) && !cmd->link->link_data) {
158 /* The SERVER is already disconnected. */
159 smlTrace(TRACE_INTERNAL,
160 "%s: The server link was already disconnected.",
161 __func__);
162 break;
163 }
164 if (!smlTransportIsServer(tsp) && !tsp->connected) {
165 /* The CLIENT is already disconnected. */
166 smlTrace(TRACE_INTERNAL,
167 "%s: The client was already disconnected.",
168 __func__);
169 break;
170 }
171 tsp->functions.disconnect(tsp->transport_data, cmd->link ? cmd->link->link_data : NULL);
172 break;
173 }
174
175 if (cmd->link)
176 smlLinkDeref(cmd->link);
177
178 if (cmd->data)
179 smlTransportDataDeref(cmd->data);
180
181 smlSafeFree((gpointer *)&cmd);
182
183 smlTrace(TRACE_EXIT, "%s", __func__);
184 return;
185 }
186
187 SmlBool smlTransportReceiveEvent(SmlTransport *tsp, SmlLink *link_, SmlTransportEventType type, SmlTransportData *data, SmlError *error)
188 {
189 smlTrace(TRACE_ENTRY, "%s(%p, %p, %i, %p, %p)", __func__, tsp, link_, type, data, error);
190 smlAssert(tsp);
191
192 SmlBool ret = TRUE;
193 if (tsp->event_callback == NULL)
194 {
195 smlTrace(TRACE_INTERNAL, "%s: no callback available", __func__);
196 if (type != SML_TRANSPORT_EVENT_ERROR &&
197 type != SML_TRANSPORT_EVENT_DISCONNECT_DONE)
198 {
199 /* Somebody forget to set the event callback.
200 * This is a heavy bug which cannot be compensated.
201 * The whole state management will fail.
202 */
203 g_error("The transport layer of type %d " \
204 "must send a normal event %d " \
205 "but there is no event callback set.",
206 tsp->type, type);
207 /* abort if g_error is not fatal */
208 exit(1);
209 }
210 /* If there is a cached error then the transport layer
211 * uses the error event without an error as a trigger
212 * to send the original error.
213 */
214 if (tsp->cached_error && error != NULL)
215 {
216 /* Wow, this is the second error and
217 * there is still no event callback.
218 */
219 g_warning("The transport layer already caches an error " \
220 "because the event callback is not present until now." \
221 "The received error is ignored. %s",
222 smlErrorPrint(&error));
223 // do not free the error here because you are not the owner of the error
224 }
225 if (!tsp->cached_error && error) {
226 // we stop all communications here
227 smlTrace(TRACE_INTERNAL, "%s: init failed in transport protocol -> %s", __func__, smlErrorPrint(&error));
228 tsp->state = SML_TRANSPORT_ERROR;
229 tsp->cached_error = error;
230 smlErrorRef(&error);
231 }
232 } else {
233 smlTrace(TRACE_INTERNAL, "%s: callback available", __func__);
234 if (tsp->cached_error != NULL)
235 {
236 /* First we have to send the cached error. */
237 smlTrace(TRACE_INTERNAL, "%s: cached error detected - %s", __func__,
238 smlErrorPrint(&(tsp->cached_error)));
239 g_atomic_int_inc(&(tsp->event_callback_ref_count));
240 ret = tsp->event_callback(
241 tsp, NULL,
242 SML_TRANSPORT_EVENT_ERROR, NULL,
243 tsp->cached_error, tsp->event_callback_userdata);
244 smlTrace(TRACE_INTERNAL, "%s: %d event callbacks",
245 __func__, g_atomic_int_dec_and_test(&(tsp->event_callback_ref_count)));
246
247 /* Second we have to prepare the internal states. */
248
249 /* Third we have to send the original event. */
250 }
251
252 /* If a connect or disconnect is received without a link
253 * then this means that this is a new connection or a
254 * closed connection of a client. If such a constallation
255 * happens with a server then this is a fatal error.
256 *
257 * If a connect is reveived with a link
258 * then this must be a server and
259 * the link_data must be set.
260 *
261 * If a disconnect is received with a link
262 * then this must be a server and
263 * the link_data must not be present
264 * because the link is down now.
265 */
266
267 if (type == SML_TRANSPORT_EVENT_CONNECT_DONE && !link_)
268 {
269 if (smlTransportIsServer(tsp))
270 g_error("A connect event without a link was received " \
271 "but the transport layer is a server.");
272 if (tsp->connected)
273 g_error("A connect event was received " \
274 "but the transport layer is already connected.");
275 smlTrace(TRACE_INTERNAL, "%s: connect + no link");
276 tsp->connected = TRUE;
277 }
278
279 if (type == SML_TRANSPORT_EVENT_DISCONNECT_DONE && !link_)
280 {
281 if (smlTransportIsServer(tsp))
282 g_error("A disconnect event without a link was received " \
283 "but the transport layer is a server.");
284 if (!tsp->connected)
285 g_error("A disconnect event was received " \
286 "but there is no connected transport.");
287 smlTrace(TRACE_INTERNAL, "%s: disconnect + no link");
288 tsp->connected = FALSE;
289 }
290
291 if (type == SML_TRANSPORT_EVENT_CONNECT_DONE && link_)
292 {
293 if (!smlTransportIsServer(tsp))
294 g_error("A connect event with a link was received " \
295 "but the transport layer is a server.");
296 if (!link_->link_data)
297 g_error("A connect event with a link was received " \
298 "but the link does not contain the required " \
299 "transport environment.");
300 smlTrace(TRACE_INTERNAL, "%s: connect + link");
301 g_mutex_lock(tsp->connections_mutex);
302 tsp->connections++;
303 g_mutex_unlock(tsp->connections_mutex);
304 }
305
306 if (type == SML_TRANSPORT_EVENT_DISCONNECT_DONE && link_)
307 {
308 if (!smlTransportIsServer(tsp))
309 g_error("A disconnect event with a link was received " \
310 "but the transport layer is not a server.");
311 if (link_->link_data)
312 g_error("A disconnect event with a link was received " \
313 "but the link still contains the " \
314 "transport environment.");
315 smlTrace(TRACE_INTERNAL, "%s: disconnect + link");
316 g_mutex_lock(tsp->connections_mutex);
317 tsp->connections--;
318 g_mutex_unlock(tsp->connections_mutex);
319 }
320
321 /* now execute the callback */
322
323 if (!(tsp->cached_error &&
324 type == SML_TRANSPORT_EVENT_ERROR &&
325 error == NULL))
326 {
327 /* send the event */
328 g_atomic_int_inc(&(tsp->event_callback_ref_count));
329 ret = tsp->event_callback(tsp, link_, type, data, error, tsp->event_callback_userdata);
330 smlTrace(TRACE_INTERNAL, "%s: %d event callbacks",
331 __func__, g_atomic_int_dec_and_test(&(tsp->event_callback_ref_count)));
332 }
333 if (tsp->cached_error) {
334 smlErrorDeref(&(tsp->cached_error));
335 tsp->cached_error = NULL;
336 }
337 } /* end of tsp->event_callback */
338
339 smlTrace(TRACE_EXIT, "%s: %i", __func__, ret);
340 return ret;
341 }
342
343 /* FIXME: DEPRECATED */
344 SmlBool smlTransportRunAsync(SmlTransport *tsp, SmlError **error)
345 {
346 smlTrace(TRACE_ERROR, "%s(%p,%p)", __func__, tsp, error);
347 CHECK_ERROR_REF
348 /* All transports run in asynchronous mode.
349 * Therefore this function should never be called.
350 * The functions always returns TRUE to avoid any complications.
351 */
352 return TRUE;
353 }
354
355 static void _smlTransportStop(SmlTransport *tsp)
356 {
357 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, tsp);
358
359 smlAssert(tsp->thread);
360
361 smlThreadStop(tsp->thread);
362
363 smlThreadFree(tsp->thread);
364 tsp->thread = NULL;
365
366 smlTrace(TRACE_EXIT, "%s", __func__);
367 }
368
369 /* FIXME: DEPRECATED */
370 void smlTransportStop(SmlTransport *tsp)
371 {
372 _smlTransportStop(tsp);
373 }
374
375 SmlBool smlTransportConnect(SmlTransport *tsp, SmlError **error)
376 {
377 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
378 CHECK_ERROR_REF
379 smlAssert(tsp);
380
381 /* Only clients can actively connect and
382 * a client can only connect once.
383 */
384
385 if (smlTransportIsServer(tsp))
386 {
387 smlErrorSet(error, SML_ERROR_GENERIC,
388 "Only a transport client can be actively connected.");
389 goto error;
390 }
391
392 if (tsp->connected)
393 {
394 smlErrorSet(error, SML_ERROR_GENERIC,
395 "A transport client can be connected only once.");
396 goto error;
397 }
398
399 /* put the command into the queue */
400
401 SmlTransportCommand *cmd = smlTryMalloc0(sizeof(SmlTransportCommand), error);
402 if (!cmd)
403 goto error;
404
405 cmd->type = SML_TRANSPORT_CMD_CONNECT;
406
407 //Call the connect function
408 smlQueueSend(tsp->command_queue, cmd);
409
410 smlTrace(TRACE_EXIT, "%s", __func__);
411 return TRUE;
412
413 error:
414 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
415 return FALSE;
416 }
417
418 SmlBool smlTransportDisconnect(SmlTransport *tsp, SmlLink *link_, SmlError **error)
419 {
420 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, tsp, link_, error);
421 CHECK_ERROR_REF
422 smlAssert(tsp);
423
424 /* The transport must (still) be connected. */
425
426 if (!tsp->connected)
427 {
428 smlErrorSet(error, SML_ERROR_GENERIC,
429 "The transport is not connected and so it cannot be disconnected.");
430 goto error;
431 }
432
433 /* If this is a client then there must not be a link
434 * because every client can have only one connection.
435 */
436
437 if (link_ && !smlTransportIsServer(tsp))
438 {
439 smlErrorSet(error, SML_ERROR_GENERIC,
440 "A transport client has no link " \
441 "because there is only one connection.");
442 goto error;
443 }
444
445 /* If this is a server then there are two types of disconnects.
446 *
447 * If there is link then this is a shutdown for a connection.
448 * If a connection is closed then there must be link_data in
449 * the link because otherwise the connection is already closed.
450 *
451 * If there is no link then this is a shutdown for the server.
452 * This means that all links must be shut down.
453 */
454
455 if (link_ && !link_->link_data)
456 {
457 /* If there is a very fast or aggressive client
458 * then it can happen that the client closes
459 * the connection and the server registers this
460 * event before the disconnect function was
461 * called.
462 *
463 * If this happens then the link_data is perhaps
464 * already empty and the disconnect event was
465 * already triggered.
466 */
467 smlTrace(TRACE_EXIT,
468 "%s: A server connection should be closed " \
469 "but the connection is already closed.",
470 __func__);
471 return TRUE;
472 }
473
474 if (!link_ && smlTransportIsServer(tsp) && tsp->connections)
475 {
476 smlErrorSet(error, SML_ERROR_GENERIC,
477 "A server shutdown is requested " \
478 "but there are still open connections (%d).",
479 tsp->connections);
480 goto error;
481 }
482
483 /* check that the link is used at the correct transport layer
484 * this is also a check for the correct registration of the link
485 */
486
487 if (link_)
488 {
489 if (tsp != link_->tsp)
490 {
491 smlErrorSet(error, SML_ERROR_GENERIC,
492 "The link %p is registered at another transport layer %p than this one %p.",
493 link_, link_->tsp, tsp);
494 goto error;
495 }
496
497 g_mutex_lock(tsp->links_mutex);
498 if (!g_hash_table_lookup(tsp->links, link_)) {
499 g_mutex_unlock(tsp->links_mutex);
500 smlErrorSet(error, SML_ERROR_GENERIC,
501 "The link %p is not registered at the transport layer %p",
502 link_, tsp);
503 goto error;
504 }
505 g_mutex_unlock(tsp->links_mutex);
506 }
507
508 /* send the command */
509
510 SmlTransportCommand *cmd = smlTryMalloc0(sizeof(SmlTransportCommand), error);
511 if (!cmd)
512 goto error;
513
514 cmd->type = SML_TRANSPORT_CMD_DISCONNECT;
515 if (link_) {
516 cmd->link = link_;
517 smlLinkRef(cmd->link);
518 }
519
520 //Call the disconnect function
521 smlQueueSend(tsp->command_queue, cmd);
522
523 smlTrace(TRACE_EXIT, "%s", __func__);
524 return TRUE;
525
526 error:
527 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
528 return FALSE;
529 }
530
531 SmlTransportType smlTransportGetType(SmlTransport *tsp)
532 {
533 smlAssert(tsp);
534 return tsp->type;
535 }
536
537 SmlTransportData *smlTransportDataNew(char *data, unsigned long size, SmlMimeType mimetype, SmlBool ownsData, SmlError **error)
538 {
539 smlTrace(TRACE_ENTRY, "%s(%p, %d, %i, %i, %p)", __func__, data, size, mimetype, ownsData, error);
540 CHECK_ERROR_REF
541
542 //Append the data to the outgoing queue
543 SmlTransportData *cmd = smlTryMalloc0(sizeof(SmlTransportData), error);
544 if (!cmd)
545 goto error;
546
547 cmd->type = mimetype;
548 cmd->data = data;
549 cmd->size = size;
550 cmd->ownsData = ownsData;
551 cmd->refCount = 1;
552 cmd->needsAnswer = TRUE;
553 cmd->type_get = SML_MIMETYPE_UNKNOWN;
554
555 smlTrace(TRACE_EXIT, "%s: %p", __func__, cmd);
556 return cmd;
557
558 error:
559 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
560 return NULL;
561 }
562
563 SmlTransportData *smlTransportDataRef(SmlTransportData *data)
564 {
565 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
566 smlAssert(data);
567
568 g_atomic_int_inc(&(data->refCount));
569
570 smlTrace(TRACE_EXIT, "%s", __func__);
571 return data;
572 }
573
574 void smlTransportDataDeref(SmlTransportData *data)
575 {
576 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
577 smlAssert(data);
578
579 if (!g_atomic_int_dec_and_test(&(data->refCount))) {
580 smlTrace(TRACE_EXIT, "%s: refCount > 0", __func__);
581 return;
582 }
583
584 if (data->ownsData && data->data != NULL)
585 smlSafeCFree(&(data->data));
586
587 smlSafeFree((gpointer *)&data);
588
589 smlTrace(TRACE_EXIT, "%s: Freed", __func__);
590 }
591
592 void smlTransportSetError(SmlTransport *tsp, SmlLink *link_, SmlError **error)
593 {
594 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p(%p))", __func__, tsp, link_, error, error ? *error : NULL);
595 /* Do no use CHECK_ERROR_REF here.
596 * SmlError **error contains an error which should be send.
597 */
598 smlAssert(tsp);
599
600 SmlError *locerror = NULL;
601 SmlTransportCommand *cmd = smlTryMalloc0(sizeof(SmlTransportCommand), &locerror);
602 if (!cmd)
603 return;
604
605 cmd->type = SML_TRANSPORT_CMD_SEND;
606 cmd->data = NULL;
607 if (link_) {
608 cmd->link = link_;
609 smlLinkRef(cmd->link);
610 }
611
612 if (error && *error) {
613 cmd->error = *error;
614 smlErrorRef(error);
615 }
616
617 //Call the fin function
618 smlQueueSend(tsp->command_queue, cmd);
619
620 smlTrace(TRACE_EXIT, "%s", __func__);
621 }
622
623 SmlLink *smlLinkNew(SmlTransport *tsp, void *link_data, SmlError **error)
624 {
625 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, tsp, link_data, error);
626 CHECK_ERROR_REF
627 smlAssert(link_data);
628
629 SmlLink *link_ = smlTryMalloc0(sizeof(SmlLink), error);
630 if (!link_)
631 goto error;
632 link_->tsp = tsp;
633 link_->link_data = link_data;
634 link_->refCount = 1;
635
636 /* register the link at the transport */
637
638 g_mutex_lock(tsp->links_mutex);
639 g_hash_table_insert(tsp->links, link_, GINT_TO_POINTER(1));
640 g_mutex_unlock(tsp->links_mutex);
641
642 smlTrace(TRACE_EXIT, "%s: %p", __func__, link_);
643 return link_;
644
645 error:
646 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
647 return NULL;
648 }
649
650 /* FIXME: DEPRECATED */
651 SmlLink *smlLinkFind(SmlTransport *tsp, void *link_data)
652 {
653 /* The function always returns NULL.
654 * The function is not removed to protect the intergrity of the library.
655 * Actually no user of this function is known.
656 */
657 smlTrace(TRACE_INTERNAL, "%s(%p, %p)", __func__, tsp, link_data);
658 return NULL;
659 }
660
661 SmlLink *smlLinkRef(SmlLink *link_)
662 {
663 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, link_);
664 smlAssert(link_);
665
666 g_atomic_int_inc(&(link_->refCount));
667
668 smlTrace(TRACE_EXIT, "%s", __func__);
669 return link_;
670 }
671
672 void smlLinkDeref(SmlLink *link_)
673 {
674 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, link_);
675 smlAssert(link_);
676
677 if (!g_atomic_int_dec_and_test(&(link_->refCount))) {
678 smlTrace(TRACE_EXIT, "%s: refCount > 0", __func__);
679 return;
680 }
681
682 /* deregister the link from the transport */
683
684 g_mutex_lock(link_->tsp->links_mutex);
685 if (!g_hash_table_remove(link_->tsp->links, link_))
686 g_warning("The link %p was never registered.", link_);
687 g_mutex_unlock(link_->tsp->links_mutex);
688
689 /* free the memory */
690
691 smlSafeFree((gpointer *)&link_);
692
693 smlTrace(TRACE_EXIT, "%s: Freed", __func__);
694 }
695
705
711
721 SmlTransport *smlTransportNew(SmlTransportType type, SmlError **error)
722 {
723 smlTrace(TRACE_ENTRY, "%s(%i, %p)", __func__, type, error);
724 CHECK_ERROR_REF
725
726 /* Initialize GLib thread system. */
727 if (!g_thread_supported ()) g_thread_init (NULL);
728 g_type_init();
729
730 SmlTransport *tsp = smlTryMalloc0(sizeof(SmlTransport), error);
731 if (!tsp)
732 goto error;
733 tsp->type = type;
734 tsp->cached_error = NULL;
735 tsp->event_callback = NULL;
736 tsp->context = NULL;
737
738 switch (type) {
739 #ifdef ENABLE_HTTP
740 case SML_TRANSPORT_HTTP_SERVER:
741 if (!smlTransportHttpServerNew(tsp, error))
742 goto error_free_tsp;
743 break;
744 case SML_TRANSPORT_HTTP_CLIENT:
745 if (!smlTransportHttpClientNew(tsp, error))
746 goto error_free_tsp;
747 break;
748 #else
749 case SML_TRANSPORT_HTTP_SERVER:
750 case SML_TRANSPORT_HTTP_CLIENT:
751 smlErrorSet(error, SML_ERROR_GENERIC, "HTTP Transport not enabled in this build");
752 goto error_free_tsp;
753 #endif
754 #ifdef ENABLE_OBEX
755 case SML_TRANSPORT_OBEX_CLIENT:
756 if (!smlTransportObexClientNew(tsp, error))
757 goto error_free_tsp;
758 break;
759 case SML_TRANSPORT_OBEX_SERVER:
760 if (!smlTransportObexServerNew(tsp, error))
761 goto error_free_tsp;
762 break;
763 #else
764 case SML_TRANSPORT_OBEX_SERVER:
765 case SML_TRANSPORT_OBEX_CLIENT:
766 smlErrorSet(error, SML_ERROR_GENERIC, "OBEX Transport not enabled in this build");
767 goto error_free_tsp;
768 #endif
769 }
770
771 tsp->command_queue = smlQueueNew(error);
772 if (!tsp->command_queue)
773 goto error_free_tsp;
774
775 /* links must be tracked to detect server connections
776 * which are not cleaned up before server finalization
777 *
778 * connections must be tracked to detect open server connections
779 * which were not closed before server shutdown
780 */
781 if (smlTransportIsServer(tsp))
782 {
783 tsp->links = g_hash_table_new(g_direct_hash, g_direct_equal);
784 tsp->links_mutex = g_mutex_new();
785 tsp->connections = 0;
786 tsp->connections_mutex = g_mutex_new();
787 } else {
788 tsp->links = NULL;
789 tsp->links_mutex = NULL;
790 tsp->connections = 0;
791 tsp->connections_mutex = NULL;
792 }
793 tsp->connected = FALSE;
794
795 smlTrace(TRACE_EXIT, "%s: %p", __func__, tsp);
796 return tsp;
797
798 error_free_tsp:
799 smlTransportFree(tsp);
800 error:
801 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
802 return NULL;
803 }
804
810 void smlTransportFree(SmlTransport *tsp)
811 {
812 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, tsp);
813 smlAssert(tsp);
814
815 if (tsp->command_queue)
816 smlQueueFree(tsp->command_queue);
817
818 SmlError *error = NULL;
819 if (tsp->transport_data &&
820 !tsp->functions.finalize(tsp->transport_data, &error))
821 {
822 g_warning("The library libsyncml cannot free the transport. %s",
823 smlErrorPrint(&error));
824 smlErrorDeref(&error);
825 }
826
827 if (tsp->context)
828 g_main_context_unref(tsp->context);
829 tsp->context = NULL;
830
831 /* check for open connections */
832
833 if (tsp->connections)
834 {
835 g_warning("The transport layer of libsyncml is freed " \
836 "but not all connections were close (%d).",
837 tsp->connections);
838 }
839 if (tsp->connections_mutex)
840 g_mutex_free(tsp->connections_mutex);
841
842 /* check for forgotten connections */
843
844 if (tsp->links && g_hash_table_size(tsp->links))
845 g_warning("The transport layer of libsyncml is freed " \
846 "but not all connections were cleaned up (%d).",
847 g_hash_table_size(tsp->links));
848 if (tsp->links)
849 g_hash_table_unref(tsp->links);
850 if (tsp->links_mutex)
851 g_mutex_free(tsp->links_mutex);
852
853 /* check for forgotten errors */
854
855 if (tsp->cached_error) {
856 g_warning("The transport layer is cleaned up and an error is ignored. %s",
857 smlErrorPrint(&(tsp->cached_error)));
858 smlErrorDeref(&(tsp->cached_error));
859 }
860
861 /* free the memory */
862
863 smlSafeFree((gpointer *)&tsp);
864
865 smlTrace(TRACE_EXIT, "%s", __func__);
866 }
867
883 SmlBool smlTransportSetConfigOption(
884 SmlTransport *tsp,
885 const char *name,
886 const char *value,
887 SmlError **error)
888 {
889 smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %p)", __func__, tsp, VA_STRING(name), strcmp(name, "PASSWORD") ? VA_STRING(value) : "***sensitive***", error);
890 /* Do not change the assertion to simple return FALSE stuff
891 * because to many developers incl. me ignore returned FALSE
892 * if it is only configuration.
893 */
894 CHECK_ERROR_REF
895 smlAssert(tsp);
896 smlAssert(tsp->state == SML_TRANSPORT_UNINITIALIZED);
897 smlAssert(tsp->functions.set_config_option);
898
899 if (!name)
900 {
901 smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
902 "The name of the configuration option is missing.");
903 goto error;
904 }
905
906 if (!tsp->functions.set_config_option(tsp, name, value, error))
907 goto error;
908
909 smlTrace(TRACE_EXIT, "%s", __func__);
910 return TRUE;
911 error:
912 smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
913 return FALSE;
914 }
915
931 SmlBool smlTransportSetConnectionType(
932 SmlTransport *tsp,
933 SmlTransportConnectionType type,
934 SmlError **error)
935 {
936 smlTrace(TRACE_ENTRY, "%s(%p, %i, %p)", __func__, tsp, type, error);
937 /* Do not change the assertion to simple return FALSE stuff
938 * because to many developers incl. me ignore returned FALSE
939 * if it is only configuration.
940 */
941 CHECK_ERROR_REF
942 smlAssert(tsp);
943 smlAssert(type != SML_TRANSPORT_CONNECTION_TYPE_UNKNOWN);
944 smlAssert(tsp->state == SML_TRANSPORT_UNINITIALIZED);
945 smlAssert(tsp->functions.set_connection_type);
946
947 if (tsp->functions.set_connection_type(tsp, type, error))
948 {
949 smlTrace(TRACE_EXIT, "%s", __func__);
950 return TRUE;
951 } else {
952 smlTrace(TRACE_EXIT_ERROR, "%s", __func__);
953 return FALSE;
954 }
955 }
956
969 SmlBool smlTransportInitialize(SmlTransport *tsp, SmlError **error)
970 {
971 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
972 CHECK_ERROR_REF
973 smlAssert(tsp);
974 smlAssert(tsp->state == SML_TRANSPORT_UNINITIALIZED);
975
976 /* Intialize the context and thread for the g_main_loop. */
977
978 tsp->context = g_main_context_new();
979 if (!tsp->context) {
980 smlErrorSet(error, SML_ERROR_GENERIC,
981 "Cannot create new GMainContext for asynchronous transport.");
982 goto error;
983 }
984
985 tsp->thread = smlThreadNew(tsp->context, error);
986 if (!tsp->thread)
987 goto error_free_loop;
988
989 /* The transport always needs a context based loop for the
990 * command queue handling. Never attach this queue to
991 * another context because this can block the transport.
992 */
993
994 /* start the queue - e.g. to handle errors */
995 smlQueueSetHandler(tsp->command_queue, (SmlQueueHandler)smlTransportWorkerHandler, tsp);
996 smlQueueAttach(tsp->command_queue, tsp->context);
997
998 if (tsp->functions.initialize && !tsp->functions.initialize(tsp, error))
999 goto error_detach;
1000
1001 /* Now start the GMainLoop.
1002 * Do not start earlier to avoid confusion with libsoup
1003 * because of registering a context at a running main loop. */
1004 smlThreadStart(tsp->thread);
1005
1006 tsp->state = SML_TRANSPORT_INITIALIZED;
1007
1008 /* If this is a server then the server is running now. */
1009 if (smlTransportIsServer(tsp))
1010 tsp->connected = TRUE;
1011
1012 smlTrace(TRACE_EXIT, "%s", __func__);
1013 return TRUE;
1014
1015 error_detach:
1016 smlQueueDetach(tsp->command_queue);
1017 error_free_loop:
1018 if (tsp->context) {
1019 g_main_context_unref(tsp->context);
1020 tsp->context = NULL;
1021 }
1022 error:
1023 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1024 return FALSE;
1025 }
1026
1041 SmlBool smlTransportSetResponseURI(
1042 SmlTransport *tsp,
1043 const char *uri,
1044 SmlError **error)
1045 {
1046 smlTrace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, tsp, VA_STRING(uri), error);
1047 /* Do not change the assertion to simple return FALSE stuff
1048 * because to many developers incl. me ignore returned FALSE
1049 * if it is only configuration.
1050 */
1051 CHECK_ERROR_REF
1052 smlAssert(tsp);
1053 smlAssert(uri);
1054 smlAssert(tsp->state == SML_TRANSPORT_INITIALIZED ||
1055 tsp->state == SML_TRANSPORT_CONNECTED);
1056 smlAssert(tsp->functions.set_response_uri);
1057
1058 if (tsp->functions.set_response_uri(tsp, uri, error))
1059 {
1060 smlTrace(TRACE_EXIT, "%s", __func__);
1061 return TRUE;
1062 } else {
1063 smlTrace(TRACE_EXIT_ERROR, "%s", __func__);
1064 return FALSE;
1065 }
1066 }
1067
1068 static SmlBool smlTransportDetachQueueCallback(
1069 gpointer data,
1070 SmlError **error)
1071 {
1072 smlTrace(TRACE_ENTRY, "%s", __func__);
1073 CHECK_ERROR_REF
1074 smlAssert(data);
1075 SmlQueue *queue = data;
1076 smlQueueDetach(queue);
1077 smlTrace(TRACE_EXIT, "%s", __func__);
1078 return TRUE;
1079 }
1080
1081 static SmlBool smlTransportDispatchQueueCallback(
1082 gpointer data,
1083 SmlError **error)
1084 {
1085 smlTrace(TRACE_ENTRY, "%s", __func__);
1086 CHECK_ERROR_REF
1087 smlAssert(data);
1088 SmlQueue *queue = data;
1089 smlQueueDispatch(queue);
1090 smlTrace(TRACE_EXIT, "%s", __func__);
1091 return TRUE;
1092 }
1093
1104 SmlBool smlTransportFinalize(SmlTransport *tsp, SmlError **error)
1105 {
1106 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
1107 CHECK_ERROR_REF
1108 smlAssert(tsp);
1109 smlAssert(tsp->functions.finalize);
1110
1111 if (tsp->connected && !smlTransportIsServer(tsp))
1112 {
1113 smlErrorSet(error, SML_ERROR_GENERIC,
1114 "If a client transport is connected then it cannot be finalized.");
1115 goto error;
1116 }
1117
1118 if (tsp->state != SML_TRANSPORT_INITIALIZED &&
1119 tsp->state != SML_TRANSPORT_ERROR) {
1120 smlErrorSet(error, SML_ERROR_GENERIC, "Transport was not in the state \"Initialized\"");
1121 goto error;
1122 }
1123
1124 /* FIXME: Is there a situation where no thread is present? */
1125
1126 /* OBEX */
1127 if (tsp->type != SML_TRANSPORT_HTTP_CLIENT &&
1128 tsp->type != SML_TRANSPORT_HTTP_SERVER && tsp->thread)
1129 _smlTransportStop(tsp);
1130
1131 /* stop the command queue */
1132 if (tsp->thread) {
1133 /* HTTP - thread safe destroy */
1134 if (!smlThreadCallFunction(
1135 tsp->thread,
1136 smlTransportDetachQueueCallback,
1137 tsp->command_queue,
1138 error))
1139 goto error;
1140 } else {
1141 /* OBEX */
1142 smlQueueDetach(tsp->command_queue);
1143 }
1144
1145 /* give all jobs a chance to finish cleanly */
1146 unsigned int i = 0;
1147 unsigned int queueLength = smlQueueLength(tsp->command_queue);
1148 for (; i < queueLength; i++) {
1149 if (tsp->thread) {
1150 /* HTTP */
1151 if (!smlThreadCallFunction(
1152 tsp->thread,
1153 smlTransportDispatchQueueCallback,
1154 tsp->command_queue,
1155 error))
1156 goto error;
1157 } else {
1158 /* OBEX */
1159 smlQueueDispatch(tsp->command_queue);
1160 }
1161 }
1162
1163 if (!tsp->functions.finalize(tsp->transport_data, error))
1164 goto error;
1165
1166 /* HTTP */
1167 if (tsp->thread)
1168 _smlTransportStop(tsp);
1169
1170 tsp->transport_data = NULL;
1171
1172 tsp->state = SML_TRANSPORT_UNINITIALIZED;
1173
1174 /* If this is a server then the server is shutdown now. */
1175 if (smlTransportIsServer(tsp))
1176 tsp->connected = FALSE;
1177
1178 smlTrace(TRACE_EXIT, "%s", __func__);
1179 return TRUE;
1180
1181 error:
1182 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
1183 return FALSE;
1184 }
1185
1186 char * smlTransportGetResponseURI(SmlLink *link_, SmlSession *session, SmlError **error)
1187 {
1188 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, link_, session, error);
1189 /* Do not change the assertion to simple return FALSE stuff
1190 * because to many developers incl. me ignore returned FALSE
1191 * if it is only configuration.
1192 */
1193 CHECK_ERROR_REF
1194 smlAssert(link_);
1195 smlAssert(link_->tsp);
1196 smlAssert(session);
1197
1198 if (link_->tsp->functions.get_response_uri)
1199 {
1200 char *result = link_->tsp->functions.get_response_uri(link_, session, error);
1201 smlTrace(TRACE_EXIT, "%s - %s", __func__, VA_STRING(result));
1202 return result;
1203 } else {
1204 smlTrace(TRACE_EXIT, "%s - unsupported feature", __func__);
1205 return NULL;
1206 }
1207 }
1208