/* $Id: idwsf2_data_service.c 3101 2007-05-30 11:40:10Z dlaniel $ * * 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:idwsf2_data_service * @short_description: ID-WSF 2.0 Data Service profile * * DataService allows Attribute Consumers (WSC) to request an Attribute Provider (WSP) to get * or modify data about users with their consent. */ #include "../xml/private.h" #include #include #include "discovery.h" #include "data_service.h" #include "../xml/id-wsf-2.0/disco_service_type.h" #include "../xml/id-wsf-2.0/dstref_query.h" #include "../xml/id-wsf-2.0/dstref_query_response.h" #include "../xml/id-wsf-2.0/dstref_data.h" #include "../xml/id-wsf-2.0/util_status.h" #include "../xml/id-wsf-2.0/sb2_redirect_request.h" #include "../xml/id-wsf-2.0/dstref_modify.h" #include "../xml/id-wsf-2.0/dstref_modify_item.h" #include "../xml/id-wsf-2.0/dstref_modify_response.h" #include "../xml/soap_fault.h" #include "../utils.h" #include "./private.h" struct _LassoIdWsf2DataServicePrivate { gboolean dispose_has_run; LassoWsAddrEndpointReference *epr; GList *credentials; }; extern GHashTable *idwsf2_dst_services_by_prefix; /* cf xml/xml.c */ static void lasso_register_idwsf2_xpath_namespaces(xmlXPathContext *xpathCtx); static GList* duplicate_glist_of_xmlnodes(GList*); static gint lasso_idwsf2_data_service_parse_one_modify_item(LassoIdWsf2DstRefModifyItem *item, xmlDoc *cur_doc, xmlXPathContext *cur_xpathCtx, int *error_code_ptr); /*****************************************************************************/ /* public methods */ /*****************************************************************************/ /** * lasso_idwsf2_data_service_init_query: * @service: a #LassoIdWsf2DataService * * Initialise an ID-WSF 2.0 DataService query request. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_init_query(LassoIdWsf2DataService *service) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2DstRefQuery *query; LassoWsAddrEndpointReference *epr; GList *metadata_item; GList *i; gchar *service_type = NULL; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); query = lasso_idwsf2_dstref_query_new(); if (LASSO_PROFILE(profile)->request) { lasso_node_destroy(LASSO_NODE(LASSO_PROFILE(profile)->request)); } lasso_assign_new_gobject(LASSO_PROFILE(profile)->request, LASSO_NODE(query)); if (service == NULL || service->private_data == NULL || service->private_data->epr == NULL || service->private_data->epr->Metadata == NULL) { return LASSO_PROFILE_ERROR_MISSING_ENDPOINT_REFERENCE; } epr = service->private_data->epr; /* Get the service type from the EPR */ metadata_item = epr->Metadata->any; for (i = g_list_first(metadata_item); i != NULL; i = g_list_next(i)) { if (LASSO_IS_IDWSF2_DISCO_SERVICE_TYPE(i->data)) { service_type = LASSO_IDWSF2_DISCO_SERVICE_TYPE(i->data)->content; break; } } /* Set hrefServiceType and prefixServiceType in query in order to set the profile */ /* namespace in the request */ if (service_type != NULL) { query->hrefServiceType = g_strdup(service_type); query->prefixServiceType = lasso_get_prefix_for_idwsf2_dst_service_href( query->hrefServiceType); } if (query->prefixServiceType == NULL) { return LASSO_DATA_SERVICE_ERROR_UNREGISTERED_DST; } lasso_idwsf2_profile_init_soap_request(profile, LASSO_NODE(query), service_type); /* Set msg_url as epr address, which is the SoapEndpoint */ if (epr->Address != NULL) { lasso_assign_string(LASSO_PROFILE(profile)->msg_url, epr->Address->content); } else { return LASSO_PROFILE_ERROR_MISSING_ENDPOINT_REFERENCE_ADDRESS; } return 0; } /** * lasso_idwsf2_data_service_add_query_item: * @service: a #LassoIdWsf2DataService * @item_xpath: XPATH of the queried item * @item_id: identifier of the queried item, which will allow to retrieve it in the response * * Add an item in the query request. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_add_query_item(LassoIdWsf2DataService *service, const gchar *item_xpath, const gchar *item_id) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2DstRefQuery *query; LassoIdWsf2DstRefQueryItem *item; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); g_return_val_if_fail(item_xpath != NULL, LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); g_return_val_if_fail(item_id != NULL, LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); if (! LASSO_IS_IDWSF2_DSTREF_QUERY(LASSO_PROFILE(profile)->request)) { return LASSO_PROFILE_ERROR_MISSING_REQUEST; } query = LASSO_IDWSF2_DSTREF_QUERY(LASSO_PROFILE(profile)->request); item = lasso_idwsf2_dstref_query_item_new_full(item_xpath, item_id); query->QueryItem = g_list_append(query->QueryItem, item); return 0; } /** * lasso_idwsf2_data_service_process_query_msg: * @service: a #LassoIdWsf2DataService * @message: received query soap request * * Process received query request. * Parse query items and put the list of queried XPATH into @service->query_items. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_process_query_msg(LassoIdWsf2DataService *service, const gchar *message) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2DstRefQuery *request = NULL; LassoIdWsf2DstRefResultQuery *item = NULL; GList *i; int res = 0; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); g_return_val_if_fail(message != NULL, LASSO_PARAM_ERROR_INVALID_VALUE); res = lasso_idwsf2_profile_process_soap_request_msg(profile, message); if (! LASSO_IS_IDWSF2_DSTREF_QUERY(LASSO_PROFILE(profile)->request)) { res = LASSO_PROFILE_ERROR_INVALID_SOAP_MSG; } else { request = LASSO_IDWSF2_DSTREF_QUERY(LASSO_PROFILE(profile)->request); lasso_assign_string(service->type, request->hrefServiceType); } if (res == 0) { /* Parse QueryItems to get a list of Xpath strings */ for (i = g_list_first(request->QueryItem); i != NULL; i = g_list_next(i)) { item = LASSO_IDWSF2_DSTREF_RESULT_QUERY(i->data); if (item->Select != NULL) { service->query_items = g_list_append( service->query_items, g_strdup(item->Select)); } } } return res; } /** * lasso_idwsf2_data_service_parse_query_items: * @service: a #LassoIdWsf2DataService * * Parse query items and user data from @service->data and fill response->Data accordingly. * Set response status code to OK is all items were parsed correctly, FAILED if no item was parsed * correctly, or PARTIAL if some items were parsed corretly and some others not. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_parse_query_items(LassoIdWsf2DataService *service) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2DstRefQuery *request; LassoIdWsf2DstRefQueryResponse *response; LassoIdWsf2UtilResponse *response2; LassoSoapEnvelope *envelope; xmlDoc *doc; xmlXPathContext *xpathCtx; xmlXPathObject *xpathObj; LassoIdWsf2DstRefQueryItem *item; LassoIdWsf2DstRefResultQuery *item_result_query; LassoIdWsf2DstResultQueryBase *item_result_query_base; xmlNode *node; GList *iter; int i; /* How much query did we execute */ int executed = 0; /* How much query did succeed */ int successful = 0; int rc = 0; int error_code = 0; lasso_bad_param(IDWSF2_DATA_SERVICE, service); lasso_extract_node_or_fail(request, LASSO_PROFILE(profile)->request, IDWSF2_DSTREF_QUERY, LASSO_PROFILE_ERROR_MISSING_REQUEST); if (service->data == NULL) { return LASSO_DST_ERROR_MISSING_SERVICE_DATA; } /* Response envelope and body */ lasso_extract_node_or_fail(envelope, profile->soap_envelope_response, SOAP_ENVELOPE, LASSO_SOAP_ERROR_MISSING_ENVELOPE); response = lasso_idwsf2_dstref_query_response_new(); lasso_assign_string(response->prefixServiceType, request->prefixServiceType); lasso_assign_string(response->hrefServiceType, request->hrefServiceType); lasso_assign_new_gobject(LASSO_PROFILE(profile)->response, LASSO_NODE(response)); lasso_list_add_gobject(envelope->Body->any, response); /* Initialise XML parsing */ doc = xmlNewDoc((xmlChar*)"1.0"); xmlDocSetRootElement(doc, service->data); xpathCtx = xmlXPathNewContext(doc); lasso_register_idwsf2_xpath_namespaces(xpathCtx); /* Parse request QueryItems and fill response Data accordingly */ /* XXX: needs another level, since there may be more than one */ for (iter = g_list_first(request->QueryItem); iter != NULL; iter = g_list_next(iter)) { LassoIdWsf2DstRefData *data; LassoIdWsf2DstRefItemData *data_item; executed++; item = iter->data; item_result_query = LASSO_IDWSF2_DSTREF_RESULT_QUERY(item); item_result_query_base = LASSO_IDWSF2_DST_RESULT_QUERY_BASE(item); if (lasso_eval_xpath_expression(xpathCtx, item_result_query->Select, &xpathObj, &error_code)) { /* XXX: assuming there is only one matching node */ data = lasso_idwsf2_dstref_data_new(); data_item = LASSO_IDWSF2_DSTREF_ITEM_DATA(data); for (i = 0; i < xpathObj->nodesetval->nodeNr; i++) { node = xpathObj->nodesetval->nodeTab[i]; if (node->type == XML_ATTRIBUTE_NODE) { LASSO_IDWSF2_DSTREF_APP_DATA(data_item)->any = g_list_append( LASSO_IDWSF2_DSTREF_APP_DATA(data_item)->any, xmlNewText(xmlGetProp(node->parent, node->name))); } else { LASSO_IDWSF2_DSTREF_APP_DATA(data_item)->any = g_list_append( LASSO_IDWSF2_DSTREF_APP_DATA(data_item)->any, xmlCopyNode(node, 1)); } } lasso_release_xpath_object(xpathObj); } else if (xpathObj && xpathObj->type == XPATH_STRING) { data = lasso_idwsf2_dstref_data_new(); data_item = LASSO_IDWSF2_DSTREF_ITEM_DATA(data); LASSO_IDWSF2_DSTREF_APP_DATA(data_item)->any = g_list_append( LASSO_IDWSF2_DSTREF_APP_DATA(data_item)->any, xmlNewText(xpathObj->stringval)); lasso_release_xpath_object(xpathObj); } else { lasso_release_xpath_object(xpathObj); /* Stop processing at first error, according to the specifications, or not * ;) */ if (lasso_flag_follow_id_wsf_stupid_semantic) break; else continue; } /* Finish handling of a successful item parsing */ successful++; lasso_assign_string(data_item->itemIDRef, item_result_query_base->itemID); lasso_list_add_new_gobject(response->Data, data); } /* Free XML parsing objects */ xmlUnlinkNode(service->data); lasso_release_xpath_job(xpathObj, xpathCtx, doc); xmlSetTreeDoc(service->data, NULL); response2 = LASSO_IDWSF2_UTIL_RESPONSE(response); lasso_assign_new_gobject(response2->Status, lasso_idwsf2_util_status_new()); if (executed == successful) { lasso_assign_string(response2->Status->code, LASSO_DST_STATUS_CODE_OK); } else if (successful == 0) { lasso_assign_string(response2->Status->code, LASSO_DST_STATUS_CODE_FAILED); rc = LASSO_DST_ERROR_QUERY_FAILED; } else { lasso_assign_string(response2->Status->code, LASSO_DST_STATUS_CODE_PARTIAL); rc = LASSO_DST_ERROR_QUERY_PARTIALLY_FAILED; } if (error_code) { LassoIdWsf2UtilStatus *status; status = lasso_idwsf2_util_status_new(); lasso_list_add_new_gobject(response2->Status->Status, status); status->code = g_strdup_printf("LIBXML_XPATH_ERROR_%d", error_code); } cleanup: return rc; } static gint lasso_idwsf2_data_service_process_query_response_soap_fault_msg(LassoIdWsf2DataService *service) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoSoapFault *fault; LassoIdWsf2Sb2RedirectRequest *redirect_request = NULL; GList *iter; int res; if (! LASSO_IS_SOAP_FAULT(LASSO_PROFILE(profile)->response)) { /* Should not happen as it should be checked in caller */ return 0; } fault = LASSO_SOAP_FAULT(LASSO_PROFILE(profile)->response); if (fault->Detail == NULL || fault->Detail->any == NULL) { return LASSO_SOAP_ERROR_MISSING_SOAP_FAULT_DETAIL; } /* Get RedirectRequest element from soap fault detail */ for (iter = fault->Detail->any; iter != NULL; iter = iter->next) { if (LASSO_IS_IDWSF2_SB2_REDIRECT_REQUEST(iter->data) == TRUE) { redirect_request = LASSO_IDWSF2_SB2_REDIRECT_REQUEST(iter->data); break; } } if (redirect_request != NULL) { /* This is not a failure, this exception code indicates the WSP needs to ask */ /* user consent to get an attribute */ res = LASSO_SOAP_FAULT_REDIRECT_REQUEST; /* Get redirect request url */ service->redirect_url = g_strdup(redirect_request->redirectURL); } return res; } /** * lasso_idwsf2_data_service_process_query_response_msg: * @service: a #LassoIdWsf2DataService * @message: received query soap response * * Process received query response. * Check if the response if a normal response or a soap fault. * Check response status code. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_process_query_response_msg(LassoIdWsf2DataService *service, const gchar *message) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2UtilResponse *response; int res; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); g_return_val_if_fail(message != NULL, LASSO_PARAM_ERROR_INVALID_VALUE); res = lasso_idwsf2_profile_process_soap_response_msg(profile, message); if (res != 0) { return res; } /* Message can be either a SoapFault or a QueryResponse */ if (LASSO_IS_SOAP_FAULT(LASSO_PROFILE(profile)->response)) { return lasso_idwsf2_data_service_process_query_response_soap_fault_msg( service); } if (! LASSO_IS_IDWSF2_DSTREF_QUERY_RESPONSE(LASSO_PROFILE(profile)->response)) { return LASSO_PROFILE_ERROR_INVALID_SOAP_MSG; } /* Check response status code */ response = LASSO_IDWSF2_UTIL_RESPONSE(LASSO_PROFILE(profile)->response); if (response->Status == NULL || response->Status->code == NULL) { return LASSO_PROFILE_ERROR_MISSING_STATUS_CODE; } if (strcmp(response->Status->code, LASSO_DST_STATUS_CODE_PARTIAL) == 0) { return LASSO_DST_ERROR_QUERY_PARTIALLY_FAILED; } else if (strcmp(response->Status->code, LASSO_DST_STATUS_CODE_OK) != 0) { return LASSO_DST_ERROR_QUERY_FAILED; } return 0; } /** * lasso_idwsf2_data_service_get_attribute_nodes: * @service: a #LassoIdWsf2DataService * @item_id: identifier of the item to retrieve * * Get one of several items for specified @item_id and return them raw as a list of xmlnodes. * * Return value: a list of xmlnodes; or NULL if no item with the specified @item_id was found. **/ GList* lasso_idwsf2_data_service_get_attribute_nodes(LassoIdWsf2DataService *service, const gchar *item_id) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2DstRefQueryResponse *response; LassoIdWsf2DstRefAppData *data = NULL; GList *iter; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), NULL); g_return_val_if_fail(LASSO_IS_IDWSF2_DSTREF_QUERY_RESPONSE( LASSO_PROFILE(profile)->response), NULL); response = LASSO_IDWSF2_DSTREF_QUERY_RESPONSE(LASSO_PROFILE(profile)->response); /* If no item_id is given, return the first item */ if (item_id == NULL && response->Data != NULL && response->Data->data != NULL) { data = LASSO_IDWSF2_DSTREF_APP_DATA(response->Data->data); if (data->any != NULL && data->any->data != NULL) { return duplicate_glist_of_xmlnodes(data->any); } } if (item_id == NULL) { return NULL; } /* Find the item which has the given item_id */ for (iter = g_list_first(response->Data); iter != NULL; iter = g_list_next(iter)) { if (! LASSO_IS_IDWSF2_DSTREF_ITEM_DATA(iter->data)) { continue; } if (strcmp(LASSO_IDWSF2_DSTREF_ITEM_DATA(iter->data)->itemIDRef, item_id) == 0) { data = LASSO_IDWSF2_DSTREF_APP_DATA(iter->data); break; } } if (data == NULL || data->any == NULL || data->any->data == NULL) { /* Item not found */ return NULL; } return duplicate_glist_of_xmlnodes(data->any); } /** * lasso_idwsf2_data_service_get_attribute_node: * @service: a #LassoIdWsf2DataService * @item_id: identifier of the item to retrieve * * Get one item for specified @item_id and return it raw as an xmlnode. * * Return value: an xmlnode; or NULL if no item with the specified @item_id was found. **/ xmlNode* lasso_idwsf2_data_service_get_attribute_node(LassoIdWsf2DataService *service, const gchar *item_id) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2DstRefQueryResponse *response; LassoIdWsf2DstRefAppData *data = NULL; GList *iter; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), NULL); g_return_val_if_fail(LASSO_IS_IDWSF2_DSTREF_QUERY_RESPONSE( LASSO_PROFILE(profile)->response), NULL); response = LASSO_IDWSF2_DSTREF_QUERY_RESPONSE(LASSO_PROFILE(profile)->response); /* If no item_id is given, return the first item */ if (item_id == NULL && response->Data != NULL && response->Data->data != NULL) { data = LASSO_IDWSF2_DSTREF_APP_DATA(response->Data->data); if (data->any != NULL && data->any->data != NULL) { return xmlCopyNode(data->any->data, 1); } } if (item_id == NULL) { return NULL; } /* Find the item which has the given item_id */ for (iter = g_list_first(response->Data); iter != NULL; iter = g_list_next(iter)) { if (! LASSO_IS_IDWSF2_DSTREF_ITEM_DATA(iter->data)) { continue; } if (strcmp(LASSO_IDWSF2_DSTREF_ITEM_DATA(iter->data)->itemIDRef, item_id) == 0) { data = LASSO_IDWSF2_DSTREF_APP_DATA(iter->data); break; } } if (data == NULL || data->any == NULL || data->any->data == NULL) { /* Item not found */ return NULL; } /* XXX: there may be more than one xmlnode */ return xmlCopyNode(data->any->data, 1); } /** * lasso_idwsf2_data_service_get_attribute_strings: * @service: a #LassoIdWsf2DataService * @item_id: identifier of the item to retrieve * * Get one of several items for specified @item_id and return their content as a list of strings. * * Return value: a list of strings; or NULL if no item with the specified @item_id was found. **/ GList* lasso_idwsf2_data_service_get_attribute_strings(LassoIdWsf2DataService *service, const gchar *item_id) { GList *nodes = NULL; xmlChar *xml_content = NULL; GList *contents = NULL; GList *iter; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), NULL); nodes = lasso_idwsf2_data_service_get_attribute_nodes(service, item_id); for (iter = nodes; iter; iter = g_list_next(iter)) { xml_content = xmlNodeGetContent(iter->data); contents = g_list_append(contents, g_strdup((gchar*)xml_content)); xmlFree(xml_content); xmlFreeNode(iter->data); } g_list_free(nodes); return contents; } /** * lasso_idwsf2_data_service_get_attribute_string: * @service: a #LassoIdWsf2DataService * @item_id: identifier of the item to retrieve * * Get one item for specified @item_id and return its content as a string. * * Return value: a string; or NULL if no item with the specified @item_id was found. **/ gchar* lasso_idwsf2_data_service_get_attribute_string(LassoIdWsf2DataService *service, const gchar *item_id) { xmlNode *node; xmlChar *xml_content; gchar *content; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), NULL); node = lasso_idwsf2_data_service_get_attribute_node(service, item_id); xml_content = xmlNodeGetContent(node); content = g_strdup((gchar*)xml_content); xmlFree(xml_content); xmlFreeNode(node); return content; } /** * lasso_idwsf2_data_service_init_redirect_user_for_consent: * @service: a #LassoIdWsf2DataService * @redirect_url: URL to ask for redirection * * Initialise a soap fault response to ask the requesting service to redirect to the @redirect_url. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_init_redirect_user_for_consent(LassoIdWsf2DataService *service, const gchar *redirect_url) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoSoapEnvelope *envelope; LassoSoapFault *fault; LassoSoapDetail *detail; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); g_return_val_if_fail(redirect_url != NULL, LASSO_PARAM_ERROR_INVALID_VALUE); /* Response envelope */ envelope = profile->soap_envelope_response; if (envelope == NULL) { return LASSO_SOAP_ERROR_MISSING_ENVELOPE; } /* Build soap fault node */ fault = lasso_soap_fault_new(); fault->faultcode = g_strdup(LASSO_SOAP_FAULT_CODE_SERVER); fault->faultstring = g_strdup(LASSO_SOAP_FAULT_STRING_SERVER); detail = lasso_soap_detail_new(); detail->any = g_list_append( detail->any, lasso_idwsf2_sb2_redirect_request_new_full(redirect_url)); fault->Detail = detail; /* Response envelope body */ envelope->Body->any = g_list_append(envelope->Body->any, fault); return 0; } /** * lasso_idwsf2_data_service_init_modify: * @service: a #LassoIdWsf2DataService * * Initialise an ID-WSF 2.0 DataService modify request. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_init_modify(LassoIdWsf2DataService *service) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2DstRefModify *modify; LassoWsAddrEndpointReference *epr; GList *metadata_item; GList *i; gchar *service_type = NULL; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); modify = lasso_idwsf2_dstref_modify_new(); lasso_assign_new_gobject(LASSO_PROFILE(profile)->request, LASSO_NODE(modify)); if (service == NULL || service->private_data == NULL || service->private_data->epr == NULL || service->private_data->epr->Metadata == NULL) { return LASSO_PROFILE_ERROR_MISSING_ENDPOINT_REFERENCE; } epr = service->private_data->epr; /* Get the service type from the EPR */ metadata_item = epr->Metadata->any; for (i = g_list_first(metadata_item); i != NULL; i = g_list_next(i)) { if (LASSO_IS_IDWSF2_DISCO_SERVICE_TYPE(i->data)) { service_type = LASSO_IDWSF2_DISCO_SERVICE_TYPE(i->data)->content; break; } } /* Set hrefServiceType and prefixServiceType in query in order to set the profile */ /* namespace in the request */ if (service_type != NULL) { modify->hrefServiceType = g_strdup(service_type); modify->prefixServiceType = lasso_get_prefix_for_idwsf2_dst_service_href( modify->hrefServiceType); } if (modify->prefixServiceType == NULL) { return LASSO_DATA_SERVICE_ERROR_UNREGISTERED_DST; } lasso_idwsf2_profile_init_soap_request(profile, LASSO_NODE(modify), service_type); /* Set msg_url as epr address, which is the SoapEndpoint */ if (epr->Address != NULL) { LASSO_PROFILE(profile)->msg_url = g_strdup(epr->Address->content); } else { return LASSO_PROFILE_ERROR_MISSING_ENDPOINT_REFERENCE_ADDRESS; } return 0; } static void set_xml_string(xmlNode **xmlnode, const char* string) { xmlDoc *doc; xmlNode *node; doc = xmlReadDoc((xmlChar*)string, NULL, NULL, XML_PARSE_NONET); node = xmlDocGetRootElement(doc); if (node != NULL) { node = xmlCopyNode(node, 1); } lasso_release_doc(doc); if (*xmlnode) { xmlFreeNode(*xmlnode); } *xmlnode = node; } /** * lasso_idwsf2_data_service_add_modify_item: * @service: a #LassoIdWsf2DataService * @item_xpath: XPATH of the item to modify * @item_id: identifier of the item to modify * @new_data: new value for the selected item * @overrideAllowed: FALSE means only allowing to create a new item, but not modify existing one, * TRUE means allowing to modify existing item * * Add an item in the modification request. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_add_modify_item(LassoIdWsf2DataService *service, const gchar *item_xpath, const gchar *item_id, const gchar *new_data, gboolean overrideAllowed) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2DstRefModify *modify; LassoIdWsf2DstRefModifyItem *item; xmlNode *new_data_node = NULL; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); g_return_val_if_fail(item_xpath != NULL, LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); g_return_val_if_fail(item_id != NULL, LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); if (! LASSO_IS_IDWSF2_DSTREF_MODIFY(LASSO_PROFILE(profile)->request)) { return LASSO_PROFILE_ERROR_MISSING_REQUEST; } modify = LASSO_IDWSF2_DSTREF_MODIFY(LASSO_PROFILE(profile)->request); set_xml_string(&new_data_node, new_data); item = lasso_idwsf2_dstref_modify_item_new_full( item_xpath, item_id, new_data_node, overrideAllowed); modify->ModifyItem = g_list_append(modify->ModifyItem, item); return 0; } /** * lasso_idwsf2_data_service_process_modify_msg: * @service: a #LassoIdWsf2DataService * @message: received modify soap request * * Process received modify request. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_process_modify_msg(LassoIdWsf2DataService *service, const gchar *message) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2DstRefModify *request = NULL; int res = 0; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); g_return_val_if_fail(message != NULL, LASSO_PARAM_ERROR_INVALID_VALUE); res = lasso_idwsf2_profile_process_soap_request_msg(profile, message); if (! LASSO_IS_IDWSF2_DSTREF_MODIFY(LASSO_PROFILE(profile)->request)) { res = LASSO_PROFILE_ERROR_INVALID_SOAP_MSG; } else { request = LASSO_IDWSF2_DSTREF_MODIFY(LASSO_PROFILE(profile)->request); service->type = g_strdup(request->hrefServiceType); } return res; } static gint lasso_idwsf2_data_service_parse_one_modify_item(LassoIdWsf2DstRefModifyItem *item, xmlDoc *cur_doc, xmlXPathContext *cur_xpathCtx, int *error_code_ptr) { xmlXPathObject *cur_xpathObj = NULL; xmlNode *new_node = NULL; xmlNode *cur_node = NULL; gint rc = 0; g_return_val_if_fail(item != NULL && cur_doc != NULL && cur_xpathCtx != NULL, LASSO_DST_ERROR_MODIFY_FAILED); /* Check NewData existence */ if (item->NewData == NULL || item->NewData->any == NULL || item->NewData->any->data == NULL) { if (item->overrideAllowed == TRUE) { new_node = NULL; } else { goto_cleanup_with_rc(LASSO_DST_ERROR_NEW_DATA_MISSING); } } else { new_node = (xmlNode*)item->NewData->any->data; } if (item->overrideAllowed == FALSE) { /* Add the new item and add it to the current data */ /* FIXME : when the ancestor nodes of the new node do not */ /* exist either, they MUST be added */ xmlAddChild(xmlDocGetRootElement(cur_doc), xmlCopyNode(new_node, 1)); } else { /* Modify an existing node */ if (lasso_eval_xpath_expression(cur_xpathCtx, item->Select, &cur_xpathObj, error_code_ptr)) { /* XXX: assuming there is only one matching node */ cur_node = cur_xpathObj->nodesetval->nodeTab[0]; if (new_node != NULL) { /* Replace old node with new data */ cur_node = xmlReplaceNode(cur_node, xmlCopyNode(new_node, 1)); } lasso_release_xml_node(cur_node); } else { rc = LASSO_DST_ERROR_MODIFY_FAILED; } lasso_release_xpath_object(cur_xpathObj); } cleanup: return rc; } /** * lasso_idwsf2_data_service_parse_modify_items: * @service: a #LassoIdWsf2DataService * * Parse modify items and modify @service->data accordingly. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_parse_modify_items(LassoIdWsf2DataService *service) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2DstRefModify *request; LassoIdWsf2DstRefModifyResponse *response; LassoIdWsf2UtilResponse *response2; LassoSoapEnvelope *envelope; xmlDoc *cur_doc; xmlXPathContext *cur_xpathCtx; LassoIdWsf2DstRefModifyItem *item; xmlNode *cur_data; LassoIdWsf2UtilStatus *secondary_status; GList *iter; int res = 0; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); if (! LASSO_IS_IDWSF2_DSTREF_MODIFY(LASSO_PROFILE(profile)->request)) { return LASSO_PROFILE_ERROR_MISSING_REQUEST; } request = LASSO_IDWSF2_DSTREF_MODIFY(LASSO_PROFILE(profile)->request); if (service->data == NULL) { return LASSO_DST_ERROR_MISSING_SERVICE_DATA; } else { cur_data = xmlCopyNode(service->data, 1); } /* Response envelope and body */ envelope = profile->soap_envelope_response; if (envelope == NULL) { return LASSO_SOAP_ERROR_MISSING_ENVELOPE; } response = lasso_idwsf2_dstref_modify_response_new(); response->prefixServiceType = g_strdup(request->prefixServiceType); response->hrefServiceType = g_strdup(request->hrefServiceType); lasso_assign_new_gobject(LASSO_PROFILE(profile)->response, LASSO_NODE(response)); lasso_list_add_gobject(envelope->Body->any, response); response2 = LASSO_IDWSF2_UTIL_RESPONSE(response); response2->Status = lasso_idwsf2_util_status_new(); /* Initialize XML parsing */ cur_doc = xmlNewDoc((xmlChar*)"1.0"); xmlDocSetRootElement(cur_doc, cur_data); cur_xpathCtx = xmlXPathNewContext(cur_doc); lasso_register_idwsf2_xpath_namespaces(cur_xpathCtx); /* Parse request ModifyItems and modify user current data accordingly */ /* XXX: needs another level, since there may be more than one */ for (iter = g_list_first(request->ModifyItem); iter != NULL; iter = g_list_next(iter)) { int errorCode; item = iter->data; res = lasso_idwsf2_data_service_parse_one_modify_item( item, cur_doc, cur_xpathCtx, &errorCode); if (res != 0) { /* If one item fails, stop and roll back */ break; } } if (res == 0) { response2->Status->code = g_strdup(LASSO_DST_STATUS_CODE_OK); /* Save new service data */ xmlFreeNode(service->data); service->data = xmlCopyNode(cur_data, 1); } else { response2->Status->code = g_strdup(LASSO_DST_STATUS_CODE_FAILED); if (res == LASSO_DST_ERROR_NEW_DATA_MISSING) { secondary_status = lasso_idwsf2_util_status_new(); secondary_status->code = g_strdup( LASSO_DST_STATUS_CODE_MISSING_NEW_DATA_ELEMENT); response2->Status->Status = g_list_append( response2->Status->Status, secondary_status); } } /* Free XML parsing objects */ xmlXPathFreeContext(cur_xpathCtx); lasso_release_doc(cur_doc); return res; } /** * lasso_idwsf2_data_service_process_modify_response_msg: * @service: a #LassoIdWsf2DataService * @message: received modify soap response * * Process received modify response. * Check response status code. * * Return value: 0 on success; or a negative value otherwise. **/ gint lasso_idwsf2_data_service_process_modify_response_msg(LassoIdWsf2DataService *service, const gchar *message) { LassoIdWsf2Profile *profile = LASSO_IDWSF2_PROFILE(service); LassoIdWsf2UtilResponse *response; int res; g_return_val_if_fail(LASSO_IS_IDWSF2_DATA_SERVICE(service), LASSO_PARAM_ERROR_BAD_TYPE_OR_NULL_OBJ); g_return_val_if_fail(message != NULL, LASSO_PARAM_ERROR_INVALID_VALUE); res = lasso_idwsf2_profile_process_soap_response_msg(profile, message); if (res != 0) { return res; } if (! LASSO_IS_IDWSF2_DSTREF_MODIFY_RESPONSE(LASSO_PROFILE(profile)->response)) { return LASSO_PROFILE_ERROR_INVALID_SOAP_MSG; } /* Check response status code */ response = LASSO_IDWSF2_UTIL_RESPONSE(LASSO_PROFILE(profile)->response); if (response->Status == NULL || response->Status->code == NULL) { return LASSO_PROFILE_ERROR_MISSING_STATUS_CODE; } if (strcmp(response->Status->code, LASSO_DST_STATUS_CODE_PARTIAL) == 0) { return LASSO_DST_ERROR_MODIFY_PARTIALLY_FAILED; } else if (strcmp(response->Status->code, LASSO_DST_STATUS_CODE_OK) != 0) { return LASSO_DST_ERROR_MODIFY_FAILED; } return 0; } /*****************************************************************************/ /* 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_idwsf2_xpath_namespaces(xmlXPathContext *xpathCtx) { if (idwsf2_dst_services_by_prefix == NULL) return; g_hash_table_foreach(idwsf2_dst_services_by_prefix, (GHFunc)register_xpath_namespace, xpathCtx); } static GList* duplicate_glist_of_xmlnodes(GList *list) { GList *r = NULL; GList *t; for (t = list; t; t = g_list_next(t)) { r = g_list_append(r, xmlCopyNode(t->data, 1)); } return r; } /*****************************************************************************/ /* overrided parent class methods */ /*****************************************************************************/ static void dispose(GObject *object) { LassoIdWsf2DataService *service = LASSO_IDWSF2_DATA_SERVICE(object); if (service->private_data->dispose_has_run == TRUE) return; service->private_data->dispose_has_run = TRUE; g_free(service->type); service->type = NULL; g_free(service->redirect_url); service->redirect_url = NULL; if (service->query_items != NULL) { g_list_foreach(service->query_items, (GFunc)g_free, NULL); g_list_free(service->query_items); service->query_items = NULL; } lasso_release_gobject(service->private_data->epr); G_OBJECT_CLASS(parent_class)->dispose(object); } static void finalize(GObject *object) { LassoIdWsf2DataService *service = LASSO_IDWSF2_DATA_SERVICE(object); g_free(service->private_data); service->private_data = NULL; G_OBJECT_CLASS(parent_class)->finalize(object); } /*****************************************************************************/ /* instance and class init functions */ /*****************************************************************************/ static void instance_init(LassoIdWsf2DataService *service) { service->data = NULL; service->type = NULL; service->redirect_url = NULL; service->query_items = NULL; service->private_data = g_new0(LassoIdWsf2DataServicePrivate, 1); service->private_data->epr = NULL; service->private_data->credentials = NULL; } static void class_init(LassoIdWsf2DataServiceClass *klass) { parent_class = g_type_class_peek_parent(klass); G_OBJECT_CLASS(klass)->dispose = dispose; G_OBJECT_CLASS(klass)->finalize = finalize; } GType lasso_idwsf2_data_service_get_type() { static GType this_type = 0; if (!this_type) { static const GTypeInfo this_info = { sizeof(LassoIdWsf2DataServiceClass), NULL, NULL, (GClassInitFunc) class_init, NULL, NULL, sizeof(LassoIdWsf2DataService), 0, (GInstanceInitFunc) instance_init, NULL }; this_type = g_type_register_static(LASSO_TYPE_IDWSF2_PROFILE, "LassoIdWsf2DataService", &this_info, 0); } return this_type; } /** * lasso_idwsf2_data_service_new: * @server: the #LassoServer * * Create a new #LassoIdWsf2DataService. * * Return value: a newly created #LassoIdWsf2DataService object **/ LassoIdWsf2DataService* lasso_idwsf2_data_service_new(LassoServer *server) { LassoIdWsf2DataService *service; g_return_val_if_fail(LASSO_IS_SERVER(server), NULL); service = g_object_new(LASSO_TYPE_IDWSF2_DATA_SERVICE, NULL); LASSO_PROFILE(service)->server = g_object_ref(server); return service; } /** * lasso_idwsf2_data_service_new_full: * @server: the #LassoServer * @epr: the #LassoWsAddrEndpointReference * * Create a new #LassoIdWsf2DataService. * * Return value: a newly created #LassoIdWsf2DataService object; or NULL if an error occured. **/ LassoIdWsf2DataService* lasso_idwsf2_data_service_new_full(LassoServer *server, LassoWsAddrEndpointReference *epr) { LassoIdWsf2DataService *service; g_return_val_if_fail(LASSO_IS_SERVER(server), NULL); g_return_val_if_fail(LASSO_IS_WSA_ENDPOINT_REFERENCE(epr), NULL); service = lasso_idwsf2_data_service_new(server); service->private_data->epr = g_object_ref(epr); return service; }