Add an XML-RPC interface with some simple test methods. --- ChangeLog Sat Feb 16 14:58:05 2002 +++ ChangeLog Sat Feb 16 15:01:54 2002 @@ -1,3 +1,11 @@ +2002-02-15 Gary Benson + + * Makefile: + * mod_virgule.c (virgule_handler): + * xmlrpc.c, xmlrpc.h: + * xmlrpc-methods.c, xmlrpc-methods.h: + * sample_db/site/xmlrpc.xml: Add XML-RPC API. + 2002-02-14 Gary Benson * acct_maint.c (acct_person_diary_xml_serve): diff -ruN sample_db/site/xmlrpc.xml --- sample_db/site/xmlrpc.xml Thu Jan 1 01:00:00 1970 +++ sample_db/site/xmlrpc.xml Thu Feb 14 19:23:57 2002 @@ -0,0 +1,38 @@ + +Advogato XML-RPC Interface + + +

Advogato now has an XML-RPC +interface available at the URL http://www.advogato.org/XMLRPC. +To test it, try calling one of the test functions below. If you are +using Python, for example, try the +following:

+ +
+>>> import xmlrpclib
+>>> server = xmlrpclib.Server("http://www.advogato.org/XMLRPC")
+>>> server.test.sumprod(5, 7)
+
+ +

It should return [12, 35].

+ +

Test functions

+ +
+
string s, int i = test.guess()
+
Guess a number
+ +
int result = test.square(int x)
+
Square a number
+ +
int sum, int product = test.sumprod(int x, int y)
+
Return the sum and product of a pair of numbers
+ +
int len = test.strlen(string s)
+
Return the length of a string
+ +
string s = test.capitalize(string s)
+
Capitalize a string
+
+ +
Index: Makefile =================================================================== RCS file: /cvs/gnome/mod_virgule/Makefile,v retrieving revision 1.4 diff -u -r1.4 Makefile --- Makefile 2000/08/24 23:42:54 1.4 +++ Makefile 2002/02/15 09:36:29 @@ -25,7 +25,8 @@ db.o db_ops.o db_xml.o schema.o \ net_flow.o tmetric.o \ diary.o article.o rss_export.o proj.o \ - wiki.o + wiki.o \ + xmlrpc.o xmlrpc-methods.o # the default target all: mod_virgule.so Index: mod_virgule.c =================================================================== RCS file: /cvs/gnome/mod_virgule/mod_virgule.c,v retrieving revision 1.4 diff -u -r1.4 mod_virgule.c --- mod_virgule.c 2000/08/24 23:42:54 1.4 +++ mod_virgule.c 2002/02/15 09:36:30 @@ -33,6 +33,7 @@ #include "tmetric.h" #include "auth.h" #include "db_ops.h" +#include "xmlrpc.h" typedef struct { char *dir; @@ -250,6 +251,10 @@ ap_table_add (r->headers_out, "Set-Cookie", "p=password3; path=/; Expires=Wednesday, 09-Nov-99 23:12:40 GMT"); #endif + status = xmlrpc_serve (vr); + if (status != DECLINED) + return status; + status = site_serve (vr); if (status != DECLINED) return status; diff -uN xmlrpc.c --- xmlrpc.c Thu Jan 1 01:00:00 1970 +++ xmlrpc.c Thu Feb 14 19:39:13 2002 @@ -0,0 +1,315 @@ +#include "httpd.h" +#include "http_log.h" + +#include +#include +#include + +#include "buffer.h" +#include "db.h" +#include "req.h" + +#include "xmlrpc.h" +#include "xmlrpc-methods.h" + + +/* Create s */ +static xmlNode * +xmlrpc_value_int (xmlNode *node, long i) +{ + char buf[12]; + ap_snprintf(buf, 12, "%ld", i); + + node = xmlNewChild (node, NULL, "value", NULL); + node = xmlNewChild (node, NULL, "int", buf); + return node; +} + +static xmlNode * +xmlrpc_value_string (xmlNode *node, const char *s) +{ + node = xmlNewChild (node, NULL, "value", NULL); + node = xmlNewChild (node, NULL, "string", s); + return node; +} + +static xmlNode * +xmlrpc_value_struct (xmlNode *node) +{ + node = xmlNewChild (node, NULL, "value", NULL); + node = xmlNewChild (node, NULL, "struct", NULL); + return node; +} + +static xmlNode * +xmlrpc_struct_add_member (xmlNode *node, const char *name) +{ + node = xmlNewChild (node, NULL, "member", NULL); + xmlNewChild (node, NULL, "name", name); + return node; +} + + +/* Create and send responses */ +static xmlNode * +xmlrpc_create_response (VirguleReq *vr) +{ + xmlDoc *r = xmlNewDoc ("1.0"); + r->root = xmlNewDocNode (r, NULL, "methodResponse", NULL); + return r->root->doc == r ? r->root : NULL; +} + +static int +xmlrpc_send_response (VirguleReq *vr, xmlNode *r) +{ + xmlChar *mem; + int size; + + xmlDocDumpMemory (r->doc, &mem, &size); + buffer_write (vr->b, mem, size); + xmlFree (mem); + xmlFreeDoc (r->doc); + + vr->r->content_type = "text/xml"; + return send_response (vr); +} + + +/* Create and send a fault response */ +int +xmlrpc_fault (VirguleReq *vr, int code, const char *fmt, ...) +{ + xmlNode *resp, *str; + va_list ap; + char *msg; + + va_start (ap, fmt); + msg = ap_pvsprintf (vr->r->pool, fmt, ap); + va_end (ap); + + resp = xmlrpc_create_response (vr); + + str = xmlrpc_value_struct (xmlNewChild (resp, NULL, "fault", NULL)); + xmlrpc_value_int (xmlrpc_struct_add_member (str, "faultCode"), code); + xmlrpc_value_string (xmlrpc_struct_add_member (str, "faultString"), msg); + + return xmlrpc_send_response (vr, resp); +} + + +/* Create and send a normal response */ +int +xmlrpc_response (VirguleReq *vr, const char *types, ...) +{ + xmlNode *resp; + xmlNode *container; + va_list va; + int i; + + if (!strlen (types)) + return xmlrpc_fault (vr, 1, "internal error: must return something!"); + + resp = xmlrpc_create_response (vr); + container = xmlNewChild (xmlNewChild (resp, NULL, "params", NULL), + NULL, "param", NULL); + + if (strlen (types) > 1) + container = xmlNewChild (xmlNewChild (resp, NULL, "array", NULL), + NULL, "data", NULL); + + va_start (va, types); + for (i=0; iname, "params")) + return xmlrpc_fault (vr, 1, "expecting , got <%s>", + params->name); + + for (param = params->childs; param; param = param->next) + { + if (xmlIsBlankNode (param)) + continue; + argc++; + } + if (argc != strlen (types)) + return xmlrpc_fault (vr, 1, "expecting %d parameters, got %d", + strlen (types), argc); + } + else + { + if (strlen(types)) + return xmlrpc_fault (vr, 1, "expecting %d parameters, got 0", + strlen (types)); + return OK; + } + + /* unmarshal the parameters */ + param = params->childs; + va_start (va, types); + for (i=0; inext; + + value = param->childs; + while (xmlIsBlankNode (value)) + value = value->next; + + value = value->childs; + while (xmlIsBlankNode (value)) + value = value->next; + + switch (types[i]) + { + case 'i': + if (!strcmp (value->name, "int") || !strcmp (value->name, "i4")) + { + char *val; + + val = xmlNodeListGetString (value->doc, value->childs, 1); + *va_arg (va, int *) = atoi (val); + xmlFree (val); + } + else + { + va_end (va); + return xmlrpc_fault (vr, 1, + "param %d: expecting , got <%s>", + i, value->name); + } + break; + + case 's': + if (!strcmp (value->name, "string")) + { + char *val; + + val = xmlNodeListGetString (value->doc, value->childs, 1); + *va_arg (va, char **) = ap_pstrdup (vr->r->pool, val); + xmlFree (val); + } + else + { + va_end (va); + return xmlrpc_fault (vr, 1, + "param %d: expecting , got <%s>", + i, value->name); + } + + break; + + default: + va_end (va); + return xmlrpc_fault (vr, 1, "internal error: unknown type '%c'", + types[i]); + } + param = param->next; + } + va_end (va); + + return OK; +} + + +/* Fob the request off at the appropriate method function */ +static int +xmlrpc_unmarshal_request (VirguleReq *vr, xmlNode *xr) +{ + extern xmlrpc_method xmlrpc_method_table[]; + xmlNode *n; + char *tmp; + const char *name; + xmlrpc_method *m; + + /* root element should be a */ + if (strcmp (xr->name, "methodCall")) + return xmlrpc_fault (vr, 1, "expecting , got <%s>", xr->name); + + /* first element of methodCall should be a */ + n = xr->childs; + while (n && xmlIsBlankNode (n)) + n = n->next; + + if (!n || strcmp (n->name, "methodName")) + return xmlrpc_fault (vr, 1, "expecting , got <%s>", n->name); + + tmp = xmlNodeListGetString (n->doc, n->childs, 1); + name = ap_pstrdup (vr->r->pool, tmp); + xmlFree (tmp); + + /* second element of methodCall should be a */ + n = n->next; + while (n && xmlIsBlankNode (n)) + n = n->next; + + /* dispatch the method */ + for (m = xmlrpc_method_table; m->name != NULL; m++) + { + if (!strcmp (m->name, name)) + break; + } + if (m->name == NULL) + return xmlrpc_fault (vr, 1, "%s: method not implemented", name); + + return m->func (vr, n); +} + + +/* Main handler for requests */ +int +xmlrpc_serve (VirguleReq *vr) +{ + xmlDoc *request = NULL; + int ret; + + if (strcmp (vr->uri, "/XMLRPC")) + return DECLINED; + + if (vr->r->method_number != M_POST) + return METHOD_NOT_ALLOWED; + + request = xmlParseMemory (vr->args, vr->r->read_length); + if (request == NULL) + return xmlrpc_fault (vr, 1, "unable to parse request"); + + ret = xmlrpc_unmarshal_request (vr, request->root); + xmlFreeDoc (request); + return ret; +} diff -uN xmlrpc.h --- xmlrpc.h Thu Jan 1 01:00:00 1970 +++ xmlrpc.h Thu Feb 14 18:42:32 2002 @@ -0,0 +1,13 @@ +#define XMLRPC_NO_PARAMS "" + +int +xmlrpc_serve (VirguleReq *vr); + +int +xmlrpc_unmarshal_params (VirguleReq *vr, xmlNode *params, + const char *types, ...); +int +xmlrpc_response (VirguleReq *vr, const char *types, ...); + +int +xmlrpc_fault (VirguleReq *vr, int code, const char *fmt, ...); diff -uN xmlrpc-methods.c --- xmlrpc-methods.c Thu Jan 1 01:00:00 1970 +++ xmlrpc-methods.c Thu Feb 14 19:38:33 2002 @@ -0,0 +1,105 @@ +/* The mod_virgule XML-RPC API in four easy steps: + * + * 1. the method should call xmlrpc_unmarshal_params() even if it + * doesn't have any parameters. + * 2. a) on success the method should call xmlrpc_response() + * b) on failure the method should call xmlrpc_fault() + * 3. add your method to method_table[] so it can be called. + * 4. add your method to sample_db/site/xmlrpc.xml + */ + +#include "httpd.h" +#include "http_log.h" + +#include + +#include "buffer.h" +#include "db.h" +#include "req.h" + +#include "xmlrpc.h" +#include "xmlrpc-methods.h" + + +/* Simple functions to test the stuff in xmlrpc.c + */ + +static int +test_guess (VirguleReq *vr, xmlNode *params) +{ + int ret; + + ret = xmlrpc_unmarshal_params (vr, params, XMLRPC_NO_PARAMS); + if (ret != OK) + return ret; + + return xmlrpc_response (vr, "si", "You guessed", 42); +} + +static int +test_square (VirguleReq *vr, xmlNode *params) +{ + int x; + int ret; + + ret = xmlrpc_unmarshal_params (vr, params, "i", &x); + if (ret != OK) + return ret; + + return xmlrpc_response (vr, "i", x*x); +} + +static int +test_sumprod (VirguleReq *vr, xmlNode *params) +{ + int x, y; + int ret; + + ret = xmlrpc_unmarshal_params (vr, params, "ii", &x, &y); + if (ret != OK) + return ret; + + return xmlrpc_response (vr, "ii", x+y, x*y); +} + +static int +test_strlen (VirguleReq *vr, xmlNode *params) +{ + char *s; + int ret; + + ret = xmlrpc_unmarshal_params (vr, params, "s", &s); + if (ret != OK) + return ret; + + return xmlrpc_response (vr, "i", strlen (s)); +} + +static int +test_capitalize (VirguleReq *vr, xmlNode *params) +{ + char *s; + int i; + int ret; + + ret = xmlrpc_unmarshal_params (vr, params, "s", &s); + if (ret != OK) + return ret; + + for (i=0; i