libsyncml  0.5.4
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 
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 
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 
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 
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 
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 
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 
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 
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 
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