1143 lines
34 KiB
C
1143 lines
34 KiB
C
/* $Id$
|
|
*
|
|
* Lasso - A free implementation of the Liberty Alliance specifications.
|
|
*
|
|
* Copyright (C) 2004-2007 Entr'ouvert
|
|
* http://lasso.entrouvert.org
|
|
*
|
|
* Authors: See AUTHORS file in top-level directory.
|
|
*
|
|
* This program 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/**
|
|
* SECTION:data_service
|
|
* @short_description: ID-WSF Data Service profile
|
|
*
|
|
* DataService allows Attribute Consumers (WSC) to request an Attribute Provider (WSP) to get
|
|
* or modify data about users with their consent.
|
|
*
|
|
* Following up on #LassoDiscovery first example, it created a @service object,
|
|
* this is a #LassoDataService instance. This example continues from that step
|
|
* and retrieves the name of the principal:
|
|
*
|
|
* <informalexample>
|
|
* <programlisting>
|
|
* char *soap_answer; // SOAP answer from data service
|
|
* xmlNode *principal_name; // libxml2 xmlNode with the principal name
|
|
*
|
|
* service = lasso_discovery_get_service(discovery);
|
|
* lasso_data_service_init_query(service, "/pp:PP/pp:InformalName", NULL);
|
|
* lasso_data_service_build_request_msg(service);
|
|
*
|
|
* // service must perform SOAP call to LASSO_WSF_PROFILE(service)->msg_url
|
|
* // the SOAP message is LASSO_WSF_PROFILE(service)->msg_body. The answer
|
|
* // is stored in char* soap_answer;
|
|
*
|
|
* lasso_data_service_process_query_response_msg(service, soap_answer);
|
|
* principal_name = lasso_data_service_get_answer(service, "/pp:PP/pp:InformalName");
|
|
*
|
|
* // app should probably then use xmlNodeGetContent libxml2 function to get
|
|
* // access to node content.
|
|
* </programlisting>
|
|
* </informalexample>
|
|
*
|
|
*/
|
|
|
|
#include "../xml/private.h"
|
|
#include "../utils.h"
|
|
#include <libxml/xpath.h>
|
|
#include <libxml/xpathInternals.h>
|
|
|
|
#include "discovery.h"
|
|
#include "data_service.h"
|
|
#include "../xml/dst_query.h"
|
|
#include "../xml/dst_query_response.h"
|
|
#include "../xml/dst_modify.h"
|
|
#include "../xml/dst_modify_response.h"
|
|
#include "../xml/soap_binding_correlation.h"
|
|
#include "../xml/soap_fault.h"
|
|
#include "../xml/is_redirect_request.h"
|
|
|
|
#include <xmlsec/xmltree.h>
|
|
#include <xmlsec/xmldsig.h>
|
|
#include <xmlsec/templates.h>
|
|
#include <xmlsec/crypto.h>
|
|
#include "./wsf_profile_private.h"
|
|
|
|
extern GHashTable *dst_services_by_prefix; /* cf xml/xml.c */
|
|
|
|
static void lasso_register_idwsf_xpath_namespaces(xmlXPathContext *xpathCtx);
|
|
static gint lasso_data_service_apply_query(LassoDataService *service,
|
|
LassoDstQueryResponse *query_response, xmlXPathContext *xpathCtx,
|
|
LassoDstQueryItem *item);
|
|
G_GNUC_UNUSED static gint lasso_data_service_apply_queries(LassoDataService *service,
|
|
LassoDstQueryResponse *query_response, xmlNode *data, GList *queries);
|
|
|
|
|
|
struct _LassoDataServicePrivate {
|
|
xmlNode *resource_data;
|
|
LassoDiscoResourceID *ResourceID;
|
|
LassoDiscoResourceID *EncryptedResourceID;
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
/* public methods */
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* lasso_data_service_init_query
|
|
* @service: a #LassoDataService
|
|
* @select: resource selection string (typically a XPath query)
|
|
* @item_id: query item identifier (optional)
|
|
*
|
|
* Initializes a new dst:Query request, asking for element @select (with optional itemID set to
|
|
* @item_id). @item_id may be NULL only if the query won't contain other query items. You must
|
|
* follow this constraint, it will not be checked.
|
|
*
|
|
* If both @select and @item_id are NULL, only a skeleton request is created and calls to
|
|
* lasso_data_service_add_query_item() will need to be done.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_data_service_init_query(LassoDataService *service, const char *select,
|
|
const char *item_id, G_GNUC_UNUSED const char *security_mech_id)
|
|
{
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
LassoDstQuery *query = NULL;
|
|
LassoDiscoResourceOffering *offering = NULL;
|
|
gint rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
wsf_profile = &service->parent;
|
|
|
|
/* 1. build the message content */
|
|
if (select) {
|
|
query = lasso_dst_query_new(lasso_dst_query_item_new(select, item_id));
|
|
} else {
|
|
query = lasso_dst_query_new(NULL);
|
|
}
|
|
lasso_assign_string(query->hrefServiceType, offering->ServiceInstance->ServiceType);
|
|
lasso_assign_new_string(query->prefixServiceType, lasso_get_prefix_for_dst_service_href(
|
|
query->hrefServiceType));
|
|
goto_cleanup_if_fail_with_rc (query->prefixServiceType != NULL,
|
|
LASSO_DATA_SERVICE_ERROR_UNREGISTERED_DST);
|
|
|
|
/* 2. build the envelope */
|
|
rc = lasso_wsf_profile_init_soap_request(wsf_profile, &query->parent);
|
|
|
|
cleanup:
|
|
lasso_release_gobject(query);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_add_query_item:
|
|
* @service: a #LassoDataService
|
|
* @select: resource selection string (typically a XPath query)
|
|
* @item_id: query item identifier
|
|
*
|
|
* Adds a dst:QueryItem to the current dst:Query request. If there are already query item in the
|
|
* request and @itemId is NULL, the call will fail.
|
|
*
|
|
* Return value: 0 if sucessfull, an error code otherwise.
|
|
**/
|
|
gint
|
|
lasso_data_service_add_query_item(LassoDataService *service,
|
|
const char *select, const char *item_id)
|
|
{
|
|
LassoWsfProfile *wsf_profile;
|
|
LassoDstQuery *query = NULL;
|
|
int rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
wsf_profile = &service->parent;
|
|
|
|
lasso_return_val_if_invalid_param(DST_QUERY, wsf_profile->request,
|
|
LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
query = (LassoDstQuery*)wsf_profile->request;
|
|
|
|
/** Check that we can add a new item */
|
|
if (query->QueryItem && (
|
|
(query->QueryItem->data &&
|
|
(! LASSO_IS_DST_QUERY_ITEM(query->QueryItem->data) ||
|
|
LASSO_DST_QUERY_ITEM(query->QueryItem->data)->itemID == NULL)) ||
|
|
(item_id == NULL))) {
|
|
return LASSO_DATA_SERVICE_CANNOT_ADD_ITEM;
|
|
}
|
|
|
|
lasso_list_add_new_gobject(query->QueryItem, lasso_dst_query_item_new(select, item_id));
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_get_query_item:
|
|
* @service: a #LassoDataService
|
|
* @select: the select string of the query item to found
|
|
* @item_id: the item id of the query item to found
|
|
* @output: a #LassoDstQueryItem handle to store the result object, its reference count is not
|
|
* incremented.
|
|
*
|
|
* Look up the first query item in the current request matching the given criteria, @select or
|
|
* @item_id. At least one of the criteria must be present for the call to succeed.
|
|
*
|
|
* Return value: 0 if successful, an error code otherwise.
|
|
*/
|
|
gint
|
|
lasso_data_service_get_query_item(LassoDataService *service,
|
|
const char *select, const char *item_id, LassoDstQueryItem **output)
|
|
{
|
|
LassoDstQuery *query = NULL;
|
|
GList *query_items = NULL;
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
gint rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
g_return_val_if_fail(select || item_id, LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ);
|
|
wsf_profile = &service->parent;
|
|
lasso_extract_node_or_fail(query, wsf_profile->request, DST_QUERY, LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
lasso_foreach(query_items, query->QueryItem)
|
|
{
|
|
LassoDstQueryItem *query_item = NULL;
|
|
lasso_extract_node_or_fail(query_item, query_items->data, DST_QUERY_ITEM, LASSO_ERROR_CAST_FAILED);
|
|
if ((select && g_strcmp0(select, query_item->Select)) ||
|
|
(item_id && g_strcmp0(item_id, query_item->itemID)))
|
|
{
|
|
if (output) {
|
|
lasso_assign_new_gobject(*output, query_item);
|
|
}
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_process_query_msg:
|
|
* @service: a #LassoDataService
|
|
* @message: the dst query message
|
|
*
|
|
* Processes a dst:Query message. Rebuilds a request object from the message
|
|
* and extracts ResourceID.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_data_service_process_query_msg(LassoDataService *service, const char *message,
|
|
const char *security_mech_id)
|
|
{
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
LassoDstQuery *query = NULL;
|
|
LassoDstQueryResponse *query_response = NULL;
|
|
gchar *service_type = NULL;
|
|
int rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
lasso_null_param(message);
|
|
wsf_profile = &service->parent;
|
|
|
|
rc = lasso_wsf_profile_process_soap_request_msg(wsf_profile, message, security_mech_id);
|
|
goto_cleanup_if_fail(! rc);
|
|
|
|
lasso_return_val_if_invalid_param(DST_QUERY, wsf_profile->request,
|
|
LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
|
|
query = (LassoDstQuery*)wsf_profile->request;
|
|
lasso_assign_string(service_type, query->hrefServiceType);
|
|
|
|
lasso_wsf_profile_helper_assign_resource_id(service->private_data, query);
|
|
query_response = lasso_dst_query_response_new(NULL);
|
|
/* FIXME: initialize the response object */
|
|
rc = lasso_wsf_profile_init_soap_response(wsf_profile, LASSO_NODE(query_response));
|
|
cleanup:
|
|
lasso_release_gobject(query_response);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/**
|
|
* lasso_data_service_build_response_msg:
|
|
* @service: a #LassoDataService
|
|
*
|
|
* Builds a dst:QueryResponse message.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_data_service_build_query_response_msg(LassoDataService *service)
|
|
{
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
LassoDstQuery *request = NULL;
|
|
LassoDstQueryResponse *response = NULL;
|
|
LassoSoapEnvelope *envelope = NULL;
|
|
gint rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
wsf_profile = &service->parent;
|
|
lasso_extract_node_or_fail(request, wsf_profile->request, DST_QUERY, LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
|
|
envelope = wsf_profile->soap_envelope_response;
|
|
|
|
response = lasso_dst_query_response_new(
|
|
lasso_utility_status_new(LASSO_DST_STATUS_CODE_OK));
|
|
wsf_profile->response = LASSO_NODE(response);
|
|
response->prefixServiceType = g_strdup(request->prefixServiceType);
|
|
response->hrefServiceType = g_strdup(request->hrefServiceType);
|
|
envelope->Body->any = g_list_append(envelope->Body->any, response);
|
|
|
|
|
|
return lasso_wsf_profile_build_soap_response_msg(wsf_profile);
|
|
cleanup:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_get_answers:
|
|
* @service: a #LassoDataService object.
|
|
* @output: an xmlNode** pointer where to put the xmlNode* of the result
|
|
*
|
|
* Get all the xmlNode content of the first Data element of the QueryResponse message.
|
|
*
|
|
* Return value: 0 if sucessful, an error code otherwise.
|
|
*/
|
|
gint
|
|
lasso_data_service_get_answers(LassoDataService *service, GList **output)
|
|
{
|
|
LassoDstQueryResponse *query_response = NULL;
|
|
LassoDstData *data = NULL;
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
GList *datas = NULL;
|
|
int rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
wsf_profile = &service->parent;
|
|
lasso_extract_node_or_fail(query_response, wsf_profile->request, DST_QUERY_RESPONSE,
|
|
LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
|
|
datas = query_response->Data;
|
|
|
|
if (datas) {
|
|
lasso_extract_node_or_fail(data, datas->data, DST_DATA,
|
|
LASSO_ERROR_CAST_FAILED);
|
|
}
|
|
|
|
if (data) {
|
|
if (output) {
|
|
GList *data_content = data->any;
|
|
lasso_release_list_of_xml_node(*output);
|
|
for (;data_content; data_content = g_list_next(data_content)) {
|
|
lasso_list_add_xml_node(*output, data_content->data);
|
|
}
|
|
}
|
|
} else {
|
|
rc = LASSO_DST_ERROR_NO_DATA;
|
|
}
|
|
|
|
cleanup:
|
|
return rc;
|
|
}
|
|
/**
|
|
* lasso_data_service_get_answer:
|
|
* @service: a #LassoDataService object.
|
|
* @output: an xmlNode** pointer where to put the xmlNode* of the result
|
|
*
|
|
* Get the first xmlNode of the first Data element of the QueryResponse message.
|
|
*
|
|
* Return value: 0 if sucessful, an error code otherwise.
|
|
*/
|
|
gint
|
|
lasso_data_service_get_answer(LassoDataService *service, xmlNode **output)
|
|
{
|
|
LassoDstQueryResponse *query_response = NULL;
|
|
LassoDstData *data = NULL;
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
GList *datas = NULL;
|
|
int rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
wsf_profile = &service->parent;
|
|
lasso_extract_node_or_fail(query_response, wsf_profile->request, DST_QUERY_RESPONSE,
|
|
LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
|
|
datas = query_response->Data;
|
|
|
|
if (datas) {
|
|
lasso_extract_node_or_fail(data, datas->data, DST_DATA,
|
|
LASSO_ERROR_CAST_FAILED);
|
|
}
|
|
|
|
if (data) {
|
|
if (output) {
|
|
xmlNode *first_element = NULL;
|
|
if (data->any) {
|
|
first_element = data->any->data;
|
|
}
|
|
lasso_assign_xml_node(*output, first_element);
|
|
}
|
|
} else {
|
|
rc = LASSO_DST_ERROR_NO_DATA;
|
|
}
|
|
|
|
cleanup:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_get_answers_by_select:
|
|
* @service: a #LassoDataService
|
|
* @select: resource selection string (typically a XPath query)
|
|
* @output: (allow-none) (element-type xmlNode): a GList** to store a GList* containing the result, it must be freed.
|
|
*
|
|
* Returns the answers for the specified @select request.
|
|
*
|
|
* Return value: 0 if successful, an error code otheriwse
|
|
*
|
|
**/
|
|
gint
|
|
lasso_data_service_get_answers_by_select(LassoDataService *service, const char *select, GList **output)
|
|
{
|
|
LassoDstQuery *query = NULL;
|
|
LassoDstQueryResponse *query_response = NULL;
|
|
LassoDstData *data = NULL;
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
LassoDstQueryItem *query_item = NULL;
|
|
GList *iter = NULL;
|
|
GList *datas = NULL;
|
|
int rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
lasso_null_param(select);
|
|
wsf_profile = &service->parent;
|
|
lasso_extract_node_or_fail(query, wsf_profile->request, DST_QUERY,
|
|
LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
lasso_extract_node_or_fail(query_response, wsf_profile->request, DST_QUERY_RESPONSE,
|
|
LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
|
|
iter = query->QueryItem;
|
|
datas = query_response->Data;
|
|
|
|
/* one query, no need for itemID, data is the first one, or absent */
|
|
if (iter && iter->next == NULL) {
|
|
lasso_extract_node_or_fail(query_item, iter->data, DST_QUERY_ITEM,
|
|
LASSO_ERROR_CAST_FAILED);
|
|
|
|
if (datas) {
|
|
lasso_extract_node_or_fail(data, datas->data, DST_DATA,
|
|
LASSO_ERROR_CAST_FAILED);
|
|
if (g_strcmp0(select, query_item->Select) != 0) {
|
|
data = NULL;
|
|
rc = LASSO_DST_ERROR_QUERY_NOT_FOUND;
|
|
}
|
|
} else {
|
|
rc = LASSO_DST_ERROR_NO_DATA;
|
|
}
|
|
/* many queries */
|
|
} else {
|
|
/* lookup select in query to get itemId, then get data with itemIdRef */
|
|
while (iter) {
|
|
lasso_extract_node_or_fail(query_item, iter->data, DST_QUERY_ITEM,
|
|
LASSO_ERROR_CAST_FAILED);
|
|
if (g_strcmp0(query_item->Select, select) == 0) {
|
|
break;
|
|
}
|
|
query_item = NULL;
|
|
iter = g_list_next(iter);
|
|
}
|
|
if (query_item && ! query_item->itemID) {
|
|
goto_cleanup_with_rc(LASSO_DST_ERROR_MALFORMED_QUERY);
|
|
}
|
|
|
|
while (datas) {
|
|
lasso_extract_node_or_fail(data, datas->data, DST_DATA,
|
|
LASSO_ERROR_CAST_FAILED);
|
|
if (g_strcmp0(data->itemIDRef, query_item->itemID) == 0) {
|
|
break;
|
|
}
|
|
data = NULL;
|
|
datas = g_list_next(datas);
|
|
}
|
|
}
|
|
|
|
if (data) {
|
|
if (output) {
|
|
GList *data_content = data->any;
|
|
lasso_release_list_of_xml_node(*output);
|
|
for (;data_content; data_content = g_list_next(data_content)) {
|
|
lasso_list_add_xml_node(*output, data_content->data);
|
|
}
|
|
}
|
|
} else {
|
|
rc = LASSO_DST_ERROR_NO_DATA;
|
|
}
|
|
|
|
cleanup:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_get_answer_for_item_id:
|
|
* @service: a #LassoDataService
|
|
* @item_id: query item identifier
|
|
* @output: (allow-none) (element-type xmlNode): a GList** to store a GList* containing the result, it must be freed.
|
|
*
|
|
* Returns the answers for the specified @itemID request.
|
|
*
|
|
* Return value: 0 if successful, an error code otherwise
|
|
*
|
|
**/
|
|
gint
|
|
lasso_data_service_get_answers_by_item_id(LassoDataService *service, const char *item_id, GList **output)
|
|
{
|
|
LassoDstQueryResponse *query_response = NULL;
|
|
LassoDstData *data = NULL;
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
GList *datas = NULL;
|
|
int rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
lasso_null_param(select);
|
|
wsf_profile = &service->parent;
|
|
lasso_extract_node_or_fail(query_response, wsf_profile->request, DST_QUERY_RESPONSE,
|
|
LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
|
|
datas = query_response->Data;
|
|
while (datas) {
|
|
lasso_extract_node_or_fail(data, datas->data, DST_DATA, LASSO_ERROR_CAST_FAILED);
|
|
if (g_strcmp0(data->itemIDRef, item_id) == 0) {
|
|
break;
|
|
}
|
|
data = NULL;
|
|
datas = g_list_next(datas);
|
|
}
|
|
|
|
if (data) {
|
|
if (output) {
|
|
GList *data_content = data->any;
|
|
lasso_release_list_of_xml_node(*output);
|
|
for (;data_content; data_content = g_list_next(data_content)) {
|
|
lasso_list_add_xml_node(*output, data_content->data);
|
|
}
|
|
}
|
|
} else {
|
|
rc = LASSO_DST_ERROR_NO_DATA;
|
|
}
|
|
|
|
cleanup:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_process_query_response_msg:
|
|
* @service: a #LassoDataService
|
|
* @message: the dst query response message
|
|
*
|
|
* Processes a dst:Query message. Rebuilds a request object from the message
|
|
* and extracts ResourcedID.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_data_service_process_query_response_msg(LassoDataService *service,
|
|
const char *message)
|
|
{
|
|
int rc = 0;
|
|
|
|
rc = lasso_wsf_profile_process_soap_response_msg(LASSO_WSF_PROFILE(service), message);
|
|
if (! rc && ! LASSO_IS_DST_QUERY_RESPONSE(service->parent.response)) {
|
|
rc = LASSO_PROFILE_ERROR_MISSING_RESPONSE;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_get_redirect_request_url:
|
|
* @service: a #LassoDataService
|
|
* @message: the dst query message
|
|
*
|
|
* Tells if Attribute Provider needs user interaction.
|
|
*
|
|
* Return value: TRUE if needed; or FALSE otherwise.
|
|
**/
|
|
gchar*
|
|
lasso_data_service_get_redirect_request_url(LassoDataService *service)
|
|
{
|
|
LassoSoapFault *fault = NULL;
|
|
LassoIsRedirectRequest *redirect_request = NULL;
|
|
GList *iter;
|
|
|
|
if (LASSO_WSF_PROFILE(service)->soap_envelope_response == NULL ||
|
|
LASSO_WSF_PROFILE(service)->soap_envelope_response->Body == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
iter = LASSO_WSF_PROFILE(service)->soap_envelope_response->Body->any;
|
|
while (iter) {
|
|
if (LASSO_IS_SOAP_FAULT(iter->data) == TRUE) {
|
|
fault = LASSO_SOAP_FAULT(iter->data);
|
|
break;
|
|
}
|
|
iter = iter->next;
|
|
}
|
|
if (fault == NULL || fault->Detail == NULL)
|
|
return NULL;
|
|
|
|
iter = fault->Detail->any;
|
|
while (iter) {
|
|
if (LASSO_IS_IS_REDIRECT_REQUEST(iter->data) == TRUE) {
|
|
redirect_request = LASSO_IS_REDIRECT_REQUEST(iter->data);
|
|
break;
|
|
}
|
|
iter = g_list_next(iter);
|
|
}
|
|
if (redirect_request == NULL)
|
|
return NULL;
|
|
|
|
return g_strdup(redirect_request->redirectURL);
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_init_modify:
|
|
* @service: a #LassoDataService object
|
|
* @select: an Select command (usually using the XPath syntax)
|
|
* @xmlData: the XML data for replacing the data
|
|
*
|
|
* Initialize a Data Service Template Modify request using a command to select some data, and an XML
|
|
* fragment to replace the selected data.
|
|
*
|
|
* Return value: 0 if successful, an error code otherwise.
|
|
*/
|
|
gint
|
|
lasso_data_service_init_modify(LassoDataService *service)
|
|
{
|
|
LassoDiscoResourceOffering *offering = NULL;
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
LassoDstModify *modify = NULL;
|
|
gint rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
lasso_null_param(service);
|
|
|
|
wsf_profile = &service->parent;
|
|
/* Build the Modify request */
|
|
modify = lasso_dst_modify_new(NULL);
|
|
|
|
offering = lasso_wsf_profile_get_resource_offering(wsf_profile);
|
|
goto_cleanup_if_fail_with_rc (offering, LASSO_PROFILE_ERROR_MISSING_RESOURCE_OFFERING);
|
|
goto_cleanup_if_fail_with_rc (offering->ServiceInstance != NULL &&
|
|
offering->ServiceInstance->ServiceType != NULL,
|
|
LASSO_PROFILE_ERROR_MISSING_SERVICE_TYPE);
|
|
lasso_assign_string(modify->hrefServiceType, offering->ServiceInstance->ServiceType);
|
|
lasso_assign_new_string(modify->prefixServiceType, lasso_get_prefix_for_dst_service_href(
|
|
modify->hrefServiceType));
|
|
goto_cleanup_if_fail_with_rc (modify->prefixServiceType != NULL, LASSO_DATA_SERVICE_ERROR_UNREGISTERED_DST);
|
|
lasso_wsf_profile_helper_assign_resource_id(modify, offering);
|
|
|
|
rc = lasso_wsf_profile_init_soap_request(wsf_profile, &modify->parent);
|
|
cleanup:
|
|
lasso_release_gobject(modify);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_add_modification:
|
|
* @service: a #LassoDataService object
|
|
* @select: a selector string
|
|
* @xmlData: optional NewData content
|
|
* @overrideAllowed: wheter to permit delete or replace of existings
|
|
* @notChagendSince: if not NULL, give the time (as a local time_t value) of the last known
|
|
* modification to the datas, it is used to permit secure concurrent accesses.
|
|
*
|
|
* Add a new modification to the current modify request. If overrideAllowed is FALSE, xmlData must
|
|
* absolutely be present. Refer to the ID-WSF DST 1.0 specification for the semantic of the created
|
|
* message.
|
|
*
|
|
* Return value: 0 if successful and the new modification object in *output, an error code
|
|
* otherwise.
|
|
*/
|
|
gint
|
|
lasso_data_service_add_modification(LassoDataService *service, const gchar *select,
|
|
xmlNode *xmlData, gboolean overrideAllowed, time_t *notChangedSince,
|
|
LassoDstModification **output)
|
|
{
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
LassoDstModification *modification = NULL;
|
|
LassoDstNewData *newData = NULL;
|
|
LassoDstModify *modify = NULL;
|
|
gint rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
lasso_null_param(select);
|
|
|
|
wsf_profile = &service->parent;
|
|
lasso_extract_node_or_fail(modify, wsf_profile->request, DST_MODIFY,
|
|
LASSO_ERROR_CAST_FAILED);
|
|
|
|
modification = lasso_dst_modification_new(select);
|
|
newData = lasso_dst_new_data_new();
|
|
lasso_release_list_of_xml_node(newData->any);
|
|
lasso_list_add_xml_node(newData->any, xmlData);
|
|
lasso_assign_new_gobject(modification->NewData, newData);
|
|
lasso_list_add_new_gobject(modify->Modification,
|
|
modification);
|
|
modification->overrideAllowed = overrideAllowed;
|
|
if (notChangedSince) {
|
|
lasso_assign_new_string(modification->notChangedSince,
|
|
lasso_time_to_iso_8601_gmt(*notChangedSince));
|
|
}
|
|
|
|
if (*output) {
|
|
lasso_assign_gobject(*output, modification);
|
|
}
|
|
|
|
cleanup:
|
|
return rc;
|
|
}
|
|
|
|
static gint
|
|
lasso_data_service_apply_modification(G_GNUC_UNUSED LassoDataService *service, G_GNUC_UNUSED LassoDstModification *modification, G_GNUC_UNUSED xmlXPathContext *xpath_ctxt)
|
|
{
|
|
gint rc = 0;
|
|
return rc;
|
|
}
|
|
|
|
G_GNUC_UNUSED static gint
|
|
lasso_data_service_apply_modifications(LassoDataService *service, GList *modifications, xmlXPathContext *xpath_ctxt)
|
|
{
|
|
gint rc = 0;
|
|
GList *i;
|
|
|
|
lasso_foreach(i, modifications) {
|
|
LassoDstModification *dst_modification;
|
|
|
|
if (LASSO_IS_DST_MODIFICATION(i->data)) {
|
|
rc = lasso_data_service_apply_modification(service, dst_modification, xpath_ctxt);
|
|
// First error, stop
|
|
if (rc) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
gint
|
|
lasso_data_service_build_modify_response_msg(LassoDataService *service)
|
|
{
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
LassoDstModify *request = NULL;
|
|
LassoDstModifyResponse *response = NULL;
|
|
GList *iter = NULL;
|
|
xmlNode *cur_data = NULL;
|
|
xmlDoc *doc = NULL;
|
|
xmlXPathContext *xpathCtx = NULL;
|
|
xmlXPathObject *xpathObj = NULL;
|
|
GList *node_to_free = NULL;
|
|
int res = 0;
|
|
xmlNode* resource_data;
|
|
gint rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
wsf_profile = &service->parent;
|
|
g_return_val_if_fail(service->private_data, LASSO_PARAM_ERROR_NON_INITIALIZED_OBJECT);
|
|
lasso_extract_node_or_fail(request, wsf_profile->request, DST_MODIFY,
|
|
LASSO_PROFILE_ERROR_MISSING_REQUEST);
|
|
resource_data = service->private_data->resource_data;
|
|
|
|
if (resource_data == NULL) {
|
|
return LASSO_DST_ERROR_MISSING_SERVICE_DATA;
|
|
} else {
|
|
cur_data = xmlCopyNode(resource_data, 1);
|
|
}
|
|
|
|
/* Build the Response object */
|
|
response = lasso_dst_modify_response_new(
|
|
lasso_utility_status_new(LASSO_DST_STATUS_CODE_OK));
|
|
lasso_assign_string(response->prefixServiceType, request->prefixServiceType);
|
|
lasso_assign_string(response->hrefServiceType, request->hrefServiceType);
|
|
|
|
/* Create the XPath workbench */
|
|
doc = xmlNewDoc((xmlChar*)"1.0");
|
|
xmlDocSetRootElement(doc, cur_data);
|
|
xpathCtx = xmlXPathNewContext(doc);
|
|
lasso_register_idwsf_xpath_namespaces(xpathCtx);
|
|
|
|
/* Apply modifications */
|
|
for (iter = request->Modification; iter != NULL; iter = g_list_next(iter)) {
|
|
LassoDstModification *modification = iter->data;
|
|
xmlNode *newNode = modification->NewData->any->data;
|
|
xpathObj = xmlXPathEvalExpression((xmlChar*)modification->Select,
|
|
xpathCtx);
|
|
if (xpathObj && xpathObj->nodesetval && xpathObj->nodesetval->nodeNr) {
|
|
xmlNode *node = xpathObj->nodesetval->nodeTab[0];
|
|
if (node != NULL) {
|
|
/* If we must replace the root element, change it in the xmlDoc */
|
|
if (node == cur_data) {
|
|
xmlDocSetRootElement(doc, xmlCopyNode(newNode,1));
|
|
lasso_list_add_xml_node(node_to_free, node);
|
|
cur_data = NULL;
|
|
} else {
|
|
xmlReplaceNode(node, xmlCopyNode(newNode,1));
|
|
/* Node is a free node now but is still reference by the xpath nodeset
|
|
we must wait for the deallocation of the nodeset to free it. */
|
|
lasso_list_add_xml_node(node_to_free, node);
|
|
}
|
|
}
|
|
} else {
|
|
res = LASSO_DST_ERROR_MODIFY_FAILED;
|
|
}
|
|
xmlXPathFreeObject(xpathObj);
|
|
xpathObj = NULL;
|
|
}
|
|
|
|
if (res == 0 && doc->children != NULL) {
|
|
/* Save new service resource data */
|
|
xmlNode *root = xmlDocGetRootElement(doc);
|
|
lasso_assign_xml_node(service->private_data->resource_data, root);
|
|
}
|
|
|
|
rc = lasso_wsf_profile_build_soap_response_msg(wsf_profile);
|
|
cleanup:
|
|
xmlXPathFreeContext(xpathCtx);
|
|
g_list_foreach(node_to_free, (GFunc)xmlFreeNode, NULL);
|
|
lasso_release_doc(doc);
|
|
lasso_release_list(node_to_free);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_process_modify_msg:
|
|
* @service: a #LassoDataService object
|
|
* @modify_soap_msg: the SOAP request string
|
|
* @security_mech_id: the security mechanism to apply
|
|
*
|
|
* Parse the given request message, and initialize needed structures.
|
|
*
|
|
* Return value: 0 if successful, an error code otherwise.
|
|
*/
|
|
gint
|
|
lasso_data_service_process_modify_msg(LassoDataService *service,
|
|
const gchar *modify_soap_msg, const gchar *security_mech_id)
|
|
{
|
|
LassoDstModify *modify = NULL;
|
|
LassoDstModifyResponse *modify_response = NULL;
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
int rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
lasso_null_param(modify_soap_msg);
|
|
|
|
wsf_profile = &service->parent;
|
|
|
|
rc = lasso_wsf_profile_process_soap_request_msg(wsf_profile, modify_soap_msg, security_mech_id);
|
|
goto_cleanup_if_fail(! rc);
|
|
goto_cleanup_if_fail_with_rc( ! LASSO_IS_DST_MODIFY(wsf_profile->request),
|
|
LASSO_PROFILE_ERROR_MISSING_RESPONSE);
|
|
|
|
modify = (LassoDstModify*)wsf_profile->request;
|
|
lasso_wsf_profile_helper_assign_resource_id(service->private_data, modify);
|
|
modify_response = lasso_dst_modify_response_new(NULL);
|
|
rc = lasso_wsf_profile_init_soap_response(wsf_profile, LASSO_NODE(modify_response));
|
|
cleanup:
|
|
lasso_release_gobject(modify_response);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_process_modify_response_msg
|
|
* @service: a #LassoDataService
|
|
* @soap_msg: the SOAP message
|
|
*
|
|
* Process a modify response message.
|
|
*
|
|
* Return value: 0 on success; or a negative value otherwise.
|
|
**/
|
|
gint
|
|
lasso_data_service_process_modify_response_msg(LassoDataService *service, const gchar *soap_msg)
|
|
{
|
|
int rc = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
lasso_null_param(soap_msg);
|
|
|
|
rc = lasso_wsf_profile_process_soap_response_msg(&service->parent, soap_msg);
|
|
|
|
if (! LASSO_IS_DST_MODIFY_RESPONSE(service->parent.response)) {
|
|
rc = LASSO_PROFILE_ERROR_MISSING_RESPONSE;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* private methods */
|
|
/*****************************************************************************/
|
|
|
|
static LassoNodeClass *parent_class = NULL;
|
|
|
|
static void
|
|
register_xpath_namespace(gchar *prefix, gchar *href, xmlXPathContext *xpathCtx)
|
|
{
|
|
xmlXPathRegisterNs(xpathCtx, (xmlChar*)prefix, (xmlChar*)href);
|
|
}
|
|
|
|
static void
|
|
lasso_register_idwsf_xpath_namespaces(xmlXPathContext *xpathCtx)
|
|
{
|
|
xmlXPathRegisterNs(xpathCtx, (xmlChar*)LASSO_PP_PREFIX,
|
|
(xmlChar*)LASSO_PP_HREF);
|
|
xmlXPathRegisterNs(xpathCtx, (xmlChar*)LASSO_EP_PREFIX,
|
|
(xmlChar*)LASSO_EP_HREF);
|
|
if (dst_services_by_prefix == NULL)
|
|
return;
|
|
g_hash_table_foreach(dst_services_by_prefix,
|
|
(GHFunc)register_xpath_namespace, xpathCtx);
|
|
}
|
|
|
|
static gint
|
|
lasso_data_service_apply_query(LassoDataService *service, LassoDstQueryResponse *query_response, xmlXPathContext *xpathCtx, LassoDstQueryItem *item)
|
|
{
|
|
gint rc = 0;
|
|
xmlXPathObject *xpathObj = NULL;
|
|
gint xpath_error_code = 0;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
lasso_bad_param(DST_QUERY_RESPONSE, query_response);
|
|
lasso_bad_param(DST_QUERY_ITEM, item);
|
|
|
|
if (! item->Select) {
|
|
lasso_wsf_profile_helper_set_status(query_response, LASSO_DST_STATUS_CODE_FAILED);
|
|
lasso_wsf_profile_helper_set_status(query_response->Status, LASSO_DST_STATUS_CODE_MISSING_SELECT);
|
|
goto_cleanup_with_rc(1);
|
|
}
|
|
|
|
if (lasso_eval_xpath_expression(xpathCtx, item->Select, &xpathObj, &xpath_error_code)) {
|
|
LassoDstData *data = NULL;
|
|
|
|
/* Found zero or more node answers */
|
|
if (xpathObj && xpathObj->type == XPATH_NODESET){
|
|
int i = 0;
|
|
|
|
if (xpathObj->nodesetval && xpathObj->nodesetval->nodeNr) {
|
|
data = lasso_dst_data_new();
|
|
}
|
|
|
|
for (i = 0; xpathObj->nodesetval && i < xpathObj->nodesetval->nodeNr;
|
|
i++) {
|
|
lasso_list_add_xml_node(data->any,
|
|
xpathObj->nodesetval->nodeTab[i]);
|
|
}
|
|
/* Found other kind of answers, convert to string */
|
|
} else {
|
|
xmlChar *str;
|
|
|
|
data = lasso_dst_data_new();
|
|
str = xmlXPathCastToString(xpathObj);
|
|
lasso_list_add_xml_node(data->any, xmlNewText(str));
|
|
lasso_release_xml_string(str);
|
|
}
|
|
if (data) {
|
|
lasso_assign_string(data->itemIDRef, item->itemID);
|
|
lasso_list_add_gobject(query_response->Data, data);
|
|
}
|
|
lasso_release_gobject(data);
|
|
} else {
|
|
char *code = g_strdup_printf("LIBXML_XPATH_ERROR_%d", xpath_error_code);
|
|
|
|
lasso_wsf_profile_helper_set_status(query_response, LASSO_DST_STATUS_CODE_FAILED);
|
|
lasso_wsf_profile_helper_set_status(query_response->Status, LASSO_DST_STATUS_CODE_INVALID_SELECT);
|
|
lasso_wsf_profile_helper_set_status(query_response->Status->Status, code);
|
|
lasso_release_string(code);
|
|
goto_cleanup_with_rc(1);
|
|
}
|
|
|
|
cleanup:
|
|
lasso_release_xpath_object(xpathObj);
|
|
return rc;
|
|
}
|
|
|
|
G_GNUC_UNUSED static gint
|
|
lasso_data_service_apply_queries(LassoDataService *service, LassoDstQueryResponse *query_response, xmlNode *data, GList *queries)
|
|
{
|
|
gint rc = 0;
|
|
LassoWsfProfile *wsf_profile = NULL;
|
|
xmlDoc *doc = NULL;
|
|
xmlXPathContext *xpathCtx = NULL;
|
|
|
|
lasso_bad_param(DATA_SERVICE, service);
|
|
g_return_val_if_fail(service->private_data, LASSO_PARAM_ERROR_NON_INITIALIZED_OBJECT);
|
|
wsf_profile = &service->parent;
|
|
|
|
/* 1. Check query */
|
|
if (queries && queries->next) {
|
|
GList *q = queries;
|
|
while (q) {
|
|
if (! LASSO_IS_DST_QUERY_ITEM(q->data) || !
|
|
LASSO_DST_QUERY_ITEM(q->data)->itemID) {
|
|
lasso_wsf_profile_helper_set_status(query_response,
|
|
LASSO_DST_STATUS_CODE_FAILED);
|
|
lasso_wsf_profile_helper_set_status(query_response->Status,
|
|
"itemID expected");
|
|
goto_cleanup_with_rc(LASSO_DST_ERROR_QUERY_FAILED);
|
|
}
|
|
q = g_list_next(q);
|
|
}
|
|
}
|
|
|
|
/* 1. Setup workbench */
|
|
doc = xmlNewDoc((xmlChar*)"1.0");
|
|
xmlDocSetRootElement(doc, data);
|
|
xpathCtx = xmlXPathNewContext(doc);
|
|
lasso_register_idwsf_xpath_namespaces(xpathCtx);
|
|
|
|
lasso_foreach (queries, queries) {
|
|
LassoDstQueryItem *item = queries->data;
|
|
|
|
goto_cleanup_if_fail_with_rc(lasso_data_service_apply_query(service, query_response,
|
|
xpathCtx, item) == 0, query_response->Data ?
|
|
LASSO_DST_ERROR_QUERY_PARTIALLY_FAILED :
|
|
LASSO_DST_ERROR_QUERY_FAILED);
|
|
}
|
|
|
|
cleanup:
|
|
xmlUnlinkNode(service->private_data->resource_data);
|
|
xmlSetTreeDoc(service->private_data->resource_data, NULL);
|
|
lasso_release_xpath_context(xpathCtx);
|
|
lasso_release_doc(doc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* overrided parent class methods */
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
dispose(GObject *object)
|
|
{
|
|
LassoDataService *service = LASSO_DATA_SERVICE(object);
|
|
|
|
lasso_release_xml_node(service->private_data->resource_data);
|
|
lasso_release_gobject(service->private_data->ResourceID);
|
|
lasso_release_gobject(service->private_data->EncryptedResourceID);
|
|
|
|
G_OBJECT_CLASS(parent_class)->dispose(object);
|
|
}
|
|
|
|
static void
|
|
finalize(GObject *object)
|
|
{
|
|
g_free(((LassoDataService*)object)->private_data);
|
|
|
|
G_OBJECT_CLASS(parent_class)->finalize(object);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* instance and class init functions */
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
instance_init(LassoDataService *service)
|
|
{
|
|
service->private_data = g_new0(LassoDataServicePrivate, 1);
|
|
}
|
|
|
|
static void
|
|
class_init(LassoDataServiceClass *klass)
|
|
{
|
|
parent_class = g_type_class_peek_parent(klass);
|
|
|
|
G_OBJECT_CLASS(klass)->dispose = dispose;
|
|
G_OBJECT_CLASS(klass)->finalize = finalize;
|
|
}
|
|
|
|
GType
|
|
lasso_data_service_get_type()
|
|
{
|
|
static GType this_type = 0;
|
|
|
|
if (!this_type) {
|
|
static const GTypeInfo this_info = {
|
|
sizeof(LassoDataServiceClass),
|
|
NULL,
|
|
NULL,
|
|
(GClassInitFunc) class_init,
|
|
NULL,
|
|
NULL,
|
|
sizeof(LassoDataService),
|
|
0,
|
|
(GInstanceInitFunc) instance_init,
|
|
NULL
|
|
};
|
|
|
|
this_type = g_type_register_static(LASSO_TYPE_WSF_PROFILE,
|
|
"LassoDataService", &this_info, 0);
|
|
}
|
|
return this_type;
|
|
}
|
|
|
|
|
|
/**
|
|
* lasso_data_service_new:
|
|
* @server: the #LassoServer
|
|
*
|
|
* Creates a new #LassoDataService.
|
|
*
|
|
* Return value: a newly created #LassoDataService object; or NULL if an
|
|
* error occured.
|
|
**/
|
|
LassoDataService*
|
|
lasso_data_service_new(LassoServer *server)
|
|
{
|
|
LassoDataService *service;
|
|
|
|
g_return_val_if_fail(LASSO_IS_SERVER(server), NULL);
|
|
|
|
service = g_object_new(LASSO_TYPE_DATA_SERVICE, NULL);
|
|
LASSO_WSF_PROFILE(service)->server = g_object_ref(server);
|
|
|
|
return service;
|
|
}
|
|
|
|
/**
|
|
* lasso_data_service_new_full:
|
|
* @server: the #LassoServer
|
|
* @offering: the #LassoDiscoResourceOffering
|
|
*
|
|
* Creates a new #LassoDataService.
|
|
*
|
|
* Return value: a newly created #LassoDataService object; or NULL if an error occured.
|
|
**/
|
|
LassoDataService*
|
|
lasso_data_service_new_full(LassoServer *server, LassoDiscoResourceOffering *offering)
|
|
{
|
|
LassoDataService *service = lasso_data_service_new(server);
|
|
|
|
g_return_val_if_fail(LASSO_IS_DISCO_RESOURCE_OFFERING(offering), NULL);
|
|
|
|
if (service == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
lasso_wsf_profile_set_resource_offering(&service->parent, offering);
|
|
|
|
return service;
|
|
}
|