libsyncml  0.5.4
http_server.c
1 /*
2  * libsyncml - A syncml protocol implementation
3  * Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
4  * Copyright (C) 2007-2009 Michael Bell <michael.bell@opensync.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include <glib.h>
23 #include <libsyncml/syncml.h>
24 #include <libsyncml/syncml_internals.h>
25 
26 #include <libsyncml/sml_error_internals.h>
27 #include <libsyncml/sml_transport_internals.h>
28 
29 #include "http_server_internals.h"
30 
31 #ifdef HAVE_LIBSOUP22
32 static void smlTransportHttpServerCallback(
33  SoupServerContext *context,
34  SoupMessage *msg,
35  gpointer data)
36 {
37  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, context, msg, data);
38  SmlMimeType mimetype = SML_MIMETYPE_UNKNOWN;
39  smlAssert(data);
40  SmlTransportHttpServerEnv *env = data;
41  SmlLinkHttpServerEnv *linkenv = NULL;
42  SmlLink *link_ = NULL;
43  SmlError *error = NULL;
44 
45  char *path = soup_uri_to_string (soup_message_get_uri(msg), TRUE);
46  smlTrace(TRACE_INTERNAL, "%s: %s %s HTTP/1.%d",
47  __func__, VA_STRING(msg->method), VA_STRING(path), soup_message_get_http_version(msg));
48 
49  if (g_strcasecmp(path, env->url)) {
50  /* check that this is a registered session */
51  smlSafeCFree(&path);
52  path = soup_uri_to_string (soup_message_get_uri(msg), FALSE);
53  link_ = g_hash_table_lookup(env->uriToLink, path);
54  if (!link_)
55  {
56  /* The URL was changed and there is no
57  * key/value pair for this URL available.
58  *
59  * SECURITY: This is a potential attack.
60  * FALSE POSITIVE: Session with a timeout.
61  */
62  smlErrorSet(&error, SML_ERROR_INTERNAL_FILE_NOT_FOUND,
63  "Not Found (%s).", path);
64  soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
65  smlSafeCFree(&path);
66  goto error;
67  }
68  smlLinkRef(link_);
69  }
70  smlSafeCFree(&path);
71 
72  if (soup_message_get_http_version(msg) != 1) {
73  smlErrorSet(&error, SML_ERROR_NOT_IMPLEMENTED, "Wrong http version");
74  soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
75  goto error;
76  }
77 
78  if (soup_method_get_id(msg->method) != SOUP_METHOD_ID_POST) {
79  smlErrorSet(&error, SML_ERROR_NOT_IMPLEMENTED, "Wrong method");
80  soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
81  goto error;
82  }
83 
84  const char *header = soup_message_get_header(msg->request_headers, "Content-Type");
85  if (header && !g_strncasecmp(header, SML_ELEMENT_XML, strlen(SML_ELEMENT_XML)))
86  mimetype = SML_MIMETYPE_XML;
87  else if(header && !g_strncasecmp(header, SML_ELEMENT_WBXML, strlen(SML_ELEMENT_WBXML)))
88  mimetype = SML_MIMETYPE_WBXML;
89  else if(header && !g_strncasecmp(header, SML_ELEMENT_SAN, strlen(SML_ELEMENT_SAN)))
90  mimetype = SML_MIMETYPE_SAN;
91  else if (header) {
92  smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mimetype");
93  soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
94  goto error;
95  } else {
96  smlErrorSet(&error, SML_ERROR_GENERIC, "Faulty mimetype");
97  soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
98  goto error;
99  }
100 
101  /* prepare link and linkenv */
102  if (!link_)
103  {
104  /* This is a potentially new session.
105  * It is not possible to check this here.
106  * smlTransportGetResponseURI must be used
107  * to verify this.
108  */
109  linkenv = smlTryMalloc0(sizeof(SmlLinkHttpServerEnv), &error);
110  if (!linkenv)
111  goto error;
112  linkenv->env = env;
113  link_ = smlLinkNew(env->tsp, linkenv, &error);
114  if (!link_)
115  goto error;
116  linkenv->msg = msg;
117  /* the link must be cached for disconnect signaling */
118  linkenv->link = link_;
119  smlLinkRef(linkenv->link);
120  /* We think positive here and signal connect.
121  * the link is now complete, so send the connect event
122  */
123  smlTransportReceiveEvent(env->tsp, link_, SML_TRANSPORT_EVENT_CONNECT_DONE, NULL, NULL);
124 
125  } else {
126  linkenv = link_->link_data;
127  if (linkenv->msg)
128  smlTrace(TRACE_INTERNAL,
129  "%s: WARNING: ResponseURI should not be in use - test mode.",
130  __func__);
131  linkenv->msg = msg;
132  }
133 
134  smlTrace(TRACE_INTERNAL, "%s: The message length is %i.", __func__, msg->request.length);
135 
136  /* We need one byte more than the request length
137  * because the data can be a native XML message.
138  * If the data is a native XML message then it is
139  * sometimes directly used as string.
140  *
141  * The string is automatically NULL terminated
142  * because smlTryMalloc0 fills the new memory with NULLs.
143  */
144  char *body = smlTryMalloc0(msg->request.length + 1, &error);
145  if (!body)
146  goto error;
147  memcpy(body, msg->request.body, msg->request.length);
148 
149  SmlTransportData *tspdata = smlTransportDataNew(body, msg->request.length, mimetype, TRUE, &error);
150  body = NULL;
151  if (!tspdata)
152  goto error_unref_msg;
153 
154  smlTransportReceiveEvent(env->tsp, link_, SML_TRANSPORT_EVENT_DATA, tspdata, NULL);
155 
156  smlLinkDeref(link_);
157  smlTransportDataDeref(tspdata);
158 
159  soup_message_io_pause(msg);
160 
161  smlTrace(TRACE_EXIT, "%s", __func__);
162  return;
163 
164 error_unref_msg:
165  link_->link_data = NULL;
166  smlLinkDeref(link_);
167  linkenv->msg = NULL;
168  smlSafeFree((gpointer *) &linkenv);
169 error:
170  soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (msg), SOUP_TRANSFER_CONTENT_LENGTH);
171  soup_message_io_unpause(msg);
172  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
173  smlErrorDeref(&error);
174 }
175 
176 #else /* !HAVE_LIBSOUP22 == libsoup 2.4 */
177 
178 static void smlTransportHttpServerCallback(
179  SoupServer *server,
180  SoupMessage *msg,
181  const char *path,
182  GHashTable *query,
183  SoupClientContext *client,
184  gpointer data)
185 {
186  smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p, %p, %p)", __func__, server, msg, VA_STRING(path), query, client, data);
187  SmlMimeType mimetype = SML_MIMETYPE_UNKNOWN;
188  smlAssert(data);
189  SmlTransportHttpServerEnv *env = data;
190  SmlLinkHttpServerEnv *linkenv = NULL;
191  SmlLink *link_ = NULL;
192  SmlError *error = NULL;
193 
194  smlTrace(TRACE_INTERNAL, "%s: %s %s HTTP/1.%d",
195  __func__, VA_STRING(msg->method), VA_STRING(path), soup_message_get_http_version(msg));
196 
197  if (msg->method != SOUP_METHOD_POST) {
198  smlErrorSet(&error, SML_ERROR_NOT_IMPLEMENTED, "Wrong method");
199  soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
200  goto error;
201  }
202 
203  if (g_strcasecmp(path, env->url)) {
204  /* check that this is a registered session */
205  char *fullpath = soup_uri_to_string (soup_message_get_uri(msg), FALSE);
206  link_ = g_hash_table_lookup(env->uriToLink, fullpath);
207  if (!link_)
208  {
209  /* The URL was changed and there is no
210  * key/value pair for this URL available.
211  *
212  * SECURITY: This is a potential attack.
213  * FALSE POSITIVE: Session with a timeout.
214  */
215  smlErrorSet(&error, SML_ERROR_INTERNAL_FILE_NOT_FOUND,
216  "Not Found (%s).", fullpath);
217  soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
218  smlSafeCFree(&fullpath);
219  goto error;
220  }
221  smlLinkRef(link_);
222  smlSafeCFree(&fullpath);
223  }
224 
225  const char *header = soup_message_headers_get(msg->request_headers, "Content-Type");
226  if (header && !g_strncasecmp(header, SML_ELEMENT_XML, strlen(SML_ELEMENT_XML)))
227  mimetype = SML_MIMETYPE_XML;
228  else if(header && !g_strncasecmp(header, SML_ELEMENT_WBXML, strlen(SML_ELEMENT_WBXML)))
229  mimetype = SML_MIMETYPE_WBXML;
230  else if(header && !g_strncasecmp(header, SML_ELEMENT_SAN, strlen(SML_ELEMENT_SAN)))
231  mimetype = SML_MIMETYPE_SAN;
232  else if (header) {
233  smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mimetype");
234  soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
235  goto error;
236  } else {
237  smlErrorSet(&error, SML_ERROR_GENERIC, "Faulty mimetype");
238  soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
239  goto error;
240  }
241 
242  /* prepare link and linkenv */
243  if (!link_)
244  {
245  /* This is a potentially new session.
246  * It is not possible to check this here.
247  * smlTransportGetResponseURI must be used
248  * to verify this.
249  */
250  linkenv = smlTryMalloc0(sizeof(SmlLinkHttpServerEnv), &error);
251  if (!linkenv)
252  goto error;
253  linkenv->env = env;
254  link_ = smlLinkNew(env->tsp, linkenv, &error);
255  if (!link_)
256  goto error;
257  linkenv->msg = msg;
258  /* the link must be cached for disconnect signaling */
259  linkenv->link = link_;
260  smlLinkRef(linkenv->link);
261  /* We think positive here and signal connect.
262  * the link is now complete, so send the connect event
263  */
264  smlTransportReceiveEvent(env->tsp, link_, SML_TRANSPORT_EVENT_CONNECT_DONE, NULL, NULL);
265  } else {
266  linkenv = link_->link_data;
267  if (linkenv->msg)
268  smlTrace(TRACE_INTERNAL,
269  "%s: WARNING: ResponseURI should not be in use - test mode.",
270  __func__);
271  linkenv->msg = msg;
272  }
273 
274  if (msg->request_body) {
275  smlTrace(TRACE_INTERNAL, "%s: The message length is %i.",
276  __func__, msg->request_body->length);
277  } else {
278  smlTrace(TRACE_INTERNAL, "%s: The message has no request body.", __func__);
279  }
280 
281  /* We need one byte more than the request length
282  * because the data can be a native XML message.
283  * If the data is a native XML message then it is
284  * sometimes directly used as string.
285  *
286  * The string is automatically NULL terminated
287  * because smlTryMalloc0 fills the new memory with NULLs.
288  */
289  char *body = smlTryMalloc0(msg->request_body->length + 1, &error);
290  if (!body)
291  goto error;
292  memcpy(body, msg->request_body->data, msg->request_body->length);
293 
294  SmlTransportData *tspdata = smlTransportDataNew(body, msg->request_body->length, mimetype, TRUE, &error);
295  body = NULL;
296  if (!tspdata)
297  goto error_unref_msg;
298 
299  smlTransportReceiveEvent(env->tsp, link_, SML_TRANSPORT_EVENT_DATA, tspdata, NULL);
300  smlLinkDeref(link_);
301 
302  /* If we activate this function call then we get segfaults */
303  /* smlLinkDeref(link_); */
304  smlTransportDataDeref(tspdata);
305 
306  soup_server_pause_message(server, msg);
307 
308  smlTrace(TRACE_EXIT, "%s", __func__);
309  return;
310 
311 error_unref_msg:
312  smlLinkDeref(link_);
313 error:
314  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
315  smlErrorDeref(&error);
316 }
317 
318 #endif /* HAVE_LIBSOUP22 */
319 
320 static SmlBool smlTransportHttpServerSetConfigOption(
321  SmlTransport *tsp,
322  const char *name,
323  const char *value,
324  SmlError **error)
325 {
326  smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %p)", __func__, tsp, VA_STRING(name), VA_STRING(value), error);
327  CHECK_ERROR_REF
328  smlAssert(tsp);
329  smlAssert(tsp->transport_data);
330  SmlTransportHttpServerEnv *env = tsp->transport_data;
331 
332  if (!strcmp(name, SML_TRANSPORT_CONFIG_PORT)) {
333  env->port = atoi(value);
334  if (!(env->port > 0 && env->port < 65535)) {
335  smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "specified port was wrong");
336  smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
337  return FALSE;
338  }
339  smlTrace(TRACE_INTERNAL, "%s: Port %i detected", __func__, env->port);
340  } else if (!strcmp(name, SML_TRANSPORT_CONFIG_URL)) {
341  env->url = g_strdup(value);
342  smlTrace(TRACE_INTERNAL, "%s: URL %s detected", __func__, VA_STRING(env->url));
343  } else if (!strcmp(name, SML_TRANSPORT_CONFIG_SSL_KEY)) {
344  env->ssl_key = g_strdup(value);
345  smlTrace(TRACE_INTERNAL, "%s: SSL key %s detected", __func__, VA_STRING(env->ssl_key));
346  } else if (!strcmp(name, SML_TRANSPORT_CONFIG_SSL_SERVER_CERT)) {
347  env->ssl_cert = g_strdup(value);
348  smlTrace(TRACE_INTERNAL, "%s: SSL server certificate %s detected", __func__, VA_STRING(env->ssl_cert));
349  } else {
350  smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "Unknown parameter %s found.", name);
351  smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
352  return FALSE;
353  }
354 
355  smlTrace(TRACE_EXIT, "%s", __func__);
356  return TRUE;
357 }
358 
359 static SmlBool smlTransportHttpServerInit(SmlTransport *tsp, SmlError **error)
360 {
361  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
362  CHECK_ERROR_REF
363  smlAssert(tsp);
364  smlAssert(tsp->context);
365  smlAssert(tsp->transport_data);
366  SmlTransportHttpServerEnv *env = tsp->transport_data;
367 
368  if (!env->port)
369  {
370  smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
371  "The HTTP server needs a port where it has to run.");
372  goto error_free_env;
373  }
374 
375  if (!env->url)
376  {
377  /* configure a default location */
378  env->url = g_strdup("/");
379  }
380 
381  smlTrace(TRACE_INTERNAL, "%s: config: port %i, url %s", __func__, env->port, VA_STRING(env->url));
382  smlTrace(TRACE_INTERNAL, "%s: http server uses context %p.", __func__, tsp->context);
383 
384  if(!env->ssl_key || !env->ssl_cert) {
385  env->server = soup_server_new(
386  SOUP_SERVER_ASYNC_CONTEXT, tsp->context,
387  SOUP_SERVER_PORT, env->port,
388  NULL);
389  } else {
390  env->server = soup_server_new(
391  SOUP_SERVER_ASYNC_CONTEXT, tsp->context,
392  SOUP_SERVER_PORT, env->port,
393  SOUP_SERVER_SSL_CERT_FILE, env->ssl_cert,
394  SOUP_SERVER_SSL_KEY_FILE, env->ssl_key,
395  NULL);
396  }
397  if (!env->server) {
398  smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "Unable to spawn server");
399  goto error_free_env;
400  }
401 
402 #ifdef HAVE_LIBSOUP22
403  soup_server_add_handler(env->server, NULL, NULL, smlTransportHttpServerCallback, NULL, env);
404 #else
405  soup_server_add_handler(env->server, NULL, smlTransportHttpServerCallback, env, NULL);
406 #endif
407 
408  soup_server_run_async(env->server);
409 
410  smlTrace(TRACE_EXIT, "%s", __func__);
411  return TRUE;
412 
413 error_free_env:
414  if (env->url)
415  smlSafeCFree(&(env->url));
416 
417  smlSafeFree((gpointer *)&env);
418 // error:
419  smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
420  return FALSE;
421 }
422 
423 static void smlTransportHttpServerDisconnect(void *data, void *linkdata)
424 {
425  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, linkdata);
426  smlAssert(linkdata);
427  SmlTransportHttpServerEnv *env = data;
428  SmlLinkHttpServerEnv *linkenv = linkdata;
429 
430  /* remove session from sessionToUri */
431  smlTrace(TRACE_INTERNAL, "%s: remove link from session/uri cache", __func__);
432  if (linkenv->session)
433  {
434  char *uri = g_hash_table_lookup(linkenv->env->sessionToUri, linkenv->session);
435  g_hash_table_remove(linkenv->env->sessionToUri, linkenv->session);
436  if (uri)
437  smlSafeCFree(&uri);
438  /* This is not a bug. We added two references to session
439  * because the session is used in two locations.
440  */
441  smlSessionUnref(linkenv->session);
442  smlSessionUnref(linkenv->session);
443  linkenv->session = NULL;
444  }
445 
446  /* remove link from uriToLink */
447  SmlLink *link_ = NULL;
448  smlTrace(TRACE_INTERNAL, "%s: remove link from uri/link cache", __func__);
449  if (linkenv->url)
450  {
451  link_ = g_hash_table_lookup(linkenv->env->uriToLink, linkenv->url);
452  g_hash_table_remove(linkenv->env->uriToLink, linkenv->url);
453  smlLinkDeref(link_);
454  smlSafeCFree(&(linkenv->url));
455  }
456 
457  /* save link for disconnect event */
458  link_ = linkenv->link;
459 
460  /* signal disconnect to socket and potential client */
461  if (linkenv->msg)
462  {
463  smlTrace(TRACE_INTERNAL, "%s: close open message/connection", __func__);
464  soup_message_set_status (linkenv->msg, SOUP_STATUS_SERVICE_UNAVAILABLE);
465 #ifdef HAVE_LIBSOUP22
466  //soup_server_message_set_encoding (
467  // SOUP_SERVER_MESSAGE (linkenv->msg),
468  // SOUP_TRANSFER_CONTENT_LENGTH);
469  soup_message_io_unpause(linkenv->msg);
470 #else
471  soup_server_unpause_message(linkenv->env->server, linkenv->msg);
472 #endif
473  linkenv->msg = NULL;
474  }
475 
476  /* cleanup */
477  smlTrace(TRACE_INTERNAL, "%s: free memory", __func__);
478  linkenv->env = NULL;
479  link_->link_data = NULL;
480  smlSafeFree((gpointer *)&linkenv);
481 
482  smlTrace(TRACE_INTERNAL, "%s: signal and unref link", __func__);
483  smlTransportReceiveEvent(env->tsp, link_, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
484  smlLinkDeref(link_);
485  smlTrace(TRACE_EXIT, "%s", __func__);
486 }
487 
488 static void smlTransportHttpServerFreeResponseURI(gpointer key)
489 {
490  smlTrace(TRACE_ENTRY, "%s(%p)", __func__, key);
491  smlSafeCFree((char **)&key);
492  smlTrace(TRACE_EXIT, "%s", __func__);
493 }
494 
495 static gboolean smlTransportHttpServerFreeUriToLink(
496  gpointer key,
497  gpointer value,
498  gpointer user_data)
499 {
500  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, key, value, user_data);
501  // SmlTransportHttpServerEnv *env = user_data;
502  char *uri = key;
503  SmlLink *link_ = value;
504 
505  smlLinkDeref(link_);
506  smlSafeCFree(&uri);
507 
508  smlTrace(TRACE_EXIT, "%s", __func__);
509  return TRUE;
510 }
511 
512 static gboolean smlTransportHttpServerFreeSessionToUri(
513  gpointer key,
514  gpointer value,
515  gpointer user_data)
516 {
517  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, key, value, user_data);
518  // SmlTransportHttpServerEnv *env = user_data;
519  SmlSession *session = key;
520  char *uri = value;
521 
522  smlSafeCFree(&uri);
523  smlSessionUnref(session);
524 
525  smlTrace(TRACE_EXIT, "%s", __func__);
526  return TRUE;
527 }
528 
529 static SmlBool smlTransportHttpServerCleanupSocket(
530  gpointer data,
531  SmlError **error)
532 {
533  smlTrace(TRACE_ENTRY, "%s", __func__);
534  CHECK_ERROR_REF
535  smlAssert(data);
536 
537  SoupServer *server = data;
538 
539  soup_server_quit(server);
540  g_object_unref(server);
541 
542  smlTrace(TRACE_EXIT, "%s", __func__);
543  return TRUE;
544 }
545 
546 static SmlBool smlTransportHttpServerFinalize(void *data, SmlError **error)
547 {
548  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, error);
549  CHECK_ERROR_REF
550  smlAssert(data);
551  SmlTransportHttpServerEnv *env = data;
552 
553  smlAssert(env->tsp);
554 
555  if (env->server) {
556  /* If there was no server init
557  * then there is no server.
558  * (e.g. failure after smlTransportHttpServerNew)
559  */
561  env->tsp->thread,
562  smlTransportHttpServerCleanupSocket,
563  env->server,
564  error))
565  goto error;
566  env->server = NULL;
567  }
568 
569  if (env->url)
570  smlSafeCFree(&(env->url));
571 
572  guint count = 0;
573  guint removed = 0;
574 
575  /* cleanup hash table uriToLink */
576 
577  count = g_hash_table_size(env->uriToLink);
578  removed = g_hash_table_foreach_steal(
579  env->uriToLink,
580  smlTransportHttpServerFreeUriToLink,
581  env);
582  if (count != removed)
583  {
584  smlErrorSet(error, SML_ERROR_GENERIC,
585  "The glib function g_hash_table_foreach_steal could " \
586  "not remove all key/value pairs from uriToLink (%u of %u).",
587  removed, count);
588  goto error;
589  } else {
590  g_hash_table_unref(env->uriToLink);
591  env->uriToLink = NULL;
592  }
593 
594  /* cleanup hash table sessionToUri */
595 
596  count = g_hash_table_size(env->sessionToUri);
597  removed = g_hash_table_foreach_steal(
598  env->sessionToUri,
599  smlTransportHttpServerFreeSessionToUri,
600  env);
601  if (count != removed)
602  {
603  smlErrorSet(error, SML_ERROR_GENERIC,
604  "The glib function g_hash_table_foreach_steal could not " \
605  "remove all key/value pairs from sessionToUri. (%u of %u)",
606  removed, count);
607  goto error;
608  } else {
609  g_hash_table_unref(env->sessionToUri);
610  env->sessionToUri = NULL;
611  }
612 
613  smlSafeFree((gpointer *)&env);
614 
615  smlTrace(TRACE_EXIT, "%s", __func__);
616  return TRUE;
617 error:
618  smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
619  return FALSE;
620 }
621 
622 static void smlTransportHttpServerSend(void *userdata, void *linkdata, SmlTransportData *data, SmlError *error)
623 {
624  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, userdata, linkdata, data, error);
625  // smlAssert(userdata);
626  smlAssert(linkdata);
627  smlAssert(error || data);
628  // SmlTransportHttpServerEnv *env = userdata;
629  SmlLinkHttpServerEnv *linkenv = linkdata;
630  smlAssert(linkenv->msg);
631 
632  SoupMessage *msg = linkenv->msg;
633  linkenv->msg = NULL;
634 
635  if (error)
636  goto error_free_message;
637 
638  soup_message_set_status (msg, SOUP_STATUS_OK);
639 #ifdef HAVE_LIBSOUP22
640  soup_server_message_set_encoding (SOUP_SERVER_MESSAGE(msg), SOUP_TRANSFER_CONTENT_LENGTH);
641 #endif
642 
643  const char *content_type;
644  switch (data->type) {
645  case SML_MIMETYPE_XML:
646  content_type = SML_ELEMENT_XML;
647  break;
648  case SML_MIMETYPE_WBXML:
649  content_type = SML_ELEMENT_WBXML;
650  break;
651  case SML_MIMETYPE_SAN:
652  content_type = SML_ELEMENT_SAN;
653  break;
654  default:
655  smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown Mimetype");
656  goto error_free_message;
657  }
658 
659 #ifdef HAVE_LIBSOUP22
660  // duplicate the data because sometimes data is freed to early
661  char *soupcopy = (char *) smlTryMalloc0(data->size, &error);
662  if (error)
663  goto error_free_message;
664  memcpy(soupcopy, data->data, data->size);
665  soup_message_set_response (msg, content_type, SOUP_BUFFER_SYSTEM_OWNED,
666  soupcopy, data->size);
667  soup_message_io_unpause(msg);
668 #else
669  soup_message_set_response(msg, content_type,
670  SOUP_MEMORY_COPY, data->data, data->size);
671  soup_server_unpause_message(linkenv->env->server, msg);
672 #endif
673 
674  smlTrace(TRACE_EXIT, "%s", __func__);
675  return;
676 
677 error_free_message:
678  if (smlErrorGetClass(&error) <= SML_ERRORCLASS_RETRY)
679  soup_message_set_status_full(msg, SOUP_STATUS_BAD_REQUEST, smlErrorPrint(&error));
680  else
681  soup_message_set_status_full(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, smlErrorPrint(&error));
682 
683 #ifdef HAVE_LIBSOUP22
684  soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (msg), SOUP_TRANSFER_CONTENT_LENGTH);
685  soup_message_io_unpause(msg);
686 #else
687  soup_server_unpause_message(linkenv->env->server, msg);
688 #endif
689 
690  smlErrorDeref(&error);
691  smlTrace(TRACE_EXIT, "%s: Sent Error", __func__);
692  return;
693 }
694 
695 char * smlTransportHttpServerGetResponseURI(SmlLink *link_, SmlSession *session, SmlError **error)
696 {
697  smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, link_, session, error);
698  /* Do not change the assertion to simple return FALSE stuff
699  * because to many developers incl. me ignore returned FALSE
700  * if it is only configuration.
701  */
702  CHECK_ERROR_REF
703  smlAssert(link_);
704  smlAssert(session);
705  smlAssert(link_->link_data);
706  SmlLinkHttpServerEnv *linkenv = link_->link_data;
707  smlAssert(linkenv->env);
708  SmlTransportHttpServerEnv *env = linkenv->env;
709  char *responseURI = NULL;
710  smlTrace(TRACE_INTERNAL, "%s: params checked", __func__);
711 
712  /* SECURITY NOTE
713  * This is the function were we have to create and validate
714  * the used URLs. If you are not sure about what you are doing
715  * then do not touch the code and contact the developers.
716  */
717 
718  /* check the session */
719 
720  if (!g_hash_table_lookup(env->sessionToUri, session))
721  {
722  /* This is a potentially new session.
723  * The URL must be the original URL.
724  */
725 
726  smlTrace(TRACE_INTERNAL, "%s: new session detected", __func__);
727 
728  char *soupURI = soup_uri_to_string (soup_message_get_uri(linkenv->msg), TRUE);
729  if (strcmp(env->url, soupURI))
730  {
731  /* SECURITY: This can be an attack. */
732  smlErrorSet(error, SML_ERROR_RETRY_LATER,
733  "A new session with a configured HTTP session was detected.");
734  smlSafeCFree(&soupURI);
735  goto error;
736  }
737  smlSafeCFree(&soupURI);
738 
739  /* create new random response ID
740  *
741  * glib only supports 32 bit random numbers.
742  * Therefore 4 random numbers are created
743  * and a fixed length is used.
744  *
745  * Do not send the response ID as HTTP GET parameter
746  * because this can cause conflicts
747  * because SyncML requires HTTP POST and
748  * libsoup 2.4 filters such parameters
749  * from the path in the callback interface.
750  */
751  soupURI = soup_uri_to_string (soup_message_get_uri(linkenv->msg), FALSE),
752  responseURI = g_strdup_printf("%s0X%08X%08X%08X%08X",
753  soupURI,
754  g_random_int (),
755  g_random_int (),
756  g_random_int (),
757  g_random_int ());
758  smlSafeCFree(&soupURI);
759 
760  /* map URI to link */
761  if (g_hash_table_lookup(env->uriToLink, responseURI))
762  {
763  /* SECURITY: This can be an insecure random number generator. */
764  smlErrorSet(error, SML_ERROR_RETRY_LATER,
765  "A fresh random session ID is already in use.");
766  goto error;
767  }
768  smlLinkRef(link_);
769  g_hash_table_insert(env->uriToLink, g_strdup(responseURI), link_);
770  smlTrace(TRACE_INTERNAL, "%s: ResponseURI is %s.", __func__, VA_STRING(responseURI));
771 
772  /* map session to URI */
773  g_hash_table_insert(env->sessionToUri, session, g_strdup(responseURI));
774  smlSessionRef(session);
775 
776  /* cache session and URI in linkenv - necessary for cleanup */
777  linkenv->session = session;
778  smlSessionRef(session);
779  linkenv->url = g_strdup(responseURI);
780  } else {
781  /* The session already exists.
782  * The referenced URL must be checked against the cached URL.
783  * The used URL must be checked against the cached URL too.
784  */
785 
786  smlTrace(TRACE_INTERNAL, "%s: cached session detected", __func__);
787 
788  char *soupURI = soup_uri_to_string (soup_message_get_uri(linkenv->msg), FALSE);
789  const char *sessionURI = g_hash_table_lookup(env->sessionToUri, session);
790  if (strcmp(soupURI, sessionURI))
791  {
792  /* SECURITY: This is a potential attack. */
793  smlErrorSet(error, SML_ERROR_RETRY_LATER,
794  "Another session (%s) re-used an already existing HTTP session (%s).",
795  soupURI, sessionURI);
796  smlSafeCFree(&soupURI);
797  goto error;
798  }
799 
800  SmlLink *cachedLink = g_hash_table_lookup(env->uriToLink, soupURI);
801  if (!cachedLink)
802  {
803  /* SECURITY: This is an internal bug. */
804  smlErrorSet(error, SML_ERROR_GENERIC,
805  "Cannot find link for used URL.");
806  smlSafeCFree(&soupURI);
807  goto error;
808  }
809  if (cachedLink != link_)
810  {
811  /* SECURITY: This is an internal bug. */
812  smlErrorSet(error, SML_ERROR_GENERIC,
813  "The link objects mismatch.");
814  smlSafeCFree(&soupURI);
815  goto error;
816  }
817  if (strcmp(linkenv->url, soupURI))
818  {
819  /* SECURITY: This can be an attack. */
820  smlErrorSet(error, SML_ERROR_GENERIC,
821  "The URL of the link object is wrong.");
822  smlSafeCFree(&soupURI);
823  goto error;
824  }
825 
826  responseURI = soupURI;
827  }
828  smlTrace(TRACE_EXIT, "%s - %s", __func__, VA_STRING(responseURI));
829  return responseURI;
830 error:
831  smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
832  return NULL;
833 }
834 
835 SmlBool smlTransportHttpServerNew(SmlTransport *tsp, SmlError **error)
836 {
837  smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
838  CHECK_ERROR_REF
839  smlAssert(tsp);
840 
841  tsp->functions.set_config_option = smlTransportHttpServerSetConfigOption;
842  tsp->functions.initialize = smlTransportHttpServerInit;
843  tsp->functions.finalize = smlTransportHttpServerFinalize;
844  tsp->functions.send = smlTransportHttpServerSend;
845  tsp->functions.get_response_uri = smlTransportHttpServerGetResponseURI;
846  tsp->functions.disconnect = smlTransportHttpServerDisconnect;
847 
849  if (!env)
850  return FALSE;
851  tsp->transport_data = env;
852  env->tsp = tsp;
853  env->sessionToUri = g_hash_table_new(g_direct_hash, g_direct_equal);
854  env->uriToLink = g_hash_table_new_full(g_str_hash, g_str_equal, smlTransportHttpServerFreeResponseURI, NULL);
855 
856  smlTrace(TRACE_EXIT, "%s", __func__);
857  return TRUE;
858 }
859 
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
SmlBool smlThreadCallFunction(SmlThread *thread, SmlThreadCallFunctionType func, gpointer data, SmlError **error)
Definition: sml_support.c:561
SmlErrorClass smlErrorGetClass(SmlError **error)
Gets the error class.
Definition: sml_error.c:382
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
void * smlTryMalloc0(long n_bytes, SmlError **error)
Safely mallocs.
Definition: sml_support.c:335
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
Represent an error.