doubango/trunk/tinyXCAP/tinyXCAP/src/xdm.c

498 lines
14 KiB
C

/****************************************************************************
_ _
| | | |
_ | | ___ _ _| | _ ____ ____ ____ ___
/ || |/ _ \| | | | || \ / _ | _ \ / _ |/ _ \
( (_| | |_| | |_| | |_) | ( | | | | ( ( | | |_| |
\____|\___/ \____|____/ \_||_|_| |_|\_|| |\___/
(_____|
Copyright (C) 2009 xxxyyyzzz <imsframework(at)gmail.com>
This file is part of Open Source Doubango IMS Client Framework project.
DOUBANGO is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DOUBANGO is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU General Public License
along with DOUBANGO.
****************************************************************************/
#include "xdm.h"
#include "xdm_utils.h"
#define PANIC_AND_JUMP(code, request)\
{\
request->panic = code; \
goto bail;\
}
/* get content type */
static const char* get_mime_type(const xdm_context_t* context, const xdm_request_t* request, xdm_selection_type_t sel)
{
/* return the user provided content type */
if(request && request->content && request->content->type) return request->content->type;
switch(sel)
{
case sl_element:return XDM_MIME_TYPE_ELEMENT;
case sl_document:
{
const xdm_auid_t* auid = xdm_auid_findby_name(context->auids, request->auid);
return auid?auid->content_type:"application/unknown+xml";
}
case sl_attribute:return XDM_MIME_TYPE_ATTRIBUTE;
default: return "application/unknown+xml";
}/* switch */
}
/* init content */
void xdm_content_init(xdm_content_t *content)
{
memset(content, 0, sizeof(xdm_content_t));
}
/* set content values */
void xdm_content_set(xdm_content_t* content, const char *data, size_t size, const char *type)
{
if(content)
{
xdm_strupdate(&(content->data), data);
xdm_strupdate(&(content->type), type);
content->size = size;
}
}
/* free content */
void xdm_content_free(xdm_content_t** content)
{
if(*content)
{
XDM_FREE((*content)->data);
XDM_FREE((*content)->type);
free(*content);
(*content) = 0;
}
}
/* init auid */
void xdm_auid_init(xdm_auid_t *auid)
{
memset(auid, 0, sizeof(xdm_auid_t));
}
/* find auid object by type */
xdm_auid_t* xdm_auid_findby_type(const xdm_auid_L_t* auids, xcap_auid_type_t auid_type)
{
xdm_list_item_t* item = 0;
if(!auids) return 0;
xdm_list_foreach(item, auids)
{
xdm_auid_t *auid = ((xdm_auid_t*)item->data);
if(auid->type == auid_type)
{
return auid;
}
}
return 0;
}
/* find auid object by type */
xdm_auid_t* xdm_auid_findby_name(const xdm_auid_L_t* auids, const char* name)
{
xdm_list_item_t* item = 0;
if(!auids) return 0;
xdm_list_foreach(item, auids)
{
xdm_auid_t *auid = ((xdm_auid_t*)item->data);
if(!xdm_stricmp(auid->name, name))
{
return auid;
}
}
return 0;
}
/* returns 1 if availabe and 0 otherwise */
int xdm_auid_is_available(const xdm_auid_L_t* auids, xcap_auid_type_t type)
{
const xdm_auid_t *auid = xdm_auid_findby_type(auids, type);
return (auid && auid->available) ? 1 : 0;
}
/* change auid availability */
void xdm_auid_set_availability(const xdm_auid_L_t* auids, xcap_auid_type_t type, int avail)
{
xdm_auid_t* auid = xdm_auid_findby_type(auids, type);
if(auid)
{
auid->available = avail;
}
}
/* change the document name of the auid with type=@type */
void xdm_auid_update(xdm_context_t* context, xcap_auid_type_t type, const char* document)
{
xdm_auid_t* auid = xdm_auid_findby_type(context->auids, type);
if(auid)
{
xdm_strupdate(&(auid->document), document);
}
}
/* free auid */
void xdm_auid_free(void **_auid)
{
xdm_auid_t **auid = (xdm_auid_t **)_auid;
XDM_FREE((*auid)->name);
XDM_FREE((*auid)->description);
XDM_FREE((*auid)->content_type);
XDM_FREE((*auid)->document);
free(*_auid);
(*_auid) = 0;
}
/* initialize xdm context */
/* ATTENTION: context MUST be initialized before use*/
void xdm_context_init(xdm_context_t* context)
{
int i;
memset(context, 0, sizeof(xdm_context_t));
/* copy all default auids */
XDM_LIST_CREATE(context->auids);
for(i = 0; i< (sizeof(xdm_auids)/sizeof(xdm_auid_t)); i++)
{
xdm_auid_t* auid = 0;
XDM_AUID_CREATE(auid);
auid->type = xdm_auids[i].type;
auid->available = xdm_auids[i].available;
auid->content_type = xdm_strdup(xdm_auids[i].content_type);
auid->description = xdm_strdup(xdm_auids[i].description);
auid->document = xdm_strdup(xdm_auids[i].document);
auid->name = xdm_strdup(xdm_auids[i].name);
xdm_list_add_data((context->auids), ((void**)&auid), xdm_auid_free);
}
/* initialize libcurl */
#ifdef WIN32
curl_global_init(CURL_GLOBAL_WIN32);
#else
curl_global_init();
#endif
}
/* update available auids using those provided by the xdms */
void xdm_conttext_update_available_auids(xdm_context_t *context, const xdm_list_t* avail_auids)
{
xdm_list_item_t *item = 0;
if(!context || !avail_auids) return;
/* avail auids */
xdm_list_foreach(item, avail_auids)
{
/* context auids */
xdm_auid_t* auid = xdm_auid_findby_name(context->auids, ((const char*)item->data));
if(auid && !(auid->available)) auid->available = 1;
}
}
/* free a previously initialized context */
void xdm_context_free(xdm_context_t** context)
{
XDM_FREE((*context)->xdm_root);
XDM_FREE((*context)->xui);
XDM_FREE((*context)->password);
XDM_FREE((*context)->proxy_host);
XDM_FREE((*context)->proxy_usr);
XDM_FREE((*context)->proxy_pwd);
XDM_FREE((*context)->user_agent);
XDM_FREE((*context)->pragma);
XDM_LIST_SAFE_FREE((*context)->auids);
XDM_EASYHANDLE_SAFE_FREE((*context)->easyhandle);
free(*context);
(*context) = 0;
/* cleanup libcurl */
curl_global_cleanup();
}
/* initialize xdm request */
/* ATTENTION: request MUST be initialized before use*/
void xdm_request_init(xdm_request_t* request)
{
memset(request, 0, sizeof(xdm_request_t));
XDM_CONTENT_CREATE(request->content);
request->status = 0;
request->panic = xpa_success;
request->timeout = XDM_HTTP_DEFAULT_TIMEOUT;
}
/* free a previously initialized request */
void xdm_request_free(xdm_request_t** request)
{
XDM_FREE((*request)->auid);
XDM_FREE((*request)->url);
XDM_FREE((*request)->http_accept);
XDM_FREE((*request)->http_expect);
XDM_CONTENT_SAFE_FREE((*request)->content);
free(*request);
(*request) = 0;
}
/* libcurl write callback */
size_t write_data(void *buffer, size_t size, size_t nmemb, xdm_content_t *userp)
{
size_t total_size = (size * nmemb);
if(!userp) return -1;
/* new chinck ==> realloc */
userp->data = (char *)realloc(userp->data, (1 + userp->size + total_size));
if (userp->data)
{ /* append new chunck */
memcpy(userp->data + userp->size, buffer, total_size);
userp->size += total_size;
userp->data[userp->size] = '\0';
}
return total_size;
}
/* libcurl read callback */
size_t read_data(void *buffer, size_t size, size_t nmemb, xdm_content_t *userp)
{
size_t bytes_to_copy = 0;
if(!userp) return -1;
if(userp->cursor >= userp->size) return 0;
bytes_to_copy = nmemb < userp->size ? nmemb : userp->size;
memcpy(buffer, userp->data, bytes_to_copy);
userp->cursor += bytes_to_copy;
return bytes_to_copy;
}
/* ioctl function to handle HTTP PUT or POST with a multi-pass authentication method */
curlioerr ioctl_callback(CURL *handle, int cmd, xdm_content_t *userp)
{
switch (cmd)
{
case CURLIOCMD_NOP: return CURLIOE_OK;
case CURLIOCMD_RESTARTREAD:
{
userp->cursor = 0;
return CURLIOE_OK;
}
default: return CURLIOE_UNKNOWNCMD;
}
}
/* perform xcap operation */
/* returns 0 if success and false otherwise. check request panic code for more information */
/* if request panic code is equal to 'xpa_libcurl_error', then you should check the return code*/
int xdm_xcap_perform(xdm_context_t* context, xdm_request_t* request, xdm_oper_type_t oper, xdm_selection_type_t sel)
{
struct curl_slist *headers = 0;
CURLcode code = CURLE_OK;
char* temp_str = 0;
/* check context */
XDM_CONTEXT_CHECK(context, request->panic);
/* Get an easy handle */
if(!context->easyhandle) context->easyhandle = curl_easy_init();
else if(!(context->reuse_http_connection)){
curl_easy_cleanup(context->easyhandle);
context->easyhandle = curl_easy_init();
}
if(!context->easyhandle) PANIC_AND_JUMP(xpa_libcurl_error, request)
#if DEBUG || _DEBUG
curl_easy_setopt(context->easyhandle, CURLOPT_VERBOSE, 1);
#endif
/* set xcap URL */
if(request->url && (code=curl_easy_setopt(context->easyhandle, CURLOPT_URL, request->url)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* set request timeout */
if(code=curl_easy_setopt(context->easyhandle, CURLOPT_TIMEOUT, request->timeout/1000))
PANIC_AND_JUMP(xpa_libcurl_error, request)
switch(oper)
{
case op_fetch:
{ /* GET */
curl_easy_setopt(context->easyhandle, CURLOPT_CUSTOMREQUEST, "GET");
/* tell libcurl to pass all data to this function */
if(code=curl_easy_setopt(context->easyhandle, CURLOPT_WRITEFUNCTION, write_data))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* set data for our callback */
if(code=curl_easy_setopt(context->easyhandle, CURLOPT_WRITEDATA, (request->content)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
break;
}
case op_create:
case op_replace:
{ /* PUT */
curl_easy_setopt(context->easyhandle, CURLOPT_CUSTOMREQUEST, "PUT");
/* read callback */
if(code=curl_easy_setopt(context->easyhandle, CURLOPT_READFUNCTION, read_data))
PANIC_AND_JUMP(xpa_libcurl_error, request)
if(code=curl_easy_setopt(context->easyhandle, CURLOPT_UPLOAD, 1L))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* content */
if(request->content && request->content->data && request->content->size)
{
/* content data*/
if(code=curl_easy_setopt(context->easyhandle, CURLOPT_READDATA , (request->content)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* content length */
xdm_sprintf(&temp_str, "Content-Length: %u", request->content->size);
headers = curl_slist_append(headers, temp_str);
XDM_SAFE_FREE(temp_str);
/* ioctl function callback */
if(code=curl_easy_setopt(context->easyhandle, CURLOPT_IOCTLFUNCTION , ioctl_callback))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* ioctl data */
if(code=curl_easy_setopt(context->easyhandle, CURLOPT_IOCTLDATA , (request->content)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
}
break;
}
case op_delete:
{ /* DELETE */
curl_easy_setopt(context->easyhandle, CURLOPT_CUSTOMREQUEST, "DELETE");
break;
}
}/* switch */
/* set user name */
if(context->xui && (code=curl_easy_setopt(context->easyhandle, CURLOPT_USERNAME, context->xui)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* set user password */
if(context->password && (code=curl_easy_setopt(context->easyhandle, CURLOPT_PASSWORD, context->password)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* authentication type */
if((code=curl_easy_setopt(context->easyhandle, CURLOPT_HTTPAUTH, CURLAUTH_ANY)) ||
(code=curl_easy_setopt(context->easyhandle, CURLOPT_PROXYAUTH, CURLAUTH_ANY)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* set user-agent */
if(context->user_agent && (code=curl_easy_setopt(context->easyhandle, CURLOPT_USERAGENT, context->user_agent)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* X-3GPP-Intended-Identity */
xdm_sprintf(&temp_str, "X-3GPP-Intended-Identity: \"%s\"", context->xui);
headers = curl_slist_append(headers, temp_str);
XDM_SAFE_FREE(temp_str);
/* Content-Type */
xdm_sprintf(&temp_str, "Content-Type: %s", get_mime_type(context, request, sel));
headers = curl_slist_append(headers, temp_str);
XDM_SAFE_FREE(temp_str);
/* set proxy host */
if(context->proxy_host && (code=curl_easy_setopt(context->easyhandle, CURLOPT_PROXY, context->proxy_host)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* set proxy port */
if(context->proxy_port && (code=curl_easy_setopt(context->easyhandle, CURLOPT_PROXYPORT, context->proxy_port)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* set proxy usr*/
if(context->proxy_usr && (code=curl_easy_setopt(context->easyhandle, CURLOPT_PROXYUSERNAME, context->proxy_usr)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* set proxy pwd */
if(context->proxy_pwd && (code=curl_easy_setopt(context->easyhandle, CURLOPT_PROXYPASSWORD, context->proxy_pwd)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* http proxy tunneling? */
if(context->proxy_tunneling && (code=curl_easy_setopt(context->easyhandle, CURLOPT_HTTPPROXYTUNNEL, 1L)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* Pragma */
if(context->pragma)
{
xdm_sprintf(&temp_str, "Pragma: %s", context->pragma);
headers = curl_slist_append(headers, temp_str);
XDM_SAFE_FREE(temp_str);
}
/* Accept */
if(request->http_accept)
{
xdm_sprintf(&temp_str, "Accept: \"%s\"", request->http_accept);
headers = curl_slist_append(headers, temp_str);
XDM_SAFE_FREE(temp_str);
}
else headers = curl_slist_append(headers, "Accept:");
/* Expect */
if(request->http_expect)
{
xdm_sprintf(&temp_str, "Expect: %s", request->http_expect);
headers = curl_slist_append(headers, temp_str);
XDM_SAFE_FREE(temp_str);
}
/* pass our list of custom made headers */
if(code=curl_easy_setopt(context->easyhandle, CURLOPT_HTTPHEADER, headers))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/*** perform operation ***/
if(code=curl_easy_perform(context->easyhandle))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* get response code */
if(code=curl_easy_getinfo(context->easyhandle, CURLINFO_RESPONSE_CODE, &(request->status)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
/* get response content-type */
if(code=curl_easy_getinfo(context->easyhandle, CURLINFO_CONTENT_TYPE, (&temp_str)))
PANIC_AND_JUMP(xpa_libcurl_error, request)
else request->content->type = xdm_strdup((const char*)temp_str);
bail:
/* free the header list */
curl_slist_free_all(headers);
/* cleanup libcurl handle if !stateful*/
if(!(context->reuse_http_connection))
XDM_EASYHANDLE_SAFE_FREE(context->easyhandle);
return code;
}