minixmlrpc000755 000423 000000 00000000000 11020634120 013404 5ustar00luigiwheel000000 000000 minixmlrpc/minixmlrpc000755 000423 000000 00000000000 11020634120 015641 2.ustar00luigiwheel000000 000000 minixmlrpc/dynstring.c000644 000423 000000 00000011703 11020630566 015664 0ustar00luigiwheel000000 000000 /* * $Id: dynstring.c 351 2008-06-01 23:38:18Z luigi $ * * An implementation of dynamic strings (and in general, extensible * data structures) inherited from the one i wrote myself for asterisk. */ #include "dynstring.h" #include /* vsnprintf */ #include /* varargs */ #include #include /* bcopy */ #include struct __dynstr { size_t len; /* The current size of the buffer */ size_t used; /* Amount of space used */ char str[0]; /* The string buffer */ }; const char *ds_data(dynstr s) { return s ? s->str : ""; } void ds_free(dynstr s) { if (s) free(s); } int ds_len(dynstr s) { return s ? s->used : 0; } static dynstr dynstr_create(size_t init_len) { dynstr buf; buf = (dynstr)calloc(1, sizeof(*buf) + init_len); if (buf == NULL) return NULL; buf->len = init_len; buf->used = 0; return buf; } void ds_reset(dynstr buf) { if (buf) { buf->used = 0; if (buf->len) buf->str[0] = '\0'; } } static int dynstr_make_space(dynstr *buf, size_t new_len) { if (buf == NULL) return 0; if (new_len <= (*buf)->len) return 0; /* success */ *buf = (dynstr)realloc(*buf, new_len + sizeof(struct __dynstr)); if (*buf == NULL) /* XXX watch out, we leak memory here */ return -1; (*buf)->len = new_len; return 0; } static int __dynstr_helper(dynstr *buf, size_t max_len, int append, const char *fmt, va_list ap); #define DYNSTR_BUILD_RETRY -2 #define DYNSTR_BUILD_FAILED -3 /* * Append to a dynamic string using a va_list */ #define vadsprintf(buf, max_len, fmt, ap) \ ({ \ int __res; \ while ((__res = __dynstr_helper(buf, max_len, \ 1, fmt, ap)) == DYNSTR_BUILD_RETRY) { \ va_end(ap); \ va_start(ap, fmt); \ } \ (__res); \ }) /*! * Append to a dynamic string - same as sprintf(). */ int __attribute__ ((format (printf, 2, 3))) dsprintf(dynstr *buf, const char *fmt, ...) { int res; va_list ap; if (buf == NULL) return 0; va_start(ap, fmt); res = vadsprintf(buf, 0 /* max_len */, fmt, ap); va_end(ap); return res; } /* * Append a buffer to a dynamic string (and also a '\0' to ease printing). */ int ds_append(dynstr *buf, const void *d, int len) { int need; if (buf == NULL) return 0; if (*buf == NULL) *buf = dynstr_create(48); /* initial size */ if (*buf == NULL) return DYNSTR_BUILD_FAILED; need = (*buf)->used + len + 1; if (need > (*buf)->len) { if (dynstr_make_space(buf, need)) return DYNSTR_BUILD_FAILED; } bcopy(d, (*buf)->str + (*buf)->used, len); (*buf)->used += len; (*buf)->str[(*buf)->used] = '\0'; return 0; } __attribute__((format (printf, 4, 0))) static int __dynstr_helper(dynstr *buf, size_t max_len, int append, const char *fmt, va_list ap) { int res, need; int offset; if (buf == NULL) return 0; if (*buf == NULL) *buf = dynstr_create(10); if (*buf == NULL) return DYNSTR_BUILD_FAILED; offset = (append && (*buf)->len) ? (*buf)->used : 0; if (max_len < 0) max_len = (*buf)->len; /* don't exceed the allocated space */ /* * Ask vsnprintf how much space we need. Remember that vsnprintf * does not count the final '\0' so we must add 1. */ res = vsnprintf((*buf)->str + offset, (*buf)->len - offset, fmt, ap); need = res + offset + 1; /* * If there is not enough space and we are below the max length, * reallocate the buffer and return a message telling to retry. */ if (need > (*buf)->len && (max_len == 0 || (*buf)->len < max_len) ) { if (max_len && max_len < need) /* truncate as needed */ need = max_len; else if (max_len == 0) /* if unbounded, give more room for next time */ need += 16 + need/4; if (dynstr_make_space(buf, need)) return DYNSTR_BUILD_FAILED; (*buf)->str[offset] = '\0'; /* Truncate the partial write. */ /* va_end() and va_start() must be done before calling * vsnprintf() again. */ return DYNSTR_BUILD_RETRY; } /* update space used, keep in mind the truncation */ (*buf)->used = (res + offset > (*buf)->len) ? (*buf)->len : res + offset; return res; } minixmlrpc/sslsock.c000644 000423 000000 00000014050 11020630205 015310 0ustar00luigiwheel000000 000000 /* sslsock.c ** Routines to use ssl sockets ** Originally based on a simple version by Al Globus . ** Debugged and prettified by Jef Poskanzer . Also includes ** ifdefs to handle https via OpenSSL. */ #include "myhdr.h" /* Some code to open a tcp or ssl socket */ #ifdef USE_SSL #if defined(USE_XYSSL) /* stubs for non-ssl */ #include "xyssl/net.h" #include "xyssl/ssl.h" #include "xyssl/havege.h" typedef ssl_context SSL_CTX; typedef ssl_session SSL; #else /* regular openssl */ #include #endif #else /* stubs for non-ssl */ typedef struct _a {} SSL_CTX; typedef struct _b {} SSL; #endif /* a socket descriptor with room for ssl state */ struct my_sock { int sockfd; SSL_CTX *ssl_ctx; SSL *ssl; }; void safe_close(struct my_sock *fd) { #ifdef USE_SSL if ( fd->ssl ) { #ifdef USE_XYSSL ssl_close_notify( fd->ssl_ctx ); close(fd->sockfd); ssl_free( fd->ssl_ctx ); #else SSL_free( fd->ssl ); SSL_CTX_free( fd->ssl_ctx ); #endif } #endif close( fd->sockfd ); bzero(fd, sizeof(*fd)); free(fd); } int safe_write(struct my_sock *fd, const char *buf, int bytes) { int i; while (bytes > 0) { #ifdef USE_SSL if (fd->ssl) { #ifdef USE_XYSSL while( ( i = ssl_write( fd->ssl_ctx, (char *)buf, bytes ) ) <= 0 ) { if ( i != XYSSL_ERR_NET_TRY_AGAIN ) break; } #else i = SSL_write( fd->ssl, buf, bytes); #endif } else #endif i = write( fd->sockfd, buf, bytes ); if (i <= 0) return -1; buf += i; bytes -= i; } return 0; } int safe_read(struct my_sock *fd, char *buf, int len) { #ifdef USE_SSL if ( fd->ssl ) { #ifdef USE_XYSSL int i; do { i = ssl_read( fd->ssl_ctx, buf, len ); if (i == XYSSL_ERR_NET_TRY_AGAIN ) continue; } while (0); return i > 0 ? i : 0 ; #else return SSL_read( fd->ssl, buf, len); #endif } else #endif return read( fd->sockfd, buf, len); } #if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED) #define USE_IPV6 #endif struct my_sock * open_client_socket( char* hostname, unsigned short port, int do_ssl ) { #ifdef USE_IPV6 struct addrinfo hints; char portstr[10]; int gaierr; struct addrinfo* ai; struct addrinfo* ai2; struct addrinfo* aiv4; struct addrinfo* aiv6; struct sockaddr_in6 sa; #else /* USE_IPV6 */ struct hostent *he; struct sockaddr_in sa; #endif /* USE_IPV6 */ int sa_len, sock_family, sock_type, sock_protocol; struct my_sock *fd = calloc(1, sizeof(*fd)); if (fd == NULL) return NULL; memset( (void*) &sa, 0, sizeof(sa) ); //fprintf(stderr, "host %s port %d %s\n", hostname, port, do_ssl ? "SSL":""); #ifdef USE_IPV6 memset( &hints, 0, sizeof(hints) ); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; snprintf( portstr, sizeof(portstr), "%d", (int) port ); if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 ) { fprintf( stderr, "getaddrinfo %s - %s\n", hostname, gai_strerror( gaierr ) ); exit( 1 ); } /* Find the first IPv4 and IPv6 entries. */ aiv4 = NULL; aiv6 = NULL; for ( ai2 = ai; ai2 != NULL; ai2 = ai2->ai_next ) { if ( ai2->ai_family == AF_INET && !aiv4 ) aiv4 = ai2; else if ( ai2->ai_family == AF_INET6 && !aiv6 ) aiv6 = ai2; } /* If there's an IPv4 address, use that, otherwise try IPv6. */ if ( aiv4 != NULL ) { if ( sizeof(sa) < aiv4->ai_addrlen ) { fprintf( stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname, (unsigned long) sizeof(sa), (unsigned long) aiv4->ai_addrlen ); exit( 1 ); } sock_family = aiv4->ai_family; sock_type = aiv4->ai_socktype; sock_protocol = aiv4->ai_protocol; sa_len = aiv4->ai_addrlen; memmove( &sa, aiv4->ai_addr, sa_len ); } else if ( aiv6 != NULL) { if ( sizeof(sa) < aiv6->ai_addrlen ) { fprintf( stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname, (unsigned long) sizeof(sa), (unsigned long) aiv6->ai_addrlen ); exit( 1 ); } sock_family = aiv6->ai_family; sock_type = aiv6->ai_socktype; sock_protocol = aiv6->ai_protocol; sa_len = aiv6->ai_addrlen; memmove( &sa, aiv6->ai_addr, sa_len ); } else { fprintf( stderr, "no valid address found for host %s\n", hostname ); exit( 1 ); } freeaddrinfo( ai ); #else /* USE_IPV6 */ he = gethostbyname( hostname ); if ( he == NULL ) { fprintf( stderr, "%s: unknown host - %s\n", argv0, hostname ); exit( 1 ); } sock_family = sa.sin_family = he->h_addrtype; sock_type = SOCK_STREAM; sock_protocol = 0; sa_len = sizeof(sa); memmove( &sa.sin_addr, he->h_addr, he->h_length ); sa.sin_port = htons( port ); #endif /* USE_IPV6 */ fd->sockfd = socket( sock_family, sock_type, sock_protocol ); if ( fd->sockfd < 0 ) perror( "socket" ); if ( connect( fd->sockfd, (struct sockaddr*) &sa, sa_len ) < 0 ) perror( "connect" ); #ifdef USE_SSL if ( do_ssl ) { #ifdef USE_XYSSL int ret; havege_state *hs = calloc(1, sizeof(*hs)); fd->ssl = calloc(1, sizeof (*fd->ssl)); fd->ssl_ctx = calloc(1, sizeof (*fd->ssl_ctx)); if (!hs || !fd->ssl || !fd->ssl_ctx) { fprintf(stderr, "cannot allocate xyssl context\n"); exit(1); } havege_init( hs ); ret = ssl_init(fd->ssl_ctx); if (ret) { perror("ssl_init"); exit(1); } ssl_set_endpoint( fd->ssl_ctx, SSL_IS_CLIENT ); ssl_set_authmode( fd->ssl_ctx, SSL_VERIFY_NONE ); ssl_set_rng( fd->ssl_ctx, havege_rand, hs ); //ssl_set_dbg( &ssl, my_debug, stdout ); ssl_set_bio( fd->ssl_ctx, net_recv, &fd->sockfd, net_send, &fd->sockfd ); ssl_set_ciphers( fd->ssl_ctx, ssl_default_ciphers ); ssl_set_session( fd->ssl_ctx, 1, 600, fd->ssl ); #else /* Make SSL connection. */ int r; SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); fd->ssl_ctx = SSL_CTX_new( SSLv23_client_method() ); if (fd->ssl_ctx == NULL) perror("ssl_ctx"); fd->ssl = SSL_new( fd->ssl_ctx ); if (fd->ssl == NULL) perror("ssl"); SSL_set_fd( fd->ssl, fd->sockfd ); r = SSL_connect( fd->ssl ); if ( r <= 0 ) { fprintf(stderr, "%s:%d SSL connection failed - %d\n", hostname, port, r ); exit( 1 ); } #endif } #endif return fd; } minixmlrpc/myhdr.h000644 000423 000000 00000001537 11020240052 014762 0ustar00luigiwheel000000 000000 /* headers for http_get and friends ** ** Originally based on a simple version by Al Globus . ** Debugged and prettified by Jef Poskanzer . Also includes ** ifdefs to handle https via OpenSSL. */ #include #include #include #include #include #include #include #include #include #include // va_arg #include // free void usage(); void show_error( char* cause ); #include "dynstring.h" /* ssl socket support */ struct my_sock; struct my_sock *open_client_socket( char* hostname, unsigned short port, int do_ssl ); void safe_close(struct my_sock *fd); int safe_write(struct my_sock *fd, const char *buf, int bytes); int safe_read(struct my_sock *fd, char *buf, int len); #include "minixml.h" minixmlrpc/Makefile000644 000423 000000 00000003026 11020631167 015134 0ustar00luigiwheel000000 000000 # Makefile for xmlrpc # CONFIGURE: If you are using a SystemV-based operating system, such as # Solaris, you will need to uncomment this definition. #SYSV_LIBS = -lnsl -lsocket # CONFIGURE: If you want to compile in support for https, uncomment these # definitions. You will need to have already built OpenSSL, available at # http://www.openssl.org/ Make sure the SSL_TREE definition points to the # tree with your OpenSSL installation - depending on how you installed it, # it may be in /usr/local instead of /usr/local/ssl. #SSL_TREE = /usr/local/ssl SSL_TREE = /usr/local SSL_DEFS = -DUSE_SSL SSL_INC = -I$(SSL_TREE)/include SSL_LIBS = -L$(SSL_TREE)/lib -lssl -lcrypto STATIC_LIBS= ifneq ($(XYSSL),) # XYSSL support XYSSL_TREE?= /usr/ports/security/xyssl/work/xyssl-0.9 SSL_DEFS += -DUSE_XYSSL SSL_INC += -I$(XYSSL_TREE)/include SSL_LIBS = #-L$(XYSSL_TREE)/library -lxyssl STATIC_LIBS += $(XYSSL_TREE)/library/libxyssl.a endif CFLAGS = -Wall -Werror -O $(SSL_DEFS) $(SSL_INC) LDFLAGS = -s $(SSL_LIBS) $(SYSV_LIBS) #LDFLAGS = -g $(SSL_LIBS) $(SYSV_LIBS) all: xmlrpc SRCS= xmlrpc.c sslsock.c dynstring.c minixml.c SRCS+= myhdr.h minixml.h dynstring.h SRCS+= Makefile xmlrpc.1 README OBJS= xmlrpc.o sslsock.o dynstring.o minixml.o xmlrpc: $(OBJS) $(CC) $(LDFLAGS) -o xmlrpc $(OBJS) $(STATIC_LIBS) $(OBJS): myhdr.h dynstring.h minixml.h clean: rm -f xmlrpc *.o core core.* *.core *.tgz minixmlrpc tgz: $(SRCS) clean - rm minixmlrpc ln -s . minixmlrpc tar cHvzf xmlrpc.tgz --exclude .svn --exclude test\* --exclude \*.tgz minixmlrpc minixmlrpc/README000644 000423 000000 00000002261 11020631511 014345 0ustar00luigiwheel000000 000000 xmlrpc - simple xmlrpc client and object manipulator (parts derived from http_get, Jef Poskanzer, jef@mail.acme.com http://www.acme.com/jef/ ) -- SYNTAX -- ./xmlrpc url argument [filters] Calls the XMLRPC server at url with the specified argument, and prints the results (possibly after applying the filters). Supported url are http and https (or '-' which is special and tells the code to apply the filter to the argument itself and not to the result). Arguments can be any composition of integer 123 strings "abc foo bar" arrays [ "one", 2, "tree" ] structs { foo=1, bar=2 } functions f(x, y, z) and all nesting of them. The filters can extract individual elements of the reply, print it in XML or functional form, etc. See minixml.h and dynstring.h for details. -- BUILD REQUIREMENTS -- ssl support uses openssl by default. If you are looking for a small libssl library, you can use XYSSL (http://xyssl.org/), which is also supported. Build it as 'gmake XYSSL=1 XYSSL_TREE=...' providing a path for the XYSSL tree. EXAMPLES URL=http://xmlrpc-c.sourceforge.net/api/sample.php ./xmlrpc $URL 'sample.sumAndDifference(10,20)' '' '[].keys()' '[][sum]' minixmlrpc/minixml.c000644 000423 000000 00000052347 11020614713 015325 0ustar00luigiwheel000000 000000 /* * mini xml parse/encode/decode for xmlrpc * $Id: minixml.c 350 2008-06-01 21:57:20Z luigi $ * * This librA small set a functions that takes a string that resembles * a scripting language, and encodes it in XML. * Basic data types are int, string (in quotes), * array [ a, b, c, ...] and structs { a=one, b=two, ... }. * Things can be nested. Finally, you can call functions * foo(a, b, c) The encode routine also returns a binary description like this: */ #include "myhdr.h" #include /* helper function to remove blanks */ static const char *skip_blanks(const char *s) { if (s) { while (*s && index(" \t\r\n", *s)) s++; } return s; } int obj_getlen(const struct __obj *o) { if (!o) return 0; switch (o->type) { case TY_ARRAY: return o->v.a[0].len; case TY_MAP: return o->v.m[0].len; case TY_FUNCTION: return o->v.f[0].len; default: return 1; } } const struct __obj *obj_get(const struct __obj *o, uint32_t i, const char **key) { if (!o) return NULL; switch (o->type) { default: return o; case TY_ARRAY: return (i < o->v.a[0].len) ? o->v.a[0].d[i] : NULL; case TY_MAP: if (i >= o->v.m[0].len) return NULL; if (key) *key = o->v.m[0].d[i].key; return o->v.m[0].d[i].value; case TY_FUNCTION: return (i < o->v.f[0].len) ? o->v.f[0].arg[i] : NULL; case TY_MAP_ENTRY: if (key) *key = o->v.me[0].key_value; return o->v.me[0].value; } } const struct __obj *obj_getfield(const struct __obj *o, const char *key, int keylen) { int i; if (!o || o->type != TY_MAP || !key || !*key) return NULL; if (keylen == 0) keylen = strlen(key); for (i=0; i < o->v.m[0].len; i++) if (!strncmp(key, o->v.m[0].d[i].key, keylen) && strlen(o->v.m[0].d[i].key) == keylen) return o->v.m[0].d[i].value; return NULL; } /* * print an object. 0 = functional, 1 = xml, 2 = debugging */ const char *obj_print(dynstr *dst, const struct __obj *o, enum expr_fmt mode) { char *sep = ""; int i; enum expr_fmt mode_in = mode; if (dst == NULL) return NULL; if (!o) return ds_data(*dst); if (mode == FMT_FLAT) mode = FMT_FUNC; switch (mode) { case FMT_FLAT: /* functional form */ case FMT_FUNC: /* functional form */ switch (o->type) { case TY_INT: dsprintf(dst, "%d", o->v.i[0]); break; case TY_DOUBLE: dsprintf(dst, "%f", o->v.d[0]); break; case TY_STRING: dsprintf(dst, "\"%s\"", o->v.s); break; case TY_FUNCTION: i = o->v.f[0].len; dsprintf(dst, "%s(", (const char *)&(o->v.f[0].arg[i])); for (i=0; i < o->v.f[0].len; i++) { dsprintf(dst, "%s", sep); sep = ","; obj_print(dst, o->v.f[0].arg[i], mode); } dsprintf(dst, ")"); break; case TY_ARRAY: if (mode_in == FMT_FUNC) dsprintf(dst, "["); for (i=0; i < o->v.a[0].len; i++) { dsprintf(dst, "%s", sep); sep = (mode_in == FMT_FUNC) ? "," : " "; obj_print(dst, o->v.a[0].d[i], mode); } if (mode_in == FMT_FUNC) dsprintf(dst, "]"); break; case TY_MAP: if (mode_in == FMT_FUNC) dsprintf(dst, "{"); for (i=0; i < o->v.m[0].len; i++) { dsprintf(dst, "%s%s=", sep, o->v.m[0].d[i].key); sep = (mode_in == FMT_FUNC) ? "," : " "; obj_print(dst, o->v.m[0].d[i].value, mode); } if (mode_in == FMT_FUNC) dsprintf(dst, "}"); break; case TY_MAP_ENTRY: dsprintf(dst, "%s=", o->v.me[0].key); obj_print(dst, o->v.me[0].value, mode); } break; case FMT_XML: /* XML */ switch (o->type) { case TY_INT: dsprintf(dst, "%d", o->v.i[0]); break; case TY_DOUBLE: dsprintf(dst, "%f", o->v.d[0]); break; case TY_STRING: dsprintf(dst, "%s", o->v.s); break; case TY_FUNCTION: i = o->v.f[0].len; dsprintf(dst, "\n" "%s\n\n", (const char *)&(o->v.f[0].arg[i])); for (i=0; i < o->v.f[0].len; i++) { dsprintf(dst, "\n"); obj_print(dst, o->v.f[0].arg[i], mode); dsprintf(dst, "\n\n"); } dsprintf(dst, "\n\n"); break; case TY_ARRAY: dsprintf(dst, "\n"); for (i=0; i < o->v.a[0].len; i++) { dsprintf(dst, ""); obj_print(dst, o->v.a[0].d[i], mode); dsprintf(dst, "\n"); } dsprintf(dst, "\n"); break; case TY_MAP: dsprintf(dst, "\n"); for (i=0; i < o->v.m[0].len; i++) { dsprintf(dst, "\n%s\n", o->v.m[0].d[i].key); obj_print(dst, o->v.m[0].d[i].value, mode); dsprintf(dst, "\n\n"); } dsprintf(dst, "\n"); break; case TY_MAP_ENTRY: dsprintf(dst, "%s=", o->v.me[0].key); obj_print(dst, o->v.me[0].value, mode); } break; #if 0 case 2: /* debugging */ switch (o->type) { case TY_INT: dsprintf(dst, "integer: %d\n", o->v.i[0]); break; case TY_DOUBLE: dsprintf(dst, "double: %f\n", o->v.d[0]); break; case TY_STRING: dsprintf(dst, "string len %d [%s]\n", strlen(o->v.s), o->v.s); break; case TY_FUNCTION: i = o->v.f[0].len; dsprintf(dst, "function: [%s] (%d args)\n", (const char *)&(o->v.f[0].arg[i]), i); for (i=0; i < o->v.f[0].len; i++) { dsprintf(dst, "argument %d: ", i); obj_print(dst, o->v.f[0].arg[i], mode); dsprintf(dst, "end argument %d: ", i); } dsprintf(dst, "end function: [%s]\n", (const char *)&(o->v.f[0].arg[i])); break; case TY_ARRAY: dsprintf(dst, "array: %d entries\n", o->v.a[0].len); for (i=0; i < o->v.a[0].len; i++) { dsprintf(dst, "entry %d: ", i); obj_print(dst, o->v.a[0].d[i], mode); dsprintf(dst, "end entry %d: ", i); } dsprintf(dst, "end array\n"); break; case TY_MAP: dsprintf(dst, "map: %d entries\n", o->v.m[0].len); for (i=0; i < o->v.m[0].len; i++) { dsprintf(dst, "member %d [%s]:", i, o->v.m[0].d[i].key); obj_print(dst, o->v.m[0].d[i].value, mode); dsprintf(dst, "end member %d\n", i); } dsprintf(dst, "end map\n"); break; case TY_MAP_ENTRY: dsprintf(dst, "map entry: [%s] : ", o->v.me[0].key); obj_print(dst, o->v.me[0].value, mode); dsprintf(dst, "end map entry: [%s] : ", o->v.me[0].key); } break; #endif /* case 2 */ } return ds_data(*dst); } /* * allocation and free routines for objects. * The allocator takes a size and optionally an initializer. * The free routine does a recursive free. */ struct __obj *obj_free(struct __obj *obj) { static char *obj_names[TY_FUNCTION+1] = { "TY_INT", "TY_DOUBLE", "TY_STRING", "TY_ARRAY", "TY_MAP", "TY_MAP_ENTRY", "TY_FUNCTION" }; char *ss = (obj && obj->type <= TY_FUNCTION ? obj_names[obj->type] : "??"); int i; if (0) fprintf(stderr, "%s %p ty %d %s\n", __FUNCTION__, obj, obj ? obj->type : -1, ss); if (obj == NULL) return NULL; switch (obj->type) { case TY_INT: break; // nothing special, all data in one chunk case TY_DOUBLE: break; // nothing special, all data in one chunk case TY_STRING: //fprintf(stderr, "%s string value [%s]\n", __FUNCTION__, obj->v.s); break; // nothing special, all data in one chunk case TY_MAP_ENTRY: { struct __map_entry *me = obj->v.me; if (0) fprintf(stderr, "%s map entry for [%s]: %p\n", __FUNCTION__, me->key_value, me->value); me->value = obj_free(me->value); if (me->key && me->key != me->key_value) free(me->key); me->key = NULL; } break; case TY_FUNCTION: for (i = 0; i < obj->v.f[0].len; i++) { // fprintf(stderr, "argument %d %p\n", i, obj->v.f[0].arg[i]); obj->v.f[0].arg[i] = obj_free(obj->v.f[0].arg[i]); } break; case TY_ARRAY: // fprintf(stderr, "array of size %d\n", obj->v.a[0].len); for (i = 0; i < obj->v.a[0].len; i++) obj->v.a[0].d[i] = obj_free(obj->v.a[0].d[i]); break; case TY_MAP: { struct __map_entry *me = obj->v.m[0].d; // fprintf(stderr, "map of size %d\n", obj->v.m[0].len); for (i = 0; i < obj->v.m[0].len; i++) { me[i].value = obj_free(me[i].value); if (me[i].key && me[i].key != me[i].key_value) free(me[i].key); me[i].key = NULL; } } break; } free(obj); return NULL; } /* * Allocates and fills an object. * For string and map entry, copies the string in the descriptor. * For int, len is the value. * For double, src points to the value. * For arrays, copy also the object pointers. * For maps, copy also the map entries, (and frees the leftover stuff). * For functions, only copy the functor. Need extra argument for the pointers. */ struct __obj *obj_calloc(enum __xml_types type, int32_t len, const void *src) { int l = 0; struct __obj *o; switch (type) { case TY_INT: l = sizeof(int32_t); break; case TY_DOUBLE: l = sizeof(double); break; case TY_STRING: case TY_MAP_ENTRY: l = len + 1; /* length of the string. Add one extra byte for NUL */ if (src) { int n = strlen((const char *)src) + 1; if (n > l) l = n; } if (type == TY_MAP_ENTRY) l += sizeof(struct __map_entry); break; case TY_ARRAY: l = len * sizeof(struct __obj) + sizeof(struct __array_data); break; case TY_MAP: l = len * sizeof(struct __map_entry) + sizeof(struct __map_data); break; case TY_FUNCTION: l = len * sizeof(struct __obj) + sizeof(struct __func_data) + 1 /* name */; if (src) l += strlen((const char *)src); break; } l += sizeof(struct __obj); o = calloc(1, l); if (!o) return NULL; o->type = type; switch (type) { case TY_INT: o->v.i[0] = len; break; case TY_DOUBLE: o->v.d[0] = *(const double *)src; break; case TY_STRING: if (src) strcpy(o->v.s, (const char *)src); break; case TY_ARRAY: o->v.a[0].len = len; if (src) bcopy(src, o->v.a[0].d, len * sizeof(struct __obj *)); break; case TY_MAP_ENTRY: /* src is the key_value, obj must be copied */ if (src) { strcpy(o->v.me[0].key_value, (const char *)src); o->v.me[0].key = o->v.me[0].key_value; } break; case TY_MAP: o->v.m[0].len = len; if (src) { // src is an array of TY_MAP_ENTRY int i; struct __obj **me = (struct __obj **)(void *)src; for (i = 0; i < len; i++) { if (me[i]->type != TY_MAP_ENTRY) fprintf(stderr, "XXX XXX bad object ty %d %d %p to %s TY_MAP\n", me[i]->type, i, me[i], __FUNCTION__); o->v.m[0].d[i].value = me[i]->v.me[0].value; me[i]->v.me[0].value = NULL; o->v.m[0].d[i].key = strdup(me[i]->v.me[0].key_value); obj_free(me[i]); } } break; case TY_FUNCTION: o->v.f[0].len = len; { char *ss = (char *)&(o->v.f[0].arg[o->v.f[0].len]); strcpy(ss, (const char *)src); } // fprintf(stderr, "XXX %s TY_FUNCTION remember to copy the args\n", __FUNCTION__); break; } return o; } /* * Individual routines take a dynstr pointer and a pointer to * the next char to parse. They return a pointer to the next * char to parse. */ /* * encode_name() copies a string into the obj * with the usual quoting and escaping. * Returns the next non-whitespace char after the string. * Leading whitespace is skipped, trailing whitespace is not considered * part of the string. * Any char in [a-zA-Z0-9_.] is considered valid, others * must be escaped or quoted. * XXX later, add encoding */ static const char * encode_name(const char *d, struct __obj **obj) { const char *cur; #define NO_QUOTE ' ' char quote = NO_QUOTE; int escape = 0; dynstr __s = NULL; dynstr *s = &__s; if (obj) *obj = NULL; d = skip_blanks(d); for (cur = d; *cur; cur++) { if (escape) { ds_append(s, cur, 1); escape = 0; continue; } if (*cur == '\\') { escape = 1; continue; } if (quote != NO_QUOTE) { /* anything until closing quote */ if (*cur == quote) // exit from quote mode quote = NO_QUOTE; else ds_append(s, cur, 1); } else if (*cur == '"' || *cur == '\'') { quote = *cur; } else if (isalnum(*cur) || (*cur && index("_.:/+-", *cur)) ) { ds_append(s, cur, 1); } else { break; } } /* skip the blanks */ cur = skip_blanks(cur); if (0) fprintf(stderr, "%s [%s] gets [%s] rest [%s]\n", __FUNCTION__, d, ds_data(*s), cur); if (obj) { char *endp = NULL; long l = strtol(ds_data(*s), &endp, 10); if (*endp == '\0') { // a valid int *obj = obj_calloc(TY_INT, l, NULL); } else { *obj = obj_calloc(TY_STRING, 0, ds_data(*s)); } } ds_free(*s); return cur; } static const char *_expr_parse(const char *s, struct __obj **obj); /* * Helper routine to encode an argument for a list. * 'hint' is a hint on the type. * { -> struct (expect name-pair values) * ' ' -> everything else */ static const char *encode_arg(const char *d, char hint, struct __obj **obj) { char c = *d; int closing_paren = 0; if (obj) *obj = NULL; //fprintf(stderr, "-- %s arg [%s]\n", __FUNCTION__, d); if ( index("0123456789+-.", c)) { // a number c = '0'; } else if (isalnum(c) || c == '"' || c == '\'') { c = 's'; } else if (c == '[') { // array closing_paren = 1; } else if (c == '{') { // struct closing_paren = 1; } else { c = '?'; fprintf(stderr, "unknown arg [%s]\n", d); } if (hint == '{') { // struct, we expect 'name = value' struct __obj *entry_name, *entry; if (c != '0' && c != 's') { fprintf(stderr, "invalid field name %s\n", d); return d+1; } d = encode_name(d, obj ? &entry_name : NULL); if (*d == '=') { d = _expr_parse(skip_blanks(d+1), obj ? &entry : NULL); } if (obj) { *obj = obj_calloc(TY_MAP_ENTRY, 0, entry_name->v.s); if (*obj) (*obj)->v.me[0].value = entry; else obj_free(entry); obj_free(entry_name); } } else if (hint == '(') { d = _expr_parse(d, obj); } else if (hint == '[') { d = _expr_parse(d, obj); } else { // scalar or function d = _expr_parse(d, obj); } return d; } /* * This function takes a compactly encoded XMLRPC call * and produces a suitable XML string. * Returns a pointer to the first non-blank char after the parsed text. MyFunc() --> MyFunc * By extension, it also encodes a plain object. * The last argument, if present, returns a pointer to the struct __obj * representation of the argument. */ static const char *_expr_parse(const char *s, struct __obj **obj) { const char *orig = skip_blanks(s); char c; /* initial char, or type of the object */ s = orig; if (obj) *obj = NULL; if (!s || !*s) return s; c = *s; if (c != '[' && c != '{' && c != '(') { /* scalar or function. First take the name, then look for args. */ s = encode_name(s, obj); /* function name */ c = *s; if (c != '(') { /* no arguments, just a name */ c = ' '; /* signal skip arguments */ } } if (c != ' ') { dynstr args = NULL; // build the array of arguments int argslen; // const char *startarg = s; // debugging s = skip_blanks(s+1); // skip opening parenthesis // fprintf(stderr, "--%s args %s\n", __FUNCTION__, s); if (*s != ']' && *s != '}' && *s != ')') { for (;;) { struct __obj *entry; s = encode_arg(s, c, obj ? &entry : NULL); if (obj) ds_append(&args, (void *)&entry, sizeof(entry)); if (*s != ',') break; s = skip_blanks(s+1); } } argslen = ds_len(args) / sizeof(struct __obj *); // fprintf(stderr, "-- arglist %c %s size %d\n", c, startarg, argslen); // fprintf(stderr, "+++ open '%s' close '%s'---\n", orig, s); s = skip_blanks(s+1); // skip closing parenthesis if (c == '[') { if (obj) (*obj) = obj_calloc(TY_ARRAY, argslen, ds_data(args)); } else if (c == '(') { if (obj) { struct __obj *functor = *obj; (*obj) = obj_calloc(TY_FUNCTION, argslen, functor->v.s); if (*obj) bcopy( ds_data(args), (*obj)->v.f[0].arg, ds_len(args)); obj_free(functor); } } else if (c == '{') { if (obj) (*obj) = obj_calloc(TY_MAP, argslen, ds_data(args)); } ds_free(args); } return skip_blanks(s); } /* * extract the name of the xml token * Sets 'end' to 1 if the token starts or ends with '/>' */ static const char *xml_token(const char *s, const char **tok, int *l, int *end) { *end = 0; *l = 0; *tok = NULL; s = skip_blanks(s); if (*s != '<') return s; s = skip_blanks(s+1); if (*s == '/') { *end = 1; s++; } *tok = s; while (*s && (isalnum(*s) || *s == '_')) { s++; (*l)++; } while (*s && *s != '>') s++; if (*s) { if (s[-1] == '/') *end = 1; s = skip_blanks(s+1); } return s; } static const char *xml_parse(const char *s, struct __obj **obj) { const char *p; const char *orig = s; int l, end; char buf[50]; struct __obj *my_obj; if (obj == NULL) { fprintf(stderr, "%s requires a non null obj\n", __FUNCTION__); obj = &my_obj; } //fprintf(stderr, "-- in %s [%s]\n", __FUNCTION__, s); again: s = skip_blanks(s); if (*s != '<') return encode_name(s, obj); s = xml_token(s, &p, &l, &end); if (!p) return s; if (l == 4 && !strncasecmp(p, "?xml", 4)) { fprintf(stderr, "start found %s", orig); goto again; } if (l > sizeof(buf) - 1) l = sizeof(buf) - 1; strncpy(buf, p, l); buf[l] = '\0'; #define M(x) (!strncasecmp(p, x, strlen(x))) if (M("methodcall")) { struct __obj *functor, *args; int argslen; s = xml_parse(s, &functor); // methodname s = xml_parse(s, &args); // these come as an array argslen = args->v.a[0].len; /* now pack functor and arg */ (*obj) = obj_calloc(TY_FUNCTION, argslen, functor->v.s); if (*obj) { bcopy(args->v.a[0].d, (*obj)->v.f[0].arg, argslen * sizeof(struct __obj *)); bzero(args->v.a[0].d, argslen * sizeof(struct __obj *)); } obj_free(args); obj_free(functor); } else if (M("methodname")) { /* pack as a string */ s = xml_parse(s, obj); } else if (M("array")) { s = xml_parse(s, obj); } else if (M("member")) { struct __obj *name, *val; s = xml_parse(s, &name); // name s = xml_parse(s, &val); // value *obj = obj_calloc(TY_MAP_ENTRY, 0, name->v.s); if (*obj) (*obj)->v.me[0].value = val; else obj_free(val); obj_free(name); } else if (M("struct")) { dynstr args = NULL; int n; while (*s == '<' && s[1] && s[1] != '/') { struct __obj *entry; s = xml_parse(s, &entry); ds_append(&args, (const char *)&entry, sizeof(entry)); } n = ds_len(args)/sizeof(struct __obj *); *obj = obj_calloc(TY_MAP, n, ds_data(args)); ds_free(args); } else if (M("data")||M("params")) { dynstr args = NULL; int argslen; while (*s == '<' && s[1] && s[1] != '/') { struct __obj *entry; s = xml_parse(s, &entry); ds_append(&args, (const char *)&entry, sizeof(entry)); } argslen = ds_len(args)/sizeof(struct __obj *); *obj = obj_calloc(TY_ARRAY, argslen, ds_data(args)); ds_free(args); } else if (M("nil")) { /* map to empty string */ *obj = obj_calloc(TY_STRING, 0, NULL); } else if (M("string")) { const char *begin = s; while (*s && *s != '<') s++; *obj = obj_calloc(TY_STRING, s - begin, NULL); bcopy(begin, (*obj)->v.s, s - begin); (*obj)->v.s[s - begin] = '\0'; } else { /* anything else, just dump the content */ s = xml_parse(s, obj); } if (!end) s = xml_token(s, &p, &l, &end); return s; } const char *expr_parse(const char *s, struct __obj **o, enum expr_fmt mode) { switch (mode) { default: return s; case FMT_FUNC: return _expr_parse(s, o); case FMT_XML: return xml_parse(s, o); } } /* * apply a filter to a reply: * * .length() -> returns object length * .type() -> returns object type * .keys() -> returns all keys * [] -> iterate on all elements * {} -> return all keys * [n,...,m] -> iterate on selected elements * {n,...,m} -> iterate on selected keys * empty: -> print object. */ const char *obj_filter(dynstr *s, const struct __obj *o, const char *filter, enum expr_fmt mode) { int n; const char *nf; /* next filter */ if (!o || !filter) return NULL; n = obj_getlen(o); //dsprintf(s, "obj %p ty %d len %d\n", o, o->type, n); switch(*filter++) { case '\0': obj_print(s, o, mode); break; case '.': if (!strncmp(filter, "length()", 7)) { dsprintf(s, "%d", n); } else if (!strncmp(filter, "type()", 6)) { dsprintf(s, "%d", o->type); } else if (!strncmp(filter, "xml()", 5)) { obj_print(s, o, FMT_XML); } else if (!strncmp(filter, "func()", 6)) { obj_print(s, o, FMT_FUNC); } else if (!strncmp(filter, "keys()", 6)) { int i; if (o->type != TY_MAP) { dsprintf(s, "\"\""); break; } for (i=0; i < n; i++) dsprintf(s, "%s ", o->v.m[0].d[i].key); } // else other methods break; case '[': case '{': if (*filter == ']' || *filter == '}') { // all elements int i; for (i = 0; i < n; i++) { obj_filter(s, obj_get(o, i, NULL), filter + 1, mode); dsprintf(s, " "); } break; } // try selected elements on the portion past the ']' or '}' for (nf = filter; *nf && !index("}]", *nf) ; nf++) ; if (*nf) nf++; for (;;) { int i; char *endp = NULL; enum expr_fmt mode2 = mode; if (!*filter || *filter == ']' || *filter == '}') break; i = strtol(filter, &endp, 10); if (endp != filter) { if (*endp == '=') mode2 = FMT_FLAT; filter = endp; obj_filter(s, obj_get(o, i, NULL), nf, mode2); dsprintf(s, " "); } else { const char *begin = filter; // collect an index XXX use the string routines while (*filter && !index("}]=,", *filter)) filter++; if (begin != filter) { if (*filter == '=') mode2 = FMT_FLAT; obj_filter(s, obj_getfield(o, begin, filter - begin), nf, mode2); dsprintf(s, " "); } } while (*filter && index(" \t=,", *filter)) filter++; } break; } // dsprintf(s, "end obj %p -\n", o); return ds_data(*s); } /*--- end of file ---*/ minixmlrpc/minixml.h000644 000423 000000 00000012225 11020553676 015333 0ustar00luigiwheel000000 000000 /* * $Id: minixml.h 348 2008-06-01 17:15:30Z luigi $ * * This library implements a small set of functions to parse, manipulate * and print, in functional or XML format, expressions involving * scalars, arrays, maps, and function calls. * * The main purpose is to issue and manipulate XMLRPC calls and results * from the command line or a shell. * The library uses the 'dynstring' functions to manipulate extensible * strings and byte arrays -- dynstrings are very simple to use, * think of them as a C string that grows as needed, and you use * ds_data(s) to access the actual string data. * * --- LIBRARY USAGE --- * * Declare an object with struct __obj *o; * * You can fill it from a string using * * expr_parse(char *s, struct __obj **o, fmt) * * where fmt is FMT_FUNC or FMT_XML to parse text expressions or XMLRPC. * You can also create the object programmatically using * obj_calloc(...) -- see the .c file for more detail. * * Once the object is encoded, you can print it using * * obj_print(dynstr *dst, const struct __obj *o, fmt); * * The result is stored into the dynamic string passed as first arg. * fmt = FMT_FUNC or FMT_XML selects the print format. * * An object is destroyed with obj_free(o); * * To manipulate objects, e.g. iterate and extract fields, use: * * obj_getlen(const struct __obj *) : * to know the number of elements in the object * * obj_get(const struct __obj *, uint32_t i, const char **key) : * to extract the i-th component (and possibly the key); * * obj_getfield(const struct __obj *, const char *key, int keylen) : * to extract the component with the given key; * * The auxiliary function * obj_filter(dynstr *s, const struct __obj *o, const char *filter, fmt) * * supports a simple filtering language to extract, in textual form, * components from nested ojects, or list of keys, and so on. * * Filters are a sequence of character, applied one after the other * to the "current object", i.e. the result of previous filtering: * * (empty) prints the object in functional form * .xml() prints the object in XML * .func() prints the object in functional form * .length() returns the number of elements in the object * (1 for a scalar, or number of arguments or * elements for functions, arrays and maps) * .keys() returns the space-separated list of keys for a map * [] applies the following filter to all components * of the current object * [k1,k2,...] applies the following filter to the selected keys * of the current object. Number are considered indexes. * If a key ends with '=', then the object is printed * replacing first-level separators with spaces. * * Filter examples: * assume the object is {a=[1,[2,3],4],b=20,c=f(1,2,3)} * '' returns {a=[1,[2,3],4],b=20,c=f(1,2,3)} * .length() returns 3 * .keys() returns a b c * [] returns [1,[2,3],4] 20 f(1,2,3) * [0,2] returns [1,[2,3],4] f(1,2,3) * [0][1] returns [2,3] * [a=] returns 1 [2,3] 4 */ #include #include "dynstring.h" /* avoid using 0 and 1 to catch errors * FMT_FUNC is a functional format * FMT_FLAT is above but separators at the first level are * replaced by blanks */ enum expr_fmt { FMT_FUNC = 80, FMT_FLAT = 81, FMT_XML= 90 }; enum __xml_types { TY_INT=51, TY_DOUBLE, TY_STRING, TY_ARRAY, TY_MAP, TY_MAP_ENTRY, TY_FUNCTION, }; struct __obj; struct __map_entry { struct __obj *value; char *key; /* key_len + 1 */ char key_value[0]; /* if allocated inline */ }; struct __func_data { uint32_t len; /* number of arguments */ struct __obj *arg[0]; // len entries */ // char name[0]; /* dynamic */ }; struct __map_data { uint32_t len; struct __map_entry d[0]; }; struct __array_data { uint32_t len; struct __obj *d[0]; }; /* * The main object. The type here only serves to access * the field, allocation should include enough room for the * data elements. */ struct __obj { enum __xml_types type; union { int32_t i[0]; double d[0]; char s[0]; /* nul-terminated string */ struct __func_data f[0]; struct __map_entry me[0]; struct __map_data m[0]; struct __array_data a[0]; } v; }; /* * expr_parse() parses a functional or xml object * obj_print(dst, obj, fmt) prints the object in functional or xml form, * obj_calloc() creates a new object of specific type. * obj_free() releases the storage for the object */ const char *expr_parse(const char *s, struct __obj **, enum expr_fmt); const char *obj_print(dynstr *dst, const struct __obj *o, enum expr_fmt); struct __obj *obj_calloc(enum __xml_types, int len, const void *src); struct __obj *obj_free(struct __obj *); /* * Support functions for navigating in the object. * obj_get() returns the i-th element of an array or map, * and also the key value in case of a map. * obj_getlen() returns the length of the array or map. * obj_getfield() returns the element with the given key for a map. */ const struct __obj *obj_get(const struct __obj *, uint32_t i, const char **key); const struct __obj *obj_getfield(const struct __obj *, const char *key, int keylen); int obj_getlen(const struct __obj *); const char *obj_filter(dynstr *s, const struct __obj *o, const char *filter, enum expr_fmt); minixmlrpc/dynstring.h000644 000423 000000 00000002157 11020530037 015664 0ustar00luigiwheel000000 000000 /* * $Id: dynstring.h 339 2008-06-01 14:26:27Z luigi $ * * An implementation of dynamic strings (and in general, extensible * data structures) inherited from the one i wrote myself for asterisk. * * USE: declare the dynamic string: dynstr s = NULL; * then use as asprintf(), e.g. dsprintf(&s, fmt, ...); * or, to append a chunk of bytes: ds_append(&s, ptr, len) * * Use ds_len(s), ds_data(s), ds_reset(s), ds_free(s) to get the * length, data pointer, reset the content, and free the memory. * * This code has been originally designed for strings, however * ds_append() supports appending arbitrary chunks of bytes to * the structure. */ #ifndef __DYNSTRING_H #define __DYNSTRING_H typedef struct __dynstr * dynstr; /* sprintf and append bytes to a dynamic string */ int dsprintf(dynstr *s, const char *fmt, ...); int ds_append(dynstr *s, const void *d, int len); const char *ds_data(dynstr s); // returns a pointer to the content int ds_len(dynstr s); // returns the string lenght void ds_reset(dynstr s); // resets the buffer to empty string void ds_free(dynstr s); // frees the space #endif /* __DYNSTRING_H */ minixmlrpc/index.html000644 000423 000000 00000012276 11020634106 015474 0ustar00luigiwheel000000 000000

minixmlrpc - simple xmlrpc client and object manipulator

This library implements a small set of functions to parse, manipulate and print, in functional or XML format, expressions involving scalars, arrays, maps, and function calls.

The main purpose is to issue and manipulate XMLRPC calls and results from the command line or a shell, without requiring massive amounts of third party libraries or interpreters.

As it is now, the only external dependency is an SSL library, and supported options are:

  • openssl, which is slightly large (over 1MB for the libcrypto part) but rather standard, so you might have it already available on the target system;
  • xyssl, a compact (about 300k) and also BSD-licensed library with SSL support. You can select this version building it with
    'gmake XYSSL=1 XYSSL_TREE=...'
The binary, dynamically linked, occupies 20-30Kbytes and builds also on OpenWRT. We are exploring the use of dropbear as an alternative to openssl.

Download xmlrpc-20080602a.tgz

LIBRARY USAGE

Using this library in your code is extremely simple and documented in the header file, minixml.h, portions of which are reported below.

STATUS, BUGS and TODO

The code is very recent so the parser is not complete and does not handle malformed input in an easy-to-control way. It should not crash, however it may return bogus responses on malformed expressions or xml, or filters.

In particular, there is still no explcit representation of 'fault' responses from the XMLRPC servers.

  • 20080602a: added XYSSL support, fixed some trivial bugs in ssl_read and dynstr_create;
  • 20080601: initial release

Example servers

The xmlrpc site reports a partial list of public services that are based on XMLRPC. Some of them are not running anymore, but you can use the others as test servers to try this program:

  • Time server:
    ./xmlrpc http://time.xmlrpc.com/RPC2 'currentTime.getCurrentTime()'
  • A simple sum and difference server
    ./xmlrpc http://xmlrpc-c.sourceforge.net/api/sample.php 'sample.sumAndDifference(10,20)' '' '[].keys()' '[][sum]'

minixml.h

For detailed instructions on how to use the code, please look at minixml.h and xmlrpc.1 . Below are some relevant pieces from these files:

 Declare an object with 	struct __obj *o;

 You can fill it from a string using

	expr_parse(char *s, struct __obj **o, fmt)

 where fmt is FMT_FUNC or FMT_XML to parse text expressions or XMLRPC.
 You can also create the object programmatically using
 obj_calloc(...) -- see the .c file for more detail.

 Once the object is encoded, you can print it using

	obj_print(dynstr *dst, const struct __obj *o, fmt);

 The result is stored into the dynamic string passed as first arg.
 fmt = FMT_FUNC or FMT_XML selects the print format.

 An object is destroyed with	obj_free(o);

'dynstr' is an extensible string/byte array implemented by the 'dynstring' functions (also included, and very small). dynstrings are very simple to use, think of them as a C string that grows as needed, and you use ds_data(s) to access the actual string data.

 To manipulate objects, e.g. iterate and extract fields, use:

    obj_getlen(const struct __obj *) :
	    to know the number of elements in the object

    obj_get(const struct __obj *, uint32_t i, const char **key) :
	    to extract the i-th component (and possibly the key);

    obj_getfield(const struct __obj *, const char *key, int keylen) :
	    to extract the component with the given key;

 The auxiliary function
    obj_filter(dynstr *s, const struct __obj *o, const char *filter, fmt)

 supports a simple filtering language to extract, in textual form,
 components from nested ojects, or list of keys, and so on.

 Filters are a sequence of character, applied one after the other
 to the "current object", i.e. the result of previous filtering:

	(empty)		prints the object in functional form
	.xml()		prints the object in XML
	.func()		prints the object in functional form
	.length()	returns the number of elements in the object
			(1 for a scalar, or number of arguments or
			elements for functions, arrays and maps)
	.keys()		returns the space-separated list of keys for a map
	[]		applies the following filter to all components
			of the current object
	[k1,k2,...]	applies the following filter to the selected keys
			of the current object. Number are considered indexes.
			If a key ends with '=', then the object is printed
			replacing first-level separators with spaces.

 Filter examples:
   assume the object is	{a=[1,[2,3],4],b=20,c=f(1,2,3)}
   ''		returns		{a=[1,[2,3],4],b=20,c=f(1,2,3)}
   .length()	returns		3
   .keys()	returns		a b c
   []		returns		[1,[2,3],4] 20 f(1,2,3)
   [0,2]	returns		[1,[2,3],4] f(1,2,3)
   [0][1]	returns		[2,3]
   [a=]	returns		1 [2,3] 4
minixmlrpc/xmlrpc.1000644 000423 000000 00000007532 11020543751 015072 0ustar00luigiwheel000000 000000 .TH xmlrpc 1 "1 june 2008" .SH NAME xmlrpc - issue xmlrpc commands, and manipulate XML or plaintext expression .SH SYNOPSIS .B xmlrpc .RB [ -t .IR timeout ] .RB [ -r .IR referer ] .RB [ -u .IR user-agent ] .RB [ -a .IR username:password ] .RB [ -c .IR cookie ] .RB [ -v ] .I url [ expression ] [ filter ... ] .SH DESCRIPTION .PP .I xmlrpc issues an http/https query to the URL specified, passing the optional expression as an XMLRPC query. The result is dumped to stdout, but also parsed and stored in an internal format to allow manipulation. .Pp The expression can be specified in a simple functional form where .nf 12 is an integer (scalar) "ab cd ef" is a string (scalar) [a,b,...] is an array {k1=v1,k2=v2,...} is a map of key-value entries f(x,y,...) is a function with arguments .fi Objects can be nested, so function arguments, array values or map values can be any scalar, array, map or function. .Pp When doing an XMLRPC call, the incoming XML is parsed and by default printed in the same functional form as above. However, you can apply "filters" to the reply in order to extract selected values. Currently, the following filters are implemented: .nf empty string prints the object in text format .length() returns the number of elements in the object (1 for scalars, number of arguments or elements for functions, arrays and maps). .keys() returns the keys of a map .type() returns a scalar representing the type .xml() prints the object in xml .func() prints the object in text format (default) [] applies the next filter to all components of the current object [k1,k2,...] applies the next filter to components with the specified indexes or keys. If the key is followed by '=' then the object in the first level is printed in 'flat' form, i.e. without separators or braces. E.g. an array [a,b,c] becomes a b c .fi .PP The -t flag specifies a timeout in seconds. If that much time passes with no activity, the fetch is aborted. The default is 60 seconds. .PP The -r flag specifies a Referer header to send. Some tightly-clenched web admins like to jigger their server to only return files if the proper Referer is given. .PP The -u flag specifies a User-Agent header to send. Some *really* tightly-clenched web admins like to jigger their server to only return files if a well-known User-Agent is given. .PP The -a flag lets you do Basic Authentication. .PP The -c flag lets you pass in a cookie. .PP The -v flag is for debugging; it tells http_get to show the HTTP headers as well as the rest of the response. .SH "EXIT STATUS" .PP If the fetch is successful (HTTP status 200), the command's exit status is 0. Otherwise the exit status is the HTTP status code. However, since a Unix command exit status is only one byte, the HTTP codes show up mod 256. Adding to the confusion, csh interprets the status byte as signed, while in other shells it is unsigned. Anyway, here is a table of some common status values: .nf HTTP sh csh 200 0 0 302 46 46 304 48 48 400 144 -112 401 145 -111 403 147 -109 404 148 -108 408 152 -104 500 244 -12 501 245 -11 503 247 -9 .fi .SH AUTHOR The original code to issue http requests comes from the .I http_get program, originally based on a simple version by Al Globus . Debugged, prettified, and extended by Jef Poskanzer . .Pp The extensions to support dynamic strings, xml and functional parsing and encoding are by Luigi Rizzo. minixmlrpc/xmlrpc.c000644 000423 000000 00000031542 11020630277 015152 0ustar00luigiwheel000000 000000 /* http_get - fetch the contents of an http URL ** ** Originally based on a simple version by Al Globus . ** Debugged and prettified by Jef Poskanzer . Also includes ** ifdefs to handle https via OpenSSL. */ #include "myhdr.h" #define MAX_COOKIES 20 struct url_parts { char *url; char *method; char *hostname; int port; char *file; char *referer; char *user_agent; char *auth_token; int protocol; int timeout; int ncookies; char *cookies[MAX_COOKIES]; dynstr xmlrpc; /* the call argument */ dynstr resp_head; dynstr resp_body; struct __obj *obj; /* received or command line object */ /* leftover arguments, for parsing */ int argc; char **argv; }; /* Forwards. */ static int getURL( struct url_parts *u); static int getURLbyParts( struct url_parts *u); static void sigcatch( int sig ); static int b64_encode( char* ptr, int len, char* space, int size ); /* Globals. */ static char* argv0; static int verbose; /* Protocol symbols. */ enum proto_type { PROTO_HTTP=0, PROTO_HTTPS }; int main( int argc, char** argv ) { int argn; struct url_parts u; int c, status; bzero(&u, sizeof(u)); argv0 = argv[0]; argn = 1; verbose = 0; u.timeout = 60; u.user_agent = "http_get"; u.method = "GET"; // XXX replace with getopt while ( (c = getopt(argc, argv, "vt:m:r:u:s:a:c:")) != -1) { switch (c) { default: fprintf(stderr, "unknown argument %c\n", c); usage(); break; case 'v': verbose = 1; break; case 't': u.timeout = atoi( optarg ); break; case 'm': if (!strcasecmp(optarg, "POST")) u.method = "POST"; else u.method = "GET"; break; case 'r': u.referer = optarg; break; case 'u': u.user_agent = optarg; break; case 'a': u.auth_token = argv[argn]; break; case 'c': if ( u.ncookies >= MAX_COOKIES ) { fprintf( stderr, "too many cookies\n"); exit( 1 ); } u.cookies[u.ncookies++] = optarg; break; } } argc -= optind; argv += optind; if (argc == 0) usage(); u.url = argv[0]; if (argc > 1 && !u.xmlrpc) { dsprintf(&u.xmlrpc, "\n"); expr_parse(argv[1], &u.obj, FMT_FUNC); obj_print(&u.xmlrpc, u.obj, FMT_XML); u.method = "POST"; u.argc = argc - 2; u.argv = argv+2; } if (!*u.url || !strcmp(u.url, "-")) { /* empty URL, take the argument as the result */ status = 0; } else { /* call the server */ u.obj = obj_free(u.obj); signal( SIGALRM, sigcatch ); status = getURL( &u); if ( status == 200 ) status = 0; else if ( status == 0 ) status = 1; } /* now print the results */ if (!status) { dynstr r = NULL; int i; obj_print(&r, u.obj, FMT_FUNC); if (!u.argc) fprintf(stdout, "%s\n", ds_data(r)); if (verbose) // this goes to stderr! fprintf(stderr, "%s\n", ds_data(r)); for (i=0; i < u.argc; i++) { char *filter = u.argv[i]; dynstr s = NULL; obj_filter(&s, u.obj, filter, FMT_FUNC); if (verbose) // this goes to stderr fprintf(stderr, "apply filter %d %s : %s\n", i, filter, ds_data(s)); fprintf(stdout, "%s\n", ds_data(s)); ds_free(s); } ds_free(r); } // XXX free all objects in u exit( status ); } void usage() { fprintf( stderr, "usage: program [-c cookie] [-t timeout] [-r referer] [-u user-agent] [-a username:password] [-v] url\n"); exit( 1 ); } /* URL must be of the form http://host-name[:port]/file-name */ static int getURL( struct url_parts *u) { char* s, *h; char host[2000]; int host_len; int proto_len; if ( u->url == NULL) { fprintf( stderr, "null URL\n"); exit( 1 ); } if ( strncmp( "http://", u->url, 7 ) == 0 ) { proto_len = 7; u->protocol = PROTO_HTTP; } #ifdef USE_SSL else if ( !strncasecmp( "https://", u->url, 8 ) ) { proto_len = 8; u->protocol = PROTO_HTTPS; } #endif /* USE_SSL */ else { fprintf( stderr, "unsupported URL %s\n", u->url ); exit( 1 ); } /* Get the host name. */ h = u->url + proto_len; /* the host part */ for ( s = h; *s != '\0' && *s != ':' && *s != '/'; ++s ) ; host_len = s - h; if (host_len > sizeof(host) - 1) host_len = sizeof(host) - 1; strncpy( host, h, host_len ); host[host_len] = '\0'; u->hostname = host; /* Get port number. */ if ( *s == ':' ) { u->port = (unsigned short) atoi( ++s ); while ( *s != '\0' && *s != '/' ) ++s; } else { #ifdef USE_SSL if ( u->protocol == PROTO_HTTPS ) u->port = 443; else #endif u->port = 80; } /* Get the file name. */ u->file = ( *s == '\0' ) ? "/" : s; return getURLbyParts( u ); //protocol, host, port, method, file, referer, user_agent, auth_token, ncookies, cookies ); } /* Header FSM states. * Depending on the input char we move from one state to the next one. */ enum hdr_states { HDST_LINE1_PROTOCOL, HDST_LINE1_STATUS, HDST_TEXT, HDST_LF, HDST_CR, HDST_CRLF, HDST_CRLFCR, }; /* parse the header 1 char at a time, returns 1 when done. * sets status and internal state. */ static int parse_hdr_char(enum hdr_states *_s, int *http_status, char c) { enum hdr_states header_state = *_s; switch ( header_state ) { case HDST_LINE1_PROTOCOL: *http_status = 0; if (c == ' ' || c == '\t') { header_state = HDST_LINE1_STATUS; /* should come next */ } else if (c == '\n') header_state = HDST_LF ; else if (c == '\r') header_state = HDST_CR; break; case HDST_LINE1_STATUS: if (c >= '0' && c <= '9') { *http_status = *http_status * 10 + c - '0'; } else if (c == '\n') header_state = HDST_LF ; else if (c == '\r') header_state = HDST_CR; else header_state = HDST_TEXT; break; case HDST_TEXT: if (c == '\n') header_state = HDST_LF; else if (c == '\r') header_state = HDST_CR; break; case HDST_LF: if (c == '\n') return 1; else if (c == '\r') header_state = HDST_CR; else header_state = HDST_TEXT; break; case HDST_CR: if (c == '\r') return 1; else if (c == '\n') header_state = HDST_CRLF; else header_state = HDST_TEXT; break; case HDST_CRLF: if (c == '\n') return 1; else if (c == '\r') header_state = HDST_CRLFCR; else header_state = HDST_TEXT; break; case HDST_CRLFCR: if (c == '\n' || c == '\r') return 1; header_state = HDST_TEXT; break; } *_s = header_state; return 0; } static int getURLbyParts(struct url_parts *u) { struct my_sock *fd; dynstr head_buf = NULL; dynstr body = NULL; int i, bytes, status; enum hdr_states header_state; int b = 0; char buf[1024]; alarm( u->timeout ); fd = open_client_socket( u->hostname, u->port, u->protocol == PROTO_HTTPS ); /* Build request buffer, starting with the method. */ alarm( u->timeout ); dsprintf( &head_buf, "%s %s HTTP/1.0\r\n", u->method, u->file ); /* HTTP/1.1 host header - some servers want it even in HTTP/1.0. */ dsprintf( &head_buf, "Host: %s\r\n", u->hostname ); if ( u->referer ) dsprintf( &head_buf, "Referer: %s\r\n", u->referer ); /* User-agent. */ dsprintf( &head_buf, "User-Agent: %s\r\n", u->user_agent ); /* Fixed headers. */ dsprintf( &head_buf, "Accept: */*\r\n" ); dsprintf( &head_buf, "Accept-Encoding: gzip, compress\r\n" ); dsprintf( &head_buf, "Accept-Language: en\r\n" ); dsprintf( &head_buf, "Accept-Charset: iso-8859-1,*,utf-8\r\n" ); if ( u->auth_token ) { /* Basic Auth info. */ char token_buf[1000]; token_buf[b64_encode( u->auth_token, strlen( u->auth_token ), token_buf, sizeof(token_buf) )] = '\0'; dsprintf( &head_buf, "Authorization: Basic %s\r\n", token_buf ); } /* Cookies. */ for ( i = 0; i < u->ncookies; ++i ) dsprintf( &head_buf, "Cookie: %s\r\n", u->cookies[i] ); if (!strcasecmp(u->method, "POST")) { // dsprintf( &head_buf, "Content-type: application/x-www-form-urlencoded\r\n" ); dsprintf( &head_buf, "Content-type: text/xml\r\n" ); // XXX put the actual length /* read from stdin if body is not specified */ if (u->xmlrpc) body = u->xmlrpc; else for (;;) { bytes = read(0, buf, sizeof(buf)); if (bytes <= 0) break; dsprintf(&body, buf, bytes); } dsprintf( &head_buf, "Content-length: %d\r\n", ds_len(body) ); } /* Blank line. */ dsprintf( &head_buf, "\r\n" ); /* Now actually send it. */ safe_write(fd, ds_data(head_buf), ds_len(head_buf)); if (body) safe_write(fd, ds_data(body), ds_len(body)); /* Get lines until a blank one. */ alarm( u->timeout ); header_state = HDST_LINE1_PROTOCOL; status = 0; for (;;) { int i = 0; bytes = safe_read( fd, buf, sizeof(buf) ); if ( bytes <= 0 ) break; for ( b = 0; b < bytes; ++b ) { char c = buf[b]; if ( verbose ) write( 1, &c, 1 ); i= parse_hdr_char(&header_state, &status, c); if (i) break; } if (b) dsprintf(&u->resp_head, buf, b); if (i) break; } /* Dump out the rest of the headers buffer. */ ++b; if (verbose) write( 1, &buf[b], bytes - b ); if (bytes - b) dsprintf(&u->resp_body, buf+b, bytes - b); /* Copy the data. */ for (;;) { if (verbose) fprintf(stderr, "now writing data\n"); alarm( u->timeout ); bytes = safe_read( fd, buf, sizeof(buf) ); if ( bytes < 0 ) perror( "read" ); if ( bytes <= 0 ) break; ds_append(&u->resp_body, buf, bytes); if (verbose) write( 1, buf, bytes ); } safe_close(fd); if (verbose) { fprintf(stderr, "resp-hdr: [\n%s\n]\n", ds_data(u->resp_head)); fprintf(stderr, "resp-body: len %d 0x%x [\n%s\n]\n", ds_len(u->resp_body), ds_len(u->resp_body), ds_data(u->resp_body)); } expr_parse(ds_data(u->resp_body), &u->obj, FMT_XML); return status; } static void sigcatch( int sig ) { fprintf( stderr, "%s: timed out\n", argv0 ); exit( 1 ); } /* Base-64 encoding. This encodes binary data as printable ASCII characters. ** Three 8-bit binary bytes are turned into four 6-bit values, like so: ** ** [11111111] [22222222] [33333333] ** ** [111111] [112222] [222233] [333333] ** ** Then the 6-bit values are represented using the characters "A-Za-z0-9+/". */ static char b64_encode_table[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', /* 0-7 */ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 8-15 */ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', /* 16-23 */ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', /* 24-31 */ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 32-39 */ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', /* 40-47 */ 'w', 'x', 'y', 'z', '0', '1', '2', '3', /* 48-55 */ '4', '5', '6', '7', '8', '9', '+', '/' /* 56-63 */ }; static int b64_decode_table[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */ }; /* Do base-64 encoding on a hunk of bytes. Return the actual number of ** bytes generated. Base-64 encoding takes up 4/3 the space of the original, ** plus a bit for end-padding. 3/2+5 gives a safe margin. */ static int b64_encode( char* ptr, int len, char* space, int size ) { int ptr_idx = 0, space_idx = 0, phase = 0; for ( ptr_idx = 0; ptr_idx < len; ++ptr_idx ) { char c; switch ( phase ) { case 0: c = b64_encode_table[ptr[ptr_idx] >> 2]; if ( space_idx < size ) space[space_idx++] = c; c = b64_encode_table[( ptr[ptr_idx] & 0x3 ) << 4]; if ( space_idx < size ) space[space_idx++] = c; ++phase; break; case 1: space[space_idx - 1] = b64_encode_table[ b64_decode_table[(int) space[space_idx - 1]] | ( ptr[ptr_idx] >> 4 ) ]; c = b64_encode_table[( ptr[ptr_idx] & 0xf ) << 2]; if ( space_idx < size ) space[space_idx++] = c; ++phase; break; case 2: space[space_idx - 1] = b64_encode_table[ b64_decode_table[(int) space[space_idx - 1]] | ( ptr[ptr_idx] >> 6 ) ]; c = b64_encode_table[ptr[ptr_idx] & 0x3f]; if ( space_idx < size ) space[space_idx++] = c; phase = 0; break; } } /* Pad with ='s. */ while ( phase++ < 3 ) if ( space_idx < size ) space[space_idx++] = '='; return space_idx; }