diff options
author | Dan Libby <danda@php.net> | 2001-09-06 04:13:30 +0000 |
---|---|---|
committer | Dan Libby <danda@php.net> | 2001-09-06 04:13:30 +0000 |
commit | 6883b9211652163ef3f50f448db2ddd8fae6ff99 (patch) | |
tree | dd1897cb279181689cd41c92318cfb8b30963a05 | |
parent | 903c831be5cd65e5070182a74f5336eba168807d (diff) | |
download | php-git-6883b9211652163ef3f50f448db2ddd8fae6ff99.tar.gz |
adding xmlrpc extension, per Stig's request
70 files changed, 19104 insertions, 0 deletions
diff --git a/ext/rpc/xmlrpc/CREDITS b/ext/rpc/xmlrpc/CREDITS new file mode 100644 index 0000000000..cfb14faf80 --- /dev/null +++ b/ext/rpc/xmlrpc/CREDITS @@ -0,0 +1,2 @@ +xmlrpc +Dan Libby diff --git a/ext/rpc/xmlrpc/EXPERIMENTAL b/ext/rpc/xmlrpc/EXPERIMENTAL new file mode 100644 index 0000000000..6443e99646 --- /dev/null +++ b/ext/rpc/xmlrpc/EXPERIMENTAL @@ -0,0 +1,5 @@ +this extension is experimental, +its functions may change their names +or move to extension all together +so do not rely to much on them +you have been warned! diff --git a/ext/rpc/xmlrpc/Makefile.in b/ext/rpc/xmlrpc/Makefile.in new file mode 100644 index 0000000000..a3ba38b901 --- /dev/null +++ b/ext/rpc/xmlrpc/Makefile.in @@ -0,0 +1,12 @@ +# $Id$ + +LTLIBRARY_NAME = libxmlrpc.la +LTLIBRARY_SOURCES = xmlrpc-epi-php.c +LTLIBRARY_SHARED_NAME = xmlrpc.la +EXTRA_INCLUDES = -I$(XMLRPC_INC_DIR) +LTLIBRARY_LIBADD = $(XMLRPC_LIBADD) +LTLIBRARY_SHARED_LIBADD = $(XMLRPC_SHARED_LIBADD) + +SUBDIRS = $(XMLRPC_SUBDIRS) + +include $(top_srcdir)/build/dynlib.mk diff --git a/ext/rpc/xmlrpc/config.m4 b/ext/rpc/xmlrpc/config.m4 new file mode 100644 index 0000000000..18809032db --- /dev/null +++ b/ext/rpc/xmlrpc/config.m4 @@ -0,0 +1,122 @@ + +dnl $Id$ + +sinclude(ext/xmlrpc/libxmlrpc/acinclude.m4) +sinclude(ext/xmlrpc/libxmlrpc/xmlrpc.m4) +sinclude(libxmlrpc/acinclude.m4) +sinclude(libxmlrpc/xmlrpc.m4) + +AC_DEFUN(XMLRPC_LIB_CHK,[ + str="$XMLRPC_DIR/$1/libxmlrpc.*" + for j in `echo $str`; do + if test -r $j; then + XMLRPC_LIB_DIR=$XMLRPC_DIR/$1 + break 2 + fi + done +]) + +PHP_ARG_WITH(xmlrpc, for XMLRPC-EPI support, +[ --with-xmlrpc[=DIR] Include XMLRPC-EPI support. DIR is the XMLRPC-EPI base + directory. If unspecified, the bundled XMLRPC-EPI library + will be used.], yes) + +PHP_ARG_WITH(expat-dir, libexpat dir for XMLRPC-EPI +[ --with-expat-dir=DIR XMLRPC-EPI: libexpat dir for XMLRPC-EPI]) + + +if test "$PHP_XMLRPC" != "no"; then + AC_DEFINE(HAVE_XMLRPC, 1, [Whether you have XMLRPC-EPI]) + PHP_EXTENSION(xmlrpc,$ext_shared) + +dnl check for iconv + found_iconv=no + AC_CHECK_LIB(c, iconv_open, found_iconv=yes) + if test "$found_iconv" = "no"; then + for i in /usr /usr/local $ICONV_DIR; do + if test -f $i/lib/libconv.a -o -f $i/lib/libiconv.s?; then + PHP_ADD_LIBRARY_WITH_PATH(iconv, $i/lib, XMLRPC_SHARED_LIBADD) + found_iconv=yes + fi + done + fi + + if test "$found_iconv" = "no"; then + AC_MSG_ERROR(iconv not found, in order to build XMLRPC-EPI you need the iconv library) + fi + +dnl check for expat + testval=no + for i in $PHP_EXPAT_DIR; do + if test -f $i/lib/libexpat.a -o -f $i/lib/libexpat.s?; then + AC_DEFINE(HAVE_LIBEXPAT2,1,[ ]) + PHP_ADD_LIBRARY_WITH_PATH(expat, $i/lib, XMLRPC_SHARED_LIBADD) + PHP_ADD_INCLUDE($i/include) + testval=yes + fi + done + + if test "$testval" = "no"; then + PHP_ADD_LIBRARY(xmlparse) + PHP_ADD_LIBRARY(xmltok) + fi + +fi + +if test "$PHP_XMLRPC" = "yes"; then + XMLRPC_CHECKS + XMLRPC_LIBADD=libxmlrpc/libxmlrpc.la + XMLRPC_SHARED_LIBADD=libxmlrpc/libxmlrpc.la + XMLRPC_SUBDIRS=libxmlrpc + PHP_SUBST(XMLRPC_LIBADD) + PHP_SUBST(XMLRPC_SUBDIRS) + LIB_BUILD($ext_builddir/libxmlrpc,$ext_shared,yes) + PHP_ADD_INCLUDE($ext_srcdir/libxmlrpc) + XMLRPC_MODULE_TYPE=builtin +elif test "$PHP_XMLRPC" != "no"; then + for i in $PHP_XMLRPC; do + if test -r $i/include/xmlrpc/xmlrpc.h; then + XMLRPC_DIR=$i + XMLRPC_INC_DIR=$i/include/xmlrpc + elif test -r $i/include/xmlrpc.h; then + XMLRPC_DIR=$i + XMLRPC_INC_DIR=$i/include + fi + done + + if test -z "$XMLRPC_DIR"; then + AC_MSG_ERROR(Cannot find header files under $PHP_XMLRPC) + fi + + XMLRPC_MODULE_TYPE=external + + for i in lib lib/xmlrpc; do + XMLRPC_LIB_CHK($i) + done + + if test -z "$XMLRPC_LIB_DIR"; then + AC_MSG_ERROR(Cannot find xmlrpc library under $XMLRPC_DIR) + fi + + if test "$PHP_ZLIB_DIR" != "no"; then + PHP_ADD_LIBRARY(z,, XMLRPC_SHARED_LIBADD) + XMLRPC_LIBS="-L$PHP_ZLIB_DIR -z" + fi + + PHP_ADD_LIBRARY_WITH_PATH(xmlrpc, $XMLRPC_LIB_DIR, XMLRPC_SHARED_LIBADD) + XMLRPC_LIBS="-L$XMLRPC_LIB_DIR -lxmlrpc $XMLRPC_LIBS" + + PHP_ADD_INCLUDE($XMLRPC_INC_DIR) + XMLRPC_INCLUDE="-I$XMLRPC_INC_DIR" + +else + XMLRPC_MODULE_TYPE=none +fi + + +PHP_SUBST(XMLRPC_SHARED_LIBADD) +PHP_SUBST_OLD(XMLRPC_MODULE_TYPE) +PHP_SUBST_OLD(XMLRPC_LIBS) +PHP_SUBST_OLD(XMLRPC_INCLUDE) + + diff --git a/ext/rpc/xmlrpc/libs.mk b/ext/rpc/xmlrpc/libs.mk new file mode 100644 index 0000000000..7c6e176717 --- /dev/null +++ b/ext/rpc/xmlrpc/libs.mk @@ -0,0 +1,7 @@ +include $(top_builddir)/config_vars.mk +LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X) +LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo) +$(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) + $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD) + +targets = $(LTLIBRARY_NAME) diff --git a/ext/rpc/xmlrpc/libxmlrpc/Makefile.in b/ext/rpc/xmlrpc/libxmlrpc/Makefile.in new file mode 100644 index 0000000000..44257c4882 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/Makefile.in @@ -0,0 +1,9 @@ + +LTLIBRARY_NAME = libxmlrpc.la +LTLIBRARY_SOURCES = base64.c simplestring.c xml_to_dandarpc.c \ + xmlrpc_introspection.c encodings.c system_methods.c \ + xml_to_xmlrpc.c queue.c xml_element.c xmlrpc.c + +DEFS = -DVERSION="0.41" + +include $(top_srcdir)/build/dynlib.mk diff --git a/ext/rpc/xmlrpc/libxmlrpc/README b/ext/rpc/xmlrpc/libxmlrpc/README new file mode 100644 index 0000000000..323edfa671 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/README @@ -0,0 +1,17 @@ +organization of this directory is moving towards this approach: + +<module>.h -- public API and data types +<module>_private.h -- protected API and data types +<module>.c -- implementation and private API / types + +The rules are: +.c files may include *_private.h. +.h files may not include *_private.h + +This allows us to have a nicely encapsulated C api with opaque data types and private functions +that are nonetheless shared between source files without redundant extern declarations.. + + + + + diff --git a/ext/rpc/xmlrpc/libxmlrpc/acinclude.m4 b/ext/rpc/xmlrpc/libxmlrpc/acinclude.m4 new file mode 100644 index 0000000000..9535e45972 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/acinclude.m4 @@ -0,0 +1,38 @@ +# Local macros for automake & autoconf + +AC_DEFUN(XMLRPC_FUNCTION_CHECKS,[ + +# Standard XMLRPC list +AC_CHECK_FUNCS( \ + strtoul strtoull snprintf \ + strstr strpbrk strerror\ + memcpy memmove) + +]) + +AC_DEFUN(XMLRPC_HEADER_CHECKS,[ +AC_HEADER_STDC +AC_CHECK_HEADERS(xmlparse.h xmltok.h stdlib.h strings.h string.h) +]) + +AC_DEFUN(XMLRPC_TYPE_CHECKS,[ + +AC_REQUIRE([AC_C_CONST]) +AC_REQUIRE([AC_C_INLINE]) +AC_CHECK_SIZEOF(char, 1) + +AC_CHECK_SIZEOF(int, 4) +AC_CHECK_SIZEOF(long, 4) +AC_CHECK_SIZEOF(long long, 8) +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_TYPE_UID_T + +XMLRPC_CHECK_ULONG +XMLRPC_CHECK_UCHAR +XMLRPC_CHECK_UINT +XMLRPC_CHECK_INT_8_16_32 + +XMLRPC_TYPE_QSORT + +]) diff --git a/ext/rpc/xmlrpc/libxmlrpc/base64.c b/ext/rpc/xmlrpc/libxmlrpc/base64.c new file mode 100644 index 0000000000..bdbf95bcba --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/base64.c @@ -0,0 +1,192 @@ +static const char rcsid[] = "#(@) $Id$"; + +/* + + Encode or decode file as MIME base64 (RFC 1341) + + by John Walker + http://www.fourmilab.ch/ + + This program is in the public domain. + +*/ +#include <stdio.h> + +/* ENCODE -- Encode binary file into base64. */ +#include <stdlib.h> + +#include "base64.h" + +static unsigned char dtable[512]; + +void buffer_new(struct buffer_st *b) +{ + b->length = 512; + b->data = malloc(sizeof(char)*(b->length)); + b->data[0] = 0; + b->ptr = b->data; + b->offset = 0; +} + +void buffer_add(struct buffer_st *b, char c) +{ + *(b->ptr++) = c; + b->offset++; + if (b->offset == b->length) { + b->length += 512; + b->data = realloc(b->data, b->length); + b->ptr = b->data + b->offset; + } +} + +void buffer_delete(struct buffer_st *b) +{ + free(b->data); + b->length = 0; + b->offset = 0; + b->ptr = NULL; + b->data = NULL; +} + +void base64_encode(struct buffer_st *b, const char *source, int length) +{ + int i, hiteof = 0; + int offset = 0; + int olen; + char *dest; + + olen = 0; + + buffer_new(b); + + /* Fill dtable with character encodings. */ + + for (i = 0; i < 26; i++) { + dtable[i] = 'A' + i; + dtable[26 + i] = 'a' + i; + } + for (i = 0; i < 10; i++) { + dtable[52 + i] = '0' + i; + } + dtable[62] = '+'; + dtable[63] = '/'; + + while (!hiteof) { + unsigned char igroup[3], ogroup[4]; + int c, n; + + igroup[0] = igroup[1] = igroup[2] = 0; + for (n = 0; n < 3; n++) { + c = *(source++); + offset++; + if (offset > length) { + hiteof = 1; + break; + } + igroup[n] = (unsigned char) c; + } + if (n > 0) { + ogroup[0] = dtable[igroup[0] >> 2]; + ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)]; + ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)]; + ogroup[3] = dtable[igroup[2] & 0x3F]; + + /* Replace characters in output stream with "=" pad + characters if fewer than three characters were + read from the end of the input stream. */ + + if (n < 3) { + ogroup[3] = '='; + if (n < 2) { + ogroup[2] = '='; + } + } + for (i = 0; i < 4; i++) { + buffer_add(b, ogroup[i]); + if (!(b->offset % 72)) { + // buffer_add(b, '\r'); + buffer_add(b, '\n'); + } + } + } + } + // buffer_add(b, '\r'); + buffer_add(b, '\n'); +} + +void base64_decode(struct buffer_st *bfr, const char *source, int length) +{ + int i; + int offset = 0; + int endoffile; + int count; + + buffer_new(bfr); + + for (i = 0; i < 255; i++) { + dtable[i] = 0x80; + } + for (i = 'A'; i <= 'Z'; i++) { + dtable[i] = 0 + (i - 'A'); + } + for (i = 'a'; i <= 'z'; i++) { + dtable[i] = 26 + (i - 'a'); + } + for (i = '0'; i <= '9'; i++) { + dtable[i] = 52 + (i - '0'); + } + dtable['+'] = 62; + dtable['/'] = 63; + dtable['='] = 0; + + endoffile = 0; + + /*CONSTANTCONDITION*/ + while (1) { + unsigned char a[4], b[4], o[3]; + + for (i = 0; i < 4; i++) { + int c; + while (1) { + c = *(source++); + offset++; + if (offset > length) endoffile = 1; + if (isspace(c) || c == '\n' || c == '\r') continue; + break; + } + + if (endoffile) { + /* + if (i > 0) { + fprintf(stderr, "Input file incomplete.\n"); + exit(1); + } + */ + return; + } + + if (dtable[c] & 0x80) { + /* + fprintf(stderr, "Offset %i length %i\n", offset, length); + fprintf(stderr, "character '%c:%x:%c' in input file.\n", c, c, dtable[c]); + exit(1); + */ + i--; + continue; + } + a[i] = (unsigned char) c; + b[i] = (unsigned char) dtable[c]; + } + o[0] = (b[0] << 2) | (b[1] >> 4); + o[1] = (b[1] << 4) | (b[2] >> 2); + o[2] = (b[2] << 6) | b[3]; + i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3); + count = 0; + while (count < i) { + buffer_add(bfr, o[count++]); + } + if (i < 3) { + return; + } + } +} diff --git a/ext/rpc/xmlrpc/libxmlrpc/base64.h b/ext/rpc/xmlrpc/libxmlrpc/base64.h new file mode 100644 index 0000000000..4cf156ad1e --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/base64.h @@ -0,0 +1,38 @@ +/* + + Encode or decode file as MIME base64 (RFC 1341) + + by John Walker + http://www.fourmilab.ch/ + + This program is in the public domain. + +*/ + + +struct buffer_st { + char *data; + int length; + char *ptr; + int offset; +}; + +void buffer_new(struct buffer_st *b); +void buffer_add(struct buffer_st *b, char c); +void buffer_delete(struct buffer_st *b); + +void base64_encode(struct buffer_st *b, const char *source, int length); +void base64_decode(struct buffer_st *b, const char *source, int length); + +/* +#define DEBUG_MALLOC + */ + +#ifdef DEBUG_MALLOC +void *_malloc_real(size_t s, char *file, int line); +void _free_real(void *p, char *file, int line); + +#define malloc(s) _malloc_real(s,__FILE__,__LINE__) +#define free(p) _free_real(p, __FILE__,__LINE__) +#endif + diff --git a/ext/rpc/xmlrpc/libxmlrpc/encodings.c b/ext/rpc/xmlrpc/libxmlrpc/encodings.c new file mode 100644 index 0000000000..544cfa2559 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/encodings.c @@ -0,0 +1,103 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +static const char rcsid[] = "#(@) $Id$"; + +#include <errno.h> +#include <iconv.h> +#include "encodings.h" + +static char* convert(const char* src, int src_len, int *new_len, const char* from_enc, const char* to_enc) { + char* outbuf = 0; + + if(src && src_len && from_enc && to_enc) { + int outlenleft = src_len; + int outlen = src_len; + int inlenleft = src_len; + iconv_t ic = iconv_open(to_enc, from_enc); + char* src_ptr = (char*)src; + char* out_ptr = 0; + + if(ic != (iconv_t)-1) { + size_t st; + outbuf = (char*)malloc(outlen + 1); + + if(outbuf) { + out_ptr = (char*)outbuf; + while(inlenleft) { + st = iconv(ic, &src_ptr, &inlenleft, &out_ptr, &outlenleft); + if(st == -1) { + if(errno == E2BIG) { + int diff = out_ptr - outbuf; + outlen += inlenleft; + outlenleft += inlenleft; + outbuf = (char*)realloc(outbuf, outlen + 1); + if(!outbuf) { + break; + } + out_ptr = outbuf + diff; + } + else { + free(outbuf); + outbuf = 0; + break; + } + } + } + } + iconv_close(ic); + } + outlen -= outlenleft; + + if(new_len) { + *new_len = outbuf ? outlen : 0; + } + if(outbuf) { + outbuf[outlen] = 0; + } + } + return outbuf; +} + +/* returns a new string that must be freed */ +char* utf8_encode(const char *s, int len, int *newlen, const char* encoding) +{ + return convert(s, len, newlen, encoding, "UTF-8"); +} + +/* returns a new string, possibly decoded */ +char* utf8_decode(const char *s, int len, int *newlen, const char* encoding) +{ + return convert(s, len, newlen, "UTF-8", encoding); +} + diff --git a/ext/rpc/xmlrpc/libxmlrpc/encodings.h b/ext/rpc/xmlrpc/libxmlrpc/encodings.h new file mode 100644 index 0000000000..486360b1be --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/encodings.h @@ -0,0 +1,46 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +#ifndef __ENCODINGS__H +#define __ENCODINGS__H + +/* these defines are for legacy purposes. */ +#define encoding_utf_8 "UTF-8" +typedef const char* ENCODING_ID; +#define utf8_get_encoding_id_string(desired_enc) ((const char*)desired_enc) +#define utf8_get_encoding_id_from_string(id_string) ((ENCODING_ID)id_string) + +char* utf8_encode(const char *s, int len, int *newlen, ENCODING_ID encoding); +char* utf8_decode(const char *s, int len, int *newlen, ENCODING_ID encoding); + +#endif /* __ENCODINGS__H */ diff --git a/ext/rpc/xmlrpc/libxmlrpc/libs.mk b/ext/rpc/xmlrpc/libxmlrpc/libs.mk new file mode 100644 index 0000000000..7c6e176717 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/libs.mk @@ -0,0 +1,7 @@ +include $(top_builddir)/config_vars.mk +LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X) +LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo) +$(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) + $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD) + +targets = $(LTLIBRARY_NAME) diff --git a/ext/rpc/xmlrpc/libxmlrpc/queue.c b/ext/rpc/xmlrpc/libxmlrpc/queue.c new file mode 100644 index 0000000000..25d62b96a7 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/queue.c @@ -0,0 +1,979 @@ +static const char rcsid[] = "#(@) $Id$"; + +/* + * Date last modified: Jan 2001 + * Modifications by Dan Libby (dan@libby.com), including: + * - various fixes, null checks, etc + * - addition of Q_Iter funcs, macros + */ + + +/*-************************************************************** + * + * File : q.c + * + * Author: Peter Yard [1993.01.02] -- 02 Jan 1993 + * + * Disclaimer: This code is released to the public domain. + * + * Description: + * Generic double ended queue (Deque pronounced DEK) for handling + * any data types, with sorting. + * + * By use of various functions in this module the caller + * can create stacks, queues, lists, doubly linked lists, + * sorted lists, indexed lists. All lists are dynamic. + * + * It is the responsibility of the caller to malloc and free + * memory for insertion into the queue. A pointer to the object + * is used so that not only can any data be used but various kinds + * of data can be pushed on the same queue if one so wished e.g. + * various length string literals mixed with pointers to structures + * or integers etc. + * + * Enhancements: + * A future improvement would be the option of multiple "cursors" + * so that multiple locations could occur in the one queue to allow + * placemarkers and additional flexibility. Perhaps even use queue + * itself to have a list of cursors. + * + * Usage: + * + * /x init queue x/ + * queue q; + * Q_Init(&q); + * + * To create a stack : + * + * Q_PushHead(&q, &mydata1); /x push x/ + * Q_PushHead(&q, &mydata2); + * ..... + * data_ptr = Q_PopHead(&q); /x pop x/ + * ..... + * data_ptr = Q_Head(&q); /x top of stack x/ + * + * To create a FIFO: + * + * Q_PushHead(&q, &mydata1); + * ..... + * data_ptr = Q_PopTail(&q); + * + * To create a double list: + * + * data_ptr = Q_Head(&q); + * .... + * data_ptr = Q_Next(&q); + * data_ptr = Q_Tail(&q); + * if (Q_IsEmpty(&q)) .... + * ..... + * data_ptr = Q_Previous(&q); + * + * To create a sorted list: + * + * Q_PushHead(&q, &mydata1); /x push x/ + * Q_PushHead(&q, &mydata2); + * ..... + * if (!Q_Sort(&q, MyFunction)) + * .. error .. + * + * /x fill in key field of mydata1. + * * NB: Q_Find does linear search + * x/ + * + * if (Q_Find(&q, &mydata1, MyFunction)) + * { + * /x found it, queue cursor now at correct record x/ + * /x can retrieve with x/ + * data_ptr = Q_Get(&q); + * + * /x alter data , write back with x/ + * Q_Put(&q, data_ptr); + * } + * + * /x Search with binary search x/ + * if (Q_Seek(&q, &mydata, MyFunction)) + * /x etc x/ + * + * + ****************************************************************/ + + +#include <stdlib.h> +#include "queue.h" + + +static void QuickSort(void *list[], int low, int high, + int (*Comp)(const void *, const void *)); +static int Q_BSearch(queue *q, void *key, + int (*Comp)(const void *, const void *)); + +/* The index: a pointer to pointers */ + +static void **index; +static datanode **posn_index; + + +/*** + * + ** function : Q_Init + * + ** purpose : Initialise queue object and pointers. + * + ** parameters : 'queue' pointer. + * + ** returns : True_ if init successful else False_ + * + ** comments : + ***/ + +int Q_Init(queue *q) +{ + if(q) { + q->head = q->tail = NULL; + q->cursor = q->head; + q->size = 0; + q->sorted = False_; + } + + return True_; +} + +/*** + * + ** function : Q_AtHead + * + ** purpose : tests if cursor is at head of queue + * + ** parameters : 'queue' pointer. + * + ** returns : boolean - True_ is at head else False_ + * + ** comments : + * + ***/ + +int Q_AtHead(queue *q) +{ + return(q && q->cursor == q->head); +} + + +/*** + * + ** function : Q_AtTail + * + ** purpose : boolean test if cursor at tail of queue + * + ** parameters : 'queue' pointer to test. + * + ** returns : True_ or False_ + * + ** comments : + * + ***/ + +int Q_AtTail(queue *q) +{ + return(q && q->cursor == q->tail); +} + + +/*** + * + ** function : Q_IsEmpty + * + ** purpose : test if queue has nothing in it. + * + ** parameters : 'queue' pointer + * + ** returns : True_ if IsEmpty queue, else False_ + * + ** comments : + * + ***/ + +inline int Q_IsEmpty(queue *q) +{ + return(!q || q->size == 0); +} + +/*** + * + ** function : Q_Size + * + ** purpose : return the number of elements in the queue + * + ** parameters : queue pointer + * + ** returns : number of elements + * + ** comments : + * + ***/ + +int Q_Size(queue *q) +{ + return q ? q->size : 0; +} + + +/*** + * + ** function : Q_Head + * + ** purpose : position queue cursor to first element (head) of queue. + * + ** parameters : 'queue' pointer + * + ** returns : pointer to data at head. If queue is IsEmpty returns NULL + * + ** comments : + * + ***/ + +void *Q_Head(queue *q) +{ + if(Q_IsEmpty(q)) + return NULL; + + q->cursor = q->head; + + return q->cursor->data; +} + + +/*** + * + ** function : Q_Tail + * + ** purpose : locate cursor at tail of queue. + * + ** parameters : 'queue' pointer + * + ** returns : pointer to data at tail , if queue IsEmpty returns NULL + * + ** comments : + * + ***/ + +void *Q_Tail(queue *q) +{ + if(Q_IsEmpty(q)) + return NULL; + + q->cursor = q->tail; + + return q->cursor->data; +} + + +/*** + * + ** function : Q_PushHead + * + ** purpose : put a data pointer at the head of the queue + * + ** parameters : 'queue' pointer, void pointer to the data. + * + ** returns : True_ if success else False_ if unable to push data. + * + ** comments : + * + ***/ + +int Q_PushHead(queue *q, void *d) +{ + if(q && d) { + node *n; + datanode *p; + + p = malloc(sizeof(datanode)); + if(p == NULL) + return False_; + + n = q->head; + + q->head = (node*)p; + q->head->prev = NULL; + + if(q->size == 0) { + q->head->next = NULL; + q->tail = q->head; + } + else { + q->head->next = (datanode*)n; + n->prev = q->head; + } + + q->head->data = d; + q->size++; + + q->cursor = q->head; + + q->sorted = False_; + + return True_; + } + return False_; +} + + + +/*** + * + ** function : Q_PushTail + * + ** purpose : put a data element pointer at the tail of the queue + * + ** parameters : queue pointer, pointer to the data + * + ** returns : True_ if data pushed, False_ if data not inserted. + * + ** comments : + * + ***/ + +int Q_PushTail(queue *q, void *d) +{ + if(q && d) { + node *p; + datanode *n; + + n = malloc(sizeof(datanode)); + if(n == NULL) + return False_; + + p = q->tail; + q->tail = (node *)n; + + if(q->size == 0) { + q->tail->prev = NULL; + q->head = q->tail; + } + else { + q->tail->prev = (datanode *)p; + p->next = q->tail; + } + + q->tail->next = NULL; + + q->tail->data = d; + q->cursor = q->tail; + q->size++; + + q->sorted = False_; + + return True_; + } +} + + + +/*** + * + ** function : Q_PopHead + * + ** purpose : remove and return the top element at the head of the + * queue. + * + ** parameters : queue pointer + * + ** returns : pointer to data element or NULL if queue is IsEmpty. + * + ** comments : + * + ***/ + +void *Q_PopHead(queue *q) +{ + datanode *n; + void *d; + + if(Q_IsEmpty(q)) + return NULL; + + d = q->head->data; + n = q->head->next; + free(q->head); + + q->size--; + + if(q->size == 0) + q->head = q->tail = q->cursor = NULL; + else { + q->head = (node *)n; + q->head->prev = NULL; + q->cursor = q->head; + } + + q->sorted = False_; + + return d; +} + + +/*** + * + ** function : Q_PopTail + * + ** purpose : remove element from tail of queue and return data. + * + ** parameters : queue pointer + * + ** returns : pointer to data element that was at tail. NULL if queue + * IsEmpty. + * + ** comments : + * + ***/ + +void *Q_PopTail(queue *q) +{ + datanode *p; + void *d; + + if(Q_IsEmpty(q)) + return NULL; + + d = q->tail->data; + p = q->tail->prev; + free(q->tail); + q->size--; + + if(q->size == 0) + q->head = q->tail = q->cursor = NULL; + else { + q->tail = (node *)p; + q->tail->next = NULL; + q->cursor = q->tail; + } + + q->sorted = False_; + + return d; +} + + + +/*** + * + ** function : Q_Next + * + ** purpose : Move to the next element in the queue without popping + * + ** parameters : queue pointer. + * + ** returns : pointer to data element of new element or NULL if end + * of the queue. + * + ** comments : This uses the cursor for the current position. Q_Next + * only moves in the direction from the head of the queue + * to the tail. + ***/ + +void *Q_Next(queue *q) +{ + if(!q) + return NULL; + + if(q->cursor->next == NULL) + return NULL; + + q->cursor = (node *)q->cursor->next; + + return q->cursor->data ; +} + + + +/*** + * + ** function : Q_Previous + * + ** purpose : Opposite of Q_Next. Move to next element closer to the + * head of the queue. + * + ** parameters : pointer to queue + * + ** returns : pointer to data of new element else NULL if queue IsEmpty + * + ** comments : Makes cursor move towards the head of the queue. + * + ***/ + +void *Q_Previous(queue *q) +{ + if(!q) + return NULL; + + if(q->cursor->prev == NULL) + return NULL; + + q->cursor = (node *)q->cursor->prev; + + return q->cursor->data; +} + + +void *Q_Iter_Del(queue *q, q_iter iter) +{ + void *d; + datanode *n, *p; + + if(!q) + return NULL; + + if(iter == NULL) + return NULL; + + if(iter == (q_iter)q->head) + return Q_PopHead(q); + + if(iter == (q_iter)q->tail) + return Q_PopTail(q); + + n = ((node*)iter)->next; + p = ((node*)iter)->prev; + d = ((node*)iter)->data; + + free(iter); + + if(p) { + p->next = n; + } + if (q->cursor == (node*)iter) { + if (p) { + q->cursor = p; + } else { + q->cursor = n; + } + } + + + if (n != NULL) { + n->prev = p; + } + + q->size--; + + q->sorted = False_; + + return d; +} + + + +/*** + * + ** function : Q_DelCur + * + ** purpose : Delete the current queue element as pointed to by + * the cursor. + * + ** parameters : queue pointer + * + ** returns : pointer to data element. + * + ** comments : WARNING! It is the responsibility of the caller to + * free any memory. Queue cannot distinguish between + * pointers to literals and malloced memory. + * + ***/ + +void *Q_DelCur(queue* q) { + if(q) { + return Q_Iter_Del(q, (q_iter)q->cursor); + } + return 0; +} + + +/*** + * + ** function : Q_Destroy + * + ** purpose : Free all queue resources + * + ** parameters : queue pointer + * + ** returns : null. + * + ** comments : WARNING! It is the responsibility of the caller to + * free any memory. Queue cannot distinguish between + * pointers to literals and malloced memory. + * + ***/ + +void Q_Destroy(queue *q) +{ + while(!Q_IsEmpty(q)) { + Q_PopHead(q); + } +} + + +/*** + * + ** function : Q_Get + * + ** purpose : get the pointer to the data at the cursor location + * + ** parameters : queue pointer + * + ** returns : data element pointer + * + ** comments : + * + ***/ + +void *Q_Get(queue *q) +{ + if(!q) + return NULL; + + if(q->cursor == NULL) + return NULL; + return q->cursor->data; +} + + + +/*** + * + ** function : Q_Put + * + ** purpose : replace pointer to data with new pointer to data. + * + ** parameters : queue pointer, data pointer + * + ** returns : boolean- True_ if successful, False_ if cursor at NULL + * + ** comments : + * + ***/ + +int Q_Put(queue *q, void *data) +{ + if(q && data) { + if(q->cursor == NULL) + return False_; + + q->cursor->data = data; + return True_; + } + return False_; +} + + +/*** + * + ** function : Q_Find + * + ** purpose : Linear search of queue for match with key in *data + * + ** parameters : queue pointer q, data pointer with data containing key + * comparison function here called Comp. + * + ** returns : True_ if found , False_ if not in queue. + * + ** comments : Useful for small queues that are constantly changing + * and would otherwise need constant sorting with the + * Q_Seek function. + * For description of Comp see Q_Sort. + * Queue cursor left on position found item else at end. + * + ***/ + +int Q_Find(queue *q, void *data, + int (*Comp)(const void *, const void *)) +{ + void *d; + + if (q == NULL) { + return False_; + } + + d = Q_Head(q); + do { + if(Comp(d, data) == 0) + return True_; + d = Q_Next(q); + } while(!Q_AtTail(q)); + + if(Comp(d, data) == 0) + return True_; + + return False_; +} + +/*======== Sorted Queue and Index functions ========= */ + + +static void QuickSort(void *list[], int low, int high, + int (*Comp)(const void *, const void *)) +{ + int flag = 1, i, j; + void *key, *temp; + + if(low < high) { + i = low; + j = high + 1; + + key = list[ low ]; + + while(flag) { + i++; + while(Comp(list[i], key) < 0) + i++; + + j--; + while(Comp(list[j], key) > 0) + j--; + + if(i < j) { + temp = list[i]; + list[i] = list[j]; + list[j] = temp; + } + else flag = 0; + } + + temp = list[low]; + list[low] = list[j]; + list[j] = temp; + + QuickSort(list, low, j-1, Comp); + QuickSort(list, j+1, high, Comp); + } +} + + +/*** + * + ** function : Q_Sort + * + ** purpose : sort the queue and allow index style access. + * + ** parameters : queue pointer, comparison function compatible with + * with 'qsort'. + * + ** returns : True_ if sort succeeded. False_ if error occurred. + * + ** comments : Comp function supplied by caller must return + * -1 if data1 < data2 + * 0 if data1 == data2 + * +1 if data1 > data2 + * + * for Comp(data1, data2) + * + * If queue is already sorted it frees the memory of the + * old index and starts again. + * + ***/ + +int Q_Sort(queue *q, int (*Comp)(const void *, const void *)) +{ + int i; + void *d; + datanode *dn; + + /* if already sorted free memory for tag array */ + + if(q->sorted) { + free(index); + free(posn_index); + q->sorted = False_; + } + + /* Now allocate memory of array, array of pointers */ + + index = malloc(q->size * sizeof(q->cursor->data)); + if(index == NULL) + return False_; + + posn_index = malloc(q->size * sizeof(q->cursor)); + if(posn_index == NULL) { + free(index); + return False_; + } + + /* Walk queue putting pointers into array */ + + d = Q_Head(q); + for(i=0; i < q->size; i++) { + index[i] = d; + posn_index[i] = q->cursor; + d = Q_Next(q); + } + + /* Now sort the index */ + + QuickSort(index, 0, q->size - 1, Comp); + + /* Rearrange the actual queue into correct order */ + + dn = q->head; + i = 0; + while(dn != NULL) { + dn->data = index[i++]; + dn = dn->next; + } + + /* Re-position to original element */ + + if(d != NULL) + Q_Find(q, d, Comp); + else Q_Head(q); + + q->sorted = True_; + + return True_; +} + + +/*** + * + ** function : Q_BSearch + * + ** purpose : binary search of queue index for node containing key + * + ** parameters : queue pointer 'q', data pointer of key 'key', + * Comp comparison function. + * + ** returns : integer index into array of node pointers, + * or -1 if not found. + * + ** comments : see Q_Sort for description of 'Comp' function. + * + ***/ + +static int Q_BSearch( queue *q, void *key, + int (*Comp)(const void *, const void*)) +{ + int low, mid, hi, val; + + low = 0; + hi = q->size - 1; + + while(low <= hi) { + mid = (low + hi) / 2; + val = Comp(key, index[ mid ]); + + if(val < 0) + hi = mid - 1; + + else if(val > 0) + low = mid + 1; + + else /* Success */ + return mid; + } + + /* Not Found */ + + return -1; +} + + +/*** + * + ** function : Q_Seek + * + ** purpose : use index to locate data according to key in 'data' + * + ** parameters : queue pointer 'q', data pointer 'data', Comp comparison + * function. + * + ** returns : pointer to data or NULL if could not find it or could + * not sort queue. + * + ** comments : see Q_Sort for description of 'Comp' function. + * + ***/ + +void *Q_Seek(queue *q, void *data, int (*Comp)(const void *, const void *)) +{ + int idx; + + if (q == NULL) { + return NULL; + } + + if(!q->sorted) { + if(!Q_Sort(q, Comp)) + return NULL; + } + + idx = Q_BSearch(q, data, Comp); + + if(idx < 0) + return NULL; + + q->cursor = posn_index[idx]; + + return index[idx]; +} + + + +/*** + * + ** function : Q_Insert + * + ** purpose : Insert an element into an indexed queue + * + ** parameters : queue pointer 'q', data pointer 'data', Comp comparison + * function. + * + ** returns : pointer to data or NULL if could not find it or could + * not sort queue. + * + ** comments : see Q_Sort for description of 'Comp' function. + * WARNING! This code can be very slow since each new + * element means a new Q_Sort. Should only be used for + * the insertion of the odd element ,not the piecemeal + * building of an entire queue. + ***/ + +int Q_Insert(queue *q, void *data, int (*Comp)(const void *, const void *)) +{ + if (q == NULL) { + return False_; + } + + Q_PushHead(q, data); + + if(!Q_Sort(q, Comp)) + return False_; + + return True_; +} + +/* read only funcs for iterating through queue. above funcs modify queue */ +q_iter Q_Iter_Head(queue *q) { + return q ? (q_iter)q->head : NULL; +} + +q_iter Q_Iter_Tail(queue *q) { + return q ? (q_iter)q->tail : NULL; +} + +q_iter Q_Iter_Next(q_iter qi) { + return qi ? (q_iter)((node*)qi)->next : NULL; +} + +q_iter Q_Iter_Prev(q_iter qi) { + return qi ? (q_iter)((node*)qi)->prev : NULL; +} + +void * Q_Iter_Get(q_iter qi) { + return qi ? ((node*)qi)->data : NULL; +} + +int Q_Iter_Put(q_iter qi, void* data) { + if(qi) { + ((node*)qi)->data = data; + return True_; + } + return False_; +} diff --git a/ext/rpc/xmlrpc/libxmlrpc/queue.h b/ext/rpc/xmlrpc/libxmlrpc/queue.h new file mode 100644 index 0000000000..e850b57e6c --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/queue.h @@ -0,0 +1,89 @@ +/* + * Date last modified: Jan 2001 + * Modifications by Dan Libby (dan@libby.com), including: + * - various fixes, null checks, etc + * - addition of Q_Iter funcs, macros + */ + +/* + * File : q.h + * + * Peter Yard 02 Jan 1993. + * + * Disclaimer: This code is released to the public domain. + */ + +#ifndef Q__H +#define Q__H + +#ifndef False_ + #define False_ 0 +#endif + +#ifndef True_ + #define True_ 1 +#endif + +typedef struct nodeptr datanode; + +typedef struct nodeptr { + void *data ; + datanode *prev, *next ; +} node ; + +/* For external use with Q_Iter* funcs */ +typedef struct nodeptr* q_iter; + +typedef struct { + node *head, *tail, *cursor; + int size, sorted, item_deleted; +} queue; + +typedef struct { + void *dataptr; + node *loc ; +} index_elt ; + + +int Q_Init(queue *q); +void Q_Destroy(queue *q); +int Q_IsEmpty(queue *q); +int Q_Size(queue *q); +int Q_AtHead(queue *q); +int Q_AtTail(queue *q); +int Q_PushHead(queue *q, void *d); +int Q_PushTail(queue *q, void *d); +void *Q_Head(queue *q); +void *Q_Tail(queue *q); +void *Q_PopHead(queue *q); +void *Q_PopTail(queue *q); +void *Q_Next(queue *q); +void *Q_Previous(queue *q); +void *Q_DelCur(queue *q); +void *Q_Get(queue *q); +int Q_Put(queue *q, void *data); +int Q_Sort(queue *q, int (*Comp)(const void *, const void *)); +int Q_Find(queue *q, void *data, + int (*Comp)(const void *, const void *)); +void *Q_Seek(queue *q, void *data, + int (*Comp)(const void *, const void *)); +int Q_Insert(queue *q, void *data, + int (*Comp)(const void *, const void *)); + +/* read only funcs for iterating through queue. above funcs modify queue */ +q_iter Q_Iter_Head(queue *q); +q_iter Q_Iter_Tail(queue *q); +q_iter Q_Iter_Next(q_iter qi); +q_iter Q_Iter_Prev(q_iter qi); +void* Q_Iter_Get(q_iter qi); +int Q_Iter_Put(q_iter qi, void* data); // not read only! here for completeness. +void* Q_Iter_Del(queue *q, q_iter iter); // not read only! here for completeness. + +/* Fast (macro'd) versions of above */ +#define Q_Iter_Head_F(q) (q ? (q_iter)((queue*)q)->head : NULL) +#define Q_Iter_Tail_F(q) (q ? (q_iter)((queue*)q)->tail : NULL) +#define Q_Iter_Next_F(qi) (qi ? (q_iter)((node*)qi)->next : NULL) +#define Q_Iter_Prev_F(qi) (qi ? (q_iter)((node*)qi)->prev : NULL) +#define Q_Iter_Get_F(qi) (qi ? ((node*)qi)->data : NULL) + +#endif /* Q__H */ diff --git a/ext/rpc/xmlrpc/libxmlrpc/simplestring.c b/ext/rpc/xmlrpc/libxmlrpc/simplestring.c new file mode 100644 index 0000000000..7ce68d3410 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/simplestring.c @@ -0,0 +1,234 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +static const char rcsid[] = "#(@) $Id$"; + + +#define SIMPLESTRING_INCR 32 + +/****h* ABOUT/simplestring + * NAME + * simplestring + * AUTHOR + * Dan Libby, aka danda (dan@libby.com) + * CREATION DATE + * 06/2000 + * HISTORY + * 10/15/2000 -- danda -- adding robodoc documentation + * PORTABILITY + * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just + * about anything with minor mods. + * NOTES + * This code was written primarily for xmlrpc, but has found some other uses. + * + * simplestring is, as the name implies, a simple API for dealing with C strings. + * Why would I write yet another string API? Because I couldn't find any that were + * a) free / GPL, b) simple/lightweight, c) fast, not doing unneccesary strlens all + * over the place. So. It is simple, and it seems to work, and it is pretty fast. + * + * Oh, and it is also binary safe, ie it can handle strings with embedded NULLs, + * so long as the real length is passed in. + * + * And the masses rejoiced. + * + * BUGS + * there must be some. + ******/ + + +#include "simplestring.h" + +#define my_free(thing) if(thing) {free(thing); thing = 0;} + +/*----------------------** +* Begin String Functions * +*-----------------------*/ + +/****f* FUNC/simplestring_init + * NAME + * simplestring_init + * SYNOPSIS + * void simplestring_init(simplestring* string) + * FUNCTION + * initialize string + * INPUTS + * string - pointer to a simplestring struct that will be initialized + * RESULT + * void + * NOTES + * SEE ALSO + * simplestring_free () + * simplestring_clear () + * SOURCE + */ +void simplestring_init(simplestring* string) { + memset(string, 0, sizeof(simplestring)); +} +/******/ + +static void simplestring_init_str(simplestring* string) { + string->str = (char*)malloc(SIMPLESTRING_INCR); + if(string->str) { + string->str[0] = 0; + string->len = 0; + string->size = SIMPLESTRING_INCR; + } + else { + string->size = 0; + } +} + +/****f* FUNC/simplestring_clear + * NAME + * simplestring_clear + * SYNOPSIS + * void simplestring_clear(simplestring* string) + * FUNCTION + * clears contents of a string + * INPUTS + * string - the string value to clear + * RESULT + * void + * NOTES + * This function is very fast as it does not de-allocate any memory. + * SEE ALSO + * + * SOURCE + */ +void simplestring_clear(simplestring* string) { + if(string->str) { + string->str[0] = 0; + } + string->len = 0; +} +/******/ + +/****f* FUNC/simplestring_free + * NAME + * simplestring_free + * SYNOPSIS + * void simplestring_free(simplestring* string) + * FUNCTION + * frees contents of a string, if any. Does *not* free the simplestring struct itself. + * INPUTS + * string - value containing string to be free'd + * RESULT + * void + * NOTES + * caller is responsible for allocating and freeing simplestring* struct itself. + * SEE ALSO + * simplestring_init () + * SOURCE + */ +void simplestring_free(simplestring* string) { + if(string && string->str) { + my_free(string->str); + string->len = 0; + } +} +/******/ + +/****f* FUNC/simplestring_addn + * NAME + * simplestring_addn + * SYNOPSIS + * void simplestring_addn(simplestring* string, const char* add, int add_len) + * FUNCTION + * copies n characters from source to target string + * INPUTS + * target - target string + * source - source string + * add_len - number of characters to copy + * RESULT + * void + * NOTES + * SEE ALSO + * simplestring_add () + * SOURCE + */ +void simplestring_addn(simplestring* target, const char* source, int add_len) { + if(target && source) { + if(!target->str) { + simplestring_init_str(target); + } + if(target->len + add_len + 1 > target->size) { + /* newsize is current length + new length */ + int newsize = target->len + add_len + 1; + int incr = target->size * 2; + + /* align to SIMPLESTRING_INCR increments */ + newsize = newsize - (newsize % incr) + incr; + target->str = (char*)realloc(target->str, newsize); + + target->size = target->str ? newsize : 0; + } + + if(target->str) { + if(add_len) { + memcpy(target->str + target->len, source, add_len); + } + target->len += add_len; + target->str[target->len] = 0; /* null terminate */ + } + } +} +/******/ + +/****f* FUNC/simplestring_add + * NAME + * simplestring_add + * SYNOPSIS + * void simplestring_add(simplestring* string, const char* add) + * FUNCTION + * appends a string of unknown length from source to target + * INPUTS + * target - the target string to append to + * source - the source string of unknown length + * RESULT + * void + * NOTES + * SEE ALSO + * simplestring_addn () + * SOURCE + */ +void simplestring_add(simplestring* target, const char* source) { + if(target && source) { + simplestring_addn(target, source, strlen(source)); + } +} +/******/ + + +/*---------------------- +* End String Functions * +*--------------------**/ diff --git a/ext/rpc/xmlrpc/libxmlrpc/simplestring.h b/ext/rpc/xmlrpc/libxmlrpc/simplestring.h new file mode 100644 index 0000000000..a891ba6d78 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/simplestring.h @@ -0,0 +1,75 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +#ifndef __SIMPLESTRING_H__ + #define __SIMPLESTRING_H__ + +/*-******************************** +* begin simplestring header stuff * +**********************************/ + +#ifdef __cplusplus +extern "C" { +#endif + + /****s* struct/simplestring + * NAME + * simplestring + * NOTES + * represents a string efficiently for fast appending, etc. + * SOURCE + */ +typedef struct _simplestring { + char* str; /* string buf */ + int len; /* length of string/buf */ + int size; /* size of allocated buffer */ +} simplestring; +/******/ + +#ifndef NULL + #define NULL 0 +#endif + +void simplestring_init(simplestring* string); +void simplestring_clear(simplestring* string); +void simplestring_free(simplestring* string); +void simplestring_addn(simplestring* string, const char* add, int add_len); + +#ifdef __cplusplus +} +#endif + +/*-****************************** +* end simplestring header stuff * +********************************/ + +#endif /* __SIMPLESTRING_H__ */ diff --git a/ext/rpc/xmlrpc/libxmlrpc/system_methods.c b/ext/rpc/xmlrpc/libxmlrpc/system_methods.c new file mode 100644 index 0000000000..742b837143 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/system_methods.c @@ -0,0 +1,371 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +/****h* ABOUT/system_methods + * AUTHOR + * Dan Libby, aka danda (dan@libby.com) + * HISTORY + * 4/28/2001 -- danda -- adding system.multicall and separating out system methods. + * TODO + * NOTES + *******/ + + +#include "queue.h" +#include "xmlrpc.h" +#include "xmlrpc_private.h" +#include "xmlrpc_introspection_private.h" +#include "system_methods_private.h" +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> + + +static const char* xsm_introspection_xml = +"<?xml version='1.0' ?>" + +"<introspection version='1.0'>" + "<typeList>" + + "<typeDescription name='system.value' basetype='struct' desc='description of a value'>" + "<value type='string' name='name' optional='yes'>value identifier</value>" + "<value type='string' name='type'>value's xmlrpc or user-defined type</value>" + "<value type='string' name='description'>value's textual description</value> " + "<value type='boolean' name='optional'>true if value is optional, else it is required</value> " + "<value type='any' name='member' optional='yes'>a child of this element. n/a for scalar types</value> " + "</typeDescription>" + + "<typeDescription name='system.valueList' basetype='array' desc='list of value descriptions'>" + "<value type='system.value'/>" + "</typeDescription>" + + "<typeDescription name='system.stringList' basetype='array' desc='list of strings'>" + "<value type='string'/>" + "</typeDescription>" + + + "</typeList>" + + "<methodList>" + + "<!-- system.describeMethods -->" + "<methodDescription name='system.describeMethods'>" + "<author>Dan Libby</author>" + "<purpose>fully describes the methods and types implemented by this XML-RPC server.</purpose>" + "<version>1.1</version>" + "<signatures>" + "<signature>" + "<params>" + "<value type='array' name='methodList' optional='yes' desc='a list of methods to be described. if omitted, all are described.'>" + "<value type='string'>a valid method name</value>" + "</value>" + "</params>" + "<returns>" + "<value type='struct' desc='contains methods list and types list'>" + "<value type='array' name='methodList' desc='a list of methods'>" + "<value type='struct' desc='representation of a single method'>" + "<value type='string' name='name'>method name</value>" + "<value type='string' name='version' optional='yes'>method version</value>" + "<value type='string' name='author' optional='yes'>method author</value>" + "<value type='string' name='purpose' optional='yes'>method purpose</value>" + "<value type='array' name='signatures' desc='list of method signatures'>" + "<value type='struct' desc='representation of a single signature'>" + "<value type='system.valueList' name='params' optional='yes'>parameter list</value>" + "<value type='system.valueList' name='returns' optional='yes'>return value list</value>" + "</value>" + "</value>" + "<value type='system.stringList' name='bugs' optional='yes'>list of known bugs</value>" + "<value type='system.stringList' name='errors' optional='yes'>list of possible errors and error codes</value>" + "<value type='system.stringList' name='examples' optional='yes'>list of examples</value>" + "<value type='system.stringList' name='history' optional='yes'>list of modifications</value>" + "<value type='system.stringList' name='notes' optional='yes'>list of notes</value>" + "<value type='system.stringList' name='see' optional='yes'>see also. list of related methods</value>" + "<value type='system.stringList' name='todo' optional='yes'>list of unimplemented features</value>" + "</value>" + "</value>" + "<value type='array' name='typeList' desc='a list of type descriptions. Typically used for referencing complex types'>" + "<value type='system.value'>a type description</value>" + "</value>" + "</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.listMethods' />" + "<item name='system.methodSignature' />" + "<item name='system.methodHelp' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "<!-- system.listMethods -->" + "<methodDescription name='system.listMethods'>" + "<author>Dan Libby</author>" + "<purpose>enumerates the methods implemented by this XML-RPC server.</purpose>" + "<version>1.0</version>" + "<signatures>" + "<signature>" + "<returns>" + "<value type='array' desc='an array of strings'>" + "<value type='string'>name of a method implemented by the server.</value>" + "</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.describeMethods' />" + "<item name='system.methodSignature' />" + "<item name='system.methodHelp' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "<!-- system.methodHelp -->" + "<methodDescription name='system.methodHelp'>" + "<author>Dan Libby</author>" + "<purpose>provides documentation string for a single method</purpose>" + "<version>1.0</version>" + "<signatures>" + "<signature>" + "<params>" + "<value type='string' name='methodName'>name of the method for which documentation is desired</value>" + "</params>" + "<returns>" + "<value type='string'>help text if defined for the method passed, otherwise an empty string</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.listMethods' />" + "<item name='system.methodSignature' />" + "<item name='system.methodHelp' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "<!-- system.methodSignature -->" + "<methodDescription name='system.methodSignature'>" + "<author>Dan Libby</author>" + "<purpose>provides 1 or more signatures for a single method</purpose>" + "<version>1.0</version>" + "<signatures>" + "<signature>" + "<params>" + "<value type='string' name='methodName'>name of the method for which documentation is desired</value>" + "</params>" + "<returns>" + "<value type='array' desc='a list of arrays, each representing a signature'>" + "<value type='array' desc='a list of strings. the first element represents the method return value. subsequent elements represent parameters.'>" + "<value type='string'>a string indicating the xmlrpc type of a value. one of: string, int, double, base64, datetime, array, struct</value>" + "</value>" + "</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.listMethods' />" + "<item name='system.methodHelp' />" + "<item name='system.describeMethods' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "<!-- system.multiCall -->" + "<methodDescription name='system.multiCall'>" + "<author>Dan Libby</author>" + "<purpose>executes multiple methods in sequence and returns the results</purpose>" + "<version>1.0</version>" + "<signatures>" + "<signature>" + "<params>" + "<value type='array' name='methodList' desc='an array of method call structs'>" + "<value type='struct' desc='a struct representing a single method call'>" + "<value type='string' name='methodName' desc='name of the method to be executed'/>" + "<value type='array' name='params' desc='an array representing the params to a method. sub-elements should match method signature'/>" + "</value>" + "</value>" + "</params>" + "<returns>" + "<value type='array' desc='an array of method responses'>" + "<value type='array' desc='an array containing a single value, which is the method's response'/>" + "</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.listMethods' />" + "<item name='system.methodHelp' />" + "<item name='system.describeMethods' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "<!-- system.getCapabilities -->" + "<methodDescription name='system.getCapabilities'>" + "<author>Dan Libby</author>" + "<purpose>returns a list of capabilities supported by this server</purpose>" + "<version>1.0</version>" + "<notes><item>spec url: http://groups.yahoo.com/group/xml-rpc/message/2897</item></notes>" + "<signatures>" + "<signature>" + "<returns>" + "<value type='struct' desc='list of capabilities, each with a unique key defined by the capability's spec'>" + "<value type='struct' desc='definition of a single capability'>" + "<value type='string' name='specURL'>www address of the specification defining this capability</value>" + "<value type='int' name='specVersion'>version of the spec that this server's implementation conforms to</value>" + "</value>" + "</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.listMethods' />" + "<item name='system.methodHelp' />" + "<item name='system.describeMethods' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "</methodList>" +"</introspection>"; + + +/* forward declarations for static (non public, non api) funcs */ +static XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); +static XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); + +/*-******************* +* System Methods API * +*********************/ + +static void xsm_lazy_doc_methods_cb(XMLRPC_SERVER server, void* userData) { + XMLRPC_VALUE xDesc = XMLRPC_IntrospectionCreateDescription(xsm_introspection_xml, NULL); + XMLRPC_ServerAddIntrospectionData(server, xDesc); + XMLRPC_CleanupValue(xDesc); +} + +void xsm_register(XMLRPC_SERVER server) { + xi_register_system_methods(server); + + XMLRPC_ServerRegisterMethod(server, xsm_token_system_multicall, xsm_system_multicall_cb); + XMLRPC_ServerRegisterMethod(server, xsm_token_system_get_capabilities, xsm_system_get_capabilities_cb); + + /* callback for documentation generation should it be requested */ + XMLRPC_ServerRegisterIntrospectionCallback(server, xsm_lazy_doc_methods_cb); +} + +XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + XMLRPC_VALUE xArray = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)); + XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_array); + + if (xArray) { + XMLRPC_VALUE xMethodIter = XMLRPC_VectorRewind(xArray); + + while (xMethodIter) { + XMLRPC_REQUEST request = XMLRPC_RequestNew(); + if(request) { + const char* methodName = XMLRPC_VectorGetStringWithID(xMethodIter, "methodName"); + XMLRPC_VALUE params = XMLRPC_VectorGetValueWithID(xMethodIter, "params"); + + if(methodName && params) { + XMLRPC_VALUE xRandomArray = XMLRPC_CreateVector(0, xmlrpc_vector_array); + XMLRPC_RequestSetMethodName(request, methodName); + XMLRPC_RequestSetData(request, params); + XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); + + XMLRPC_AddValueToVector(xRandomArray, + XMLRPC_ServerCallMethod(server, request, userData)); + + XMLRPC_AddValueToVector(xReturn, xRandomArray); + } + XMLRPC_RequestFree(request, 1); + } + xMethodIter = XMLRPC_VectorNext(xArray); + } + } + return xReturn; +} + + +XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_struct); + XMLRPC_VALUE xFaults = XMLRPC_CreateVector("faults_interop", xmlrpc_vector_struct); + XMLRPC_VALUE xIntro = XMLRPC_CreateVector("introspection", xmlrpc_vector_struct); + + /* support for fault spec */ + XMLRPC_VectorAppendString(xFaults, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php", 0); + XMLRPC_VectorAppendInt(xFaults, "specVersion", 20010516); + + /* support for introspection spec */ + XMLRPC_VectorAppendString(xIntro, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.introspection.php", 0); + XMLRPC_VectorAppendInt(xIntro, "specVersion", 20010516); + + XMLRPC_AddValuesToVector(xReturn, + xFaults, + xIntro, + NULL); + + return xReturn; + +} + +/*-*********************** +* End System Methods API * +*************************/ + + + diff --git a/ext/rpc/xmlrpc/libxmlrpc/system_methods_private.h b/ext/rpc/xmlrpc/libxmlrpc/system_methods_private.h new file mode 100644 index 0000000000..72408fd3c4 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/system_methods_private.h @@ -0,0 +1,91 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Dan Libby, Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* IMPORTANT! + * + * only non-public things should be in this file. It is fine for any .c file + * in xmlrpc/src to include it, but users of the public API should never + * include it, and thus *.h files that are part of the public API should + * never include it, or they would break if this file is not present. + */ + + +#ifndef __SYSTEM_METHODS_PRIVATE_H +/* + * Avoid include redundancy. + */ +#define __SYSTEM_METHODS_PRIVATE_H + +/*---------------------------------------------------------------------------- + * system_methods_private.h + * + * Purpose: + * define non-public system.* methods + * Comments: + * xsm = xmlrpc system methods + */ + +/*---------------------------------------------------------------------------- + * Constants + */ +#define xsm_token_system_multicall "system.multiCall" +#define xsm_token_system_get_capabilities "system.getCapabilities" + + +/*---------------------------------------------------------------------------- + * Includes + */ + +/*---------------------------------------------------------------------------- + * Structures + */ + +/*---------------------------------------------------------------------------- + * Globals + */ + +/*---------------------------------------------------------------------------- + * Functions + */ +void xsm_register(XMLRPC_SERVER server); +int xsm_is_system_method(XMLRPC_Callback cb); + +/*---------------------------------------------------------------------------- + * Macros + */ + + +#endif /* __SYSTEM_METHODS_PRIVATE_H */ + + + + diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_element.c b/ext/rpc/xmlrpc/libxmlrpc/xml_element.c new file mode 100644 index 0000000000..255c0c572e --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xml_element.c @@ -0,0 +1,712 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +static const char rcsid[] = "#(@) $Id$"; + + + +/****h* ABOUT/xml_element + * NAME + * xml_element + * AUTHOR + * Dan Libby, aka danda (dan@libby.com) + * CREATION DATE + * 06/2000 + * HISTORY + * 10/15/2000 -- danda -- adding robodoc documentation + * TODO + * Nicer external API. Get rid of macros. Make opaque types, etc. + * PORTABILITY + * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just + * about anything with minor mods. + * NOTES + * This code incorporates ideas from expat-ensor from http://xml.ensor.org. + * + * It was coded primarily to act as a go-between for expat and xmlrpc. To this + * end, it stores xml elements, their sub-elements, and their attributes in an + * in-memory tree. When expat is done parsing, the tree can be walked, thus + * retrieving the values. The code can also be used to build a tree via API then + * write out the tree to a buffer, thus "serializing" the xml. + * + * It turns out this is useful for other purposes, such as parsing config files. + * YMMV. + * + * Some Features: + * - output option for xml escaping data. Choices include no escaping, entity escaping, + * or CDATA sections. + * - output option for character encoding. Defaults to (none) utf-8. + * - output option for verbosity/readability. ultra-compact, newlines, pretty/level indented. + * + * BUGS + * there must be some. + ******/ + + +#include <stdlib.h> +#include <string.h> + +#include "xml_element.h" +#include "queue.h" +#include "expat.h" +#include "encodings.h" + +#define my_free(thing) if(thing) {free(thing); thing = 0;} + +#define XML_DECL_START "<?xml" +#define XML_DECL_START_LEN sizeof(XML_DECL_START) - 1 +#define XML_DECL_VERSION "version='1.0'" +#define XML_DECL_VERSION_LEN sizeof(XML_DECL_VERSION) - 1 +#define XML_DECL_ENCODING_ATTR "encoding" +#define XML_DECL_ENCODING_ATTR_LEN sizeof(XML_DECL_ENCODING_ATTR) - 1 +#define XML_DECL_ENCODING_DEFAULT "utf-8" +#define XML_DECL_ENCODING_DEFAULT_LEN sizeof(XML_DECL_ENCODING_DEFAULT) - 1 +#define XML_DECL_END "?>" +#define XML_DECL_END_LEN sizeof(XML_DECL_END) - 1 +#define START_TOKEN_BEGIN "<" +#define START_TOKEN_BEGIN_LEN sizeof(START_TOKEN_BEGIN) - 1 +#define START_TOKEN_END ">" +#define START_TOKEN_END_LEN sizeof(START_TOKEN_END) - 1 +#define EMPTY_START_TOKEN_END "/>" +#define EMPTY_START_TOKEN_END_LEN sizeof(EMPTY_START_TOKEN_END) - 1 +#define END_TOKEN_BEGIN "</" +#define END_TOKEN_BEGIN_LEN sizeof(END_TOKEN_BEGIN) - 1 +#define END_TOKEN_END ">" +#define END_TOKEN_END_LEN sizeof(END_TOKEN_END) - 1 +#define ATTR_DELIMITER "\"" +#define ATTR_DELIMITER_LEN sizeof(ATTR_DELIMITER) - 1 +#define CDATA_BEGIN "<![CDATA[" +#define CDATA_BEGIN_LEN sizeof(CDATA_BEGIN) - 1 +#define CDATA_END "]]>" +#define CDATA_END_LEN sizeof(CDATA_END) - 1 +#define EQUALS "=" +#define EQUALS_LEN sizeof(EQUALS) - 1 +#define WHITESPACE " " +#define WHITESPACE_LEN sizeof(WHITESPACE) - 1 +#define NEWLINE "\n" +#define NEWLINE_LEN sizeof(NEWLINE) - 1 +#define MAX_VAL_BUF 144 +#define SCALAR_STR "SCALAR" +#define SCALAR_STR_LEN sizeof(SCALAR_STR) - 1 +#define VECTOR_STR "VECTOR" +#define VECTOR_STR_LEN sizeof(VECTOR_STR) - 1 +#define RESPONSE_STR "RESPONSE" +#define RESPONSE_STR_LEN sizeof(RESPONSE_STR) - 1 + + +/*----------------------------- +- Begin xml_element Functions - +-----------------------------*/ + +/****f* xml_element/xml_elem_free_non_recurse + * NAME + * xml_elem_free_non_recurse + * SYNOPSIS + * void xml_elem_free_non_recurse(xml_element* root) + * FUNCTION + * free a single xml element. child elements will not be freed. + * INPUTS + * root - the element to free + * RESULT + * void + * NOTES + * SEE ALSO + * xml_elem_free () + * xml_elem_new () + * SOURCE + */ +void xml_elem_free_non_recurse(xml_element* root) { + if(root) { + xml_element_attr* attrs = Q_Head(&root->attrs); + while(attrs) { + my_free(attrs->key); + my_free(attrs->val); + my_free(attrs); + attrs = Q_Next(&root->attrs); + } + + Q_Destroy(&root->children); + Q_Destroy(&root->attrs); + my_free((char*)root->name); + simplestring_free(&root->text); + my_free(root); + } +} +/******/ + +/****f* xml_element/xml_elem_free + * NAME + * xml_elem_free + * SYNOPSIS + * void xml_elem_free(xml_element* root) + * FUNCTION + * free an xml element and all of its child elements + * INPUTS + * root - the root of an xml tree you would like to free + * RESULT + * void + * NOTES + * SEE ALSO + * xml_elem_free_non_recurse () + * xml_elem_new () + * SOURCE + */ +void xml_elem_free(xml_element* root) { + if(root) { + xml_element* kids = Q_Head(&root->children); + while(kids) { + xml_elem_free(kids); + kids = Q_Next(&root->children); + } + xml_elem_free_non_recurse(root); + } +} +/******/ + +/****f* xml_element/xml_elem_new + * NAME + * xml_elem_new + * SYNOPSIS + * xml_element* xml_elem_new() + * FUNCTION + * allocates and initializes a new xml_element + * INPUTS + * none + * RESULT + * xml_element* or NULL. NULL indicates an out-of-memory condition. + * NOTES + * SEE ALSO + * xml_elem_free () + * xml_elem_free_non_recurse () + * SOURCE + */ +xml_element* xml_elem_new() { + xml_element* elem = calloc(1, sizeof(xml_element)); + if(elem) { + Q_Init(&elem->children); + Q_Init(&elem->attrs); + simplestring_init(&elem->text); + + /* init empty string in case we don't find any char data */ + simplestring_addn(&elem->text, "", 0); + } + return elem; +} +/******/ + +static int xml_elem_writefunc(int (*fptr)(void *data, const char *text, int size), const char *text, void *data, int len) +{ + return fptr && text ? fptr(data, text, len ? len : strlen(text)) : 0; +} + + + +static int create_xml_escape(char *pString, unsigned char c) +{ + int counter = 0; + + pString[counter++] = '&'; + pString[counter++] = '#'; + if(c >= 100) { + pString[counter++] = c / 100 + '0'; + c = c % 100; + } + if(c >= 10) { + pString[counter++] = c / 10 + '0'; + c = c % 10; + } + pString[counter++] = c + '0'; + pString[counter++] = ';'; + return counter; +} + +#define non_ascii(c) (c > 127) +#define non_print(c) (!isprint(c)) +#define markup(c) (c == '&' || c == '\"' || c == '>' || c == '<') +#define entity_length(c) ( (c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3; /* "&#" + c + ";" */ + +/* + * xml_elem_entity_escape + * + * Purpose: + * escape reserved xml chars and non utf-8 chars as xml entities + * Comments: + * The return value may be a new string, or null if no + * conversion was performed. In the latter case, *newlen will + * be 0. + * Flags (to escape) + * xml_elem_no_escaping = 0x000, + * xml_elem_entity_escaping = 0x002, // escape xml special chars as entities + * xml_elem_non_ascii_escaping = 0x008, // escape chars above 127 + * xml_elem_cdata_escaping = 0x010, // wrap in cdata + */ +static char* xml_elem_entity_escape(const char* buf, int old_len, int *newlen, XML_ELEM_ESCAPING flags) { + char *pRetval = 0; + int iNewBufLen=0; + +#define should_escape(c, flag) ( ((flag & xml_elem_markup_escaping) && markup(c)) || \ + ((flag & xml_elem_non_ascii_escaping) && non_ascii(c)) || \ + ((flag & xml_elem_non_print_escaping) && non_print(c)) ) + + if(buf && *buf) { + const unsigned char *bufcopy; + char *NewBuffer; + int ToBeXmlEscaped=0; + int iLength; + bufcopy = buf; + iLength= old_len ? old_len : strlen(buf); + while(*bufcopy) { + if( should_escape(*bufcopy, flags) ) { + /* the length will increase by length of xml escape - the character length */ + iLength += entity_length(*bufcopy); + ToBeXmlEscaped=1; + } + bufcopy++; + } + + if(ToBeXmlEscaped) { + + NewBuffer= malloc(iLength+1); + if(NewBuffer) { + bufcopy=buf; + while(*bufcopy) { + if(should_escape(*bufcopy, flags)) { + iNewBufLen += create_xml_escape(NewBuffer+iNewBufLen,*bufcopy); + } + else { + NewBuffer[iNewBufLen++]=*bufcopy; + } + bufcopy++; + } + NewBuffer[iNewBufLen] = 0; + pRetval = NewBuffer; + } + } + } + + if(newlen) { + *newlen = iNewBufLen; + } + + return pRetval; +} + + +static void xml_element_serialize(xml_element *el, int (*fptr)(void *data, const char *text, int size), void *data, XML_ELEM_OUTPUT_OPTIONS options, int depth) +{ + int i; + static STRUCT_XML_ELEM_OUTPUT_OPTIONS default_opts = {xml_elem_pretty, xml_elem_markup_escaping | xml_elem_non_print_escaping, XML_DECL_ENCODING_DEFAULT}; + static char whitespace[] = " " + " " + " "; + depth++; + + if(!el) { + fprintf(stderr, "Nothing to write\n"); + return; + } + if(!options) { + options = &default_opts; + } + + /* print xml declaration if at root level */ + if(depth == 1) { + xml_elem_writefunc(fptr, XML_DECL_START, data, XML_DECL_START_LEN); + xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); + xml_elem_writefunc(fptr, XML_DECL_VERSION, data, XML_DECL_VERSION_LEN); + if(options->encoding && *options->encoding) { + xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); + xml_elem_writefunc(fptr, XML_DECL_ENCODING_ATTR, data, XML_DECL_ENCODING_ATTR_LEN); + xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN); + xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); + xml_elem_writefunc(fptr, options->encoding, data, 0); + xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); + } + xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); + xml_elem_writefunc(fptr, XML_DECL_END, data, XML_DECL_END_LEN); + if(options->verbosity != xml_elem_no_white_space) { + xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); + } + } + + if(options->verbosity == xml_elem_pretty && depth > 2) { + xml_elem_writefunc(fptr, whitespace, data, depth - 2); + } + /* begin element */ + xml_elem_writefunc(fptr,START_TOKEN_BEGIN, data, START_TOKEN_BEGIN_LEN); + if(el->name) { + xml_elem_writefunc(fptr, el->name, data, 0); + + /* write attrs, if any */ + if(Q_Size(&el->attrs)) { + xml_element_attr* iter = Q_Head(&el->attrs); + while( iter ) { + xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); + xml_elem_writefunc(fptr, iter->key, data, 0); + xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN); + xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); + xml_elem_writefunc(fptr, iter->val, data, 0); + xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); + + iter = Q_Next(&el->attrs); + } + } + } + else { + xml_elem_writefunc(fptr, "None", data, 0); + } + /* if no text and no children, use abbreviated form, eg: <foo/> */ + if(!el->text.len && !Q_Size(&el->children)) { + xml_elem_writefunc(fptr, EMPTY_START_TOKEN_END, data, EMPTY_START_TOKEN_END_LEN); + } + /* otherwise, print element contents */ + else { + xml_elem_writefunc(fptr, START_TOKEN_END, data, START_TOKEN_END_LEN); + + /* print text, if any */ + if(el->text.len) { + char* escaped_str = el->text.str; + int buflen = el->text.len; + + if(options->escaping && options->escaping != xml_elem_cdata_escaping) { + escaped_str = xml_elem_entity_escape(el->text.str, buflen, &buflen, options->escaping ); + if(!escaped_str) { + escaped_str = el->text.str; + } + } + + if(options->escaping & xml_elem_cdata_escaping) { + xml_elem_writefunc(fptr, CDATA_BEGIN, data, CDATA_BEGIN_LEN); + } + + xml_elem_writefunc(fptr, escaped_str, data, buflen); + + if(escaped_str != el->text.str) { + my_free(escaped_str); + } + + if(options->escaping & xml_elem_cdata_escaping) { + xml_elem_writefunc(fptr, CDATA_END, data, CDATA_END_LEN); + } + } + /* no text, so print child elems */ + else { + xml_element *kids = Q_Head(&el->children); + i = 0; + while( kids ) { + if(i++ == 0) { + if(options->verbosity != xml_elem_no_white_space) { + xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); + } + } + xml_element_serialize(kids, fptr, data, options, depth); + kids = Q_Next(&el->children); + } + if(i) { + if(options->verbosity == xml_elem_pretty && depth > 2) { + xml_elem_writefunc(fptr, whitespace, data, depth - 2); + } + } + } + + xml_elem_writefunc(fptr, END_TOKEN_BEGIN, data, END_TOKEN_BEGIN_LEN); + xml_elem_writefunc(fptr,el->name ? el->name : "None", data, 0); + xml_elem_writefunc(fptr, END_TOKEN_END, data, END_TOKEN_END_LEN); + } + if(options->verbosity != xml_elem_no_white_space) { + xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); + } +} + +/* print buf to file */ +static file_out_fptr(void *f, const char *text, int size) +{ + fputs(text, (FILE *)f); +} + +/* print buf to simplestring */ +static simplestring_out_fptr(void *f, const char *text, int size) +{ + simplestring* buf = (simplestring*)f; + if(buf) { + simplestring_addn(buf, text, size); + } +} + +/****f* xml_element/xml_elem_serialize_to_string + * NAME + * xml_elem_serialize_to_string + * SYNOPSIS + * void xml_element_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len) + * FUNCTION + * writes element tree as XML into a newly allocated buffer + * INPUTS + * el - root element of tree + * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS + * buf_len - length of returned buffer, if not null. + * RESULT + * char* or NULL. Must be free'd by caller. + * NOTES + * SEE ALSO + * xml_elem_serialize_to_stream () + * xml_elem_parse_buf () + * SOURCE + */ +char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len) +{ + simplestring buf; + simplestring_init(&buf); + + xml_element_serialize(el, simplestring_out_fptr, (void *)&buf, options, 0); + + if(buf_len) { + *buf_len = buf.len; + } + + return buf.str; +} +/******/ + +/****f* xml_element/xml_elem_serialize_to_stream + * NAME + * xml_elem_serialize_to_stream + * SYNOPSIS + * void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options) + * FUNCTION + * writes element tree as XML into a stream (typically an opened file) + * INPUTS + * el - root element of tree + * output - stream handle + * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS + * RESULT + * void + * NOTES + * SEE ALSO + * xml_elem_serialize_to_string () + * xml_elem_parse_buf () + * SOURCE + */ +void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options) +{ + xml_element_serialize(el, file_out_fptr, (void *)output, options, 0); +} +/******/ + +/*--------------------------* +* End xml_element Functions * +*--------------------------*/ + + +/*---------------------- +* Begin Expat Handlers * +*---------------------*/ + +typedef struct _xml_elem_data { + xml_element* root; + xml_element* current; + XML_ELEM_INPUT_OPTIONS input_options; + int needs_enc_conversion; +} xml_elem_data; + + +/* expat start of element handler */ +static void startElement(void *userData, const char *name, const char **attrs) +{ + int i; + xml_element *c; + xml_elem_data* mydata = (xml_elem_data*)userData; + const char** p = attrs; + + if(mydata) { + c = mydata->current; + + mydata->current = xml_elem_new(); + mydata->current->name = (char*)strdup(name); + mydata->current->parent = c; + + /* init attrs */ + while(p && *p) { + xml_element_attr* attr = malloc(sizeof(xml_element_attr)); + if(attr) { + attr->key = strdup(*p); + attr->val = strdup(*(p+1)); + Q_PushTail(&mydata->current->attrs, attr); + + p += 2; + } + } + } +} + +/* expat end of element handler */ +static void endElement(void *userData, const char *name) +{ + xml_elem_data* mydata = (xml_elem_data*)userData; + + if(mydata && mydata->current && mydata->current->parent) { + Q_PushTail(&mydata->current->parent->children, mydata->current); + + mydata->current = mydata->current->parent; + } +} + +/* expat char data handler */ +static void charHandler(void *userData, + const char *s, + int len) +{ + xml_elem_data* mydata = (xml_elem_data*)userData; + if(mydata && mydata->current) { + + /* Check if we need to decode utf-8 parser output to another encoding */ + if(mydata->needs_enc_conversion && mydata->input_options->encoding) { + char* add_text = utf8_decode(s, len, &len, mydata->input_options->encoding); + if(add_text) { + simplestring_addn(&mydata->current->text, add_text, len); + free(add_text); + return; + } + } + simplestring_addn(&mydata->current->text, s, len); + } +} +/******/ + +/*-------------------* +* End Expat Handlers * +*-------------------*/ + +/*-------------------* +* xml_elem_parse_buf * +*-------------------*/ + +/****f* xml_element/xml_elem_parse_buf + * NAME + * xml_elem_parse_buf + * SYNOPSIS + * xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error) + * FUNCTION + * parse a buffer containing XML into an xml_element in-memory tree + * INPUTS + * in_buf - buffer containing XML document + * len - length of buffer + * options - input options. optional + * error - error result data. optional. check if result is null. + * RESULT + * void + * NOTES + * The returned data must be free'd by caller + * SEE ALSO + * xml_elem_serialize_to_string () + * xml_elem_free () + * SOURCE + */ +xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error) +{ + xml_element* xReturn = NULL; + char buf[100] = ""; + static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8}; + + if(!options) { + options = &default_opts; + } + + if(in_buf) { + XML_Parser parser; + xml_elem_data mydata = {0}; + + parser = XML_ParserCreate(NULL); + + mydata.root = xml_elem_new(); + mydata.current = mydata.root; + mydata.input_options = options; + mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8); + + XML_SetElementHandler(parser, startElement, endElement); + XML_SetCharacterDataHandler(parser, charHandler); + + /* pass the xml_elem_data struct along */ + XML_SetUserData(parser, (void*)&mydata); + + if(!len) { + len = strlen(in_buf); + } + + /* parse the XML */ + if(XML_Parse(parser, in_buf, len, 1) == 0) { + enum XML_Error err_code = XML_GetErrorCode(parser); + int line_num = XML_GetCurrentLineNumber(parser); + int col_num = XML_GetCurrentColumnNumber(parser); + long byte_idx = XML_GetCurrentByteIndex(parser); + int byte_total = XML_GetCurrentByteCount(parser); + const char * error_str = XML_ErrorString(err_code); + if(byte_idx >= 0) { + snprintf(buf, + sizeof(buf), + "\n\tdata beginning %i before byte index: %s\n", + byte_idx > 10 ? 10 : byte_idx, + in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx)); + } + + fprintf(stderr, "expat reports error code %i\n" + "\tdescription: %s\n" + "\tline: %i\n" + "\tcolumn: %i\n" + "\tbyte index: %i\n" + "\ttotal bytes: %i\n%s ", + err_code, error_str, line_num, + col_num, byte_idx, byte_total, buf); + + + /* error condition */ + if(error) { + error->parser_code = (long)err_code; + error->line = line_num; + error->column = col_num; + error->byte_index = byte_idx; + error->parser_error = error_str; + } + } + else { + xReturn = (xml_element*)Q_Head(&mydata.root->children); + } + + XML_ParserFree(parser); + + + xml_elem_free_non_recurse(mydata.root); + } + + return xReturn; +} + +/******/ diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_element.h b/ext/rpc/xmlrpc/libxmlrpc/xml_element.h new file mode 100644 index 0000000000..bf4f3a8ba4 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xml_element.h @@ -0,0 +1,202 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +#ifndef __XML_ELEMENT_H__ + #define __XML_ELEMENT_H__ + +/* includes */ +#include <stdio.h> +#include "queue.h" +#include "simplestring.h" +#include "encodings.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/****d* enum/XML_ELEM_VERBOSITY + * NAME + * XML_ELEM_VERBOSITY + * NOTES + * verbosity/readability options for generated xml + * SEE ALSO + * XML_ELEM_OUTPUT_OPTIONS + * SOURCE + */ +typedef enum _xml_elem_verbosity { + xml_elem_no_white_space, /* compact xml with no white space */ + xml_elem_newlines_only, /* add newlines for enhanced readability */ + xml_elem_pretty /* add newlines and indent accordint to depth */ +} XML_ELEM_VERBOSITY; +/******/ + + +/****d* enum/XML_ELEM_ESCAPING + * NAME + * XML_ELEM_ESCAPING + * NOTES + * xml escaping options for generated xml + * SEE ALSO + * XML_ELEM_OUTPUT_OPTIONS + * SOURCE + */ +typedef enum _xml_elem_escaping { + xml_elem_no_escaping = 0x000, + xml_elem_markup_escaping = 0x002, /* entity escape xml special chars */ + xml_elem_non_ascii_escaping = 0x008, /* entity escape chars above 127 */ + xml_elem_non_print_escaping = 0x010, /* entity escape non print (illegal) chars */ + xml_elem_cdata_escaping = 0x020, /* wrap in cdata section */ +} XML_ELEM_ESCAPING; +/******/ + + +/****s* struct/XML_ELEM_OUTPUT_OPTIONS + * NAME + * XML_ELEM_OUTPUT_OPTIONS + * NOTES + * defines various output options + * SOURCE + */ +typedef struct _xml_output_options { + XML_ELEM_VERBOSITY verbosity; /* length/verbosity of xml */ + XML_ELEM_ESCAPING escaping; /* how to escape special chars */ + const char* encoding; /* <?xml encoding="<encoding>" ?> */ +} STRUCT_XML_ELEM_OUTPUT_OPTIONS, *XML_ELEM_OUTPUT_OPTIONS; +/******/ + +/****s* struct/XML_ELEM_INPUT_OPTIONS + * NAME + * XML_ELEM_INPUT_OPTIONS + * NOTES + * defines various input options + * SOURCE + */ +typedef struct _xml_input_options { + ENCODING_ID encoding; /* which encoding to use. */ +} STRUCT_XML_ELEM_INPUT_OPTIONS, *XML_ELEM_INPUT_OPTIONS; +/******/ + +/****s* struct/XML_ELEM_ERROR + * NAME + * XML_ELEM_ERROR + * NOTES + * defines an xml parser error + * SOURCE + */ +typedef struct _xml_elem_error { + int parser_code; + const char* parser_error; + long line; + long column; + long byte_index; +} STRUCT_XML_ELEM_ERROR, *XML_ELEM_ERROR; +/******/ + + +/*-************************ +* begin xml element stuff * +**************************/ + +/****s* struct/xml_elem_attr + * NAME + * xml_elem_attr + * NOTES + * representation of an xml attribute, foo="bar" + * SOURCE + */ +typedef struct _xml_element_attr { + char* key; /* attribute key */ + char* val; /* attribute value */ +} xml_element_attr; +/******/ + +/****s* struct/xml_elem_attr + * NAME + * xml_elem_attr + * NOTES + * representation of an xml element, eg <candidate name="Harry Browne" party="Libertarian"/> + * SOURCE + */ +typedef struct _xml_element { + const char* name; /* element identifier */ + simplestring text; /* text contained between element begin/end pairs */ + struct _xml_element* parent; /* element's parent */ + + queue attrs; /* attribute list */ + queue children; /* child element list */ +} xml_element; +/******/ + +void xml_elem_free(xml_element* root); +void xml_elem_free_non_recurse(xml_element* root); +xml_element* xml_elem_new(); +char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len); +void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options); +xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error); + +/*-********************** +* end xml element stuff * +************************/ + +/*-********************** +* Begin xml_element API * +************************/ + +/****d* VALUE/XMLRPC_MACROS + * NAME + * Some Helpful Macros + * NOTES + * Some macros for making life easier. Should be self-explanatory. + * SEE ALSO + * XMLRPC_AddValueToVector () + * XMLRPC_VectorGetValueWithID_Case () + * XMLRPC_VALUE + * SOURCE + */ +#define xml_elem_next_element(el) ((el) ? (xml_element *)Q_Next(&el->children) : NULL) +#define xml_elem_head_element(el) ((el) ? (xml_element *)Q_Head(&el->children) : NULL) +#define xml_elem_next_attr(el) ((el) ? (xml_element_attr *)Q_Next(&el->attrs) : NULL) +#define xml_elem_head_attr(el) ((el) ? (xml_element_attr *)Q_Head(&el->attrs) : NULL) +#define xml_elem_get_name(el) (char *)((el) ? el->name : NULL) +#define xml_elem_get_val(el) (char *)((el) ? el->text.str : NULL) +/******/ + + +/*-******************** +* End xml_element API * +**********************/ + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_ELEMENT_H__ */ diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c b/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c new file mode 100644 index 0000000000..0d827a5fb0 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.c @@ -0,0 +1,316 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +#include <string.h> +#include <stdlib.h> +#include "xml_to_dandarpc.h" +#include "base64.h" + +/* list of tokens used in vocab */ +#define ELEM_METHODCALL "methodCall" +#define ELEM_METHODNAME "methodName" +#define ELEM_METHODRESPONSE "methodResponse" +#define ELEM_ROOT "simpleRPC" + +#define ATTR_ARRAY "array" +#define ATTR_BASE64 "base64" +#define ATTR_BOOLEAN "boolean" +#define ATTR_DATETIME "dateTime.iso8601" +#define ATTR_DOUBLE "double" +#define ATTR_ID "id" +#define ATTR_INT "int" +#define ATTR_MIXED "mixed" +#define ATTR_SCALAR "scalar" +#define ATTR_STRING "string" +#define ATTR_STRUCT "struct" +#define ATTR_TYPE "type" +#define ATTR_VECTOR "vector" +#define ATTR_VERSION "version" + +#define VAL_VERSION_0_9 "0.9" + + +XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE xCurrent, xml_element* el) { + if(!xCurrent) { + xCurrent = XMLRPC_CreateValueEmpty(); + } + + if(el->name) { + const char* id = NULL; + const char* type = NULL; + xml_element_attr* attr_iter = Q_Head(&el->attrs); + + while(attr_iter) { + if(!strcmp(attr_iter->key, ATTR_ID)) { + id = attr_iter->val; + } + if(!strcmp(attr_iter->key, ATTR_TYPE)) { + type = attr_iter->val; + } + attr_iter = Q_Next(&el->attrs); + } + + if(id) { + XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact); + } + + if(!strcmp(el->name, ATTR_SCALAR)) { + if(!type || !strcmp(type, ATTR_STRING)) { + XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len); + } + else if(!strcmp(type, ATTR_INT)) { + XMLRPC_SetValueInt(xCurrent, atoi(el->text.str)); + } + else if(!strcmp(type, ATTR_BOOLEAN)) { + XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str)); + } + else if(!strcmp(type, ATTR_DOUBLE)) { + XMLRPC_SetValueDouble(xCurrent, atof(el->text.str)); + } + else if(!strcmp(type, ATTR_DATETIME)) { + XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str); + } + else if(!strcmp(type, ATTR_BASE64)) { + struct buffer_st buf; + base64_decode(&buf, el->text.str, el->text.len); + XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset); + buffer_delete(&buf); + } + } + else if(!strcmp(el->name, ATTR_VECTOR)) { + xml_element* iter = (xml_element*)Q_Head(&el->children); + + if(!type || !strcmp(type, ATTR_MIXED)) { + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); + } + else if(!strcmp(type, ATTR_ARRAY)) { + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); + } + else if(!strcmp(type, ATTR_STRUCT)) { + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct); + } + while( iter ) { + XMLRPC_VALUE xNext = XMLRPC_CreateValueEmpty(); + xml_element_to_DANDARPC_REQUEST_worker(request, xNext, iter); + XMLRPC_AddValueToVector(xCurrent, xNext); + iter = (xml_element*)Q_Next(&el->children); + } + } + else { + xml_element* iter = (xml_element*)Q_Head(&el->children); + while( iter ) { + xml_element_to_DANDARPC_REQUEST_worker(request, xCurrent, iter); + iter = (xml_element*)Q_Next(&el->children); + } + + if(!strcmp(el->name, ELEM_METHODCALL)) { + if(request) { + XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); + } + } + else if(!strcmp(el->name, ELEM_METHODRESPONSE)) { + if(request) { + XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); + } + } + else if(!strcmp(el->name, ELEM_METHODNAME)) { + if(request) { + XMLRPC_RequestSetMethodName(request, el->text.str); + } + } + } + } + return xCurrent; +} + +XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el) +{ + return xml_element_to_DANDARPC_REQUEST_worker(NULL, NULL, el); +} + +XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el) +{ + if(request) { + return XMLRPC_RequestSetData(request, xml_element_to_DANDARPC_REQUEST_worker(request, NULL, el)); + } + return NULL; +} + +xml_element* DANDARPC_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) { +#define BUF_SIZE 512 + xml_element* root = NULL; + if(node) { + char buf[BUF_SIZE]; + const char* id = XMLRPC_GetValueID(node); + XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node); + XMLRPC_REQUEST_OUTPUT_OPTIONS output = XMLRPC_RequestGetOutputOptions(request); + int bNoAddType = (type == xmlrpc_string && request && output && output->xml_elem_opts.verbosity == xml_elem_no_white_space); + xml_element* elem_val = xml_elem_new(); + const char* pAttrType = NULL; + + xml_element_attr* attr_type = bNoAddType ? NULL : malloc(sizeof(xml_element_attr)); + + if(attr_type) { + attr_type->key = strdup(ATTR_TYPE); + attr_type->val = 0; + Q_PushTail(&elem_val->attrs, attr_type); + } + + elem_val->name = (type == xmlrpc_vector) ? strdup(ATTR_VECTOR) : strdup(ATTR_SCALAR); + + if(id && *id) { + xml_element_attr* attr_id = malloc(sizeof(xml_element_attr)); + if(attr_id) { + attr_id->key = strdup(ATTR_ID); + attr_id->val = strdup(id); + Q_PushTail(&elem_val->attrs, attr_id); + } + } + + switch(type) { + case xmlrpc_string: + pAttrType = ATTR_STRING; + simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); + break; + case xmlrpc_int: + pAttrType = ATTR_INT; + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_boolean: + pAttrType = ATTR_BOOLEAN; + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_double: + pAttrType = ATTR_DOUBLE; + snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_datetime: + pAttrType = ATTR_DATETIME; + simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node)); + break; + case xmlrpc_base64: + { + struct buffer_st buf; + pAttrType = ATTR_BASE64; + base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); + simplestring_addn(&elem_val->text, buf.data, buf.offset ); + buffer_delete(&buf); + } + break; + case xmlrpc_vector: + { + XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node); + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); + + switch(my_type) { + case xmlrpc_vector_array: + pAttrType = ATTR_ARRAY; + break; + case xmlrpc_vector_mixed: + pAttrType = ATTR_MIXED; + break; + case xmlrpc_vector_struct: + pAttrType = ATTR_STRUCT; + break; + default: + break; + } + + /* recurse through sub-elements */ + while( xIter ) { + xml_element* next_el = DANDARPC_to_xml_element_worker(request, xIter); + if(next_el) { + Q_PushTail(&elem_val->children, next_el); + } + xIter = XMLRPC_VectorNext(node); + } + } + break; + default: + break; + } + if(pAttrType && attr_type && !bNoAddType) { + attr_type->val = strdup(pAttrType); + } + root = elem_val; + } + return root; +} + +xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node) { + return DANDARPC_to_xml_element_worker(NULL, node); +} + +xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { + xml_element* wrapper = NULL; + xml_element* root = NULL; + if(request) { + XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request); + const char* pStr = NULL; + xml_element_attr* version = malloc(sizeof(xml_element_attr)); + version->key = strdup(ATTR_VERSION); + version->val = strdup(VAL_VERSION_0_9); + + wrapper = xml_elem_new(); + + if(request_type == xmlrpc_request_response) { + pStr = ELEM_METHODRESPONSE; + } + else if(request_type == xmlrpc_request_call) { + pStr = ELEM_METHODCALL; + } + if(pStr) { + wrapper->name = strdup(pStr); + } + + root = xml_elem_new(); + root->name = strdup(ELEM_ROOT); + Q_PushTail(&root->attrs, version); + Q_PushTail(&root->children, wrapper); + + pStr = XMLRPC_RequestGetMethodName(request); + + if(pStr) { + xml_element* method = xml_elem_new(); + method->name = strdup(ELEM_METHODNAME); + simplestring_add(&method->text, pStr); + Q_PushTail(&wrapper->children, method); + } + Q_PushTail(&wrapper->children, + DANDARPC_to_xml_element_worker(request, XMLRPC_RequestGetData(request))); + } + return root; +} + diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.h b/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.h new file mode 100644 index 0000000000..6facb55778 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xml_to_dandarpc.h @@ -0,0 +1,44 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +#ifndef XML_TO_DANDARPC_H + #define XML_TO_DANDARPC_H + +#include "time.h" +#include "xmlrpc.h" + +XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el); +XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el); +xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node); +xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request); + +#endif /* XML_TO_DANDARPC_H */ diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c b/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c new file mode 100644 index 0000000000..1cd3d9ac2b --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.c @@ -0,0 +1,365 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +static const char rcsid[] = "#(@) $Id$"; + +#include <string.h> +#include <stdlib.h> +#include "xml_to_xmlrpc.h" +#include "base64.h" + +/* list of tokens used in vocab */ +#define ELEM_ARRAY "array" +#define ELEM_BASE64 "base64" +#define ELEM_BOOLEAN "boolean" +#define ELEM_DATA "data" +#define ELEM_DATETIME "dateTime.iso8601" +#define ELEM_DOUBLE "double" +#define ELEM_FAULT "fault" +#define ELEM_FAULTCODE "faultCode" +#define ELEM_FAULTSTRING "faultString" +#define ELEM_I4 "i4" +#define ELEM_INT "int" +#define ELEM_MEMBER "member" +#define ELEM_METHODCALL "methodCall" +#define ELEM_METHODNAME "methodName" +#define ELEM_METHODRESPONSE "methodResponse" +#define ELEM_NAME "name" +#define ELEM_PARAM "param" +#define ELEM_PARAMS "params" +#define ELEM_STRING "string" +#define ELEM_STRUCT "struct" +#define ELEM_VALUE "value" + + +XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) { + if (!current_val) { + /* This should only be the case for the first element */ + current_val = XMLRPC_CreateValueEmpty(); + } + + if (el->name) { + if (!strcmp(el->name, ELEM_DATA) /* should be ELEM_ARRAY, but there is an extra level. weird */ + || ((!strcmp(el->name, ELEM_PARAMS)) && + (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call)) /* this "PARAMS" concept is silly. dave?! */ + || !strcmp(el->name, ELEM_FAULT)) { /* so is this "FAULT" nonsense. */ + xml_element* iter = (xml_element*)Q_Head(&el->children); + XMLRPC_SetIsVector(current_val, xmlrpc_vector_array); + + while (iter) { + XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); + xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); + XMLRPC_AddValueToVector(current_val, xNextVal); + iter = (xml_element*)Q_Next(&el->children); + } + } else if (!strcmp(el->name, ELEM_STRUCT)) { + xml_element* iter = (xml_element*)Q_Head(&el->children); + XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct); + + while ( iter ) { + XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); + xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); + XMLRPC_AddValueToVector(current_val, xNextVal); + iter = (xml_element*)Q_Next(&el->children); + } + } else if (!strcmp(el->name, ELEM_STRING) || + (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) { + XMLRPC_SetValueString(current_val, el->text.str, el->text.len); + } else if (!strcmp(el->name, ELEM_NAME)) { + XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact); + } else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) { + XMLRPC_SetValueInt(current_val, atoi(el->text.str)); + } else if (!strcmp(el->name, ELEM_BOOLEAN)) { + XMLRPC_SetValueBoolean(current_val, atoi(el->text.str)); + } else if (!strcmp(el->name, ELEM_DOUBLE)) { + XMLRPC_SetValueDouble(current_val, atof(el->text.str)); + } else if (!strcmp(el->name, ELEM_DATETIME)) { + XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str); + } else if (!strcmp(el->name, ELEM_BASE64)) { + struct buffer_st buf; + base64_decode(&buf, el->text.str, el->text.len); + XMLRPC_SetValueBase64(current_val, buf.data, buf.offset); + buffer_delete(&buf); + } else { + xml_element* iter; + + if (!strcmp(el->name, ELEM_METHODCALL)) { + if (request) { + XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); + } + } else if (!strcmp(el->name, ELEM_METHODRESPONSE)) { + if (request) { + XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); + } + } else if (!strcmp(el->name, ELEM_METHODNAME)) { + if (request) { + XMLRPC_RequestSetMethodName(request, el->text.str); + } + } + + iter = (xml_element*)Q_Head(&el->children); + while ( iter ) { + xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector, + current_val, iter); + iter = (xml_element*)Q_Next(&el->children); + } + } + } + return current_val; +} + +XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el) +{ + return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el); +} + +XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el) +{ + if (request) { + return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el)); + } + return NULL; +} + +xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node, + XMLRPC_REQUEST_TYPE request_type, int depth) { +#define BUF_SIZE 512 + xml_element* root = NULL; + if (node) { + char buf[BUF_SIZE]; + XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node); + XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node); + xml_element* elem_val = xml_elem_new(); + + /* special case for when root element is not an array */ + if (depth == 0 && + !(type == xmlrpc_vector && + vtype == xmlrpc_vector_array && + request_type == xmlrpc_request_call) ) { + int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)); + + xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1); + if (next_el) { + Q_PushTail(&elem_val->children, next_el); + } + elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS); + } else { + switch (type) { + case xmlrpc_string: + elem_val->name = strdup(ELEM_STRING); + simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); + break; + case xmlrpc_int: + elem_val->name = strdup(ELEM_INT); + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_boolean: + elem_val->name = strdup(ELEM_BOOLEAN); + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_double: + elem_val->name = strdup(ELEM_DOUBLE); + snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_datetime: + elem_val->name = strdup(ELEM_DATETIME); + simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node)); + break; + case xmlrpc_base64: + { + struct buffer_st buf; + elem_val->name = strdup(ELEM_BASE64); + base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); + simplestring_addn(&elem_val->text, buf.data, buf.offset ); + buffer_delete(&buf); + } + break; + case xmlrpc_vector: + { + XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node); + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); + xml_element* root_vector_elem = elem_val; + + switch (my_type) { + case xmlrpc_vector_array: + { + if(depth == 0) { + elem_val->name = strdup(ELEM_PARAMS); + } + else { + /* Hi my name is Dave and I like to make things as confusing + * as possible, thus I will throw in this 'data' element + * where it absolutely does not belong just so that people + * cannot code arrays and structs in a similar and straight + * forward manner. Have a good day. + * + * GRRRRRRRRR! + */ + xml_element* data = xml_elem_new(); + data->name = strdup(ELEM_DATA); + + elem_val->name = strdup(ELEM_ARRAY); + Q_PushTail(&elem_val->children, data); + root_vector_elem = data; + } + } + break; + case xmlrpc_vector_mixed: /* not officially supported */ + case xmlrpc_vector_struct: + elem_val->name = strdup(ELEM_STRUCT); + break; + default: + break; + } + + /* recurse through sub-elements */ + while ( xIter ) { + xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1); + if (next_el) { + Q_PushTail(&root_vector_elem->children, next_el); + } + xIter = XMLRPC_VectorNext(node); + } + } + break; + default: + break; + } + } + + { + XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector); + + if (depth == 1) { + xml_element* value = xml_elem_new(); + value->name = strdup(ELEM_VALUE); + + /* yet another hack for the "fault" crap */ + if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) { + root = value; + } else { + xml_element* param = xml_elem_new(); + param->name = strdup(ELEM_PARAM); + + Q_PushTail(¶m->children, value); + + root = param; + } + Q_PushTail(&value->children, elem_val); + } else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) { + xml_element* member = xml_elem_new(); + xml_element* name = xml_elem_new(); + xml_element* value = xml_elem_new(); + + member->name = strdup(ELEM_MEMBER); + name->name = strdup(ELEM_NAME); + value->name = strdup(ELEM_VALUE); + + simplestring_add(&name->text, XMLRPC_GetValueID(node)); + + Q_PushTail(&member->children, name); + Q_PushTail(&member->children, value); + Q_PushTail(&value->children, elem_val); + + root = member; + } else if (vtype == xmlrpc_vector_array) { + xml_element* value = xml_elem_new(); + + value->name = strdup(ELEM_VALUE); + + Q_PushTail(&value->children, elem_val); + + root = value; + } else if (vtype == xmlrpc_vector_none) { + /* no parent. non-op */ + root = elem_val; + } else { + xml_element* value = xml_elem_new(); + + value->name = strdup(ELEM_VALUE); + + Q_PushTail(&value->children, elem_val); + + root = value; + } + } + } + return root; +} + +xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) { + return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0); +} + +xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { + xml_element* wrapper = NULL; + if (request) { + const char* pStr = NULL; + XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request); + XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request); + + wrapper = xml_elem_new(); + + if (request_type == xmlrpc_request_call) { + pStr = ELEM_METHODCALL; + } else if (request_type == xmlrpc_request_response) { + pStr = ELEM_METHODRESPONSE; + } + if (pStr) { + wrapper->name = strdup(pStr); + } + + pStr = XMLRPC_RequestGetMethodName(request); + + if (pStr) { + xml_element* method = xml_elem_new(); + method->name = strdup(ELEM_METHODNAME); + simplestring_add(&method->text, pStr); + Q_PushTail(&wrapper->children, method); + } + if (xParams) { + Q_PushTail(&wrapper->children, + XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0)); + } else { + /* Despite the spec, the xml-rpc list folk want me to send an empty params element */ + xml_element* params = xml_elem_new(); + params->name = strdup(ELEM_PARAMS); + Q_PushTail(&wrapper->children, params); + } + } + return wrapper; +} + diff --git a/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.h b/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.h new file mode 100644 index 0000000000..234a153460 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xml_to_xmlrpc.h @@ -0,0 +1,45 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +#ifndef XML_TO_XMLRPC_H + #define XML_TO_XMLRPC_H + +#include "time.h" +#include "xmlrpc.h" + +XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el); +XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el); +xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node); +xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request); + +#endif /* XML_TO_XMLRPC_H */ diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c new file mode 100644 index 0000000000..f95ac949b3 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.c @@ -0,0 +1,2471 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +static const char rcsid[] = "#(@) $Id$"; + + +/****h* ABOUT/xmlrpc + * NAME + * XMLRPC_VALUE + * AUTHOR + * Dan Libby, aka danda (dan@libby.com) + * CREATION DATE + * 9/1999 - 10/2000 + * HISTORY + * 09/1999 -- danda -- Initial API, before I even knew of standard XMLRPC vocab. Response only. + * 06/2000 -- danda -- played with expat-ensor from www.ensor.org. Cool, but some flaws. + * 07/2000 -- danda -- wrote new implementation to be compatible with xmlrpc standard and + * incorporated some ideas from ensor, most notably the separation of + * xml dom from xmlrpc api. + * 08/2000 -- danda -- support for two vocabularies: danda-rpc and xml-rpc + * 08/2000 -- danda -- PHP C extension that uses XMLRPC + * 10/15/2000 -- danda -- adding robodoc documentation + * TODO + * Server method introspection. (Enumerate available methods, describe I/O) + * PORTABILITY + * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just + * about anything with minor mods. + * NOTES + * Welcome to XMLRPC. For more info on the specification and history, see + * http://www.xmlrpc.org. + * + * This code aims to be a full-featured C implementation of XMLRPC. It does not + * have any networking code. Rather, it is intended to be plugged into apps + * or libraries with existing networking facilities, eg PHP, apache, perl, mozilla, + * home-brew application servers, etc. + * + * Usage Paradigm: + * The user of this library will typically be implementing either an XMLRPC server, + * an XMLRPC client, or both. The client will use the library to build an in-memory + * representation of a request, and then serialize (encode) that request into XML. The + * client will then send the XML to the server via external mechanism. The server will + * de-serialize the XML back into an binary representation, call the appropriate registered + * method -- thereby generating a response. The response will be serialized into XML and + * sent back to the client. The client will de-serialize it into memory, and can + * iterate through the results via API. + * + * Both the request and the response may consist of arbitrarily long, arbitrarily nested + * values. The values may be one of several types, as defined by XMLRPC_VALUE_TYPE. + * + * Features and Architecture: + * - The XML parsing (xml_element.c) is completely independent of the XMLRPC api. In fact, + * it can be used as a standalone dom implementation. + * - Because of this, the same XMLRPC data can be serialized into multiple xml vocabularies. + * It is simply a matter of writing a transport. So far, two transports have been defined. + * The default xmlrpc vocab (xml_to_xmlrpc.c), and simple-rpc (xml_to_dandarpc.c) which is + * proprietary, but imho more readable, and nice for proprietary legacy reasons. + * - Various output options, including: xml escaping via CDATA or entity, case folding, + * vocab version, and character encoding. + * - One to One mapping between C structures and actual values, unlike ensor which forces + * one to understand the arcana of the xmlrpc vocab. + * - support for mixed indexed/keyed vector types, making it more compatible with + * languages such as PHP. + * - quite speedy compared to implementations written in interpreted languages. Also, uses + * intelligent string handling, so not many strlen() calls, etc. + * - comprehensive API for manipulation of values + *******/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <time.h> + +#include "queue.h" +#include "xmlrpc.h" +#include "expat.h" +#include "base64.h" + +#include "xml_to_xmlrpc.h" +#include "xml_to_dandarpc.h" +#include "xml_element.h" +#include "xmlrpc_private.h" +#include "xmlrpc_introspection_private.h" + + + +/*-********************* +* Begin Time Functions * +***********************/ + +static int date_from_ISO8601(const char *text, time_t *value) +{ + struct tm tm; + int n; + int i; + time_t t; + + tm.tm_isdst = -1; + + if(strlen(text) < 17) { + return -1; + } + + n = 1000; + tm.tm_year = 0; + for(i = 0; i < 4; i++) { + tm.tm_year += (text[i]-'0')*n; + n /= 10; + } + n = 10; + tm.tm_mon = 0; + for(i = 0; i < 2; i++) { + tm.tm_mon += (text[i+4]-'0')*n; + n /= 10; + } + tm.tm_mon --; + + n = 10; + tm.tm_mday = 0; + for(i = 0; i < 2; i++) { + tm.tm_mday += (text[i+6]-'0')*n; + n /= 10; + } + + n = 10; + tm.tm_hour = 0; + for(i = 0; i < 2; i++) { + tm.tm_hour += (text[i+9]-'0')*n; + n /= 10; + } + + n = 10; + tm.tm_min = 0; + for(i = 0; i < 2; i++) { + tm.tm_min += (text[i+12]-'0')*n; + n /= 10; + } + + n = 10; + tm.tm_sec = 0; + for(i = 0; i < 2; i++) { + tm.tm_sec += (text[i+15]-'0')*n; + n /= 10; + } + + tm.tm_year -= 1900; + + *value = mktime(&tm); + + return 0; + +} + +static int date_to_ISO8601(time_t value, char *buf, int length) +{ + struct tm *tm; + tm = localtime(&value); + + return strftime(buf, length, "%Y%m%dT%H:%M:%S", tm); +} + +/*-******************* +* End Time Functions * +*********************/ + + +/*-*************************** +* Begin XMLRPC_REQUEST funcs * +*****************************/ + +/****f* REQUEST/XMLRPC_RequestNew + * NAME + * XMLRPC_RequestNew + * SYNOPSIS + * XMLRPC_REQUEST XMLRPC_RequestNew() + * FUNCTION + * Creates a new XMLRPC_Request data struct + * INPUTS + * none + * SEE ALSO + * XMLRPC_RequestFree () + * SOURCE + */ +XMLRPC_REQUEST XMLRPC_RequestNew() { + XMLRPC_REQUEST xRequest = calloc(1, sizeof(STRUCT_XMLRPC_REQUEST)); + if(xRequest) { + simplestring_init(&xRequest->methodName); + } + return xRequest; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestFree + * NAME + * XMLRPC_RequestFree + * SYNOPSIS + * void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) + * FUNCTION + * Free XMLRPC Request and all sub-values + * INPUTS + * request -- previously allocated request struct + * bFreeIO -- 1 = also free request value data, if any, 0 = ignore. + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_CleanupValue () + * SOURCE + */ +void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) { + if(request) { + simplestring_free(&request->methodName); + + if(request->io && bFreeIO) { + XMLRPC_CleanupValue(request->io); + } + if(request->error) { + XMLRPC_CleanupValue(request->error); + } + my_free(request); + } +} +/*******/ + +/* Set Method Name to call */ +/****f* REQUEST/XMLRPC_RequestSetMethodName + * NAME + * XMLRPC_RequestSetMethodName + * SYNOPSIS + * const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName) + * FUNCTION + * Set name of method to call with this request. + * INPUTS + * request -- previously allocated request struct + * methodName -- name of method + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestGetMethodName () + * XMLRPC_RequestFree () + * SOURCE + */ +const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName) { + if(request) { + simplestring_clear(&request->methodName); + simplestring_add(&request->methodName, methodName); + return request->methodName.str; + } + return NULL; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestGetMethodName + * NAME + * XMLRPC_RequestGetMethodName + * SYNOPSIS + * const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) + * FUNCTION + * Get name of method called by this request + * INPUTS + * request -- previously allocated request struct + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestSetMethodName () + * XMLRPC_RequestFree () + * SOURCE + */ +const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) { + return request ? request->methodName.str : NULL; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestSetRequestType + * NAME + * XMLRPC_RequestSetRequestType + * SYNOPSIS + * XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) + * FUNCTION + * A request struct may be allocated by a caller or by xmlrpc + * in response to a request. This allows setting the + * request type. + * INPUTS + * request -- previously allocated request struct + * type -- request type [xmlrpc_method_call | xmlrpc_method_response] + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestGetRequestType () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST_TYPE + * SOURCE + */ +XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) { + if(request) { + request->request_type = type; + return request->request_type; + } + return xmlrpc_request_none; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestGetRequestType + * NAME + * XMLRPC_RequestGetRequestType + * SYNOPSIS + * XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) + * FUNCTION + * A request struct may be allocated by a caller or by xmlrpc + * in response to a request. This allows setting the + * request type. + * INPUTS + * request -- previously allocated request struct + * RESULT + * type -- request type [xmlrpc_method_call | xmlrpc_method_response] + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestSetRequestType () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST_TYPE + * SOURCE + */ +XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) { + return request ? request->request_type : xmlrpc_request_none; +} +/*******/ + + +/****f* REQUEST/XMLRPC_RequestSetData + * NAME + * XMLRPC_RequestSetData + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) + * FUNCTION + * Associates a block of xmlrpc data with the request. The + * data is *not* copied. A pointer is kept. The caller + * should be careful not to doubly free the data value, + * which may optionally be free'd by XMLRPC_RequestFree(). + * INPUTS + * request -- previously allocated request struct + * data -- previously allocated data struct + * RESULT + * XMLRPC_VALUE -- pointer to value stored, or NULL + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestGetData () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) { + if(request && data) { + request->io = XMLRPC_CopyValue(data); + return request->io; + } + return NULL; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestGetData + * NAME + * XMLRPC_RequestGetData + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) + * FUNCTION + * Returns data associated with request, if any. + * INPUTS + * request -- previously allocated request struct + * RESULT + * XMLRPC_VALUE -- pointer to value stored, or NULL + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestSetData () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) { + return request ? request->io : NULL; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestSetOutputOptions + * NAME + * XMLRPC_RequestSetOutputOptions + * SYNOPSIS + * XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output) + * FUNCTION + * Sets output options used for generating XML. The output struct + * is copied, and may be freed by the caller. + * INPUTS + * request -- previously allocated request struct + * output -- output options struct initialized by caller + * RESULT + * XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to value stored, or NULL + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestGetOutputOptions () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST + * XMLRPC_REQUEST_OUTPUT_OPTIONS + * SOURCE + */ +XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output) { + if(request && output) { + memcpy(&request->output, output, sizeof(STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS)); + return &request->output; + } + return NULL; +} +/*******/ + + +/****f* REQUEST/XMLRPC_RequestGetOutputOptions + * NAME + * XMLRPC_RequestGetOutputOptions + * SYNOPSIS + * XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) + * FUNCTION + * Gets a pointer to output options used for generating XML. + * INPUTS + * request -- previously allocated request struct + * RESULT + * XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to options stored, or NULL + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestSetOutputOptions () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST + * XMLRPC_REQUEST_OUTPUT_OPTIONS + * SOURCE + */ +XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) { + return request ? &request->output : NULL; +} +/*******/ + +/*-************************* +* End XMLRPC_REQUEST funcs * +***************************/ + + +/*-*************************** +* Begin Serializiation funcs * +*****************************/ + +/****f* SERIALIZE/XMLRPC_VALUE_ToXML + * NAME + * XMLRPC_VALUE_ToXML + * SYNOPSIS + * char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val) + * FUNCTION + * encode XMLRPC_VALUE into XML buffer. Note that the generated + * buffer will not contain a methodCall. + * INPUTS + * val -- previously allocated XMLRPC_VALUE + * buf_len -- length of returned buffer, if not null + * RESULT + * char* -- newly allocated buffer containing XML. + * It is the caller's responsibility to free it. + * SEE ALSO + * XMLRPC_REQUEST_ToXML () + * XMLRPC_VALUE_FromXML () + * XMLRPC_Free () + * XMLRPC_VALUE + * SOURCE + */ +char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len) { + xml_element *root_elem = XMLRPC_VALUE_to_xml_element(val); + char* pRet = NULL; + + if(root_elem) { + pRet = xml_elem_serialize_to_string(root_elem, NULL, buf_len); + xml_elem_free(root_elem); + } + return pRet; +} +/*******/ + +/****f* SERIALIZE/XMLRPC_REQUEST_ToXML + * NAME + * XMLRPC_REQUEST_ToXML + * SYNOPSIS + * char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request) + * FUNCTION + * encode XMLRPC_REQUEST into XML buffer + * INPUTS + * request -- previously allocated XMLRPC_REQUEST + * buf_len -- size of returned buf, if not null + * RESULT + * char* -- newly allocated buffer containing XML. + * It is the caller's responsibility to free it. + * SEE ALSO + * XMLRPC_REQUEST_ToXML () + * XMLRPC_REQUEST_FromXML () + * XMLRPC_Free () + * XMLRPC_VALUE_ToXML () + * XMLRPC_REQUEST + * SOURCE + */ +char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int* buf_len) { + if(request) { + xml_element *root_elem = (request->output.version == xmlrpc_version_simple) ? + DANDARPC_REQUEST_to_xml_element(request) : + XMLRPC_REQUEST_to_xml_element(request); + char* pRet = NULL; + + if(root_elem) { + pRet = xml_elem_serialize_to_string(root_elem, &request->output.xml_elem_opts, buf_len); + xml_elem_free(root_elem); + } + return pRet; + } +} +/*******/ + +/****f* SERIALIZE/XMLRPC_VALUE_FromXML + * NAME + * XMLRPC_VALUE_FromXML + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int le + * FUNCTION + * Retrieve XMLRPC_VALUE from XML buffer. Note that this will + * ignore any methodCall. See XMLRPC_REQUEST_FromXML + * INPUTS + * in_buf -- character buffer containing XML + * len -- length of buffer + * RESULT + * XMLRPC_VALUE -- newly allocated data, or NULL if error. Should + * be free'd by caller. + * SEE ALSO + * XMLRPC_VALUE_ToXML () + * XMLRPC_REQUEST_FromXML () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) +{ + XMLRPC_VALUE xResponse = NULL; + XMLRPC_REQUEST req = XMLRPC_REQUEST_FromXML(in_buf, len, in_options); + + if(req) { + xResponse = req->io; + XMLRPC_RequestFree(req, 0); + } + return xResponse; +} +/*******/ + +/* map parser errors to standard xml-rpc errors */ +static XMLRPC_VALUE map_expat_errors(XML_ELEM_ERROR error) { + XMLRPC_VALUE xReturn = NULL; + if(error) { + XMLRPC_ERROR_CODE code; + char buf[1024]; + snprintf(buf, sizeof(buf), + "error occurred at line %i, column %i, byte index %i", + error->line, error->column, + error->byte_index); + + /* expat specific errors */ + switch(error->parser_code) { + case XML_ERROR_UNKNOWN_ENCODING: + code = xmlrpc_error_parse_unknown_encoding; + break; + case XML_ERROR_INCORRECT_ENCODING: + code = xmlrpc_error_parse_bad_encoding; + break; + default: + code = xmlrpc_error_parse_xml_syntax; + break; + } + xReturn = XMLRPC_UtilityCreateFault(code, buf); + } + return xReturn; +} + +/****f* SERIALIZE/XMLRPC_REQUEST_FromXML + * NAME + * XMLRPC_REQUEST_FromXML + * SYNOPSIS + * XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int le + * FUNCTION + * Retrieve XMLRPC_REQUEST from XML buffer + * INPUTS + * in_buf -- character buffer containing XML + * len -- length of buffer + * RESULT + * XMLRPC_REQUEST -- newly allocated data, or NULL if error. Should + * be free'd by caller. + * SEE ALSO + * XMLRPC_REQUEST_ToXML () + * XMLRPC_VALUE_FromXML () + * XMLRPC_REQUEST + * SOURCE + */ +XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) +{ + XMLRPC_REQUEST request = XMLRPC_RequestNew(); + STRUCT_XML_ELEM_ERROR error = {0}; + + if(request) { + xml_element *root_elem = xml_elem_parse_buf(in_buf, len, (in_options ? &in_options->xml_elem_opts : NULL), &error); + + if(root_elem) { + if(!strcmp(root_elem->name, "simpleRPC")) { + request->output.version = xmlrpc_version_simple; + xml_element_to_DANDARPC_REQUEST(request, root_elem); + } + else { + request->output.version = xmlrpc_version_1_0; + xml_element_to_XMLRPC_REQUEST(request, root_elem); + } + xml_elem_free(root_elem); + } + else { + if(error.parser_error) { + request->error = map_expat_errors(&error); + } + } + } + + return request; +} +/*******/ + +/*-************************ +* End Serialization Funcs * +**************************/ + + + +/****f* VALUE/XMLRPC_CreateValueEmpty + * NAME + * XMLRPC_CreateValueEmpty + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueEmpty () + * FUNCTION + * Create an XML value to be used/modified elsewhere. + * INPUTS + * RESULT + * XMLRPC_VALUE. The new value, or NULL on failure. + * SEE ALSO + * XMLRPC_CleanupValue () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueEmpty() { + XMLRPC_VALUE v = calloc(1, sizeof(STRUCT_XMLRPC_VALUE)); + if(v) { + v->type = xmlrpc_empty; + simplestring_init(&v->id); + simplestring_init(&v->str); + } + return v; +} + +static const char* get_string(const char* buf, int bDup) { + if(bDup) { + return strdup(buf); + } + return buf; +} +/*******/ + +/****f* VALUE/XMLRPC_SetValueID_Case + * NAME + * XMLRPC_SetValueID_Case + * SYNOPSIS + * const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case) + * FUNCTION + * Assign an ID (key) to an XMLRPC value. + * INPUTS + * value The xml value who's ID we will set. + * id The desired new id. + * len length of id string if known, or 0 if unknown. + * id_case one of XMLRPC_CASE + * RESULT + * const char* pointer to the newly allocated id string, or NULL + * SEE ALSO + * XMLRPC_SetValueID () + * XMLRPC_GetValueID () + * XMLRPC_VALUE + * XMLRPC_CASE + * SOURCE + */ +const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case) { + const char* pRetval = NULL; + if(value) { + if(id) { + simplestring_clear(&value->id); + (len > 0) ? simplestring_addn(&value->id, id, len) : + simplestring_add(&value->id, id); + + /* upper or lower case string in place if required. could be a seperate func. */ + if(id_case == xmlrpc_case_lower || id_case == xmlrpc_case_upper) { + int i; + for(i = 0; i < value->id.len; i++) { + value->id.str[i] = (id_case == xmlrpc_case_lower) ? tolower(value->id.str[i]) : toupper(value->id.str[i]); + } + } + + pRetval = value->id.str; + +#ifdef XMLRPC_DEBUG_REFCOUNT + printf("set value id: %s\n", pRetval); +#endif + } + } + + return pRetval; +} +/*******/ + + +/****f* VALUE/XMLRPC_SetValueString + * NAME + * XMLRPC_SetValueString + * SYNOPSIS + * const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) + * FUNCTION + * Assign a string value to an XMLRPC_VALUE, and set it to type xmlrpc_string + * INPUTS + * value The xml value who's ID we will set. + * val The desired new string val. + * len length of val string if known, or 0 if unknown. + * RESULT + * const char* pointer to the newly allocated value string, or NULL + * SEE ALSO + * XMLRPC_GetValueString () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) { + char *pRetval = NULL; + if(value && val) { + simplestring_clear(&value->str); + (len > 0) ? simplestring_addn(&value->str, val, len) : + simplestring_add(&value->str, val); + value->type = xmlrpc_string; + pRetval = (char *)value->str.str; + } + + return pRetval; +} +/*******/ + +/****f* VALUE/XMLRPC_SetValueInt + * NAME + * XMLRPC_SetValueInt + * SYNOPSIS + * void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) + * FUNCTION + * Assign an int value to an XMLRPC_VALUE, and set it to type xmlrpc_int + * INPUTS + * value The xml value who's ID we will set. + * val The desired new integer value + * RESULT + * SEE ALSO + * XMLRPC_GetValueInt () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) { + if(value) { + value->type = xmlrpc_int; + value->i = val; + } +} +/*******/ + +/****f* VALUE/XMLRPC_SetValueBoolean + * NAME + * XMLRPC_SetValueBoolean + * SYNOPSIS + * void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) + * FUNCTION + * Assign a boolean value to an XMLRPC_VALUE, and set it to type xmlrpc_boolean + * INPUTS + * value The xml value who's value we will set. + * val The desired new boolean value. [0 | 1] + * RESULT + * SEE ALSO + * XMLRPC_GetValueBoolean () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) { + if(value) { + value->type = xmlrpc_boolean; + value->i = val ? 1 : 0; + } +} +/*******/ + + +/****f* VECTOR/XMLRPC_SetIsVector + * NAME + * XMLRPC_SetIsVector + * SYNOPSIS + * int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) + * FUNCTION + * Set the XMLRPC_VALUE to be a vector (list) type. The vector may be one of + * [xmlrpc_array | xmlrpc_struct | xmlrpc_mixed]. An array has only index values. + * A struct has key/val pairs. Mixed allows both index and key/val combinations. + * INPUTS + * value The xml value who's vector type we will set + * type New type of vector as enumerated by XMLRPC_VECTOR_TYPE + * RESULT + * int 1 if successful, 0 otherwise + * SEE ALSO + * XMLRPC_GetValueType () + * XMLRPC_GetVectorType () + * XMLRPC_VALUE + * XMLRPC_VECTOR_TYPE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) { + int bSuccess = 0; + + if(value && value->type != xmlrpc_vector) { + value->v = calloc(1, sizeof(STRUCT_XMLRPC_VECTOR)); + if(value->v) { + value->v->q = (queue*)malloc(sizeof(queue)); + if(value->v->q) { + Q_Init(value->v->q); + value->v->type = type; + value->type = xmlrpc_vector; + bSuccess = 1; + } + } + } + + return bSuccess; +} +/*******/ + +/****f* VECTOR/XMLRPC_CreateVector + * NAME + * XMLRPC_CreateVector + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) + * FUNCTION + * Create a new vector and optionally set an id. + * INPUTS + * id The id of the vector, or NULL + * type New type of vector as enumerated by XMLRPC_VECTOR_TYPE + * RESULT + * XMLRPC_VALUE The new vector, or NULL on failure. + * SEE ALSO + * XMLRPC_CreateValueEmpty () + * XMLRPC_SetIsVector () + * XMLRPC_GetValueType () + * XMLRPC_GetVectorType () + * XMLRPC_VALUE + * XMLRPC_VECTOR_TYPE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) { + XMLRPC_VALUE val = NULL; + + val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_VECTOR *pSIV = NULL; + + if(XMLRPC_SetIsVector(val, type)) { + if(id) { + const char *pSVI = NULL; + + pSVI = XMLRPC_SetValueID(val, id, 0); + if(NULL == pSVI) { + val = NULL; + } + } + } + else { + val = NULL; + } + } + return val; +} +/*******/ + + +/* Not yet implemented. + * + * This should use a hash to determine if a given target id has already + * been appended. + * + * Alternately, it could walk the entire vector, but that could be quite + * slow for very large lists. + */ +static int isDuplicateEntry(XMLRPC_VALUE target, XMLRPC_VALUE source) { + return 0; +} + +/****f* VECTOR/XMLRPC_AddValueToVector + * NAME + * XMLRPC_AddValueToVector + * SYNOPSIS + * int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) + * FUNCTION + * Add (append) an existing XMLRPC_VALUE to a vector. + * INPUTS + * target The target vector + * source The source value to append + * RESULT + * int 1 if successful, else 0 + * SEE ALSO + * XMLRPC_AddValuesToVector () + * XMLRPC_VectorGetValueWithID_Case () + * XMLRPC_VALUE + * NOTES + * The function will fail and return 0 if an attempt is made to add + * a value with an ID into a vector of type xmlrpc_vector_array. Such + * values can only be added to xmlrpc_vector_struct. + * SOURCE + */ +int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) { + if(target && source) { + if(target->type == xmlrpc_vector && target->v && + target->v->q && target->v->type != xmlrpc_vector_none) { + + /* guard against putting value of unknown type into vector */ + switch(source->type) { + case xmlrpc_empty: + case xmlrpc_base64: + case xmlrpc_boolean: + case xmlrpc_datetime: + case xmlrpc_double: + case xmlrpc_int: + case xmlrpc_string: + case xmlrpc_vector: + /* Guard against putting a key/val pair into an array vector */ + if( !(source->id.len && target->v->type == xmlrpc_vector_array) ) { + if(isDuplicateEntry(target, source) || Q_PushTail(target->v->q, XMLRPC_CopyValue(source))) { + return 1; + } + } + else { + fprintf(stderr, "xmlrpc: attempted to add key/val pair to vector of type array\n"); + } + break; + default: + fprintf(stderr, "xmlrpc: attempted to add value of unknown type to vector\n"); + break; + } + } + } + return 0; +} +/*******/ + + +/****f* VECTOR/XMLRPC_AddValuesToVector + * NAME + * XMLRPC_AddValuesToVector + * SYNOPSIS + * XMLRPC_AddValuesToVector ( target, val1, val2, val3, val(n), 0 ) + * XMLRPC_AddValuesToVector( XMLRPC_VALUE, ... ) + * FUNCTION + * Add (append) a series of existing XMLRPC_VALUE to a vector. + * INPUTS + * target The target vector + * ... The source value(s) to append. The last item *must* be 0. + * RESULT + * int 1 if successful, else 0 + * SEE ALSO + * XMLRPC_AddValuesToVector () + * XMLRPC_VectorGetValueWithID_Case () + * XMLRPC_VALUE + * NOTES + * This function may actually return failure after it has already modified + * or added items to target. You can not trust the state of target + * if this function returns failure. + * SOURCE + */ +int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) { + int iRetval = 0; + + if(target) { + if(target->type == xmlrpc_vector) { + XMLRPC_VALUE v = NULL; + va_list vl; + + va_start(vl, target); + + do { + v = va_arg(vl, XMLRPC_VALUE); + if(v) { + if(!XMLRPC_AddValueToVector(target, v)) { + iRetval = 0; + break; + } + } + } while(v); + + va_end(vl); + + if(NULL == v) { + iRetval = 1; + } + } + } + return iRetval; +} +/*******/ + + +/****f* VECTOR/XMLRPC_VectorGetValueWithID_Case + * NAME + * XMLRPC_VectorGetValueWithID_Case + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) + * FUNCTION + * Get value from vector matching id (key) + * INPUTS + * vector The source vector + * id The key to find + * id_case Rule for how to match key + * RESULT + * int 1 if successful, else 0 + * SEE ALSO + * XMLRPC_SetValueID_Case () + * XMLRPC_VALUE + * XMLRPC_CASE_COMPARISON + * SOURCE + */ +XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) { + if(vector && vector->v && vector->v->q) { + q_iter qi = Q_Iter_Head_F(vector->v->q); + + while(qi) { + XMLRPC_VALUE xIter = Q_Iter_Get_F(qi); + if(xIter && xIter->id.str) { + if(id_case == xmlrpc_case_sensitive) { + if(!strcmp(xIter->id.str, id)) { + return xIter; + } + } + else if(id_case == xmlrpc_case_insensitive) { + if(!strcasecmp(xIter->id.str, id)) { + return xIter; + } + } + } + qi = Q_Iter_Next_F(qi); + } + } + return NULL; +} +/*******/ + + +int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value) { + if(vector && vector->v && vector->v->q && value) { + q_iter qi = Q_Iter_Head_F(vector->v->q); + + while(qi) { + XMLRPC_VALUE xIter = Q_Iter_Get_F(qi); + if(xIter == value) { + XMLRPC_CleanupValue(xIter); + Q_Iter_Del(vector->v->q, qi); + return 1; + } + qi = Q_Iter_Next_F(qi); + } + } + return 0; +} + + +/****f* VALUE/XMLRPC_CreateValueString + * NAME + * XMLRPC_CreateValueString + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) + * FUNCTION + * Create an XMLRPC_VALUE, and assign a string to it + * INPUTS + * id The id of the value, or NULL + * val The desired new string val. + * len length of val string if known, or 0 if unknown. + * RESULT + * newly allocated XMLRPC_VALUE, or NULL + * SEE ALSO + * XMLRPC_GetValueString () + * XMLRPC_CreateValueEmpty () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) { + XMLRPC_VALUE value = NULL; + if(val) { + value = XMLRPC_CreateValueEmpty(); + if(value) { + XMLRPC_SetValueString(value, val, len); + if(id) { + XMLRPC_SetValueID(value, id, 0); + } + } + } + return value; +} +/*******/ + +/****f* VALUE/XMLRPC_CreateValueInt + * NAME + * XMLRPC_CreateValueInt + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) + * FUNCTION + * Create an XMLRPC_VALUE, and assign an int to it + * INPUTS + * id The id of the value, or NULL + * i The desired new int val. + * RESULT + * newly allocated XMLRPC_VALUE, or NULL + * SEE ALSO + * XMLRPC_GetValueInt () + * XMLRPC_CreateValueEmpty () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueInt(val, i); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + +/****f* VALUE/XMLRPC_CreateValueBoolean + * NAME + * XMLRPC_CreateValueBoolean + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) + * FUNCTION + * Create an XMLRPC_VALUE, and assign an int to it + * INPUTS + * id The id of the value, or NULL + * i The desired new int val. + * RESULT + * newly allocated XMLRPC_VALUE, or NULL + * SEE ALSO + * XMLRPC_GetValueBoolean () + * XMLRPC_CreateValueEmpty () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueBoolean(val, i); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + + +/****f* VALUE/XMLRPC_CleanupValue + * NAME + * XMLRPC_CleanupValue + * SYNOPSIS + * void XMLRPC_CleanupValue(XMLRPC_VALUE value) + * FUNCTION + * Frees all memory allocated for an XMLRPC_VALUE and any of its children (if a vector) + * INPUTS + * value The id of the value to be cleaned up. + * RESULT + * void + * NOTES + * Normally this function will be called for the topmost vector, thus free-ing + * all children. If a child of a vector is free'd first, results are undefined. + * Failure to call this function *will* cause memory leaks. + * + * Also, this function is implemented using reference counting. Thus a value + * may be added and freed from multiple parents so long as a reference is added + * first using XMLRPC_CopyValue() + * SEE ALSO + * XMLRPC_RequestFree () + * XMLRPC_CreateValueEmpty () + * XMLRPC_CopyValue() + * XMLRPC_VALUE + * SOURCE + */ +void XMLRPC_CleanupValue(XMLRPC_VALUE value) { + if(value) { + if(value->iRefCount > 0) { + value->iRefCount --; + } + +#ifdef XMLRPC_DEBUG_REFCOUNT + if(value->id.str) { + printf("decremented refcount of %s, now %i\n", value->id.str, value->iRefCount); + } + else { + printf("decremented refcount of 0x%x, now %i\n", value, value->iRefCount); + } +#endif + + if(value->type == xmlrpc_vector) { + if(value->v) { + if(value->iRefCount == 0) { + XMLRPC_VALUE cur = (XMLRPC_VALUE)Q_Head(value->v->q); + while( cur ) { + XMLRPC_CleanupValue(cur); + + /* Make sure some idiot didn't include a vector as a child of itself + * and thus it would have already free'd these. + */ + if(value->v && value->v->q) { + cur = Q_Next(value->v->q); + } + else { + break; + } + } + + Q_Destroy(value->v->q); + my_free(value->v->q); + my_free(value->v); + } + } + } + + + if(value->iRefCount == 0) { + + /* guard against freeing invalid types */ + switch(value->type) { + case xmlrpc_empty: + case xmlrpc_base64: + case xmlrpc_boolean: + case xmlrpc_datetime: + case xmlrpc_double: + case xmlrpc_int: + case xmlrpc_string: + case xmlrpc_vector: +#ifdef XMLRPC_DEBUG_REFCOUNT + if(value->id.str) { + printf("free'd %s\n", value->id.str); + } + else { + printf("free'd 0x%x\n", value); + } +#endif + simplestring_free(&value->id); + simplestring_free(&value->str); + + memset(value, 0, sizeof(STRUCT_XMLRPC_VALUE)); + my_free(value); + break; + default: + fprintf(stderr, "xmlrpc: attempted to free value of invalid type\n"); + break; + } + } + } +} +/*******/ + + +/****f* VALUE/XMLRPC_SetValueDateTime + * NAME + * XMLRPC_SetValueDateTime + * SYNOPSIS + * void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) + * FUNCTION + * Assign time value to XMLRPC_VALUE + * INPUTS + * value The target XMLRPC_VALUE + * time The desired new unix time value (time_t) + * RESULT + * void + * SEE ALSO + * XMLRPC_GetValueDateTime () + * XMLRPC_SetValueDateTime_ISO8601 () + * XMLRPC_CreateValueDateTime () + * XMLRPC_VALUE + * SOURCE + */ +void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) { + if(value) { + char timeBuf[30]; + value->type = xmlrpc_datetime; + value->i = time; + + timeBuf[0] = 0; + + date_to_ISO8601(time, timeBuf, sizeof(timeBuf)); + + if(timeBuf[0]) { + simplestring_clear(&value->str); + simplestring_add(&value->str, timeBuf); + } + } +} +/*******/ + +/****f* VALUE/XMLRPC_CopyValue + * NAME + * XMLRPC_CopyValue + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) + * FUNCTION + * Make a copy of an XMLRPC_VALUE + * INPUTS + * value The target XMLRPC_VALUE + * RESULT + * XMLRPC_VALUE -- address of the copy + * SEE ALSO + * XMLRPC_CleanupValue () + * NOTES + * This function is implemented via reference counting, so the + * returned value is going to be the same as the passed in value. + * The value must be freed the same number of times it is copied + * or there will be a memory leak. + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) { + if(value) { + value->iRefCount ++; +#ifdef XMLRPC_DEBUG_REFCOUNT + if(value->id.str) { + printf("incremented refcount of %s\n", value->id.str); + } +#endif + } + return value; +} +/*******/ + + + +/****f* VALUE/XMLRPC_CreateValueDateTime + * NAME + * XMLRPC_CreateValueDateTime + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) + * FUNCTION + * Create new datetime value from time_t + * INPUTS + * id id of the new value, or NULL + * time The desired unix time value (time_t) + * RESULT + * void + * SEE ALSO + * XMLRPC_GetValueDateTime () + * XMLRPC_SetValueDateTime () + * XMLRPC_CreateValueDateTime_ISO8601 () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueDateTime(val, time); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + + +/****f* VALUE/XMLRPC_SetValueDateTime_ISO8601 + * NAME + * XMLRPC_SetValueDateTime_ISO8601 + * SYNOPSIS + * void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) + * FUNCTION + * Set datetime value from IS08601 encoded string + * INPUTS + * value The target XMLRPC_VALUE + * s The desired new time value + * RESULT + * void + * BUGS + * This function currently attempts to convert the time string to a valid unix time + * value before passing it. Behavior when the string is invalid or out of range + * is not well defined, but will probably result in Jan 1, 1970 (0) being passed. + * SEE ALSO + * XMLRPC_GetValueDateTime_ISO8601 () + * XMLRPC_CreateValueDateTime_ISO8601 () + * XMLRPC_CreateValueDateTime () + * XMLRPC_VALUE + * SOURCE + */ +void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) { + if(value) { + time_t time_val = 0; + if(s) { + date_from_ISO8601(s, &time_val); + XMLRPC_SetValueDateTime(value, time_val); + } + } +} +/*******/ + +/****f* VALUE/XMLRPC_CreateValueDateTime_ISO8601 + * NAME + * XMLRPC_CreateValueDateTime_ISO8601 + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) + * FUNCTION + * Create datetime value from IS08601 encoded string + * INPUTS + * id The id of the new value, or NULL + * s The desired new time value + * RESULT + * newly allocated XMLRPC_VALUE, or NULL if no value created. + * BUGS + * See XMLRPC_SetValueDateTime_ISO8601 () + * SEE ALSO + * XMLRPC_GetValueDateTime_ISO8601 () + * XMLRPC_SetValueDateTime_ISO8601 () + * XMLRPC_CreateValueDateTime () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueDateTime_ISO8601(val, s); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + + +/****f* VALUE/XMLRPC_SetValueBase64 + * NAME + * XMLRPC_SetValueBase64 + * SYNOPSIS + * void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) + * FUNCTION + * Set base64 value. Base64 is useful for transferring binary data, such as an image. + * INPUTS + * value The target XMLRPC_VALUE + * s The desired new binary value + * len The length of s, or NULL. If buffer is not null terminated, len *must* be passed. + * RESULT + * void + * NOTES + * Data is set/stored/retrieved as passed in, but is base64 encoded for XML transfer, and + * decoded on the other side. This is transparent to the caller. + * SEE ALSO + * XMLRPC_GetValueBase64 () + * XMLRPC_CreateValueBase64 () + * XMLRPC_VALUE + * SOURCE + */ +void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) { + if(value && s) { + simplestring_clear(&value->str); + (len > 0) ? simplestring_addn(&value->str, s, len) : + simplestring_add(&value->str, s); + value->type = xmlrpc_base64; + } +} +/*******/ + + +/****f* VALUE/XMLRPC_CreateValueBase64 + * NAME + * XMLRPC_CreateValueBase64 + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) + * FUNCTION + * Create base64 value. Base64 is useful for transferring binary data, such as an image. + * INPUTS + * id id of the new value, or NULL + * s The desired new binary value + * len The length of s, or NULL. If buffer is not null terminated, len *must* be passed. + * RESULT + * newly allocated XMLRPC_VALUE, or NULL if error + * NOTES + * See XMLRPC_SetValueBase64 () + * SEE ALSO + * XMLRPC_GetValueBase64 () + * XMLRPC_SetValueBase64 () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueBase64(val, s, len); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + +/****f* VALUE/XMLRPC_SetValueDouble + * NAME + * XMLRPC_SetValueDouble + * SYNOPSIS + * void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) + * FUNCTION + * Set double (floating point) value. + * INPUTS + * value The target XMLRPC_VALUE + * val The desired new double value + * RESULT + * void + * SEE ALSO + * XMLRPC_GetValueDouble () + * XMLRPC_CreateValueDouble () + * XMLRPC_VALUE + * SOURCE + */ +void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) { + if(value) { + value->type = xmlrpc_double; + value->d = val; + } +} +/*******/ + +/****f* VALUE/XMLRPC_CreateValueDouble + * NAME + * XMLRPC_CreateValueDouble + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) + * FUNCTION + * Create double (floating point) value. + * INPUTS + * id id of the newly created value, or NULL + * d The desired new double value + * RESULT + * void + * SEE ALSO + * XMLRPC_GetValueDouble () + * XMLRPC_CreateValueDouble () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueDouble(val, d); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueString + * NAME + * XMLRPC_GetValueString + * SYNOPSIS + * const char* XMLRPC_GetValueString(XMLRPC_VALUE value) + * FUNCTION + * retrieve string value + * INPUTS + * value source XMLRPC_VALUE of type xmlrpc_string + * RESULT + * void + * SEE ALSO + * XMLRPC_SetValueString () + * XMLRPC_GetValueType () + * XMLRPC_VALUE + * SOURCE + */ +const char* XMLRPC_GetValueString(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_string) ? value->str.str : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueStringLen + * NAME + * XMLRPC_GetValueStringLen + * SYNOPSIS + * int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) + * FUNCTION + * determine length of string value + * INPUTS + * value XMLRPC_VALUE of type xmlrpc_string + * RESULT + * length of string, or 0 + * NOTES + * SEE ALSO + * XMLRPC_SetValueString () + * XMLRPC_GetValueString () + * SOURCE + */ +int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) { + return ((value) ? value->str.len : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueInt + * NAME + * XMLRPC_GetValueInt + * SYNOPSIS + * int XMLRPC_GetValueInt(XMLRPC_VALUE value) + * FUNCTION + * retrieve integer value. + * INPUTS + * value XMLRPC_VALUE of type xmlrpc_int + * RESULT + * integer value or 0 if value is not valid int + * NOTES + * use XMLRPC_GetValueType () to be sure if 0 is real return value or not + * SEE ALSO + * XMLRPC_SetValueInt () + * XMLRPC_CreateValueInt () + * SOURCE + */ +int XMLRPC_GetValueInt(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_int) ? value->i : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueBoolean + * NAME + * XMLRPC_GetValueBoolean + * SYNOPSIS + * int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) + * FUNCTION + * retrieve boolean value. + * INPUTS + * XMLRPC_VALUE of type xmlrpc_boolean + * RESULT + * boolean value or 0 if value is not valid boolean + * NOTES + * use XMLRPC_GetValueType() to be sure if 0 is real value or not + * SEE ALSO + * XMLRPC_SetValueBoolean () + * XMLRPC_CreateValueBoolean () + * SOURCE + */ +int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_boolean) ? value->i : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueDouble + * NAME + * XMLRPC_GetValueDouble + * SYNOPSIS + * double XMLRPC_GetValueDouble(XMLRPC_VALUE value) + * FUNCTION + * retrieve double value + * INPUTS + * XMLRPC_VALUE of type xmlrpc_double + * RESULT + * double value or 0 if value is not valid double. + * NOTES + * use XMLRPC_GetValueType() to be sure if 0 is real value or not + * SEE ALSO + * XMLRPC_SetValueDouble () + * XMLRPC_CreateValueDouble () + * SOURCE + */ +double XMLRPC_GetValueDouble(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_double) ? value->d : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueBase64 + * NAME + * XMLRPC_GetValueBase64 + * SYNOPSIS + * const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) + * FUNCTION + * retrieve binary value + * INPUTS + * XMLRPC_VALUE of type xmlrpc_base64 + * RESULT + * pointer to binary value or 0 if value is not valid. + * SEE ALSO + * XMLRPC_SetValueBase64 () + * XMLRPC_CreateValueBase64 () + * NOTES + * Call XMLRPC_GetValueStringLen() to retrieve real length of binary data. strlen() + * will not be accurate, as returned data may contain embedded nulls. + * SOURCE + */ +const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_base64) ? value->str.str : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueDateTime + * NAME + * XMLRPC_GetValueDateTime + * SYNOPSIS + * time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) + * FUNCTION + * retrieve time_t value + * INPUTS + * XMLRPC_VALUE of type xmlrpc_datetime + * RESULT + * time_t value or 0 if value is not valid datetime. + * NOTES + * use XMLRPC_GetValueType() to be sure if 0 is real value or not + * SEE ALSO + * XMLRPC_SetValueDateTime () + * XMLRPC_GetValueDateTime_ISO8601 () + * XMLRPC_CreateValueDateTime () + * SOURCE + */ +time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) { + return (time_t)((value && value->type == xmlrpc_datetime) ? value->i : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueDateTime_IOS8601 + * NAME + * XMLRPC_GetValueDateTime_IOS8601 + * SYNOPSIS + * const char* XMLRPC_GetValueDateTime_IOS8601(XMLRPC_VALUE value) + * FUNCTION + * retrieve ISO8601 formatted time value + * INPUTS + * XMLRPC_VALUE of type xmlrpc_datetime + * RESULT + * const char* value or 0 if value is not valid datetime. + * SEE ALSO + * XMLRPC_SetValueDateTime_IOS8601 () + * XMLRPC_GetValueDateTime () + * XMLRPC_CreateValueDateTime_IOS8601 () + * SOURCE + */ +const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_datetime) ? value->str.str : 0); +} +/*******/ + +/* Get ID (key) of value or NULL */ +/****f* VALUE/XMLRPC_GetValueID + * NAME + * XMLRPC_GetValueID + * SYNOPSIS + * const char* XMLRPC_GetValueID(XMLRPC_VALUE value) + * FUNCTION + * retrieve id (key) of value + * INPUTS + * XMLRPC_VALUE of any type + * RESULT + * const char* pointer to id of value, or NULL + * NOTES + * SEE ALSO + * XMLRPC_SetValueID() + * XMLRPC_CreateValueEmpty() + * SOURCE + */ +const char* XMLRPC_GetValueID(XMLRPC_VALUE value) { + return (const char*)((value && value->id.len) ? value->id.str : 0); +} +/*******/ + + +/****f* VECTOR/XMLRPC_VectorSize + * NAME + * XMLRPC_VectorSize + * SYNOPSIS + * int XMLRPC_VectorSize(XMLRPC_VALUE value) + * FUNCTION + * retrieve size of vector + * INPUTS + * XMLRPC_VALUE of type xmlrpc_vector + * RESULT + * count of items in vector + * NOTES + * This is a cheap operation even on large vectors. Vector size is + * maintained by queue during add/remove ops. + * SEE ALSO + * XMLRPC_AddValueToVector () + * SOURCE + */ +int XMLRPC_VectorSize(XMLRPC_VALUE value) { + int size = 0; + if(value && value->type == xmlrpc_vector && value->v) { + size = Q_Size(value->v->q); + } + return size; +} +/*******/ + +/****f* VECTOR/XMLRPC_VectorRewind + * NAME + * XMLRPC_VectorRewind + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) + * FUNCTION + * reset vector to first item + * INPUTS + * XMLRPC_VALUE of type xmlrpc_vector + * RESULT + * first XMLRPC_VALUE in list, or NULL if empty or error. + * NOTES + * Be careful to rewind any vector passed in to you if you expect to + * iterate through the entire list. + * SEE ALSO + * XMLRPC_VectorNext () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) { + XMLRPC_VALUE xReturn = NULL; + if(value && value->type == xmlrpc_vector && value->v) { + xReturn = (XMLRPC_VALUE)Q_Head(value->v->q); + } + return xReturn; +} +/*******/ + +/****f* VECTOR/XMLRPC_VectorNext + * NAME + * XMLRPC_VectorNext + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) + * FUNCTION + * Iterate vector to next item in list. + * INPUTS + * XMLRPC_VALUE of type xmlrpc_vector + * RESULT + * Next XMLRPC_VALUE in vector, or NULL if at end. + * NOTES + * SEE ALSO + * XMLRPC_VectorRewind () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) { + XMLRPC_VALUE xReturn = NULL; + if(value && value->type == xmlrpc_vector && value->v) { + xReturn = (XMLRPC_VALUE)Q_Next(value->v->q); + } + return xReturn; +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueType + * NAME + * XMLRPC_GetValueType + * SYNOPSIS + * XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) + * FUNCTION + * determine data type of the XMLRPC_VALUE + * INPUTS + * XMLRPC_VALUE target of query + * RESULT + * data type of value as enumerated by XMLRPC_VALUE_TYPE + * NOTES + * all values are of type xmlrpc_empty until set. + * SEE ALSO + * XMLRPC_SetValue* + * XMLRPC_CreateValue* + * XMLRPC_Append* + * SOURCE + */ +XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) { + return value ? value->type : xmlrpc_empty; +} +/*******/ + +/* Vector type accessor */ +/****f* VALUE/XMLRPC_GetVectorType + * NAME + * XMLRPC_GetVectorType + * SYNOPSIS + * XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) + * FUNCTION + * determine vector type of the XMLRPC_VALUE + * INPUTS + * XMLRPC_VALUE of type xmlrpc_vector + * RESULT + * vector type of value as enumerated by XMLRPC_VECTOR_TYPE + * NOTES + * xmlrpc_none is returned if value is not a vector + * SEE ALSO + * XMLRPC_SetIsVector () + * XMLRPC_GetValueType () + * SOURCE + */ +XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) { + return(value && value->v) ? value->v->type : xmlrpc_none; +} +/*******/ + + +/*-******************* +* Begin Server Funcs * +*********************/ + + +/****f* VALUE/XMLRPC_ServerCreate + * NAME + * XMLRPC_ServerCreate + * SYNOPSIS + * XMLRPC_SERVER XMLRPC_ServerCreate() + * FUNCTION + * Allocate/Init XMLRPC Server Resources. + * INPUTS + * none + * RESULT + * newly allocated XMLRPC_SERVER + * NOTES + * SEE ALSO + * XMLRPC_ServerDestroy () + * XMLRPC_GetGlobalServer () + * SOURCE + */ +XMLRPC_SERVER XMLRPC_ServerCreate() { + XMLRPC_SERVER server = calloc(1, sizeof(STRUCT_XMLRPC_SERVER)); + if(server) { + Q_Init(&server->methodlist); + Q_Init(&server->docslist); + + /* register system methods */ + xsm_register(server); + } + return server; +} +/*******/ + +/* Return global server. Not locking! Not Thread Safe! */ +/****f* VALUE/XMLRPC_GetGlobalServer + * NAME + * XMLRPC_GetGlobalServer + * SYNOPSIS + * XMLRPC_SERVER XMLRPC_GetGlobalServer() + * FUNCTION + * Allocates a global (process-wide) server, or returns pointer if pre-existing. + * INPUTS + * none + * RESULT + * pointer to global server, or 0 if error. + * NOTES + * ***WARNING*** This function is not thread safe. It is included only for the very lazy. + * Multi-threaded programs that use this may experience problems. + * BUGS + * There is currently no way to cleanup the global server gracefully. + * SEE ALSO + * XMLRPC_ServerCreate () + * SOURCE + */ +XMLRPC_SERVER XMLRPC_GetGlobalServer() { + static XMLRPC_SERVER xsServer = 0; + if(!xsServer) { + xsServer = XMLRPC_ServerCreate(); + } + return xsServer; +} +/*******/ + +/****f* VALUE/XMLRPC_ServerDestroy + * NAME + * XMLRPC_ServerDestroy + * SYNOPSIS + * void XMLRPC_ServerDestroy(XMLRPC_SERVER server) + * FUNCTION + * Free Server Resources + * INPUTS + * server The server to be free'd + * RESULT + * void + * NOTES + * This frees the server struct and any methods that have been added. + * SEE ALSO + * XMLRPC_ServerCreate () + * SOURCE + */ +void XMLRPC_ServerDestroy(XMLRPC_SERVER server) { + if(server) { + doc_method* dm = Q_Head(&server->docslist); + server_method* sm = Q_Head(&server->methodlist); + while( dm ) { + my_free(dm); + dm = Q_Next(&server->docslist); + } + while( sm ) { + if(sm->name) { + my_free(sm->name); + } + if(sm->desc) { + XMLRPC_CleanupValue(sm->desc); + } + my_free(sm); + sm = Q_Next(&server->methodlist); + } + if(server->xIntrospection) { + XMLRPC_CleanupValue(server->xIntrospection); + } + + Q_Destroy(&server->methodlist); + Q_Destroy(&server->docslist); + my_free(server); + } +} +/*******/ + + +/****f* VALUE/XMLRPC_ServerRegisterMethod + * NAME + * XMLRPC_ServerRegisterMethod + * SYNOPSIS + * void XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb) + * FUNCTION + * Register new XMLRPC method with server + * INPUTS + * server The XMLRPC_SERVER to register the method with + * name public name of the method + * cb C function that implements the method + * RESULT + * int - 1 if success, else 0 + * NOTES + * A C function must be registered for every "method" that the server recognizes. The + * method name is equivalent to <methodCall><name> method name </name></methodCall> in the + * XML syntax. + * SEE ALSO + * XMLRPC_ServerFindMethod () + * XMLRPC_ServerCallMethod () + * SOURCE + */ +int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb) { + if(server && name && cb) { + + server_method* sm = malloc(sizeof(server_method)); + + if(sm) { + sm->name = strdup(name); + sm->method = cb; + sm->desc = NULL; + + return Q_PushTail(&server->methodlist, sm); + } + } + return 0; +} +/*******/ + +inline server_method* find_method(XMLRPC_SERVER server, const char* name) { + server_method* sm; + + q_iter qi = Q_Iter_Head_F(&server->methodlist); + + while( qi ) { + sm = Q_Iter_Get_F(qi); + if(sm && !strcmp(sm->name, name)) { + return sm; + } + qi = Q_Iter_Next_F(qi); + } + return NULL; +} + + +const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) { + switch(type) { + case xmlrpc_none: + return "none"; + case xmlrpc_empty: + return "empty"; + case xmlrpc_base64: + return "base64"; + case xmlrpc_boolean: + return "boolean"; + case xmlrpc_datetime: + return "datetime"; + case xmlrpc_double: + return "double"; + case xmlrpc_int: + return "int"; + case xmlrpc_string: + return "string"; + case xmlrpc_vector: + switch(vtype) { + case xmlrpc_vector_none: + return "none"; + case xmlrpc_vector_array: + return "array"; + case xmlrpc_vector_mixed: + return "mixed vector (struct)"; + case xmlrpc_vector_struct: + return "struct"; + } + } +} + +/****f* VALUE/XMLRPC_ServerFindMethod + * NAME + * XMLRPC_ServerFindMethod + * SYNOPSIS + * XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName) + * FUNCTION + * retrieve C callback associated with a given method name. + * INPUTS + * server The XMLRPC_SERVER the method is registered with + * callName the method to find + * RESULT + * previously registered XMLRPC_Callback, or NULL + * NOTES + * Typically, this is used to determine if a requested method exists, without actually calling it. + * SEE ALSO + * XMLRPC_ServerCallMethod () + * XMLRPC_ServerRegisterMethod () + * SOURCE + */ +XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName) { + if(server && callName) { + q_iter qi = Q_Iter_Head_F(&server->methodlist); + while( qi ) { + server_method* sm = Q_Iter_Get_F(qi); + if(sm && !strcmp(sm->name, callName)) { + return sm->method; + } + qi = Q_Iter_Next_F(qi); + } + } + return NULL; +} +/*******/ + + +/* Call method specified in request */ +/****f* VALUE/XMLRPC_ServerCallMethod + * NAME + * XMLRPC_ServerCallMethod + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData) + * FUNCTION + * + * INPUTS + * server The XMLRPC_SERVER the method is registered with + * request the request to handle + * userData any additional data to pass to the C callback, or NULL + * RESULT + * XMLRPC_VALUE allocated by the callback, or NULL + * NOTES + * It is typically the caller's responsibility to free the returned value. + * + * Often the caller will want to serialize the result as XML, via + * XMLRPC_VALUE_To_XML () or XMLRPC_REQUEST_To_XML () + * SEE ALSO + * XMLRPC_ServerFindMethod () + * XMLRPC_ServerRegisterMethod () + * XMLRPC_CleanupValue () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData) { + XMLRPC_VALUE xReturn = NULL; + + /* check for error set during request parsing / generation */ + if(request && request->error) { + xReturn = XMLRPC_CopyValue(request->error); + } + else if(server && request && request->methodName.str) { + XMLRPC_Callback cb = XMLRPC_ServerFindMethod(server, request->methodName.str); + if(cb) { + xReturn = cb(server, request, userData); + } + else { + xReturn = XMLRPC_UtilityCreateFault(xmlrpc_error_unknown_method, request->methodName.str); + } + } + return xReturn; +} +/*******/ + +/*-***************** +* End server funcs * +*******************/ + + +/*-*********************************** +* Begin XMLRPC General Options funcs * +*************************************/ + +/* For options used by XMLRPC_VALUE funcs that otherwise do not have + * parameters for options. Kind of gross. :( + */ +typedef struct _xmlrpc_options { + XMLRPC_CASE id_case; + XMLRPC_CASE_COMPARISON id_case_compare; +} STRUCT_XMLRPC_OPTIONS, *XMLRPC_OPTIONS; + +static XMLRPC_OPTIONS XMLRPC_GetDefaultOptions() { + static STRUCT_XMLRPC_OPTIONS options = { + xmlrpc_case_exact, + xmlrpc_case_sensitive + }; + return &options; +} + +/****f* VALUE/XMLRPC_GetDefaultIdCase + * NAME + * XMLRPC_GetDefaultIdCase + * SYNOPSIS + * XMLRPC_CASE XMLRPC_GetDefaultIdCase() + * FUNCTION + * Gets default case options used by XMLRPC_VALUE funcs + * INPUTS + * none + * RESULT + * XMLRPC_CASE + * BUGS + * Nasty and gross. Should be server specific, but that requires changing all + * the XMLRPC_VALUE api's. + * SEE ALSO + * XMLRPC_SetDefaultIdCase () + * SOURCE + */ +XMLRPC_CASE XMLRPC_GetDefaultIdCase() { + XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); + return options->id_case; +} +/*******/ + +/****f* VALUE/XMLRPC_SetDefaultIdCase + * NAME + * XMLRPC_SetDefaultIdCase + * SYNOPSIS + * XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) + * FUNCTION + * Sets default case options used by XMLRPC_VALUE funcs + * INPUTS + * id_case case options as enumerated by XMLRPC_CASE + * RESULT + * XMLRPC_CASE -- newly set option + * BUGS + * Nasty and gross. Should be server specific, but that requires changing all + * the XMLRPC_VALUE api's. + * SEE ALSO + * XMLRPC_GetDefaultIdCase () + * SOURCE + */ +XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) { + XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); + options->id_case = id_case; + return options->id_case; +} +/*******/ + +/****f* VALUE/XMLRPC_GetDefaultIdCaseComparison + * NAME + * XMLRPC_GetDefaultIdCaseComparison + * SYNOPSIS + * XMLRPC_CASE XMLRPC_GetDefaultIdCaseComparison( ) + * FUNCTION + * Gets default case comparison options used by XMLRPC_VALUE funcs + * INPUTS + * none + * RESULT + * XMLRPC_CASE_COMPARISON default + * BUGS + * Nasty and gross. Should be server specific, but that requires changing all + * the XMLRPC_VALUE api's. + * SEE ALSO + * XMLRPC_SetDefaultIdCaseComparison () + * SOURCE + */ +XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison() { + XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); + return options->id_case_compare; +} +/*******/ + +/****f* VALUE/XMLRPC_SetDefaultIdCaseComparison + * NAME + * XMLRPC_SetDefaultIdCaseComparison + * SYNOPSIS + * XMLRPC_CASE XMLRPC_SetDefaultIdCaseComparison( XMLRPC_CASE_COMPARISON id_case_compare ) + * FUNCTION + * Gets default case comparison options used by XMLRPC_VALUE funcs + * INPUTS + * id_case_compare case comparison rule to set as default + * RESULT + * XMLRPC_CASE_COMPARISON newly set default + * BUGS + * Nasty and gross. Should be server specific, but that requires changing all + * the XMLRPC_VALUE api's. + * SEE ALSO + * XMLRPC_GetDefaultIdCaseComparison () + * SOURCE + */ +XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case_compare) { + XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); + options->id_case_compare = id_case_compare; + return options->id_case_compare; +} +/*******/ + +/*-********************************* +* End XMLRPC General Options funcs * +***********************************/ + + +/*-****************** +* Utility API funcs * +********************/ + +/****f* UTILITY/XMLRPC_UtilityCreateFault + * NAME + * XMLRPC_UtilityCreateFault + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_UtilityCreateFault( int fault_code, const char* fault_string ) + * FUNCTION + * generates a struct containing a string member with id "faultString" and an int member + * with id "faultCode". When using the xmlrpc xml serialization, these will be translated + * to <fault><value><struct>... format. + * INPUTS + * fault_code application specific error code. can be 0. + * fault_string application specific error string. cannot be null. + * RESULT + * XMLRPC_VALUE a newly created struct vector representing the error, or null on error. + * NOTES + * This is a utility function. xmlrpc "faults" are not directly represented in this xmlrpc + * API or data structures. It is the author's view, that this API is intended for simple + * data types, and a "fault" is a complex data type consisting of multiple simple data + * types. This function is provided for convenience only, the same result could be + * achieved directly by the application. + * + * This function now supports some "standardized" fault codes, as specified at. + * http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php. + * If one of these fault codes is received, the description string will automatically + * be prefixed with a standard error string and 2 newlines. + * + * The actual transformation between this complex type and the xml "<fault>" element takes + * place in the xmlrpc to xml serialization layer. This step is not performed when using the + * simplerpc serialization, meaning that there will be no "<fault>" element in that + * serialization. There will simply be a standard struct with 2 child elements. + * imho, the "<fault>" element is unnecessary and/or out of place as part of the standard API. + * SOURCE + */ +XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) { + XMLRPC_VALUE xOutput = NULL; + + char* string = NULL; + simplestring description; + simplestring_init(&description); + + switch (fault_code) { + case xmlrpc_error_parse_xml_syntax: string = xmlrpc_error_parse_xml_syntax_str; break; + case xmlrpc_error_parse_unknown_encoding: string = xmlrpc_error_parse_unknown_encoding_str; break; + case xmlrpc_error_parse_bad_encoding: string = xmlrpc_error_parse_bad_encoding_str; break; + case xmlrpc_error_invalid_xmlrpc: string = xmlrpc_error_invalid_xmlrpc_str; break; + case xmlrpc_error_unknown_method: string = xmlrpc_error_unknown_method_str; break; + case xmlrpc_error_invalid_params: string = xmlrpc_error_invalid_params_str; break; + case xmlrpc_error_internal_server: string = xmlrpc_error_internal_server_str; break; + case xmlrpc_error_application: string = xmlrpc_error_application_str; break; + case xmlrpc_error_system: string = xmlrpc_error_system_str; break; + case xmlrpc_error_transport: string = xmlrpc_error_transport_str; break; + } + + simplestring_add(&description, string); + + if(string && fault_string) { + simplestring_add(&description, "\n\n"); + } + simplestring_add(&description, fault_string); + + + if(description.len) { + xOutput = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + + XMLRPC_VectorAppendString(xOutput, "faultString", description.str, description.len); + XMLRPC_VectorAppendInt(xOutput, "faultCode", fault_code); + } + + simplestring_free(&description); + + return xOutput; +} +/*******/ + + +/****f* UTILITY/XMLRPC_Free + * NAME + * XMLRPC_Free + * SYNOPSIS + * void XMLRPC_Free(void* mem) + * FUNCTION + * frees a block of memory allocated by xmlrpc. + * INPUTS + * mem memory to free + * RESULT + * void + * NOTES + * Useful for OS's where memory must be free'd + * in the same library in which it is allocated. + * SOURCE + */ +void XMLRPC_Free(void* mem) { + my_free(mem); +} +/*******/ + + +/****f* UTILITY/XMLRPC_GetVersionString + * NAME + * XMLRPC_GetVersionString + * SYNOPSIS + * const char* XMLRPC_GetVersionString() + * FUNCTION + * returns library version string + * INPUTS + * + * RESULT + * const char* + * NOTES + * SOURCE + */ +const char* XMLRPC_GetVersionString() { + return XMLRPC_VERSION_STR; +} +/*******/ + + +/*-********************** +* End Utility API funcs * +************************/ + + + + + diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h new file mode 100644 index 0000000000..2cfe7cb2d9 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.h @@ -0,0 +1,402 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +#ifndef XMLRPC_ALREADY_INCLUDED +#define XMLRPC_ALREADY_INCLUDED 1 + +/* includes */ +#include "xml_element.h" +#include <time.h> /* for time_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* this number, representing the date, must be increased each time the API changes */ +#define XMLRPC_API_NO 20010721 + +/* this string should be changed with each packaged release */ +#define XMLRPC_VERSION_STR "xmlrpc-epi v. " VERSION + +/* where to find more info. shouldn't need to change much */ +#define XMLRPC_HOME_PAGE_STR "http://xmlprc-epi.sourceforge.net/" + + +/****d* VALUE/XMLRPC_VALUE_TYPE + * NAME + * XMLRPC_VALUE_TYPE + * NOTES + * Defines data types for XMLRPC_VALUE + * SEE ALSO + * XMLRPC_VECTOR_TYPE + * XMLRPC_REQUEST_TYPE + * SOURCE + */ +typedef enum _XMLRPC_VALUE_TYPE { + xmlrpc_none, /* not a value */ + xmlrpc_empty, /* empty value, eg NULL */ + xmlrpc_base64, /* base64 value, eg binary data */ + xmlrpc_boolean, /* boolean [0 | 1] */ + xmlrpc_datetime, /* datetime [ISO8601 | time_t] */ + xmlrpc_double, /* double / floating point */ + xmlrpc_int, /* integer */ + xmlrpc_string, /* string */ + xmlrpc_vector /* vector, aka list, array */ +} XMLRPC_VALUE_TYPE; +/*******/ + +/****d* VALUE/XMLRPC_VECTOR_TYPE + * NAME + * XMLRPC_VECTOR_TYPE + * NOTES + * Defines data types for XMLRPC_VECTOR + * SEE ALSO + * XMLRPC_VALUE_TYPE + * XMLRPC_REQUEST_TYPE + * SOURCE + */ +typedef enum _XMLRPC_VECTOR_TYPE { + xmlrpc_vector_none, /* not an array */ + xmlrpc_vector_array, /* no values may have key names */ + xmlrpc_vector_mixed, /* some values may have key names */ + xmlrpc_vector_struct /* all values must have key names */ +} XMLRPC_VECTOR_TYPE; +/*******/ + +/****d* VALUE/XMLRPC_REQUEST_TYPE + * NAME + * XMLRPC_REQUEST_TYPE + * NOTES + * Defines data types for XMLRPC_REQUEST + * SEE ALSO + * XMLRPC_VALUE_TYPE + * XMLRPC_VECTOR_TYPE + * SOURCE + */ +typedef enum _xmlrpc_request_type { + xmlrpc_request_none, /* not a valid request */ + xmlrpc_request_call, /* calling/invoking a method */ + xmlrpc_request_response, /* responding to a method call */ +} XMLRPC_REQUEST_TYPE; +/*******/ + +/****d* VALUE/XMLRPC_ERROR_CODE + * NAME + * XMLRPC_ERROR_CODE + * NOTES + * All existing error codes + * SEE ALSO + * XMLRPC_REQUEST_ERROR + * SOURCE + */ +typedef enum _xmlrpc_error_code { + xmlrpc_error_none = 0, /* not an error */ + xmlrpc_error_parse_xml_syntax = -32700, + xmlrpc_error_parse_unknown_encoding = -32701, + xmlrpc_error_parse_bad_encoding = -32702, + xmlrpc_error_invalid_xmlrpc = -32600, + xmlrpc_error_unknown_method = -32601, + xmlrpc_error_invalid_params = -32602, + xmlrpc_error_internal_server = -32603, + xmlrpc_error_application = -32500, + xmlrpc_error_system = -32400, + xmlrpc_error_transport = -32300 +} XMLRPC_ERROR_CODE; +/******/ + +#define xmlrpc_error_parse_xml_syntax_str "parse error. not well formed." +#define xmlrpc_error_parse_unknown_encoding_str "parse error. unknown encoding" +#define xmlrpc_error_parse_bad_encoding_str "parse error. invalid character for encoding" +#define xmlrpc_error_invalid_xmlrpc_str "server error. xml-rpc not conforming to spec" +#define xmlrpc_error_unknown_method_str "server error. method not found." +#define xmlrpc_error_invalid_params_str "server error. invalid method parameters" +#define xmlrpc_error_internal_server_str "server error. internal xmlrpc library error" +#define xmlrpc_error_application_str "application error." +#define xmlrpc_error_system_str "system error." +#define xmlrpc_error_transport_str "transport error." + + + +/****d* VALUE/XMLRPC_VERSION + * NAME + * XMLRPC_VERSION + * NOTES + * Defines xml vocabulary used for generated xml + * SEE ALSO + * XMLRPC_REQUEST_OUTPUT_OPTIONS + * XMLRPC_REQUEST_To_XML () + * SOURCE + */ +typedef enum _xmlrpc_version { + xmlrpc_version_none, /* not a recognized vocabulary */ + xmlrpc_version_1_0, /* xmlrpc 1.0 standard vocab */ + xmlrpc_version_simple = 2, /* alt more readable vocab */ + xmlrpc_version_danda = 2 /* same as simple. legacy */ +} XMLRPC_VERSION; +/******/ + +/****s* VALUE/XMLRPC_REQUEST_OUTPUT_OPTIONS + * NAME + * XMLRPC_REQUEST_OUTPUT_OPTIONS + * NOTES + * Defines output options for generated xml + * SEE ALSO + * XMLRPC_VERSION + * XML_ELEM_OUTPUT_OPTIONS + * XMLRPC_REQUEST_To_XML () + * SOURCE + */ +typedef struct _xmlrpc_request_output_options { + STRUCT_XML_ELEM_OUTPUT_OPTIONS xml_elem_opts; /* xml_element specific output options */ + XMLRPC_VERSION version; /* xml vocabulary to use */ +} STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS, *XMLRPC_REQUEST_OUTPUT_OPTIONS; +/******/ + +/****s* VALUE/XMLRPC_REQUEST_INPUT_OPTIONS + * NAME + * XMLRPC_REQUEST_INPUT_OPTIONS + * NOTES + * Defines options for reading in xml data + * SEE ALSO + * XMLRPC_VERSION + * XML_ELEM_INPUT_OPTIONS + * XMLRPC_REQUEST_From_XML () + * SOURCE + */ +typedef struct _xmlrpc_request_input_options { + STRUCT_XML_ELEM_INPUT_OPTIONS xml_elem_opts; /* xml_element specific output options */ +} STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS, *XMLRPC_REQUEST_INPUT_OPTIONS; +/******/ + +/****s* VALUE/XMLRPC_ERROR + * NAME + * XMLRPC_ERROR + * NOTES + * For the reporting and handling of errors + * SOURCE + */ +typedef struct _xmlrpc_error { + XMLRPC_ERROR_CODE code; + STRUCT_XML_ELEM_ERROR xml_elem_error; /* xml_element errors (parser errors) */ +} STRUCT_XMLRPC_ERROR, *XMLRPC_ERROR; +/******/ + + +/****d* VALUE/XMLRPC_CASE_COMPARISON + * NAME + * XMLRPC_CASE_COMPARISON + * NOTES + * Defines case comparison options for XMLRPC_VALUE/VECTOR API's + * SEE ALSO + * XMLRPC_CASE + * XMLRPC_VALUE + * SOURCE + */ +typedef enum _xmlrpc_case_comparison { + xmlrpc_case_insensitive, /* use case-insensitive compare */ + xmlrpc_case_sensitive /* use case-sensitive compare */ +} XMLRPC_CASE_COMPARISON; +/******/ + +/****d* VALUE/XMLRPC_CASE + * NAME + * XMLRPC_CASE + * NOTES + * Defines case behavior when setting IDs in XMLRPC_VALUE API's + * SEE ALSO + * XMLRPC_CASE_COMPARISON + * XMLRPC_VALUE + * SOURCE + */ +typedef enum _xmlrpc_case { + xmlrpc_case_exact, /* leave case alone */ + xmlrpc_case_lower, /* lower-case id */ + xmlrpc_case_upper /* upper-case id */ +} XMLRPC_CASE; +/******/ + +/* if you don't like these defaults, you can set them with XMLRPC_SetDefaultIdCase*() */ +#define XMLRPC_DEFAULT_ID_CASE XMLRPC_GetDefaultIdCase() +#define XMLRPC_DEFAULT_ID_CASE_SENSITIVITY XMLRPC_GetDefaultIdCaseComparison() + +/* opaque (non-public) types. defined locally in xmlrpc.c */ +typedef struct _xmlrpc_request* XMLRPC_REQUEST; +typedef struct _xmlrpc_server* XMLRPC_SERVER; +typedef struct _xmlrpc_value* XMLRPC_VALUE; + +/****d* VALUE/XMLRPC_Callback + * NAME + * XMLRPC_Callback + * NOTES + * Function prototype for user defined method handlers (callbacks). + * SEE ALSO + * XMLRPC_ServerRegisterMethod () + * XMLRPC_ServerCallMethod () + * XMLRPC_REQUEST + * XMLRPC_VALUE + * SOURCE + */ +typedef XMLRPC_VALUE (*XMLRPC_Callback)(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); +/******/ + +/* ID Case Defaults */ +XMLRPC_CASE XMLRPC_GetDefaultIdCase(); +XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case); +XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison(); +XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case); + +/* Vector manipulation */ +int XMLRPC_VectorSize(XMLRPC_VALUE value); +XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value); +XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value); +int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type); +int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source); +int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...); +int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value); +XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case); + + +/* Create values */ +XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int truth); +XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len); +XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time); +XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s); +XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double f); +XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i); +XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* s, int len); +XMLRPC_VALUE XMLRPC_CreateValueEmpty(); +XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type); + +/* Cleanup values */ +void XMLRPC_CleanupValue(XMLRPC_VALUE value); +XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value); + +/* Set Values */ +void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time); +void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s); +void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val); +void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val); +void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val); +const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* s, int len); +void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len); +const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case); +#define XMLRPC_SetValueID(value, id, len) XMLRPC_SetValueID_Case(value, id, len, XMLRPC_DEFAULT_ID_CASE) + +/* Get Values */ +const char* XMLRPC_GetValueString(XMLRPC_VALUE value); +int XMLRPC_GetValueStringLen(XMLRPC_VALUE value); +int XMLRPC_GetValueInt(XMLRPC_VALUE value); +int XMLRPC_GetValueBoolean(XMLRPC_VALUE value); +double XMLRPC_GetValueDouble(XMLRPC_VALUE value); +const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value); +time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value); +const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value); +const char* XMLRPC_GetValueID(XMLRPC_VALUE value); + +/* Type introspection */ +XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE v); +XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE v); + +/* Parsing and Creating XML */ +XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options); +XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options); +char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int *buf_len); +char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len); + +/* Request manipulation funcs */ +const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName); +const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request); +XMLRPC_REQUEST XMLRPC_RequestNew(); +void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO); +XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output); +XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request); +XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data); +XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request); +XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type); +XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request); + +/* Server Creation/Destruction; Method Registration and Invocation */ +XMLRPC_SERVER XMLRPC_ServerCreate(); +XMLRPC_SERVER XMLRPC_GetGlobalServer(); /* better to use XMLRPC_ServerCreate if you can */ +void XMLRPC_ServerDestroy(XMLRPC_SERVER server); +int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb); +XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName); +XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData); + +#include "xmlrpc_introspection.h" + +/* Public Utility funcs */ +XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string); +void XMLRPC_Free(void* mem); +const char* XMLRPC_GetVersionString(); + +/****d* VALUE/XMLRPC_MACROS + * NAME + * Some Helpful Macros + * NOTES + * Some macros for making life easier. Should be self-explanatory. + * SEE ALSO + * XMLRPC_AddValueToVector () + * XMLRPC_VectorGetValueWithID_Case () + * XMLRPC_VALUE + * SOURCE + */ + +/* Append values to vector */ +#define XMLRPC_VectorAppendString(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueString(id, s, len)) +#define XMLRPC_VectorAppendBase64(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBase64(id, s, len)) +#define XMLRPC_VectorAppendDateTime(vector, id, time) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime(id, time)) +#define XMLRPC_VectorAppendDateTime_ISO8601(vector, id, s) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime_ISO8601(id, s)) +#define XMLRPC_VectorAppendDouble(vector, id, f) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDouble(id, f)) +#define XMLRPC_VectorAppendInt(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueInt(id, i)) +#define XMLRPC_VectorAppendBoolean(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBoolean(id, i)) + +/* Get named values from vector */ +#define XMLRPC_VectorGetValueWithID(vector, id) XMLRPC_VectorGetValueWithID_Case(vector, id, XMLRPC_DEFAULT_ID_CASE_SENSITIVITY) +#define XMLRPC_VectorGetStringWithID(vector, id) XMLRPC_GetValueString(XMLRPC_VectorGetValueWithID(vector, id)) +#define XMLRPC_VectorGetBase64WithID(vector, id) XMLRPC_GetValueBase64(XMLRPC_VectorGetValueWithID(vector, id)) +#define XMLRPC_VectorGetDateTimeWithID(vector, id) XMLRPC_GetValueDateTime(XMLRPC_VectorGetValueWithID(vector, id)) +#define XMLRPC_VectorGetDoubleWithID(vector, id) XMLRPC_GetValueDouble(XMLRPC_VectorGetValueWithID(vector, id)) +#define XMLRPC_VectorGetIntWithID(vector, id) XMLRPC_GetValueInt(XMLRPC_VectorGetValueWithID(vector, id)) +#define XMLRPC_VectorGetBooleanWithID(vector, id) XMLRPC_GetValueBoolean(XMLRPC_VectorGetValueWithID(vector, id)) + +/******/ + + +#ifdef __cplusplus +} +#endif + +#endif /* not XMLRPC_ALREADY_INCLUDED */ + + + diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.m4 b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.m4 new file mode 100644 index 0000000000..18d36067dc --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc.m4 @@ -0,0 +1,16 @@ +CPPFLAGS="$CPPFLAGS -DVERSION=\\\"0.42\\\"" + +AC_DEFUN(XMLRPC_CHECKS,[ + +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_LN_S]) +AC_REQUIRE([AC_PROG_RANLIB]) + +AC_DEFINE(UNDEF_THREADS_HACK,,[ ]) + +XMLRPC_HEADER_CHECKS +XMLRPC_TYPE_CHECKS +XMLRPC_FUNCTION_CHECKS + +PHP_FAST_OUTPUT($ext_builddir/libxmlrpc/Makefile) +]) diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.c b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.c new file mode 100644 index 0000000000..5b15d4b071 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.c @@ -0,0 +1,591 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +/****h* ABOUT/xmlrpc_introspection + * AUTHOR + * Dan Libby, aka danda (dan@libby.com) + * HISTORY + * 4/10/2001 -- danda -- initial introspection support + * TODO + * NOTES + *******/ + + +#include "queue.h" +#include "xmlrpc.h" +#include "xmlrpc_private.h" +#include "xmlrpc_introspection_private.h" +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> + + +/* forward declarations for static (non public, non api) funcs */ +static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); +static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); +static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); +static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); + + +/*-********************************** +* Introspection Callbacks (methods) * +************************************/ + +/* iterates through a list of structs and finds the one with key "name" matching + * needle. slow, would benefit from a struct key hash. + */ +inline XMLRPC_VALUE find_named_value(XMLRPC_VALUE list, const char* needle) { + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(list); + while(xIter) { + const char* name = XMLRPC_VectorGetStringWithID(xIter, xi_token_name); + if(name && !strcmp(name, needle)) { + return xIter; + } + xIter = XMLRPC_VectorNext(list); + } + return NULL; +} + + +/* iterates through docs callbacks and calls any that have not yet been called */ +static void check_docs_loaded(XMLRPC_SERVER server, void* userData) { + if(server) { + q_iter qi = Q_Iter_Head_F(&server->docslist); + while( qi ) { + doc_method* dm = Q_Iter_Get_F(qi); + if(dm && !dm->b_called) { + dm->method(server, userData); + dm->b_called = 1; + } + qi = Q_Iter_Next_F(qi); + } + } +} + + +/* utility function for xi_system_describe_methods_cb */ +inline void describe_method(XMLRPC_SERVER server, XMLRPC_VALUE vector, const char* method) { + if(method) { + server_method* sm = find_method(server, method); + if(sm) { + XMLRPC_AddValueToVector(vector, sm->desc); + } + } +} + + + +/* system.describeMethods() callback */ +static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + XMLRPC_VALUE xParams = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)); + XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + XMLRPC_VALUE xMethodList = XMLRPC_CreateVector("methodList", xmlrpc_vector_array); + XMLRPC_VALUE xTypeList = NULL; + int bAll = 1; + + /* lazy loading of introspection data */ + check_docs_loaded(server, userData); + + xTypeList = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList"); + + XMLRPC_AddValueToVector(xResponse, xTypeList); + XMLRPC_AddValueToVector(xResponse, xMethodList); + + /* check if we have any param */ + if(xParams) { + /* check if string or vector (1 or n) */ + XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(xParams); + if(type == xmlrpc_string) { + /* just one. spit it out. */ + describe_method(server, xMethodList, XMLRPC_GetValueString(xParams)); + bAll = 0; + } + else if(type == xmlrpc_vector) { + /* multiple. spit all out */ + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xParams); + while(xIter) { + describe_method(server, xMethodList, XMLRPC_GetValueString(xIter)); + xIter = XMLRPC_VectorNext(xParams); + } + bAll = 0; + } + } + + /* otherwise, default to sending all methods */ + if(bAll) { + q_iter qi = Q_Iter_Head_F(&server->methodlist); + while( qi ) { + server_method* sm = Q_Iter_Get_F(qi); + if(sm) { + XMLRPC_AddValueToVector(xMethodList, sm->desc); + } + qi = Q_Iter_Next_F(qi); + } + } + + return xResponse; +} + +/* this complies with system.listMethods as defined at http://xmlrpc.usefulinc.com/doc/reserved.html */ +static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); + + q_iter qi = Q_Iter_Head_F(&server->methodlist); + while( qi ) { + server_method* sm = Q_Iter_Get_F(qi); + if(sm) { + XMLRPC_VectorAppendString(xResponse, 0, sm->name, 0); + } + qi = Q_Iter_Next_F(qi); + } + return xResponse; +} + +/* this complies with system.methodSignature as defined at + * http://xmlrpc.usefulinc.com/doc/sysmethodsig.html + */ +static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input))); + XMLRPC_VALUE xResponse = NULL; + + /* lazy loading of introspection data */ + check_docs_loaded(server, userData); + + if(method) { + server_method* sm = find_method(server, method); + if(sm && sm->desc) { + XMLRPC_VALUE xTypesArray = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); + XMLRPC_VALUE xIter, xParams, xSig, xSigIter; + const char* type; + + /* array of possible signatures. */ + xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); + + /* find first signature */ + xSig = XMLRPC_VectorGetValueWithID(sm->desc, xi_token_signatures); + xSigIter = XMLRPC_VectorRewind( xSig ); + + /* iterate through sigs */ + while(xSigIter) { + /* first type is the return value */ + type = XMLRPC_VectorGetStringWithID(XMLRPC_VectorRewind( + XMLRPC_VectorGetValueWithID(xSigIter, xi_token_returns)), + xi_token_type); + XMLRPC_AddValueToVector(xTypesArray, + XMLRPC_CreateValueString(NULL, + type ? type : type_to_str(xmlrpc_none, 0), + 0)); + + /* the rest are parameters */ + xParams = XMLRPC_VectorGetValueWithID(xSigIter, xi_token_params); + xIter = XMLRPC_VectorRewind(xParams); + + /* iter through params, adding to types array */ + while(xIter) { + XMLRPC_AddValueToVector(xTypesArray, + XMLRPC_CreateValueString(NULL, + XMLRPC_VectorGetStringWithID(xIter, xi_token_type), + 0)); + xIter = XMLRPC_VectorNext(xParams); + } + + /* add types for this signature */ + XMLRPC_AddValueToVector(xResponse, xTypesArray); + + xSigIter = XMLRPC_VectorNext( xSig ); + } + } + } + + return xResponse; +} + +/* this complies with system.methodHelp as defined at + * http://xmlrpc.usefulinc.com/doc/sysmethhelp.html + */ +static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input))); + XMLRPC_VALUE xResponse = NULL; + + /* lazy loading of introspection data */ + check_docs_loaded(server, userData); + + if(method) { + server_method* sm = find_method(server, method); + if(sm && sm->desc) { + const char* help = XMLRPC_VectorGetStringWithID(sm->desc, xi_token_purpose); + + /* returns a documentation string, or empty string */ + xResponse = XMLRPC_CreateValueString(NULL, help ? help : xi_token_empty, 0); + } + } + + return xResponse; +} + +/*-************************************** +* End Introspection Callbacks (methods) * +****************************************/ + + +/*-************************ +* Introspection Utilities * +**************************/ + +/* performs registration of introspection methods */ +void xi_register_system_methods(XMLRPC_SERVER server) { + XMLRPC_ServerRegisterMethod(server, xi_token_system_list_methods, xi_system_list_methods_cb); + XMLRPC_ServerRegisterMethod(server, xi_token_system_method_help, xi_system_method_help_cb); + XMLRPC_ServerRegisterMethod(server, xi_token_system_method_signature, xi_system_method_signature_cb); + XMLRPC_ServerRegisterMethod(server, xi_token_system_describe_methods, xi_system_describe_methods_cb); +} + +/* describe a value (param, return, type) */ +static XMLRPC_VALUE describeValue_worker(const char* type, const char* id, const char* desc, int optional, const char* default_val, XMLRPC_VALUE sub_params) { + XMLRPC_VALUE xParam = NULL; + if(id || desc) { + xParam = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + XMLRPC_VectorAppendString(xParam, xi_token_name, id, 0); + XMLRPC_VectorAppendString(xParam, xi_token_type, type, 0); + XMLRPC_VectorAppendString(xParam, xi_token_description, desc, 0); + if(optional != 2) { + XMLRPC_VectorAppendInt(xParam, xi_token_optional, optional); + } + if(optional == 1 && default_val) { + XMLRPC_VectorAppendString(xParam, xi_token_default, default_val, 0); + } + XMLRPC_AddValueToVector(xParam, sub_params); + } + return xParam; +} + + +/* convert an xml tree conforming to spec <url tbd> to XMLRPC_VALUE + * suitable for use with XMLRPC_ServerAddIntrospectionData + */ +XMLRPC_VALUE xml_element_to_method_description(xml_element* el, XMLRPC_ERROR err) { + XMLRPC_VALUE xReturn = NULL; + + if(el->name) { + const char* name = NULL; + const char* type = NULL; + const char* basetype = NULL; + const char* desc = NULL; + const char* def = NULL; + int optional = 0; + xml_element_attr* attr_iter = Q_Head(&el->attrs); + + /* grab element attributes up front to save redundant while loops */ + while(attr_iter) { + if(!strcmp(attr_iter->key, "name")) { + name = attr_iter->val; + } + else if(!strcmp(attr_iter->key, "type")) { + type = attr_iter->val; + } + else if(!strcmp(attr_iter->key, "basetype")) { + basetype = attr_iter->val; + } + else if(!strcmp(attr_iter->key, "desc")) { + desc = attr_iter->val; + } + else if(!strcmp(attr_iter->key, "optional")) { + if(attr_iter->val && !strcmp(attr_iter->val, "yes")) { + optional = 1; + } + } + else if(!strcmp(attr_iter->key, "default")) { + def = attr_iter->val; + } + attr_iter = Q_Next(&el->attrs); + } + + /* value and typeDescription behave about the same */ + if(!strcmp(el->name, "value") || !strcmp(el->name, "typeDescription")) { + XMLRPC_VALUE xSubList = NULL; + const char* ptype = !strcmp(el->name, "value") ? type : basetype; + if(ptype) { + if(Q_Size(&el->children) && + !strcmp(ptype, "array") || !strcmp(ptype, "struct") || !strcmp(ptype, "mixed")) { + xSubList = XMLRPC_CreateVector("member", xmlrpc_vector_array); + + if(xSubList) { + xml_element* elem_iter = Q_Head(&el->children); + while(elem_iter) { + XMLRPC_AddValueToVector(xSubList, + xml_element_to_method_description(elem_iter, err)); + elem_iter = Q_Next(&el->children); + } + } + } + xReturn = describeValue_worker(ptype, name, (desc ? desc : (xSubList ? NULL : el->text.str)), optional, def, xSubList); + } + } + + /* these three kids are about equivalent */ + else if(!strcmp(el->name, "params") || + !strcmp(el->name, "returns") || + !strcmp(el->name, "signature")) { + if(Q_Size(&el->children)) { + xml_element* elem_iter = Q_Head(&el->children); + xReturn = XMLRPC_CreateVector(!strcmp(el->name, "signature") ? NULL : el->name, xmlrpc_vector_struct); + + + while(elem_iter) { + XMLRPC_AddValueToVector(xReturn, + xml_element_to_method_description(elem_iter, err)); + elem_iter = Q_Next(&el->children); + } + } + } + + + else if(!strcmp(el->name, "methodDescription")) { + xml_element* elem_iter = Q_Head(&el->children); + xReturn = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + + XMLRPC_VectorAppendString(xReturn, xi_token_name, name, 0); + + while(elem_iter) { + XMLRPC_AddValueToVector(xReturn, + xml_element_to_method_description(elem_iter, err)); + elem_iter = Q_Next(&el->children); + } + } + + /* items are slightly special */ + else if(!strcmp(el->name, "item")) { + xReturn = XMLRPC_CreateValueString(name, el->text.str, el->text.len); + } + + /* sure. we'll let any ol element with children through */ + else if(Q_Size(&el->children)) { + xml_element* elem_iter = Q_Head(&el->children); + xReturn = XMLRPC_CreateVector(el->name, xmlrpc_vector_mixed); + + while(elem_iter) { + XMLRPC_AddValueToVector(xReturn, + xml_element_to_method_description(elem_iter, err)); + elem_iter = Q_Next(&el->children); + } + } + + /* or anything at all really, so long as its got some text. + * no reason being all snotty about a spec, right? + */ + else if(el->name && el->text.len) { + xReturn = XMLRPC_CreateValueString(el->name, el->text.str, el->text.len); + } + } + + return xReturn; +} + +/*-**************************** +* End Introspection Utilities * +******************************/ + + + +/*-****************** +* Introspection API * +********************/ + + +/****f* VALUE/XMLRPC_IntrospectionCreateDescription + * NAME + * XMLRPC_IntrospectionCreateDescription + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) + * FUNCTION + * converts raw xml describing types and methods into an + * XMLRPC_VALUE suitable for use with XMLRPC_ServerAddIntrospectionData() + * INPUTS + * xml - xml data conforming to introspection spec at <url tbd> + * err - optional pointer to error struct. filled in if error occurs and not NULL. + * RESULT + * XMLRPC_VALUE - newly created value, or NULL if fatal error. + * BUGS + * Currently does little or no validation of xml. + * Only parse errors are currently reported in err, not structural errors. + * SEE ALSO + * XMLRPC_ServerAddIntrospectionData () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) { + XMLRPC_VALUE xReturn = NULL; + xml_element* root = xml_elem_parse_buf(xml, 0, 0, err ? &err->xml_elem_error : NULL); + + if(root) { + xReturn = xml_element_to_method_description(root, err); + + xml_elem_free(root); + } + + return xReturn; + +} +/*******/ + + +/****f* SERVER/XMLRPC_ServerAddIntrospectionData + * NAME + * XMLRPC_ServerAddIntrospectionData + * SYNOPSIS + * int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) + * FUNCTION + * updates server with additional introspection data + * INPUTS + * server - target server + * desc - introspection data, should be a struct generated by + * XMLRPC_IntrospectionCreateDescription () + * RESULT + * int - 1 if success, else 0 + * NOTES + * - function will fail if neither typeList nor methodList key is present in struct. + * - if method or type already exists, it will be replaced. + * - desc is never freed by the server. caller is responsible for cleanup. + * BUGS + * - horribly slow lookups. prime candidate for hash improvements. + * - uglier and more complex than I like to see for API functions. + * SEE ALSO + * XMLRPC_ServerAddIntrospectionData () + * XMLRPC_ServerRegisterIntrospectionCallback () + * XMLRPC_CleanupValue () + * SOURCE + */ +int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) { + int bSuccess = 0; + if(server && desc) { + XMLRPC_VALUE xNewTypes = XMLRPC_VectorGetValueWithID(desc, "typeList"); + XMLRPC_VALUE xNewMethods = XMLRPC_VectorGetValueWithID(desc, "methodList"); + XMLRPC_VALUE xServerTypes = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList"); + + if(xNewMethods) { + XMLRPC_VALUE xMethod = XMLRPC_VectorRewind(xNewMethods); + + while(xMethod) { + const char* name = XMLRPC_VectorGetStringWithID(xMethod, xi_token_name); + server_method* sm = find_method(server, name); + + if(sm) { + if(sm->desc) { + XMLRPC_CleanupValue(sm->desc); + } + sm->desc = XMLRPC_CopyValue(xMethod); + bSuccess = 1; + } + + xMethod = XMLRPC_VectorNext(xNewMethods); + } + } + if(xNewTypes) { + if(!xServerTypes) { + if(!server->xIntrospection) { + server->xIntrospection = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + } + + XMLRPC_AddValueToVector(server->xIntrospection, xNewTypes); + bSuccess = 1; + } + else { + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xNewTypes); + while(xIter) { + /* get rid of old values */ + XMLRPC_VALUE xPrev = find_named_value(xServerTypes, XMLRPC_VectorGetStringWithID(xIter, xi_token_name)); + if(xPrev) { + XMLRPC_VectorRemoveValue(xServerTypes, xPrev); + } + XMLRPC_AddValueToVector(xServerTypes, xIter); + bSuccess = 1; + xIter = XMLRPC_VectorNext(xNewTypes); + } + } + } + } + return bSuccess; +} +/*******/ + + +/****f* SERVER/XMLRPC_ServerRegisterIntrospectionCallback + * NAME + * XMLRPC_ServerRegisterIntrospectionCallback + * SYNOPSIS + * int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) + * FUNCTION + * registers a callback for lazy generation of introspection data + * INPUTS + * server - target server + * cb - callback that will generate introspection data + * RESULT + * int - 1 if success, else 0 + * NOTES + * parsing xml and generating introspection data is fairly expensive, thus a + * server may wish to wait until this data is actually requested before generating + * it. Any number of callbacks may be registered at any time. A given callback + * will only ever be called once, the first time an introspection request is + * processed after the time of callback registration. + * SEE ALSO + * XMLRPC_ServerAddIntrospectionData () + * XMLRPC_IntrospectionCreateDescription () + * SOURCE + */ +int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) { + int bSuccess = 0; + if(server && cb) { + + doc_method* dm = calloc(1, sizeof(doc_method)); + + if(dm) { + dm->method = cb; + dm->b_called = 0; + + if(Q_PushTail(&server->docslist, dm)) { + bSuccess = 1; + } + else { + my_free(dm); + } + } + } + return 0; +} +/*******/ + +/*-********************** +* End Introspection API * +************************/ + + + diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.h b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.h new file mode 100644 index 0000000000..656e441b96 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection.h @@ -0,0 +1,101 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* IMPORTANT! + * + * only public (official API) things should be in this file. Anything else + * should go in <group>_private.h, or in the appropriate .c file. + */ + + +#ifndef __XI_INTROSPECTION_H +/* + * Avoid include redundancy. + */ +#define __XI_INTROSPECTION_H + +/*---------------------------------------------------------------------------- + * xmlrpc_introspection.h + * + * Purpose: + * define public introspection API + * Comments: + */ + +/*---------------------------------------------------------------------------- + * Constants + */ + #define xi_token_params "params" + #define xi_token_returns "returns" + #define xi_token_related "related" + #define xi_token_sub "sub" + + +/*---------------------------------------------------------------------------- + * Includes + */ + +/*---------------------------------------------------------------------------- + * Structures + */ + + /****d* VALUE/XMLRPC_IntrospectionCallback + * NAME + * XMLRPC_IntrospectionCallback + * NOTES + * Function prototype for lazy documentation generation (not generated until requested). + * SOURCE + */ +typedef void (*XMLRPC_IntrospectionCallback)(XMLRPC_SERVER server, void* userData); +/******/ + + +/*---------------------------------------------------------------------------- + * Globals + */ + +/*---------------------------------------------------------------------------- + * Functions + */ +XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR error); +int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc); +int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb); + +/*---------------------------------------------------------------------------- + * Macros + */ + + +#endif /* __XI_INTROSPECTION_H */ + + + diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h new file mode 100644 index 0000000000..7b97fa7ed7 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h @@ -0,0 +1,106 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Dan Libby, Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* IMPORTANT! + * + * only non-public things should be in this file. It is fine for any .c file + * in xmlrpc/src to include it, but users of the public API should never + * include it, and thus *.h files that are part of the public API should + * never include it, or they would break if this file is not present. + */ + + +#ifndef __XI_INTROSPECTION_PRIVATE_H +/* + * Avoid include redundancy. + */ +#define __XI_INTROSPECTION_PRIVATE_H + +/*---------------------------------------------------------------------------- + * xmlrpc_introspection_private.h + * + * Purpose: + * define non-public introspection routines + * Comments: + */ + +/*---------------------------------------------------------------------------- + * Constants + */ +#define xi_token_default "default" +#define xi_token_description "description" +#define xi_token_name "name" +#define xi_token_optional "optional" +#define xi_token_params "params" +#define xi_token_purpose "purpose" +#define xi_token_returns "returns" +#define xi_token_signatures "signatures" +#define xi_token_type "type" +#define xi_token_version "version" +#define xi_token_empty "" +#define xi_token_system_describe_methods "system.describeMethods" +#define xi_token_system_list_methods "system.listMethods" +#define xi_token_system_method_help "system.methodHelp" +#define xi_token_system_method_signature "system.methodSignature" + + +/*---------------------------------------------------------------------------- + * Includes + */ + +/*---------------------------------------------------------------------------- + * Structures + */ +typedef struct _doc_method { + XMLRPC_IntrospectionCallback method; + int b_called; +} doc_method; + +/*---------------------------------------------------------------------------- + * Globals + */ + +/*---------------------------------------------------------------------------- + * Functions + */ +void xi_register_system_methods(XMLRPC_SERVER server); + +/*---------------------------------------------------------------------------- + * Macros + */ + + +#endif /* __XI_INTROSPECTION_PRIVATE_H */ + + + + diff --git a/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h new file mode 100644 index 0000000000..0b4a078ee9 --- /dev/null +++ b/ext/rpc/xmlrpc/libxmlrpc/xmlrpc_private.h @@ -0,0 +1,179 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* only non-public things should be in this file. It is fine for any .c file + * in xmlrpc/src to include it, but users of the public API should never + * include it, and thus *.h files that are part of the public API should + * never include it, or they would break if this file is not present. + */ + +#ifndef XMLRPC_PRIVATE_ALREADY_INCLUDED +/* + * Avoid include redundancy. + */ +#define XMLRPC_PRIVATE_ALREADY_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + + +/*---------------------------------------------------------------------------- + * xmlrpc_private.h + * + * Purpose: + * define non-public intra-library routines & data + * Comments: + */ + +/*---------------------------------------------------------------------------- + * Constants + */ + + +/*---------------------------------------------------------------------------- + * Includes + */ + +/*---------------------------------------------------------------------------- + * Structures + */ + +/* Some of these are typedef'd in xmlrpc.h for public use */ + +typedef struct _xmlrpc_vector* XMLRPC_VECTOR; + +/****s* VALUE/XMLRPC_VALUE + * NAME + * XMLRPC_VALUE + * NOTES + * A value of variable data type. The most important object in this API. :) + * + * This struct is opaque to callers and should be accessed only via accessor functions. + * SEE ALSO + * XMLRPC_REQUEST + * XMLRPC_CreateValueEmpty () + * XMLRPC_CleanupValue () + * SOURCE + */ +typedef struct _xmlrpc_value { + XMLRPC_VALUE_TYPE type; /* data type of this value */ + XMLRPC_VECTOR v; /* vector type specific info */ + simplestring str; /* string value buffer */ + simplestring id; /* id of this value. possibly empty. */ + int i; /* integer value. */ + double d; /* double value */ + int iRefCount; /* So we know when we can delete the value . */ +} STRUCT_XMLRPC_VALUE; +/******/ + +/****s* VALUE/XMLRPC_REQUEST + * NAME + * XMLRPC_REQUEST + * NOTES + * Internal representation of an XML request. + * + * This struct is opaque to callers and should be accessed only via accessor functions. + * + * SEE ALSO + * XMLRPC_VALUE + * XMLRPC_RequestNew () + * XMLRPC_RequestFree () + * SOURCE + */ +typedef struct _xmlrpc_request { + XMLRPC_VALUE io; /* data associated with this request */ + simplestring methodName; /* name of method being called */ + XMLRPC_REQUEST_TYPE request_type; /* type of request */ + STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS output; /* xml output options */ + XMLRPC_VALUE error; /* error codes */ +} STRUCT_XMLRPC_REQUEST; +/******/ + +/* Vector type. Used by XMLRPC_VALUE. Never visible to users of the API. */ +typedef struct _xmlrpc_vector { + XMLRPC_VECTOR_TYPE type; /* vector type */ + const char* id; /* ??? unused? */ + queue *q; /* list of child values */ +} STRUCT_XMLRPC_VECTOR; +/******/ + +/****s* VALUE/XMLRPC_SERVER + * NAME + * XMLRPC_SERVER + * NOTES + * internal representation of an xmlrpc server + * + * This struct is opaque to callers and should be accessed only via accessor functions. + * + * SEE ALSO + * XMLRPC_ServerCreate () + * XMLRPC_ServerDestroy () + * SOURCE + */ +typedef struct _xmlrpc_server { + queue methodlist; /* list of callback methods */ + queue docslist; /* list of introspection callbacks */ + XMLRPC_VALUE xIntrospection; +} STRUCT_XMLRPC_SERVER; +/******/ + +typedef struct _server_method { + char* name; + XMLRPC_VALUE desc; + XMLRPC_Callback method; +} server_method; + + +/*---------------------------------------------------------------------------- + * Globals + */ + +/*---------------------------------------------------------------------------- + * Functions + */ +server_method* find_method(XMLRPC_SERVER server, const char* name); +const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype); + +/*---------------------------------------------------------------------------- + * Macros + */ +#define my_free(thing) if(thing) {free(thing); thing = 0;} + + +#ifdef __cplusplus +} +#endif + + +#endif /* XMLRPC_PRIVATE_ALREADY_INCLUDED */ + diff --git a/ext/rpc/xmlrpc/php_config.h.in b/ext/rpc/xmlrpc/php_config.h.in new file mode 100644 index 0000000000..2c8a1bf2e2 --- /dev/null +++ b/ext/rpc/xmlrpc/php_config.h.in @@ -0,0 +1,11 @@ +/* php_config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Whether you have XMLRPC */ +#undef HAVE_XMLRPC + +/* Whether to build xmlrpc as dynamic module */ +#undef COMPILE_DL_XMLRPC + diff --git a/ext/rpc/xmlrpc/php_xmlrpc.h b/ext/rpc/xmlrpc/php_xmlrpc.h new file mode 100644 index 0000000000..77fbd573ee --- /dev/null +++ b/ext/rpc/xmlrpc/php_xmlrpc.h @@ -0,0 +1,133 @@ +/* + This file is part of, or distributed with, libXMLRPC - a C library for + xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* auto-generated portions of this file are also subject to the php license */ + +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: | + | | + +----------------------------------------------------------------------+ + */ + +#ifndef _PHP_XMLRPC_H +#define _PHP_XMLRPC_H + +/* You should tweak config.m4 so this symbol (or some else suitable) + gets defined. +*/ +#if 1 /* HAVE_XMLRPC */ + +extern zend_module_entry xmlrpc_module_entry; +#define phpext_xmlrpc_ptr &xmlrpc_module_entry + +#ifdef PHP_WIN32 +#define PHP_XMLRPC_API __declspec(dllexport) +#else +#define PHP_XMLRPC_API +#endif + +PHP_MINIT_FUNCTION(xmlrpc); +PHP_MSHUTDOWN_FUNCTION(xmlrpc); +PHP_RINIT_FUNCTION(xmlrpc); +PHP_RSHUTDOWN_FUNCTION(xmlrpc); +PHP_MINFO_FUNCTION(xmlrpc); + +PHP_FUNCTION(xmlrpc_encode); +PHP_FUNCTION(xmlrpc_decode); +PHP_FUNCTION(xmlrpc_decode_request); +PHP_FUNCTION(xmlrpc_encode_request); +PHP_FUNCTION(xmlrpc_get_type); +PHP_FUNCTION(xmlrpc_set_type); +PHP_FUNCTION(xmlrpc_server_create); +PHP_FUNCTION(xmlrpc_server_destroy); +PHP_FUNCTION(xmlrpc_server_register_method); +PHP_FUNCTION(xmlrpc_server_call_method); +PHP_FUNCTION(xmlrpc_parse_method_descriptions); +PHP_FUNCTION(xmlrpc_server_add_introspection_data); +PHP_FUNCTION(xmlrpc_server_register_introspection_callback); + +/* Fill in this structure and use entries in it + for thread safety instead of using true globals. +*/ +typedef struct { + /* You can use the next one as type if your module registers any + resources. Oh, you can of course rename it to something more + suitable, add list entry types or remove it if it not needed. + It's just an example. + */ + int le_xmlrpc_server; +} php_xmlrpc_globals; + +/* In every function that needs to use variables in php_xmlrpc_globals, + do call XMLRPCLS_FETCH(); after declaring other variables used by + that function, and always refer to them as XMLRPCG(variable). + You are encouraged to rename these macros something shorter, see + examples in any other php module directory. +*/ + +#ifdef ZTS +#define XMLRPCG(v) (xmlrpc_globals->v) +#define XMLRPCLS_FETCH() php_xmlrpc_globals *xmlrpc_globals = ts_resource(gd_xmlrpc_id) +#else +#define XMLRPCG(v) (xmlrpc_globals.v) +#define XMLRPCLS_FETCH() +#endif + +#else + +#define phpext_xmlrpc_ptr NULL + +#endif + +#endif /* _PHP_XMLRPC_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/rpc/xmlrpc/xmlrpc-epi-php.c b/ext/rpc/xmlrpc/xmlrpc-epi-php.c new file mode 100644 index 0000000000..589adaad05 --- /dev/null +++ b/ext/rpc/xmlrpc/xmlrpc-epi-php.c @@ -0,0 +1,1421 @@ +/* + This file is part of, or distributed with, libXMLRPC - a C library for + xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* auto-generated portions of this file are also subject to the php license */ + +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: | + | | + +----------------------------------------------------------------------+ + */ + +#include "php.h" +#include "php_ini.h" +#include "php_xmlrpc.h" +#include "xmlrpc.h" + +/* You should tweak config.m4 so this symbol (or some else suitable) + gets defined. +*/ + +#ifdef ZTS +int xmlrpc_globals_id; +#else +php_xmlrpc_globals xmlrpc_globals; +#endif + + +/* Every user visible function must have an entry in xmlrpc_functions[]. +*/ +function_entry xmlrpc_functions[] = { + PHP_FE(xmlrpc_encode, NULL) + PHP_FE(xmlrpc_decode, NULL) + PHP_FE(xmlrpc_decode_request, NULL) + PHP_FE(xmlrpc_encode_request, NULL) + PHP_FE(xmlrpc_get_type, NULL) + PHP_FE(xmlrpc_set_type, NULL) + PHP_FE(xmlrpc_server_create, NULL) + PHP_FE(xmlrpc_server_destroy, NULL) + PHP_FE(xmlrpc_server_register_method, NULL) + PHP_FE(xmlrpc_server_call_method, NULL) + PHP_FE(xmlrpc_parse_method_descriptions, NULL) + PHP_FE(xmlrpc_server_add_introspection_data, NULL) + PHP_FE(xmlrpc_server_register_introspection_callback, NULL) + {NULL, NULL, NULL} /* Must be the last line in xmlrpc_functions[] */ +}; + +zend_module_entry xmlrpc_module_entry = { + "xmlrpc", + xmlrpc_functions, + PHP_MINIT(xmlrpc), + PHP_MSHUTDOWN(xmlrpc), + PHP_RINIT(xmlrpc), /* Replace with NULL if there's nothing to do at request start */ + PHP_RSHUTDOWN(xmlrpc), /* Replace with NULL if there's nothing to do at request end */ + PHP_MINFO(xmlrpc), + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_XMLRPC +ZEND_GET_MODULE(xmlrpc) +#endif + +/* Remove comments and fill if you need to have entries in php.ini +PHP_INI_BEGIN() +PHP_INI_END() +*/ + +/******************************* +* local structures and defines * +*******************************/ + +// per server data +typedef struct _xmlrpc_server_data { + pval* method_map; + pval* introspection_map; + XMLRPC_SERVER server_ptr; +} xmlrpc_server_data; + + +// how to format output +typedef struct _php_output_options { + int b_php_out; + STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out; +} php_output_options; + +// data passed to C callback +typedef struct _xmlrpc_callback_data { + pval* xmlrpc_method; + pval* php_function; + pval* caller_params; + pval* return_data; + xmlrpc_server_data* server; + char php_executed; +} xmlrpc_callback_data; + +#define PHP_EXT_VERSION "0.41" + +// output options +#define OUTPUT_TYPE_KEY "output_type" +#define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1) +#define OUTPUT_TYPE_VALUE_PHP "php" +#define OUTPUT_TYPE_VALUE_XML "xml" + +#define VERBOSITY_KEY "verbosity" +#define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1) +#define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space" +#define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only" +#define VERBOSITY_VALUE_PRETTY "pretty" + +#define ESCAPING_KEY "escaping" +#define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1) +#define ESCAPING_VALUE_CDATA "cdata" +#define ESCAPING_VALUE_NON_ASCII "non-ascii" +#define ESCAPING_VALUE_NON_PRINT "non-print" +#define ESCAPING_VALUE_MARKUP "markup" + +#define VERSION_KEY "version" +#define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1) +#define VERSION_VALUE_SIMPLE "simple" +#define VERSION_VALUE_XMLRPC "xmlrpc" + +#define ENCODING_KEY "encoding" +#define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1) +#define ENCODING_DEFAULT "iso-8859-1" + +// value types +#define OBJECT_TYPE_ATTR "xmlrpc_type" +#define OBJECT_VALUE_ATTR "scalar" + + + +/*********************** +* forward declarations * +***********************/ +XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue); +static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data); + +/********************* +* startup / shutdown * +*********************/ + +static void destroy_server_data(xmlrpc_server_data *server) { + if(server) { + XMLRPC_ServerDestroy(server->server_ptr); + + zval_dtor(server->method_map); + FREE_ZVAL(server->method_map); + + zval_dtor(server->introspection_map); + FREE_ZVAL(server->introspection_map); + + efree(server); + } +} + +/* called when server is being destructed. either when xmlrpc_server_destroy + * is called, or when request ends. + */ +static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc) { + if(rsrc && rsrc->ptr) { + destroy_server_data((xmlrpc_server_data*)rsrc->ptr); + } +} + +/* module init */ +PHP_MINIT_FUNCTION(xmlrpc) +{ +/* Remove comments if you have entries in php.ini + REGISTER_INI_ENTRIES(); +*/ + XMLRPCG(le_xmlrpc_server) = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number); + + return SUCCESS; +} + +/* module shutdown */ +PHP_MSHUTDOWN_FUNCTION(xmlrpc) +{ +/* Remove comments if you have entries in php.ini + UNREGISTER_INI_ENTRIES(); +*/ + return SUCCESS; +} + +/* Remove if there's nothing to do at request start */ +PHP_RINIT_FUNCTION(xmlrpc) +{ + return SUCCESS; +} + +/* Remove if there's nothing to do at request end */ +PHP_RSHUTDOWN_FUNCTION(xmlrpc) +{ + return SUCCESS; +} + +/* display info in phpinfo() */ +PHP_MINFO_FUNCTION(xmlrpc) +{ + php_info_print_table_start(); + php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString()); + php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION); + php_info_print_table_row(2, "author", "Dan Libby"); + php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net"); + php_info_print_table_row(2, "open sourced by", "Epinions.com"); + php_info_print_table_end(); + + /* + DISPLAY_INI_ENTRIES(); + */ +} + +/******************* +* random utilities * +*******************/ + +/* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc). + * Could easily be further generalized to work with objects. + */ +static int add_long(pval* list, char* id, int num) { + if(id) return add_assoc_long(list, id, num); + else return add_next_index_long(list, num); +} + +static int add_double(pval* list, char* id, double num) { + if(id) return add_assoc_double(list, id, num); + else return add_next_index_double(list, num); +} + +static int add_string(pval* list, char* id, char* string, int duplicate) { + if(id) return add_assoc_string(list, id, string, duplicate); + else return add_next_index_string(list, string, duplicate); +} + +static int add_stringl(pval* list, char* id, char* string, uint length, int duplicate) { + if(id) return add_assoc_stringl(list, id, string, length, duplicate); + else return add_next_index_stringl(list, string, length, duplicate); +} + +static int add_pval(pval* list, const char* id, pval** val) { + if(list && val) { + if(id) return zend_hash_update(list->value.ht, (char*)id, strlen(id)+1, (void *) val, sizeof(pval **), NULL); + else return zend_hash_next_index_insert(list->value.ht, (void *) val, sizeof(pval **), NULL); + } +} + +#if ZEND_MODULE_API_NO >= 20001222 +#define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0) +#else +#define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index) +#endif + + +/************************* +* input / output options * +*************************/ + +/* parse an array (user input) into output options suitable for use by xmlrpc engine + * and determine whether to return data as xml or php vars + */ +static void set_output_options(php_output_options* options, pval* output_opts) { + + if(options) { + + /* defaults */ + options->b_php_out = 0; + options->xmlrpc_out.version = xmlrpc_version_1_0; + options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT; + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping; + + if(output_opts && output_opts->type == IS_ARRAY) { + pval** val; + + /* verbosity of generated xml */ + if(zend_hash_find(output_opts->value.ht, + OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, + (void**)&val) == SUCCESS) { + if((*val)->type == IS_STRING) { + if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_PHP)) { + options->b_php_out = 1; + } + else if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_XML)) { + options->b_php_out = 0; + } + } + } + + /* verbosity of generated xml */ + if(zend_hash_find(output_opts->value.ht, + VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, + (void**)&val) == SUCCESS) { + if((*val)->type == IS_STRING) { + if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NO_WHITE_SPACE)) { + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space; + } + else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NEWLINES_ONLY)) { + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only; + } + else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_PRETTY)) { + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; + } + } + } + + /* version of xml to output */ + if(zend_hash_find(output_opts->value.ht, + VERSION_KEY, VERSION_KEY_LEN + 1, + (void**)&val) == SUCCESS) { + if((*val)->type == IS_STRING) { + if(!strcmp((*val)->value.str.val, VERSION_VALUE_XMLRPC)) { + options->xmlrpc_out.version = xmlrpc_version_1_0; + } + else if(!strcmp((*val)->value.str.val, VERSION_VALUE_SIMPLE)) { + options->xmlrpc_out.version = xmlrpc_version_simple; + } + } + } + + /* encoding code set */ + if(zend_hash_find(output_opts->value.ht, + ENCODING_KEY, ENCODING_KEY_LEN + 1, + (void**)&val) == SUCCESS) { + if((*val)->type == IS_STRING) { + options->xmlrpc_out.xml_elem_opts.encoding = estrdup((*val)->value.str.val); + } + } + + /* escaping options */ + if(zend_hash_find(output_opts->value.ht, + ESCAPING_KEY, ESCAPING_KEY_LEN + 1, + (void**)&val) == SUCCESS) { + /* multiple values allowed. check if array */ + if((*val)->type == IS_ARRAY) { + pval** iter_val; + zend_hash_internal_pointer_reset((*val)->value.ht); + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping; + while(1) { + if(zend_hash_get_current_data((*val)->value.ht, (void**)&iter_val) == SUCCESS) { + if((*iter_val)->type == IS_STRING && (*iter_val)->value.str.val) { + if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_CDATA)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping; + } + else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping; + } + else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping; + } + else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_MARKUP)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping; + } + } + } + else { + break; + } + + zend_hash_move_forward((*val)->value.ht); + } + } + /* else, check for single value */ + else if((*val)->type == IS_STRING) { + if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_CDATA)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping; + } + else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping; + } + else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping; + } + else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_MARKUP)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping; + } + } + } + } + } +} + + +/****************** +* encode / decode * +******************/ + +/* php arrays have no distinction between array and struct types. + * they even allow mixed. Thus, we determine the type by iterating + * through the entire array and figuring out each element. + * room for some optimation here if we stop after a specific # of elements. + */ +static XMLRPC_VECTOR_TYPE determine_vector_type(HashTable *ht) { + int bArray = 0, bStruct = 0, bMixed = 0; + unsigned long num_index; + char* my_key; + + zend_hash_internal_pointer_reset(ht); + while(1) { + int res = my_zend_hash_get_current_key(ht, &my_key, &num_index); + if(res == HASH_KEY_IS_LONG) { + if(bStruct) { + bMixed = 1; + break; + } + bArray = 1; + } + else if(res == HASH_KEY_NON_EXISTANT) { + break; + } + else if(res == HASH_KEY_IS_STRING) { + if(bArray) { + bMixed = 1; + break; + } + bStruct = 1; + } + + zend_hash_move_forward(ht); + } + return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array); +} + +/* recursively convert php values into xmlrpc values */ +static XMLRPC_VALUE PHP_to_XMLRPC_worker(const char* key, pval* in_val, int depth) { + XMLRPC_VALUE xReturn = NULL; + if(in_val) { + pval* val = NULL; + XMLRPC_VALUE_TYPE type = get_pval_xmlrpc_type(in_val, &val); + if(val) { + switch(type) { + case xmlrpc_base64: + if(val->type == IS_NULL) { + xReturn = XMLRPC_CreateValueBase64(key, "", 1); + } + else { + xReturn = XMLRPC_CreateValueBase64(key, val->value.str.val, val->value.str.len); + } + break; + case xmlrpc_datetime: + convert_to_string(val); + xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, val->value.str.val); + break; + case xmlrpc_boolean: + convert_to_boolean(val); + xReturn = XMLRPC_CreateValueBoolean(key, val->value.lval); + break; + case xmlrpc_int: + convert_to_long(val); + xReturn = XMLRPC_CreateValueInt(key, val->value.lval); + break; + case xmlrpc_double: + convert_to_double(val); + xReturn = XMLRPC_CreateValueDouble(key, val->value.dval); + break; + case xmlrpc_string: + convert_to_string(val); + xReturn = XMLRPC_CreateValueString(key, val->value.str.val, val->value.str.len); + break; + case xmlrpc_vector: + { + unsigned long num_index; + pval** pIter; + char* my_key; + + convert_to_array(val); + + xReturn = XMLRPC_CreateVector(key, determine_vector_type(val->value.ht)); + + zend_hash_internal_pointer_reset(val->value.ht); + while(1) { + int res = my_zend_hash_get_current_key(val->value.ht, &my_key, &num_index); + if(res == HASH_KEY_IS_LONG) { + if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) { + XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(0, *pIter, depth++)); + } + } + else if(res == HASH_KEY_NON_EXISTANT) { + break; + } + else if(res == HASH_KEY_IS_STRING) { + if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) { + XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key, *pIter, depth++)); + } + } + + zend_hash_move_forward(val->value.ht); + } + } + break; + default: + break; + } + } + } + return xReturn; +} + +static XMLRPC_VALUE PHP_to_XMLRPC(pval* root_val) { + return PHP_to_XMLRPC_worker(NULL, root_val, 0); +} + +/* recursively convert xmlrpc values into php values */ +static pval* XMLRPC_to_PHP(XMLRPC_VALUE el) { + pval* elem = NULL; + char* pBuf; + const char* pStr; + + if(el) { + XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el); + + MAKE_STD_ZVAL(elem); /* init. very important. spent a frustrating day finding this out. */ + + switch(type) { + case xmlrpc_empty: + elem->type = IS_NULL; + break; + case xmlrpc_string: + pStr = XMLRPC_GetValueString(el); + if(pStr) { + elem->value.str.len = XMLRPC_GetValueStringLen(el); + elem->value.str.val = estrndup(pStr, elem->value.str.len); + elem->type = IS_STRING; + } + break; + case xmlrpc_int: + elem->value.lval = XMLRPC_GetValueInt(el); + elem->type = IS_LONG; + break; + case xmlrpc_boolean: + elem->value.lval = XMLRPC_GetValueBoolean(el); + elem->type = IS_BOOL; + break; + case xmlrpc_double: + elem->value.dval = XMLRPC_GetValueDouble(el); + elem->type = IS_DOUBLE; + break; + case xmlrpc_datetime: + elem->value.str.len = XMLRPC_GetValueStringLen(el); + elem->value.str.val = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), elem->value.str.len); + elem->type = IS_STRING; + break; + case xmlrpc_base64: + pStr = XMLRPC_GetValueBase64(el); + if(pStr) { + elem->value.str.len = XMLRPC_GetValueStringLen(el); + elem->value.str.val = estrndup(pStr, elem->value.str.len); + elem->type = IS_STRING; + } + break; + case xmlrpc_vector: + if(array_init(elem) == SUCCESS) { + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el); + + while( xIter ) { + pval* val = XMLRPC_to_PHP(xIter); + if(val) { + add_pval(elem, XMLRPC_GetValueID(xIter), &val); + } + xIter = XMLRPC_VectorNext(el); + } + } + break; + default: + break; + } + set_pval_xmlrpc_type(elem, type); + } + return elem; +} + +/* {{{ proto string xmlrpc_encode_request(string method, mixed params) + generate xml for a method request */ +PHP_FUNCTION(xmlrpc_encode_request) { + XMLRPC_REQUEST xRequest = NULL; + pval* method, *vals, *out_opts; + char* outBuf; + php_output_options out; + + if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || + getParameters(ht, ARG_COUNT(ht), &method, &vals, &out_opts) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + set_output_options(&out, (ARG_COUNT(ht) == 3) ? out_opts : 0); + + if(return_value_used) { + xRequest = XMLRPC_RequestNew(); + + if(xRequest) { + XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out); + if(method->type == IS_NULL) { + XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response); + } + else { + XMLRPC_RequestSetMethodName(xRequest, method->value.str.val); + XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call); + } + if(vals->type != IS_NULL) { + XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals)); + } + + outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0); + if(outBuf) { + RETVAL_STRING(outBuf, 1); + free(outBuf); + } + XMLRPC_RequestFree(xRequest, 1); + } + } +} + +/* {{{ proto string xmlrpc_encode(mixed value) + generate xml for a PHP value */ +PHP_FUNCTION(xmlrpc_encode) +{ + XMLRPC_VALUE xOut = NULL; + pval* arg1, *out_opts; + php_output_options out; + char* outBuf; + + if( !(ARG_COUNT(ht) == 1) || + getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + if( return_value_used ) { + /* convert native php type to xmlrpc type */ + xOut = PHP_to_XMLRPC(arg1); + + /* generate raw xml from xmlrpc data */ + outBuf = XMLRPC_VALUE_ToXML(xOut, 0); + + if(xOut) { + if(outBuf) { + RETVAL_STRING(outBuf, 1); + free(outBuf); + } + /* cleanup */ + XMLRPC_CleanupValue(xOut); + } + } +} + + +/* }}} */ + +pval* decode_request_worker(pval* xml_in, pval* encoding_in, pval* method_name_out) { + pval* retval = NULL; + XMLRPC_REQUEST response; + STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {0}; + opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in->value.str.val) : ENCODING_DEFAULT; + + /* generate XMLRPC_REQUEST from raw xml */ + response = XMLRPC_REQUEST_FromXML(xml_in->value.str.val, xml_in->value.str.len, &opts); + if(response) { + /* convert xmlrpc data to native php types */ + retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response)); + + if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) { + if(method_name_out) { + convert_to_string(method_name_out); + method_name_out->type = IS_STRING; + method_name_out->value.str.val = estrdup(XMLRPC_RequestGetMethodName(response)); + method_name_out->value.str.len = strlen(method_name_out->value.str.val); + } + } + + /* dust, sweep, and mop */ + XMLRPC_RequestFree(response, 1); + } + return retval; +} + +/* {{{ proto array xmlrpc_decode_request(string xml, string& method, [string encoding]) + decode xml into native php types */ +PHP_FUNCTION(xmlrpc_decode_request) +{ + pval* xml, *method, *encoding = NULL; + + if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &xml, &method, &encoding) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } +#if ZEND_MODULE_API_NO < 20010901 + if (!ParameterPassedByReference(ht,2)) { + zend_error(E_WARNING,"second argument to xmlrpc_decode_request() passed by value, expecting reference"); + } +#endif + + convert_to_string(xml); + convert_to_string(method); + if(ARG_COUNT(ht) == 3) { + convert_to_string(encoding); + } + + if(return_value_used) { + pval* retval = decode_request_worker(xml, encoding, method); + if(retval) { + *return_value = *retval; + zval_copy_ctor(return_value); + } + } +} +/* }}} */ + + +/* {{{ proto array xmlrpc_decode(string xml, [string encoding]) + decode xml into native php types */ +PHP_FUNCTION(xmlrpc_decode) +{ + pval* arg1, *arg2 = NULL; + + if( !(ARG_COUNT(ht) == 1 || ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg1, &arg2) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + convert_to_string(arg1); + if(ARG_COUNT(ht) == 2) { + convert_to_string(arg2); + } + + if(return_value_used) { + pval* retval = decode_request_worker(arg1, arg2, NULL); + if(retval) { + *return_value = *retval; + zval_copy_ctor(return_value); + } + } +} +/* }}} */ + + +/************************* +* server related methods * +*************************/ + +/* {{{ proto handle xmlrpc_server_create() + create an xmlrpc server */ +PHP_FUNCTION(xmlrpc_server_create) { + if(ARG_COUNT(ht) != 0) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + if(return_value_used) { + pval *method_map, *introspection_map; + MAKE_STD_ZVAL(method_map); + MAKE_STD_ZVAL(introspection_map); + + if(array_init(method_map) == SUCCESS && array_init(introspection_map) == SUCCESS) { + /* allocate server data. free'd in destroy_server_data() */ + xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data)); + + if(server) { + server->method_map = method_map; + server->introspection_map = introspection_map; + server->server_ptr = XMLRPC_ServerCreate(); + + XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback); + + /* store for later use */ + ZEND_REGISTER_RESOURCE(return_value,server, XMLRPCG(le_xmlrpc_server)); + } + } + } +} + +/* {{{ proto void xmlrpc_server_destroy(handle server) + destroy server resources */ +PHP_FUNCTION(xmlrpc_server_destroy) { + pval* arg1; + int bSuccess = FAILURE; + + if(ARG_COUNT(ht) != 1 || getParameters(ht, 1, &arg1) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + if(arg1->type == IS_RESOURCE) { + int type; + + xmlrpc_server_data *server = zend_list_find(arg1->value.lval, &type); + + if(server && type == XMLRPCG(le_xmlrpc_server)) { + bSuccess = zend_list_delete(arg1->value.lval); + + /* called by hashtable destructor + * destroy_server_data(server); + */ + } + } + RETVAL_LONG(bSuccess == SUCCESS); +} + + +/* called by xmlrpc C engine as method handler for all registered methods. + * it then calls the corresponding PHP function to handle the method. + */ +static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) { + pval *retval_ptr; + xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data; + pval* xmlrpc_params; + pval* callback_params[3]; + + /* convert xmlrpc to native php types */ + xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest)); + + /* setup data hoojum */ + callback_params[0] = pData->xmlrpc_method; + callback_params[1] = xmlrpc_params; + callback_params[2] = pData->caller_params; + + /* Use same C function for all methods */ + + /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */ + call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params); + + pData->php_executed = 1; +} + +/* called by the C server when it first receives an introspection request. We pass this on to + * our PHP listeners, if any + */ +static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) { + pval *retval_ptr, **php_function; + pval* callback_params[1]; + xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data; + + MAKE_STD_ZVAL(retval_ptr); + retval_ptr->type = IS_NULL; + + /* setup data hoojum */ + callback_params[0] = pData->caller_params; + + /* loop through and call all registered callbacks */ + zend_hash_internal_pointer_reset(pData->server->introspection_map->value.ht); + while(1) { + if(zend_hash_get_current_data(pData->server->introspection_map->value.ht, + (void**)&php_function) == SUCCESS) { + + /* php func prototype: function string user_func($user_params) */ + if(call_user_function(CG(function_table), NULL, *php_function, + retval_ptr, 1, callback_params) == SUCCESS) { + XMLRPC_VALUE xData; + STRUCT_XMLRPC_ERROR err = {0}; + + /* return value should be a string */ + convert_to_string(retval_ptr); + + xData = XMLRPC_IntrospectionCreateDescription(retval_ptr->value.str.val, &err); + + if(xData) { + if(!XMLRPC_ServerAddIntrospectionData(server, xData)) { + zend_error(E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", (*php_function)->value.str.val); + } + XMLRPC_CleanupValue(xData); + } + else { + /* could not create description */ + if(err.xml_elem_error.parser_code) { + zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to add introspection data returned from %s()", + err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, (*php_function)->value.str.val); + } + else { + zend_error(E_WARNING, "Unable to add introspection data returned from %s()", + (*php_function)->value.str.val); + } + } + } + else { + /* user func failed */ + zend_error(E_WARNING, "Error calling user introspection callback: %s()", (*php_function)->value.str.val); + } + } + else { + break; + } + + zend_hash_move_forward(pData->server->introspection_map->value.ht); + } + + /* so we don't call the same callbacks ever again */ + zend_hash_clean(pData->server->introspection_map->value.ht); +} + +/* {{{ proto boolean xmlrpc_server_register_method(handle server, string method_name, string function) + register a php function to handle method matching method_name */ +PHP_FUNCTION(xmlrpc_server_register_method) { + + pval* method_key, *method_name, *handle, *method_name_save; + int type; + xmlrpc_server_data* server; + + /* get some params. should be 3 */ + if( !(ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &handle, &method_key, &method_name) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + server = zend_list_find(handle->value.lval, &type); + + if(type == XMLRPCG(le_xmlrpc_server)) { + /* register with C engine. every method just calls our standard callback, + * and it then dispatches to php as necessary + */ + if(XMLRPC_ServerRegisterMethod(server->server_ptr, method_key->value.str.val, php_xmlrpc_callback)) { + /* save for later use */ + MAKE_STD_ZVAL(method_name_save); + *method_name_save = *method_name; + zval_copy_ctor(method_name_save); + + /* register our php method */ + add_pval(server->method_map, method_key->value.str.val, &method_name_save); + + RETURN_BOOL(1); + } + } + RETURN_BOOL(0); +} + + +/* {{{ proto boolean xmlrpc_server_register_introspection_callback(handle server, string function) + register a php function to generate documentation */ +PHP_FUNCTION(xmlrpc_server_register_introspection_callback) { + + pval* method_key, *method_name, *handle, *method_name_save; + int type; + xmlrpc_server_data* server; + + /* get some params. should be 2 */ + if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &method_name) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + server = zend_list_find(handle->value.lval, &type); + + if(type == XMLRPCG(le_xmlrpc_server)) { + { + /* save for later use */ + MAKE_STD_ZVAL(method_name_save); + *method_name_save = *method_name; + zval_copy_ctor(method_name_save); + + /* register our php method */ + add_pval(server->introspection_map, NULL, &method_name_save); + + RETURN_BOOL(1); + } + } + RETURN_BOOL(0); +} + + +/* this function is itchin for a re-write */ + +/* {{{ proto mixed xmlrpc_server_call_method(handle server, string xml, mixed user_data, [array output_options]) + parse xml request and call method */ +PHP_FUNCTION(xmlrpc_server_call_method) { + xmlrpc_callback_data data = {0}; + XMLRPC_REQUEST xRequest; + STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts; + xmlrpc_server_data* server; + pval *rawxml, *caller_params, *handle, *output_opts; + int type; + php_output_options out; + + /* get params. 3 or 4 params ok */ + if(ARG_COUNT(ht) == 4) { + if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params, &output_opts) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + /* user output options */ + set_output_options(&out, output_opts); + } + else if(ARG_COUNT(ht) == 3) { + if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params) != SUCCESS) { + WRONG_PARAM_COUNT; + } + /* user output options */ + set_output_options(&out, NULL); + } + else { + WRONG_PARAM_COUNT; + } + + server = zend_list_find(handle->value.lval, &type); + + if(type == XMLRPCG(le_xmlrpc_server)) { + /* HACK: use output encoding for now */ + input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding); + + /* generate an XMLRPC_REQUEST from the raw xml input */ + xRequest = XMLRPC_REQUEST_FromXML(rawxml->value.str.val, rawxml->value.str.len, &input_opts); + + if(xRequest) { + + /* check if we have a method name -- indicating success and all manner of good things */ + if(XMLRPC_RequestGetMethodName(xRequest)) { + pval** php_function, *returned = NULL; + XMLRPC_VALUE xAnswer = NULL; + MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */ + MAKE_STD_ZVAL(data.return_data); + data.return_data->type = IS_NULL; /* in case value is never init'd, we don't dtor to think it is a string or something */ + data.xmlrpc_method->type = IS_NULL; + + /* setup some data to pass to the callback function */ + data.xmlrpc_method->value.str.val = estrdup(XMLRPC_RequestGetMethodName(xRequest)); + data.xmlrpc_method->value.str.len = strlen(data.xmlrpc_method->value.str.val); + data.xmlrpc_method->type = IS_STRING; + data.caller_params = caller_params; + data.php_executed = 0; + data.server = server; + + /* check if the called method has been previous registered */ + if(zend_hash_find(server->method_map->value.ht, + data.xmlrpc_method->value.str.val, + data.xmlrpc_method->value.str.len + 1, + (void**)&php_function) == SUCCESS) { + + data.php_function = *php_function; + } + + /* We could just call the php method directly ourselves at this point, but we do this + * with a C callback in case the xmlrpc library ever implements some cool usage stats, + * or somesuch. + */ + xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data); + if(xAnswer) { + if(out.b_php_out) { + zval_dtor(data.return_data); + FREE_ZVAL(data.return_data); + data.return_data = XMLRPC_to_PHP(xAnswer); + } + } + else if(data.php_executed) { + if(!out.b_php_out) { + xAnswer = PHP_to_XMLRPC(data.return_data); + } + } + + /* should we return data as xml? */ + if(!out.b_php_out) { + XMLRPC_REQUEST xResponse = XMLRPC_RequestNew(); + if(xResponse) { + char* outBuf = 0; + int buf_len = 0; + + /* set some required request hoojum */ + XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out); + XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response); + XMLRPC_RequestSetData(xResponse, xAnswer); + + /* generate xml */ + outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len); + if(outBuf) { + RETVAL_STRINGL(outBuf, buf_len, 1); + free(outBuf); + } + /* cleanup after ourselves. what a sty! */ + XMLRPC_RequestFree(xResponse, 0); + } + } + /* or as native php types? */ + else { + *return_value = *data.return_data; + zval_copy_ctor(return_value); + } + + /* cleanup after ourselves. what a sty! */ + zval_dtor(data.xmlrpc_method); + FREE_ZVAL(data.xmlrpc_method); + zval_dtor(data.return_data); + FREE_ZVAL(data.return_data); + + if(xAnswer) { + XMLRPC_CleanupValue(xAnswer); + } + } + + XMLRPC_RequestFree(xRequest, 1); + } + } +} + + +/* {{{ proto int xmlrpc_server_add_introspection_data(handle server, array desc) + add introspection documentation */ +PHP_FUNCTION(xmlrpc_server_add_introspection_data) { + + pval *method, *handle, *desc; + int type; + xmlrpc_server_data* server; + + /* get some params. should be 2 */ + if ( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &desc) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + server = zend_list_find(handle->value.lval, &type); + + if (type == XMLRPCG(le_xmlrpc_server)) { + XMLRPC_VALUE xDesc = PHP_to_XMLRPC(desc); + if (xDesc) { + int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc); + XMLRPC_CleanupValue(xDesc); + RETURN_LONG(retval); + } + } + RETURN_LONG(0); +} + + +/* {{{ proto array xmlrpc_parse_method_descriptions(string xml) + decode xml into a list of method descriptions */ +PHP_FUNCTION(xmlrpc_parse_method_descriptions) +{ + pval* arg1, *arg2, *retval; + + if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + convert_to_string(arg1); + + if(return_value_used) { + STRUCT_XMLRPC_ERROR err = {0}; + XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1->value.str.val, &err); + if(xVal) { + retval = XMLRPC_to_PHP(xVal); + + if(retval) { + *return_value = *retval; + zval_copy_ctor(return_value); + } + /* dust, sweep, and mop */ + XMLRPC_CleanupValue(xVal); + } + else { + /* could not create description */ + if(err.xml_elem_error.parser_code) { + zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to create introspection data", + err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error); + } + else { + zend_error(E_WARNING, "Invalid xml structure. Unable to create introspection data"); + } + + zend_error(E_WARNING, "xml parse error. no method description created"); + } + } +} + + +/************ +* type data * +************/ + +#define XMLRPC_TYPE_COUNT 9 +#define XMLRPC_VECTOR_TYPE_COUNT 4 +#define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT) + +/* return a string matching a given xmlrpc type */ +static const char** get_type_str_mapping() { + static const char* str_mapping[TYPE_STR_MAP_SIZE]; + static int first = 1; + if(first) { + /* warning. do not add/delete without changing size define */ + str_mapping[xmlrpc_none] = "none"; + str_mapping[xmlrpc_empty] = "empty"; + str_mapping[xmlrpc_base64] = "base64"; + str_mapping[xmlrpc_boolean] = "boolean"; + str_mapping[xmlrpc_datetime] = "datetime"; + str_mapping[xmlrpc_double] = "double"; + str_mapping[xmlrpc_int] = "int"; + str_mapping[xmlrpc_string] = "string"; + str_mapping[xmlrpc_vector] = "vector"; + str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none"; + str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array"; + str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed"; + str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct"; + first = 0; + } + return (const char**)str_mapping; +} + +/* map an xmlrpc type to a string */ +const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) { + const char** str_mapping = get_type_str_mapping(); + if(vtype == xmlrpc_vector_none) { + return str_mapping[type]; + } + else { + return str_mapping[XMLRPC_TYPE_COUNT + vtype]; + } +} + +/* map a string to an xmlrpc type */ +XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) { + const char** str_mapping = get_type_str_mapping(); + int i; + + if(str) { + for(i = 0; i < XMLRPC_TYPE_COUNT; i++) { + if(!strcmp(str_mapping[i], str)) { + return (XMLRPC_VALUE_TYPE)i; + } + } + } + return xmlrpc_none; +} + +/* map a string to an xmlrpc vector type */ +XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) { + const char** str_mapping = get_type_str_mapping(); + int i; + + if(str) { + for(i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) { + if(!strcmp(str_mapping[i], str)) { + return (XMLRPC_VECTOR_TYPE)(i - XMLRPC_TYPE_COUNT); + } + } + } + return xmlrpc_none; +} + + +/* set a given value to a particular type. + * note: this only works on strings, and only for date and base64, + * which do not have native php types. black magic lies herein. + */ +int set_pval_xmlrpc_type(pval* value, XMLRPC_VALUE_TYPE type) { + int bSuccess = FAILURE; + + /* we only really care about strings because they can represent + * base64 and datetime. all other types have corresponding php types + */ + if(value->type == IS_STRING) { + if(type == xmlrpc_base64 || type == xmlrpc_datetime) { + const char* typestr = xmlrpc_type_as_str(type, xmlrpc_vector_none); + pval* type; + + MAKE_STD_ZVAL(type); + + type->type = IS_STRING; + type->value.str.val = estrdup(typestr); + type->value.str.len = strlen(typestr); + + convert_to_object(value); + bSuccess = zend_hash_update(value->value.obj.properties, OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL); + } + } + + return bSuccess; +} + +/* return xmlrpc type of a php value */ +XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue) { + XMLRPC_VALUE_TYPE type = xmlrpc_none; + + if(value) { + switch(value->type) { + case IS_NULL: + type = xmlrpc_base64; + break; + #ifndef BOOL_AS_LONG + + /* Right thing to do, but it breaks some legacy code. */ + case IS_BOOL: + type = xmlrpc_boolean; + break; + #else + case IS_BOOL: + #endif + case IS_LONG: + case IS_RESOURCE: + type = xmlrpc_int; + break; + case IS_DOUBLE: + type = xmlrpc_double; + break; + case IS_CONSTANT: + type = xmlrpc_string; + break; + case IS_STRING: + type = xmlrpc_string; + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + type = xmlrpc_vector; + break; + case IS_OBJECT: + { + pval** attr; + type = xmlrpc_vector; + + if(zend_hash_find(value->value.obj.properties, + OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), + (void**)&attr) == SUCCESS) { + if((*attr)->type == IS_STRING) { + type = xmlrpc_str_as_type((*attr)->value.str.val); + } + } + break; + } + } + + /* if requested, return an unmolested (magic removed) copy of the value */ + if(newvalue) { + pval** val; + if( (type == xmlrpc_base64 && value->type != IS_NULL) || type == xmlrpc_datetime) { + if(zend_hash_find(value->value.obj.properties, + OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), + (void**)&val) == SUCCESS) { + *newvalue = *val; + } + } + else { + *newvalue = value; + } + } + } + + return type; +} + + +/* {{{ proto bool xmlrpc_set_type(string value, string type) + set xmlrpc type, base64 or datetime, for a php string value */ +PHP_FUNCTION(xmlrpc_set_type) { + pval* arg, *type; + XMLRPC_VALUE_TYPE vtype; + + if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg, &type) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } +#if ZEND_MODULE_API_NO < 20010901 + if (!ParameterPassedByReference(ht,1)) { + zend_error(E_WARNING,"first argument to xmlrpc_set_type() passed by value, expecting reference"); + } +#endif + + convert_to_string(type); + vtype = xmlrpc_str_as_type(type->value.str.val); + if(vtype != xmlrpc_none) { + if(set_pval_xmlrpc_type(arg, vtype) == SUCCESS) { + RETURN_TRUE; + } + } + else { + zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type->value.str.val); + } + RETURN_FALSE; +} + +/* {{{ proto string xmlrpc_get_type(mixed value) + get xmlrpc type for a php value. especially useful for base64 and datetime strings */ +PHP_FUNCTION(xmlrpc_get_type) { + pval* arg; + XMLRPC_VALUE_TYPE type; + XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none; + + if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + type = get_pval_xmlrpc_type(arg, 0); + if(type == xmlrpc_vector) { + vtype = determine_vector_type(arg->value.ht); + } + + RETURN_STRING((char*)xmlrpc_type_as_str(type, vtype), 1); +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + diff --git a/ext/xmlrpc/CREDITS b/ext/xmlrpc/CREDITS new file mode 100644 index 0000000000..cfb14faf80 --- /dev/null +++ b/ext/xmlrpc/CREDITS @@ -0,0 +1,2 @@ +xmlrpc +Dan Libby diff --git a/ext/xmlrpc/EXPERIMENTAL b/ext/xmlrpc/EXPERIMENTAL new file mode 100644 index 0000000000..6443e99646 --- /dev/null +++ b/ext/xmlrpc/EXPERIMENTAL @@ -0,0 +1,5 @@ +this extension is experimental, +its functions may change their names +or move to extension all together +so do not rely to much on them +you have been warned! diff --git a/ext/xmlrpc/Makefile.in b/ext/xmlrpc/Makefile.in new file mode 100644 index 0000000000..a3ba38b901 --- /dev/null +++ b/ext/xmlrpc/Makefile.in @@ -0,0 +1,12 @@ +# $Id$ + +LTLIBRARY_NAME = libxmlrpc.la +LTLIBRARY_SOURCES = xmlrpc-epi-php.c +LTLIBRARY_SHARED_NAME = xmlrpc.la +EXTRA_INCLUDES = -I$(XMLRPC_INC_DIR) +LTLIBRARY_LIBADD = $(XMLRPC_LIBADD) +LTLIBRARY_SHARED_LIBADD = $(XMLRPC_SHARED_LIBADD) + +SUBDIRS = $(XMLRPC_SUBDIRS) + +include $(top_srcdir)/build/dynlib.mk diff --git a/ext/xmlrpc/config.m4 b/ext/xmlrpc/config.m4 new file mode 100644 index 0000000000..18809032db --- /dev/null +++ b/ext/xmlrpc/config.m4 @@ -0,0 +1,122 @@ + +dnl $Id$ + +sinclude(ext/xmlrpc/libxmlrpc/acinclude.m4) +sinclude(ext/xmlrpc/libxmlrpc/xmlrpc.m4) +sinclude(libxmlrpc/acinclude.m4) +sinclude(libxmlrpc/xmlrpc.m4) + +AC_DEFUN(XMLRPC_LIB_CHK,[ + str="$XMLRPC_DIR/$1/libxmlrpc.*" + for j in `echo $str`; do + if test -r $j; then + XMLRPC_LIB_DIR=$XMLRPC_DIR/$1 + break 2 + fi + done +]) + +PHP_ARG_WITH(xmlrpc, for XMLRPC-EPI support, +[ --with-xmlrpc[=DIR] Include XMLRPC-EPI support. DIR is the XMLRPC-EPI base + directory. If unspecified, the bundled XMLRPC-EPI library + will be used.], yes) + +PHP_ARG_WITH(expat-dir, libexpat dir for XMLRPC-EPI +[ --with-expat-dir=DIR XMLRPC-EPI: libexpat dir for XMLRPC-EPI]) + + +if test "$PHP_XMLRPC" != "no"; then + AC_DEFINE(HAVE_XMLRPC, 1, [Whether you have XMLRPC-EPI]) + PHP_EXTENSION(xmlrpc,$ext_shared) + +dnl check for iconv + found_iconv=no + AC_CHECK_LIB(c, iconv_open, found_iconv=yes) + if test "$found_iconv" = "no"; then + for i in /usr /usr/local $ICONV_DIR; do + if test -f $i/lib/libconv.a -o -f $i/lib/libiconv.s?; then + PHP_ADD_LIBRARY_WITH_PATH(iconv, $i/lib, XMLRPC_SHARED_LIBADD) + found_iconv=yes + fi + done + fi + + if test "$found_iconv" = "no"; then + AC_MSG_ERROR(iconv not found, in order to build XMLRPC-EPI you need the iconv library) + fi + +dnl check for expat + testval=no + for i in $PHP_EXPAT_DIR; do + if test -f $i/lib/libexpat.a -o -f $i/lib/libexpat.s?; then + AC_DEFINE(HAVE_LIBEXPAT2,1,[ ]) + PHP_ADD_LIBRARY_WITH_PATH(expat, $i/lib, XMLRPC_SHARED_LIBADD) + PHP_ADD_INCLUDE($i/include) + testval=yes + fi + done + + if test "$testval" = "no"; then + PHP_ADD_LIBRARY(xmlparse) + PHP_ADD_LIBRARY(xmltok) + fi + +fi + +if test "$PHP_XMLRPC" = "yes"; then + XMLRPC_CHECKS + XMLRPC_LIBADD=libxmlrpc/libxmlrpc.la + XMLRPC_SHARED_LIBADD=libxmlrpc/libxmlrpc.la + XMLRPC_SUBDIRS=libxmlrpc + PHP_SUBST(XMLRPC_LIBADD) + PHP_SUBST(XMLRPC_SUBDIRS) + LIB_BUILD($ext_builddir/libxmlrpc,$ext_shared,yes) + PHP_ADD_INCLUDE($ext_srcdir/libxmlrpc) + XMLRPC_MODULE_TYPE=builtin +elif test "$PHP_XMLRPC" != "no"; then + for i in $PHP_XMLRPC; do + if test -r $i/include/xmlrpc/xmlrpc.h; then + XMLRPC_DIR=$i + XMLRPC_INC_DIR=$i/include/xmlrpc + elif test -r $i/include/xmlrpc.h; then + XMLRPC_DIR=$i + XMLRPC_INC_DIR=$i/include + fi + done + + if test -z "$XMLRPC_DIR"; then + AC_MSG_ERROR(Cannot find header files under $PHP_XMLRPC) + fi + + XMLRPC_MODULE_TYPE=external + + for i in lib lib/xmlrpc; do + XMLRPC_LIB_CHK($i) + done + + if test -z "$XMLRPC_LIB_DIR"; then + AC_MSG_ERROR(Cannot find xmlrpc library under $XMLRPC_DIR) + fi + + if test "$PHP_ZLIB_DIR" != "no"; then + PHP_ADD_LIBRARY(z,, XMLRPC_SHARED_LIBADD) + XMLRPC_LIBS="-L$PHP_ZLIB_DIR -z" + fi + + PHP_ADD_LIBRARY_WITH_PATH(xmlrpc, $XMLRPC_LIB_DIR, XMLRPC_SHARED_LIBADD) + XMLRPC_LIBS="-L$XMLRPC_LIB_DIR -lxmlrpc $XMLRPC_LIBS" + + PHP_ADD_INCLUDE($XMLRPC_INC_DIR) + XMLRPC_INCLUDE="-I$XMLRPC_INC_DIR" + +else + XMLRPC_MODULE_TYPE=none +fi + + +PHP_SUBST(XMLRPC_SHARED_LIBADD) +PHP_SUBST_OLD(XMLRPC_MODULE_TYPE) +PHP_SUBST_OLD(XMLRPC_LIBS) +PHP_SUBST_OLD(XMLRPC_INCLUDE) + + diff --git a/ext/xmlrpc/libs.mk b/ext/xmlrpc/libs.mk new file mode 100644 index 0000000000..7c6e176717 --- /dev/null +++ b/ext/xmlrpc/libs.mk @@ -0,0 +1,7 @@ +include $(top_builddir)/config_vars.mk +LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X) +LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo) +$(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) + $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD) + +targets = $(LTLIBRARY_NAME) diff --git a/ext/xmlrpc/libxmlrpc/Makefile.in b/ext/xmlrpc/libxmlrpc/Makefile.in new file mode 100644 index 0000000000..44257c4882 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/Makefile.in @@ -0,0 +1,9 @@ + +LTLIBRARY_NAME = libxmlrpc.la +LTLIBRARY_SOURCES = base64.c simplestring.c xml_to_dandarpc.c \ + xmlrpc_introspection.c encodings.c system_methods.c \ + xml_to_xmlrpc.c queue.c xml_element.c xmlrpc.c + +DEFS = -DVERSION="0.41" + +include $(top_srcdir)/build/dynlib.mk diff --git a/ext/xmlrpc/libxmlrpc/README b/ext/xmlrpc/libxmlrpc/README new file mode 100644 index 0000000000..323edfa671 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/README @@ -0,0 +1,17 @@ +organization of this directory is moving towards this approach: + +<module>.h -- public API and data types +<module>_private.h -- protected API and data types +<module>.c -- implementation and private API / types + +The rules are: +.c files may include *_private.h. +.h files may not include *_private.h + +This allows us to have a nicely encapsulated C api with opaque data types and private functions +that are nonetheless shared between source files without redundant extern declarations.. + + + + + diff --git a/ext/xmlrpc/libxmlrpc/acinclude.m4 b/ext/xmlrpc/libxmlrpc/acinclude.m4 new file mode 100644 index 0000000000..9535e45972 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/acinclude.m4 @@ -0,0 +1,38 @@ +# Local macros for automake & autoconf + +AC_DEFUN(XMLRPC_FUNCTION_CHECKS,[ + +# Standard XMLRPC list +AC_CHECK_FUNCS( \ + strtoul strtoull snprintf \ + strstr strpbrk strerror\ + memcpy memmove) + +]) + +AC_DEFUN(XMLRPC_HEADER_CHECKS,[ +AC_HEADER_STDC +AC_CHECK_HEADERS(xmlparse.h xmltok.h stdlib.h strings.h string.h) +]) + +AC_DEFUN(XMLRPC_TYPE_CHECKS,[ + +AC_REQUIRE([AC_C_CONST]) +AC_REQUIRE([AC_C_INLINE]) +AC_CHECK_SIZEOF(char, 1) + +AC_CHECK_SIZEOF(int, 4) +AC_CHECK_SIZEOF(long, 4) +AC_CHECK_SIZEOF(long long, 8) +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_TYPE_UID_T + +XMLRPC_CHECK_ULONG +XMLRPC_CHECK_UCHAR +XMLRPC_CHECK_UINT +XMLRPC_CHECK_INT_8_16_32 + +XMLRPC_TYPE_QSORT + +]) diff --git a/ext/xmlrpc/libxmlrpc/base64.c b/ext/xmlrpc/libxmlrpc/base64.c new file mode 100644 index 0000000000..bdbf95bcba --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/base64.c @@ -0,0 +1,192 @@ +static const char rcsid[] = "#(@) $Id$"; + +/* + + Encode or decode file as MIME base64 (RFC 1341) + + by John Walker + http://www.fourmilab.ch/ + + This program is in the public domain. + +*/ +#include <stdio.h> + +/* ENCODE -- Encode binary file into base64. */ +#include <stdlib.h> + +#include "base64.h" + +static unsigned char dtable[512]; + +void buffer_new(struct buffer_st *b) +{ + b->length = 512; + b->data = malloc(sizeof(char)*(b->length)); + b->data[0] = 0; + b->ptr = b->data; + b->offset = 0; +} + +void buffer_add(struct buffer_st *b, char c) +{ + *(b->ptr++) = c; + b->offset++; + if (b->offset == b->length) { + b->length += 512; + b->data = realloc(b->data, b->length); + b->ptr = b->data + b->offset; + } +} + +void buffer_delete(struct buffer_st *b) +{ + free(b->data); + b->length = 0; + b->offset = 0; + b->ptr = NULL; + b->data = NULL; +} + +void base64_encode(struct buffer_st *b, const char *source, int length) +{ + int i, hiteof = 0; + int offset = 0; + int olen; + char *dest; + + olen = 0; + + buffer_new(b); + + /* Fill dtable with character encodings. */ + + for (i = 0; i < 26; i++) { + dtable[i] = 'A' + i; + dtable[26 + i] = 'a' + i; + } + for (i = 0; i < 10; i++) { + dtable[52 + i] = '0' + i; + } + dtable[62] = '+'; + dtable[63] = '/'; + + while (!hiteof) { + unsigned char igroup[3], ogroup[4]; + int c, n; + + igroup[0] = igroup[1] = igroup[2] = 0; + for (n = 0; n < 3; n++) { + c = *(source++); + offset++; + if (offset > length) { + hiteof = 1; + break; + } + igroup[n] = (unsigned char) c; + } + if (n > 0) { + ogroup[0] = dtable[igroup[0] >> 2]; + ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)]; + ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)]; + ogroup[3] = dtable[igroup[2] & 0x3F]; + + /* Replace characters in output stream with "=" pad + characters if fewer than three characters were + read from the end of the input stream. */ + + if (n < 3) { + ogroup[3] = '='; + if (n < 2) { + ogroup[2] = '='; + } + } + for (i = 0; i < 4; i++) { + buffer_add(b, ogroup[i]); + if (!(b->offset % 72)) { + // buffer_add(b, '\r'); + buffer_add(b, '\n'); + } + } + } + } + // buffer_add(b, '\r'); + buffer_add(b, '\n'); +} + +void base64_decode(struct buffer_st *bfr, const char *source, int length) +{ + int i; + int offset = 0; + int endoffile; + int count; + + buffer_new(bfr); + + for (i = 0; i < 255; i++) { + dtable[i] = 0x80; + } + for (i = 'A'; i <= 'Z'; i++) { + dtable[i] = 0 + (i - 'A'); + } + for (i = 'a'; i <= 'z'; i++) { + dtable[i] = 26 + (i - 'a'); + } + for (i = '0'; i <= '9'; i++) { + dtable[i] = 52 + (i - '0'); + } + dtable['+'] = 62; + dtable['/'] = 63; + dtable['='] = 0; + + endoffile = 0; + + /*CONSTANTCONDITION*/ + while (1) { + unsigned char a[4], b[4], o[3]; + + for (i = 0; i < 4; i++) { + int c; + while (1) { + c = *(source++); + offset++; + if (offset > length) endoffile = 1; + if (isspace(c) || c == '\n' || c == '\r') continue; + break; + } + + if (endoffile) { + /* + if (i > 0) { + fprintf(stderr, "Input file incomplete.\n"); + exit(1); + } + */ + return; + } + + if (dtable[c] & 0x80) { + /* + fprintf(stderr, "Offset %i length %i\n", offset, length); + fprintf(stderr, "character '%c:%x:%c' in input file.\n", c, c, dtable[c]); + exit(1); + */ + i--; + continue; + } + a[i] = (unsigned char) c; + b[i] = (unsigned char) dtable[c]; + } + o[0] = (b[0] << 2) | (b[1] >> 4); + o[1] = (b[1] << 4) | (b[2] >> 2); + o[2] = (b[2] << 6) | b[3]; + i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3); + count = 0; + while (count < i) { + buffer_add(bfr, o[count++]); + } + if (i < 3) { + return; + } + } +} diff --git a/ext/xmlrpc/libxmlrpc/base64.h b/ext/xmlrpc/libxmlrpc/base64.h new file mode 100644 index 0000000000..4cf156ad1e --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/base64.h @@ -0,0 +1,38 @@ +/* + + Encode or decode file as MIME base64 (RFC 1341) + + by John Walker + http://www.fourmilab.ch/ + + This program is in the public domain. + +*/ + + +struct buffer_st { + char *data; + int length; + char *ptr; + int offset; +}; + +void buffer_new(struct buffer_st *b); +void buffer_add(struct buffer_st *b, char c); +void buffer_delete(struct buffer_st *b); + +void base64_encode(struct buffer_st *b, const char *source, int length); +void base64_decode(struct buffer_st *b, const char *source, int length); + +/* +#define DEBUG_MALLOC + */ + +#ifdef DEBUG_MALLOC +void *_malloc_real(size_t s, char *file, int line); +void _free_real(void *p, char *file, int line); + +#define malloc(s) _malloc_real(s,__FILE__,__LINE__) +#define free(p) _free_real(p, __FILE__,__LINE__) +#endif + diff --git a/ext/xmlrpc/libxmlrpc/encodings.c b/ext/xmlrpc/libxmlrpc/encodings.c new file mode 100644 index 0000000000..544cfa2559 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/encodings.c @@ -0,0 +1,103 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +static const char rcsid[] = "#(@) $Id$"; + +#include <errno.h> +#include <iconv.h> +#include "encodings.h" + +static char* convert(const char* src, int src_len, int *new_len, const char* from_enc, const char* to_enc) { + char* outbuf = 0; + + if(src && src_len && from_enc && to_enc) { + int outlenleft = src_len; + int outlen = src_len; + int inlenleft = src_len; + iconv_t ic = iconv_open(to_enc, from_enc); + char* src_ptr = (char*)src; + char* out_ptr = 0; + + if(ic != (iconv_t)-1) { + size_t st; + outbuf = (char*)malloc(outlen + 1); + + if(outbuf) { + out_ptr = (char*)outbuf; + while(inlenleft) { + st = iconv(ic, &src_ptr, &inlenleft, &out_ptr, &outlenleft); + if(st == -1) { + if(errno == E2BIG) { + int diff = out_ptr - outbuf; + outlen += inlenleft; + outlenleft += inlenleft; + outbuf = (char*)realloc(outbuf, outlen + 1); + if(!outbuf) { + break; + } + out_ptr = outbuf + diff; + } + else { + free(outbuf); + outbuf = 0; + break; + } + } + } + } + iconv_close(ic); + } + outlen -= outlenleft; + + if(new_len) { + *new_len = outbuf ? outlen : 0; + } + if(outbuf) { + outbuf[outlen] = 0; + } + } + return outbuf; +} + +/* returns a new string that must be freed */ +char* utf8_encode(const char *s, int len, int *newlen, const char* encoding) +{ + return convert(s, len, newlen, encoding, "UTF-8"); +} + +/* returns a new string, possibly decoded */ +char* utf8_decode(const char *s, int len, int *newlen, const char* encoding) +{ + return convert(s, len, newlen, "UTF-8", encoding); +} + diff --git a/ext/xmlrpc/libxmlrpc/encodings.h b/ext/xmlrpc/libxmlrpc/encodings.h new file mode 100644 index 0000000000..486360b1be --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/encodings.h @@ -0,0 +1,46 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +#ifndef __ENCODINGS__H +#define __ENCODINGS__H + +/* these defines are for legacy purposes. */ +#define encoding_utf_8 "UTF-8" +typedef const char* ENCODING_ID; +#define utf8_get_encoding_id_string(desired_enc) ((const char*)desired_enc) +#define utf8_get_encoding_id_from_string(id_string) ((ENCODING_ID)id_string) + +char* utf8_encode(const char *s, int len, int *newlen, ENCODING_ID encoding); +char* utf8_decode(const char *s, int len, int *newlen, ENCODING_ID encoding); + +#endif /* __ENCODINGS__H */ diff --git a/ext/xmlrpc/libxmlrpc/libs.mk b/ext/xmlrpc/libxmlrpc/libs.mk new file mode 100644 index 0000000000..7c6e176717 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/libs.mk @@ -0,0 +1,7 @@ +include $(top_builddir)/config_vars.mk +LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X) +LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo) +$(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) + $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD) + +targets = $(LTLIBRARY_NAME) diff --git a/ext/xmlrpc/libxmlrpc/queue.c b/ext/xmlrpc/libxmlrpc/queue.c new file mode 100644 index 0000000000..25d62b96a7 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/queue.c @@ -0,0 +1,979 @@ +static const char rcsid[] = "#(@) $Id$"; + +/* + * Date last modified: Jan 2001 + * Modifications by Dan Libby (dan@libby.com), including: + * - various fixes, null checks, etc + * - addition of Q_Iter funcs, macros + */ + + +/*-************************************************************** + * + * File : q.c + * + * Author: Peter Yard [1993.01.02] -- 02 Jan 1993 + * + * Disclaimer: This code is released to the public domain. + * + * Description: + * Generic double ended queue (Deque pronounced DEK) for handling + * any data types, with sorting. + * + * By use of various functions in this module the caller + * can create stacks, queues, lists, doubly linked lists, + * sorted lists, indexed lists. All lists are dynamic. + * + * It is the responsibility of the caller to malloc and free + * memory for insertion into the queue. A pointer to the object + * is used so that not only can any data be used but various kinds + * of data can be pushed on the same queue if one so wished e.g. + * various length string literals mixed with pointers to structures + * or integers etc. + * + * Enhancements: + * A future improvement would be the option of multiple "cursors" + * so that multiple locations could occur in the one queue to allow + * placemarkers and additional flexibility. Perhaps even use queue + * itself to have a list of cursors. + * + * Usage: + * + * /x init queue x/ + * queue q; + * Q_Init(&q); + * + * To create a stack : + * + * Q_PushHead(&q, &mydata1); /x push x/ + * Q_PushHead(&q, &mydata2); + * ..... + * data_ptr = Q_PopHead(&q); /x pop x/ + * ..... + * data_ptr = Q_Head(&q); /x top of stack x/ + * + * To create a FIFO: + * + * Q_PushHead(&q, &mydata1); + * ..... + * data_ptr = Q_PopTail(&q); + * + * To create a double list: + * + * data_ptr = Q_Head(&q); + * .... + * data_ptr = Q_Next(&q); + * data_ptr = Q_Tail(&q); + * if (Q_IsEmpty(&q)) .... + * ..... + * data_ptr = Q_Previous(&q); + * + * To create a sorted list: + * + * Q_PushHead(&q, &mydata1); /x push x/ + * Q_PushHead(&q, &mydata2); + * ..... + * if (!Q_Sort(&q, MyFunction)) + * .. error .. + * + * /x fill in key field of mydata1. + * * NB: Q_Find does linear search + * x/ + * + * if (Q_Find(&q, &mydata1, MyFunction)) + * { + * /x found it, queue cursor now at correct record x/ + * /x can retrieve with x/ + * data_ptr = Q_Get(&q); + * + * /x alter data , write back with x/ + * Q_Put(&q, data_ptr); + * } + * + * /x Search with binary search x/ + * if (Q_Seek(&q, &mydata, MyFunction)) + * /x etc x/ + * + * + ****************************************************************/ + + +#include <stdlib.h> +#include "queue.h" + + +static void QuickSort(void *list[], int low, int high, + int (*Comp)(const void *, const void *)); +static int Q_BSearch(queue *q, void *key, + int (*Comp)(const void *, const void *)); + +/* The index: a pointer to pointers */ + +static void **index; +static datanode **posn_index; + + +/*** + * + ** function : Q_Init + * + ** purpose : Initialise queue object and pointers. + * + ** parameters : 'queue' pointer. + * + ** returns : True_ if init successful else False_ + * + ** comments : + ***/ + +int Q_Init(queue *q) +{ + if(q) { + q->head = q->tail = NULL; + q->cursor = q->head; + q->size = 0; + q->sorted = False_; + } + + return True_; +} + +/*** + * + ** function : Q_AtHead + * + ** purpose : tests if cursor is at head of queue + * + ** parameters : 'queue' pointer. + * + ** returns : boolean - True_ is at head else False_ + * + ** comments : + * + ***/ + +int Q_AtHead(queue *q) +{ + return(q && q->cursor == q->head); +} + + +/*** + * + ** function : Q_AtTail + * + ** purpose : boolean test if cursor at tail of queue + * + ** parameters : 'queue' pointer to test. + * + ** returns : True_ or False_ + * + ** comments : + * + ***/ + +int Q_AtTail(queue *q) +{ + return(q && q->cursor == q->tail); +} + + +/*** + * + ** function : Q_IsEmpty + * + ** purpose : test if queue has nothing in it. + * + ** parameters : 'queue' pointer + * + ** returns : True_ if IsEmpty queue, else False_ + * + ** comments : + * + ***/ + +inline int Q_IsEmpty(queue *q) +{ + return(!q || q->size == 0); +} + +/*** + * + ** function : Q_Size + * + ** purpose : return the number of elements in the queue + * + ** parameters : queue pointer + * + ** returns : number of elements + * + ** comments : + * + ***/ + +int Q_Size(queue *q) +{ + return q ? q->size : 0; +} + + +/*** + * + ** function : Q_Head + * + ** purpose : position queue cursor to first element (head) of queue. + * + ** parameters : 'queue' pointer + * + ** returns : pointer to data at head. If queue is IsEmpty returns NULL + * + ** comments : + * + ***/ + +void *Q_Head(queue *q) +{ + if(Q_IsEmpty(q)) + return NULL; + + q->cursor = q->head; + + return q->cursor->data; +} + + +/*** + * + ** function : Q_Tail + * + ** purpose : locate cursor at tail of queue. + * + ** parameters : 'queue' pointer + * + ** returns : pointer to data at tail , if queue IsEmpty returns NULL + * + ** comments : + * + ***/ + +void *Q_Tail(queue *q) +{ + if(Q_IsEmpty(q)) + return NULL; + + q->cursor = q->tail; + + return q->cursor->data; +} + + +/*** + * + ** function : Q_PushHead + * + ** purpose : put a data pointer at the head of the queue + * + ** parameters : 'queue' pointer, void pointer to the data. + * + ** returns : True_ if success else False_ if unable to push data. + * + ** comments : + * + ***/ + +int Q_PushHead(queue *q, void *d) +{ + if(q && d) { + node *n; + datanode *p; + + p = malloc(sizeof(datanode)); + if(p == NULL) + return False_; + + n = q->head; + + q->head = (node*)p; + q->head->prev = NULL; + + if(q->size == 0) { + q->head->next = NULL; + q->tail = q->head; + } + else { + q->head->next = (datanode*)n; + n->prev = q->head; + } + + q->head->data = d; + q->size++; + + q->cursor = q->head; + + q->sorted = False_; + + return True_; + } + return False_; +} + + + +/*** + * + ** function : Q_PushTail + * + ** purpose : put a data element pointer at the tail of the queue + * + ** parameters : queue pointer, pointer to the data + * + ** returns : True_ if data pushed, False_ if data not inserted. + * + ** comments : + * + ***/ + +int Q_PushTail(queue *q, void *d) +{ + if(q && d) { + node *p; + datanode *n; + + n = malloc(sizeof(datanode)); + if(n == NULL) + return False_; + + p = q->tail; + q->tail = (node *)n; + + if(q->size == 0) { + q->tail->prev = NULL; + q->head = q->tail; + } + else { + q->tail->prev = (datanode *)p; + p->next = q->tail; + } + + q->tail->next = NULL; + + q->tail->data = d; + q->cursor = q->tail; + q->size++; + + q->sorted = False_; + + return True_; + } +} + + + +/*** + * + ** function : Q_PopHead + * + ** purpose : remove and return the top element at the head of the + * queue. + * + ** parameters : queue pointer + * + ** returns : pointer to data element or NULL if queue is IsEmpty. + * + ** comments : + * + ***/ + +void *Q_PopHead(queue *q) +{ + datanode *n; + void *d; + + if(Q_IsEmpty(q)) + return NULL; + + d = q->head->data; + n = q->head->next; + free(q->head); + + q->size--; + + if(q->size == 0) + q->head = q->tail = q->cursor = NULL; + else { + q->head = (node *)n; + q->head->prev = NULL; + q->cursor = q->head; + } + + q->sorted = False_; + + return d; +} + + +/*** + * + ** function : Q_PopTail + * + ** purpose : remove element from tail of queue and return data. + * + ** parameters : queue pointer + * + ** returns : pointer to data element that was at tail. NULL if queue + * IsEmpty. + * + ** comments : + * + ***/ + +void *Q_PopTail(queue *q) +{ + datanode *p; + void *d; + + if(Q_IsEmpty(q)) + return NULL; + + d = q->tail->data; + p = q->tail->prev; + free(q->tail); + q->size--; + + if(q->size == 0) + q->head = q->tail = q->cursor = NULL; + else { + q->tail = (node *)p; + q->tail->next = NULL; + q->cursor = q->tail; + } + + q->sorted = False_; + + return d; +} + + + +/*** + * + ** function : Q_Next + * + ** purpose : Move to the next element in the queue without popping + * + ** parameters : queue pointer. + * + ** returns : pointer to data element of new element or NULL if end + * of the queue. + * + ** comments : This uses the cursor for the current position. Q_Next + * only moves in the direction from the head of the queue + * to the tail. + ***/ + +void *Q_Next(queue *q) +{ + if(!q) + return NULL; + + if(q->cursor->next == NULL) + return NULL; + + q->cursor = (node *)q->cursor->next; + + return q->cursor->data ; +} + + + +/*** + * + ** function : Q_Previous + * + ** purpose : Opposite of Q_Next. Move to next element closer to the + * head of the queue. + * + ** parameters : pointer to queue + * + ** returns : pointer to data of new element else NULL if queue IsEmpty + * + ** comments : Makes cursor move towards the head of the queue. + * + ***/ + +void *Q_Previous(queue *q) +{ + if(!q) + return NULL; + + if(q->cursor->prev == NULL) + return NULL; + + q->cursor = (node *)q->cursor->prev; + + return q->cursor->data; +} + + +void *Q_Iter_Del(queue *q, q_iter iter) +{ + void *d; + datanode *n, *p; + + if(!q) + return NULL; + + if(iter == NULL) + return NULL; + + if(iter == (q_iter)q->head) + return Q_PopHead(q); + + if(iter == (q_iter)q->tail) + return Q_PopTail(q); + + n = ((node*)iter)->next; + p = ((node*)iter)->prev; + d = ((node*)iter)->data; + + free(iter); + + if(p) { + p->next = n; + } + if (q->cursor == (node*)iter) { + if (p) { + q->cursor = p; + } else { + q->cursor = n; + } + } + + + if (n != NULL) { + n->prev = p; + } + + q->size--; + + q->sorted = False_; + + return d; +} + + + +/*** + * + ** function : Q_DelCur + * + ** purpose : Delete the current queue element as pointed to by + * the cursor. + * + ** parameters : queue pointer + * + ** returns : pointer to data element. + * + ** comments : WARNING! It is the responsibility of the caller to + * free any memory. Queue cannot distinguish between + * pointers to literals and malloced memory. + * + ***/ + +void *Q_DelCur(queue* q) { + if(q) { + return Q_Iter_Del(q, (q_iter)q->cursor); + } + return 0; +} + + +/*** + * + ** function : Q_Destroy + * + ** purpose : Free all queue resources + * + ** parameters : queue pointer + * + ** returns : null. + * + ** comments : WARNING! It is the responsibility of the caller to + * free any memory. Queue cannot distinguish between + * pointers to literals and malloced memory. + * + ***/ + +void Q_Destroy(queue *q) +{ + while(!Q_IsEmpty(q)) { + Q_PopHead(q); + } +} + + +/*** + * + ** function : Q_Get + * + ** purpose : get the pointer to the data at the cursor location + * + ** parameters : queue pointer + * + ** returns : data element pointer + * + ** comments : + * + ***/ + +void *Q_Get(queue *q) +{ + if(!q) + return NULL; + + if(q->cursor == NULL) + return NULL; + return q->cursor->data; +} + + + +/*** + * + ** function : Q_Put + * + ** purpose : replace pointer to data with new pointer to data. + * + ** parameters : queue pointer, data pointer + * + ** returns : boolean- True_ if successful, False_ if cursor at NULL + * + ** comments : + * + ***/ + +int Q_Put(queue *q, void *data) +{ + if(q && data) { + if(q->cursor == NULL) + return False_; + + q->cursor->data = data; + return True_; + } + return False_; +} + + +/*** + * + ** function : Q_Find + * + ** purpose : Linear search of queue for match with key in *data + * + ** parameters : queue pointer q, data pointer with data containing key + * comparison function here called Comp. + * + ** returns : True_ if found , False_ if not in queue. + * + ** comments : Useful for small queues that are constantly changing + * and would otherwise need constant sorting with the + * Q_Seek function. + * For description of Comp see Q_Sort. + * Queue cursor left on position found item else at end. + * + ***/ + +int Q_Find(queue *q, void *data, + int (*Comp)(const void *, const void *)) +{ + void *d; + + if (q == NULL) { + return False_; + } + + d = Q_Head(q); + do { + if(Comp(d, data) == 0) + return True_; + d = Q_Next(q); + } while(!Q_AtTail(q)); + + if(Comp(d, data) == 0) + return True_; + + return False_; +} + +/*======== Sorted Queue and Index functions ========= */ + + +static void QuickSort(void *list[], int low, int high, + int (*Comp)(const void *, const void *)) +{ + int flag = 1, i, j; + void *key, *temp; + + if(low < high) { + i = low; + j = high + 1; + + key = list[ low ]; + + while(flag) { + i++; + while(Comp(list[i], key) < 0) + i++; + + j--; + while(Comp(list[j], key) > 0) + j--; + + if(i < j) { + temp = list[i]; + list[i] = list[j]; + list[j] = temp; + } + else flag = 0; + } + + temp = list[low]; + list[low] = list[j]; + list[j] = temp; + + QuickSort(list, low, j-1, Comp); + QuickSort(list, j+1, high, Comp); + } +} + + +/*** + * + ** function : Q_Sort + * + ** purpose : sort the queue and allow index style access. + * + ** parameters : queue pointer, comparison function compatible with + * with 'qsort'. + * + ** returns : True_ if sort succeeded. False_ if error occurred. + * + ** comments : Comp function supplied by caller must return + * -1 if data1 < data2 + * 0 if data1 == data2 + * +1 if data1 > data2 + * + * for Comp(data1, data2) + * + * If queue is already sorted it frees the memory of the + * old index and starts again. + * + ***/ + +int Q_Sort(queue *q, int (*Comp)(const void *, const void *)) +{ + int i; + void *d; + datanode *dn; + + /* if already sorted free memory for tag array */ + + if(q->sorted) { + free(index); + free(posn_index); + q->sorted = False_; + } + + /* Now allocate memory of array, array of pointers */ + + index = malloc(q->size * sizeof(q->cursor->data)); + if(index == NULL) + return False_; + + posn_index = malloc(q->size * sizeof(q->cursor)); + if(posn_index == NULL) { + free(index); + return False_; + } + + /* Walk queue putting pointers into array */ + + d = Q_Head(q); + for(i=0; i < q->size; i++) { + index[i] = d; + posn_index[i] = q->cursor; + d = Q_Next(q); + } + + /* Now sort the index */ + + QuickSort(index, 0, q->size - 1, Comp); + + /* Rearrange the actual queue into correct order */ + + dn = q->head; + i = 0; + while(dn != NULL) { + dn->data = index[i++]; + dn = dn->next; + } + + /* Re-position to original element */ + + if(d != NULL) + Q_Find(q, d, Comp); + else Q_Head(q); + + q->sorted = True_; + + return True_; +} + + +/*** + * + ** function : Q_BSearch + * + ** purpose : binary search of queue index for node containing key + * + ** parameters : queue pointer 'q', data pointer of key 'key', + * Comp comparison function. + * + ** returns : integer index into array of node pointers, + * or -1 if not found. + * + ** comments : see Q_Sort for description of 'Comp' function. + * + ***/ + +static int Q_BSearch( queue *q, void *key, + int (*Comp)(const void *, const void*)) +{ + int low, mid, hi, val; + + low = 0; + hi = q->size - 1; + + while(low <= hi) { + mid = (low + hi) / 2; + val = Comp(key, index[ mid ]); + + if(val < 0) + hi = mid - 1; + + else if(val > 0) + low = mid + 1; + + else /* Success */ + return mid; + } + + /* Not Found */ + + return -1; +} + + +/*** + * + ** function : Q_Seek + * + ** purpose : use index to locate data according to key in 'data' + * + ** parameters : queue pointer 'q', data pointer 'data', Comp comparison + * function. + * + ** returns : pointer to data or NULL if could not find it or could + * not sort queue. + * + ** comments : see Q_Sort for description of 'Comp' function. + * + ***/ + +void *Q_Seek(queue *q, void *data, int (*Comp)(const void *, const void *)) +{ + int idx; + + if (q == NULL) { + return NULL; + } + + if(!q->sorted) { + if(!Q_Sort(q, Comp)) + return NULL; + } + + idx = Q_BSearch(q, data, Comp); + + if(idx < 0) + return NULL; + + q->cursor = posn_index[idx]; + + return index[idx]; +} + + + +/*** + * + ** function : Q_Insert + * + ** purpose : Insert an element into an indexed queue + * + ** parameters : queue pointer 'q', data pointer 'data', Comp comparison + * function. + * + ** returns : pointer to data or NULL if could not find it or could + * not sort queue. + * + ** comments : see Q_Sort for description of 'Comp' function. + * WARNING! This code can be very slow since each new + * element means a new Q_Sort. Should only be used for + * the insertion of the odd element ,not the piecemeal + * building of an entire queue. + ***/ + +int Q_Insert(queue *q, void *data, int (*Comp)(const void *, const void *)) +{ + if (q == NULL) { + return False_; + } + + Q_PushHead(q, data); + + if(!Q_Sort(q, Comp)) + return False_; + + return True_; +} + +/* read only funcs for iterating through queue. above funcs modify queue */ +q_iter Q_Iter_Head(queue *q) { + return q ? (q_iter)q->head : NULL; +} + +q_iter Q_Iter_Tail(queue *q) { + return q ? (q_iter)q->tail : NULL; +} + +q_iter Q_Iter_Next(q_iter qi) { + return qi ? (q_iter)((node*)qi)->next : NULL; +} + +q_iter Q_Iter_Prev(q_iter qi) { + return qi ? (q_iter)((node*)qi)->prev : NULL; +} + +void * Q_Iter_Get(q_iter qi) { + return qi ? ((node*)qi)->data : NULL; +} + +int Q_Iter_Put(q_iter qi, void* data) { + if(qi) { + ((node*)qi)->data = data; + return True_; + } + return False_; +} diff --git a/ext/xmlrpc/libxmlrpc/queue.h b/ext/xmlrpc/libxmlrpc/queue.h new file mode 100644 index 0000000000..e850b57e6c --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/queue.h @@ -0,0 +1,89 @@ +/* + * Date last modified: Jan 2001 + * Modifications by Dan Libby (dan@libby.com), including: + * - various fixes, null checks, etc + * - addition of Q_Iter funcs, macros + */ + +/* + * File : q.h + * + * Peter Yard 02 Jan 1993. + * + * Disclaimer: This code is released to the public domain. + */ + +#ifndef Q__H +#define Q__H + +#ifndef False_ + #define False_ 0 +#endif + +#ifndef True_ + #define True_ 1 +#endif + +typedef struct nodeptr datanode; + +typedef struct nodeptr { + void *data ; + datanode *prev, *next ; +} node ; + +/* For external use with Q_Iter* funcs */ +typedef struct nodeptr* q_iter; + +typedef struct { + node *head, *tail, *cursor; + int size, sorted, item_deleted; +} queue; + +typedef struct { + void *dataptr; + node *loc ; +} index_elt ; + + +int Q_Init(queue *q); +void Q_Destroy(queue *q); +int Q_IsEmpty(queue *q); +int Q_Size(queue *q); +int Q_AtHead(queue *q); +int Q_AtTail(queue *q); +int Q_PushHead(queue *q, void *d); +int Q_PushTail(queue *q, void *d); +void *Q_Head(queue *q); +void *Q_Tail(queue *q); +void *Q_PopHead(queue *q); +void *Q_PopTail(queue *q); +void *Q_Next(queue *q); +void *Q_Previous(queue *q); +void *Q_DelCur(queue *q); +void *Q_Get(queue *q); +int Q_Put(queue *q, void *data); +int Q_Sort(queue *q, int (*Comp)(const void *, const void *)); +int Q_Find(queue *q, void *data, + int (*Comp)(const void *, const void *)); +void *Q_Seek(queue *q, void *data, + int (*Comp)(const void *, const void *)); +int Q_Insert(queue *q, void *data, + int (*Comp)(const void *, const void *)); + +/* read only funcs for iterating through queue. above funcs modify queue */ +q_iter Q_Iter_Head(queue *q); +q_iter Q_Iter_Tail(queue *q); +q_iter Q_Iter_Next(q_iter qi); +q_iter Q_Iter_Prev(q_iter qi); +void* Q_Iter_Get(q_iter qi); +int Q_Iter_Put(q_iter qi, void* data); // not read only! here for completeness. +void* Q_Iter_Del(queue *q, q_iter iter); // not read only! here for completeness. + +/* Fast (macro'd) versions of above */ +#define Q_Iter_Head_F(q) (q ? (q_iter)((queue*)q)->head : NULL) +#define Q_Iter_Tail_F(q) (q ? (q_iter)((queue*)q)->tail : NULL) +#define Q_Iter_Next_F(qi) (qi ? (q_iter)((node*)qi)->next : NULL) +#define Q_Iter_Prev_F(qi) (qi ? (q_iter)((node*)qi)->prev : NULL) +#define Q_Iter_Get_F(qi) (qi ? ((node*)qi)->data : NULL) + +#endif /* Q__H */ diff --git a/ext/xmlrpc/libxmlrpc/simplestring.c b/ext/xmlrpc/libxmlrpc/simplestring.c new file mode 100644 index 0000000000..7ce68d3410 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/simplestring.c @@ -0,0 +1,234 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +static const char rcsid[] = "#(@) $Id$"; + + +#define SIMPLESTRING_INCR 32 + +/****h* ABOUT/simplestring + * NAME + * simplestring + * AUTHOR + * Dan Libby, aka danda (dan@libby.com) + * CREATION DATE + * 06/2000 + * HISTORY + * 10/15/2000 -- danda -- adding robodoc documentation + * PORTABILITY + * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just + * about anything with minor mods. + * NOTES + * This code was written primarily for xmlrpc, but has found some other uses. + * + * simplestring is, as the name implies, a simple API for dealing with C strings. + * Why would I write yet another string API? Because I couldn't find any that were + * a) free / GPL, b) simple/lightweight, c) fast, not doing unneccesary strlens all + * over the place. So. It is simple, and it seems to work, and it is pretty fast. + * + * Oh, and it is also binary safe, ie it can handle strings with embedded NULLs, + * so long as the real length is passed in. + * + * And the masses rejoiced. + * + * BUGS + * there must be some. + ******/ + + +#include "simplestring.h" + +#define my_free(thing) if(thing) {free(thing); thing = 0;} + +/*----------------------** +* Begin String Functions * +*-----------------------*/ + +/****f* FUNC/simplestring_init + * NAME + * simplestring_init + * SYNOPSIS + * void simplestring_init(simplestring* string) + * FUNCTION + * initialize string + * INPUTS + * string - pointer to a simplestring struct that will be initialized + * RESULT + * void + * NOTES + * SEE ALSO + * simplestring_free () + * simplestring_clear () + * SOURCE + */ +void simplestring_init(simplestring* string) { + memset(string, 0, sizeof(simplestring)); +} +/******/ + +static void simplestring_init_str(simplestring* string) { + string->str = (char*)malloc(SIMPLESTRING_INCR); + if(string->str) { + string->str[0] = 0; + string->len = 0; + string->size = SIMPLESTRING_INCR; + } + else { + string->size = 0; + } +} + +/****f* FUNC/simplestring_clear + * NAME + * simplestring_clear + * SYNOPSIS + * void simplestring_clear(simplestring* string) + * FUNCTION + * clears contents of a string + * INPUTS + * string - the string value to clear + * RESULT + * void + * NOTES + * This function is very fast as it does not de-allocate any memory. + * SEE ALSO + * + * SOURCE + */ +void simplestring_clear(simplestring* string) { + if(string->str) { + string->str[0] = 0; + } + string->len = 0; +} +/******/ + +/****f* FUNC/simplestring_free + * NAME + * simplestring_free + * SYNOPSIS + * void simplestring_free(simplestring* string) + * FUNCTION + * frees contents of a string, if any. Does *not* free the simplestring struct itself. + * INPUTS + * string - value containing string to be free'd + * RESULT + * void + * NOTES + * caller is responsible for allocating and freeing simplestring* struct itself. + * SEE ALSO + * simplestring_init () + * SOURCE + */ +void simplestring_free(simplestring* string) { + if(string && string->str) { + my_free(string->str); + string->len = 0; + } +} +/******/ + +/****f* FUNC/simplestring_addn + * NAME + * simplestring_addn + * SYNOPSIS + * void simplestring_addn(simplestring* string, const char* add, int add_len) + * FUNCTION + * copies n characters from source to target string + * INPUTS + * target - target string + * source - source string + * add_len - number of characters to copy + * RESULT + * void + * NOTES + * SEE ALSO + * simplestring_add () + * SOURCE + */ +void simplestring_addn(simplestring* target, const char* source, int add_len) { + if(target && source) { + if(!target->str) { + simplestring_init_str(target); + } + if(target->len + add_len + 1 > target->size) { + /* newsize is current length + new length */ + int newsize = target->len + add_len + 1; + int incr = target->size * 2; + + /* align to SIMPLESTRING_INCR increments */ + newsize = newsize - (newsize % incr) + incr; + target->str = (char*)realloc(target->str, newsize); + + target->size = target->str ? newsize : 0; + } + + if(target->str) { + if(add_len) { + memcpy(target->str + target->len, source, add_len); + } + target->len += add_len; + target->str[target->len] = 0; /* null terminate */ + } + } +} +/******/ + +/****f* FUNC/simplestring_add + * NAME + * simplestring_add + * SYNOPSIS + * void simplestring_add(simplestring* string, const char* add) + * FUNCTION + * appends a string of unknown length from source to target + * INPUTS + * target - the target string to append to + * source - the source string of unknown length + * RESULT + * void + * NOTES + * SEE ALSO + * simplestring_addn () + * SOURCE + */ +void simplestring_add(simplestring* target, const char* source) { + if(target && source) { + simplestring_addn(target, source, strlen(source)); + } +} +/******/ + + +/*---------------------- +* End String Functions * +*--------------------**/ diff --git a/ext/xmlrpc/libxmlrpc/simplestring.h b/ext/xmlrpc/libxmlrpc/simplestring.h new file mode 100644 index 0000000000..a891ba6d78 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/simplestring.h @@ -0,0 +1,75 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +#ifndef __SIMPLESTRING_H__ + #define __SIMPLESTRING_H__ + +/*-******************************** +* begin simplestring header stuff * +**********************************/ + +#ifdef __cplusplus +extern "C" { +#endif + + /****s* struct/simplestring + * NAME + * simplestring + * NOTES + * represents a string efficiently for fast appending, etc. + * SOURCE + */ +typedef struct _simplestring { + char* str; /* string buf */ + int len; /* length of string/buf */ + int size; /* size of allocated buffer */ +} simplestring; +/******/ + +#ifndef NULL + #define NULL 0 +#endif + +void simplestring_init(simplestring* string); +void simplestring_clear(simplestring* string); +void simplestring_free(simplestring* string); +void simplestring_addn(simplestring* string, const char* add, int add_len); + +#ifdef __cplusplus +} +#endif + +/*-****************************** +* end simplestring header stuff * +********************************/ + +#endif /* __SIMPLESTRING_H__ */ diff --git a/ext/xmlrpc/libxmlrpc/system_methods.c b/ext/xmlrpc/libxmlrpc/system_methods.c new file mode 100644 index 0000000000..742b837143 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/system_methods.c @@ -0,0 +1,371 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +/****h* ABOUT/system_methods + * AUTHOR + * Dan Libby, aka danda (dan@libby.com) + * HISTORY + * 4/28/2001 -- danda -- adding system.multicall and separating out system methods. + * TODO + * NOTES + *******/ + + +#include "queue.h" +#include "xmlrpc.h" +#include "xmlrpc_private.h" +#include "xmlrpc_introspection_private.h" +#include "system_methods_private.h" +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> + + +static const char* xsm_introspection_xml = +"<?xml version='1.0' ?>" + +"<introspection version='1.0'>" + "<typeList>" + + "<typeDescription name='system.value' basetype='struct' desc='description of a value'>" + "<value type='string' name='name' optional='yes'>value identifier</value>" + "<value type='string' name='type'>value's xmlrpc or user-defined type</value>" + "<value type='string' name='description'>value's textual description</value> " + "<value type='boolean' name='optional'>true if value is optional, else it is required</value> " + "<value type='any' name='member' optional='yes'>a child of this element. n/a for scalar types</value> " + "</typeDescription>" + + "<typeDescription name='system.valueList' basetype='array' desc='list of value descriptions'>" + "<value type='system.value'/>" + "</typeDescription>" + + "<typeDescription name='system.stringList' basetype='array' desc='list of strings'>" + "<value type='string'/>" + "</typeDescription>" + + + "</typeList>" + + "<methodList>" + + "<!-- system.describeMethods -->" + "<methodDescription name='system.describeMethods'>" + "<author>Dan Libby</author>" + "<purpose>fully describes the methods and types implemented by this XML-RPC server.</purpose>" + "<version>1.1</version>" + "<signatures>" + "<signature>" + "<params>" + "<value type='array' name='methodList' optional='yes' desc='a list of methods to be described. if omitted, all are described.'>" + "<value type='string'>a valid method name</value>" + "</value>" + "</params>" + "<returns>" + "<value type='struct' desc='contains methods list and types list'>" + "<value type='array' name='methodList' desc='a list of methods'>" + "<value type='struct' desc='representation of a single method'>" + "<value type='string' name='name'>method name</value>" + "<value type='string' name='version' optional='yes'>method version</value>" + "<value type='string' name='author' optional='yes'>method author</value>" + "<value type='string' name='purpose' optional='yes'>method purpose</value>" + "<value type='array' name='signatures' desc='list of method signatures'>" + "<value type='struct' desc='representation of a single signature'>" + "<value type='system.valueList' name='params' optional='yes'>parameter list</value>" + "<value type='system.valueList' name='returns' optional='yes'>return value list</value>" + "</value>" + "</value>" + "<value type='system.stringList' name='bugs' optional='yes'>list of known bugs</value>" + "<value type='system.stringList' name='errors' optional='yes'>list of possible errors and error codes</value>" + "<value type='system.stringList' name='examples' optional='yes'>list of examples</value>" + "<value type='system.stringList' name='history' optional='yes'>list of modifications</value>" + "<value type='system.stringList' name='notes' optional='yes'>list of notes</value>" + "<value type='system.stringList' name='see' optional='yes'>see also. list of related methods</value>" + "<value type='system.stringList' name='todo' optional='yes'>list of unimplemented features</value>" + "</value>" + "</value>" + "<value type='array' name='typeList' desc='a list of type descriptions. Typically used for referencing complex types'>" + "<value type='system.value'>a type description</value>" + "</value>" + "</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.listMethods' />" + "<item name='system.methodSignature' />" + "<item name='system.methodHelp' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "<!-- system.listMethods -->" + "<methodDescription name='system.listMethods'>" + "<author>Dan Libby</author>" + "<purpose>enumerates the methods implemented by this XML-RPC server.</purpose>" + "<version>1.0</version>" + "<signatures>" + "<signature>" + "<returns>" + "<value type='array' desc='an array of strings'>" + "<value type='string'>name of a method implemented by the server.</value>" + "</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.describeMethods' />" + "<item name='system.methodSignature' />" + "<item name='system.methodHelp' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "<!-- system.methodHelp -->" + "<methodDescription name='system.methodHelp'>" + "<author>Dan Libby</author>" + "<purpose>provides documentation string for a single method</purpose>" + "<version>1.0</version>" + "<signatures>" + "<signature>" + "<params>" + "<value type='string' name='methodName'>name of the method for which documentation is desired</value>" + "</params>" + "<returns>" + "<value type='string'>help text if defined for the method passed, otherwise an empty string</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.listMethods' />" + "<item name='system.methodSignature' />" + "<item name='system.methodHelp' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "<!-- system.methodSignature -->" + "<methodDescription name='system.methodSignature'>" + "<author>Dan Libby</author>" + "<purpose>provides 1 or more signatures for a single method</purpose>" + "<version>1.0</version>" + "<signatures>" + "<signature>" + "<params>" + "<value type='string' name='methodName'>name of the method for which documentation is desired</value>" + "</params>" + "<returns>" + "<value type='array' desc='a list of arrays, each representing a signature'>" + "<value type='array' desc='a list of strings. the first element represents the method return value. subsequent elements represent parameters.'>" + "<value type='string'>a string indicating the xmlrpc type of a value. one of: string, int, double, base64, datetime, array, struct</value>" + "</value>" + "</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.listMethods' />" + "<item name='system.methodHelp' />" + "<item name='system.describeMethods' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "<!-- system.multiCall -->" + "<methodDescription name='system.multiCall'>" + "<author>Dan Libby</author>" + "<purpose>executes multiple methods in sequence and returns the results</purpose>" + "<version>1.0</version>" + "<signatures>" + "<signature>" + "<params>" + "<value type='array' name='methodList' desc='an array of method call structs'>" + "<value type='struct' desc='a struct representing a single method call'>" + "<value type='string' name='methodName' desc='name of the method to be executed'/>" + "<value type='array' name='params' desc='an array representing the params to a method. sub-elements should match method signature'/>" + "</value>" + "</value>" + "</params>" + "<returns>" + "<value type='array' desc='an array of method responses'>" + "<value type='array' desc='an array containing a single value, which is the method's response'/>" + "</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.listMethods' />" + "<item name='system.methodHelp' />" + "<item name='system.describeMethods' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "<!-- system.getCapabilities -->" + "<methodDescription name='system.getCapabilities'>" + "<author>Dan Libby</author>" + "<purpose>returns a list of capabilities supported by this server</purpose>" + "<version>1.0</version>" + "<notes><item>spec url: http://groups.yahoo.com/group/xml-rpc/message/2897</item></notes>" + "<signatures>" + "<signature>" + "<returns>" + "<value type='struct' desc='list of capabilities, each with a unique key defined by the capability's spec'>" + "<value type='struct' desc='definition of a single capability'>" + "<value type='string' name='specURL'>www address of the specification defining this capability</value>" + "<value type='int' name='specVersion'>version of the spec that this server's implementation conforms to</value>" + "</value>" + "</value>" + "</returns>" + "</signature>" + "</signatures>" + "<see>" + "<item name='system.listMethods' />" + "<item name='system.methodHelp' />" + "<item name='system.describeMethods' />" + "</see>" + "<example/>" + "<error/>" + "<note/>" + "<bug/>" + "<todo/>" + "</methodDescription>" + + "</methodList>" +"</introspection>"; + + +/* forward declarations for static (non public, non api) funcs */ +static XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); +static XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); + +/*-******************* +* System Methods API * +*********************/ + +static void xsm_lazy_doc_methods_cb(XMLRPC_SERVER server, void* userData) { + XMLRPC_VALUE xDesc = XMLRPC_IntrospectionCreateDescription(xsm_introspection_xml, NULL); + XMLRPC_ServerAddIntrospectionData(server, xDesc); + XMLRPC_CleanupValue(xDesc); +} + +void xsm_register(XMLRPC_SERVER server) { + xi_register_system_methods(server); + + XMLRPC_ServerRegisterMethod(server, xsm_token_system_multicall, xsm_system_multicall_cb); + XMLRPC_ServerRegisterMethod(server, xsm_token_system_get_capabilities, xsm_system_get_capabilities_cb); + + /* callback for documentation generation should it be requested */ + XMLRPC_ServerRegisterIntrospectionCallback(server, xsm_lazy_doc_methods_cb); +} + +XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + XMLRPC_VALUE xArray = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)); + XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_array); + + if (xArray) { + XMLRPC_VALUE xMethodIter = XMLRPC_VectorRewind(xArray); + + while (xMethodIter) { + XMLRPC_REQUEST request = XMLRPC_RequestNew(); + if(request) { + const char* methodName = XMLRPC_VectorGetStringWithID(xMethodIter, "methodName"); + XMLRPC_VALUE params = XMLRPC_VectorGetValueWithID(xMethodIter, "params"); + + if(methodName && params) { + XMLRPC_VALUE xRandomArray = XMLRPC_CreateVector(0, xmlrpc_vector_array); + XMLRPC_RequestSetMethodName(request, methodName); + XMLRPC_RequestSetData(request, params); + XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); + + XMLRPC_AddValueToVector(xRandomArray, + XMLRPC_ServerCallMethod(server, request, userData)); + + XMLRPC_AddValueToVector(xReturn, xRandomArray); + } + XMLRPC_RequestFree(request, 1); + } + xMethodIter = XMLRPC_VectorNext(xArray); + } + } + return xReturn; +} + + +XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_struct); + XMLRPC_VALUE xFaults = XMLRPC_CreateVector("faults_interop", xmlrpc_vector_struct); + XMLRPC_VALUE xIntro = XMLRPC_CreateVector("introspection", xmlrpc_vector_struct); + + /* support for fault spec */ + XMLRPC_VectorAppendString(xFaults, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php", 0); + XMLRPC_VectorAppendInt(xFaults, "specVersion", 20010516); + + /* support for introspection spec */ + XMLRPC_VectorAppendString(xIntro, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.introspection.php", 0); + XMLRPC_VectorAppendInt(xIntro, "specVersion", 20010516); + + XMLRPC_AddValuesToVector(xReturn, + xFaults, + xIntro, + NULL); + + return xReturn; + +} + +/*-*********************** +* End System Methods API * +*************************/ + + + diff --git a/ext/xmlrpc/libxmlrpc/system_methods_private.h b/ext/xmlrpc/libxmlrpc/system_methods_private.h new file mode 100644 index 0000000000..72408fd3c4 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/system_methods_private.h @@ -0,0 +1,91 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Dan Libby, Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* IMPORTANT! + * + * only non-public things should be in this file. It is fine for any .c file + * in xmlrpc/src to include it, but users of the public API should never + * include it, and thus *.h files that are part of the public API should + * never include it, or they would break if this file is not present. + */ + + +#ifndef __SYSTEM_METHODS_PRIVATE_H +/* + * Avoid include redundancy. + */ +#define __SYSTEM_METHODS_PRIVATE_H + +/*---------------------------------------------------------------------------- + * system_methods_private.h + * + * Purpose: + * define non-public system.* methods + * Comments: + * xsm = xmlrpc system methods + */ + +/*---------------------------------------------------------------------------- + * Constants + */ +#define xsm_token_system_multicall "system.multiCall" +#define xsm_token_system_get_capabilities "system.getCapabilities" + + +/*---------------------------------------------------------------------------- + * Includes + */ + +/*---------------------------------------------------------------------------- + * Structures + */ + +/*---------------------------------------------------------------------------- + * Globals + */ + +/*---------------------------------------------------------------------------- + * Functions + */ +void xsm_register(XMLRPC_SERVER server); +int xsm_is_system_method(XMLRPC_Callback cb); + +/*---------------------------------------------------------------------------- + * Macros + */ + + +#endif /* __SYSTEM_METHODS_PRIVATE_H */ + + + + diff --git a/ext/xmlrpc/libxmlrpc/xml_element.c b/ext/xmlrpc/libxmlrpc/xml_element.c new file mode 100644 index 0000000000..255c0c572e --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xml_element.c @@ -0,0 +1,712 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +static const char rcsid[] = "#(@) $Id$"; + + + +/****h* ABOUT/xml_element + * NAME + * xml_element + * AUTHOR + * Dan Libby, aka danda (dan@libby.com) + * CREATION DATE + * 06/2000 + * HISTORY + * 10/15/2000 -- danda -- adding robodoc documentation + * TODO + * Nicer external API. Get rid of macros. Make opaque types, etc. + * PORTABILITY + * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just + * about anything with minor mods. + * NOTES + * This code incorporates ideas from expat-ensor from http://xml.ensor.org. + * + * It was coded primarily to act as a go-between for expat and xmlrpc. To this + * end, it stores xml elements, their sub-elements, and their attributes in an + * in-memory tree. When expat is done parsing, the tree can be walked, thus + * retrieving the values. The code can also be used to build a tree via API then + * write out the tree to a buffer, thus "serializing" the xml. + * + * It turns out this is useful for other purposes, such as parsing config files. + * YMMV. + * + * Some Features: + * - output option for xml escaping data. Choices include no escaping, entity escaping, + * or CDATA sections. + * - output option for character encoding. Defaults to (none) utf-8. + * - output option for verbosity/readability. ultra-compact, newlines, pretty/level indented. + * + * BUGS + * there must be some. + ******/ + + +#include <stdlib.h> +#include <string.h> + +#include "xml_element.h" +#include "queue.h" +#include "expat.h" +#include "encodings.h" + +#define my_free(thing) if(thing) {free(thing); thing = 0;} + +#define XML_DECL_START "<?xml" +#define XML_DECL_START_LEN sizeof(XML_DECL_START) - 1 +#define XML_DECL_VERSION "version='1.0'" +#define XML_DECL_VERSION_LEN sizeof(XML_DECL_VERSION) - 1 +#define XML_DECL_ENCODING_ATTR "encoding" +#define XML_DECL_ENCODING_ATTR_LEN sizeof(XML_DECL_ENCODING_ATTR) - 1 +#define XML_DECL_ENCODING_DEFAULT "utf-8" +#define XML_DECL_ENCODING_DEFAULT_LEN sizeof(XML_DECL_ENCODING_DEFAULT) - 1 +#define XML_DECL_END "?>" +#define XML_DECL_END_LEN sizeof(XML_DECL_END) - 1 +#define START_TOKEN_BEGIN "<" +#define START_TOKEN_BEGIN_LEN sizeof(START_TOKEN_BEGIN) - 1 +#define START_TOKEN_END ">" +#define START_TOKEN_END_LEN sizeof(START_TOKEN_END) - 1 +#define EMPTY_START_TOKEN_END "/>" +#define EMPTY_START_TOKEN_END_LEN sizeof(EMPTY_START_TOKEN_END) - 1 +#define END_TOKEN_BEGIN "</" +#define END_TOKEN_BEGIN_LEN sizeof(END_TOKEN_BEGIN) - 1 +#define END_TOKEN_END ">" +#define END_TOKEN_END_LEN sizeof(END_TOKEN_END) - 1 +#define ATTR_DELIMITER "\"" +#define ATTR_DELIMITER_LEN sizeof(ATTR_DELIMITER) - 1 +#define CDATA_BEGIN "<![CDATA[" +#define CDATA_BEGIN_LEN sizeof(CDATA_BEGIN) - 1 +#define CDATA_END "]]>" +#define CDATA_END_LEN sizeof(CDATA_END) - 1 +#define EQUALS "=" +#define EQUALS_LEN sizeof(EQUALS) - 1 +#define WHITESPACE " " +#define WHITESPACE_LEN sizeof(WHITESPACE) - 1 +#define NEWLINE "\n" +#define NEWLINE_LEN sizeof(NEWLINE) - 1 +#define MAX_VAL_BUF 144 +#define SCALAR_STR "SCALAR" +#define SCALAR_STR_LEN sizeof(SCALAR_STR) - 1 +#define VECTOR_STR "VECTOR" +#define VECTOR_STR_LEN sizeof(VECTOR_STR) - 1 +#define RESPONSE_STR "RESPONSE" +#define RESPONSE_STR_LEN sizeof(RESPONSE_STR) - 1 + + +/*----------------------------- +- Begin xml_element Functions - +-----------------------------*/ + +/****f* xml_element/xml_elem_free_non_recurse + * NAME + * xml_elem_free_non_recurse + * SYNOPSIS + * void xml_elem_free_non_recurse(xml_element* root) + * FUNCTION + * free a single xml element. child elements will not be freed. + * INPUTS + * root - the element to free + * RESULT + * void + * NOTES + * SEE ALSO + * xml_elem_free () + * xml_elem_new () + * SOURCE + */ +void xml_elem_free_non_recurse(xml_element* root) { + if(root) { + xml_element_attr* attrs = Q_Head(&root->attrs); + while(attrs) { + my_free(attrs->key); + my_free(attrs->val); + my_free(attrs); + attrs = Q_Next(&root->attrs); + } + + Q_Destroy(&root->children); + Q_Destroy(&root->attrs); + my_free((char*)root->name); + simplestring_free(&root->text); + my_free(root); + } +} +/******/ + +/****f* xml_element/xml_elem_free + * NAME + * xml_elem_free + * SYNOPSIS + * void xml_elem_free(xml_element* root) + * FUNCTION + * free an xml element and all of its child elements + * INPUTS + * root - the root of an xml tree you would like to free + * RESULT + * void + * NOTES + * SEE ALSO + * xml_elem_free_non_recurse () + * xml_elem_new () + * SOURCE + */ +void xml_elem_free(xml_element* root) { + if(root) { + xml_element* kids = Q_Head(&root->children); + while(kids) { + xml_elem_free(kids); + kids = Q_Next(&root->children); + } + xml_elem_free_non_recurse(root); + } +} +/******/ + +/****f* xml_element/xml_elem_new + * NAME + * xml_elem_new + * SYNOPSIS + * xml_element* xml_elem_new() + * FUNCTION + * allocates and initializes a new xml_element + * INPUTS + * none + * RESULT + * xml_element* or NULL. NULL indicates an out-of-memory condition. + * NOTES + * SEE ALSO + * xml_elem_free () + * xml_elem_free_non_recurse () + * SOURCE + */ +xml_element* xml_elem_new() { + xml_element* elem = calloc(1, sizeof(xml_element)); + if(elem) { + Q_Init(&elem->children); + Q_Init(&elem->attrs); + simplestring_init(&elem->text); + + /* init empty string in case we don't find any char data */ + simplestring_addn(&elem->text, "", 0); + } + return elem; +} +/******/ + +static int xml_elem_writefunc(int (*fptr)(void *data, const char *text, int size), const char *text, void *data, int len) +{ + return fptr && text ? fptr(data, text, len ? len : strlen(text)) : 0; +} + + + +static int create_xml_escape(char *pString, unsigned char c) +{ + int counter = 0; + + pString[counter++] = '&'; + pString[counter++] = '#'; + if(c >= 100) { + pString[counter++] = c / 100 + '0'; + c = c % 100; + } + if(c >= 10) { + pString[counter++] = c / 10 + '0'; + c = c % 10; + } + pString[counter++] = c + '0'; + pString[counter++] = ';'; + return counter; +} + +#define non_ascii(c) (c > 127) +#define non_print(c) (!isprint(c)) +#define markup(c) (c == '&' || c == '\"' || c == '>' || c == '<') +#define entity_length(c) ( (c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3; /* "&#" + c + ";" */ + +/* + * xml_elem_entity_escape + * + * Purpose: + * escape reserved xml chars and non utf-8 chars as xml entities + * Comments: + * The return value may be a new string, or null if no + * conversion was performed. In the latter case, *newlen will + * be 0. + * Flags (to escape) + * xml_elem_no_escaping = 0x000, + * xml_elem_entity_escaping = 0x002, // escape xml special chars as entities + * xml_elem_non_ascii_escaping = 0x008, // escape chars above 127 + * xml_elem_cdata_escaping = 0x010, // wrap in cdata + */ +static char* xml_elem_entity_escape(const char* buf, int old_len, int *newlen, XML_ELEM_ESCAPING flags) { + char *pRetval = 0; + int iNewBufLen=0; + +#define should_escape(c, flag) ( ((flag & xml_elem_markup_escaping) && markup(c)) || \ + ((flag & xml_elem_non_ascii_escaping) && non_ascii(c)) || \ + ((flag & xml_elem_non_print_escaping) && non_print(c)) ) + + if(buf && *buf) { + const unsigned char *bufcopy; + char *NewBuffer; + int ToBeXmlEscaped=0; + int iLength; + bufcopy = buf; + iLength= old_len ? old_len : strlen(buf); + while(*bufcopy) { + if( should_escape(*bufcopy, flags) ) { + /* the length will increase by length of xml escape - the character length */ + iLength += entity_length(*bufcopy); + ToBeXmlEscaped=1; + } + bufcopy++; + } + + if(ToBeXmlEscaped) { + + NewBuffer= malloc(iLength+1); + if(NewBuffer) { + bufcopy=buf; + while(*bufcopy) { + if(should_escape(*bufcopy, flags)) { + iNewBufLen += create_xml_escape(NewBuffer+iNewBufLen,*bufcopy); + } + else { + NewBuffer[iNewBufLen++]=*bufcopy; + } + bufcopy++; + } + NewBuffer[iNewBufLen] = 0; + pRetval = NewBuffer; + } + } + } + + if(newlen) { + *newlen = iNewBufLen; + } + + return pRetval; +} + + +static void xml_element_serialize(xml_element *el, int (*fptr)(void *data, const char *text, int size), void *data, XML_ELEM_OUTPUT_OPTIONS options, int depth) +{ + int i; + static STRUCT_XML_ELEM_OUTPUT_OPTIONS default_opts = {xml_elem_pretty, xml_elem_markup_escaping | xml_elem_non_print_escaping, XML_DECL_ENCODING_DEFAULT}; + static char whitespace[] = " " + " " + " "; + depth++; + + if(!el) { + fprintf(stderr, "Nothing to write\n"); + return; + } + if(!options) { + options = &default_opts; + } + + /* print xml declaration if at root level */ + if(depth == 1) { + xml_elem_writefunc(fptr, XML_DECL_START, data, XML_DECL_START_LEN); + xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); + xml_elem_writefunc(fptr, XML_DECL_VERSION, data, XML_DECL_VERSION_LEN); + if(options->encoding && *options->encoding) { + xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); + xml_elem_writefunc(fptr, XML_DECL_ENCODING_ATTR, data, XML_DECL_ENCODING_ATTR_LEN); + xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN); + xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); + xml_elem_writefunc(fptr, options->encoding, data, 0); + xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); + } + xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); + xml_elem_writefunc(fptr, XML_DECL_END, data, XML_DECL_END_LEN); + if(options->verbosity != xml_elem_no_white_space) { + xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); + } + } + + if(options->verbosity == xml_elem_pretty && depth > 2) { + xml_elem_writefunc(fptr, whitespace, data, depth - 2); + } + /* begin element */ + xml_elem_writefunc(fptr,START_TOKEN_BEGIN, data, START_TOKEN_BEGIN_LEN); + if(el->name) { + xml_elem_writefunc(fptr, el->name, data, 0); + + /* write attrs, if any */ + if(Q_Size(&el->attrs)) { + xml_element_attr* iter = Q_Head(&el->attrs); + while( iter ) { + xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); + xml_elem_writefunc(fptr, iter->key, data, 0); + xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN); + xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); + xml_elem_writefunc(fptr, iter->val, data, 0); + xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); + + iter = Q_Next(&el->attrs); + } + } + } + else { + xml_elem_writefunc(fptr, "None", data, 0); + } + /* if no text and no children, use abbreviated form, eg: <foo/> */ + if(!el->text.len && !Q_Size(&el->children)) { + xml_elem_writefunc(fptr, EMPTY_START_TOKEN_END, data, EMPTY_START_TOKEN_END_LEN); + } + /* otherwise, print element contents */ + else { + xml_elem_writefunc(fptr, START_TOKEN_END, data, START_TOKEN_END_LEN); + + /* print text, if any */ + if(el->text.len) { + char* escaped_str = el->text.str; + int buflen = el->text.len; + + if(options->escaping && options->escaping != xml_elem_cdata_escaping) { + escaped_str = xml_elem_entity_escape(el->text.str, buflen, &buflen, options->escaping ); + if(!escaped_str) { + escaped_str = el->text.str; + } + } + + if(options->escaping & xml_elem_cdata_escaping) { + xml_elem_writefunc(fptr, CDATA_BEGIN, data, CDATA_BEGIN_LEN); + } + + xml_elem_writefunc(fptr, escaped_str, data, buflen); + + if(escaped_str != el->text.str) { + my_free(escaped_str); + } + + if(options->escaping & xml_elem_cdata_escaping) { + xml_elem_writefunc(fptr, CDATA_END, data, CDATA_END_LEN); + } + } + /* no text, so print child elems */ + else { + xml_element *kids = Q_Head(&el->children); + i = 0; + while( kids ) { + if(i++ == 0) { + if(options->verbosity != xml_elem_no_white_space) { + xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); + } + } + xml_element_serialize(kids, fptr, data, options, depth); + kids = Q_Next(&el->children); + } + if(i) { + if(options->verbosity == xml_elem_pretty && depth > 2) { + xml_elem_writefunc(fptr, whitespace, data, depth - 2); + } + } + } + + xml_elem_writefunc(fptr, END_TOKEN_BEGIN, data, END_TOKEN_BEGIN_LEN); + xml_elem_writefunc(fptr,el->name ? el->name : "None", data, 0); + xml_elem_writefunc(fptr, END_TOKEN_END, data, END_TOKEN_END_LEN); + } + if(options->verbosity != xml_elem_no_white_space) { + xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); + } +} + +/* print buf to file */ +static file_out_fptr(void *f, const char *text, int size) +{ + fputs(text, (FILE *)f); +} + +/* print buf to simplestring */ +static simplestring_out_fptr(void *f, const char *text, int size) +{ + simplestring* buf = (simplestring*)f; + if(buf) { + simplestring_addn(buf, text, size); + } +} + +/****f* xml_element/xml_elem_serialize_to_string + * NAME + * xml_elem_serialize_to_string + * SYNOPSIS + * void xml_element_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len) + * FUNCTION + * writes element tree as XML into a newly allocated buffer + * INPUTS + * el - root element of tree + * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS + * buf_len - length of returned buffer, if not null. + * RESULT + * char* or NULL. Must be free'd by caller. + * NOTES + * SEE ALSO + * xml_elem_serialize_to_stream () + * xml_elem_parse_buf () + * SOURCE + */ +char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len) +{ + simplestring buf; + simplestring_init(&buf); + + xml_element_serialize(el, simplestring_out_fptr, (void *)&buf, options, 0); + + if(buf_len) { + *buf_len = buf.len; + } + + return buf.str; +} +/******/ + +/****f* xml_element/xml_elem_serialize_to_stream + * NAME + * xml_elem_serialize_to_stream + * SYNOPSIS + * void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options) + * FUNCTION + * writes element tree as XML into a stream (typically an opened file) + * INPUTS + * el - root element of tree + * output - stream handle + * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS + * RESULT + * void + * NOTES + * SEE ALSO + * xml_elem_serialize_to_string () + * xml_elem_parse_buf () + * SOURCE + */ +void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options) +{ + xml_element_serialize(el, file_out_fptr, (void *)output, options, 0); +} +/******/ + +/*--------------------------* +* End xml_element Functions * +*--------------------------*/ + + +/*---------------------- +* Begin Expat Handlers * +*---------------------*/ + +typedef struct _xml_elem_data { + xml_element* root; + xml_element* current; + XML_ELEM_INPUT_OPTIONS input_options; + int needs_enc_conversion; +} xml_elem_data; + + +/* expat start of element handler */ +static void startElement(void *userData, const char *name, const char **attrs) +{ + int i; + xml_element *c; + xml_elem_data* mydata = (xml_elem_data*)userData; + const char** p = attrs; + + if(mydata) { + c = mydata->current; + + mydata->current = xml_elem_new(); + mydata->current->name = (char*)strdup(name); + mydata->current->parent = c; + + /* init attrs */ + while(p && *p) { + xml_element_attr* attr = malloc(sizeof(xml_element_attr)); + if(attr) { + attr->key = strdup(*p); + attr->val = strdup(*(p+1)); + Q_PushTail(&mydata->current->attrs, attr); + + p += 2; + } + } + } +} + +/* expat end of element handler */ +static void endElement(void *userData, const char *name) +{ + xml_elem_data* mydata = (xml_elem_data*)userData; + + if(mydata && mydata->current && mydata->current->parent) { + Q_PushTail(&mydata->current->parent->children, mydata->current); + + mydata->current = mydata->current->parent; + } +} + +/* expat char data handler */ +static void charHandler(void *userData, + const char *s, + int len) +{ + xml_elem_data* mydata = (xml_elem_data*)userData; + if(mydata && mydata->current) { + + /* Check if we need to decode utf-8 parser output to another encoding */ + if(mydata->needs_enc_conversion && mydata->input_options->encoding) { + char* add_text = utf8_decode(s, len, &len, mydata->input_options->encoding); + if(add_text) { + simplestring_addn(&mydata->current->text, add_text, len); + free(add_text); + return; + } + } + simplestring_addn(&mydata->current->text, s, len); + } +} +/******/ + +/*-------------------* +* End Expat Handlers * +*-------------------*/ + +/*-------------------* +* xml_elem_parse_buf * +*-------------------*/ + +/****f* xml_element/xml_elem_parse_buf + * NAME + * xml_elem_parse_buf + * SYNOPSIS + * xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error) + * FUNCTION + * parse a buffer containing XML into an xml_element in-memory tree + * INPUTS + * in_buf - buffer containing XML document + * len - length of buffer + * options - input options. optional + * error - error result data. optional. check if result is null. + * RESULT + * void + * NOTES + * The returned data must be free'd by caller + * SEE ALSO + * xml_elem_serialize_to_string () + * xml_elem_free () + * SOURCE + */ +xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error) +{ + xml_element* xReturn = NULL; + char buf[100] = ""; + static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8}; + + if(!options) { + options = &default_opts; + } + + if(in_buf) { + XML_Parser parser; + xml_elem_data mydata = {0}; + + parser = XML_ParserCreate(NULL); + + mydata.root = xml_elem_new(); + mydata.current = mydata.root; + mydata.input_options = options; + mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8); + + XML_SetElementHandler(parser, startElement, endElement); + XML_SetCharacterDataHandler(parser, charHandler); + + /* pass the xml_elem_data struct along */ + XML_SetUserData(parser, (void*)&mydata); + + if(!len) { + len = strlen(in_buf); + } + + /* parse the XML */ + if(XML_Parse(parser, in_buf, len, 1) == 0) { + enum XML_Error err_code = XML_GetErrorCode(parser); + int line_num = XML_GetCurrentLineNumber(parser); + int col_num = XML_GetCurrentColumnNumber(parser); + long byte_idx = XML_GetCurrentByteIndex(parser); + int byte_total = XML_GetCurrentByteCount(parser); + const char * error_str = XML_ErrorString(err_code); + if(byte_idx >= 0) { + snprintf(buf, + sizeof(buf), + "\n\tdata beginning %i before byte index: %s\n", + byte_idx > 10 ? 10 : byte_idx, + in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx)); + } + + fprintf(stderr, "expat reports error code %i\n" + "\tdescription: %s\n" + "\tline: %i\n" + "\tcolumn: %i\n" + "\tbyte index: %i\n" + "\ttotal bytes: %i\n%s ", + err_code, error_str, line_num, + col_num, byte_idx, byte_total, buf); + + + /* error condition */ + if(error) { + error->parser_code = (long)err_code; + error->line = line_num; + error->column = col_num; + error->byte_index = byte_idx; + error->parser_error = error_str; + } + } + else { + xReturn = (xml_element*)Q_Head(&mydata.root->children); + } + + XML_ParserFree(parser); + + + xml_elem_free_non_recurse(mydata.root); + } + + return xReturn; +} + +/******/ diff --git a/ext/xmlrpc/libxmlrpc/xml_element.h b/ext/xmlrpc/libxmlrpc/xml_element.h new file mode 100644 index 0000000000..bf4f3a8ba4 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xml_element.h @@ -0,0 +1,202 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +#ifndef __XML_ELEMENT_H__ + #define __XML_ELEMENT_H__ + +/* includes */ +#include <stdio.h> +#include "queue.h" +#include "simplestring.h" +#include "encodings.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/****d* enum/XML_ELEM_VERBOSITY + * NAME + * XML_ELEM_VERBOSITY + * NOTES + * verbosity/readability options for generated xml + * SEE ALSO + * XML_ELEM_OUTPUT_OPTIONS + * SOURCE + */ +typedef enum _xml_elem_verbosity { + xml_elem_no_white_space, /* compact xml with no white space */ + xml_elem_newlines_only, /* add newlines for enhanced readability */ + xml_elem_pretty /* add newlines and indent accordint to depth */ +} XML_ELEM_VERBOSITY; +/******/ + + +/****d* enum/XML_ELEM_ESCAPING + * NAME + * XML_ELEM_ESCAPING + * NOTES + * xml escaping options for generated xml + * SEE ALSO + * XML_ELEM_OUTPUT_OPTIONS + * SOURCE + */ +typedef enum _xml_elem_escaping { + xml_elem_no_escaping = 0x000, + xml_elem_markup_escaping = 0x002, /* entity escape xml special chars */ + xml_elem_non_ascii_escaping = 0x008, /* entity escape chars above 127 */ + xml_elem_non_print_escaping = 0x010, /* entity escape non print (illegal) chars */ + xml_elem_cdata_escaping = 0x020, /* wrap in cdata section */ +} XML_ELEM_ESCAPING; +/******/ + + +/****s* struct/XML_ELEM_OUTPUT_OPTIONS + * NAME + * XML_ELEM_OUTPUT_OPTIONS + * NOTES + * defines various output options + * SOURCE + */ +typedef struct _xml_output_options { + XML_ELEM_VERBOSITY verbosity; /* length/verbosity of xml */ + XML_ELEM_ESCAPING escaping; /* how to escape special chars */ + const char* encoding; /* <?xml encoding="<encoding>" ?> */ +} STRUCT_XML_ELEM_OUTPUT_OPTIONS, *XML_ELEM_OUTPUT_OPTIONS; +/******/ + +/****s* struct/XML_ELEM_INPUT_OPTIONS + * NAME + * XML_ELEM_INPUT_OPTIONS + * NOTES + * defines various input options + * SOURCE + */ +typedef struct _xml_input_options { + ENCODING_ID encoding; /* which encoding to use. */ +} STRUCT_XML_ELEM_INPUT_OPTIONS, *XML_ELEM_INPUT_OPTIONS; +/******/ + +/****s* struct/XML_ELEM_ERROR + * NAME + * XML_ELEM_ERROR + * NOTES + * defines an xml parser error + * SOURCE + */ +typedef struct _xml_elem_error { + int parser_code; + const char* parser_error; + long line; + long column; + long byte_index; +} STRUCT_XML_ELEM_ERROR, *XML_ELEM_ERROR; +/******/ + + +/*-************************ +* begin xml element stuff * +**************************/ + +/****s* struct/xml_elem_attr + * NAME + * xml_elem_attr + * NOTES + * representation of an xml attribute, foo="bar" + * SOURCE + */ +typedef struct _xml_element_attr { + char* key; /* attribute key */ + char* val; /* attribute value */ +} xml_element_attr; +/******/ + +/****s* struct/xml_elem_attr + * NAME + * xml_elem_attr + * NOTES + * representation of an xml element, eg <candidate name="Harry Browne" party="Libertarian"/> + * SOURCE + */ +typedef struct _xml_element { + const char* name; /* element identifier */ + simplestring text; /* text contained between element begin/end pairs */ + struct _xml_element* parent; /* element's parent */ + + queue attrs; /* attribute list */ + queue children; /* child element list */ +} xml_element; +/******/ + +void xml_elem_free(xml_element* root); +void xml_elem_free_non_recurse(xml_element* root); +xml_element* xml_elem_new(); +char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len); +void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options); +xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error); + +/*-********************** +* end xml element stuff * +************************/ + +/*-********************** +* Begin xml_element API * +************************/ + +/****d* VALUE/XMLRPC_MACROS + * NAME + * Some Helpful Macros + * NOTES + * Some macros for making life easier. Should be self-explanatory. + * SEE ALSO + * XMLRPC_AddValueToVector () + * XMLRPC_VectorGetValueWithID_Case () + * XMLRPC_VALUE + * SOURCE + */ +#define xml_elem_next_element(el) ((el) ? (xml_element *)Q_Next(&el->children) : NULL) +#define xml_elem_head_element(el) ((el) ? (xml_element *)Q_Head(&el->children) : NULL) +#define xml_elem_next_attr(el) ((el) ? (xml_element_attr *)Q_Next(&el->attrs) : NULL) +#define xml_elem_head_attr(el) ((el) ? (xml_element_attr *)Q_Head(&el->attrs) : NULL) +#define xml_elem_get_name(el) (char *)((el) ? el->name : NULL) +#define xml_elem_get_val(el) (char *)((el) ? el->text.str : NULL) +/******/ + + +/*-******************** +* End xml_element API * +**********************/ + +#ifdef __cplusplus +} +#endif + +#endif /* __XML_ELEMENT_H__ */ diff --git a/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c b/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c new file mode 100644 index 0000000000..0d827a5fb0 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c @@ -0,0 +1,316 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +#include <string.h> +#include <stdlib.h> +#include "xml_to_dandarpc.h" +#include "base64.h" + +/* list of tokens used in vocab */ +#define ELEM_METHODCALL "methodCall" +#define ELEM_METHODNAME "methodName" +#define ELEM_METHODRESPONSE "methodResponse" +#define ELEM_ROOT "simpleRPC" + +#define ATTR_ARRAY "array" +#define ATTR_BASE64 "base64" +#define ATTR_BOOLEAN "boolean" +#define ATTR_DATETIME "dateTime.iso8601" +#define ATTR_DOUBLE "double" +#define ATTR_ID "id" +#define ATTR_INT "int" +#define ATTR_MIXED "mixed" +#define ATTR_SCALAR "scalar" +#define ATTR_STRING "string" +#define ATTR_STRUCT "struct" +#define ATTR_TYPE "type" +#define ATTR_VECTOR "vector" +#define ATTR_VERSION "version" + +#define VAL_VERSION_0_9 "0.9" + + +XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE xCurrent, xml_element* el) { + if(!xCurrent) { + xCurrent = XMLRPC_CreateValueEmpty(); + } + + if(el->name) { + const char* id = NULL; + const char* type = NULL; + xml_element_attr* attr_iter = Q_Head(&el->attrs); + + while(attr_iter) { + if(!strcmp(attr_iter->key, ATTR_ID)) { + id = attr_iter->val; + } + if(!strcmp(attr_iter->key, ATTR_TYPE)) { + type = attr_iter->val; + } + attr_iter = Q_Next(&el->attrs); + } + + if(id) { + XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact); + } + + if(!strcmp(el->name, ATTR_SCALAR)) { + if(!type || !strcmp(type, ATTR_STRING)) { + XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len); + } + else if(!strcmp(type, ATTR_INT)) { + XMLRPC_SetValueInt(xCurrent, atoi(el->text.str)); + } + else if(!strcmp(type, ATTR_BOOLEAN)) { + XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str)); + } + else if(!strcmp(type, ATTR_DOUBLE)) { + XMLRPC_SetValueDouble(xCurrent, atof(el->text.str)); + } + else if(!strcmp(type, ATTR_DATETIME)) { + XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str); + } + else if(!strcmp(type, ATTR_BASE64)) { + struct buffer_st buf; + base64_decode(&buf, el->text.str, el->text.len); + XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset); + buffer_delete(&buf); + } + } + else if(!strcmp(el->name, ATTR_VECTOR)) { + xml_element* iter = (xml_element*)Q_Head(&el->children); + + if(!type || !strcmp(type, ATTR_MIXED)) { + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); + } + else if(!strcmp(type, ATTR_ARRAY)) { + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); + } + else if(!strcmp(type, ATTR_STRUCT)) { + XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct); + } + while( iter ) { + XMLRPC_VALUE xNext = XMLRPC_CreateValueEmpty(); + xml_element_to_DANDARPC_REQUEST_worker(request, xNext, iter); + XMLRPC_AddValueToVector(xCurrent, xNext); + iter = (xml_element*)Q_Next(&el->children); + } + } + else { + xml_element* iter = (xml_element*)Q_Head(&el->children); + while( iter ) { + xml_element_to_DANDARPC_REQUEST_worker(request, xCurrent, iter); + iter = (xml_element*)Q_Next(&el->children); + } + + if(!strcmp(el->name, ELEM_METHODCALL)) { + if(request) { + XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); + } + } + else if(!strcmp(el->name, ELEM_METHODRESPONSE)) { + if(request) { + XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); + } + } + else if(!strcmp(el->name, ELEM_METHODNAME)) { + if(request) { + XMLRPC_RequestSetMethodName(request, el->text.str); + } + } + } + } + return xCurrent; +} + +XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el) +{ + return xml_element_to_DANDARPC_REQUEST_worker(NULL, NULL, el); +} + +XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el) +{ + if(request) { + return XMLRPC_RequestSetData(request, xml_element_to_DANDARPC_REQUEST_worker(request, NULL, el)); + } + return NULL; +} + +xml_element* DANDARPC_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) { +#define BUF_SIZE 512 + xml_element* root = NULL; + if(node) { + char buf[BUF_SIZE]; + const char* id = XMLRPC_GetValueID(node); + XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node); + XMLRPC_REQUEST_OUTPUT_OPTIONS output = XMLRPC_RequestGetOutputOptions(request); + int bNoAddType = (type == xmlrpc_string && request && output && output->xml_elem_opts.verbosity == xml_elem_no_white_space); + xml_element* elem_val = xml_elem_new(); + const char* pAttrType = NULL; + + xml_element_attr* attr_type = bNoAddType ? NULL : malloc(sizeof(xml_element_attr)); + + if(attr_type) { + attr_type->key = strdup(ATTR_TYPE); + attr_type->val = 0; + Q_PushTail(&elem_val->attrs, attr_type); + } + + elem_val->name = (type == xmlrpc_vector) ? strdup(ATTR_VECTOR) : strdup(ATTR_SCALAR); + + if(id && *id) { + xml_element_attr* attr_id = malloc(sizeof(xml_element_attr)); + if(attr_id) { + attr_id->key = strdup(ATTR_ID); + attr_id->val = strdup(id); + Q_PushTail(&elem_val->attrs, attr_id); + } + } + + switch(type) { + case xmlrpc_string: + pAttrType = ATTR_STRING; + simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); + break; + case xmlrpc_int: + pAttrType = ATTR_INT; + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_boolean: + pAttrType = ATTR_BOOLEAN; + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_double: + pAttrType = ATTR_DOUBLE; + snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_datetime: + pAttrType = ATTR_DATETIME; + simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node)); + break; + case xmlrpc_base64: + { + struct buffer_st buf; + pAttrType = ATTR_BASE64; + base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); + simplestring_addn(&elem_val->text, buf.data, buf.offset ); + buffer_delete(&buf); + } + break; + case xmlrpc_vector: + { + XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node); + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); + + switch(my_type) { + case xmlrpc_vector_array: + pAttrType = ATTR_ARRAY; + break; + case xmlrpc_vector_mixed: + pAttrType = ATTR_MIXED; + break; + case xmlrpc_vector_struct: + pAttrType = ATTR_STRUCT; + break; + default: + break; + } + + /* recurse through sub-elements */ + while( xIter ) { + xml_element* next_el = DANDARPC_to_xml_element_worker(request, xIter); + if(next_el) { + Q_PushTail(&elem_val->children, next_el); + } + xIter = XMLRPC_VectorNext(node); + } + } + break; + default: + break; + } + if(pAttrType && attr_type && !bNoAddType) { + attr_type->val = strdup(pAttrType); + } + root = elem_val; + } + return root; +} + +xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node) { + return DANDARPC_to_xml_element_worker(NULL, node); +} + +xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { + xml_element* wrapper = NULL; + xml_element* root = NULL; + if(request) { + XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request); + const char* pStr = NULL; + xml_element_attr* version = malloc(sizeof(xml_element_attr)); + version->key = strdup(ATTR_VERSION); + version->val = strdup(VAL_VERSION_0_9); + + wrapper = xml_elem_new(); + + if(request_type == xmlrpc_request_response) { + pStr = ELEM_METHODRESPONSE; + } + else if(request_type == xmlrpc_request_call) { + pStr = ELEM_METHODCALL; + } + if(pStr) { + wrapper->name = strdup(pStr); + } + + root = xml_elem_new(); + root->name = strdup(ELEM_ROOT); + Q_PushTail(&root->attrs, version); + Q_PushTail(&root->children, wrapper); + + pStr = XMLRPC_RequestGetMethodName(request); + + if(pStr) { + xml_element* method = xml_elem_new(); + method->name = strdup(ELEM_METHODNAME); + simplestring_add(&method->text, pStr); + Q_PushTail(&wrapper->children, method); + } + Q_PushTail(&wrapper->children, + DANDARPC_to_xml_element_worker(request, XMLRPC_RequestGetData(request))); + } + return root; +} + diff --git a/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.h b/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.h new file mode 100644 index 0000000000..6facb55778 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.h @@ -0,0 +1,44 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +#ifndef XML_TO_DANDARPC_H + #define XML_TO_DANDARPC_H + +#include "time.h" +#include "xmlrpc.h" + +XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el); +XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el); +xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node); +xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request); + +#endif /* XML_TO_DANDARPC_H */ diff --git a/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c b/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c new file mode 100644 index 0000000000..1cd3d9ac2b --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c @@ -0,0 +1,365 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +static const char rcsid[] = "#(@) $Id$"; + +#include <string.h> +#include <stdlib.h> +#include "xml_to_xmlrpc.h" +#include "base64.h" + +/* list of tokens used in vocab */ +#define ELEM_ARRAY "array" +#define ELEM_BASE64 "base64" +#define ELEM_BOOLEAN "boolean" +#define ELEM_DATA "data" +#define ELEM_DATETIME "dateTime.iso8601" +#define ELEM_DOUBLE "double" +#define ELEM_FAULT "fault" +#define ELEM_FAULTCODE "faultCode" +#define ELEM_FAULTSTRING "faultString" +#define ELEM_I4 "i4" +#define ELEM_INT "int" +#define ELEM_MEMBER "member" +#define ELEM_METHODCALL "methodCall" +#define ELEM_METHODNAME "methodName" +#define ELEM_METHODRESPONSE "methodResponse" +#define ELEM_NAME "name" +#define ELEM_PARAM "param" +#define ELEM_PARAMS "params" +#define ELEM_STRING "string" +#define ELEM_STRUCT "struct" +#define ELEM_VALUE "value" + + +XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) { + if (!current_val) { + /* This should only be the case for the first element */ + current_val = XMLRPC_CreateValueEmpty(); + } + + if (el->name) { + if (!strcmp(el->name, ELEM_DATA) /* should be ELEM_ARRAY, but there is an extra level. weird */ + || ((!strcmp(el->name, ELEM_PARAMS)) && + (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call)) /* this "PARAMS" concept is silly. dave?! */ + || !strcmp(el->name, ELEM_FAULT)) { /* so is this "FAULT" nonsense. */ + xml_element* iter = (xml_element*)Q_Head(&el->children); + XMLRPC_SetIsVector(current_val, xmlrpc_vector_array); + + while (iter) { + XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); + xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); + XMLRPC_AddValueToVector(current_val, xNextVal); + iter = (xml_element*)Q_Next(&el->children); + } + } else if (!strcmp(el->name, ELEM_STRUCT)) { + xml_element* iter = (xml_element*)Q_Head(&el->children); + XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct); + + while ( iter ) { + XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); + xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); + XMLRPC_AddValueToVector(current_val, xNextVal); + iter = (xml_element*)Q_Next(&el->children); + } + } else if (!strcmp(el->name, ELEM_STRING) || + (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) { + XMLRPC_SetValueString(current_val, el->text.str, el->text.len); + } else if (!strcmp(el->name, ELEM_NAME)) { + XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact); + } else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) { + XMLRPC_SetValueInt(current_val, atoi(el->text.str)); + } else if (!strcmp(el->name, ELEM_BOOLEAN)) { + XMLRPC_SetValueBoolean(current_val, atoi(el->text.str)); + } else if (!strcmp(el->name, ELEM_DOUBLE)) { + XMLRPC_SetValueDouble(current_val, atof(el->text.str)); + } else if (!strcmp(el->name, ELEM_DATETIME)) { + XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str); + } else if (!strcmp(el->name, ELEM_BASE64)) { + struct buffer_st buf; + base64_decode(&buf, el->text.str, el->text.len); + XMLRPC_SetValueBase64(current_val, buf.data, buf.offset); + buffer_delete(&buf); + } else { + xml_element* iter; + + if (!strcmp(el->name, ELEM_METHODCALL)) { + if (request) { + XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); + } + } else if (!strcmp(el->name, ELEM_METHODRESPONSE)) { + if (request) { + XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); + } + } else if (!strcmp(el->name, ELEM_METHODNAME)) { + if (request) { + XMLRPC_RequestSetMethodName(request, el->text.str); + } + } + + iter = (xml_element*)Q_Head(&el->children); + while ( iter ) { + xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector, + current_val, iter); + iter = (xml_element*)Q_Next(&el->children); + } + } + } + return current_val; +} + +XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el) +{ + return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el); +} + +XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el) +{ + if (request) { + return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el)); + } + return NULL; +} + +xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node, + XMLRPC_REQUEST_TYPE request_type, int depth) { +#define BUF_SIZE 512 + xml_element* root = NULL; + if (node) { + char buf[BUF_SIZE]; + XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node); + XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node); + xml_element* elem_val = xml_elem_new(); + + /* special case for when root element is not an array */ + if (depth == 0 && + !(type == xmlrpc_vector && + vtype == xmlrpc_vector_array && + request_type == xmlrpc_request_call) ) { + int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)); + + xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1); + if (next_el) { + Q_PushTail(&elem_val->children, next_el); + } + elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS); + } else { + switch (type) { + case xmlrpc_string: + elem_val->name = strdup(ELEM_STRING); + simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); + break; + case xmlrpc_int: + elem_val->name = strdup(ELEM_INT); + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_boolean: + elem_val->name = strdup(ELEM_BOOLEAN); + snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_double: + elem_val->name = strdup(ELEM_DOUBLE); + snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); + simplestring_add(&elem_val->text, buf); + break; + case xmlrpc_datetime: + elem_val->name = strdup(ELEM_DATETIME); + simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node)); + break; + case xmlrpc_base64: + { + struct buffer_st buf; + elem_val->name = strdup(ELEM_BASE64); + base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); + simplestring_addn(&elem_val->text, buf.data, buf.offset ); + buffer_delete(&buf); + } + break; + case xmlrpc_vector: + { + XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node); + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); + xml_element* root_vector_elem = elem_val; + + switch (my_type) { + case xmlrpc_vector_array: + { + if(depth == 0) { + elem_val->name = strdup(ELEM_PARAMS); + } + else { + /* Hi my name is Dave and I like to make things as confusing + * as possible, thus I will throw in this 'data' element + * where it absolutely does not belong just so that people + * cannot code arrays and structs in a similar and straight + * forward manner. Have a good day. + * + * GRRRRRRRRR! + */ + xml_element* data = xml_elem_new(); + data->name = strdup(ELEM_DATA); + + elem_val->name = strdup(ELEM_ARRAY); + Q_PushTail(&elem_val->children, data); + root_vector_elem = data; + } + } + break; + case xmlrpc_vector_mixed: /* not officially supported */ + case xmlrpc_vector_struct: + elem_val->name = strdup(ELEM_STRUCT); + break; + default: + break; + } + + /* recurse through sub-elements */ + while ( xIter ) { + xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1); + if (next_el) { + Q_PushTail(&root_vector_elem->children, next_el); + } + xIter = XMLRPC_VectorNext(node); + } + } + break; + default: + break; + } + } + + { + XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector); + + if (depth == 1) { + xml_element* value = xml_elem_new(); + value->name = strdup(ELEM_VALUE); + + /* yet another hack for the "fault" crap */ + if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) { + root = value; + } else { + xml_element* param = xml_elem_new(); + param->name = strdup(ELEM_PARAM); + + Q_PushTail(¶m->children, value); + + root = param; + } + Q_PushTail(&value->children, elem_val); + } else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) { + xml_element* member = xml_elem_new(); + xml_element* name = xml_elem_new(); + xml_element* value = xml_elem_new(); + + member->name = strdup(ELEM_MEMBER); + name->name = strdup(ELEM_NAME); + value->name = strdup(ELEM_VALUE); + + simplestring_add(&name->text, XMLRPC_GetValueID(node)); + + Q_PushTail(&member->children, name); + Q_PushTail(&member->children, value); + Q_PushTail(&value->children, elem_val); + + root = member; + } else if (vtype == xmlrpc_vector_array) { + xml_element* value = xml_elem_new(); + + value->name = strdup(ELEM_VALUE); + + Q_PushTail(&value->children, elem_val); + + root = value; + } else if (vtype == xmlrpc_vector_none) { + /* no parent. non-op */ + root = elem_val; + } else { + xml_element* value = xml_elem_new(); + + value->name = strdup(ELEM_VALUE); + + Q_PushTail(&value->children, elem_val); + + root = value; + } + } + } + return root; +} + +xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) { + return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0); +} + +xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { + xml_element* wrapper = NULL; + if (request) { + const char* pStr = NULL; + XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request); + XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request); + + wrapper = xml_elem_new(); + + if (request_type == xmlrpc_request_call) { + pStr = ELEM_METHODCALL; + } else if (request_type == xmlrpc_request_response) { + pStr = ELEM_METHODRESPONSE; + } + if (pStr) { + wrapper->name = strdup(pStr); + } + + pStr = XMLRPC_RequestGetMethodName(request); + + if (pStr) { + xml_element* method = xml_elem_new(); + method->name = strdup(ELEM_METHODNAME); + simplestring_add(&method->text, pStr); + Q_PushTail(&wrapper->children, method); + } + if (xParams) { + Q_PushTail(&wrapper->children, + XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0)); + } else { + /* Despite the spec, the xml-rpc list folk want me to send an empty params element */ + xml_element* params = xml_elem_new(); + params->name = strdup(ELEM_PARAMS); + Q_PushTail(&wrapper->children, params); + } + } + return wrapper; +} + diff --git a/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.h b/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.h new file mode 100644 index 0000000000..234a153460 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.h @@ -0,0 +1,45 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +#ifndef XML_TO_XMLRPC_H + #define XML_TO_XMLRPC_H + +#include "time.h" +#include "xmlrpc.h" + +XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el); +XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el); +xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node); +xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request); + +#endif /* XML_TO_XMLRPC_H */ diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc.c b/ext/xmlrpc/libxmlrpc/xmlrpc.c new file mode 100644 index 0000000000..f95ac949b3 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xmlrpc.c @@ -0,0 +1,2471 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +static const char rcsid[] = "#(@) $Id$"; + + +/****h* ABOUT/xmlrpc + * NAME + * XMLRPC_VALUE + * AUTHOR + * Dan Libby, aka danda (dan@libby.com) + * CREATION DATE + * 9/1999 - 10/2000 + * HISTORY + * 09/1999 -- danda -- Initial API, before I even knew of standard XMLRPC vocab. Response only. + * 06/2000 -- danda -- played with expat-ensor from www.ensor.org. Cool, but some flaws. + * 07/2000 -- danda -- wrote new implementation to be compatible with xmlrpc standard and + * incorporated some ideas from ensor, most notably the separation of + * xml dom from xmlrpc api. + * 08/2000 -- danda -- support for two vocabularies: danda-rpc and xml-rpc + * 08/2000 -- danda -- PHP C extension that uses XMLRPC + * 10/15/2000 -- danda -- adding robodoc documentation + * TODO + * Server method introspection. (Enumerate available methods, describe I/O) + * PORTABILITY + * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just + * about anything with minor mods. + * NOTES + * Welcome to XMLRPC. For more info on the specification and history, see + * http://www.xmlrpc.org. + * + * This code aims to be a full-featured C implementation of XMLRPC. It does not + * have any networking code. Rather, it is intended to be plugged into apps + * or libraries with existing networking facilities, eg PHP, apache, perl, mozilla, + * home-brew application servers, etc. + * + * Usage Paradigm: + * The user of this library will typically be implementing either an XMLRPC server, + * an XMLRPC client, or both. The client will use the library to build an in-memory + * representation of a request, and then serialize (encode) that request into XML. The + * client will then send the XML to the server via external mechanism. The server will + * de-serialize the XML back into an binary representation, call the appropriate registered + * method -- thereby generating a response. The response will be serialized into XML and + * sent back to the client. The client will de-serialize it into memory, and can + * iterate through the results via API. + * + * Both the request and the response may consist of arbitrarily long, arbitrarily nested + * values. The values may be one of several types, as defined by XMLRPC_VALUE_TYPE. + * + * Features and Architecture: + * - The XML parsing (xml_element.c) is completely independent of the XMLRPC api. In fact, + * it can be used as a standalone dom implementation. + * - Because of this, the same XMLRPC data can be serialized into multiple xml vocabularies. + * It is simply a matter of writing a transport. So far, two transports have been defined. + * The default xmlrpc vocab (xml_to_xmlrpc.c), and simple-rpc (xml_to_dandarpc.c) which is + * proprietary, but imho more readable, and nice for proprietary legacy reasons. + * - Various output options, including: xml escaping via CDATA or entity, case folding, + * vocab version, and character encoding. + * - One to One mapping between C structures and actual values, unlike ensor which forces + * one to understand the arcana of the xmlrpc vocab. + * - support for mixed indexed/keyed vector types, making it more compatible with + * languages such as PHP. + * - quite speedy compared to implementations written in interpreted languages. Also, uses + * intelligent string handling, so not many strlen() calls, etc. + * - comprehensive API for manipulation of values + *******/ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <time.h> + +#include "queue.h" +#include "xmlrpc.h" +#include "expat.h" +#include "base64.h" + +#include "xml_to_xmlrpc.h" +#include "xml_to_dandarpc.h" +#include "xml_element.h" +#include "xmlrpc_private.h" +#include "xmlrpc_introspection_private.h" + + + +/*-********************* +* Begin Time Functions * +***********************/ + +static int date_from_ISO8601(const char *text, time_t *value) +{ + struct tm tm; + int n; + int i; + time_t t; + + tm.tm_isdst = -1; + + if(strlen(text) < 17) { + return -1; + } + + n = 1000; + tm.tm_year = 0; + for(i = 0; i < 4; i++) { + tm.tm_year += (text[i]-'0')*n; + n /= 10; + } + n = 10; + tm.tm_mon = 0; + for(i = 0; i < 2; i++) { + tm.tm_mon += (text[i+4]-'0')*n; + n /= 10; + } + tm.tm_mon --; + + n = 10; + tm.tm_mday = 0; + for(i = 0; i < 2; i++) { + tm.tm_mday += (text[i+6]-'0')*n; + n /= 10; + } + + n = 10; + tm.tm_hour = 0; + for(i = 0; i < 2; i++) { + tm.tm_hour += (text[i+9]-'0')*n; + n /= 10; + } + + n = 10; + tm.tm_min = 0; + for(i = 0; i < 2; i++) { + tm.tm_min += (text[i+12]-'0')*n; + n /= 10; + } + + n = 10; + tm.tm_sec = 0; + for(i = 0; i < 2; i++) { + tm.tm_sec += (text[i+15]-'0')*n; + n /= 10; + } + + tm.tm_year -= 1900; + + *value = mktime(&tm); + + return 0; + +} + +static int date_to_ISO8601(time_t value, char *buf, int length) +{ + struct tm *tm; + tm = localtime(&value); + + return strftime(buf, length, "%Y%m%dT%H:%M:%S", tm); +} + +/*-******************* +* End Time Functions * +*********************/ + + +/*-*************************** +* Begin XMLRPC_REQUEST funcs * +*****************************/ + +/****f* REQUEST/XMLRPC_RequestNew + * NAME + * XMLRPC_RequestNew + * SYNOPSIS + * XMLRPC_REQUEST XMLRPC_RequestNew() + * FUNCTION + * Creates a new XMLRPC_Request data struct + * INPUTS + * none + * SEE ALSO + * XMLRPC_RequestFree () + * SOURCE + */ +XMLRPC_REQUEST XMLRPC_RequestNew() { + XMLRPC_REQUEST xRequest = calloc(1, sizeof(STRUCT_XMLRPC_REQUEST)); + if(xRequest) { + simplestring_init(&xRequest->methodName); + } + return xRequest; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestFree + * NAME + * XMLRPC_RequestFree + * SYNOPSIS + * void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) + * FUNCTION + * Free XMLRPC Request and all sub-values + * INPUTS + * request -- previously allocated request struct + * bFreeIO -- 1 = also free request value data, if any, 0 = ignore. + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_CleanupValue () + * SOURCE + */ +void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) { + if(request) { + simplestring_free(&request->methodName); + + if(request->io && bFreeIO) { + XMLRPC_CleanupValue(request->io); + } + if(request->error) { + XMLRPC_CleanupValue(request->error); + } + my_free(request); + } +} +/*******/ + +/* Set Method Name to call */ +/****f* REQUEST/XMLRPC_RequestSetMethodName + * NAME + * XMLRPC_RequestSetMethodName + * SYNOPSIS + * const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName) + * FUNCTION + * Set name of method to call with this request. + * INPUTS + * request -- previously allocated request struct + * methodName -- name of method + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestGetMethodName () + * XMLRPC_RequestFree () + * SOURCE + */ +const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName) { + if(request) { + simplestring_clear(&request->methodName); + simplestring_add(&request->methodName, methodName); + return request->methodName.str; + } + return NULL; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestGetMethodName + * NAME + * XMLRPC_RequestGetMethodName + * SYNOPSIS + * const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) + * FUNCTION + * Get name of method called by this request + * INPUTS + * request -- previously allocated request struct + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestSetMethodName () + * XMLRPC_RequestFree () + * SOURCE + */ +const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) { + return request ? request->methodName.str : NULL; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestSetRequestType + * NAME + * XMLRPC_RequestSetRequestType + * SYNOPSIS + * XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) + * FUNCTION + * A request struct may be allocated by a caller or by xmlrpc + * in response to a request. This allows setting the + * request type. + * INPUTS + * request -- previously allocated request struct + * type -- request type [xmlrpc_method_call | xmlrpc_method_response] + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestGetRequestType () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST_TYPE + * SOURCE + */ +XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) { + if(request) { + request->request_type = type; + return request->request_type; + } + return xmlrpc_request_none; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestGetRequestType + * NAME + * XMLRPC_RequestGetRequestType + * SYNOPSIS + * XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) + * FUNCTION + * A request struct may be allocated by a caller or by xmlrpc + * in response to a request. This allows setting the + * request type. + * INPUTS + * request -- previously allocated request struct + * RESULT + * type -- request type [xmlrpc_method_call | xmlrpc_method_response] + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestSetRequestType () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST_TYPE + * SOURCE + */ +XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) { + return request ? request->request_type : xmlrpc_request_none; +} +/*******/ + + +/****f* REQUEST/XMLRPC_RequestSetData + * NAME + * XMLRPC_RequestSetData + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) + * FUNCTION + * Associates a block of xmlrpc data with the request. The + * data is *not* copied. A pointer is kept. The caller + * should be careful not to doubly free the data value, + * which may optionally be free'd by XMLRPC_RequestFree(). + * INPUTS + * request -- previously allocated request struct + * data -- previously allocated data struct + * RESULT + * XMLRPC_VALUE -- pointer to value stored, or NULL + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestGetData () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) { + if(request && data) { + request->io = XMLRPC_CopyValue(data); + return request->io; + } + return NULL; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestGetData + * NAME + * XMLRPC_RequestGetData + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) + * FUNCTION + * Returns data associated with request, if any. + * INPUTS + * request -- previously allocated request struct + * RESULT + * XMLRPC_VALUE -- pointer to value stored, or NULL + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestSetData () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) { + return request ? request->io : NULL; +} +/*******/ + +/****f* REQUEST/XMLRPC_RequestSetOutputOptions + * NAME + * XMLRPC_RequestSetOutputOptions + * SYNOPSIS + * XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output) + * FUNCTION + * Sets output options used for generating XML. The output struct + * is copied, and may be freed by the caller. + * INPUTS + * request -- previously allocated request struct + * output -- output options struct initialized by caller + * RESULT + * XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to value stored, or NULL + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestGetOutputOptions () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST + * XMLRPC_REQUEST_OUTPUT_OPTIONS + * SOURCE + */ +XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output) { + if(request && output) { + memcpy(&request->output, output, sizeof(STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS)); + return &request->output; + } + return NULL; +} +/*******/ + + +/****f* REQUEST/XMLRPC_RequestGetOutputOptions + * NAME + * XMLRPC_RequestGetOutputOptions + * SYNOPSIS + * XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) + * FUNCTION + * Gets a pointer to output options used for generating XML. + * INPUTS + * request -- previously allocated request struct + * RESULT + * XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to options stored, or NULL + * SEE ALSO + * XMLRPC_RequestNew () + * XMLRPC_RequestSetOutputOptions () + * XMLRPC_RequestFree () + * XMLRPC_REQUEST + * XMLRPC_REQUEST_OUTPUT_OPTIONS + * SOURCE + */ +XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) { + return request ? &request->output : NULL; +} +/*******/ + +/*-************************* +* End XMLRPC_REQUEST funcs * +***************************/ + + +/*-*************************** +* Begin Serializiation funcs * +*****************************/ + +/****f* SERIALIZE/XMLRPC_VALUE_ToXML + * NAME + * XMLRPC_VALUE_ToXML + * SYNOPSIS + * char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val) + * FUNCTION + * encode XMLRPC_VALUE into XML buffer. Note that the generated + * buffer will not contain a methodCall. + * INPUTS + * val -- previously allocated XMLRPC_VALUE + * buf_len -- length of returned buffer, if not null + * RESULT + * char* -- newly allocated buffer containing XML. + * It is the caller's responsibility to free it. + * SEE ALSO + * XMLRPC_REQUEST_ToXML () + * XMLRPC_VALUE_FromXML () + * XMLRPC_Free () + * XMLRPC_VALUE + * SOURCE + */ +char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len) { + xml_element *root_elem = XMLRPC_VALUE_to_xml_element(val); + char* pRet = NULL; + + if(root_elem) { + pRet = xml_elem_serialize_to_string(root_elem, NULL, buf_len); + xml_elem_free(root_elem); + } + return pRet; +} +/*******/ + +/****f* SERIALIZE/XMLRPC_REQUEST_ToXML + * NAME + * XMLRPC_REQUEST_ToXML + * SYNOPSIS + * char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request) + * FUNCTION + * encode XMLRPC_REQUEST into XML buffer + * INPUTS + * request -- previously allocated XMLRPC_REQUEST + * buf_len -- size of returned buf, if not null + * RESULT + * char* -- newly allocated buffer containing XML. + * It is the caller's responsibility to free it. + * SEE ALSO + * XMLRPC_REQUEST_ToXML () + * XMLRPC_REQUEST_FromXML () + * XMLRPC_Free () + * XMLRPC_VALUE_ToXML () + * XMLRPC_REQUEST + * SOURCE + */ +char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int* buf_len) { + if(request) { + xml_element *root_elem = (request->output.version == xmlrpc_version_simple) ? + DANDARPC_REQUEST_to_xml_element(request) : + XMLRPC_REQUEST_to_xml_element(request); + char* pRet = NULL; + + if(root_elem) { + pRet = xml_elem_serialize_to_string(root_elem, &request->output.xml_elem_opts, buf_len); + xml_elem_free(root_elem); + } + return pRet; + } +} +/*******/ + +/****f* SERIALIZE/XMLRPC_VALUE_FromXML + * NAME + * XMLRPC_VALUE_FromXML + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int le + * FUNCTION + * Retrieve XMLRPC_VALUE from XML buffer. Note that this will + * ignore any methodCall. See XMLRPC_REQUEST_FromXML + * INPUTS + * in_buf -- character buffer containing XML + * len -- length of buffer + * RESULT + * XMLRPC_VALUE -- newly allocated data, or NULL if error. Should + * be free'd by caller. + * SEE ALSO + * XMLRPC_VALUE_ToXML () + * XMLRPC_REQUEST_FromXML () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) +{ + XMLRPC_VALUE xResponse = NULL; + XMLRPC_REQUEST req = XMLRPC_REQUEST_FromXML(in_buf, len, in_options); + + if(req) { + xResponse = req->io; + XMLRPC_RequestFree(req, 0); + } + return xResponse; +} +/*******/ + +/* map parser errors to standard xml-rpc errors */ +static XMLRPC_VALUE map_expat_errors(XML_ELEM_ERROR error) { + XMLRPC_VALUE xReturn = NULL; + if(error) { + XMLRPC_ERROR_CODE code; + char buf[1024]; + snprintf(buf, sizeof(buf), + "error occurred at line %i, column %i, byte index %i", + error->line, error->column, + error->byte_index); + + /* expat specific errors */ + switch(error->parser_code) { + case XML_ERROR_UNKNOWN_ENCODING: + code = xmlrpc_error_parse_unknown_encoding; + break; + case XML_ERROR_INCORRECT_ENCODING: + code = xmlrpc_error_parse_bad_encoding; + break; + default: + code = xmlrpc_error_parse_xml_syntax; + break; + } + xReturn = XMLRPC_UtilityCreateFault(code, buf); + } + return xReturn; +} + +/****f* SERIALIZE/XMLRPC_REQUEST_FromXML + * NAME + * XMLRPC_REQUEST_FromXML + * SYNOPSIS + * XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int le + * FUNCTION + * Retrieve XMLRPC_REQUEST from XML buffer + * INPUTS + * in_buf -- character buffer containing XML + * len -- length of buffer + * RESULT + * XMLRPC_REQUEST -- newly allocated data, or NULL if error. Should + * be free'd by caller. + * SEE ALSO + * XMLRPC_REQUEST_ToXML () + * XMLRPC_VALUE_FromXML () + * XMLRPC_REQUEST + * SOURCE + */ +XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) +{ + XMLRPC_REQUEST request = XMLRPC_RequestNew(); + STRUCT_XML_ELEM_ERROR error = {0}; + + if(request) { + xml_element *root_elem = xml_elem_parse_buf(in_buf, len, (in_options ? &in_options->xml_elem_opts : NULL), &error); + + if(root_elem) { + if(!strcmp(root_elem->name, "simpleRPC")) { + request->output.version = xmlrpc_version_simple; + xml_element_to_DANDARPC_REQUEST(request, root_elem); + } + else { + request->output.version = xmlrpc_version_1_0; + xml_element_to_XMLRPC_REQUEST(request, root_elem); + } + xml_elem_free(root_elem); + } + else { + if(error.parser_error) { + request->error = map_expat_errors(&error); + } + } + } + + return request; +} +/*******/ + +/*-************************ +* End Serialization Funcs * +**************************/ + + + +/****f* VALUE/XMLRPC_CreateValueEmpty + * NAME + * XMLRPC_CreateValueEmpty + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueEmpty () + * FUNCTION + * Create an XML value to be used/modified elsewhere. + * INPUTS + * RESULT + * XMLRPC_VALUE. The new value, or NULL on failure. + * SEE ALSO + * XMLRPC_CleanupValue () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueEmpty() { + XMLRPC_VALUE v = calloc(1, sizeof(STRUCT_XMLRPC_VALUE)); + if(v) { + v->type = xmlrpc_empty; + simplestring_init(&v->id); + simplestring_init(&v->str); + } + return v; +} + +static const char* get_string(const char* buf, int bDup) { + if(bDup) { + return strdup(buf); + } + return buf; +} +/*******/ + +/****f* VALUE/XMLRPC_SetValueID_Case + * NAME + * XMLRPC_SetValueID_Case + * SYNOPSIS + * const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case) + * FUNCTION + * Assign an ID (key) to an XMLRPC value. + * INPUTS + * value The xml value who's ID we will set. + * id The desired new id. + * len length of id string if known, or 0 if unknown. + * id_case one of XMLRPC_CASE + * RESULT + * const char* pointer to the newly allocated id string, or NULL + * SEE ALSO + * XMLRPC_SetValueID () + * XMLRPC_GetValueID () + * XMLRPC_VALUE + * XMLRPC_CASE + * SOURCE + */ +const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case) { + const char* pRetval = NULL; + if(value) { + if(id) { + simplestring_clear(&value->id); + (len > 0) ? simplestring_addn(&value->id, id, len) : + simplestring_add(&value->id, id); + + /* upper or lower case string in place if required. could be a seperate func. */ + if(id_case == xmlrpc_case_lower || id_case == xmlrpc_case_upper) { + int i; + for(i = 0; i < value->id.len; i++) { + value->id.str[i] = (id_case == xmlrpc_case_lower) ? tolower(value->id.str[i]) : toupper(value->id.str[i]); + } + } + + pRetval = value->id.str; + +#ifdef XMLRPC_DEBUG_REFCOUNT + printf("set value id: %s\n", pRetval); +#endif + } + } + + return pRetval; +} +/*******/ + + +/****f* VALUE/XMLRPC_SetValueString + * NAME + * XMLRPC_SetValueString + * SYNOPSIS + * const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) + * FUNCTION + * Assign a string value to an XMLRPC_VALUE, and set it to type xmlrpc_string + * INPUTS + * value The xml value who's ID we will set. + * val The desired new string val. + * len length of val string if known, or 0 if unknown. + * RESULT + * const char* pointer to the newly allocated value string, or NULL + * SEE ALSO + * XMLRPC_GetValueString () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) { + char *pRetval = NULL; + if(value && val) { + simplestring_clear(&value->str); + (len > 0) ? simplestring_addn(&value->str, val, len) : + simplestring_add(&value->str, val); + value->type = xmlrpc_string; + pRetval = (char *)value->str.str; + } + + return pRetval; +} +/*******/ + +/****f* VALUE/XMLRPC_SetValueInt + * NAME + * XMLRPC_SetValueInt + * SYNOPSIS + * void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) + * FUNCTION + * Assign an int value to an XMLRPC_VALUE, and set it to type xmlrpc_int + * INPUTS + * value The xml value who's ID we will set. + * val The desired new integer value + * RESULT + * SEE ALSO + * XMLRPC_GetValueInt () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) { + if(value) { + value->type = xmlrpc_int; + value->i = val; + } +} +/*******/ + +/****f* VALUE/XMLRPC_SetValueBoolean + * NAME + * XMLRPC_SetValueBoolean + * SYNOPSIS + * void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) + * FUNCTION + * Assign a boolean value to an XMLRPC_VALUE, and set it to type xmlrpc_boolean + * INPUTS + * value The xml value who's value we will set. + * val The desired new boolean value. [0 | 1] + * RESULT + * SEE ALSO + * XMLRPC_GetValueBoolean () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) { + if(value) { + value->type = xmlrpc_boolean; + value->i = val ? 1 : 0; + } +} +/*******/ + + +/****f* VECTOR/XMLRPC_SetIsVector + * NAME + * XMLRPC_SetIsVector + * SYNOPSIS + * int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) + * FUNCTION + * Set the XMLRPC_VALUE to be a vector (list) type. The vector may be one of + * [xmlrpc_array | xmlrpc_struct | xmlrpc_mixed]. An array has only index values. + * A struct has key/val pairs. Mixed allows both index and key/val combinations. + * INPUTS + * value The xml value who's vector type we will set + * type New type of vector as enumerated by XMLRPC_VECTOR_TYPE + * RESULT + * int 1 if successful, 0 otherwise + * SEE ALSO + * XMLRPC_GetValueType () + * XMLRPC_GetVectorType () + * XMLRPC_VALUE + * XMLRPC_VECTOR_TYPE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) { + int bSuccess = 0; + + if(value && value->type != xmlrpc_vector) { + value->v = calloc(1, sizeof(STRUCT_XMLRPC_VECTOR)); + if(value->v) { + value->v->q = (queue*)malloc(sizeof(queue)); + if(value->v->q) { + Q_Init(value->v->q); + value->v->type = type; + value->type = xmlrpc_vector; + bSuccess = 1; + } + } + } + + return bSuccess; +} +/*******/ + +/****f* VECTOR/XMLRPC_CreateVector + * NAME + * XMLRPC_CreateVector + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) + * FUNCTION + * Create a new vector and optionally set an id. + * INPUTS + * id The id of the vector, or NULL + * type New type of vector as enumerated by XMLRPC_VECTOR_TYPE + * RESULT + * XMLRPC_VALUE The new vector, or NULL on failure. + * SEE ALSO + * XMLRPC_CreateValueEmpty () + * XMLRPC_SetIsVector () + * XMLRPC_GetValueType () + * XMLRPC_GetVectorType () + * XMLRPC_VALUE + * XMLRPC_VECTOR_TYPE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) { + XMLRPC_VALUE val = NULL; + + val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_VECTOR *pSIV = NULL; + + if(XMLRPC_SetIsVector(val, type)) { + if(id) { + const char *pSVI = NULL; + + pSVI = XMLRPC_SetValueID(val, id, 0); + if(NULL == pSVI) { + val = NULL; + } + } + } + else { + val = NULL; + } + } + return val; +} +/*******/ + + +/* Not yet implemented. + * + * This should use a hash to determine if a given target id has already + * been appended. + * + * Alternately, it could walk the entire vector, but that could be quite + * slow for very large lists. + */ +static int isDuplicateEntry(XMLRPC_VALUE target, XMLRPC_VALUE source) { + return 0; +} + +/****f* VECTOR/XMLRPC_AddValueToVector + * NAME + * XMLRPC_AddValueToVector + * SYNOPSIS + * int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) + * FUNCTION + * Add (append) an existing XMLRPC_VALUE to a vector. + * INPUTS + * target The target vector + * source The source value to append + * RESULT + * int 1 if successful, else 0 + * SEE ALSO + * XMLRPC_AddValuesToVector () + * XMLRPC_VectorGetValueWithID_Case () + * XMLRPC_VALUE + * NOTES + * The function will fail and return 0 if an attempt is made to add + * a value with an ID into a vector of type xmlrpc_vector_array. Such + * values can only be added to xmlrpc_vector_struct. + * SOURCE + */ +int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) { + if(target && source) { + if(target->type == xmlrpc_vector && target->v && + target->v->q && target->v->type != xmlrpc_vector_none) { + + /* guard against putting value of unknown type into vector */ + switch(source->type) { + case xmlrpc_empty: + case xmlrpc_base64: + case xmlrpc_boolean: + case xmlrpc_datetime: + case xmlrpc_double: + case xmlrpc_int: + case xmlrpc_string: + case xmlrpc_vector: + /* Guard against putting a key/val pair into an array vector */ + if( !(source->id.len && target->v->type == xmlrpc_vector_array) ) { + if(isDuplicateEntry(target, source) || Q_PushTail(target->v->q, XMLRPC_CopyValue(source))) { + return 1; + } + } + else { + fprintf(stderr, "xmlrpc: attempted to add key/val pair to vector of type array\n"); + } + break; + default: + fprintf(stderr, "xmlrpc: attempted to add value of unknown type to vector\n"); + break; + } + } + } + return 0; +} +/*******/ + + +/****f* VECTOR/XMLRPC_AddValuesToVector + * NAME + * XMLRPC_AddValuesToVector + * SYNOPSIS + * XMLRPC_AddValuesToVector ( target, val1, val2, val3, val(n), 0 ) + * XMLRPC_AddValuesToVector( XMLRPC_VALUE, ... ) + * FUNCTION + * Add (append) a series of existing XMLRPC_VALUE to a vector. + * INPUTS + * target The target vector + * ... The source value(s) to append. The last item *must* be 0. + * RESULT + * int 1 if successful, else 0 + * SEE ALSO + * XMLRPC_AddValuesToVector () + * XMLRPC_VectorGetValueWithID_Case () + * XMLRPC_VALUE + * NOTES + * This function may actually return failure after it has already modified + * or added items to target. You can not trust the state of target + * if this function returns failure. + * SOURCE + */ +int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) { + int iRetval = 0; + + if(target) { + if(target->type == xmlrpc_vector) { + XMLRPC_VALUE v = NULL; + va_list vl; + + va_start(vl, target); + + do { + v = va_arg(vl, XMLRPC_VALUE); + if(v) { + if(!XMLRPC_AddValueToVector(target, v)) { + iRetval = 0; + break; + } + } + } while(v); + + va_end(vl); + + if(NULL == v) { + iRetval = 1; + } + } + } + return iRetval; +} +/*******/ + + +/****f* VECTOR/XMLRPC_VectorGetValueWithID_Case + * NAME + * XMLRPC_VectorGetValueWithID_Case + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) + * FUNCTION + * Get value from vector matching id (key) + * INPUTS + * vector The source vector + * id The key to find + * id_case Rule for how to match key + * RESULT + * int 1 if successful, else 0 + * SEE ALSO + * XMLRPC_SetValueID_Case () + * XMLRPC_VALUE + * XMLRPC_CASE_COMPARISON + * SOURCE + */ +XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) { + if(vector && vector->v && vector->v->q) { + q_iter qi = Q_Iter_Head_F(vector->v->q); + + while(qi) { + XMLRPC_VALUE xIter = Q_Iter_Get_F(qi); + if(xIter && xIter->id.str) { + if(id_case == xmlrpc_case_sensitive) { + if(!strcmp(xIter->id.str, id)) { + return xIter; + } + } + else if(id_case == xmlrpc_case_insensitive) { + if(!strcasecmp(xIter->id.str, id)) { + return xIter; + } + } + } + qi = Q_Iter_Next_F(qi); + } + } + return NULL; +} +/*******/ + + +int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value) { + if(vector && vector->v && vector->v->q && value) { + q_iter qi = Q_Iter_Head_F(vector->v->q); + + while(qi) { + XMLRPC_VALUE xIter = Q_Iter_Get_F(qi); + if(xIter == value) { + XMLRPC_CleanupValue(xIter); + Q_Iter_Del(vector->v->q, qi); + return 1; + } + qi = Q_Iter_Next_F(qi); + } + } + return 0; +} + + +/****f* VALUE/XMLRPC_CreateValueString + * NAME + * XMLRPC_CreateValueString + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) + * FUNCTION + * Create an XMLRPC_VALUE, and assign a string to it + * INPUTS + * id The id of the value, or NULL + * val The desired new string val. + * len length of val string if known, or 0 if unknown. + * RESULT + * newly allocated XMLRPC_VALUE, or NULL + * SEE ALSO + * XMLRPC_GetValueString () + * XMLRPC_CreateValueEmpty () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) { + XMLRPC_VALUE value = NULL; + if(val) { + value = XMLRPC_CreateValueEmpty(); + if(value) { + XMLRPC_SetValueString(value, val, len); + if(id) { + XMLRPC_SetValueID(value, id, 0); + } + } + } + return value; +} +/*******/ + +/****f* VALUE/XMLRPC_CreateValueInt + * NAME + * XMLRPC_CreateValueInt + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) + * FUNCTION + * Create an XMLRPC_VALUE, and assign an int to it + * INPUTS + * id The id of the value, or NULL + * i The desired new int val. + * RESULT + * newly allocated XMLRPC_VALUE, or NULL + * SEE ALSO + * XMLRPC_GetValueInt () + * XMLRPC_CreateValueEmpty () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueInt(val, i); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + +/****f* VALUE/XMLRPC_CreateValueBoolean + * NAME + * XMLRPC_CreateValueBoolean + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) + * FUNCTION + * Create an XMLRPC_VALUE, and assign an int to it + * INPUTS + * id The id of the value, or NULL + * i The desired new int val. + * RESULT + * newly allocated XMLRPC_VALUE, or NULL + * SEE ALSO + * XMLRPC_GetValueBoolean () + * XMLRPC_CreateValueEmpty () + * XMLRPC_VALUE + * XMLRPC_VALUE_TYPE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueBoolean(val, i); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + + +/****f* VALUE/XMLRPC_CleanupValue + * NAME + * XMLRPC_CleanupValue + * SYNOPSIS + * void XMLRPC_CleanupValue(XMLRPC_VALUE value) + * FUNCTION + * Frees all memory allocated for an XMLRPC_VALUE and any of its children (if a vector) + * INPUTS + * value The id of the value to be cleaned up. + * RESULT + * void + * NOTES + * Normally this function will be called for the topmost vector, thus free-ing + * all children. If a child of a vector is free'd first, results are undefined. + * Failure to call this function *will* cause memory leaks. + * + * Also, this function is implemented using reference counting. Thus a value + * may be added and freed from multiple parents so long as a reference is added + * first using XMLRPC_CopyValue() + * SEE ALSO + * XMLRPC_RequestFree () + * XMLRPC_CreateValueEmpty () + * XMLRPC_CopyValue() + * XMLRPC_VALUE + * SOURCE + */ +void XMLRPC_CleanupValue(XMLRPC_VALUE value) { + if(value) { + if(value->iRefCount > 0) { + value->iRefCount --; + } + +#ifdef XMLRPC_DEBUG_REFCOUNT + if(value->id.str) { + printf("decremented refcount of %s, now %i\n", value->id.str, value->iRefCount); + } + else { + printf("decremented refcount of 0x%x, now %i\n", value, value->iRefCount); + } +#endif + + if(value->type == xmlrpc_vector) { + if(value->v) { + if(value->iRefCount == 0) { + XMLRPC_VALUE cur = (XMLRPC_VALUE)Q_Head(value->v->q); + while( cur ) { + XMLRPC_CleanupValue(cur); + + /* Make sure some idiot didn't include a vector as a child of itself + * and thus it would have already free'd these. + */ + if(value->v && value->v->q) { + cur = Q_Next(value->v->q); + } + else { + break; + } + } + + Q_Destroy(value->v->q); + my_free(value->v->q); + my_free(value->v); + } + } + } + + + if(value->iRefCount == 0) { + + /* guard against freeing invalid types */ + switch(value->type) { + case xmlrpc_empty: + case xmlrpc_base64: + case xmlrpc_boolean: + case xmlrpc_datetime: + case xmlrpc_double: + case xmlrpc_int: + case xmlrpc_string: + case xmlrpc_vector: +#ifdef XMLRPC_DEBUG_REFCOUNT + if(value->id.str) { + printf("free'd %s\n", value->id.str); + } + else { + printf("free'd 0x%x\n", value); + } +#endif + simplestring_free(&value->id); + simplestring_free(&value->str); + + memset(value, 0, sizeof(STRUCT_XMLRPC_VALUE)); + my_free(value); + break; + default: + fprintf(stderr, "xmlrpc: attempted to free value of invalid type\n"); + break; + } + } + } +} +/*******/ + + +/****f* VALUE/XMLRPC_SetValueDateTime + * NAME + * XMLRPC_SetValueDateTime + * SYNOPSIS + * void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) + * FUNCTION + * Assign time value to XMLRPC_VALUE + * INPUTS + * value The target XMLRPC_VALUE + * time The desired new unix time value (time_t) + * RESULT + * void + * SEE ALSO + * XMLRPC_GetValueDateTime () + * XMLRPC_SetValueDateTime_ISO8601 () + * XMLRPC_CreateValueDateTime () + * XMLRPC_VALUE + * SOURCE + */ +void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) { + if(value) { + char timeBuf[30]; + value->type = xmlrpc_datetime; + value->i = time; + + timeBuf[0] = 0; + + date_to_ISO8601(time, timeBuf, sizeof(timeBuf)); + + if(timeBuf[0]) { + simplestring_clear(&value->str); + simplestring_add(&value->str, timeBuf); + } + } +} +/*******/ + +/****f* VALUE/XMLRPC_CopyValue + * NAME + * XMLRPC_CopyValue + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) + * FUNCTION + * Make a copy of an XMLRPC_VALUE + * INPUTS + * value The target XMLRPC_VALUE + * RESULT + * XMLRPC_VALUE -- address of the copy + * SEE ALSO + * XMLRPC_CleanupValue () + * NOTES + * This function is implemented via reference counting, so the + * returned value is going to be the same as the passed in value. + * The value must be freed the same number of times it is copied + * or there will be a memory leak. + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) { + if(value) { + value->iRefCount ++; +#ifdef XMLRPC_DEBUG_REFCOUNT + if(value->id.str) { + printf("incremented refcount of %s\n", value->id.str); + } +#endif + } + return value; +} +/*******/ + + + +/****f* VALUE/XMLRPC_CreateValueDateTime + * NAME + * XMLRPC_CreateValueDateTime + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) + * FUNCTION + * Create new datetime value from time_t + * INPUTS + * id id of the new value, or NULL + * time The desired unix time value (time_t) + * RESULT + * void + * SEE ALSO + * XMLRPC_GetValueDateTime () + * XMLRPC_SetValueDateTime () + * XMLRPC_CreateValueDateTime_ISO8601 () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueDateTime(val, time); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + + +/****f* VALUE/XMLRPC_SetValueDateTime_ISO8601 + * NAME + * XMLRPC_SetValueDateTime_ISO8601 + * SYNOPSIS + * void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) + * FUNCTION + * Set datetime value from IS08601 encoded string + * INPUTS + * value The target XMLRPC_VALUE + * s The desired new time value + * RESULT + * void + * BUGS + * This function currently attempts to convert the time string to a valid unix time + * value before passing it. Behavior when the string is invalid or out of range + * is not well defined, but will probably result in Jan 1, 1970 (0) being passed. + * SEE ALSO + * XMLRPC_GetValueDateTime_ISO8601 () + * XMLRPC_CreateValueDateTime_ISO8601 () + * XMLRPC_CreateValueDateTime () + * XMLRPC_VALUE + * SOURCE + */ +void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) { + if(value) { + time_t time_val = 0; + if(s) { + date_from_ISO8601(s, &time_val); + XMLRPC_SetValueDateTime(value, time_val); + } + } +} +/*******/ + +/****f* VALUE/XMLRPC_CreateValueDateTime_ISO8601 + * NAME + * XMLRPC_CreateValueDateTime_ISO8601 + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) + * FUNCTION + * Create datetime value from IS08601 encoded string + * INPUTS + * id The id of the new value, or NULL + * s The desired new time value + * RESULT + * newly allocated XMLRPC_VALUE, or NULL if no value created. + * BUGS + * See XMLRPC_SetValueDateTime_ISO8601 () + * SEE ALSO + * XMLRPC_GetValueDateTime_ISO8601 () + * XMLRPC_SetValueDateTime_ISO8601 () + * XMLRPC_CreateValueDateTime () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueDateTime_ISO8601(val, s); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + + +/****f* VALUE/XMLRPC_SetValueBase64 + * NAME + * XMLRPC_SetValueBase64 + * SYNOPSIS + * void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) + * FUNCTION + * Set base64 value. Base64 is useful for transferring binary data, such as an image. + * INPUTS + * value The target XMLRPC_VALUE + * s The desired new binary value + * len The length of s, or NULL. If buffer is not null terminated, len *must* be passed. + * RESULT + * void + * NOTES + * Data is set/stored/retrieved as passed in, but is base64 encoded for XML transfer, and + * decoded on the other side. This is transparent to the caller. + * SEE ALSO + * XMLRPC_GetValueBase64 () + * XMLRPC_CreateValueBase64 () + * XMLRPC_VALUE + * SOURCE + */ +void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) { + if(value && s) { + simplestring_clear(&value->str); + (len > 0) ? simplestring_addn(&value->str, s, len) : + simplestring_add(&value->str, s); + value->type = xmlrpc_base64; + } +} +/*******/ + + +/****f* VALUE/XMLRPC_CreateValueBase64 + * NAME + * XMLRPC_CreateValueBase64 + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) + * FUNCTION + * Create base64 value. Base64 is useful for transferring binary data, such as an image. + * INPUTS + * id id of the new value, or NULL + * s The desired new binary value + * len The length of s, or NULL. If buffer is not null terminated, len *must* be passed. + * RESULT + * newly allocated XMLRPC_VALUE, or NULL if error + * NOTES + * See XMLRPC_SetValueBase64 () + * SEE ALSO + * XMLRPC_GetValueBase64 () + * XMLRPC_SetValueBase64 () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueBase64(val, s, len); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + +/****f* VALUE/XMLRPC_SetValueDouble + * NAME + * XMLRPC_SetValueDouble + * SYNOPSIS + * void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) + * FUNCTION + * Set double (floating point) value. + * INPUTS + * value The target XMLRPC_VALUE + * val The desired new double value + * RESULT + * void + * SEE ALSO + * XMLRPC_GetValueDouble () + * XMLRPC_CreateValueDouble () + * XMLRPC_VALUE + * SOURCE + */ +void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) { + if(value) { + value->type = xmlrpc_double; + value->d = val; + } +} +/*******/ + +/****f* VALUE/XMLRPC_CreateValueDouble + * NAME + * XMLRPC_CreateValueDouble + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) + * FUNCTION + * Create double (floating point) value. + * INPUTS + * id id of the newly created value, or NULL + * d The desired new double value + * RESULT + * void + * SEE ALSO + * XMLRPC_GetValueDouble () + * XMLRPC_CreateValueDouble () + * XMLRPC_VALUE + * SOURCE + */ +XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) { + XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); + if(val) { + XMLRPC_SetValueDouble(val, d); + if(id) { + XMLRPC_SetValueID(val, id, 0); + } + } + return val; +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueString + * NAME + * XMLRPC_GetValueString + * SYNOPSIS + * const char* XMLRPC_GetValueString(XMLRPC_VALUE value) + * FUNCTION + * retrieve string value + * INPUTS + * value source XMLRPC_VALUE of type xmlrpc_string + * RESULT + * void + * SEE ALSO + * XMLRPC_SetValueString () + * XMLRPC_GetValueType () + * XMLRPC_VALUE + * SOURCE + */ +const char* XMLRPC_GetValueString(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_string) ? value->str.str : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueStringLen + * NAME + * XMLRPC_GetValueStringLen + * SYNOPSIS + * int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) + * FUNCTION + * determine length of string value + * INPUTS + * value XMLRPC_VALUE of type xmlrpc_string + * RESULT + * length of string, or 0 + * NOTES + * SEE ALSO + * XMLRPC_SetValueString () + * XMLRPC_GetValueString () + * SOURCE + */ +int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) { + return ((value) ? value->str.len : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueInt + * NAME + * XMLRPC_GetValueInt + * SYNOPSIS + * int XMLRPC_GetValueInt(XMLRPC_VALUE value) + * FUNCTION + * retrieve integer value. + * INPUTS + * value XMLRPC_VALUE of type xmlrpc_int + * RESULT + * integer value or 0 if value is not valid int + * NOTES + * use XMLRPC_GetValueType () to be sure if 0 is real return value or not + * SEE ALSO + * XMLRPC_SetValueInt () + * XMLRPC_CreateValueInt () + * SOURCE + */ +int XMLRPC_GetValueInt(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_int) ? value->i : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueBoolean + * NAME + * XMLRPC_GetValueBoolean + * SYNOPSIS + * int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) + * FUNCTION + * retrieve boolean value. + * INPUTS + * XMLRPC_VALUE of type xmlrpc_boolean + * RESULT + * boolean value or 0 if value is not valid boolean + * NOTES + * use XMLRPC_GetValueType() to be sure if 0 is real value or not + * SEE ALSO + * XMLRPC_SetValueBoolean () + * XMLRPC_CreateValueBoolean () + * SOURCE + */ +int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_boolean) ? value->i : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueDouble + * NAME + * XMLRPC_GetValueDouble + * SYNOPSIS + * double XMLRPC_GetValueDouble(XMLRPC_VALUE value) + * FUNCTION + * retrieve double value + * INPUTS + * XMLRPC_VALUE of type xmlrpc_double + * RESULT + * double value or 0 if value is not valid double. + * NOTES + * use XMLRPC_GetValueType() to be sure if 0 is real value or not + * SEE ALSO + * XMLRPC_SetValueDouble () + * XMLRPC_CreateValueDouble () + * SOURCE + */ +double XMLRPC_GetValueDouble(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_double) ? value->d : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueBase64 + * NAME + * XMLRPC_GetValueBase64 + * SYNOPSIS + * const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) + * FUNCTION + * retrieve binary value + * INPUTS + * XMLRPC_VALUE of type xmlrpc_base64 + * RESULT + * pointer to binary value or 0 if value is not valid. + * SEE ALSO + * XMLRPC_SetValueBase64 () + * XMLRPC_CreateValueBase64 () + * NOTES + * Call XMLRPC_GetValueStringLen() to retrieve real length of binary data. strlen() + * will not be accurate, as returned data may contain embedded nulls. + * SOURCE + */ +const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_base64) ? value->str.str : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueDateTime + * NAME + * XMLRPC_GetValueDateTime + * SYNOPSIS + * time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) + * FUNCTION + * retrieve time_t value + * INPUTS + * XMLRPC_VALUE of type xmlrpc_datetime + * RESULT + * time_t value or 0 if value is not valid datetime. + * NOTES + * use XMLRPC_GetValueType() to be sure if 0 is real value or not + * SEE ALSO + * XMLRPC_SetValueDateTime () + * XMLRPC_GetValueDateTime_ISO8601 () + * XMLRPC_CreateValueDateTime () + * SOURCE + */ +time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) { + return (time_t)((value && value->type == xmlrpc_datetime) ? value->i : 0); +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueDateTime_IOS8601 + * NAME + * XMLRPC_GetValueDateTime_IOS8601 + * SYNOPSIS + * const char* XMLRPC_GetValueDateTime_IOS8601(XMLRPC_VALUE value) + * FUNCTION + * retrieve ISO8601 formatted time value + * INPUTS + * XMLRPC_VALUE of type xmlrpc_datetime + * RESULT + * const char* value or 0 if value is not valid datetime. + * SEE ALSO + * XMLRPC_SetValueDateTime_IOS8601 () + * XMLRPC_GetValueDateTime () + * XMLRPC_CreateValueDateTime_IOS8601 () + * SOURCE + */ +const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value) { + return ((value && value->type == xmlrpc_datetime) ? value->str.str : 0); +} +/*******/ + +/* Get ID (key) of value or NULL */ +/****f* VALUE/XMLRPC_GetValueID + * NAME + * XMLRPC_GetValueID + * SYNOPSIS + * const char* XMLRPC_GetValueID(XMLRPC_VALUE value) + * FUNCTION + * retrieve id (key) of value + * INPUTS + * XMLRPC_VALUE of any type + * RESULT + * const char* pointer to id of value, or NULL + * NOTES + * SEE ALSO + * XMLRPC_SetValueID() + * XMLRPC_CreateValueEmpty() + * SOURCE + */ +const char* XMLRPC_GetValueID(XMLRPC_VALUE value) { + return (const char*)((value && value->id.len) ? value->id.str : 0); +} +/*******/ + + +/****f* VECTOR/XMLRPC_VectorSize + * NAME + * XMLRPC_VectorSize + * SYNOPSIS + * int XMLRPC_VectorSize(XMLRPC_VALUE value) + * FUNCTION + * retrieve size of vector + * INPUTS + * XMLRPC_VALUE of type xmlrpc_vector + * RESULT + * count of items in vector + * NOTES + * This is a cheap operation even on large vectors. Vector size is + * maintained by queue during add/remove ops. + * SEE ALSO + * XMLRPC_AddValueToVector () + * SOURCE + */ +int XMLRPC_VectorSize(XMLRPC_VALUE value) { + int size = 0; + if(value && value->type == xmlrpc_vector && value->v) { + size = Q_Size(value->v->q); + } + return size; +} +/*******/ + +/****f* VECTOR/XMLRPC_VectorRewind + * NAME + * XMLRPC_VectorRewind + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) + * FUNCTION + * reset vector to first item + * INPUTS + * XMLRPC_VALUE of type xmlrpc_vector + * RESULT + * first XMLRPC_VALUE in list, or NULL if empty or error. + * NOTES + * Be careful to rewind any vector passed in to you if you expect to + * iterate through the entire list. + * SEE ALSO + * XMLRPC_VectorNext () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) { + XMLRPC_VALUE xReturn = NULL; + if(value && value->type == xmlrpc_vector && value->v) { + xReturn = (XMLRPC_VALUE)Q_Head(value->v->q); + } + return xReturn; +} +/*******/ + +/****f* VECTOR/XMLRPC_VectorNext + * NAME + * XMLRPC_VectorNext + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) + * FUNCTION + * Iterate vector to next item in list. + * INPUTS + * XMLRPC_VALUE of type xmlrpc_vector + * RESULT + * Next XMLRPC_VALUE in vector, or NULL if at end. + * NOTES + * SEE ALSO + * XMLRPC_VectorRewind () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) { + XMLRPC_VALUE xReturn = NULL; + if(value && value->type == xmlrpc_vector && value->v) { + xReturn = (XMLRPC_VALUE)Q_Next(value->v->q); + } + return xReturn; +} +/*******/ + +/****f* VALUE/XMLRPC_GetValueType + * NAME + * XMLRPC_GetValueType + * SYNOPSIS + * XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) + * FUNCTION + * determine data type of the XMLRPC_VALUE + * INPUTS + * XMLRPC_VALUE target of query + * RESULT + * data type of value as enumerated by XMLRPC_VALUE_TYPE + * NOTES + * all values are of type xmlrpc_empty until set. + * SEE ALSO + * XMLRPC_SetValue* + * XMLRPC_CreateValue* + * XMLRPC_Append* + * SOURCE + */ +XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) { + return value ? value->type : xmlrpc_empty; +} +/*******/ + +/* Vector type accessor */ +/****f* VALUE/XMLRPC_GetVectorType + * NAME + * XMLRPC_GetVectorType + * SYNOPSIS + * XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) + * FUNCTION + * determine vector type of the XMLRPC_VALUE + * INPUTS + * XMLRPC_VALUE of type xmlrpc_vector + * RESULT + * vector type of value as enumerated by XMLRPC_VECTOR_TYPE + * NOTES + * xmlrpc_none is returned if value is not a vector + * SEE ALSO + * XMLRPC_SetIsVector () + * XMLRPC_GetValueType () + * SOURCE + */ +XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) { + return(value && value->v) ? value->v->type : xmlrpc_none; +} +/*******/ + + +/*-******************* +* Begin Server Funcs * +*********************/ + + +/****f* VALUE/XMLRPC_ServerCreate + * NAME + * XMLRPC_ServerCreate + * SYNOPSIS + * XMLRPC_SERVER XMLRPC_ServerCreate() + * FUNCTION + * Allocate/Init XMLRPC Server Resources. + * INPUTS + * none + * RESULT + * newly allocated XMLRPC_SERVER + * NOTES + * SEE ALSO + * XMLRPC_ServerDestroy () + * XMLRPC_GetGlobalServer () + * SOURCE + */ +XMLRPC_SERVER XMLRPC_ServerCreate() { + XMLRPC_SERVER server = calloc(1, sizeof(STRUCT_XMLRPC_SERVER)); + if(server) { + Q_Init(&server->methodlist); + Q_Init(&server->docslist); + + /* register system methods */ + xsm_register(server); + } + return server; +} +/*******/ + +/* Return global server. Not locking! Not Thread Safe! */ +/****f* VALUE/XMLRPC_GetGlobalServer + * NAME + * XMLRPC_GetGlobalServer + * SYNOPSIS + * XMLRPC_SERVER XMLRPC_GetGlobalServer() + * FUNCTION + * Allocates a global (process-wide) server, or returns pointer if pre-existing. + * INPUTS + * none + * RESULT + * pointer to global server, or 0 if error. + * NOTES + * ***WARNING*** This function is not thread safe. It is included only for the very lazy. + * Multi-threaded programs that use this may experience problems. + * BUGS + * There is currently no way to cleanup the global server gracefully. + * SEE ALSO + * XMLRPC_ServerCreate () + * SOURCE + */ +XMLRPC_SERVER XMLRPC_GetGlobalServer() { + static XMLRPC_SERVER xsServer = 0; + if(!xsServer) { + xsServer = XMLRPC_ServerCreate(); + } + return xsServer; +} +/*******/ + +/****f* VALUE/XMLRPC_ServerDestroy + * NAME + * XMLRPC_ServerDestroy + * SYNOPSIS + * void XMLRPC_ServerDestroy(XMLRPC_SERVER server) + * FUNCTION + * Free Server Resources + * INPUTS + * server The server to be free'd + * RESULT + * void + * NOTES + * This frees the server struct and any methods that have been added. + * SEE ALSO + * XMLRPC_ServerCreate () + * SOURCE + */ +void XMLRPC_ServerDestroy(XMLRPC_SERVER server) { + if(server) { + doc_method* dm = Q_Head(&server->docslist); + server_method* sm = Q_Head(&server->methodlist); + while( dm ) { + my_free(dm); + dm = Q_Next(&server->docslist); + } + while( sm ) { + if(sm->name) { + my_free(sm->name); + } + if(sm->desc) { + XMLRPC_CleanupValue(sm->desc); + } + my_free(sm); + sm = Q_Next(&server->methodlist); + } + if(server->xIntrospection) { + XMLRPC_CleanupValue(server->xIntrospection); + } + + Q_Destroy(&server->methodlist); + Q_Destroy(&server->docslist); + my_free(server); + } +} +/*******/ + + +/****f* VALUE/XMLRPC_ServerRegisterMethod + * NAME + * XMLRPC_ServerRegisterMethod + * SYNOPSIS + * void XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb) + * FUNCTION + * Register new XMLRPC method with server + * INPUTS + * server The XMLRPC_SERVER to register the method with + * name public name of the method + * cb C function that implements the method + * RESULT + * int - 1 if success, else 0 + * NOTES + * A C function must be registered for every "method" that the server recognizes. The + * method name is equivalent to <methodCall><name> method name </name></methodCall> in the + * XML syntax. + * SEE ALSO + * XMLRPC_ServerFindMethod () + * XMLRPC_ServerCallMethod () + * SOURCE + */ +int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb) { + if(server && name && cb) { + + server_method* sm = malloc(sizeof(server_method)); + + if(sm) { + sm->name = strdup(name); + sm->method = cb; + sm->desc = NULL; + + return Q_PushTail(&server->methodlist, sm); + } + } + return 0; +} +/*******/ + +inline server_method* find_method(XMLRPC_SERVER server, const char* name) { + server_method* sm; + + q_iter qi = Q_Iter_Head_F(&server->methodlist); + + while( qi ) { + sm = Q_Iter_Get_F(qi); + if(sm && !strcmp(sm->name, name)) { + return sm; + } + qi = Q_Iter_Next_F(qi); + } + return NULL; +} + + +const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) { + switch(type) { + case xmlrpc_none: + return "none"; + case xmlrpc_empty: + return "empty"; + case xmlrpc_base64: + return "base64"; + case xmlrpc_boolean: + return "boolean"; + case xmlrpc_datetime: + return "datetime"; + case xmlrpc_double: + return "double"; + case xmlrpc_int: + return "int"; + case xmlrpc_string: + return "string"; + case xmlrpc_vector: + switch(vtype) { + case xmlrpc_vector_none: + return "none"; + case xmlrpc_vector_array: + return "array"; + case xmlrpc_vector_mixed: + return "mixed vector (struct)"; + case xmlrpc_vector_struct: + return "struct"; + } + } +} + +/****f* VALUE/XMLRPC_ServerFindMethod + * NAME + * XMLRPC_ServerFindMethod + * SYNOPSIS + * XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName) + * FUNCTION + * retrieve C callback associated with a given method name. + * INPUTS + * server The XMLRPC_SERVER the method is registered with + * callName the method to find + * RESULT + * previously registered XMLRPC_Callback, or NULL + * NOTES + * Typically, this is used to determine if a requested method exists, without actually calling it. + * SEE ALSO + * XMLRPC_ServerCallMethod () + * XMLRPC_ServerRegisterMethod () + * SOURCE + */ +XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName) { + if(server && callName) { + q_iter qi = Q_Iter_Head_F(&server->methodlist); + while( qi ) { + server_method* sm = Q_Iter_Get_F(qi); + if(sm && !strcmp(sm->name, callName)) { + return sm->method; + } + qi = Q_Iter_Next_F(qi); + } + } + return NULL; +} +/*******/ + + +/* Call method specified in request */ +/****f* VALUE/XMLRPC_ServerCallMethod + * NAME + * XMLRPC_ServerCallMethod + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData) + * FUNCTION + * + * INPUTS + * server The XMLRPC_SERVER the method is registered with + * request the request to handle + * userData any additional data to pass to the C callback, or NULL + * RESULT + * XMLRPC_VALUE allocated by the callback, or NULL + * NOTES + * It is typically the caller's responsibility to free the returned value. + * + * Often the caller will want to serialize the result as XML, via + * XMLRPC_VALUE_To_XML () or XMLRPC_REQUEST_To_XML () + * SEE ALSO + * XMLRPC_ServerFindMethod () + * XMLRPC_ServerRegisterMethod () + * XMLRPC_CleanupValue () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData) { + XMLRPC_VALUE xReturn = NULL; + + /* check for error set during request parsing / generation */ + if(request && request->error) { + xReturn = XMLRPC_CopyValue(request->error); + } + else if(server && request && request->methodName.str) { + XMLRPC_Callback cb = XMLRPC_ServerFindMethod(server, request->methodName.str); + if(cb) { + xReturn = cb(server, request, userData); + } + else { + xReturn = XMLRPC_UtilityCreateFault(xmlrpc_error_unknown_method, request->methodName.str); + } + } + return xReturn; +} +/*******/ + +/*-***************** +* End server funcs * +*******************/ + + +/*-*********************************** +* Begin XMLRPC General Options funcs * +*************************************/ + +/* For options used by XMLRPC_VALUE funcs that otherwise do not have + * parameters for options. Kind of gross. :( + */ +typedef struct _xmlrpc_options { + XMLRPC_CASE id_case; + XMLRPC_CASE_COMPARISON id_case_compare; +} STRUCT_XMLRPC_OPTIONS, *XMLRPC_OPTIONS; + +static XMLRPC_OPTIONS XMLRPC_GetDefaultOptions() { + static STRUCT_XMLRPC_OPTIONS options = { + xmlrpc_case_exact, + xmlrpc_case_sensitive + }; + return &options; +} + +/****f* VALUE/XMLRPC_GetDefaultIdCase + * NAME + * XMLRPC_GetDefaultIdCase + * SYNOPSIS + * XMLRPC_CASE XMLRPC_GetDefaultIdCase() + * FUNCTION + * Gets default case options used by XMLRPC_VALUE funcs + * INPUTS + * none + * RESULT + * XMLRPC_CASE + * BUGS + * Nasty and gross. Should be server specific, but that requires changing all + * the XMLRPC_VALUE api's. + * SEE ALSO + * XMLRPC_SetDefaultIdCase () + * SOURCE + */ +XMLRPC_CASE XMLRPC_GetDefaultIdCase() { + XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); + return options->id_case; +} +/*******/ + +/****f* VALUE/XMLRPC_SetDefaultIdCase + * NAME + * XMLRPC_SetDefaultIdCase + * SYNOPSIS + * XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) + * FUNCTION + * Sets default case options used by XMLRPC_VALUE funcs + * INPUTS + * id_case case options as enumerated by XMLRPC_CASE + * RESULT + * XMLRPC_CASE -- newly set option + * BUGS + * Nasty and gross. Should be server specific, but that requires changing all + * the XMLRPC_VALUE api's. + * SEE ALSO + * XMLRPC_GetDefaultIdCase () + * SOURCE + */ +XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) { + XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); + options->id_case = id_case; + return options->id_case; +} +/*******/ + +/****f* VALUE/XMLRPC_GetDefaultIdCaseComparison + * NAME + * XMLRPC_GetDefaultIdCaseComparison + * SYNOPSIS + * XMLRPC_CASE XMLRPC_GetDefaultIdCaseComparison( ) + * FUNCTION + * Gets default case comparison options used by XMLRPC_VALUE funcs + * INPUTS + * none + * RESULT + * XMLRPC_CASE_COMPARISON default + * BUGS + * Nasty and gross. Should be server specific, but that requires changing all + * the XMLRPC_VALUE api's. + * SEE ALSO + * XMLRPC_SetDefaultIdCaseComparison () + * SOURCE + */ +XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison() { + XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); + return options->id_case_compare; +} +/*******/ + +/****f* VALUE/XMLRPC_SetDefaultIdCaseComparison + * NAME + * XMLRPC_SetDefaultIdCaseComparison + * SYNOPSIS + * XMLRPC_CASE XMLRPC_SetDefaultIdCaseComparison( XMLRPC_CASE_COMPARISON id_case_compare ) + * FUNCTION + * Gets default case comparison options used by XMLRPC_VALUE funcs + * INPUTS + * id_case_compare case comparison rule to set as default + * RESULT + * XMLRPC_CASE_COMPARISON newly set default + * BUGS + * Nasty and gross. Should be server specific, but that requires changing all + * the XMLRPC_VALUE api's. + * SEE ALSO + * XMLRPC_GetDefaultIdCaseComparison () + * SOURCE + */ +XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case_compare) { + XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); + options->id_case_compare = id_case_compare; + return options->id_case_compare; +} +/*******/ + +/*-********************************* +* End XMLRPC General Options funcs * +***********************************/ + + +/*-****************** +* Utility API funcs * +********************/ + +/****f* UTILITY/XMLRPC_UtilityCreateFault + * NAME + * XMLRPC_UtilityCreateFault + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_UtilityCreateFault( int fault_code, const char* fault_string ) + * FUNCTION + * generates a struct containing a string member with id "faultString" and an int member + * with id "faultCode". When using the xmlrpc xml serialization, these will be translated + * to <fault><value><struct>... format. + * INPUTS + * fault_code application specific error code. can be 0. + * fault_string application specific error string. cannot be null. + * RESULT + * XMLRPC_VALUE a newly created struct vector representing the error, or null on error. + * NOTES + * This is a utility function. xmlrpc "faults" are not directly represented in this xmlrpc + * API or data structures. It is the author's view, that this API is intended for simple + * data types, and a "fault" is a complex data type consisting of multiple simple data + * types. This function is provided for convenience only, the same result could be + * achieved directly by the application. + * + * This function now supports some "standardized" fault codes, as specified at. + * http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php. + * If one of these fault codes is received, the description string will automatically + * be prefixed with a standard error string and 2 newlines. + * + * The actual transformation between this complex type and the xml "<fault>" element takes + * place in the xmlrpc to xml serialization layer. This step is not performed when using the + * simplerpc serialization, meaning that there will be no "<fault>" element in that + * serialization. There will simply be a standard struct with 2 child elements. + * imho, the "<fault>" element is unnecessary and/or out of place as part of the standard API. + * SOURCE + */ +XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) { + XMLRPC_VALUE xOutput = NULL; + + char* string = NULL; + simplestring description; + simplestring_init(&description); + + switch (fault_code) { + case xmlrpc_error_parse_xml_syntax: string = xmlrpc_error_parse_xml_syntax_str; break; + case xmlrpc_error_parse_unknown_encoding: string = xmlrpc_error_parse_unknown_encoding_str; break; + case xmlrpc_error_parse_bad_encoding: string = xmlrpc_error_parse_bad_encoding_str; break; + case xmlrpc_error_invalid_xmlrpc: string = xmlrpc_error_invalid_xmlrpc_str; break; + case xmlrpc_error_unknown_method: string = xmlrpc_error_unknown_method_str; break; + case xmlrpc_error_invalid_params: string = xmlrpc_error_invalid_params_str; break; + case xmlrpc_error_internal_server: string = xmlrpc_error_internal_server_str; break; + case xmlrpc_error_application: string = xmlrpc_error_application_str; break; + case xmlrpc_error_system: string = xmlrpc_error_system_str; break; + case xmlrpc_error_transport: string = xmlrpc_error_transport_str; break; + } + + simplestring_add(&description, string); + + if(string && fault_string) { + simplestring_add(&description, "\n\n"); + } + simplestring_add(&description, fault_string); + + + if(description.len) { + xOutput = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + + XMLRPC_VectorAppendString(xOutput, "faultString", description.str, description.len); + XMLRPC_VectorAppendInt(xOutput, "faultCode", fault_code); + } + + simplestring_free(&description); + + return xOutput; +} +/*******/ + + +/****f* UTILITY/XMLRPC_Free + * NAME + * XMLRPC_Free + * SYNOPSIS + * void XMLRPC_Free(void* mem) + * FUNCTION + * frees a block of memory allocated by xmlrpc. + * INPUTS + * mem memory to free + * RESULT + * void + * NOTES + * Useful for OS's where memory must be free'd + * in the same library in which it is allocated. + * SOURCE + */ +void XMLRPC_Free(void* mem) { + my_free(mem); +} +/*******/ + + +/****f* UTILITY/XMLRPC_GetVersionString + * NAME + * XMLRPC_GetVersionString + * SYNOPSIS + * const char* XMLRPC_GetVersionString() + * FUNCTION + * returns library version string + * INPUTS + * + * RESULT + * const char* + * NOTES + * SOURCE + */ +const char* XMLRPC_GetVersionString() { + return XMLRPC_VERSION_STR; +} +/*******/ + + +/*-********************** +* End Utility API funcs * +************************/ + + + + + diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc.h b/ext/xmlrpc/libxmlrpc/xmlrpc.h new file mode 100644 index 0000000000..2cfe7cb2d9 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xmlrpc.h @@ -0,0 +1,402 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +#ifndef XMLRPC_ALREADY_INCLUDED +#define XMLRPC_ALREADY_INCLUDED 1 + +/* includes */ +#include "xml_element.h" +#include <time.h> /* for time_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* this number, representing the date, must be increased each time the API changes */ +#define XMLRPC_API_NO 20010721 + +/* this string should be changed with each packaged release */ +#define XMLRPC_VERSION_STR "xmlrpc-epi v. " VERSION + +/* where to find more info. shouldn't need to change much */ +#define XMLRPC_HOME_PAGE_STR "http://xmlprc-epi.sourceforge.net/" + + +/****d* VALUE/XMLRPC_VALUE_TYPE + * NAME + * XMLRPC_VALUE_TYPE + * NOTES + * Defines data types for XMLRPC_VALUE + * SEE ALSO + * XMLRPC_VECTOR_TYPE + * XMLRPC_REQUEST_TYPE + * SOURCE + */ +typedef enum _XMLRPC_VALUE_TYPE { + xmlrpc_none, /* not a value */ + xmlrpc_empty, /* empty value, eg NULL */ + xmlrpc_base64, /* base64 value, eg binary data */ + xmlrpc_boolean, /* boolean [0 | 1] */ + xmlrpc_datetime, /* datetime [ISO8601 | time_t] */ + xmlrpc_double, /* double / floating point */ + xmlrpc_int, /* integer */ + xmlrpc_string, /* string */ + xmlrpc_vector /* vector, aka list, array */ +} XMLRPC_VALUE_TYPE; +/*******/ + +/****d* VALUE/XMLRPC_VECTOR_TYPE + * NAME + * XMLRPC_VECTOR_TYPE + * NOTES + * Defines data types for XMLRPC_VECTOR + * SEE ALSO + * XMLRPC_VALUE_TYPE + * XMLRPC_REQUEST_TYPE + * SOURCE + */ +typedef enum _XMLRPC_VECTOR_TYPE { + xmlrpc_vector_none, /* not an array */ + xmlrpc_vector_array, /* no values may have key names */ + xmlrpc_vector_mixed, /* some values may have key names */ + xmlrpc_vector_struct /* all values must have key names */ +} XMLRPC_VECTOR_TYPE; +/*******/ + +/****d* VALUE/XMLRPC_REQUEST_TYPE + * NAME + * XMLRPC_REQUEST_TYPE + * NOTES + * Defines data types for XMLRPC_REQUEST + * SEE ALSO + * XMLRPC_VALUE_TYPE + * XMLRPC_VECTOR_TYPE + * SOURCE + */ +typedef enum _xmlrpc_request_type { + xmlrpc_request_none, /* not a valid request */ + xmlrpc_request_call, /* calling/invoking a method */ + xmlrpc_request_response, /* responding to a method call */ +} XMLRPC_REQUEST_TYPE; +/*******/ + +/****d* VALUE/XMLRPC_ERROR_CODE + * NAME + * XMLRPC_ERROR_CODE + * NOTES + * All existing error codes + * SEE ALSO + * XMLRPC_REQUEST_ERROR + * SOURCE + */ +typedef enum _xmlrpc_error_code { + xmlrpc_error_none = 0, /* not an error */ + xmlrpc_error_parse_xml_syntax = -32700, + xmlrpc_error_parse_unknown_encoding = -32701, + xmlrpc_error_parse_bad_encoding = -32702, + xmlrpc_error_invalid_xmlrpc = -32600, + xmlrpc_error_unknown_method = -32601, + xmlrpc_error_invalid_params = -32602, + xmlrpc_error_internal_server = -32603, + xmlrpc_error_application = -32500, + xmlrpc_error_system = -32400, + xmlrpc_error_transport = -32300 +} XMLRPC_ERROR_CODE; +/******/ + +#define xmlrpc_error_parse_xml_syntax_str "parse error. not well formed." +#define xmlrpc_error_parse_unknown_encoding_str "parse error. unknown encoding" +#define xmlrpc_error_parse_bad_encoding_str "parse error. invalid character for encoding" +#define xmlrpc_error_invalid_xmlrpc_str "server error. xml-rpc not conforming to spec" +#define xmlrpc_error_unknown_method_str "server error. method not found." +#define xmlrpc_error_invalid_params_str "server error. invalid method parameters" +#define xmlrpc_error_internal_server_str "server error. internal xmlrpc library error" +#define xmlrpc_error_application_str "application error." +#define xmlrpc_error_system_str "system error." +#define xmlrpc_error_transport_str "transport error." + + + +/****d* VALUE/XMLRPC_VERSION + * NAME + * XMLRPC_VERSION + * NOTES + * Defines xml vocabulary used for generated xml + * SEE ALSO + * XMLRPC_REQUEST_OUTPUT_OPTIONS + * XMLRPC_REQUEST_To_XML () + * SOURCE + */ +typedef enum _xmlrpc_version { + xmlrpc_version_none, /* not a recognized vocabulary */ + xmlrpc_version_1_0, /* xmlrpc 1.0 standard vocab */ + xmlrpc_version_simple = 2, /* alt more readable vocab */ + xmlrpc_version_danda = 2 /* same as simple. legacy */ +} XMLRPC_VERSION; +/******/ + +/****s* VALUE/XMLRPC_REQUEST_OUTPUT_OPTIONS + * NAME + * XMLRPC_REQUEST_OUTPUT_OPTIONS + * NOTES + * Defines output options for generated xml + * SEE ALSO + * XMLRPC_VERSION + * XML_ELEM_OUTPUT_OPTIONS + * XMLRPC_REQUEST_To_XML () + * SOURCE + */ +typedef struct _xmlrpc_request_output_options { + STRUCT_XML_ELEM_OUTPUT_OPTIONS xml_elem_opts; /* xml_element specific output options */ + XMLRPC_VERSION version; /* xml vocabulary to use */ +} STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS, *XMLRPC_REQUEST_OUTPUT_OPTIONS; +/******/ + +/****s* VALUE/XMLRPC_REQUEST_INPUT_OPTIONS + * NAME + * XMLRPC_REQUEST_INPUT_OPTIONS + * NOTES + * Defines options for reading in xml data + * SEE ALSO + * XMLRPC_VERSION + * XML_ELEM_INPUT_OPTIONS + * XMLRPC_REQUEST_From_XML () + * SOURCE + */ +typedef struct _xmlrpc_request_input_options { + STRUCT_XML_ELEM_INPUT_OPTIONS xml_elem_opts; /* xml_element specific output options */ +} STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS, *XMLRPC_REQUEST_INPUT_OPTIONS; +/******/ + +/****s* VALUE/XMLRPC_ERROR + * NAME + * XMLRPC_ERROR + * NOTES + * For the reporting and handling of errors + * SOURCE + */ +typedef struct _xmlrpc_error { + XMLRPC_ERROR_CODE code; + STRUCT_XML_ELEM_ERROR xml_elem_error; /* xml_element errors (parser errors) */ +} STRUCT_XMLRPC_ERROR, *XMLRPC_ERROR; +/******/ + + +/****d* VALUE/XMLRPC_CASE_COMPARISON + * NAME + * XMLRPC_CASE_COMPARISON + * NOTES + * Defines case comparison options for XMLRPC_VALUE/VECTOR API's + * SEE ALSO + * XMLRPC_CASE + * XMLRPC_VALUE + * SOURCE + */ +typedef enum _xmlrpc_case_comparison { + xmlrpc_case_insensitive, /* use case-insensitive compare */ + xmlrpc_case_sensitive /* use case-sensitive compare */ +} XMLRPC_CASE_COMPARISON; +/******/ + +/****d* VALUE/XMLRPC_CASE + * NAME + * XMLRPC_CASE + * NOTES + * Defines case behavior when setting IDs in XMLRPC_VALUE API's + * SEE ALSO + * XMLRPC_CASE_COMPARISON + * XMLRPC_VALUE + * SOURCE + */ +typedef enum _xmlrpc_case { + xmlrpc_case_exact, /* leave case alone */ + xmlrpc_case_lower, /* lower-case id */ + xmlrpc_case_upper /* upper-case id */ +} XMLRPC_CASE; +/******/ + +/* if you don't like these defaults, you can set them with XMLRPC_SetDefaultIdCase*() */ +#define XMLRPC_DEFAULT_ID_CASE XMLRPC_GetDefaultIdCase() +#define XMLRPC_DEFAULT_ID_CASE_SENSITIVITY XMLRPC_GetDefaultIdCaseComparison() + +/* opaque (non-public) types. defined locally in xmlrpc.c */ +typedef struct _xmlrpc_request* XMLRPC_REQUEST; +typedef struct _xmlrpc_server* XMLRPC_SERVER; +typedef struct _xmlrpc_value* XMLRPC_VALUE; + +/****d* VALUE/XMLRPC_Callback + * NAME + * XMLRPC_Callback + * NOTES + * Function prototype for user defined method handlers (callbacks). + * SEE ALSO + * XMLRPC_ServerRegisterMethod () + * XMLRPC_ServerCallMethod () + * XMLRPC_REQUEST + * XMLRPC_VALUE + * SOURCE + */ +typedef XMLRPC_VALUE (*XMLRPC_Callback)(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); +/******/ + +/* ID Case Defaults */ +XMLRPC_CASE XMLRPC_GetDefaultIdCase(); +XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case); +XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison(); +XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case); + +/* Vector manipulation */ +int XMLRPC_VectorSize(XMLRPC_VALUE value); +XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value); +XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value); +int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type); +int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source); +int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...); +int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value); +XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case); + + +/* Create values */ +XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int truth); +XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len); +XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time); +XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s); +XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double f); +XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i); +XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* s, int len); +XMLRPC_VALUE XMLRPC_CreateValueEmpty(); +XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type); + +/* Cleanup values */ +void XMLRPC_CleanupValue(XMLRPC_VALUE value); +XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value); + +/* Set Values */ +void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time); +void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s); +void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val); +void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val); +void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val); +const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* s, int len); +void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len); +const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case); +#define XMLRPC_SetValueID(value, id, len) XMLRPC_SetValueID_Case(value, id, len, XMLRPC_DEFAULT_ID_CASE) + +/* Get Values */ +const char* XMLRPC_GetValueString(XMLRPC_VALUE value); +int XMLRPC_GetValueStringLen(XMLRPC_VALUE value); +int XMLRPC_GetValueInt(XMLRPC_VALUE value); +int XMLRPC_GetValueBoolean(XMLRPC_VALUE value); +double XMLRPC_GetValueDouble(XMLRPC_VALUE value); +const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value); +time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value); +const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value); +const char* XMLRPC_GetValueID(XMLRPC_VALUE value); + +/* Type introspection */ +XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE v); +XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE v); + +/* Parsing and Creating XML */ +XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options); +XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options); +char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int *buf_len); +char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len); + +/* Request manipulation funcs */ +const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName); +const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request); +XMLRPC_REQUEST XMLRPC_RequestNew(); +void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO); +XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output); +XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request); +XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data); +XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request); +XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type); +XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request); + +/* Server Creation/Destruction; Method Registration and Invocation */ +XMLRPC_SERVER XMLRPC_ServerCreate(); +XMLRPC_SERVER XMLRPC_GetGlobalServer(); /* better to use XMLRPC_ServerCreate if you can */ +void XMLRPC_ServerDestroy(XMLRPC_SERVER server); +int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb); +XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName); +XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData); + +#include "xmlrpc_introspection.h" + +/* Public Utility funcs */ +XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string); +void XMLRPC_Free(void* mem); +const char* XMLRPC_GetVersionString(); + +/****d* VALUE/XMLRPC_MACROS + * NAME + * Some Helpful Macros + * NOTES + * Some macros for making life easier. Should be self-explanatory. + * SEE ALSO + * XMLRPC_AddValueToVector () + * XMLRPC_VectorGetValueWithID_Case () + * XMLRPC_VALUE + * SOURCE + */ + +/* Append values to vector */ +#define XMLRPC_VectorAppendString(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueString(id, s, len)) +#define XMLRPC_VectorAppendBase64(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBase64(id, s, len)) +#define XMLRPC_VectorAppendDateTime(vector, id, time) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime(id, time)) +#define XMLRPC_VectorAppendDateTime_ISO8601(vector, id, s) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime_ISO8601(id, s)) +#define XMLRPC_VectorAppendDouble(vector, id, f) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDouble(id, f)) +#define XMLRPC_VectorAppendInt(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueInt(id, i)) +#define XMLRPC_VectorAppendBoolean(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBoolean(id, i)) + +/* Get named values from vector */ +#define XMLRPC_VectorGetValueWithID(vector, id) XMLRPC_VectorGetValueWithID_Case(vector, id, XMLRPC_DEFAULT_ID_CASE_SENSITIVITY) +#define XMLRPC_VectorGetStringWithID(vector, id) XMLRPC_GetValueString(XMLRPC_VectorGetValueWithID(vector, id)) +#define XMLRPC_VectorGetBase64WithID(vector, id) XMLRPC_GetValueBase64(XMLRPC_VectorGetValueWithID(vector, id)) +#define XMLRPC_VectorGetDateTimeWithID(vector, id) XMLRPC_GetValueDateTime(XMLRPC_VectorGetValueWithID(vector, id)) +#define XMLRPC_VectorGetDoubleWithID(vector, id) XMLRPC_GetValueDouble(XMLRPC_VectorGetValueWithID(vector, id)) +#define XMLRPC_VectorGetIntWithID(vector, id) XMLRPC_GetValueInt(XMLRPC_VectorGetValueWithID(vector, id)) +#define XMLRPC_VectorGetBooleanWithID(vector, id) XMLRPC_GetValueBoolean(XMLRPC_VectorGetValueWithID(vector, id)) + +/******/ + + +#ifdef __cplusplus +} +#endif + +#endif /* not XMLRPC_ALREADY_INCLUDED */ + + + diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc.m4 b/ext/xmlrpc/libxmlrpc/xmlrpc.m4 new file mode 100644 index 0000000000..18d36067dc --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xmlrpc.m4 @@ -0,0 +1,16 @@ +CPPFLAGS="$CPPFLAGS -DVERSION=\\\"0.42\\\"" + +AC_DEFUN(XMLRPC_CHECKS,[ + +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_LN_S]) +AC_REQUIRE([AC_PROG_RANLIB]) + +AC_DEFINE(UNDEF_THREADS_HACK,,[ ]) + +XMLRPC_HEADER_CHECKS +XMLRPC_TYPE_CHECKS +XMLRPC_FUNCTION_CHECKS + +PHP_FAST_OUTPUT($ext_builddir/libxmlrpc/Makefile) +]) diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c b/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c new file mode 100644 index 0000000000..5b15d4b071 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c @@ -0,0 +1,591 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + + +/****h* ABOUT/xmlrpc_introspection + * AUTHOR + * Dan Libby, aka danda (dan@libby.com) + * HISTORY + * 4/10/2001 -- danda -- initial introspection support + * TODO + * NOTES + *******/ + + +#include "queue.h" +#include "xmlrpc.h" +#include "xmlrpc_private.h" +#include "xmlrpc_introspection_private.h" +#include <string.h> +#include <stdlib.h> +#include <stdarg.h> + + +/* forward declarations for static (non public, non api) funcs */ +static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); +static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); +static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); +static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); + + +/*-********************************** +* Introspection Callbacks (methods) * +************************************/ + +/* iterates through a list of structs and finds the one with key "name" matching + * needle. slow, would benefit from a struct key hash. + */ +inline XMLRPC_VALUE find_named_value(XMLRPC_VALUE list, const char* needle) { + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(list); + while(xIter) { + const char* name = XMLRPC_VectorGetStringWithID(xIter, xi_token_name); + if(name && !strcmp(name, needle)) { + return xIter; + } + xIter = XMLRPC_VectorNext(list); + } + return NULL; +} + + +/* iterates through docs callbacks and calls any that have not yet been called */ +static void check_docs_loaded(XMLRPC_SERVER server, void* userData) { + if(server) { + q_iter qi = Q_Iter_Head_F(&server->docslist); + while( qi ) { + doc_method* dm = Q_Iter_Get_F(qi); + if(dm && !dm->b_called) { + dm->method(server, userData); + dm->b_called = 1; + } + qi = Q_Iter_Next_F(qi); + } + } +} + + +/* utility function for xi_system_describe_methods_cb */ +inline void describe_method(XMLRPC_SERVER server, XMLRPC_VALUE vector, const char* method) { + if(method) { + server_method* sm = find_method(server, method); + if(sm) { + XMLRPC_AddValueToVector(vector, sm->desc); + } + } +} + + + +/* system.describeMethods() callback */ +static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + XMLRPC_VALUE xParams = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)); + XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + XMLRPC_VALUE xMethodList = XMLRPC_CreateVector("methodList", xmlrpc_vector_array); + XMLRPC_VALUE xTypeList = NULL; + int bAll = 1; + + /* lazy loading of introspection data */ + check_docs_loaded(server, userData); + + xTypeList = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList"); + + XMLRPC_AddValueToVector(xResponse, xTypeList); + XMLRPC_AddValueToVector(xResponse, xMethodList); + + /* check if we have any param */ + if(xParams) { + /* check if string or vector (1 or n) */ + XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(xParams); + if(type == xmlrpc_string) { + /* just one. spit it out. */ + describe_method(server, xMethodList, XMLRPC_GetValueString(xParams)); + bAll = 0; + } + else if(type == xmlrpc_vector) { + /* multiple. spit all out */ + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xParams); + while(xIter) { + describe_method(server, xMethodList, XMLRPC_GetValueString(xIter)); + xIter = XMLRPC_VectorNext(xParams); + } + bAll = 0; + } + } + + /* otherwise, default to sending all methods */ + if(bAll) { + q_iter qi = Q_Iter_Head_F(&server->methodlist); + while( qi ) { + server_method* sm = Q_Iter_Get_F(qi); + if(sm) { + XMLRPC_AddValueToVector(xMethodList, sm->desc); + } + qi = Q_Iter_Next_F(qi); + } + } + + return xResponse; +} + +/* this complies with system.listMethods as defined at http://xmlrpc.usefulinc.com/doc/reserved.html */ +static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); + + q_iter qi = Q_Iter_Head_F(&server->methodlist); + while( qi ) { + server_method* sm = Q_Iter_Get_F(qi); + if(sm) { + XMLRPC_VectorAppendString(xResponse, 0, sm->name, 0); + } + qi = Q_Iter_Next_F(qi); + } + return xResponse; +} + +/* this complies with system.methodSignature as defined at + * http://xmlrpc.usefulinc.com/doc/sysmethodsig.html + */ +static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input))); + XMLRPC_VALUE xResponse = NULL; + + /* lazy loading of introspection data */ + check_docs_loaded(server, userData); + + if(method) { + server_method* sm = find_method(server, method); + if(sm && sm->desc) { + XMLRPC_VALUE xTypesArray = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); + XMLRPC_VALUE xIter, xParams, xSig, xSigIter; + const char* type; + + /* array of possible signatures. */ + xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); + + /* find first signature */ + xSig = XMLRPC_VectorGetValueWithID(sm->desc, xi_token_signatures); + xSigIter = XMLRPC_VectorRewind( xSig ); + + /* iterate through sigs */ + while(xSigIter) { + /* first type is the return value */ + type = XMLRPC_VectorGetStringWithID(XMLRPC_VectorRewind( + XMLRPC_VectorGetValueWithID(xSigIter, xi_token_returns)), + xi_token_type); + XMLRPC_AddValueToVector(xTypesArray, + XMLRPC_CreateValueString(NULL, + type ? type : type_to_str(xmlrpc_none, 0), + 0)); + + /* the rest are parameters */ + xParams = XMLRPC_VectorGetValueWithID(xSigIter, xi_token_params); + xIter = XMLRPC_VectorRewind(xParams); + + /* iter through params, adding to types array */ + while(xIter) { + XMLRPC_AddValueToVector(xTypesArray, + XMLRPC_CreateValueString(NULL, + XMLRPC_VectorGetStringWithID(xIter, xi_token_type), + 0)); + xIter = XMLRPC_VectorNext(xParams); + } + + /* add types for this signature */ + XMLRPC_AddValueToVector(xResponse, xTypesArray); + + xSigIter = XMLRPC_VectorNext( xSig ); + } + } + } + + return xResponse; +} + +/* this complies with system.methodHelp as defined at + * http://xmlrpc.usefulinc.com/doc/sysmethhelp.html + */ +static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { + const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input))); + XMLRPC_VALUE xResponse = NULL; + + /* lazy loading of introspection data */ + check_docs_loaded(server, userData); + + if(method) { + server_method* sm = find_method(server, method); + if(sm && sm->desc) { + const char* help = XMLRPC_VectorGetStringWithID(sm->desc, xi_token_purpose); + + /* returns a documentation string, or empty string */ + xResponse = XMLRPC_CreateValueString(NULL, help ? help : xi_token_empty, 0); + } + } + + return xResponse; +} + +/*-************************************** +* End Introspection Callbacks (methods) * +****************************************/ + + +/*-************************ +* Introspection Utilities * +**************************/ + +/* performs registration of introspection methods */ +void xi_register_system_methods(XMLRPC_SERVER server) { + XMLRPC_ServerRegisterMethod(server, xi_token_system_list_methods, xi_system_list_methods_cb); + XMLRPC_ServerRegisterMethod(server, xi_token_system_method_help, xi_system_method_help_cb); + XMLRPC_ServerRegisterMethod(server, xi_token_system_method_signature, xi_system_method_signature_cb); + XMLRPC_ServerRegisterMethod(server, xi_token_system_describe_methods, xi_system_describe_methods_cb); +} + +/* describe a value (param, return, type) */ +static XMLRPC_VALUE describeValue_worker(const char* type, const char* id, const char* desc, int optional, const char* default_val, XMLRPC_VALUE sub_params) { + XMLRPC_VALUE xParam = NULL; + if(id || desc) { + xParam = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + XMLRPC_VectorAppendString(xParam, xi_token_name, id, 0); + XMLRPC_VectorAppendString(xParam, xi_token_type, type, 0); + XMLRPC_VectorAppendString(xParam, xi_token_description, desc, 0); + if(optional != 2) { + XMLRPC_VectorAppendInt(xParam, xi_token_optional, optional); + } + if(optional == 1 && default_val) { + XMLRPC_VectorAppendString(xParam, xi_token_default, default_val, 0); + } + XMLRPC_AddValueToVector(xParam, sub_params); + } + return xParam; +} + + +/* convert an xml tree conforming to spec <url tbd> to XMLRPC_VALUE + * suitable for use with XMLRPC_ServerAddIntrospectionData + */ +XMLRPC_VALUE xml_element_to_method_description(xml_element* el, XMLRPC_ERROR err) { + XMLRPC_VALUE xReturn = NULL; + + if(el->name) { + const char* name = NULL; + const char* type = NULL; + const char* basetype = NULL; + const char* desc = NULL; + const char* def = NULL; + int optional = 0; + xml_element_attr* attr_iter = Q_Head(&el->attrs); + + /* grab element attributes up front to save redundant while loops */ + while(attr_iter) { + if(!strcmp(attr_iter->key, "name")) { + name = attr_iter->val; + } + else if(!strcmp(attr_iter->key, "type")) { + type = attr_iter->val; + } + else if(!strcmp(attr_iter->key, "basetype")) { + basetype = attr_iter->val; + } + else if(!strcmp(attr_iter->key, "desc")) { + desc = attr_iter->val; + } + else if(!strcmp(attr_iter->key, "optional")) { + if(attr_iter->val && !strcmp(attr_iter->val, "yes")) { + optional = 1; + } + } + else if(!strcmp(attr_iter->key, "default")) { + def = attr_iter->val; + } + attr_iter = Q_Next(&el->attrs); + } + + /* value and typeDescription behave about the same */ + if(!strcmp(el->name, "value") || !strcmp(el->name, "typeDescription")) { + XMLRPC_VALUE xSubList = NULL; + const char* ptype = !strcmp(el->name, "value") ? type : basetype; + if(ptype) { + if(Q_Size(&el->children) && + !strcmp(ptype, "array") || !strcmp(ptype, "struct") || !strcmp(ptype, "mixed")) { + xSubList = XMLRPC_CreateVector("member", xmlrpc_vector_array); + + if(xSubList) { + xml_element* elem_iter = Q_Head(&el->children); + while(elem_iter) { + XMLRPC_AddValueToVector(xSubList, + xml_element_to_method_description(elem_iter, err)); + elem_iter = Q_Next(&el->children); + } + } + } + xReturn = describeValue_worker(ptype, name, (desc ? desc : (xSubList ? NULL : el->text.str)), optional, def, xSubList); + } + } + + /* these three kids are about equivalent */ + else if(!strcmp(el->name, "params") || + !strcmp(el->name, "returns") || + !strcmp(el->name, "signature")) { + if(Q_Size(&el->children)) { + xml_element* elem_iter = Q_Head(&el->children); + xReturn = XMLRPC_CreateVector(!strcmp(el->name, "signature") ? NULL : el->name, xmlrpc_vector_struct); + + + while(elem_iter) { + XMLRPC_AddValueToVector(xReturn, + xml_element_to_method_description(elem_iter, err)); + elem_iter = Q_Next(&el->children); + } + } + } + + + else if(!strcmp(el->name, "methodDescription")) { + xml_element* elem_iter = Q_Head(&el->children); + xReturn = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + + XMLRPC_VectorAppendString(xReturn, xi_token_name, name, 0); + + while(elem_iter) { + XMLRPC_AddValueToVector(xReturn, + xml_element_to_method_description(elem_iter, err)); + elem_iter = Q_Next(&el->children); + } + } + + /* items are slightly special */ + else if(!strcmp(el->name, "item")) { + xReturn = XMLRPC_CreateValueString(name, el->text.str, el->text.len); + } + + /* sure. we'll let any ol element with children through */ + else if(Q_Size(&el->children)) { + xml_element* elem_iter = Q_Head(&el->children); + xReturn = XMLRPC_CreateVector(el->name, xmlrpc_vector_mixed); + + while(elem_iter) { + XMLRPC_AddValueToVector(xReturn, + xml_element_to_method_description(elem_iter, err)); + elem_iter = Q_Next(&el->children); + } + } + + /* or anything at all really, so long as its got some text. + * no reason being all snotty about a spec, right? + */ + else if(el->name && el->text.len) { + xReturn = XMLRPC_CreateValueString(el->name, el->text.str, el->text.len); + } + } + + return xReturn; +} + +/*-**************************** +* End Introspection Utilities * +******************************/ + + + +/*-****************** +* Introspection API * +********************/ + + +/****f* VALUE/XMLRPC_IntrospectionCreateDescription + * NAME + * XMLRPC_IntrospectionCreateDescription + * SYNOPSIS + * XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) + * FUNCTION + * converts raw xml describing types and methods into an + * XMLRPC_VALUE suitable for use with XMLRPC_ServerAddIntrospectionData() + * INPUTS + * xml - xml data conforming to introspection spec at <url tbd> + * err - optional pointer to error struct. filled in if error occurs and not NULL. + * RESULT + * XMLRPC_VALUE - newly created value, or NULL if fatal error. + * BUGS + * Currently does little or no validation of xml. + * Only parse errors are currently reported in err, not structural errors. + * SEE ALSO + * XMLRPC_ServerAddIntrospectionData () + * SOURCE + */ +XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) { + XMLRPC_VALUE xReturn = NULL; + xml_element* root = xml_elem_parse_buf(xml, 0, 0, err ? &err->xml_elem_error : NULL); + + if(root) { + xReturn = xml_element_to_method_description(root, err); + + xml_elem_free(root); + } + + return xReturn; + +} +/*******/ + + +/****f* SERVER/XMLRPC_ServerAddIntrospectionData + * NAME + * XMLRPC_ServerAddIntrospectionData + * SYNOPSIS + * int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) + * FUNCTION + * updates server with additional introspection data + * INPUTS + * server - target server + * desc - introspection data, should be a struct generated by + * XMLRPC_IntrospectionCreateDescription () + * RESULT + * int - 1 if success, else 0 + * NOTES + * - function will fail if neither typeList nor methodList key is present in struct. + * - if method or type already exists, it will be replaced. + * - desc is never freed by the server. caller is responsible for cleanup. + * BUGS + * - horribly slow lookups. prime candidate for hash improvements. + * - uglier and more complex than I like to see for API functions. + * SEE ALSO + * XMLRPC_ServerAddIntrospectionData () + * XMLRPC_ServerRegisterIntrospectionCallback () + * XMLRPC_CleanupValue () + * SOURCE + */ +int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) { + int bSuccess = 0; + if(server && desc) { + XMLRPC_VALUE xNewTypes = XMLRPC_VectorGetValueWithID(desc, "typeList"); + XMLRPC_VALUE xNewMethods = XMLRPC_VectorGetValueWithID(desc, "methodList"); + XMLRPC_VALUE xServerTypes = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList"); + + if(xNewMethods) { + XMLRPC_VALUE xMethod = XMLRPC_VectorRewind(xNewMethods); + + while(xMethod) { + const char* name = XMLRPC_VectorGetStringWithID(xMethod, xi_token_name); + server_method* sm = find_method(server, name); + + if(sm) { + if(sm->desc) { + XMLRPC_CleanupValue(sm->desc); + } + sm->desc = XMLRPC_CopyValue(xMethod); + bSuccess = 1; + } + + xMethod = XMLRPC_VectorNext(xNewMethods); + } + } + if(xNewTypes) { + if(!xServerTypes) { + if(!server->xIntrospection) { + server->xIntrospection = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); + } + + XMLRPC_AddValueToVector(server->xIntrospection, xNewTypes); + bSuccess = 1; + } + else { + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xNewTypes); + while(xIter) { + /* get rid of old values */ + XMLRPC_VALUE xPrev = find_named_value(xServerTypes, XMLRPC_VectorGetStringWithID(xIter, xi_token_name)); + if(xPrev) { + XMLRPC_VectorRemoveValue(xServerTypes, xPrev); + } + XMLRPC_AddValueToVector(xServerTypes, xIter); + bSuccess = 1; + xIter = XMLRPC_VectorNext(xNewTypes); + } + } + } + } + return bSuccess; +} +/*******/ + + +/****f* SERVER/XMLRPC_ServerRegisterIntrospectionCallback + * NAME + * XMLRPC_ServerRegisterIntrospectionCallback + * SYNOPSIS + * int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) + * FUNCTION + * registers a callback for lazy generation of introspection data + * INPUTS + * server - target server + * cb - callback that will generate introspection data + * RESULT + * int - 1 if success, else 0 + * NOTES + * parsing xml and generating introspection data is fairly expensive, thus a + * server may wish to wait until this data is actually requested before generating + * it. Any number of callbacks may be registered at any time. A given callback + * will only ever be called once, the first time an introspection request is + * processed after the time of callback registration. + * SEE ALSO + * XMLRPC_ServerAddIntrospectionData () + * XMLRPC_IntrospectionCreateDescription () + * SOURCE + */ +int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) { + int bSuccess = 0; + if(server && cb) { + + doc_method* dm = calloc(1, sizeof(doc_method)); + + if(dm) { + dm->method = cb; + dm->b_called = 0; + + if(Q_PushTail(&server->docslist, dm)) { + bSuccess = 1; + } + else { + my_free(dm); + } + } + } + return 0; +} +/*******/ + +/*-********************** +* End Introspection API * +************************/ + + + diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.h b/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.h new file mode 100644 index 0000000000..656e441b96 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.h @@ -0,0 +1,101 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* IMPORTANT! + * + * only public (official API) things should be in this file. Anything else + * should go in <group>_private.h, or in the appropriate .c file. + */ + + +#ifndef __XI_INTROSPECTION_H +/* + * Avoid include redundancy. + */ +#define __XI_INTROSPECTION_H + +/*---------------------------------------------------------------------------- + * xmlrpc_introspection.h + * + * Purpose: + * define public introspection API + * Comments: + */ + +/*---------------------------------------------------------------------------- + * Constants + */ + #define xi_token_params "params" + #define xi_token_returns "returns" + #define xi_token_related "related" + #define xi_token_sub "sub" + + +/*---------------------------------------------------------------------------- + * Includes + */ + +/*---------------------------------------------------------------------------- + * Structures + */ + + /****d* VALUE/XMLRPC_IntrospectionCallback + * NAME + * XMLRPC_IntrospectionCallback + * NOTES + * Function prototype for lazy documentation generation (not generated until requested). + * SOURCE + */ +typedef void (*XMLRPC_IntrospectionCallback)(XMLRPC_SERVER server, void* userData); +/******/ + + +/*---------------------------------------------------------------------------- + * Globals + */ + +/*---------------------------------------------------------------------------- + * Functions + */ +XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR error); +int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc); +int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb); + +/*---------------------------------------------------------------------------- + * Macros + */ + + +#endif /* __XI_INTROSPECTION_H */ + + + diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h b/ext/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h new file mode 100644 index 0000000000..7b97fa7ed7 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h @@ -0,0 +1,106 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Dan Libby, Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* IMPORTANT! + * + * only non-public things should be in this file. It is fine for any .c file + * in xmlrpc/src to include it, but users of the public API should never + * include it, and thus *.h files that are part of the public API should + * never include it, or they would break if this file is not present. + */ + + +#ifndef __XI_INTROSPECTION_PRIVATE_H +/* + * Avoid include redundancy. + */ +#define __XI_INTROSPECTION_PRIVATE_H + +/*---------------------------------------------------------------------------- + * xmlrpc_introspection_private.h + * + * Purpose: + * define non-public introspection routines + * Comments: + */ + +/*---------------------------------------------------------------------------- + * Constants + */ +#define xi_token_default "default" +#define xi_token_description "description" +#define xi_token_name "name" +#define xi_token_optional "optional" +#define xi_token_params "params" +#define xi_token_purpose "purpose" +#define xi_token_returns "returns" +#define xi_token_signatures "signatures" +#define xi_token_type "type" +#define xi_token_version "version" +#define xi_token_empty "" +#define xi_token_system_describe_methods "system.describeMethods" +#define xi_token_system_list_methods "system.listMethods" +#define xi_token_system_method_help "system.methodHelp" +#define xi_token_system_method_signature "system.methodSignature" + + +/*---------------------------------------------------------------------------- + * Includes + */ + +/*---------------------------------------------------------------------------- + * Structures + */ +typedef struct _doc_method { + XMLRPC_IntrospectionCallback method; + int b_called; +} doc_method; + +/*---------------------------------------------------------------------------- + * Globals + */ + +/*---------------------------------------------------------------------------- + * Functions + */ +void xi_register_system_methods(XMLRPC_SERVER server); + +/*---------------------------------------------------------------------------- + * Macros + */ + + +#endif /* __XI_INTROSPECTION_PRIVATE_H */ + + + + diff --git a/ext/xmlrpc/libxmlrpc/xmlrpc_private.h b/ext/xmlrpc/libxmlrpc/xmlrpc_private.h new file mode 100644 index 0000000000..0b4a078ee9 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/xmlrpc_private.h @@ -0,0 +1,179 @@ +/* + This file is part of libXMLRPC - a C library for xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2000 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* only non-public things should be in this file. It is fine for any .c file + * in xmlrpc/src to include it, but users of the public API should never + * include it, and thus *.h files that are part of the public API should + * never include it, or they would break if this file is not present. + */ + +#ifndef XMLRPC_PRIVATE_ALREADY_INCLUDED +/* + * Avoid include redundancy. + */ +#define XMLRPC_PRIVATE_ALREADY_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + + +/*---------------------------------------------------------------------------- + * xmlrpc_private.h + * + * Purpose: + * define non-public intra-library routines & data + * Comments: + */ + +/*---------------------------------------------------------------------------- + * Constants + */ + + +/*---------------------------------------------------------------------------- + * Includes + */ + +/*---------------------------------------------------------------------------- + * Structures + */ + +/* Some of these are typedef'd in xmlrpc.h for public use */ + +typedef struct _xmlrpc_vector* XMLRPC_VECTOR; + +/****s* VALUE/XMLRPC_VALUE + * NAME + * XMLRPC_VALUE + * NOTES + * A value of variable data type. The most important object in this API. :) + * + * This struct is opaque to callers and should be accessed only via accessor functions. + * SEE ALSO + * XMLRPC_REQUEST + * XMLRPC_CreateValueEmpty () + * XMLRPC_CleanupValue () + * SOURCE + */ +typedef struct _xmlrpc_value { + XMLRPC_VALUE_TYPE type; /* data type of this value */ + XMLRPC_VECTOR v; /* vector type specific info */ + simplestring str; /* string value buffer */ + simplestring id; /* id of this value. possibly empty. */ + int i; /* integer value. */ + double d; /* double value */ + int iRefCount; /* So we know when we can delete the value . */ +} STRUCT_XMLRPC_VALUE; +/******/ + +/****s* VALUE/XMLRPC_REQUEST + * NAME + * XMLRPC_REQUEST + * NOTES + * Internal representation of an XML request. + * + * This struct is opaque to callers and should be accessed only via accessor functions. + * + * SEE ALSO + * XMLRPC_VALUE + * XMLRPC_RequestNew () + * XMLRPC_RequestFree () + * SOURCE + */ +typedef struct _xmlrpc_request { + XMLRPC_VALUE io; /* data associated with this request */ + simplestring methodName; /* name of method being called */ + XMLRPC_REQUEST_TYPE request_type; /* type of request */ + STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS output; /* xml output options */ + XMLRPC_VALUE error; /* error codes */ +} STRUCT_XMLRPC_REQUEST; +/******/ + +/* Vector type. Used by XMLRPC_VALUE. Never visible to users of the API. */ +typedef struct _xmlrpc_vector { + XMLRPC_VECTOR_TYPE type; /* vector type */ + const char* id; /* ??? unused? */ + queue *q; /* list of child values */ +} STRUCT_XMLRPC_VECTOR; +/******/ + +/****s* VALUE/XMLRPC_SERVER + * NAME + * XMLRPC_SERVER + * NOTES + * internal representation of an xmlrpc server + * + * This struct is opaque to callers and should be accessed only via accessor functions. + * + * SEE ALSO + * XMLRPC_ServerCreate () + * XMLRPC_ServerDestroy () + * SOURCE + */ +typedef struct _xmlrpc_server { + queue methodlist; /* list of callback methods */ + queue docslist; /* list of introspection callbacks */ + XMLRPC_VALUE xIntrospection; +} STRUCT_XMLRPC_SERVER; +/******/ + +typedef struct _server_method { + char* name; + XMLRPC_VALUE desc; + XMLRPC_Callback method; +} server_method; + + +/*---------------------------------------------------------------------------- + * Globals + */ + +/*---------------------------------------------------------------------------- + * Functions + */ +server_method* find_method(XMLRPC_SERVER server, const char* name); +const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype); + +/*---------------------------------------------------------------------------- + * Macros + */ +#define my_free(thing) if(thing) {free(thing); thing = 0;} + + +#ifdef __cplusplus +} +#endif + + +#endif /* XMLRPC_PRIVATE_ALREADY_INCLUDED */ + diff --git a/ext/xmlrpc/php_config.h.in b/ext/xmlrpc/php_config.h.in new file mode 100644 index 0000000000..2c8a1bf2e2 --- /dev/null +++ b/ext/xmlrpc/php_config.h.in @@ -0,0 +1,11 @@ +/* php_config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if your C compiler doesn't accept -c and -o together. */ +#undef NO_MINUS_C_MINUS_O + +/* Whether you have XMLRPC */ +#undef HAVE_XMLRPC + +/* Whether to build xmlrpc as dynamic module */ +#undef COMPILE_DL_XMLRPC + diff --git a/ext/xmlrpc/php_xmlrpc.h b/ext/xmlrpc/php_xmlrpc.h new file mode 100644 index 0000000000..77fbd573ee --- /dev/null +++ b/ext/xmlrpc/php_xmlrpc.h @@ -0,0 +1,133 @@ +/* + This file is part of, or distributed with, libXMLRPC - a C library for + xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* auto-generated portions of this file are also subject to the php license */ + +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: | + | | + +----------------------------------------------------------------------+ + */ + +#ifndef _PHP_XMLRPC_H +#define _PHP_XMLRPC_H + +/* You should tweak config.m4 so this symbol (or some else suitable) + gets defined. +*/ +#if 1 /* HAVE_XMLRPC */ + +extern zend_module_entry xmlrpc_module_entry; +#define phpext_xmlrpc_ptr &xmlrpc_module_entry + +#ifdef PHP_WIN32 +#define PHP_XMLRPC_API __declspec(dllexport) +#else +#define PHP_XMLRPC_API +#endif + +PHP_MINIT_FUNCTION(xmlrpc); +PHP_MSHUTDOWN_FUNCTION(xmlrpc); +PHP_RINIT_FUNCTION(xmlrpc); +PHP_RSHUTDOWN_FUNCTION(xmlrpc); +PHP_MINFO_FUNCTION(xmlrpc); + +PHP_FUNCTION(xmlrpc_encode); +PHP_FUNCTION(xmlrpc_decode); +PHP_FUNCTION(xmlrpc_decode_request); +PHP_FUNCTION(xmlrpc_encode_request); +PHP_FUNCTION(xmlrpc_get_type); +PHP_FUNCTION(xmlrpc_set_type); +PHP_FUNCTION(xmlrpc_server_create); +PHP_FUNCTION(xmlrpc_server_destroy); +PHP_FUNCTION(xmlrpc_server_register_method); +PHP_FUNCTION(xmlrpc_server_call_method); +PHP_FUNCTION(xmlrpc_parse_method_descriptions); +PHP_FUNCTION(xmlrpc_server_add_introspection_data); +PHP_FUNCTION(xmlrpc_server_register_introspection_callback); + +/* Fill in this structure and use entries in it + for thread safety instead of using true globals. +*/ +typedef struct { + /* You can use the next one as type if your module registers any + resources. Oh, you can of course rename it to something more + suitable, add list entry types or remove it if it not needed. + It's just an example. + */ + int le_xmlrpc_server; +} php_xmlrpc_globals; + +/* In every function that needs to use variables in php_xmlrpc_globals, + do call XMLRPCLS_FETCH(); after declaring other variables used by + that function, and always refer to them as XMLRPCG(variable). + You are encouraged to rename these macros something shorter, see + examples in any other php module directory. +*/ + +#ifdef ZTS +#define XMLRPCG(v) (xmlrpc_globals->v) +#define XMLRPCLS_FETCH() php_xmlrpc_globals *xmlrpc_globals = ts_resource(gd_xmlrpc_id) +#else +#define XMLRPCG(v) (xmlrpc_globals.v) +#define XMLRPCLS_FETCH() +#endif + +#else + +#define phpext_xmlrpc_ptr NULL + +#endif + +#endif /* _PHP_XMLRPC_H */ + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c new file mode 100644 index 0000000000..589adaad05 --- /dev/null +++ b/ext/xmlrpc/xmlrpc-epi-php.c @@ -0,0 +1,1421 @@ +/* + This file is part of, or distributed with, libXMLRPC - a C library for + xml-encoded function calls. + + Author: Dan Libby (dan@libby.com) + Epinions.com may be contacted at feedback@epinions-inc.com +*/ + +/* + Copyright 2001 Epinions, Inc. + + Subject to the following 3 conditions, Epinions, Inc. permits you, free + of charge, to (a) use, copy, distribute, modify, perform and display this + software and associated documentation files (the "Software"), and (b) + permit others to whom the Software is furnished to do so as well. + + 1) The above copyright notice and this permission notice shall be included + without modification in all copies or substantial portions of the + Software. + + 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF + ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY + IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE OR NONINFRINGEMENT. + + 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, + SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT + OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING + NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH + DAMAGES. + +*/ + +/* auto-generated portions of this file are also subject to the php license */ + +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: | + | | + +----------------------------------------------------------------------+ + */ + +#include "php.h" +#include "php_ini.h" +#include "php_xmlrpc.h" +#include "xmlrpc.h" + +/* You should tweak config.m4 so this symbol (or some else suitable) + gets defined. +*/ + +#ifdef ZTS +int xmlrpc_globals_id; +#else +php_xmlrpc_globals xmlrpc_globals; +#endif + + +/* Every user visible function must have an entry in xmlrpc_functions[]. +*/ +function_entry xmlrpc_functions[] = { + PHP_FE(xmlrpc_encode, NULL) + PHP_FE(xmlrpc_decode, NULL) + PHP_FE(xmlrpc_decode_request, NULL) + PHP_FE(xmlrpc_encode_request, NULL) + PHP_FE(xmlrpc_get_type, NULL) + PHP_FE(xmlrpc_set_type, NULL) + PHP_FE(xmlrpc_server_create, NULL) + PHP_FE(xmlrpc_server_destroy, NULL) + PHP_FE(xmlrpc_server_register_method, NULL) + PHP_FE(xmlrpc_server_call_method, NULL) + PHP_FE(xmlrpc_parse_method_descriptions, NULL) + PHP_FE(xmlrpc_server_add_introspection_data, NULL) + PHP_FE(xmlrpc_server_register_introspection_callback, NULL) + {NULL, NULL, NULL} /* Must be the last line in xmlrpc_functions[] */ +}; + +zend_module_entry xmlrpc_module_entry = { + "xmlrpc", + xmlrpc_functions, + PHP_MINIT(xmlrpc), + PHP_MSHUTDOWN(xmlrpc), + PHP_RINIT(xmlrpc), /* Replace with NULL if there's nothing to do at request start */ + PHP_RSHUTDOWN(xmlrpc), /* Replace with NULL if there's nothing to do at request end */ + PHP_MINFO(xmlrpc), + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_XMLRPC +ZEND_GET_MODULE(xmlrpc) +#endif + +/* Remove comments and fill if you need to have entries in php.ini +PHP_INI_BEGIN() +PHP_INI_END() +*/ + +/******************************* +* local structures and defines * +*******************************/ + +// per server data +typedef struct _xmlrpc_server_data { + pval* method_map; + pval* introspection_map; + XMLRPC_SERVER server_ptr; +} xmlrpc_server_data; + + +// how to format output +typedef struct _php_output_options { + int b_php_out; + STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out; +} php_output_options; + +// data passed to C callback +typedef struct _xmlrpc_callback_data { + pval* xmlrpc_method; + pval* php_function; + pval* caller_params; + pval* return_data; + xmlrpc_server_data* server; + char php_executed; +} xmlrpc_callback_data; + +#define PHP_EXT_VERSION "0.41" + +// output options +#define OUTPUT_TYPE_KEY "output_type" +#define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1) +#define OUTPUT_TYPE_VALUE_PHP "php" +#define OUTPUT_TYPE_VALUE_XML "xml" + +#define VERBOSITY_KEY "verbosity" +#define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1) +#define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space" +#define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only" +#define VERBOSITY_VALUE_PRETTY "pretty" + +#define ESCAPING_KEY "escaping" +#define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1) +#define ESCAPING_VALUE_CDATA "cdata" +#define ESCAPING_VALUE_NON_ASCII "non-ascii" +#define ESCAPING_VALUE_NON_PRINT "non-print" +#define ESCAPING_VALUE_MARKUP "markup" + +#define VERSION_KEY "version" +#define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1) +#define VERSION_VALUE_SIMPLE "simple" +#define VERSION_VALUE_XMLRPC "xmlrpc" + +#define ENCODING_KEY "encoding" +#define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1) +#define ENCODING_DEFAULT "iso-8859-1" + +// value types +#define OBJECT_TYPE_ATTR "xmlrpc_type" +#define OBJECT_VALUE_ATTR "scalar" + + + +/*********************** +* forward declarations * +***********************/ +XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue); +static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data); + +/********************* +* startup / shutdown * +*********************/ + +static void destroy_server_data(xmlrpc_server_data *server) { + if(server) { + XMLRPC_ServerDestroy(server->server_ptr); + + zval_dtor(server->method_map); + FREE_ZVAL(server->method_map); + + zval_dtor(server->introspection_map); + FREE_ZVAL(server->introspection_map); + + efree(server); + } +} + +/* called when server is being destructed. either when xmlrpc_server_destroy + * is called, or when request ends. + */ +static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc) { + if(rsrc && rsrc->ptr) { + destroy_server_data((xmlrpc_server_data*)rsrc->ptr); + } +} + +/* module init */ +PHP_MINIT_FUNCTION(xmlrpc) +{ +/* Remove comments if you have entries in php.ini + REGISTER_INI_ENTRIES(); +*/ + XMLRPCG(le_xmlrpc_server) = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number); + + return SUCCESS; +} + +/* module shutdown */ +PHP_MSHUTDOWN_FUNCTION(xmlrpc) +{ +/* Remove comments if you have entries in php.ini + UNREGISTER_INI_ENTRIES(); +*/ + return SUCCESS; +} + +/* Remove if there's nothing to do at request start */ +PHP_RINIT_FUNCTION(xmlrpc) +{ + return SUCCESS; +} + +/* Remove if there's nothing to do at request end */ +PHP_RSHUTDOWN_FUNCTION(xmlrpc) +{ + return SUCCESS; +} + +/* display info in phpinfo() */ +PHP_MINFO_FUNCTION(xmlrpc) +{ + php_info_print_table_start(); + php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString()); + php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION); + php_info_print_table_row(2, "author", "Dan Libby"); + php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net"); + php_info_print_table_row(2, "open sourced by", "Epinions.com"); + php_info_print_table_end(); + + /* + DISPLAY_INI_ENTRIES(); + */ +} + +/******************* +* random utilities * +*******************/ + +/* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc). + * Could easily be further generalized to work with objects. + */ +static int add_long(pval* list, char* id, int num) { + if(id) return add_assoc_long(list, id, num); + else return add_next_index_long(list, num); +} + +static int add_double(pval* list, char* id, double num) { + if(id) return add_assoc_double(list, id, num); + else return add_next_index_double(list, num); +} + +static int add_string(pval* list, char* id, char* string, int duplicate) { + if(id) return add_assoc_string(list, id, string, duplicate); + else return add_next_index_string(list, string, duplicate); +} + +static int add_stringl(pval* list, char* id, char* string, uint length, int duplicate) { + if(id) return add_assoc_stringl(list, id, string, length, duplicate); + else return add_next_index_stringl(list, string, length, duplicate); +} + +static int add_pval(pval* list, const char* id, pval** val) { + if(list && val) { + if(id) return zend_hash_update(list->value.ht, (char*)id, strlen(id)+1, (void *) val, sizeof(pval **), NULL); + else return zend_hash_next_index_insert(list->value.ht, (void *) val, sizeof(pval **), NULL); + } +} + +#if ZEND_MODULE_API_NO >= 20001222 +#define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0) +#else +#define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index) +#endif + + +/************************* +* input / output options * +*************************/ + +/* parse an array (user input) into output options suitable for use by xmlrpc engine + * and determine whether to return data as xml or php vars + */ +static void set_output_options(php_output_options* options, pval* output_opts) { + + if(options) { + + /* defaults */ + options->b_php_out = 0; + options->xmlrpc_out.version = xmlrpc_version_1_0; + options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT; + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping; + + if(output_opts && output_opts->type == IS_ARRAY) { + pval** val; + + /* verbosity of generated xml */ + if(zend_hash_find(output_opts->value.ht, + OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, + (void**)&val) == SUCCESS) { + if((*val)->type == IS_STRING) { + if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_PHP)) { + options->b_php_out = 1; + } + else if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_XML)) { + options->b_php_out = 0; + } + } + } + + /* verbosity of generated xml */ + if(zend_hash_find(output_opts->value.ht, + VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, + (void**)&val) == SUCCESS) { + if((*val)->type == IS_STRING) { + if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NO_WHITE_SPACE)) { + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space; + } + else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NEWLINES_ONLY)) { + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only; + } + else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_PRETTY)) { + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; + } + } + } + + /* version of xml to output */ + if(zend_hash_find(output_opts->value.ht, + VERSION_KEY, VERSION_KEY_LEN + 1, + (void**)&val) == SUCCESS) { + if((*val)->type == IS_STRING) { + if(!strcmp((*val)->value.str.val, VERSION_VALUE_XMLRPC)) { + options->xmlrpc_out.version = xmlrpc_version_1_0; + } + else if(!strcmp((*val)->value.str.val, VERSION_VALUE_SIMPLE)) { + options->xmlrpc_out.version = xmlrpc_version_simple; + } + } + } + + /* encoding code set */ + if(zend_hash_find(output_opts->value.ht, + ENCODING_KEY, ENCODING_KEY_LEN + 1, + (void**)&val) == SUCCESS) { + if((*val)->type == IS_STRING) { + options->xmlrpc_out.xml_elem_opts.encoding = estrdup((*val)->value.str.val); + } + } + + /* escaping options */ + if(zend_hash_find(output_opts->value.ht, + ESCAPING_KEY, ESCAPING_KEY_LEN + 1, + (void**)&val) == SUCCESS) { + /* multiple values allowed. check if array */ + if((*val)->type == IS_ARRAY) { + pval** iter_val; + zend_hash_internal_pointer_reset((*val)->value.ht); + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping; + while(1) { + if(zend_hash_get_current_data((*val)->value.ht, (void**)&iter_val) == SUCCESS) { + if((*iter_val)->type == IS_STRING && (*iter_val)->value.str.val) { + if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_CDATA)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping; + } + else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping; + } + else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping; + } + else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_MARKUP)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping; + } + } + } + else { + break; + } + + zend_hash_move_forward((*val)->value.ht); + } + } + /* else, check for single value */ + else if((*val)->type == IS_STRING) { + if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_CDATA)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping; + } + else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping; + } + else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping; + } + else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_MARKUP)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping; + } + } + } + } + } +} + + +/****************** +* encode / decode * +******************/ + +/* php arrays have no distinction between array and struct types. + * they even allow mixed. Thus, we determine the type by iterating + * through the entire array and figuring out each element. + * room for some optimation here if we stop after a specific # of elements. + */ +static XMLRPC_VECTOR_TYPE determine_vector_type(HashTable *ht) { + int bArray = 0, bStruct = 0, bMixed = 0; + unsigned long num_index; + char* my_key; + + zend_hash_internal_pointer_reset(ht); + while(1) { + int res = my_zend_hash_get_current_key(ht, &my_key, &num_index); + if(res == HASH_KEY_IS_LONG) { + if(bStruct) { + bMixed = 1; + break; + } + bArray = 1; + } + else if(res == HASH_KEY_NON_EXISTANT) { + break; + } + else if(res == HASH_KEY_IS_STRING) { + if(bArray) { + bMixed = 1; + break; + } + bStruct = 1; + } + + zend_hash_move_forward(ht); + } + return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array); +} + +/* recursively convert php values into xmlrpc values */ +static XMLRPC_VALUE PHP_to_XMLRPC_worker(const char* key, pval* in_val, int depth) { + XMLRPC_VALUE xReturn = NULL; + if(in_val) { + pval* val = NULL; + XMLRPC_VALUE_TYPE type = get_pval_xmlrpc_type(in_val, &val); + if(val) { + switch(type) { + case xmlrpc_base64: + if(val->type == IS_NULL) { + xReturn = XMLRPC_CreateValueBase64(key, "", 1); + } + else { + xReturn = XMLRPC_CreateValueBase64(key, val->value.str.val, val->value.str.len); + } + break; + case xmlrpc_datetime: + convert_to_string(val); + xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, val->value.str.val); + break; + case xmlrpc_boolean: + convert_to_boolean(val); + xReturn = XMLRPC_CreateValueBoolean(key, val->value.lval); + break; + case xmlrpc_int: + convert_to_long(val); + xReturn = XMLRPC_CreateValueInt(key, val->value.lval); + break; + case xmlrpc_double: + convert_to_double(val); + xReturn = XMLRPC_CreateValueDouble(key, val->value.dval); + break; + case xmlrpc_string: + convert_to_string(val); + xReturn = XMLRPC_CreateValueString(key, val->value.str.val, val->value.str.len); + break; + case xmlrpc_vector: + { + unsigned long num_index; + pval** pIter; + char* my_key; + + convert_to_array(val); + + xReturn = XMLRPC_CreateVector(key, determine_vector_type(val->value.ht)); + + zend_hash_internal_pointer_reset(val->value.ht); + while(1) { + int res = my_zend_hash_get_current_key(val->value.ht, &my_key, &num_index); + if(res == HASH_KEY_IS_LONG) { + if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) { + XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(0, *pIter, depth++)); + } + } + else if(res == HASH_KEY_NON_EXISTANT) { + break; + } + else if(res == HASH_KEY_IS_STRING) { + if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) { + XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key, *pIter, depth++)); + } + } + + zend_hash_move_forward(val->value.ht); + } + } + break; + default: + break; + } + } + } + return xReturn; +} + +static XMLRPC_VALUE PHP_to_XMLRPC(pval* root_val) { + return PHP_to_XMLRPC_worker(NULL, root_val, 0); +} + +/* recursively convert xmlrpc values into php values */ +static pval* XMLRPC_to_PHP(XMLRPC_VALUE el) { + pval* elem = NULL; + char* pBuf; + const char* pStr; + + if(el) { + XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el); + + MAKE_STD_ZVAL(elem); /* init. very important. spent a frustrating day finding this out. */ + + switch(type) { + case xmlrpc_empty: + elem->type = IS_NULL; + break; + case xmlrpc_string: + pStr = XMLRPC_GetValueString(el); + if(pStr) { + elem->value.str.len = XMLRPC_GetValueStringLen(el); + elem->value.str.val = estrndup(pStr, elem->value.str.len); + elem->type = IS_STRING; + } + break; + case xmlrpc_int: + elem->value.lval = XMLRPC_GetValueInt(el); + elem->type = IS_LONG; + break; + case xmlrpc_boolean: + elem->value.lval = XMLRPC_GetValueBoolean(el); + elem->type = IS_BOOL; + break; + case xmlrpc_double: + elem->value.dval = XMLRPC_GetValueDouble(el); + elem->type = IS_DOUBLE; + break; + case xmlrpc_datetime: + elem->value.str.len = XMLRPC_GetValueStringLen(el); + elem->value.str.val = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), elem->value.str.len); + elem->type = IS_STRING; + break; + case xmlrpc_base64: + pStr = XMLRPC_GetValueBase64(el); + if(pStr) { + elem->value.str.len = XMLRPC_GetValueStringLen(el); + elem->value.str.val = estrndup(pStr, elem->value.str.len); + elem->type = IS_STRING; + } + break; + case xmlrpc_vector: + if(array_init(elem) == SUCCESS) { + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el); + + while( xIter ) { + pval* val = XMLRPC_to_PHP(xIter); + if(val) { + add_pval(elem, XMLRPC_GetValueID(xIter), &val); + } + xIter = XMLRPC_VectorNext(el); + } + } + break; + default: + break; + } + set_pval_xmlrpc_type(elem, type); + } + return elem; +} + +/* {{{ proto string xmlrpc_encode_request(string method, mixed params) + generate xml for a method request */ +PHP_FUNCTION(xmlrpc_encode_request) { + XMLRPC_REQUEST xRequest = NULL; + pval* method, *vals, *out_opts; + char* outBuf; + php_output_options out; + + if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || + getParameters(ht, ARG_COUNT(ht), &method, &vals, &out_opts) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + set_output_options(&out, (ARG_COUNT(ht) == 3) ? out_opts : 0); + + if(return_value_used) { + xRequest = XMLRPC_RequestNew(); + + if(xRequest) { + XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out); + if(method->type == IS_NULL) { + XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response); + } + else { + XMLRPC_RequestSetMethodName(xRequest, method->value.str.val); + XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call); + } + if(vals->type != IS_NULL) { + XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals)); + } + + outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0); + if(outBuf) { + RETVAL_STRING(outBuf, 1); + free(outBuf); + } + XMLRPC_RequestFree(xRequest, 1); + } + } +} + +/* {{{ proto string xmlrpc_encode(mixed value) + generate xml for a PHP value */ +PHP_FUNCTION(xmlrpc_encode) +{ + XMLRPC_VALUE xOut = NULL; + pval* arg1, *out_opts; + php_output_options out; + char* outBuf; + + if( !(ARG_COUNT(ht) == 1) || + getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + if( return_value_used ) { + /* convert native php type to xmlrpc type */ + xOut = PHP_to_XMLRPC(arg1); + + /* generate raw xml from xmlrpc data */ + outBuf = XMLRPC_VALUE_ToXML(xOut, 0); + + if(xOut) { + if(outBuf) { + RETVAL_STRING(outBuf, 1); + free(outBuf); + } + /* cleanup */ + XMLRPC_CleanupValue(xOut); + } + } +} + + +/* }}} */ + +pval* decode_request_worker(pval* xml_in, pval* encoding_in, pval* method_name_out) { + pval* retval = NULL; + XMLRPC_REQUEST response; + STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {0}; + opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in->value.str.val) : ENCODING_DEFAULT; + + /* generate XMLRPC_REQUEST from raw xml */ + response = XMLRPC_REQUEST_FromXML(xml_in->value.str.val, xml_in->value.str.len, &opts); + if(response) { + /* convert xmlrpc data to native php types */ + retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response)); + + if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) { + if(method_name_out) { + convert_to_string(method_name_out); + method_name_out->type = IS_STRING; + method_name_out->value.str.val = estrdup(XMLRPC_RequestGetMethodName(response)); + method_name_out->value.str.len = strlen(method_name_out->value.str.val); + } + } + + /* dust, sweep, and mop */ + XMLRPC_RequestFree(response, 1); + } + return retval; +} + +/* {{{ proto array xmlrpc_decode_request(string xml, string& method, [string encoding]) + decode xml into native php types */ +PHP_FUNCTION(xmlrpc_decode_request) +{ + pval* xml, *method, *encoding = NULL; + + if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &xml, &method, &encoding) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } +#if ZEND_MODULE_API_NO < 20010901 + if (!ParameterPassedByReference(ht,2)) { + zend_error(E_WARNING,"second argument to xmlrpc_decode_request() passed by value, expecting reference"); + } +#endif + + convert_to_string(xml); + convert_to_string(method); + if(ARG_COUNT(ht) == 3) { + convert_to_string(encoding); + } + + if(return_value_used) { + pval* retval = decode_request_worker(xml, encoding, method); + if(retval) { + *return_value = *retval; + zval_copy_ctor(return_value); + } + } +} +/* }}} */ + + +/* {{{ proto array xmlrpc_decode(string xml, [string encoding]) + decode xml into native php types */ +PHP_FUNCTION(xmlrpc_decode) +{ + pval* arg1, *arg2 = NULL; + + if( !(ARG_COUNT(ht) == 1 || ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg1, &arg2) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + convert_to_string(arg1); + if(ARG_COUNT(ht) == 2) { + convert_to_string(arg2); + } + + if(return_value_used) { + pval* retval = decode_request_worker(arg1, arg2, NULL); + if(retval) { + *return_value = *retval; + zval_copy_ctor(return_value); + } + } +} +/* }}} */ + + +/************************* +* server related methods * +*************************/ + +/* {{{ proto handle xmlrpc_server_create() + create an xmlrpc server */ +PHP_FUNCTION(xmlrpc_server_create) { + if(ARG_COUNT(ht) != 0) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + if(return_value_used) { + pval *method_map, *introspection_map; + MAKE_STD_ZVAL(method_map); + MAKE_STD_ZVAL(introspection_map); + + if(array_init(method_map) == SUCCESS && array_init(introspection_map) == SUCCESS) { + /* allocate server data. free'd in destroy_server_data() */ + xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data)); + + if(server) { + server->method_map = method_map; + server->introspection_map = introspection_map; + server->server_ptr = XMLRPC_ServerCreate(); + + XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback); + + /* store for later use */ + ZEND_REGISTER_RESOURCE(return_value,server, XMLRPCG(le_xmlrpc_server)); + } + } + } +} + +/* {{{ proto void xmlrpc_server_destroy(handle server) + destroy server resources */ +PHP_FUNCTION(xmlrpc_server_destroy) { + pval* arg1; + int bSuccess = FAILURE; + + if(ARG_COUNT(ht) != 1 || getParameters(ht, 1, &arg1) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + if(arg1->type == IS_RESOURCE) { + int type; + + xmlrpc_server_data *server = zend_list_find(arg1->value.lval, &type); + + if(server && type == XMLRPCG(le_xmlrpc_server)) { + bSuccess = zend_list_delete(arg1->value.lval); + + /* called by hashtable destructor + * destroy_server_data(server); + */ + } + } + RETVAL_LONG(bSuccess == SUCCESS); +} + + +/* called by xmlrpc C engine as method handler for all registered methods. + * it then calls the corresponding PHP function to handle the method. + */ +static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) { + pval *retval_ptr; + xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data; + pval* xmlrpc_params; + pval* callback_params[3]; + + /* convert xmlrpc to native php types */ + xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest)); + + /* setup data hoojum */ + callback_params[0] = pData->xmlrpc_method; + callback_params[1] = xmlrpc_params; + callback_params[2] = pData->caller_params; + + /* Use same C function for all methods */ + + /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */ + call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params); + + pData->php_executed = 1; +} + +/* called by the C server when it first receives an introspection request. We pass this on to + * our PHP listeners, if any + */ +static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) { + pval *retval_ptr, **php_function; + pval* callback_params[1]; + xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data; + + MAKE_STD_ZVAL(retval_ptr); + retval_ptr->type = IS_NULL; + + /* setup data hoojum */ + callback_params[0] = pData->caller_params; + + /* loop through and call all registered callbacks */ + zend_hash_internal_pointer_reset(pData->server->introspection_map->value.ht); + while(1) { + if(zend_hash_get_current_data(pData->server->introspection_map->value.ht, + (void**)&php_function) == SUCCESS) { + + /* php func prototype: function string user_func($user_params) */ + if(call_user_function(CG(function_table), NULL, *php_function, + retval_ptr, 1, callback_params) == SUCCESS) { + XMLRPC_VALUE xData; + STRUCT_XMLRPC_ERROR err = {0}; + + /* return value should be a string */ + convert_to_string(retval_ptr); + + xData = XMLRPC_IntrospectionCreateDescription(retval_ptr->value.str.val, &err); + + if(xData) { + if(!XMLRPC_ServerAddIntrospectionData(server, xData)) { + zend_error(E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", (*php_function)->value.str.val); + } + XMLRPC_CleanupValue(xData); + } + else { + /* could not create description */ + if(err.xml_elem_error.parser_code) { + zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to add introspection data returned from %s()", + err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, (*php_function)->value.str.val); + } + else { + zend_error(E_WARNING, "Unable to add introspection data returned from %s()", + (*php_function)->value.str.val); + } + } + } + else { + /* user func failed */ + zend_error(E_WARNING, "Error calling user introspection callback: %s()", (*php_function)->value.str.val); + } + } + else { + break; + } + + zend_hash_move_forward(pData->server->introspection_map->value.ht); + } + + /* so we don't call the same callbacks ever again */ + zend_hash_clean(pData->server->introspection_map->value.ht); +} + +/* {{{ proto boolean xmlrpc_server_register_method(handle server, string method_name, string function) + register a php function to handle method matching method_name */ +PHP_FUNCTION(xmlrpc_server_register_method) { + + pval* method_key, *method_name, *handle, *method_name_save; + int type; + xmlrpc_server_data* server; + + /* get some params. should be 3 */ + if( !(ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &handle, &method_key, &method_name) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + server = zend_list_find(handle->value.lval, &type); + + if(type == XMLRPCG(le_xmlrpc_server)) { + /* register with C engine. every method just calls our standard callback, + * and it then dispatches to php as necessary + */ + if(XMLRPC_ServerRegisterMethod(server->server_ptr, method_key->value.str.val, php_xmlrpc_callback)) { + /* save for later use */ + MAKE_STD_ZVAL(method_name_save); + *method_name_save = *method_name; + zval_copy_ctor(method_name_save); + + /* register our php method */ + add_pval(server->method_map, method_key->value.str.val, &method_name_save); + + RETURN_BOOL(1); + } + } + RETURN_BOOL(0); +} + + +/* {{{ proto boolean xmlrpc_server_register_introspection_callback(handle server, string function) + register a php function to generate documentation */ +PHP_FUNCTION(xmlrpc_server_register_introspection_callback) { + + pval* method_key, *method_name, *handle, *method_name_save; + int type; + xmlrpc_server_data* server; + + /* get some params. should be 2 */ + if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &method_name) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + server = zend_list_find(handle->value.lval, &type); + + if(type == XMLRPCG(le_xmlrpc_server)) { + { + /* save for later use */ + MAKE_STD_ZVAL(method_name_save); + *method_name_save = *method_name; + zval_copy_ctor(method_name_save); + + /* register our php method */ + add_pval(server->introspection_map, NULL, &method_name_save); + + RETURN_BOOL(1); + } + } + RETURN_BOOL(0); +} + + +/* this function is itchin for a re-write */ + +/* {{{ proto mixed xmlrpc_server_call_method(handle server, string xml, mixed user_data, [array output_options]) + parse xml request and call method */ +PHP_FUNCTION(xmlrpc_server_call_method) { + xmlrpc_callback_data data = {0}; + XMLRPC_REQUEST xRequest; + STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts; + xmlrpc_server_data* server; + pval *rawxml, *caller_params, *handle, *output_opts; + int type; + php_output_options out; + + /* get params. 3 or 4 params ok */ + if(ARG_COUNT(ht) == 4) { + if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params, &output_opts) != SUCCESS) { + WRONG_PARAM_COUNT; + } + + /* user output options */ + set_output_options(&out, output_opts); + } + else if(ARG_COUNT(ht) == 3) { + if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params) != SUCCESS) { + WRONG_PARAM_COUNT; + } + /* user output options */ + set_output_options(&out, NULL); + } + else { + WRONG_PARAM_COUNT; + } + + server = zend_list_find(handle->value.lval, &type); + + if(type == XMLRPCG(le_xmlrpc_server)) { + /* HACK: use output encoding for now */ + input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding); + + /* generate an XMLRPC_REQUEST from the raw xml input */ + xRequest = XMLRPC_REQUEST_FromXML(rawxml->value.str.val, rawxml->value.str.len, &input_opts); + + if(xRequest) { + + /* check if we have a method name -- indicating success and all manner of good things */ + if(XMLRPC_RequestGetMethodName(xRequest)) { + pval** php_function, *returned = NULL; + XMLRPC_VALUE xAnswer = NULL; + MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */ + MAKE_STD_ZVAL(data.return_data); + data.return_data->type = IS_NULL; /* in case value is never init'd, we don't dtor to think it is a string or something */ + data.xmlrpc_method->type = IS_NULL; + + /* setup some data to pass to the callback function */ + data.xmlrpc_method->value.str.val = estrdup(XMLRPC_RequestGetMethodName(xRequest)); + data.xmlrpc_method->value.str.len = strlen(data.xmlrpc_method->value.str.val); + data.xmlrpc_method->type = IS_STRING; + data.caller_params = caller_params; + data.php_executed = 0; + data.server = server; + + /* check if the called method has been previous registered */ + if(zend_hash_find(server->method_map->value.ht, + data.xmlrpc_method->value.str.val, + data.xmlrpc_method->value.str.len + 1, + (void**)&php_function) == SUCCESS) { + + data.php_function = *php_function; + } + + /* We could just call the php method directly ourselves at this point, but we do this + * with a C callback in case the xmlrpc library ever implements some cool usage stats, + * or somesuch. + */ + xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data); + if(xAnswer) { + if(out.b_php_out) { + zval_dtor(data.return_data); + FREE_ZVAL(data.return_data); + data.return_data = XMLRPC_to_PHP(xAnswer); + } + } + else if(data.php_executed) { + if(!out.b_php_out) { + xAnswer = PHP_to_XMLRPC(data.return_data); + } + } + + /* should we return data as xml? */ + if(!out.b_php_out) { + XMLRPC_REQUEST xResponse = XMLRPC_RequestNew(); + if(xResponse) { + char* outBuf = 0; + int buf_len = 0; + + /* set some required request hoojum */ + XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out); + XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response); + XMLRPC_RequestSetData(xResponse, xAnswer); + + /* generate xml */ + outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len); + if(outBuf) { + RETVAL_STRINGL(outBuf, buf_len, 1); + free(outBuf); + } + /* cleanup after ourselves. what a sty! */ + XMLRPC_RequestFree(xResponse, 0); + } + } + /* or as native php types? */ + else { + *return_value = *data.return_data; + zval_copy_ctor(return_value); + } + + /* cleanup after ourselves. what a sty! */ + zval_dtor(data.xmlrpc_method); + FREE_ZVAL(data.xmlrpc_method); + zval_dtor(data.return_data); + FREE_ZVAL(data.return_data); + + if(xAnswer) { + XMLRPC_CleanupValue(xAnswer); + } + } + + XMLRPC_RequestFree(xRequest, 1); + } + } +} + + +/* {{{ proto int xmlrpc_server_add_introspection_data(handle server, array desc) + add introspection documentation */ +PHP_FUNCTION(xmlrpc_server_add_introspection_data) { + + pval *method, *handle, *desc; + int type; + xmlrpc_server_data* server; + + /* get some params. should be 2 */ + if ( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &desc) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + server = zend_list_find(handle->value.lval, &type); + + if (type == XMLRPCG(le_xmlrpc_server)) { + XMLRPC_VALUE xDesc = PHP_to_XMLRPC(desc); + if (xDesc) { + int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc); + XMLRPC_CleanupValue(xDesc); + RETURN_LONG(retval); + } + } + RETURN_LONG(0); +} + + +/* {{{ proto array xmlrpc_parse_method_descriptions(string xml) + decode xml into a list of method descriptions */ +PHP_FUNCTION(xmlrpc_parse_method_descriptions) +{ + pval* arg1, *arg2, *retval; + + if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + convert_to_string(arg1); + + if(return_value_used) { + STRUCT_XMLRPC_ERROR err = {0}; + XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1->value.str.val, &err); + if(xVal) { + retval = XMLRPC_to_PHP(xVal); + + if(retval) { + *return_value = *retval; + zval_copy_ctor(return_value); + } + /* dust, sweep, and mop */ + XMLRPC_CleanupValue(xVal); + } + else { + /* could not create description */ + if(err.xml_elem_error.parser_code) { + zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to create introspection data", + err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error); + } + else { + zend_error(E_WARNING, "Invalid xml structure. Unable to create introspection data"); + } + + zend_error(E_WARNING, "xml parse error. no method description created"); + } + } +} + + +/************ +* type data * +************/ + +#define XMLRPC_TYPE_COUNT 9 +#define XMLRPC_VECTOR_TYPE_COUNT 4 +#define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT) + +/* return a string matching a given xmlrpc type */ +static const char** get_type_str_mapping() { + static const char* str_mapping[TYPE_STR_MAP_SIZE]; + static int first = 1; + if(first) { + /* warning. do not add/delete without changing size define */ + str_mapping[xmlrpc_none] = "none"; + str_mapping[xmlrpc_empty] = "empty"; + str_mapping[xmlrpc_base64] = "base64"; + str_mapping[xmlrpc_boolean] = "boolean"; + str_mapping[xmlrpc_datetime] = "datetime"; + str_mapping[xmlrpc_double] = "double"; + str_mapping[xmlrpc_int] = "int"; + str_mapping[xmlrpc_string] = "string"; + str_mapping[xmlrpc_vector] = "vector"; + str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none"; + str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array"; + str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed"; + str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct"; + first = 0; + } + return (const char**)str_mapping; +} + +/* map an xmlrpc type to a string */ +const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) { + const char** str_mapping = get_type_str_mapping(); + if(vtype == xmlrpc_vector_none) { + return str_mapping[type]; + } + else { + return str_mapping[XMLRPC_TYPE_COUNT + vtype]; + } +} + +/* map a string to an xmlrpc type */ +XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) { + const char** str_mapping = get_type_str_mapping(); + int i; + + if(str) { + for(i = 0; i < XMLRPC_TYPE_COUNT; i++) { + if(!strcmp(str_mapping[i], str)) { + return (XMLRPC_VALUE_TYPE)i; + } + } + } + return xmlrpc_none; +} + +/* map a string to an xmlrpc vector type */ +XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) { + const char** str_mapping = get_type_str_mapping(); + int i; + + if(str) { + for(i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) { + if(!strcmp(str_mapping[i], str)) { + return (XMLRPC_VECTOR_TYPE)(i - XMLRPC_TYPE_COUNT); + } + } + } + return xmlrpc_none; +} + + +/* set a given value to a particular type. + * note: this only works on strings, and only for date and base64, + * which do not have native php types. black magic lies herein. + */ +int set_pval_xmlrpc_type(pval* value, XMLRPC_VALUE_TYPE type) { + int bSuccess = FAILURE; + + /* we only really care about strings because they can represent + * base64 and datetime. all other types have corresponding php types + */ + if(value->type == IS_STRING) { + if(type == xmlrpc_base64 || type == xmlrpc_datetime) { + const char* typestr = xmlrpc_type_as_str(type, xmlrpc_vector_none); + pval* type; + + MAKE_STD_ZVAL(type); + + type->type = IS_STRING; + type->value.str.val = estrdup(typestr); + type->value.str.len = strlen(typestr); + + convert_to_object(value); + bSuccess = zend_hash_update(value->value.obj.properties, OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL); + } + } + + return bSuccess; +} + +/* return xmlrpc type of a php value */ +XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue) { + XMLRPC_VALUE_TYPE type = xmlrpc_none; + + if(value) { + switch(value->type) { + case IS_NULL: + type = xmlrpc_base64; + break; + #ifndef BOOL_AS_LONG + + /* Right thing to do, but it breaks some legacy code. */ + case IS_BOOL: + type = xmlrpc_boolean; + break; + #else + case IS_BOOL: + #endif + case IS_LONG: + case IS_RESOURCE: + type = xmlrpc_int; + break; + case IS_DOUBLE: + type = xmlrpc_double; + break; + case IS_CONSTANT: + type = xmlrpc_string; + break; + case IS_STRING: + type = xmlrpc_string; + break; + case IS_ARRAY: + case IS_CONSTANT_ARRAY: + type = xmlrpc_vector; + break; + case IS_OBJECT: + { + pval** attr; + type = xmlrpc_vector; + + if(zend_hash_find(value->value.obj.properties, + OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), + (void**)&attr) == SUCCESS) { + if((*attr)->type == IS_STRING) { + type = xmlrpc_str_as_type((*attr)->value.str.val); + } + } + break; + } + } + + /* if requested, return an unmolested (magic removed) copy of the value */ + if(newvalue) { + pval** val; + if( (type == xmlrpc_base64 && value->type != IS_NULL) || type == xmlrpc_datetime) { + if(zend_hash_find(value->value.obj.properties, + OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), + (void**)&val) == SUCCESS) { + *newvalue = *val; + } + } + else { + *newvalue = value; + } + } + } + + return type; +} + + +/* {{{ proto bool xmlrpc_set_type(string value, string type) + set xmlrpc type, base64 or datetime, for a php string value */ +PHP_FUNCTION(xmlrpc_set_type) { + pval* arg, *type; + XMLRPC_VALUE_TYPE vtype; + + if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg, &type) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } +#if ZEND_MODULE_API_NO < 20010901 + if (!ParameterPassedByReference(ht,1)) { + zend_error(E_WARNING,"first argument to xmlrpc_set_type() passed by value, expecting reference"); + } +#endif + + convert_to_string(type); + vtype = xmlrpc_str_as_type(type->value.str.val); + if(vtype != xmlrpc_none) { + if(set_pval_xmlrpc_type(arg, vtype) == SUCCESS) { + RETURN_TRUE; + } + } + else { + zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type->value.str.val); + } + RETURN_FALSE; +} + +/* {{{ proto string xmlrpc_get_type(mixed value) + get xmlrpc type for a php value. especially useful for base64 and datetime strings */ +PHP_FUNCTION(xmlrpc_get_type) { + pval* arg; + XMLRPC_VALUE_TYPE type; + XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none; + + if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg) == FAILURE) { + WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ + } + + type = get_pval_xmlrpc_type(arg, 0); + if(type == xmlrpc_vector) { + vtype = determine_vector_type(arg->value.ht); + } + + RETURN_STRING((char*)xmlrpc_type_as_str(type, vtype), 1); +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + |