00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <libsyncml/syncml.h>
00023 #include <libsyncml/syncml_internals.h>
00024 #include <libsyncml/sml_error_internals.h>
00025
00026 #include <libsyncml/sml_transport_internals.h>
00027
00028 #include "http_client_internals.h"
00029
00030 static void smlTransportHttpClientSend(void *userdata, void *link, SmlTransportData *data, SmlError *error);
00031 static void smlTransportHttpClientDisconnect(void *data, void *linkdata);
00032 static SmlBool smlTransportHttpClientFinalize(void *data, SmlError **error);
00033
00034 #ifdef HAVE_LIBSOUP22
00035 #define soup_message_headers_get soup_message_get_header
00036 #define soup_message_headers_append soup_message_add_header
00037 #endif
00038
00039 #ifdef HAVE_LIBSOUP22
00040 static void smlTransportHttpClientCallback(SoupMessage *msg, gpointer userdata)
00041 {
00042 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, msg, userdata);
00043 #else
00044 static void smlTransportHttpClientCallback(SoupSession *session, SoupMessage *msg, gpointer userdata)
00045 {
00046
00047 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, session, msg, userdata);
00048 #endif
00049 SmlTransportHttpClientEnv *env = userdata;
00050 SmlError *error = NULL;
00051
00052
00053 if (env->connectDone && msg->status_code == SOUP_STATUS_CANCELLED)
00054 {
00055
00056
00057
00058 smlTrace(TRACE_EXIT, "%s - ignoring due to shutdown", __func__);
00059 return;
00060 }
00061
00062
00063 if (!env->connectDone)
00064 {
00065
00066
00067
00068
00069 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
00070 {
00071 smlErrorSet(&error,
00072 SML_ERROR_GENERIC,
00073 "Connection failed (%d) - %s", msg->status_code, msg->reason_phrase);
00074 goto error;
00075 }
00076 else
00077 {
00078
00079
00080
00081
00082 smlTrace(TRACE_INTERNAL, "%s: Connect done", __func__);
00083 if (!env->tsp->connected)
00084 {
00085
00086
00087
00088 smlTransportReceiveEvent(
00089 env->tsp, NULL,
00090 SML_TRANSPORT_EVENT_CONNECT_DONE,
00091 NULL, NULL);
00092 }
00093 }
00094 env->connectDone = TRUE;
00095 }
00096 smlTrace(TRACE_INTERNAL, "%s: Result: %d %s",
00097 __func__, msg->status_code, msg->reason_phrase);
00098
00099
00100
00101
00102 if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
00103 smlErrorSet(&error, SML_ERROR_GENERIC, "Call not successfull: %d %s",
00104 msg->status_code, msg->reason_phrase);
00105 goto error;
00106 }
00107
00108
00109 const char *header = soup_message_headers_get(msg->response_headers, "Content-Type");
00110 smlTrace(TRACE_INTERNAL, "%s: content type ::= %s",
00111 __func__, header);
00112
00113 SmlMimeType mimetype = SML_MIMETYPE_UNKNOWN;
00114 if (header && !g_strncasecmp(header, SML_ELEMENT_XML, strlen(SML_ELEMENT_XML)))
00115 mimetype = SML_MIMETYPE_XML;
00116 else if(header && !g_strncasecmp(header, SML_ELEMENT_WBXML, strlen(SML_ELEMENT_WBXML)))
00117 mimetype = SML_MIMETYPE_WBXML;
00118 else if(header && !g_strncasecmp(header, SML_ELEMENT_SAN, strlen(SML_ELEMENT_SAN)))
00119 mimetype = SML_MIMETYPE_SAN;
00120 else if (header) {
00121 smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mimetype");
00122 goto error;
00123 } else {
00124 smlErrorSet(&error, SML_ERROR_GENERIC, "Faulty mimetype");
00125 goto error;
00126
00127 }
00128
00129
00130 char *data;
00131 gsize length;
00132
00133 #ifdef HAVE_LIBSOUP22
00134 length = msg->response.length;
00135 #else
00136 length = msg->response_body->length;
00137 #endif
00138 smlTrace(TRACE_INTERNAL, "%s: The message length is %i.", __func__, length);
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148 data = smlTryMalloc0(length + 1, &error);
00149 if (!data)
00150 goto error;
00151
00152 #ifdef HAVE_LIBSOUP22
00153 memcpy(data, msg->response.body, length);
00154 #else
00155 memcpy(data, msg->response_body->data, length);
00156 #endif
00157
00158 SmlTransportData *tspdata = smlTransportDataNew(data, length, mimetype, TRUE, &error);
00159 data = NULL;
00160 if (!tspdata)
00161 goto error_free_data;
00162
00163
00164 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DATA, tspdata, NULL);
00165
00166
00167 smlTransportDataDeref(tspdata);
00168
00169 smlTrace(TRACE_EXIT, "%s", __func__);
00170 return;
00171
00172 error_free_data:
00173 smlSafeCFree(&data);
00174 error:
00175 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
00176 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
00177 smlErrorDeref(&error);
00178 }
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188 #ifdef HAVE_LIBSOUP22
00189 static void
00190 _authenticate (SoupSession *session, SoupMessage *msg,
00191 const char *auth_type, const char *auth_realm,
00192 char **username, char **password, gpointer data)
00193 {
00194 SmlTransportHttpClientEnv *env = data;
00195 smlTrace(TRACE_INTERNAL, "%s: authentication via auth_type %s", __func__, VA_STRING(auth_type));
00196 *username = g_strdup(env->username);
00197 *password = g_strdup(env->password);
00198 }
00199 #else
00200 static void
00201 _authenticate (SoupSession *session, SoupMessage *msg,
00202 SoupAuth *auth, gboolean retrying, gpointer data)
00203 {
00204 smlTrace(TRACE_INTERNAL, "%s(%p, %p, %p, %d, %p)", __func__, session, msg, auth, retrying, data);
00205 SmlTransportHttpClientEnv *env = data;
00206 smlTrace(TRACE_INTERNAL, "%s: authentication via auth_type %s", __func__, VA_STRING(soup_auth_get_scheme_name(auth)));
00207
00208 if (!retrying)
00209 soup_auth_authenticate(auth, env->username, env->password);
00210 }
00211 #endif
00212
00213 static SmlBool smlTransportHttpClientSetConfigOption(
00214 SmlTransport *tsp,
00215 const char *name,
00216 const char *value,
00217 SmlError **error)
00218 {
00219 smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %p)", __func__, tsp, VA_STRING(name), VA_STRING(value), error);
00220 CHECK_ERROR_REF
00221 smlAssert(tsp);
00222 smlAssert(tsp->transport_data);
00223 SmlTransportHttpClientEnv *env = tsp->transport_data;
00224
00225 if (!strcmp(name, SML_TRANSPORT_CONFIG_URL)) {
00226 if (env->url)
00227 smlSafeCFree(&(env->url));
00228 if (env->uri)
00229 soup_uri_free(env->uri);
00230 env->url = g_strdup(value);
00231 env->uri = soup_uri_new(env->url);
00232 if (env->uri == NULL) {
00233 smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
00234 "The configured url %s is wrong.", env->url);
00235 smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
00236 return FALSE;
00237 }
00238 smlTrace(TRACE_INTERNAL, "%s: URL %s detected", __func__, VA_STRING(env->url));
00239 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_PROXY)) {
00240 env->proxy = g_strdup(value);
00241 smlTrace(TRACE_INTERNAL, "%s: PROXY %s detected", __func__, VA_STRING(env->proxy));
00242 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_USERNAME)) {
00243 env->username = g_strdup(value);
00244 smlTrace(TRACE_INTERNAL, "%s: USERNAME %s detected", __func__, VA_STRING(env->username));
00245 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_PASSWORD)) {
00246 env->password = g_strdup(value);
00247 smlTrace(TRACE_INTERNAL, "%s: PASSWORD ******* detected", __func__);
00248 } else if (!strcmp(name, SML_TRANSPORT_CONFIG_SSL_CA_FILE)) {
00249 env->cafile = g_strdup(value);
00250 smlTrace(TRACE_INTERNAL, "%s: SSL_CA_FILE %s detected", __func__, VA_STRING(env->cafile));
00251 } else {
00252 smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "Unknown parameter %s found.", name);
00253 smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
00254 return FALSE;
00255 }
00256
00257 smlTrace(TRACE_EXIT, "%s", __func__);
00258 return TRUE;
00259 }
00260
00261
00262
00263
00264 static void smlTransportHttpClientConnect(void *data)
00265 {
00266 smlTrace(TRACE_ENTRY, "%s(%p)", __func__, data);
00267 smlAssert(data);
00268 SmlTransportHttpClientEnv *env = data;
00269 SmlError *error = NULL;
00270
00271 if (!env->tsp->context)
00272 {
00273 smlErrorSet(&error, SML_ERROR_INTERNAL_MISCONFIGURATION,
00274 "HTTP client runs only in asynchronous mode. So the context must be set.");
00275 goto error;
00276 }
00277
00278 env->connectDone = FALSE;
00279 env->disconnectDone = FALSE;
00280
00281 if (env->proxy != NULL && strlen(env->proxy) > 0)
00282 {
00283 if (env->cafile != NULL && strlen(env->cafile))
00284 {
00285 #ifdef HAVE_LIBSOUP22_SOLARIS
00286 env->session = soup_session_sync_new_with_options(
00287 #else
00288 env->session = soup_session_async_new_with_options(
00289 SOUP_SESSION_ASYNC_CONTEXT, env->tsp->context,
00290 #endif
00291 SOUP_SESSION_PROXY_URI, env->proxy,
00292 SOUP_SESSION_SSL_CA_FILE, env->cafile,
00293 NULL);
00294
00295
00296
00297
00298 } else {
00299 #ifdef HAVE_LIBSOUP22_SOLARIS
00300 env->session = soup_session_sync_new_with_options(
00301 #else
00302 env->session = soup_session_async_new_with_options(
00303 SOUP_SESSION_ASYNC_CONTEXT, env->tsp->context,
00304 #endif
00305 SOUP_SESSION_PROXY_URI, env->proxy,
00306 NULL);
00307 }
00308 } else {
00309 if (env->cafile != NULL && strlen(env->cafile))
00310 {
00311 #ifdef HAVE_LIBSOUP22_SOLARIS
00312 env->session = soup_session_sync_new_with_options(
00313 #else
00314 env->session = soup_session_async_new_with_options(
00315 SOUP_SESSION_ASYNC_CONTEXT, env->tsp->context,
00316 #endif
00317 SOUP_SESSION_SSL_CA_FILE, env->cafile,
00318 NULL);
00319 } else {
00320 #ifdef HAVE_LIBSOUP22_SOLARIS
00321 env->session = soup_session_sync_new();
00322 #else
00323 env->session = soup_session_async_new_with_options(
00324 SOUP_SESSION_ASYNC_CONTEXT, env->tsp->context,
00325 NULL);
00326 #endif
00327 }
00328 }
00329 if (!env->session) {
00330 smlErrorSet(&error, SML_ERROR_GENERIC, "Unable to create new session");
00331 goto error;
00332 }
00333
00334
00335
00336
00337 if (env->username || env->password)
00338 {
00339 g_signal_connect (env->session, "authenticate", G_CALLBACK (_authenticate), env);
00340 } else {
00341 env->username = NULL;
00342 env->password = NULL;
00343 }
00344
00345 smlTrace(TRACE_EXIT, "%s: %p", __func__, env);
00346 return;
00347 error:
00348 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
00349 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
00350 }
00351
00352 static SmlBool smlTransportHttpClientSetResponseURI(
00353 SmlTransport *tsp,
00354 const char *uri,
00355 SmlError **error)
00356 {
00357 smlTrace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, tsp, VA_STRING(uri), error);
00358 CHECK_ERROR_REF
00359 smlAssert(tsp);
00360 smlAssert(tsp->transport_data);
00361 SmlTransportHttpClientEnv *env = tsp->transport_data;
00362 smlAssert(uri);
00363
00364 if (env->url)
00365 smlSafeCFree(&(env->url));
00366 if (env->uri)
00367 soup_uri_free(env->uri);
00368
00369 env->url = g_strdup(uri);
00370 env->uri = soup_uri_new(env->url);
00371 if (env->uri == NULL) {
00372 smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
00373 "The specified url \"%s\" is wrong.", env->url);
00374 smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
00375 return FALSE;
00376 }
00377
00378 smlTrace(TRACE_EXIT, "%s", __func__);
00379 return TRUE;
00380 }
00381
00382 static SmlBool smlTransportHttpClientCleanupSession(
00383 gpointer data,
00384 SmlError **error)
00385 {
00386 smlTrace(TRACE_ENTRY, "%s", __func__);
00387 CHECK_ERROR_REF
00388 smlAssert(data);
00389
00390 SoupSession *session = data;
00391
00392 soup_session_abort(session);
00393 g_object_unref(session);
00394
00395 smlTrace(TRACE_EXIT, "%s", __func__);
00396 return TRUE;
00397 }
00398
00399 static SmlBool smlTransportHttpClientFinalize(void *data, SmlError **error)
00400 {
00401 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, error);
00402 CHECK_ERROR_REF
00403 smlAssert(data);
00404 SmlTransportHttpClientEnv *env = data;
00405
00406 smlAssert(env->tsp);
00407
00408 if (env->session)
00409 {
00410 if (!smlThreadCallFunction(env->tsp->thread, smlTransportHttpClientCleanupSession, env->session, error))
00411 goto error;
00412 env->session = NULL;
00413 }
00414
00415 if (env->uri)
00416 soup_uri_free(env->uri);
00417
00418 if (env->url)
00419 smlSafeCFree(&(env->url));
00420
00421 if (env->proxy)
00422 smlSafeCFree(&(env->proxy));
00423
00424 smlSafeFree((gpointer *)&env);
00425
00426 smlTrace(TRACE_EXIT, "%s", __func__);
00427 return TRUE;
00428 error:
00429 smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
00430 return FALSE;
00431 }
00432
00433 static void smlTransportHttpClientSend(void *userdata, void *link_, SmlTransportData *data, SmlError *error)
00434 {
00435 smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, userdata, link_, data, error);
00436 smlAssert(error || data);
00437 smlAssert(userdata);
00438 SmlTransportHttpClientEnv *env = userdata;
00439 smlAssert(env);
00440 smlAssert(env->uri);
00441
00442 if (error)
00443 goto error;
00444
00445 if (!env->connectDone)
00446 smlTransportHttpClientConnect(userdata);
00447
00448 SoupMessage *msg = soup_message_new_from_uri(SOUP_METHOD_POST, env->uri);
00449 if (!msg) {
00450 smlErrorSet(&error, SML_ERROR_GENERIC, "unknown libsoup error during message_new");
00451 goto error;
00452 }
00453
00454 const char *content_type;
00455 switch (data->type) {
00456 case SML_MIMETYPE_XML:
00457 content_type = SML_ELEMENT_XML;
00458 break;
00459 case SML_MIMETYPE_WBXML:
00460 content_type = SML_ELEMENT_WBXML;
00461 break;
00462 case SML_MIMETYPE_SAN:
00463 content_type = SML_ELEMENT_SAN;
00464 break;
00465 default:
00466 smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown Mimetype %d", data->type);
00467 goto error_free_message;
00468 }
00469
00470 soup_message_headers_append(msg->request_headers, "Accept", content_type);
00471 soup_message_set_request (msg, content_type,
00472 #ifdef HAVE_LIBSOUP22
00473 SOUP_BUFFER_SYSTEM_OWNED,
00474 #else
00475 SOUP_MEMORY_TAKE,
00476 #endif
00477 g_memdup(data->data, data->size), data->size);
00478
00479 #ifdef HAVE_LIBSOUP22
00480 smlTrace(TRACE_INTERNAL, "%s: data length: %i", __func__, msg->request.length);
00481
00482 #else
00483 smlTrace(TRACE_INTERNAL, "%s: data length: %i", __func__, msg->request_body->length);
00484 #endif
00485
00486 #ifdef HAVE_LIBSOUP22_SOLARIS
00487 soup_session_send_message(env->session, msg);
00488 smlTrace(TRACE_INTERNAL, "%s: sent message and starting callback", __func__);
00489 smlTransportHttpClientCallback(msg, userdata);
00490 g_object_unref(msg);
00491 #else
00492 soup_session_queue_message(env->session, msg, smlTransportHttpClientCallback, userdata);
00493 #endif
00494
00495 smlTrace(TRACE_EXIT, "%s", __func__);
00496 return;
00497
00498 error_free_message:
00499 g_object_unref(msg);
00500 error:
00501 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_ERROR, NULL, error);
00502 smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
00503 smlErrorDeref(&error);
00504 }
00505
00506 static void smlTransportHttpClientDisconnect(void *data, void *linkdata)
00507 {
00508 smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, linkdata);
00509 smlAssert(data);
00510 SmlTransportHttpClientEnv *env = data;
00511 smlAssert(env);
00512 smlAssert(env->tsp);
00513
00514 if (!env->connectDone && !env->session)
00515 {
00516
00517 env->disconnectDone = TRUE;
00518 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
00519 smlTrace(TRACE_EXIT, "%s - ignoring because never connected", __func__);
00520 return;
00521 }
00522
00523 smlAssert(env->session);
00524
00525
00526
00527
00528
00529
00530
00531
00532 soup_session_abort(env->session);
00533 g_object_unref(env->session);
00534 env->session = NULL;
00535
00536
00537 env->disconnectDone = FALSE;
00538 smlTransportReceiveEvent(env->tsp, NULL, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
00539
00540 smlTrace(TRACE_EXIT, "%s", __func__);
00541 }
00542
00543 SmlBool smlTransportHttpClientNew(SmlTransport *tsp, SmlError **error)
00544 {
00545 CHECK_ERROR_REF
00546 smlAssert(tsp);
00547
00548 #ifdef HAVE_LIBSOUP22_SOLARIS
00549 smlTrace(TRACE_INTERNAL, "%s: Enabled libsoup 2.2 Solaris workarounds", __func__);
00550 #endif
00551
00552 tsp->functions.set_config_option = smlTransportHttpClientSetConfigOption;
00553 tsp->functions.set_response_uri = smlTransportHttpClientSetResponseURI;
00554 tsp->functions.disconnect = smlTransportHttpClientDisconnect;
00555 tsp->functions.finalize = smlTransportHttpClientFinalize;
00556 tsp->functions.send = smlTransportHttpClientSend;
00557
00558 SmlTransportHttpClientEnv *env = smlTryMalloc0(sizeof(SmlTransportHttpClientEnv), error);
00559 if (!env)
00560 return FALSE;
00561 tsp->transport_data = env;
00562 env->tsp = tsp;
00563
00564 return TRUE;
00565 }
00566