libsyncml 0.5.4

- Main Page
- Modules
- Data Structures
- Files

- File List

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 */
560 if (!smlThreadCallFunction(
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
848 SmlTransportHttpServerEnv *env = smlTryMalloc0(sizeof(SmlTransportHttpServerEnv), error);
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