File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / structs / structs_xmlrpc.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:25:53 2012 UTC (12 years, 8 months ago) by misho
Branches: libpdel, MAIN
CVS tags: v0_5_3, HEAD
libpdel


/*
 * Copyright (c) 2001-2002 Packet Design, LLC.
 * All rights reserved.
 * 
 * Subject to the following obligations and disclaimer of warranty,
 * use and redistribution of this software, in source or object code
 * forms, with or without modifications are expressly permitted by
 * Packet Design; provided, however, that:
 * 
 *    (i)  Any and all reproductions of the source or object code
 *         must include the copyright notice above and the following
 *         disclaimer of warranties; and
 *    (ii) No rights are granted, in any manner or form, to use
 *         Packet Design trademarks, including the mark "PACKET DESIGN"
 *         on advertising, endorsements, or otherwise except as such
 *         appears in the above copyright notice or in the software.
 * 
 * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
 * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * OR NON-INFRINGEMENT.  PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
 * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
 * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
 * RELIABILITY OR OTHERWISE.  IN NO EVENT SHALL PACKET DESIGN BE
 * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
 * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
 * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
 * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Author: Archie Cobbs <archie@freebsd.org>
 */

#include <sys/types.h>
#include <net/ethernet.h>

#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>
#include <syslog.h>
#include <string.h>
#include <errno.h>

#include "structs/structs.h"
#include "structs/type/array.h"
#include "structs/type/struct.h"
#include "structs/type/union.h"
#include "structs/type/string.h"
#include "structs/type/float.h"
#include "structs/type/boolean.h"
#include "structs/type/data.h"
#include "structs/type/time.h"
#include "structs/type/int.h"
#include "structs/xml.h"
#include "structs/xmlrpc.h"
#include "util/typed_mem.h"

/************************************************************************
				string
************************************************************************/

static const struct structs_type string_type
	= STRUCTS_STRING_TYPE("xmlrpc_string", 0);

/************************************************************************
				base64
************************************************************************/

static const struct structs_type base64_type
	= STRUCTS_DATA_TYPE(NULL, "xmlrpc_base64");

/************************************************************************
				array data
************************************************************************/

static const struct structs_type array_data_type
	= STRUCTS_ARRAY_TYPE(&structs_type_xmlrpc_value,
		"xmlrpc_array", "value");

/************************************************************************
				array
************************************************************************/

static const struct structs_field array_fields[] = {
	STRUCTS_STRUCT_FIELD(xmlrpc_array, data, &array_data_type),
	STRUCTS_STRUCT_FIELD_END
};
const struct structs_type structs_type_xmlrpc_array
	= STRUCTS_STRUCT_TYPE(xmlrpc_array, &array_fields);

/************************************************************************
				member
************************************************************************/

static const struct structs_field member_fields[] = {
	STRUCTS_STRUCT_FIELD(xmlrpc_member, name, &string_type),
	STRUCTS_STRUCT_FIELD(xmlrpc_member, value, &structs_type_xmlrpc_value),
	STRUCTS_STRUCT_FIELD_END
};
const struct structs_type structs_type_xmlrpc_member
	= STRUCTS_STRUCT_TYPE(xmlrpc_member, &member_fields);

/************************************************************************
				struct
************************************************************************/

const struct structs_type structs_type_xmlrpc_struct
	= STRUCTS_ARRAY_TYPE(&structs_type_xmlrpc_member,
		"xmlrpc_struct", "member");

/************************************************************************
				value
************************************************************************/

static const struct structs_ufield value_fields[] = {
	STRUCTS_UNION_FIELD(string, &string_type),
	STRUCTS_UNION_FIELD(i4, &structs_type_int32),
	STRUCTS_UNION_FIELD(int, &structs_type_int32),
	STRUCTS_UNION_FIELD(boolean, &structs_type_boolean_char_01),
	STRUCTS_UNION_FIELD(double, &structs_type_double),
	STRUCTS_UNION_FIELD(dateTime.iso8601, &structs_type_time_iso8601),
	STRUCTS_UNION_FIELD(base64, &base64_type),
	STRUCTS_UNION_FIELD(struct, &structs_type_xmlrpc_struct),
	STRUCTS_UNION_FIELD(array, &structs_type_xmlrpc_array),
	STRUCTS_UNION_FIELD_END
};
const struct structs_type structs_type_xmlrpc_value
	= STRUCTS_UNION_TYPE(xmlrpc_value, &value_fields);

/************************************************************************
				param
************************************************************************/

static const struct structs_field param_fields[] = {
	STRUCTS_STRUCT_FIELD(xmlrpc_param, value, &structs_type_xmlrpc_value),
	STRUCTS_STRUCT_FIELD_END
};
static const struct structs_type param_type
	= STRUCTS_STRUCT_TYPE(xmlrpc_param, &param_fields);

/************************************************************************
				params
************************************************************************/

static const struct structs_type params_type
	= STRUCTS_ARRAY_TYPE(&param_type, "xmlrpc_params", "param");

/************************************************************************
				fault
************************************************************************/

static const struct structs_field fault_fields[] = {
	STRUCTS_STRUCT_FIELD(xmlrpc_fault, value, &structs_type_xmlrpc_value),
	STRUCTS_STRUCT_FIELD_END
};
const struct structs_type structs_type_xmlrpc_fault
	= STRUCTS_STRUCT_TYPE(xmlrpc_fault, &fault_fields);

/************************************************************************
				request
************************************************************************/

static const struct structs_field request_fields[] = {
	STRUCTS_STRUCT_FIELD(xmlrpc_request, methodName, &string_type),
	STRUCTS_STRUCT_FIELD(xmlrpc_request, params, &params_type),
	STRUCTS_STRUCT_FIELD_END
};
const struct structs_type structs_type_xmlrpc_request
	= STRUCTS_STRUCT_TYPE(xmlrpc_request, &request_fields);

/************************************************************************
				response
************************************************************************/

static const struct structs_ufield response_fields[] = {
	STRUCTS_UNION_FIELD(params, &params_type),
	STRUCTS_UNION_FIELD(fault, &structs_type_xmlrpc_fault),
	STRUCTS_UNION_FIELD_END
};
const struct structs_type structs_type_xmlrpc_response
	= STRUCTS_UNION_TYPE(xmlrpc_response, &response_fields);

/************************************************************************
			fault (compact form)
************************************************************************/

static const struct structs_type fault_string_type
	= STRUCTS_STRING_TYPE("xmlrpc_fault_string", 0);

static const struct structs_field compact_fault_fields[] = {
	STRUCTS_STRUCT_FIELD(xmlrpc_compact_fault,
	    faultCode, &structs_type_int32),
	STRUCTS_STRUCT_FIELD(xmlrpc_compact_fault,
	    faultString, &fault_string_type),
	STRUCTS_STRUCT_FIELD_END
};
const struct structs_type structs_type_xmlrpc_compact_fault
	= STRUCTS_STRUCT_TYPE(xmlrpc_compact_fault, &compact_fault_fields);

/************************************************************************
				FUNCTIONS
************************************************************************/

/*
 * Create an XML-RPC request structure with the given parameters.
 */
struct xmlrpc_request *
structs_xmlrpc_build_request(const char *mtype, const char *methodName,
	u_int nparams, const struct structs_type **types, const void **datas)
{
	const struct structs_type *const xtype = &structs_type_xmlrpc_request;
	struct xmlrpc_request *xreq;
	u_int i;

	/* Create new XML-RPC request structure */
	if ((xreq = MALLOC(mtype, sizeof(*xreq))) == NULL)
		return (NULL);
	if (structs_init(xtype, NULL, xreq) == -1) {
		FREE(mtype, xreq);
		return (NULL);
	}

	/* Set method name */
	if (structs_set_string(xtype,
	    "methodName", methodName, xreq, NULL, 0) == -1)
		goto fail;

	/* Copy parameters into request */
	for (i = 0; i < nparams; i++) {
		const void *const data = datas[i];
		char xname[32];

		/* Add another element to params array */
		if (structs_array_insert(xtype, "params", i, xreq) == -1)
			goto fail;
		snprintf(xname, sizeof(xname), "params.%u.value", i);

		/* Explode parameter unless already exploded */
		if (types != NULL) {
			if (structs_struct2xmlrpc(types[i], data, NULL,
			    xtype, xreq, xname) == -1)
				goto fail;
		} else {
			if (structs_set(&structs_type_xmlrpc_value,
			    data, xname, xreq) == -1)
				goto fail;
		}
	}

	/* Done */
	return (xreq);

fail:
	structs_free(xtype, NULL, xreq);
	FREE(mtype, xreq);
	return (NULL);
}

/*
 * Create an XML-RPC response structure with the given return value.
 */
struct xmlrpc_response_union *
structs_xmlrpc_build_response(const char *mtype,
	const struct structs_type *type, const void *data)
{
	const struct structs_type *const xtype = &structs_type_xmlrpc_response;
	struct xmlrpc_response_union *xrep;

	/* Create new XML-RPC response structure */
	if ((xrep = MALLOC(mtype, sizeof(*xrep))) == NULL)
		return (NULL);
	if (structs_init(xtype, NULL, xrep) == -1) {
		FREE(mtype, xrep);
		return (NULL);
	}

	/* Add a single parameter */
	if (structs_array_insert(xtype, "params", 0, xrep) == -1)
		goto fail;

	/* Copy parameter, optionally compacting it */
	if (type != NULL) {
		if (structs_struct2xmlrpc(type, data,
		    NULL, xtype, xrep, "params.0.value") == -1)
			goto fail;
	} else {
		if (structs_set(&structs_type_xmlrpc_value,
		    data, "params.0.value", xrep) == -1)
			goto fail;
	}

	/* Done */
	return (xrep);

fail:
	structs_free(xtype, NULL, xrep);
	FREE(mtype, xrep);
	return (NULL);
}

/*
 * Create an XML-RPC fault response structure with the given fault.
 */
struct xmlrpc_response_union *
structs_xmlrpc_build_fault_response(const char *mtype,
	const struct xmlrpc_compact_fault *fault)
{
	const struct structs_type *const xtype = &structs_type_xmlrpc_response;
	struct xmlrpc_response_union *xrep;

	/* Create new XML-RPC response structure */
	if ((xrep = MALLOC(mtype, sizeof(*xrep))) == NULL)
		return (NULL);
	if (structs_init(xtype, NULL, xrep) == -1) {
		FREE(mtype, xrep);
		return (NULL);
	}

	/* Set fault */
	if (structs_struct2xmlrpc(&structs_type_xmlrpc_compact_fault,
	    fault, NULL, xtype, xrep, "fault.value") == -1)
		goto fail;

	/* Done */
	return (xrep);

fail:
	structs_free(xtype, NULL, xrep);
	FREE(mtype, xrep);
	return (NULL);
}

/*
 * Copy the contents of a normal structure into an XML-RPC "value" structure.
 */
int
structs_struct2xmlrpc(const struct structs_type *type,
	const void *data, const char *sname,
	const struct structs_type *xtype, void *xdata, const char *xname)
{

	/* Find source and dest data */
	if ((type = structs_find(type, sname, (void **)&data, 0)) == NULL)
		return (-1);
	if ((xtype = structs_find(xtype, xname, &xdata, 1)) == NULL)
		return (-1);

	/* Destination type must always be an XML-RPC value */
	if (xtype != &structs_type_xmlrpc_value) {
		errno = EINVAL;
		return (-1);
	}

	/* Dereference through pointer(s) */
	while (type->tclass == STRUCTS_TYPE_POINTER) {
		type = type->args[0].v;
		data = *((void **)data);
	}

	switch (type->tclass) {
	case STRUCTS_TYPE_PRIMITIVE:
	    {
		const char *xprim;
		char *s;

		/* Get corresponding XML-RPC primitive type */
		if (type == &structs_type_int32
		    || (sizeof(int) == 4 && type == &structs_type_int))
			xprim = "i4";
		else if (strcmp(type->name, "float") == 0
		    || strcmp(type->name, "double") == 0)
			xprim = "double";
		else if (strcmp(type->name, "boolean") == 0)
			xprim = "boolean";
		else if (type == &structs_type_time_iso8601)
			xprim = "dateTime.iso8601";
		else if (strcmp(type->name, "data") == 0
		    && type->args[0].v == NULL)		/* XXX same charmap */
			xprim = "base64";
		else
			xprim = "string";

		/* Get primitive value as a string */
		if ((s = structs_get_string(type,
		    NULL, data, TYPED_MEM_TEMP)) == NULL)
			return (-1);

		/* Set primitive value as a string */
		if (structs_set_string(xtype,
		    xprim, s, xdata, NULL, 0) == -1) {
			FREE(TYPED_MEM_TEMP, s);
			return (-1);
		}
		FREE(TYPED_MEM_TEMP, s);
		break;
	    }
	case STRUCTS_TYPE_ARRAY:
	    {
		const struct structs_type *const etype = type->args[0].v;
		const struct structs_array *const ary = data;
		u_int i;

		/* Reset destination value to be an empty array */
		if (structs_array_reset(xtype, "array.data", xdata) == -1)
			return (-1);

		/* Copy over each element in the array */
		for (i = 0; i < ary->length; i++) {
			char buf[32];

			/* Add a new element to the dest array */
			if (structs_array_insert(xtype,
			    "array.data", i, xdata) == -1)
				return (-1);

			/* Set its value */
			snprintf(buf, sizeof(buf), "array.data.%u", i);
			if (structs_struct2xmlrpc(etype,
			    (char *)ary->elems + (i * etype->size), NULL,
			    xtype, xdata, buf) == -1)
				return (-1);
		}
		break;
	    }
	case STRUCTS_TYPE_FIXEDARRAY:
	    {
		const struct structs_type *const etype = type->args[0].v;
		const u_int length = type->args[2].i;
		u_int i;

		/* Reset destination value to be an empty array */
		if (structs_array_reset(xtype, "array.data", xdata) == -1)
			return (-1);

		/* Copy over each element in the array */
		for (i = 0; i < length; i++) {
			char buf[32];

			/* Add a new element to the dest array */
			if (structs_array_insert(xtype,
			    "array.data", i, xdata) == -1)
				return (-1);

			/* Set its value */
			snprintf(buf, sizeof(buf), "array.data.%u", i);
			if (structs_struct2xmlrpc(etype,
			    (char *)data + (i * etype->size), NULL,
			    xtype, xdata, buf) == -1)
				return (-1);
		}
		break;
	    }
	case STRUCTS_TYPE_STRUCTURE:
	    {
		const struct structs_field *field = type->args[0].v;
		u_int i;

		/* Reset destination value to be an empty struct,
		   which really means just an empty array of members. */
		if (structs_array_reset(xtype, "struct", xdata) == -1)
			return (-1);

		/* Copy over each field in the structure to the member array */
		for (i = 0; field->name != NULL; i++, field++) {
			char buf[32];

			/* Add a new element to the member array */
			if (structs_array_insert(xtype,
			    "struct", i, xdata) == -1)
				return (-1);

			/* Set the new member's name */
			snprintf(buf, sizeof(buf), "struct.%u.name", i);
			if (structs_set_string(xtype,
			    buf, field->name, xdata, NULL, 0) == -1)
				return (-1);

			/* Set the new member's value */
			snprintf(buf, sizeof(buf), "struct.%u.value", i);
			if (structs_struct2xmlrpc(field->type,
			    (char *)data + field->offset, NULL,
			    xtype, xdata, buf) == -1)
				return (-1);
		}
		break;
	    }
	case STRUCTS_TYPE_UNION:
	    {
		const struct structs_ufield *const fields = type->args[0].v;
		const struct structs_union *const un = data;
		const struct structs_ufield *field;

		/* Reset destination value to be an empty struct,
		   which really means just an empty array of members. */
		if (structs_array_reset(xtype, "struct", xdata) == -1)
			return (-1);

		/* Find field */
		for (field = fields; field->name != NULL
		    && strcmp(un->field_name, field->name) != 0; field++);
		if (field->name == NULL) {
			assert(0);
			errno = EINVAL;
			return (-1);
		}

		/* Add a new element to the member array */
		if (structs_array_insert(xtype, "struct", 0, xdata) == -1)
			return (-1);

		/* Set the new member's name */
		if (structs_set_string(xtype, "struct.0.name",
		    field->name, xdata, NULL, 0) == -1)
			return (-1);

		/* Set the new member's value */
		if (structs_struct2xmlrpc(field->type,
		    un->un, NULL, xtype, xdata, "struct.0.value") == -1)
			return (-1);
		break;
	    }
	default:
		assert(0);
		return (-1);
	}
	return (0);
}

/*
 * Copy the contents of an XML-RPC "value" structure into a normal structure.
 */
int
structs_xmlrpc2struct(const struct structs_type *xtype, const void *xdata,
	const char *xname, const struct structs_type *type,
	void *data, const char *sname, char *ebuf, size_t emax)
{
	const struct structs_union *xun;

	/* Safety */
	snprintf(ebuf, emax, "Unknown error");

	/* Find source and dest data */
	if ((xtype = structs_find(xtype, xname, (void **)&xdata, 0)) == NULL)
		goto fail_errno;
	if ((type = structs_find(type, sname, &data, 1)) == NULL)
		goto fail_errno;

	/* Source type must always be an XML-RPC value */
	if (xtype != &structs_type_xmlrpc_value) {
		snprintf(ebuf, emax, "xtype != &structs_type_xmlrpc_value");
		errno = EINVAL;
		return (-1);
	}
	assert (xtype->tclass == STRUCTS_TYPE_UNION);

	/* Dereference through pointer(s) */
	while (type->tclass == STRUCTS_TYPE_POINTER) {
		type = type->args[0].v;
		data = *((void **)data);
	}

	/* Get union info */
	xun = xdata;

	/* Check which union field is in use and transfer accordingly */
	if (strcmp(xun->field_name, "i4") == 0
	    || strcmp(xun->field_name, "int") == 0
	    || strcmp(xun->field_name, "boolean") == 0
	    || strcmp(xun->field_name, "double") == 0
	    || strcmp(xun->field_name, "string") == 0
	    || strcmp(xun->field_name, "dateTime.iso8601") == 0
	    || strcmp(xun->field_name, "base64") == 0) {
		const char *xfname = xun->field_name;
		char *s;

		if ((s = structs_get_string(xtype,
		    xfname, xdata, TYPED_MEM_TEMP)) == NULL)
			goto fail_errno;
		if (structs_set_string(type, NULL, s, data, ebuf, emax) == -1) {
			FREE(TYPED_MEM_TEMP, s);
			return (-1);
		}
		FREE(TYPED_MEM_TEMP, s);
	} else if (strcmp(xun->field_name, "struct") == 0) {
		int is_struct;
		u_int len;
		u_int i;

		/* Check destination type */
		if (type->tclass != STRUCTS_TYPE_STRUCTURE
		    && type->tclass != STRUCTS_TYPE_UNION) {
			snprintf(ebuf, emax, "can't convert XML-RPC struct");
			errno = EINVAL;
			return (-1);
		}
		is_struct = (type->tclass == STRUCTS_TYPE_STRUCTURE);

		/* Copy structure members */
		if ((len = structs_array_length(xtype, "struct", xdata)) == -1)
			goto fail_errno;
		for (i = 0; i < len; i++) {
			const struct structs_type *ftype = type;
			void *fdata = data;
			char buf[32];
			char *mname;

			/* Get member field name */
			snprintf(buf, sizeof(buf), "struct.%u.name", i);
			if ((mname = structs_get_string(xtype,
			    buf, xdata, TYPED_MEM_TEMP)) == NULL)
				goto fail_errno;

			/* Find field in struct or union */
			if ((ftype = structs_find(type,
			    mname, &fdata, 1)) == NULL) {
				if (errno != ENOENT) {
					FREE(TYPED_MEM_TEMP, mname);
					goto fail_errno;
				}
				snprintf(ebuf, emax, "unknown %s field \"%s\"",
				    is_struct ? "struct" : "union", mname);
				FREE(TYPED_MEM_TEMP, mname);
				errno = ENOENT;
				return (-1);
			}
			FREE(TYPED_MEM_TEMP, mname);

			/* Copy over value */
			snprintf(buf, sizeof(buf), "struct.%u.value", i);
			if (structs_xmlrpc2struct(xtype, xdata, buf,
			    ftype, fdata, NULL, ebuf, emax) == -1)
				return (-1);
		}
	} else if (strcmp(xun->field_name, "array") == 0) {
		const struct structs_type *const etype = type->args[0].v;
		struct structs_array *const ary = data;
		const u_int length = type->args[2].i;	/* if fixedarray */
		const struct structs_type *xatype;
		const struct structs_array *xary;
		int fixed;
		u_int i;

		/* Check destination type */
		if (type->tclass != STRUCTS_TYPE_ARRAY
		    && type->tclass != STRUCTS_TYPE_FIXEDARRAY) {
			snprintf(ebuf, emax, "can't convert XML-RPC array");
			errno = EINVAL;
			return (-1);
		}
		fixed = (type->tclass == STRUCTS_TYPE_FIXEDARRAY);

		/* Dig for the actual source array */
		xary = xdata;
		if ((xatype = structs_find(xtype,
		    "array.data", (void **)&xary, 0)) == NULL)
			goto fail_errno;

		/* Reset destination array */
		if (structs_reset(type, NULL, data) == -1)
			goto fail_errno;

		/* Check length for fixed arrays */
		if (fixed && xary->length > length) {
			snprintf(ebuf, emax, "XML-RPC array is too long");
			errno = EDOM;
			return (-1);
		}

		/* Copy over each element in the array */
		for (i = 0; i < xary->length; i++) {
			char buf[32];
			void *elem;

			/* Add/set new element in the dest array */
			if (fixed)
				elem = (char *)data + (i * etype->size);
			else {
				if (structs_array_insert(type,
				    NULL, i, data) == -1)
					goto fail_errno;
				elem = (char *)ary->elems + (i * etype->size);
			}

			/* Set its value */
			snprintf(buf, sizeof(buf), "%u", i);
			if (structs_xmlrpc2struct(xatype, xary, buf, etype,
			    elem, NULL, ebuf, emax) == -1)
				return (-1);
		}
	} else {
		snprintf(ebuf, emax, "unsupported XML-RPC type \"%s\"",
		    xun->field_name);
		errno = EINVAL;
		return (-1);
	}

	/* Done */
	return (0);

fail_errno:
	/* Return with error */
	snprintf(ebuf, emax, "%s", strerror(errno));
	return (-1);
}


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>