]> xenbits.xensource.com Git - libvirt.git/commitdiff
First import of XML-RPC protocol support for newer versions of Xend. I've not
authorAnthony Liguori <aliguori@us.ibm.com>
Fri, 10 Mar 2006 17:02:25 +0000 (17:02 +0000)
committerAnthony Liguori <aliguori@us.ibm.com>
Fri, 10 Mar 2006 17:02:25 +0000 (17:02 +0000)
put it in the Makefiles yet as it's not actually being used by anything.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
src/xmlrpc.c [new file with mode: 0644]
src/xmlrpc.h [new file with mode: 0644]

diff --git a/src/xmlrpc.c b/src/xmlrpc.c
new file mode 100644 (file)
index 0000000..531c2fc
--- /dev/null
@@ -0,0 +1,608 @@
+/*
+ * xmlrpc.c: XML-RPC protocol handler for libvir library
+ *
+ * Copyright (C) 2006  IBM, Corp.
+ *
+ * See COPYING.LIB for the License of this software
+ *
+ * Anthony Liguori <aliguori@us.ibm.com>
+ */
+
+#include "xmlrpc.h"
+
+#include <libxml/nanohttp.h>
+
+#include <string.h>
+#include <errno.h>
+
+/* TODO
+   1) Lots of error checking
+   2) xmlRpcValueToSexpr
+*/
+
+#define TODO do { } while (0)
+
+static xmlNodePtr xmlFirstElement(xmlNodePtr node);
+static xmlNodePtr xmlNextElement(xmlNodePtr node);
+
+struct _xmlRpcContext
+{
+    char *uri;
+    int faultCode;
+    char *faultMessage;
+};
+
+static xmlRpcValuePtr xmlRpcValueNew(xmlRpcValueType type)
+{
+    xmlRpcValuePtr ret = malloc(sizeof(*ret));
+    if (ret)
+       ret->kind = type;
+    return ret;
+}
+
+static char *xmlGetText(xmlNodePtr node)
+{
+    for (node = node->children; node; node = node->next)
+       if (node->type == XML_TEXT_NODE)
+           return strdup((const char *)node->content);
+    return NULL;
+}
+
+static xmlNodePtr xmlFirstElement(xmlNodePtr node)
+{
+    for (node = node->children; node; node = node->next)
+       if (node->type == XML_ELEMENT_NODE)
+           break;
+    return node;
+}
+
+static xmlNodePtr xmlNextElement(xmlNodePtr node)
+{
+    for (node = node->next; node; node = node->next)
+       if (node->type == XML_ELEMENT_NODE)
+           break;
+    return node;
+}
+
+static xmlRpcValuePtr xmlRpcValueUnmarshalDateTime(xmlNodePtr node)
+{
+    /* we don't need this */
+    TODO;
+    return NULL;
+}
+
+static xmlRpcValuePtr xmlRpcValueUnmarshalString(xmlNodePtr node)
+{
+    xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_STRING);
+    ret->value.string = xmlGetText(node);
+    return ret;
+}
+
+static xmlRpcValuePtr xmlRpcValueUnmarshalBase64(xmlNodePtr node)
+{
+    /* we don't need this */
+    TODO;
+    return NULL;
+}
+
+static xmlRpcValuePtr xmlRpcValueUnmarshalInteger(xmlNodePtr node)
+{
+    xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_INTEGER);
+    char *value = xmlGetText(node);
+
+    ret->value.integer = atoi(value);
+
+    free(value);
+
+    return ret;
+}
+
+static xmlRpcValuePtr xmlRpcValueUnmarshalBoolean(xmlNodePtr node)
+{
+    xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_BOOLEAN);
+    char *value = xmlGetText(node);
+
+    if (atoi(value))
+       ret->value.boolean = true;
+    else
+       ret->value.boolean = false;
+
+    free(value);
+
+    return ret;
+}
+
+static xmlRpcValuePtr xmlRpcValueUnmarshalDouble(xmlNodePtr node)
+{
+    xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_DOUBLE);
+    char *value = xmlGetText(node);
+
+    ret->value.real = atof(value);
+
+    free(value);
+
+    return ret;
+}
+
+static xmlRpcValuePtr xmlRpcValueUnmarshalArray(xmlNodePtr node)
+{
+    xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_ARRAY);
+    xmlNodePtr cur;
+    int n_elements = 0;
+
+    for (cur = xmlFirstElement(node); cur; cur = xmlNextElement(cur))
+       n_elements += 1;
+
+    ret->value.array.elements = malloc(n_elements * sizeof(xmlRpcValue));
+
+    n_elements = 0;
+    for (cur = xmlFirstElement(node); cur; cur = xmlNextElement(cur)) {
+       ret->value.array.elements[n_elements] = xmlRpcValueUnmarshal(cur);
+       n_elements += 1;
+    }
+
+    ret->value.array.n_elements = n_elements;
+
+    return ret;
+}
+
+static xmlRpcValueDictElementPtr xmlRpcValueUnmarshalDictElement(xmlNodePtr node)
+{
+    xmlRpcValueDictElementPtr ret = malloc(sizeof(*ret));
+    xmlNodePtr cur;
+
+    memset(ret, 0, sizeof(*ret));
+
+    for (cur = xmlFirstElement(node); cur; cur = xmlNextElement(cur)) {
+       if (xmlStrEqual(cur->name, BAD_CAST "name")) {
+           ret->name = xmlGetText(cur);
+       } else if (xmlStrEqual(cur->name, BAD_CAST "value")) {
+           ret->value = xmlRpcValueUnmarshal(cur);
+       } else {
+           /* What? */
+       }
+    }
+
+    ret->next = NULL;
+
+    return ret;
+}
+
+static xmlRpcValuePtr xmlRpcValueUnmarshalDict(xmlNodePtr node)
+{
+    xmlRpcValueDictElementPtr root = NULL, *elem = &root;
+    xmlRpcValuePtr ret = xmlRpcValueNew(XML_RPC_STRUCT);
+    xmlNodePtr cur;
+
+    for (cur = xmlFirstElement(node); cur; cur = xmlNextElement(cur)) {
+       *elem = xmlRpcValueUnmarshalDictElement(cur);
+       elem = &(*elem)->next;
+    }
+
+    ret->value.dict.root = root;
+
+    return ret;
+}
+
+xmlRpcValuePtr xmlRpcValueUnmarshal(xmlNodePtr node)
+{
+    xmlNodePtr n;
+    xmlRpcValuePtr ret;
+
+    if (xmlStrEqual(node->name, BAD_CAST "value")) {
+       n = xmlFirstElement(node);
+       if (n == NULL) {
+           ret = xmlRpcValueUnmarshalString(node);
+       } else {
+           ret = xmlRpcValueUnmarshal(n);
+       }
+    } else if (xmlStrEqual(node->name, BAD_CAST "dateTime.iso8601")) {
+       ret = xmlRpcValueUnmarshalDateTime(node);
+    } else if (xmlStrEqual(node->name, BAD_CAST "string")) {
+       ret = xmlRpcValueUnmarshalString(node);
+    } else if (xmlStrEqual(node->name, BAD_CAST "base64")) {
+       ret = xmlRpcValueUnmarshalBase64(node);
+    } else if (xmlStrEqual(node->name, BAD_CAST "i4") ||
+              xmlStrEqual(node->name, BAD_CAST "int")) {
+       ret = xmlRpcValueUnmarshalInteger(node);
+    } else if (xmlStrEqual(node->name, BAD_CAST "boolean")) {
+       ret = xmlRpcValueUnmarshalBoolean(node);
+    } else if (xmlStrEqual(node->name, BAD_CAST "double")) {
+       ret = xmlRpcValueUnmarshalDouble(node);
+    } else if (xmlStrEqual(node->name, BAD_CAST "array")) {
+       ret = xmlRpcValueUnmarshal(xmlFirstElement(node));
+    } else if (xmlStrEqual(node->name, BAD_CAST "data")) {
+       ret = xmlRpcValueUnmarshalArray(node);
+    } else if (xmlStrEqual(node->name, BAD_CAST "struct")) {
+       ret = xmlRpcValueUnmarshalDict(node);
+    } else if (xmlStrEqual(node->name, BAD_CAST "nil")) {
+       ret = xmlRpcValueNew(XML_RPC_NIL);
+    } else {
+       /* bug */
+    }
+
+    return ret;
+}
+
+void xmlRpcValueFree(xmlRpcValuePtr value)
+{
+    int i;
+    xmlRpcValueDictElementPtr cur, next;
+
+    if (value == NULL)
+       return;
+
+    switch (value->kind) {
+    case XML_RPC_ARRAY:
+       for (i = 0; i < value->value.array.n_elements; i++)
+           xmlRpcValueFree(value->value.array.elements[i]);
+       free(value->value.array.elements);
+       break;
+    case XML_RPC_STRUCT:
+       next = value->value.dict.root;
+       while (next) {
+           cur = next;
+           next = next->next;
+           free(cur->name);
+           xmlRpcValueFree(cur->value);
+           free(cur);
+       }
+       break;
+    case XML_RPC_STRING:
+       free(value->value.string);
+       break;
+    default:
+       break;
+    }
+
+    free(value);
+}
+
+void xmlRpcValueMarshal(xmlRpcValuePtr value, virBufferPtr buf, int indent)
+{
+    int i;
+    xmlRpcValueDictElement *elem;
+
+    virBufferVSprintf(buf, "%*s<value>", indent, "");
+    switch (value->kind) {
+    case XML_RPC_ARRAY:
+       virBufferVSprintf(buf, "<array><data>\n", indent, "");
+       for (i = 0; i < value->value.array.n_elements; i++)
+           xmlRpcValueMarshal(value->value.array.elements[i], buf, indent+2);
+       virBufferVSprintf(buf, "%*s</data></array>", indent, "");
+       break;
+    case XML_RPC_STRUCT:
+       virBufferVSprintf(buf, "<struct>\n", indent, "");
+       indent += 2;
+       for (elem = value->value.dict.root; elem; elem = elem->next) {
+           virBufferVSprintf(buf, "%*s<member>\n", indent, "");
+           virBufferVSprintf(buf, "%*s<name>%s</name>\n",
+                             indent + 2, "", elem->name);
+           xmlRpcValueMarshal(elem->value, buf, indent + 2);
+           virBufferVSprintf(buf, "%*s</member>\n", indent, "");
+       }
+       indent -= 2;
+       virBufferVSprintf(buf, "%*s</struct>", indent, "");
+       break;
+    case XML_RPC_INTEGER:
+       virBufferVSprintf(buf, "<int>%d</int>", value->value.integer);
+       break;
+    case XML_RPC_DOUBLE:
+       virBufferVSprintf(buf, "<double>%f</double>", value->value.real);
+       break;
+    case XML_RPC_BOOLEAN:
+       if (value->value.boolean)
+           i = 1;
+       else
+           i = 0;
+       virBufferVSprintf(buf, "<boolean>%d</boolean>", i);
+       break;
+    case XML_RPC_DATE_TIME:
+       /* FIXME */
+       TODO;
+       break;
+    case XML_RPC_BASE64:
+       /* FIXME */
+       TODO;
+       break;
+    case XML_RPC_STRING:
+       virBufferVSprintf(buf, "<string>%s</string>", value->value.string);
+       break;
+    case XML_RPC_NIL:
+       virBufferVSprintf(buf, "<nil> </nil>");
+       break;
+    }
+    virBufferVSprintf(buf, "</value>\n");
+}
+
+virBufferPtr xmlRpcMarshalRequest(const char *request,
+                                 int argc, xmlRpcValuePtr *argv)
+{
+    virBufferPtr buf;
+    int i;
+
+    buf = malloc(sizeof(*buf));
+    buf->size = 1024;
+    buf->content = malloc(buf->size);
+    buf->use = 0;
+
+    virBufferVSprintf(buf,
+                     "<?xml version=\"1.0\"?>\n"
+                     "<methodCall>\n"
+                     "  <methodName>%s</methodName>\n"
+                     "  <params>\n",
+                     request);
+    for (i = 0; i < argc; i++) {
+       virBufferVSprintf(buf,
+                         "    <param>\n");
+       xmlRpcValueMarshal(argv[i], buf, 6);
+       virBufferVSprintf(buf,
+                         "    </param>\n");
+    }
+    virBufferVSprintf(buf,
+                     "  </params>\n"
+                     "</methodCall>\n");
+
+    return buf;
+}
+
+xmlRpcValuePtr xmlRpcUnmarshalResponse(xmlNodePtr node, bool *is_fault)
+{
+    if (!xmlStrEqual(node->name, BAD_CAST "methodResponse"))
+       return NULL;
+
+    node = xmlFirstElement(node);
+    if (xmlStrEqual(node->name, BAD_CAST "params")) {
+       node = xmlFirstElement(node);
+
+       if (!xmlStrEqual(node->name, BAD_CAST "param"))
+           return NULL;
+
+       *is_fault = false;
+       return xmlRpcValueUnmarshal(xmlFirstElement(node));
+    } else if (xmlStrEqual(node->name, BAD_CAST "fault")) {
+       *is_fault = true;
+       return xmlRpcValueUnmarshal(xmlFirstElement(node));
+    } else
+       return NULL;
+
+}
+
+static char *xmlRpcCallRaw(const char *url, const char *request)
+{
+       void *cxt;
+       char *contentType = "text/xml";
+       int len, ret, serrno;
+       char *response = NULL;
+
+       cxt = xmlNanoHTTPMethod(url,
+                               "POST",
+                               request,
+                               &contentType,
+                               NULL,
+                               strlen(request));
+       
+       if (cxt == NULL)
+               goto error;
+
+       if (contentType && strcmp(contentType, "text/xml") != 0) {
+               errno = EINVAL;
+               goto error;
+       }
+
+       len = xmlNanoHTTPContentLength(cxt);
+       response = malloc(len + 1);
+       if (response == NULL)
+               goto error;
+
+       ret = xmlNanoHTTPRead(cxt, response, len);
+       if (ret != len) {
+               errno = EINVAL;
+               free(response);
+               response = NULL;
+       }
+
+       response[len] = 0;
+
+ error:
+       serrno = errno;
+       if (cxt) {
+               xmlNanoHTTPClose(cxt);
+               free(contentType);
+       }
+       errno = serrno;
+
+       return response;
+}
+
+static char **xmlRpcStringArray(xmlRpcValuePtr value)
+{
+    char **ret, *ptr;
+    int i;
+    size_t size = 0;
+
+    if (value->kind != XML_RPC_ARRAY)
+       return NULL;
+
+    size = sizeof(char *) * (value->value.array.n_elements + 1);
+
+    for (i = 0; i < value->value.array.n_elements; i++)
+       if (value->value.array.elements[i]->kind == XML_RPC_STRING)
+           size += strlen(value->value.array.elements[i]->value.string) + 1;
+
+    ptr = malloc(size);
+
+    ret = (char **)ptr;
+    ptr += sizeof(char *) * (value->value.array.n_elements + 1);
+
+    for (i = 0; i < value->value.array.n_elements; i++) {
+       if (value->value.array.elements[i]->kind == XML_RPC_STRING) {
+           char *s = value->value.array.elements[i]->value.string;
+           strcpy(ptr, s);
+           ret[i] = ptr;
+           ptr += strlen(s) + 1;
+       } else
+           ret[i] = "";
+    }
+
+    ret[i] = NULL;
+
+    return ret;
+}
+
+int xmlRpcCall(xmlRpcContextPtr context, const char *method,
+              const char *retfmt, const char *fmt, ...)
+{
+    va_list ap;
+    const char *ptr;
+    int argc;
+    xmlRpcValuePtr *argv;
+    virBufferPtr buf;
+    int i;
+    char *ret;
+    xmlDocPtr xml;
+    xmlNodePtr node;
+    bool fault;
+    xmlRpcValuePtr value;
+    void *retval;
+
+    argc = strlen(fmt);
+    argv = malloc(sizeof(*argv) * argc);
+
+    va_start(ap, fmt);
+
+    if (retfmt && *retfmt)
+       retval = va_arg(ap, void *);
+
+    i = 0;
+    for (ptr = fmt; *ptr; ptr++) {
+       switch (*ptr) {
+       case 'i':
+           argv[i] = xmlRpcValueNew(XML_RPC_INTEGER);
+           argv[i]->value.integer = va_arg(ap, int32_t);
+           break;
+       case 'f':
+           argv[i] = xmlRpcValueNew(XML_RPC_DOUBLE);
+           argv[i]->value.real = va_arg(ap, double);
+           break;
+       case 'b':
+           argv[i] = xmlRpcValueNew(XML_RPC_BOOLEAN);
+           argv[i]->value.boolean = va_arg(ap, int);
+           break;
+       case 's':
+           argv[i] = xmlRpcValueNew(XML_RPC_STRING);
+           argv[i]->value.string = strdup(va_arg(ap, const char *));
+           break;
+       default:
+           return -1;
+       }
+       i++;
+    }
+    va_end(ap);
+
+    buf = xmlRpcMarshalRequest(method, argc, argv);
+
+    for (i = 0; i < argc; i++)
+       xmlRpcValueFree(argv[i]);
+
+    free(argv);
+       
+    ret = xmlRpcCallRaw(context->uri, buf->content);
+
+    free(buf->content);
+    free(buf);
+
+    xml = xmlReadDoc((const xmlChar *)ret, "response.xml", NULL,
+                    XML_PARSE_NOENT | XML_PARSE_NONET |
+                    XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
+    free(ret);
+
+    if (xml == NULL) {
+       errno = EINVAL;
+       return -1;
+    }
+
+    node = xmlDocGetRootElement(xml);
+
+    value = xmlRpcUnmarshalResponse(node, &fault);
+
+    if (!fault) {
+       switch (*retfmt) {
+       case 'i':
+           if (value->kind == XML_RPC_INTEGER)
+               *(int32_t *)retval = value->value.integer;
+           break;
+       case 'b':
+           if (value->kind == XML_RPC_BOOLEAN)
+               *(bool *)retval = value->value.boolean;
+           break;
+       case 'f':
+           if (value->kind == XML_RPC_DOUBLE)
+               *(double *)retval = value->value.real;
+           break;
+       case 's':
+           if (value->kind == XML_RPC_STRING)
+               *(char **)retval = strdup(value->value.string);
+           break;
+       case 'S':
+           *(char ***)retval = xmlRpcStringArray(value);
+           break;
+       case 'V':
+           *(xmlRpcValuePtr *)retval = value;
+           value = NULL;
+           break;
+       default:
+           printf("not supported yet\n");
+           break;
+       }
+    }
+
+    xmlFreeDoc(xml);
+
+    if (fault) { /* FIXME we need generic dict routines */
+       context->faultCode = value->value.dict.root->value->value.integer;
+       context->faultMessage = strdup(value->value.dict.root->next->value->value.string);
+       xmlRpcValueFree(value);
+       errno = EFAULT;
+       return -1;
+    }
+
+    xmlRpcValueFree(value);
+
+    return 0;
+}
+
+xmlRpcContextPtr xmlRpcContextNew(const char *uri)
+{
+    xmlRpcContextPtr ret = malloc(sizeof(*ret));
+
+    if (ret) {
+       ret->uri = strdup(uri);
+       ret->faultMessage = NULL;
+    }
+
+    return ret;
+}
+
+void xmlRpcContextFree(xmlRpcContextPtr context)
+{
+    if (context) {
+       if (context->uri)
+           free(context->uri);
+
+       if (context->faultMessage)
+           free(context->faultMessage);
+
+       free(context);
+    }
+}
+
+int xmlRpcContextFaultCode(xmlRpcContextPtr context)
+{
+    return context->faultCode;
+}
+
+const char *xmlRpcContextFaultMessage(xmlRpcContextPtr context)
+{
+    return context->faultMessage;
+}
diff --git a/src/xmlrpc.h b/src/xmlrpc.h
new file mode 100644 (file)
index 0000000..2866753
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * xmlrpc.c: XML-RPC protocol handler for libvir library
+ *
+ * Copyright (C) 2006  IBM, Corp.
+ *
+ * See COPYING.LIB for the License of this software
+ *
+ * Anthony Liguori <aliguori@us.ibm.com>
+ */
+
+#ifndef _VIR_XML_RPC_H_
+#define _VIR_XML_RPC_H_
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "xml.h"
+
+typedef enum _xmlRpcValueType xmlRpcValueType;
+
+typedef struct _xmlRpcValueArray xmlRpcValueArray;
+typedef struct _xmlRpcValueDictElement xmlRpcValueDictElement;
+typedef struct _xmlRpcValueDict xmlRpcValueDict;
+typedef struct _xmlRpcValueBase64 xmlRpcValueBase64;
+typedef struct _xmlRpcValue xmlRpcValue;
+typedef struct _xmlRpcContext xmlRpcContext;
+
+typedef xmlRpcValueArray *xmlRpcValueArrayPtr;
+typedef xmlRpcValueDictElement *xmlRpcValueDictElementPtr;
+typedef xmlRpcValueDict *xmlRpcValueDictPtr;
+typedef xmlRpcValueBase64 *xmlRpcValueBase64Ptr;
+typedef xmlRpcValue *xmlRpcValuePtr;
+typedef xmlRpcContext *xmlRpcContextPtr;
+
+enum _xmlRpcValueType {
+    XML_RPC_ARRAY,
+    XML_RPC_STRUCT,
+    XML_RPC_INTEGER,
+    XML_RPC_DOUBLE,
+    XML_RPC_BOOLEAN,
+    XML_RPC_DATE_TIME,
+    XML_RPC_BASE64,
+    XML_RPC_STRING,
+    XML_RPC_NIL,
+};
+
+struct _xmlRpcValueArray {
+    int n_elements;
+    xmlRpcValuePtr *elements;
+};
+
+struct _xmlRpcValueDictElement {
+    char *name;
+    xmlRpcValuePtr value;
+    xmlRpcValueDictElementPtr next;
+};
+
+struct _xmlRpcValueDict {
+    xmlRpcValueDictElementPtr root;
+};
+
+struct _xmlRpcValueBase64 {
+    void *data;
+    size_t n_data;
+};
+
+struct _xmlRpcValue {
+    xmlRpcValueType kind;
+
+    union {
+       char *string;
+       xmlRpcValueArray array;
+       xmlRpcValueDict dict;
+       int32_t integer;
+       double real;
+       bool boolean;
+       time_t dateTime;
+       xmlRpcValueBase64 base64;
+    } value;
+};
+
+struct _xmlRpcContext;
+
+virBufferPtr xmlRpcMarshalRequest(const char *request,
+                                 int argc, xmlRpcValuePtr *argv);
+
+xmlRpcValuePtr xmlRpcUnmarshalResponse(xmlNodePtr node, bool *is_fault);
+
+void xmlRpcValueMarshal(xmlRpcValuePtr value, virBufferPtr buf, int indent);
+
+xmlRpcValuePtr xmlRpcValueUnmarshal(xmlNodePtr node);
+
+void xmlRpcValueFree(xmlRpcValuePtr value);
+
+int xmlRpcCall(xmlRpcContextPtr context, const char *method,
+              const char *retval, const char *fmt, ...);
+
+xmlRpcContextPtr xmlRpcContextNew(const char *uri);
+
+void xmlRpcContextFree(xmlRpcContextPtr context);
+
+int xmlRpcContextFaultCode(xmlRpcContextPtr context);
+
+const char *xmlRpcContextFaultMessage(xmlRpcContextPtr context);
+
+#endif