/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*/
#include "evolution-ews-config.h"
#include <libedataserver/eds-version.h>
#include <stdlib.h>
#include <string.h>
#include <libxml/tree.h>
#include <libsoup/soup.h>
#include "e-soap-response.h"
#define E_SOAP_RESPONSE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_SOAP_RESPONSE, ESoapResponsePrivate))
struct _ESoapResponsePrivate {
/* the XML document */
xmlDocPtr xmldoc;
xmlNodePtr xml_root;
xmlNodePtr xml_body;
xmlNodePtr xml_method;
xmlNodePtr soap_fault;
GList *parameters;
};
G_DEFINE_TYPE (ESoapResponse, e_soap_response, G_TYPE_OBJECT)
static xmlNode *
soup_xml_real_node (xmlNode *node)
{
while (node && (node->type == XML_COMMENT_NODE ||
xmlIsBlankNode (node)))
node = node->next;
return node;
}
static void
soap_response_finalize (GObject *object)
{
ESoapResponsePrivate *priv;
priv = E_SOAP_RESPONSE_GET_PRIVATE (object);
if (priv->xmldoc != NULL)
xmlFreeDoc (priv->xmldoc);
g_list_free (priv->parameters);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_soap_response_parent_class)->finalize (object);
}
static void
e_soap_response_class_init (ESoapResponseClass *class)
{
GObjectClass *object_class;
g_type_class_add_private (class, sizeof (ESoapResponsePrivate));
object_class = G_OBJECT_CLASS (class);
object_class->finalize = soap_response_finalize;
}
static void
e_soap_response_init (ESoapResponse *response)
{
response->priv = E_SOAP_RESPONSE_GET_PRIVATE (response);
response->priv->xmldoc = xmlNewDoc ((const xmlChar *)"1.0");
}
/**
* e_soap_response_new:
*
* Create a new empty #ESoapResponse object, which can be modified
* with the accessor functions provided with this class.
*
* Returns: the new #ESoapResponse (or %NULL if there was an
* error).
*/
ESoapResponse *
e_soap_response_new (void)
{
ESoapResponse *response;
response = g_object_new (E_TYPE_SOAP_RESPONSE, NULL);
return response;
}
/**
* e_soap_response_new_from_string:
* @xmlstr: the XML string to parse.
* @xmlstr_len: XML string's length or -1 for a null-terminated string
*
* Create a new #ESoapResponse object from the XML string contained
* in @xmlstr.
*
* Returns: the new #ESoapResponse (or %NULL if there was an
* error).
*/
ESoapResponse *
e_soap_response_new_from_string (const gchar *xmlstr,
gint xmlstr_length)
{
ESoapResponse *response;
g_return_val_if_fail (xmlstr != NULL, NULL);
response = g_object_new (E_TYPE_SOAP_RESPONSE, NULL);
if (!e_soap_response_from_string (response, xmlstr, xmlstr_length)) {
g_object_unref (response);
return NULL;
}
return response;
}
/**
* e_soap_response_new_from_xmldoc:
* @xmldoc: the XML document to parse.
*
* Create a new #ESoapResponse object from the XML document contained
* in @xmldoc.
*
* Returns: the new #ESoapResponse (or %NULL if there was an
* error).
*/
ESoapResponse *
e_soap_response_new_from_xmldoc (xmlDoc *xmldoc)
{
ESoapResponse *response;
g_return_val_if_fail (xmldoc != NULL, NULL);
response = g_object_new (E_TYPE_SOAP_RESPONSE, NULL);
if (!e_soap_response_from_xmldoc (response, xmldoc)) {
g_object_unref (response);
return NULL;
}
return response;
}
static void
parse_parameters (ESoapResponse *response,
xmlNodePtr xml_method)
{
xmlNodePtr tmp;
for (tmp = soup_xml_real_node (xml_method->children);
tmp != NULL;
tmp = soup_xml_real_node (tmp->next)) {
if (!strcmp ((const gchar *) tmp->name, "Fault")) {
response->priv->soap_fault = tmp;
continue;
} else {
/* regular parameters */
response->priv->parameters = g_list_append (
response->priv->parameters, tmp);
}
}
}
/**
* e_soap_response_from_string:
* @response: the #ESoapResponse object.
* @xmlstr: XML string to parse.
* @xmlstr_len: XML string's length or -1 for a null-terminated string
*
* Parses the string contained in @xmlstr and sets all properties from
* it in the @response object.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
gboolean
e_soap_response_from_string (ESoapResponse *response,
const gchar *xmlstr,
gint xmlstr_length)
{
xmlDocPtr xmldoc;
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), FALSE);
g_return_val_if_fail (xmlstr != NULL, FALSE);
/* parse the string */
xmldoc = xmlParseMemory (xmlstr, xmlstr_length == -1 ? strlen (xmlstr) : xmlstr_length);
if (!xmldoc)
return FALSE;
return e_soap_response_from_xmldoc (response, xmldoc);
}
/**
* e_soap_response_from_xmldoc:
* @response: the #ESoapResponse object.
* @xmldoc: XML document.
*
* Sets all properties from the @xmldoc in the @response object.
*
* Returns: %TRUE if successful, %FALSE otherwise.
*/
gboolean
e_soap_response_from_xmldoc (ESoapResponse *response,
xmlDoc *xmldoc)
{
xmlNodePtr xml_root, xml_body, xml_method = NULL;
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), FALSE);
g_return_val_if_fail (xmldoc != NULL, FALSE);
xml_root = xmlDocGetRootElement (xmldoc);
if (!xml_root) {
xmlFreeDoc (xmldoc);
return FALSE;
}
if (strcmp ((const gchar *) xml_root->name, "Envelope") != 0) {
xmlFreeDoc (xmldoc);
return FALSE;
}
xml_body = soup_xml_real_node (xml_root->children);
if (xml_body != NULL) {
if (strcmp ((const gchar *) xml_body->name, "Header") == 0) {
/* read header parameters */
parse_parameters (response, xml_body);
xml_body = soup_xml_real_node (xml_body->next);
}
if (strcmp ((const gchar *) xml_body->name, "Body") != 0) {
xmlFreeDoc (xmldoc);
return FALSE;
}
xml_method = soup_xml_real_node (xml_body->children);
/* read all parameters */
if (xml_method)
parse_parameters (response, xml_method);
}
xmlFreeDoc (response->priv->xmldoc);
response->priv->xmldoc = xmldoc;
response->priv->xml_root = xml_root;
response->priv->xml_body = xml_body;
response->priv->xml_method = xml_method;
return TRUE;
}
/**
* e_soap_response_get_method_name:
* @response: the #ESoapResponse object.
*
* Gets the method name from the SOAP response.
*
* Returns: the method name.
*/
const gchar *
e_soap_response_get_method_name (ESoapResponse *response)
{
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL);
g_return_val_if_fail (response->priv->xml_method != NULL, NULL);
return (const gchar *) response->priv->xml_method->name;
}
/**
* e_soap_response_set_method_name:
* @response: the #ESoapResponse object.
* @method_name: the method name to set.
*
* Sets the method name on the given #ESoapResponse.
*/
void
e_soap_response_set_method_name (ESoapResponse *response,
const gchar *method_name)
{
g_return_if_fail (E_IS_SOAP_RESPONSE (response));
g_return_if_fail (response->priv->xml_method != NULL);
g_return_if_fail (method_name != NULL);
xmlNodeSetName (
response->priv->xml_method,
(const xmlChar *) method_name);
}
/**
* e_soap_parameter_get_name:
* @param: the parameter
*
* Returns the parameter name.
*
* Returns: the parameter name.
*/
const gchar *
e_soap_parameter_get_name (ESoapParameter *param)
{
g_return_val_if_fail (param != NULL, NULL);
return (const gchar *) param->name;
}
/**
* e_soap_parameter_get_int_value:
* @param: the parameter
*
* Returns the parameter's (integer) value.
*
* Returns: the parameter value as an integer
*/
gint
e_soap_parameter_get_int_value (ESoapParameter *param)
{
gint i;
xmlChar *s;
g_return_val_if_fail (param != NULL, -1);
s = xmlNodeGetContent (param);
if (s) {
i = atoi ((gchar *) s);
xmlFree (s);
return i;
}
return -1;
}
/**
* e_soap_parameter_get_string_value:
* @param: the parameter
*
* Returns the parameter's value.
*
* Returns: the parameter value as a string, which must be freed
* by the caller.
*/
gchar *
e_soap_parameter_get_string_value (ESoapParameter *param)
{
xmlChar *xml_s;
gchar *s;
g_return_val_if_fail (param != NULL, NULL);
xml_s = xmlNodeGetContent (param);
s = g_strdup ((gchar *) xml_s);
xmlFree (xml_s);
return s;
}
/**
* e_soap_parameter_get_first_child:
* @param: A #ESoapParameter.
*
* Gets the first child of the given #ESoapParameter. This is used
* for compound data types, which can contain several parameters
* themselves.
*
* Returns: the first child or %NULL if there are no children.
*/
ESoapParameter *
e_soap_parameter_get_first_child (ESoapParameter *param)
{
g_return_val_if_fail (param != NULL, NULL);
return soup_xml_real_node (param->children);
}
/**
* e_soap_parameter_get_first_child_by_name:
* @param: A #ESoapParameter.
* @name: The name of the child parameter to look for.
*
* Gets the first child of the given #ESoapParameter whose name is
* @name.
*
* Returns: the first child with the given name or %NULL if there
* are no children.
*/
ESoapParameter *
e_soap_parameter_get_first_child_by_name (ESoapParameter *param,
const gchar *name)
{
ESoapParameter *tmp;
g_return_val_if_fail (param != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
for (tmp = e_soap_parameter_get_first_child (param);
tmp != NULL;
tmp = e_soap_parameter_get_next_child (tmp)) {
if (!strcmp (name, (const gchar *) tmp->name))
return tmp;
}
return NULL;
}
/**
* e_soap_parameter_get_next_child:
* @param: A #ESoapParameter.
*
* Gets the next sibling of the given #ESoapParameter. This is used
* for compound data types, which can contain several parameters
* themselves.
*
* FIXME: the name of this method is wrong
*
* Returns: the next sibling, or %NULL if there are no more
* siblings.
*/
ESoapParameter *
e_soap_parameter_get_next_child (ESoapParameter *param)
{
g_return_val_if_fail (param != NULL, NULL);
return soup_xml_real_node (param->next);
}
/**
* e_soap_parameter_get_next_child_by_name:
* @param: A #ESoapParameter.
* @name: The name of the sibling parameter to look for.
*
* Gets the next sibling of the given #ESoapParameter whose name is
* @name.
*
* FIXME: the name of this method is wrong
*
* Returns: the next sibling with the given name, or %NULL
*/
ESoapParameter *
e_soap_parameter_get_next_child_by_name (ESoapParameter *param,
const gchar *name)
{
ESoapParameter *tmp;
g_return_val_if_fail (param != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
for (tmp = e_soap_parameter_get_next_child (param);
tmp != NULL;
tmp = e_soap_parameter_get_next_child (tmp)) {
if (!strcmp (name, (const gchar *) tmp->name))
return tmp;
}
return NULL;
}
/**
* e_soap_parameter_get_property:
* @param: the parameter
* @prop_name: Name of the property to retrieve.
*
* Returns the named property of @param.
*
* Returns: the property, which must be freed by the caller.
*/
gchar *
e_soap_parameter_get_property (ESoapParameter *param,
const gchar *prop_name)
{
xmlChar *xml_s;
gchar *s;
g_return_val_if_fail (param != NULL, NULL);
g_return_val_if_fail (prop_name != NULL, NULL);
xml_s = xmlGetProp (param, (const xmlChar *) prop_name);
s = g_strdup ((gchar *) xml_s);
xmlFree (xml_s);
return s;
}
/**
* e_soap_response_get_parameters:
* @response: the #ESoapResponse object.
*
* Returns the list of parameters received in the SOAP response.
*
* Returns: a list of #ESoapParameter
*/
const GList *
e_soap_response_get_parameters (ESoapResponse *response)
{
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL);
return (const GList *) response->priv->parameters;
}
ESoapParameter *
e_soap_response_get_parameter (ESoapResponse *response)
{
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL);
return response->priv->xml_method;
}
/**
* e_soap_response_get_first_parameter:
* @response: the #ESoapResponse object.
*
* Retrieves the first parameter contained in the SOAP response.
*
* Returns: a #ESoapParameter representing the first
* parameter, or %NULL if there are no parameters.
*/
ESoapParameter *
e_soap_response_get_first_parameter (ESoapResponse *response)
{
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL);
if (response->priv->parameters == NULL)
return NULL;
return response->priv->parameters->data;
}
/**
* e_soap_response_get_first_parameter_by_name:
* @response: the #ESoapResponse object.
* @name: the name of the parameter to look for.
* @error: return location for a #GError, or %NULL
*
* Retrieves the first parameter contained in the SOAP response whose
* name is @name. If no parameter is found, the function sets @error
* and returns %NULL.
*
* The function also checks for a SOAP "faultstring" parameter and,
* if found, uses it to set the #GError message.
*
* Returns: a #ESoapParameter representing the first parameter
* with the given name, or %NULL.
*/
ESoapParameter *
e_soap_response_get_first_parameter_by_name (ESoapResponse *response,
const gchar *name,
GError **error)
{
GList *l;
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL);
g_return_val_if_fail (name != NULL, NULL);
for (l = response->priv->parameters; l != NULL; l = l->next) {
ESoapParameter *param = (ESoapParameter *) l->data;
if (strcmp (name, (const gchar *) param->name) == 0)
return param;
}
/* XXX These are probably not the best error codes, but
* wanted to avoid EWS_CONNECTION_ERROR codes since
* this class is potentially reusable. */
for (l = response->priv->parameters; l != NULL; l = l->next) {
ESoapParameter *param = (ESoapParameter *) l->data;
if (strcmp ("faultstring", (const gchar *) param->name) == 0) {
gchar *string;
string = e_soap_parameter_get_string_value (param);
g_set_error (
error,
SOUP_HTTP_ERROR, SOUP_STATUS_IO_ERROR,
"%s", (string != NULL) ? string :
"<faultstring> in SOAP response");
g_free (string);
return NULL;
}
}
g_set_error (
error,
SOUP_HTTP_ERROR, SOUP_STATUS_MALFORMED,
"Missing <%s> in SOAP response", name);
return NULL;
}
/**
* e_soap_response_get_next_parameter:
* @response: the #ESoapResponse object.
* @from: the parameter to start from.
*
* Retrieves the parameter following @from in the #ESoapResponse
* object.
*
* Returns: a #ESoapParameter representing the parameter.
*/
ESoapParameter *
e_soap_response_get_next_parameter (ESoapResponse *response,
ESoapParameter *from)
{
GList *l;
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL);
g_return_val_if_fail (from != NULL, NULL);
l = g_list_find (response->priv->parameters, (gconstpointer) from);
if (!l)
return NULL;
return l->next ? (ESoapParameter *) l->next->data : NULL;
}
/**
* e_soap_response_get_next_parameter_by_name:
* @response: the #ESoapResponse object.
* @from: the parameter to start from.
* @name: the name of the parameter to look for.
*
* Retrieves the first parameter following @from in the
* #ESoapResponse object whose name matches @name.
*
* Returns: a #ESoapParameter representing the parameter.
*/
ESoapParameter *
e_soap_response_get_next_parameter_by_name (ESoapResponse *response,
ESoapParameter *from,
const gchar *name)
{
ESoapParameter *param;
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL);
g_return_val_if_fail (from != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
param = e_soap_response_get_next_parameter (response, from);
while (param) {
const gchar *param_name = e_soap_parameter_get_name (param);
if (param_name) {
if (!strcmp (name, param_name))
return param;
}
param = e_soap_response_get_next_parameter (response, param);
}
return NULL;
}
/**
* e_soap_response_dump_response:
**/
gint
e_soap_response_dump_response (ESoapResponse *response,
FILE *buffer)
{
xmlChar *xmlbuff;
gint buffersize, ret;
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), -1);
xmlDocDumpFormatMemory (
response->priv->xmldoc,
&xmlbuff, &buffersize, 1);
ret = fputs ((gchar *) xmlbuff, buffer);
xmlFree (xmlbuff);
return ret;
}
gchar *
e_soap_response_dump_parameter (ESoapResponse *response,
ESoapParameter *param)
{
xmlBuffer *buffer;
gint len;
gchar *data;
g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL);
g_return_val_if_fail (param != NULL, NULL);
buffer = xmlBufferCreate ();
len = xmlNodeDump (buffer, response->priv->xmldoc, param, 0, 0);
if (len <= 0) {
xmlBufferFree (buffer);
return NULL;
}
data = g_strndup ((const gchar *) buffer->content, len);
xmlBufferFree (buffer);
return data;
}