File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libpdel / http / http_xml.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 <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
#include <errno.h>
#include <pthread.h>

#include <openssl/ssl.h>

#include "structs/structs.h"
#include "structs/type/array.h"
#include "structs/type/union.h"
#include "structs/xml.h"
#include "structs/xmlrpc.h"

#include "io/boundary_fp.h"
#include "io/string_fp.h"
#include "sys/alog.h"
#include "util/typed_mem.h"

#include "http/http_defs.h"
#include "http/http_server.h"
#include "http/xml.h"

#define MEM_TYPE		"http_xml_send_xmlrpc"

/* Context for http_xml_send_xmlrpc() */
struct http_xml_send_xmlrpc_ctx {
	struct xmlrpc_request		*xreq;
	struct xmlrpc_response_union	*xrep;
};

/*
 * Internal functions
 */
static void	http_xml_send_xmlrpc_cleanup(void *arg);
static void	http_xml_send_cleanup(void *arg);

/*
 * Send an XML-RPC message
 *
 * Returns:
 *	 0	Success
 *	-1	System error, errno is set
 *	-2	XML-RPC fault received
 *
 * The reply 'rep' may be NULL to ignore any reply value, otherwise
 * it should already be initialized.
 *
 * If an XML-RPC fault is received, and "faultp" is not NULL, then
 * it will be initialized with the fault received.
 *
 * This properly handles the calling thread's being canceled.
 */
int
http_xml_send_xmlrpc(struct http_client *client,
	struct in_addr ip, u_int16_t port, int https,
	const char *username, const char *password,
	const char *methodName, u_int nparams,
	const struct structs_type **ptypes, const void **pdatas,
	const struct structs_type *rep_type, void *rep,
	struct xmlrpc_compact_fault *faultp, structs_xmllog_t *rlogger)
{
	const struct structs_type *const xreq_type
	    = &structs_type_xmlrpc_request;
	const struct structs_type *const xrep_type
	    = &structs_type_xmlrpc_response;
	const struct structs_type *const ftype
	    = &structs_type_xmlrpc_compact_fault;
	struct http_xml_send_xmlrpc_ctx *ctx;
	struct xmlrpc_compact_fault fault;
	char ebuf[128];
	int ret = -1;
	int r;

	/* Get context */
	if ((ctx = MALLOC(MEM_TYPE, sizeof(*ctx))) == NULL) {
		alogf(LOG_ERR, "%s: %m", "malloc");
		return (-1);
	}
	memset(ctx, 0, sizeof(*ctx));
	pthread_cleanup_push(http_xml_send_xmlrpc_cleanup, ctx);

	/* Build XML-RPC request and reply */
	if ((ctx->xreq = structs_xmlrpc_build_request(MEM_TYPE,
	    methodName, nparams, ptypes, pdatas)) == NULL) {
		alogf(LOG_ERR, "%s: %m", "structs_xmlrpc_build_request");
		goto fail;
	}
	if ((ctx->xrep = MALLOC(MEM_TYPE, xrep_type->size)) == NULL) {
		alogf(LOG_ERR, "%s: %m", "malloc");
		goto fail;
	}
	if (structs_init(xrep_type, NULL, ctx->xrep) == -1) {
		alogf(LOG_ERR, "%s: %m", "structs_init");
		FREE(MEM_TYPE, ctx->xrep);
		ctx->xrep = NULL;
		goto fail;
	}

#ifdef XML_RPC_DEBUG
	printf("%s: sending this XML-RPC request:\n", __FUNCTION__);
	(void)structs_xml_output(xreq_type, XML_RPC_REQUEST_TAG,
	    NULL, ctx->xreq, stdout, NULL, 0);
#endif

	/* Send request and get reply; note: we could get canceled here. */
	r = http_xml_send(client, ip, port, https, XML_RPC_URL,
	    username, password, XML_RPC_REQUEST_TAG, NULL, xreq_type,
	    ctx->xreq, STRUCTS_XML_FULL, XML_RPC_REPLY_TAG, NULL, NULL,
	    xrep_type, ctx->xrep, 0, rlogger);

#ifdef XML_RPC_DEBUG
	printf("%s: got this XML-RPC reply (error=%s):\n",
	    __FUNCTION__, r == -1 ? strerror(errno) : "none");
	(void)structs_xml_output(xrep_type, XML_RPC_REPLY_TAG,
	    NULL, ctx->xrep, stdout, NULL, 0);
#endif

	/* Check error */
	if (r == -1)
		goto fail;

	/* Check for fault */
	if (structs_init(ftype, NULL, &fault) == -1) {
		alogf(LOG_ERR, "%s: %m", "structs_init");
		goto fail;
	}
	if (structs_xmlrpc2struct(xrep_type, ctx->xrep,
	    "fault.value", ftype, &fault, NULL, NULL, 0) == 0) {
		if (faultp != NULL)
			*faultp = fault;
		else
			structs_free(ftype, NULL, &fault);
		ret = -2;			/* -2 indicates fault */
		goto fail;
	}
	structs_free(ftype, NULL, &fault);

	/* Extract response (if desired) */
	if (rep != NULL) {
		if (rep_type != NULL) {		/* return compact type */
			if (structs_xmlrpc2struct(xrep_type, ctx->xrep,
			    "params.0.value", rep_type, rep, NULL, ebuf,
			    sizeof(ebuf)) == -1) {
				(*rlogger)(LOG_ERR, "error decoding XML-RPC"
				    " response: %s", ebuf);
				goto fail;
			}
		} else {			/* return exploded type */
			if (structs_get(&structs_type_xmlrpc_value,
			    "params.0.value", ctx->xrep, rep) == -1) {
				alogf(LOG_ERR, "structs_get: %m");
				goto fail;
			}
		}
	}

	/* OK */
	ret = 0;

fail:;
	/* Done */
	pthread_cleanup_pop(1);
	return (ret);
}

/*
 * Cleanup for http_xml_send_xmlrpc()
 */
static void
http_xml_send_xmlrpc_cleanup(void *arg)
{
	struct http_xml_send_xmlrpc_ctx *const ctx = arg;
	const struct structs_type *const xreq_type
	    = &structs_type_xmlrpc_request;
	const struct structs_type *const xrep_type
	    = &structs_type_xmlrpc_response;

	if (ctx->xreq != NULL) {
		structs_free(xreq_type, NULL, ctx->xreq);
		FREE(MEM_TYPE, ctx->xreq);
	}
	if (ctx->xrep != NULL) {
		structs_free(xrep_type, NULL, ctx->xrep);
		FREE(MEM_TYPE, ctx->xrep);
	}
	FREE(MEM_TYPE, ctx);
}

/*
 * Send a copy of the message, wait for a reply, and return the reply.
 *
 * The "reply" should already be initialized.
 *
 * This properly handles the calling thread's being canceled.
 */
int
http_xml_send(struct http_client *client, struct in_addr ip,
	u_int16_t port, int https, const char *urlpath, const char *username,
	const char *password, const char *ptag, const char *pattrs,
	const struct structs_type *ptype, const void *payload, int pflags,
	const char *rtag, char **rattrsp, const char *rattrs_mtype,
	const struct structs_type *rtype, void *reply, int rflags,
	structs_xmllog_t *rlogger)
{
	struct http_client_connection *cc;
	struct http_request *req;
	struct http_response *resp;
	int ret = -1;
	u_int code;
	FILE *fp;

	/* Get HTTP connection */
	if ((cc = http_client_connect(client, ip, port, https)) == NULL) {
		alogf(LOG_ERR, "can't %s for %s:%u: %m",
		    "get HTTP client", inet_ntoa(ip), port);
		return -1;
	}

	/* Push cleanup hook */
	pthread_cleanup_push(http_xml_send_cleanup, cc);

	/* Set up request */
	req = http_client_get_request(cc);
	if (http_request_set_method(req,
	    payload != NULL ? HTTP_METHOD_POST : HTTP_METHOD_GET) == -1) {
		alogf(LOG_ERR, "can't %s for %s:%u: %m",
		    "set method", inet_ntoa(ip), port);
		goto fail;
	}
	if (http_request_set_path(req, urlpath) == -1) {
		alogf(LOG_ERR, "can't %s for %s:%u: %m",
		    "set path", inet_ntoa(ip), port);
		goto fail;
	}
	if (http_request_set_header(req, 0, "Content-Type", "text/xml") == -1) {
		alogf(LOG_ERR, "can't %s for %s:%u: %m",
		    "set content-type", inet_ntoa(ip), port);
		goto fail;
	}
	if (username != NULL && password != NULL) {
		char *auth;

		if ((auth = http_request_encode_basic_auth(TYPED_MEM_TEMP,
		    username, password)) == NULL) {
			alogf(LOG_ERR, "can't %s for %s:%u: %m",
			    "encode authorization", inet_ntoa(ip), port);
			goto fail;
		}
		if (http_request_set_header(req, 0, "Authorization",
		    "Basic %s", auth) == -1) {
			alogf(LOG_ERR, "can't %s for %s:%u: %m",
			    "set authorization header", inet_ntoa(ip), port);
			FREE(TYPED_MEM_TEMP, auth);
			goto fail;
		}
		FREE(TYPED_MEM_TEMP, auth);
	}

	/* Write XML data to HTTP client output stream */
	if (payload != NULL) {
		if ((fp = http_request_get_output(req, 1)) == NULL) {
			alogf(LOG_ERR, "can't %s for %s:%u: %m",
			    "get output", inet_ntoa(ip), port);
			goto fail;
		}
		if (structs_xml_output(ptype,
		    ptag, pattrs, payload, fp, NULL, pflags) == -1) {
			alogf(LOG_ERR, "can't %s for %s:%u: %m",
			    "write XML", inet_ntoa(ip), port);
			goto fail;
		}
	}

	/* Get response */
	if ((resp = http_client_get_response(cc)) == NULL) {
		alogf(LOG_ERR, "can't %s for %s:%u: %m",
		    "get response", inet_ntoa(ip), port);
		goto fail;
	}
	if ((code = http_response_get_code(resp)) != HTTP_STATUS_OK) {
		alogf(LOG_ERR, "rec'd HTTP error code %d from"
		    "http%s://%s:%u%s: %s", code, https ? "s" : "",
		    inet_ntoa(ip), port, urlpath,
		    http_response_status_msg(code));
		goto fail;
	}

	/* Read XML reply from client input stream */
	if ((fp = http_response_get_input(resp)) == NULL) {
		alogf(LOG_ERR, "can't %s for %s:%u: %m",
		    "get input", inet_ntoa(ip), port);
		goto fail;
	}
	if (structs_xml_input(rtype, rtag, rattrsp,
	    rattrs_mtype, fp, reply, rflags, rlogger) == -1) {
		alogf(LOG_ERR, "can't %s for %s:%u: %m",
		    "read XML reply", inet_ntoa(ip), port);
		goto fail;
	}

	/* OK */
	ret = 0;

fail:;
	/* Done */
	pthread_cleanup_pop(1);
	return (ret);
}

/*
 * Cleanup for http_xml_send()
 */
static void
http_xml_send_cleanup(void *arg)
{
	struct http_client_connection *cc = arg;

	http_client_close(&cc);
}


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