diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/ChangeLog.txt | 4 | ||||
-rw-r--r-- | apps/examples/Kconfig | 4 | ||||
-rw-r--r-- | apps/examples/Make.defs | 4 | ||||
-rw-r--r-- | apps/examples/README.txt | 25 | ||||
-rw-r--r-- | apps/examples/xmlrpc/Kconfig | 45 | ||||
-rw-r--r-- | apps/examples/xmlrpc/Makefile | 106 | ||||
-rw-r--r-- | apps/examples/xmlrpc/calls.c | 122 | ||||
-rw-r--r-- | apps/examples/xmlrpc/xmlrpc_main.c | 419 | ||||
-rw-r--r-- | apps/include/netutils/xmlrpc.h | 123 | ||||
-rw-r--r-- | apps/netutils/Kconfig | 4 | ||||
-rw-r--r-- | apps/netutils/Make.defs | 4 | ||||
-rw-r--r-- | apps/netutils/Makefile | 2 | ||||
-rw-r--r-- | apps/netutils/README.txt | 2 | ||||
-rw-r--r-- | apps/netutils/xmlrpc/Kconfig | 23 | ||||
-rw-r--r-- | apps/netutils/xmlrpc/Makefile | 99 | ||||
-rw-r--r-- | apps/netutils/xmlrpc/response.c | 287 | ||||
-rw-r--r-- | apps/netutils/xmlrpc/xmlparser.c | 416 |
17 files changed, 1685 insertions, 4 deletions
diff --git a/apps/ChangeLog.txt b/apps/ChangeLog.txt index b7b71862e..7650b0ff4 100644 --- a/apps/ChangeLog.txt +++ b/apps/ChangeLog.txt @@ -325,4 +325,8 @@ utility (also contribed by Max Holtzberg). * apps/examples/*/main.c: Too many files called main.c. Each renamed to something unique so that they will not collide in the archive. + * apps/netutils/xmlrpc: The Embeddable Lightweight XML-RPC Server + discussed at http://www.drdobbs.com/web-development/\ + an-embeddable-lightweight-xml-rpc-server/184405364. Contributed by + Max Holtzberg. diff --git a/apps/examples/Kconfig b/apps/examples/Kconfig index 786012593..865268add 100644 --- a/apps/examples/Kconfig +++ b/apps/examples/Kconfig @@ -202,3 +202,7 @@ endmenu menu "WLAN Example" source "$APPSDIR/examples/wlan/Kconfig" endmenu + +menu "XML RPC Example" +source "$APPSDIR/examples/xmlrpc/Kconfig" +endmenu diff --git a/apps/examples/Make.defs b/apps/examples/Make.defs index 3bc72b52e..a6e0ae88e 100644 --- a/apps/examples/Make.defs +++ b/apps/examples/Make.defs @@ -225,3 +225,7 @@ endif ifeq ($(CONFIG_EXAMPLES_WLAN),y) CONFIGURED_APPS += examples/wlan endif + +ifeq ($(CONFIG_EXAMPLES_XMLRPC),y) +CONFIGURED_APPS += examples/xmlrpc +endif diff --git a/apps/examples/README.txt b/apps/examples/README.txt index 934eded4d..12d6d3892 100644 --- a/apps/examples/README.txt +++ b/apps/examples/README.txt @@ -1668,8 +1668,27 @@ examples/wget file in the configuration driver with instruction to build applications like: - CONFIGURED_APPS += uiplib - CONFIGURED_APPS += resolv - CONFIGURED_APPS += webclient + CONFIGURED_APPS += uiplib + CONFIGURED_APPS += resolv + CONFIGURED_APPS += webclient +examples/xmlrpc + + This example exercises the "Embeddable Lightweight XML-RPC Server" which + is discussed at: + + http://www.drdobbs.com/web-development/an-embeddable-lightweight-xml-rpc-server/184405364 + + Configuration options: + CONFIG_EXAMPLES_XMLRPC_BUFFERSIZE - HTTP buffer size. Default 1024 + CONFIG_EXAMPLES_XMLRPC_DHCPC - Use DHCP Client. Default n. Ignored + if CONFIG_NSH_BUILTIN_APPS is selected. + CONFIG_EXAMPLES_XMLRPC_NOMAC - Use Canned MAC Address. Defaul n. Ignored + if CONFIG_NSH_BUILTIN_APPS is selected. + CONFIG_EXAMPLES_XMLRPC_IPADDR - Target IP address. Default 0x0a000002. + Ignored if CONFIG_NSH_BUILTIN_APPS is selected. + CONFIG_EXAMPLES_XMLRPC_DRIPADDR - Default Router IP address (Gateway). + Default 0x0a000001. Ignored if CONFIG_NSH_BUILTIN_APPS is selected. + CONFIG_EXAMPLES_XMLRPC_NETMASK - Network Mask. Default 0xffffff00 + Ignored if CONFIG_NSH_BUILTIN_APPS is selected. diff --git a/apps/examples/xmlrpc/Kconfig b/apps/examples/xmlrpc/Kconfig new file mode 100644 index 000000000..ee61feb50 --- /dev/null +++ b/apps/examples/xmlrpc/Kconfig @@ -0,0 +1,45 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config EXAMPLES_XMLRPC + bool "XML RPC example" + default n + depends on NET_TCP && !DISABLE_POLL + select NETUTILS_XMLRPC + ---help--- + An example for the netutils/xmlrpc library. + This example implements a lightweight HTTP server and uses the xmlrpc lib + for parsing xml remote procedure calls. + +config EXAMPLES_XMLRPC_BUFFERSIZE + int "HTTP buffer size" + default 1024 + +config EXAMPLES_XMLRPC_DHCPC + bool "DHCP Client" + default n + depends on EXAMPLES_XMLRPC && !NSH_BUILTIN_APPS + select NETUTILS_DHCPC + select NETUTILS_RESOLV + +config EXAMPLES_XMLRPC_NOMAC + bool "Use Canned MAC Address" + default n + depends on EXAMPLES_XMLRPC && !NSH_BUILTIN_APPS + +config EXAMPLES_XMLRPC_IPADDR + hex "Target IP address" + default 0x0a000002 + depends on EXAMPLES_XMLRPC && !NSH_BUILTIN_APPS && !EXAMPLES_XMLRPC_DHCPC + +config EXAMPLES_XMLRPC_DRIPADDR + hex "Default Router IP address (Gateway)" + default 0x0a000001 + depends on EXAMPLES_XMLRPC && !NSH_BUILTIN_APPS + +config EXAMPLES_XMLRPC_NETMASK + hex "Network Mask" + default 0xffffff00 + depends on EXAMPLES_XMLRPC && !NSH_BUILTIN_APPS diff --git a/apps/examples/xmlrpc/Makefile b/apps/examples/xmlrpc/Makefile new file mode 100644 index 000000000..9fa03bf7e --- /dev/null +++ b/apps/examples/xmlrpc/Makefile @@ -0,0 +1,106 @@ +############################################################################ +# apps/examples/xmlrpc/Makefile +# +# Copyright (C) 2012 Max Holtzberg. All rights reserved. +# Copyright (C) 2008, 2010-2012 Gregory Nutt. All rights reserved. +# +# Authors: Max Holtzberg <mh@uvc.de> +# Gregory Nutt <gnutt@nuttx.org> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +# XML RPC built-in application info + +APPNAME = xmlrpc +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = 2048 + +ASRCS = +CSRCS = xmlrpc_main.c calls.c + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(WINTOOL),y) + BIN = "${shell cygpath -w $(APPDIR)/libapps$(LIBEXT)}" +else + BIN = "$(APPDIR)/libapps$(LIBEXT)" +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: clean depend distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + @( for obj in $(OBJS) ; do \ + $(call ARCHIVE, $(BIN), $${obj}); \ + done ; ) + @touch .built + +.context: +ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) + $(call REGISTER,$(APPNAME),$(PRIORITY),$(STACKSIZE),$(APPNAME)_main) + @touch $@ +endif + +context: .context + +.depend: Makefile $(SRCS) + @$(MKDEP) $(ROOTDEPPATH) $(CC) -- $(CFLAGS) -- $(SRCS) >Make.dep + @touch $@ + +depend: .depend + +clean: + @rm -f *.o *~ .*.swp .built + $(call CLEAN) + +distclean: clean + @rm -f Make.dep .depend + +-include Make.dep diff --git a/apps/examples/xmlrpc/calls.c b/apps/examples/xmlrpc/calls.c new file mode 100644 index 000000000..bda164f4d --- /dev/null +++ b/apps/examples/xmlrpc/calls.c @@ -0,0 +1,122 @@ +/**************************************************************************** + * apps/examples/xmlrpc/calls.c + * + * Copyright (C) 2012 Max Holtzberg. All rights reserved. + * Author: Max Holtzberg <mh@uvc.de> + * + * Based on the embeddable lightweight XML-RPC server code discussed + * in the article at: http://www.drdobbs.com/web-development/\ + * an-embeddable-lightweight-xml-rpc-server/184405364 + * + * Copyright (c) 2002 Cogito LLC. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, is hereby granted without fee provided + * that the following conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * 3. Neither the name of Cogito LLC nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY COGITO LLC AND CONTRIBUTERS 'AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COGITO LLC + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARAY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <string.h> +#include <apps/netutils/xmlrpc.h> + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int calls_get_device_stats(struct xmlrpc_s *xmlcall); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +struct xmlrpc_entry_s get_device_stats = +{ + .name = "get_device_stats", + .func = calls_get_device_stats +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int calls_get_device_stats(struct xmlrpc_s *xmlcall) +{ + char username[80], password[80]; + char lastCommand[80], curState[80]; + int request = 0, status, ret; + + do + { + ret = xmlrpc_getstring(xmlcall, username); + if (ret != XMLRPC_NO_ERROR) + { + break; + } + + ret = xmlrpc_getstring(xmlcall, password); + if (ret != XMLRPC_NO_ERROR) + { + break; + } + + ret = xmlrpc_getinteger(xmlcall, &request); + if (ret != XMLRPC_NO_ERROR) + { + break; + } + } + while (0); + + if (ret == XMLRPC_NO_ERROR) + { + /* Dummy up some data... */ + + status = 1; + strcpy(lastCommand, "reboot"); + strcpy(curState, "Normal Operation"); + + ret = xmlrpc_buildresponse(xmlcall, "{iss}", + "status", status, + "lastCommand", lastCommand, + "currentState", curState); + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +void calls_register(void) +{ + xmlrpc_register(&get_device_stats); +} diff --git a/apps/examples/xmlrpc/xmlrpc_main.c b/apps/examples/xmlrpc/xmlrpc_main.c new file mode 100644 index 000000000..5f5ce5f06 --- /dev/null +++ b/apps/examples/xmlrpc/xmlrpc_main.c @@ -0,0 +1,419 @@ +/**************************************************************************** + * apps/examples/xmlrpc/xmlrpc_main.c + * + * Copyright (C) 2012 Max Holtzberg. All rights reserved. + * Author: Max Holtzberg <mh@uvc.de> + * + * Based on the embeddable lightweight XML-RPC server code discussed + * in the article at: http://www.drdobbs.com/web-development/\ + * an-embeddable-lightweight-xml-rpc-server/184405364 + * + * Copyright (c) 2002 Cogito LLC. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, is hereby granted without fee provided + * that the following conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * 3. Neither the name of Cogito LLC nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY COGITO LLC AND CONTRIBUTERS 'AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COGITO LLC + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARAY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ****************************************************************************/ + +/* + * Lightweight Embedded XML-RPC Server main + * + * mtj@cogitollc.com + * + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <debug.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <arpa/inet.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <net/if.h> +#include <nuttx/net/uip/uip.h> +#include <nuttx/net/uip/uip-arp.h> + +#include <apps/netutils/uiplib.h> +#include <apps/netutils/xmlrpc.h> + +#ifdef CONFIG_EXAMPLES_XMLRPC_DHCPC +# include <arpa/inet.h> +#endif + +/* Here we include the header file for the application(s) we use in + * our project as defined in the config/<board-name>/defconfig file + */ + +/* DHCPC may be used in conjunction with any other feature (or not) */ + +#ifdef CONFIG_EXAMPLES_XMLRPC_DHCPC +# include <apps/netutils/resolv.h> +# include <apps/netutils/dhcpc.h> +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const char *notimplemented = { "HTTP/1.1 501 Not Implemented\n\n" }; +static const char *separator = { "\015\012\015\012" }; + +/**************************************************************************** + * External Function Prototypes + ****************************************************************************/ + +extern void calls_register(void); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xmlrpc_findbody + * + * Description: + * Find the message body of an HTTP Request Message + * + ****************************************************************************/ + +static char *xmlrpc_findbody(char *buf) +{ + char *temp; + + temp = strstr(buf, separator); + + if (temp == NULL) + { + return NULL; + } + else + { + return temp + 4; + } +} + +/**************************************************************************** + * Name: xmlrpc_getheader + * + * Description: + * Find the HTTP header and return it's value. + * + ****************************************************************************/ + +static int xmlrpc_getheader(char *buffer, char *header, char *value, int size) +{ + char *temp; + int i = 0; + + temp = strstr(buffer, header); + if (temp) + { + /* Skip the header element */ + + temp += strlen(header); + + /* Skip any white-space */ + + while (*temp == ' ') + { + temp++; + } + + /* Copy the rest to the value parameter */ + + while ((*temp != ' ') && (*temp != '\n') && (i < size)) + { + value[i++] = *temp++; + } + + value[i] = 0; + return i; + } + + return -1; +} + +/**************************************************************************** + * Name: xmlrpc_handler + * + * Description: + * Parse and handle the current HTTP request message. + * + ****************************************************************************/ + +static void xmlrpc_handler(int fd) +{ + fd_set rfds; + struct timeval tv; + int ret, len, max = 0, loadlen = -1; + char buffer[CONFIG_EXAMPLES_XMLRPC_BUFFERSIZE] = { 0 }; + char value[CONFIG_XMLRPC_STRINGSIZE + 1]; + char *temp; + + /* Read in the Request Header */ + + do + { + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + tv.tv_sec = 1; + tv.tv_usec = 0; + + ndbg("[%d] select...\n", fd); + ret = select(fd + 1, &rfds, NULL, NULL, &tv); + ndbg("[%d] data ready\n", fd); + + if (ret > 0) + { + if (FD_ISSET(fd, &rfds)) + { + len = recv(fd, &buffer[max], 1024, 0); + ndbg("[%d] %d bytes received\n", fd, len); + + if (len > 0) + { + max += len; + buffer[max] = 0; + + ret = xmlrpc_getheader(buffer, "Content-Length:", value, + CONFIG_EXAMPLES_XMLRPC_BUFFERSIZE); + if (ret > 0) + loadlen = atoi(value); + } + else + { + ret = -1; + break; + } + } + } + else + { + /* Timeout... */ + + ndbg("[%d] timeout\n", fd); + ret = -1; + break; + } + + temp = strstr(buffer, separator); + + if (temp) + { + if (strlen(temp) - 4 == loadlen) + break; + } + + } + while (1); + + /* Determine request */ + + if (!strncmp(buffer, "POST", 4)) + { + temp = xmlrpc_findbody(buffer); + xmlrpc_parse(fd, temp); + } + else + { + write(fd, notimplemented, strlen(notimplemented)); + } +} + +/**************************************************************************** + * Name: xmlrpc_netinit + * + * Description: + * Setup network configuration. + * + ****************************************************************************/ + +static int xmlrpc_netinit(void) +{ + /* If this task is excecutated as an NSH built-in function, then the network + * has already been configured by NSH's start-up logic. + */ + +#ifndef CONFIG_NSH_BUILTIN_APPS + struct in_addr addr; +#if defined(CONFIG_EXAMPLES_XMLRPC_DHCPC) || defined(CONFIG_EXAMPLES_XMLRPC_NOMAC) + uint8_t mac[IFHWADDRLEN]; +#endif +#ifdef CONFIG_EXAMPLES_XMLRPC_DHCPC + void *handle; +#endif + +/* Many embedded network interfaces must have a software assigned MAC */ + +#ifdef CONFIG_EXAMPLES_XMLRPC_NOMAC + mac[0] = 0x00; + mac[1] = 0xe0; + mac[2] = 0xde; + mac[3] = 0xad; + mac[4] = 0xbe; + mac[5] = 0xef; + uip_setmacaddr("eth0", mac); +#endif + + /* Set up our host address */ + +#ifdef CONFIG_EXAMPLES_XMLRPC_DHCPC + addr.s_addr = 0; +#else + addr.s_addr = HTONL(CONFIG_EXAMPLES_XMLRPC_IPADDR); +#endif + uip_sethostaddr("eth0", &addr); + + /* Set up the default router address */ + + addr.s_addr = HTONL(CONFIG_EXAMPLES_XMLRPC_DRIPADDR); + uip_setdraddr("eth0", &addr); + + /* Setup the subnet mask */ + + addr.s_addr = HTONL(CONFIG_EXAMPLES_XMLRPC_NETMASK); + uip_setnetmask("eth0", &addr); + +#ifdef CONFIG_EXAMPLES_XMLRPC_DHCPC + /* Set up the resolver */ + + resolv_init(); + + /* Get the MAC address of the NIC */ + + uip_getmacaddr("eth0", mac); + + /* Set up the DHCPC modules */ + + handle = dhcpc_open(&mac, IFHWADDRLEN); + + /* Get an IP address. Note: there is no logic here for renewing the address + * in this example. The address should be renewed in ds.lease_time/2 + * seconds. + */ + + printf("Getting IP address\n"); + if (handle) + { + struct dhcpc_state ds; + (void)dhcpc_request(handle, &ds); + uip_sethostaddr("eth1", &ds.ipaddr); + + if (ds.netmask.s_addr != 0) + { + uip_setnetmask("eth0", &ds.netmask); + } + + if (ds.default_router.s_addr != 0) + { + uip_setdraddr("eth0", &ds.default_router); + } + + if (ds.dnsaddr.s_addr != 0) + { + resolv_conf(&ds.dnsaddr); + } + + dhcpc_close(handle); + printf("IP: %s\n", inet_ntoa(ds.ipaddr)); + } + +#endif /* CONFIG_EXAMPLES_XMLRPC_DHCPC */ +#endif /* CONFIG_NSH_BUILTIN_APPS */ + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: xmlrpc_main + * + * Description: + * The embedded HTTP server main + * + ****************************************************************************/ + +int xmlrpc_main(int argc, char *argv[]) +{ + int listenfd, connfd, on = 1; + socklen_t clilen; + struct sockaddr_in cliaddr, servaddr; + + if (xmlrpc_netinit() < 0) + { + ndbg("Could not initialize the network interface\n"); + return ERROR; + } + + /* Register RPC functions. */ + + calls_register(); + + listenfd = socket(AF_INET, SOCK_STREAM, 0); + + setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + bzero((void *)&servaddr, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = htonl(INADDR_ANY); + servaddr.sin_port = htons(80); + + bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); + + listen(listenfd, 5); + + for (;;) + { + clilen = sizeof(cliaddr); + connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen); + if (connfd <= 0) + { + break; + } + ndbg("Connection accepted: %d\n", connfd); + + xmlrpc_handler(connfd); + close(connfd); + ndbg("[%d] connection closed\n", connfd); + } + + close(listenfd); + return (0); +} diff --git a/apps/include/netutils/xmlrpc.h b/apps/include/netutils/xmlrpc.h new file mode 100644 index 000000000..3136e35ef --- /dev/null +++ b/apps/include/netutils/xmlrpc.h @@ -0,0 +1,123 @@ +/**************************************************************************** + * apps/include/netutils/xmlrpc.h + * + * Copyright (C) 2012 Max Holtzberg. All rights reserved. + * Author: Max Holtzberg <mh@uvc.de> + * + * Based on the embeddable lightweight XML-RPC server code discussed + * in the article at: http://www.drdobbs.com/web-development/\ + * an-embeddable-lightweight-xml-rpc-server/184405364 + * + * Copyright (c) 2002 Cogito LLC. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, is hereby granted without fee provided + * that the following conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * 3. Neither the name of Cogito LLC nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY COGITO LLC AND CONTRIBUTERS 'AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COGITO LLC + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARAY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ****************************************************************************/ + +/* + * Lightweight Embedded XML-RPC Server Types and Definitions + * + * mtj@cogitollc.com + * + */ + +#ifndef __APPS_INCLUDE_NETUTILS_XMLRPC_H +#define __APPS_INCLUDE_NETUTILS_XMLRPC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Error definitions. */ + +#define XMLRPC_NO_ERROR (0) +#define XMLRPC_PARSE_ERROR (-1) +#define XMLRPC_NO_SUCH_FUNCTION (-2) +#define XMLRPC_UNEXPECTED_INTEGER_ARG (-3) +#define XMLRPC_UNEXPECTED_BOOLEAN_ARG (-4) +#define XMLRPC_UNEXPECTED_DOUBLE_ARG (-5) +#define XMLRPC_UNEXPECTED_STRING_ARG (-6) +#define XMLRPC_BAD_RESPONSE_ARG (-7) +#define XMLRPC_INTERNAL_ERROR (-99) + +#define MAX_ARGS 10 +#define MAX_RESPONSE 2048 + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct xmlrpc_arg_s +{ + union + { + int i; + char boolean; + double d; + char string[CONFIG_XMLRPC_STRINGSIZE+1]; + } u; +}; + +struct xmlrpc_s +{ + char name[CONFIG_XMLRPC_STRINGSIZE+1]; + struct xmlrpc_arg_s arguments[MAX_ARGS]; + char args[MAX_ARGS]; + int argsize; + int arg; + char response[MAX_RESPONSE]; + int error; +}; + +struct xmlrpc_entry_s +{ + struct xmlrpc_entry_s *next; + int (*func)(struct xmlrpc_s*); + char *name; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +void xmlrpc_register(struct xmlrpc_entry_s *call); +int xmlrpc_parse(int sock, char *buffer); +int xmlrpc_getinteger(struct xmlrpc_s *xmlcall, int *arg); +int xmlrpc_getbool(struct xmlrpc_s *xmlcall, int *arg); +int xmlrpc_getdouble(struct xmlrpc_s *xmlcall, double *arg); +int xmlrpc_getstring(struct xmlrpc_s *xmlcall, char *arg); +int xmlrpc_buildresponse(struct xmlrpc_s *, char *, ...); + +#endif /* __APPS_INCLUDE_NETUTILS_XMLRPC_H */ diff --git a/apps/netutils/Kconfig b/apps/netutils/Kconfig index aa0f14963..4141e5b03 100644 --- a/apps/netutils/Kconfig +++ b/apps/netutils/Kconfig @@ -56,3 +56,7 @@ endmenu menu "UDP Discovery Utility" source "$APPSDIR/netutils/discover/Kconfig" endmenu + +menu "XML-RPC library" +source "$APPSDIR/netutils/xmlrpc/Kconfig" +endmenu diff --git a/apps/netutils/Make.defs b/apps/netutils/Make.defs index f957009b5..ae72ab0fd 100644 --- a/apps/netutils/Make.defs +++ b/apps/netutils/Make.defs @@ -85,3 +85,7 @@ endif ifeq ($(CONFIG_NETUTILS_DISCOVER),y) CONFIGURED_APPS += netutils/discover endif + +ifeq ($(CONFIG_NETUTILS_XMLRPC),y) +CONFIGURED_APPS += netutils/xmlrpc +endif diff --git a/apps/netutils/Makefile b/apps/netutils/Makefile index 03261c7a3..058b0f629 100644 --- a/apps/netutils/Makefile +++ b/apps/netutils/Makefile @@ -39,7 +39,7 @@ ifeq ($(CONFIG_NET),y) SUBDIRS = uiplib dhcpc dhcpd discover ftpc ftpd resolv smtp telnetd -SUBDIRS += webclient webserver tftpc thttpd +SUBDIRS += webclient webserver tftpc thttpd xmlrpc endif all: nothing diff --git a/apps/netutils/README.txt b/apps/netutils/README.txt index 231cf62c1..73e6689fe 100644 --- a/apps/netutils/README.txt +++ b/apps/netutils/README.txt @@ -66,6 +66,8 @@ highly influenced by uIP) include: CONFIGURED_APPS += uiplib CONFIGURED_APPS += thttpd + xmlrpc - The Embeddable Lightweight XML-RPC Server discussed at + http://www.drdobbs.com/web-development/an-embeddable-lightweight-xml-rpc-server/184405364 Tips for Using Telnetd ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/apps/netutils/xmlrpc/Kconfig b/apps/netutils/xmlrpc/Kconfig new file mode 100644 index 000000000..6765bda07 --- /dev/null +++ b/apps/netutils/xmlrpc/Kconfig @@ -0,0 +1,23 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config NETUTILS_XMLRPC + bool "XML RPC library" + default n + depends on NET_TCP + select NETUTILS_UIPLIB + ---help--- + Enables the Embeddable Lightweight XML-RPC Server discussed at + http://www.drdobbs.com/web-development/an-embeddable-lightweight-xml-rpc-server/184405364 + +if NETUTILS_XMLRPC + +config XMLRPC_STRINGSIZE + int "Maximum string length" + default 64 + ---help--- + Maximum string length for method names and XML RPC string values. + +endif diff --git a/apps/netutils/xmlrpc/Makefile b/apps/netutils/xmlrpc/Makefile new file mode 100644 index 000000000..903506f46 --- /dev/null +++ b/apps/netutils/xmlrpc/Makefile @@ -0,0 +1,99 @@ +############################################################################ +# apps/netutils/xmlrpc/Makefile +# +# Copyright (C) 2012 Max Holtzberg. All rights reserved. +# Copyright (C) 2011-2012 Gregory Nutt. All rights reserved. +# +# Authors: Max Holtzberg <mh@uvc.de> +# Gregory Nutt <gnutt@nuttx.org> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + + +ASRCS = +CSRCS = + +ifeq ($(CONFIG_NET_TCP),y) +CSRCS += xmlparser.c response.c +endif + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(WINTOOL),y) + BIN = "${shell cygpath -w $(APPDIR)/libapps$(LIBEXT)}" +else + BIN = "$(APPDIR)/libapps$(LIBEXT)" +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: context depend clean distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + @( for obj in $(OBJS) ; do \ + $(call ARCHIVE, $(BIN), $${obj}); \ + done ; ) + @touch .built + +context: + +.depend: Makefile $(SRCS) + @$(MKDEP) $(ROOTDEPPATH) $(CC) -- $(CFLAGS) -- $(SRCS) >Make.dep + @touch $@ + +depend: .depend + +clean: + @rm -f *.o *~ .*.swp .built + $(call CLEAN) + +distclean: clean + @rm -f Make.dep .depend + +-include Make.dep diff --git a/apps/netutils/xmlrpc/response.c b/apps/netutils/xmlrpc/response.c new file mode 100644 index 000000000..2ae7414a7 --- /dev/null +++ b/apps/netutils/xmlrpc/response.c @@ -0,0 +1,287 @@ +/**************************************************************************** + * apps/netutils/xmlrpc/response.c + * + * Copyright (C) 2012 Max Holtzberg. All rights reserved. + * Author: Max Holtzberg <mh@uvc.de> + * + * Based on the embeddable lightweight XML-RPC server code discussed + * in the article at: http://www.drdobbs.com/web-development/\ + * an-embeddable-lightweight-xml-rpc-server/184405364 + * + * Copyright (c) 2002 Cogito LLC. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, is hereby granted without fee provided + * that the following conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * 3. Neither the name of Cogito LLC nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY COGITO LLC AND CONTRIBUTERS 'AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COGITO LLC + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARAY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ****************************************************************************/ + +/* + * Lightweight Embedded XML-RPC Server Response Generator + * + * mtj@cogitollc.com + * + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> +#include <apps/netutils/xmlrpc.h> + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int xmlrpc_insertlength(struct xmlrpc_s * xmlcall) +{ + int len, digit, xdigit = 1000, i = 0; + char *temp; + + temp = strstr(xmlcall->response, "<?xml"); + len = strlen(temp); + + temp = strstr(xmlcall->response, "xyza"); + + do + { + digit = (len / xdigit); + len -= (digit * xdigit); + xdigit /= 10; + + if ((digit == 0) && (xdigit > 1)) + temp[i++] = ' '; + else + temp[i++] = (0x30 + digit); + } + while (i < 4); + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int xmlrpc_getinteger(struct xmlrpc_s * xmlcall, int *arg) +{ + if ((xmlcall == NULL) || (arg == NULL)) + { + return XMLRPC_INTERNAL_ERROR; + } + + if ((xmlcall->arg < xmlcall->argsize) && + (xmlcall->args[xmlcall->arg] == 'i')) + { + *arg = xmlcall->arguments[xmlcall->arg++].u.i; + return 0; + } + + return XMLRPC_UNEXPECTED_INTEGER_ARG; +} + +int xmlrpc_getbool(struct xmlrpc_s * xmlcall, int *arg) +{ + if ((xmlcall == NULL) || (arg == NULL)) + { + return XMLRPC_INTERNAL_ERROR; + } + + if ((xmlcall->arg < xmlcall->argsize) && + (xmlcall->args[xmlcall->arg] == 'b')) + { + *arg = xmlcall->arguments[xmlcall->arg++].u.i; + return 0; + } + + return XMLRPC_UNEXPECTED_BOOLEAN_ARG; +} + +int xmlrpc_getdouble(struct xmlrpc_s * xmlcall, double *arg) +{ + if ((xmlcall == NULL) || (arg == NULL)) + { + return XMLRPC_INTERNAL_ERROR; + } + + if ((xmlcall->arg < xmlcall->argsize) && + (xmlcall->args[xmlcall->arg] == 'd')) + { + *arg = xmlcall->arguments[xmlcall->arg++].u.d; + return 0; + } + + return XMLRPC_UNEXPECTED_DOUBLE_ARG; +} + +int xmlrpc_getstring(struct xmlrpc_s* xmlcall, char *arg) +{ + if ((xmlcall == NULL) || (arg == NULL)) + { + return XMLRPC_INTERNAL_ERROR; + } + + if ((xmlcall->arg < xmlcall->argsize) && + (xmlcall->args[xmlcall->arg] == 's')) + { + strcpy(arg, xmlcall->arguments[xmlcall->arg++].u.string); + return 0; + } + + return XMLRPC_UNEXPECTED_STRING_ARG; +} + +int xmlrpc_buildresponse(struct xmlrpc_s* xmlcall, char *args, ...) +{ + va_list argp; + int i, ret = 0, index = 0, close = 0; + double d; + char *s; + int isStruct = 0; + + if ((xmlcall == NULL) || (args == NULL)) + { + return -1; + } + + strcpy(xmlcall->response, "HTTP/1.1 200 OK\n" + "Connection: close\n" + "Content-length: xyza\n" + "Content-Type: text/xml\n" + "Server: Lightweight XMLRPC\n\n" + "<?xml version=\"1.0\"?>\n" "<methodResponse>\n"); + + if (xmlcall->error) + { + strcat(&xmlcall->response[strlen(xmlcall->response)], " <fault>\n"); + } + else + { + strcat(&xmlcall->response[strlen(xmlcall->response)], + " <params><param>\n"); + } + + va_start(argp, args); + + while (args[index]) + { + if (isStruct) + { + if ((args[index] != '{') && (args[index] != '}')) + { + sprintf(&xmlcall->response[strlen(xmlcall->response)], + " <member>\n"); + sprintf(&xmlcall->response[strlen(xmlcall->response)], + " <name>%s</name>\n", va_arg(argp, char *)); + close = 1; + } + } + + switch (args[index]) + { + case '{': + sprintf(&xmlcall->response[strlen(xmlcall->response)], + " <value><struct>\n"); + isStruct = 1; + break; + + case '}': + sprintf(&xmlcall->response[strlen(xmlcall->response)], + " </struct></value>\n"); + isStruct = 0; + break; + + case 'i': + i = va_arg(argp, int); + sprintf(&xmlcall->response[strlen(xmlcall->response)], + " <value><int>%d</int></value>\r\n", i); + break; + + case 'b': + i = va_arg(argp, int); + sprintf(&xmlcall->response[strlen(xmlcall->response)], + " <value><boolean>%d</boolean></value>\r\n", i); + break; + + case 'd': + d = va_arg(argp, double); + sprintf(&xmlcall->response[strlen(xmlcall->response)], + " <value><double>%f</double></value>\r\n", d); + break; + + case 's': + s = va_arg(argp, char *); + sprintf(&xmlcall->response[strlen(xmlcall->response)], + " <value><string>%s</string></value>\r\n", s); + break; + + default: + return (XMLRPC_BAD_RESPONSE_ARG); + break; + + } + + if (close) + { + sprintf(&xmlcall->response[strlen(xmlcall->response)], + " </member>\n"); + close = 0; + } + + index++; + } + + va_end(argp); + + if (xmlcall->error) + { + strcat(&xmlcall->response[strlen(xmlcall->response)], " </fault>\r\n"); + } + else + { + strcat(&xmlcall->response[strlen(xmlcall->response)], + " </param></params>\r\n"); + } + + if (ret == 0) + { + strcat(&xmlcall->response[strlen(xmlcall->response)], + "</methodResponse>\r\n"); + + xmlrpc_insertlength(xmlcall); + } + else + { + xmlcall->response[0] = 0; + } + + return ret; +} diff --git a/apps/netutils/xmlrpc/xmlparser.c b/apps/netutils/xmlrpc/xmlparser.c new file mode 100644 index 000000000..72387a08e --- /dev/null +++ b/apps/netutils/xmlrpc/xmlparser.c @@ -0,0 +1,416 @@ +/**************************************************************************** + * apps/netutils/xmlrpc/xmlparser.c + * + * Copyright (C) 2012 Max Holtzberg. All rights reserved. + * Author: Max Holtzberg <mh@uvc.de> + * + * Based on the embeddable lightweight XML-RPC server code discussed + * in the article at: http://www.drdobbs.com/web-development/\ + * an-embeddable-lightweight-xml-rpc-server/184405364 + * + * Copyright (c) 2002 Cogito LLC. All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, is hereby granted without fee provided + * that the following conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * 3. Neither the name of Cogito LLC nor the names of its + * contributors may be used to endorse or promote products + * derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY COGITO LLC AND CONTRIBUTERS 'AS IS' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COGITO LLC + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARAY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + ****************************************************************************/ + +/* + * Lightweight Embedded XML-RPC Server XML Parser + * + * mtj@cogitollc.com + * + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <ctype.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <apps/netutils/xmlrpc.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define TAG 0 +#define VALUE 1 +#define DONE 2 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct xmlrpc_s g_xmlcall; +static char g_data[CONFIG_XMLRPC_STRINGSIZE+1]; +static struct xmlrpc_entry_s *g_entries = NULL; + +static const char *errorStrings[] = +{ + /* 0 */ "Internal error (unknown)", + /* 1 */ "Parse Error...", + /* 2 */ "Function not found...", + /* 3 */ "Unexpected Integer Argument...", + /* 4 */ "Unexpected Boolean Argument...", + /* 5 */ "Unexpected Double Argument...", + /* 6 */ "Unexpected String Argument...", + /* 7 */ "Bad Response Argument..." +}; + +#define MAX_ERROR_CODE (sizeof(errorStrings)/sizeof(char *)) + +struct parsebuf_s +{ + char *buf; + int len; + int index; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int xmlrpc_call(struct xmlrpc_s * call) +{ + int ret = XMLRPC_NO_SUCH_FUNCTION; + struct xmlrpc_entry_s *entry = g_entries; + + while (entry != NULL) + { + if (strcmp(call->name, entry->name) == 0) + { + ret = entry->func(call); + break; + } + else + { + entry = entry->next; + } + } + + return ret; +} + +static int xmlrpc_getelement(struct parsebuf_s * pbuf, char *data, int dataSize) +{ + int j = 0; + int ret = XMLRPC_NO_ERROR; + + while (!isprint(pbuf->buf[pbuf->index])) + { + pbuf->index++; + } + + if (pbuf->index >= pbuf->len) + { + return DONE; + } + + if (pbuf->buf[pbuf->index] == '<') + { + ret = TAG; + } + else + { + ret = VALUE; + } + + data[j++] = pbuf->buf[pbuf->index++]; + + while (j < dataSize) + { + if (pbuf->buf[pbuf->index] == '>') + { + data[j++] = pbuf->buf[pbuf->index++]; + break; + } + else if ((pbuf->buf[pbuf->index] == '\n') || + (pbuf->buf[pbuf->index] == '<')) + { + break; + } + else + { + data[j++] = pbuf->buf[pbuf->index++]; + if (j >= dataSize) + ret = XMLRPC_PARSE_ERROR; + } + } + + data[j] = 0; + return ret; +} + +static int xmlrpc_parseparam(struct parsebuf_s * pbuf) +{ + int type; + + /* Next, we need a <value> tag */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if (!((type == TAG) && (!strncmp(g_data, "<value>", 7)))) + { + return XMLRPC_PARSE_ERROR; + } + + /* Now we get a variable tag, the type of the value */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if (type != TAG) + { + return XMLRPC_PARSE_ERROR; + } + + if (!strncmp(g_data, "<i4>", 4)) + { + g_xmlcall.args[g_xmlcall.argsize] = 'i'; + } + else if (!strncmp(g_data, "<int>", 5)) + { + g_xmlcall.args[g_xmlcall.argsize] = 'i'; + } + else if (!strncmp(g_data, "<boolean>", 9)) + { + g_xmlcall.args[g_xmlcall.argsize] = 'b'; + } + else if (!strncmp(g_data, "<double>", 8)) + { + g_xmlcall.args[g_xmlcall.argsize] = 'd'; + } + else if (!strncmp(g_data, "<string>", 8)) + { + g_xmlcall.args[g_xmlcall.argsize] = 's'; + } + else + { + return XMLRPC_PARSE_ERROR; + } + + /* Now, parse the actual value */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if (type != VALUE) + { + return XMLRPC_PARSE_ERROR; + } + + switch (g_xmlcall.args[g_xmlcall.argsize]) + { + case 'i': + case 'b': + g_xmlcall.arguments[g_xmlcall.argsize].u.i = atoi(g_data); + break; + case 'd': + g_xmlcall.arguments[g_xmlcall.argsize].u.d = atof(g_data); + break; + case 's': + strcpy(g_xmlcall.arguments[g_xmlcall.argsize].u.string, g_data); + break; + default: + return XMLRPC_PARSE_ERROR; + } + + g_xmlcall.argsize++; + + /* Now we close out the tag, starting with the type */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if (!((type == TAG) && (!strncmp(g_data, "</", 2)))) + { + return XMLRPC_PARSE_ERROR; + } + + /* Next, look for the </value> close */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if (!((type == TAG) && (!strncmp(g_data, "</value>", 8)))) + { + return XMLRPC_PARSE_ERROR; + } + + /* Finally, close out the </param> tag */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if (!((type == TAG) && (!strncmp(g_data, "</param>", 8)))) + { + return XMLRPC_PARSE_ERROR; + } + + return XMLRPC_NO_ERROR; +} + +static int xmlrpc_parseparams(struct parsebuf_s * pbuf) +{ + int type, ret = XMLRPC_PARSE_ERROR; + + /* First, look for the params tag */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if ((type == TAG) && (!strncmp(g_data, "<params>", 8))) + { + while (1) + { + /* Get next tag */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if ((type == TAG) && (!strncmp(g_data, "<param>", 7))) + { + ret = xmlrpc_parseparam(pbuf); + } + else if ((type == TAG) && (!strncmp(g_data, "</params>", 9))) + { + return XMLRPC_NO_ERROR; + } + else + { + return XMLRPC_PARSE_ERROR; + } + } + } + + return ret; +} + +static int xmlrpc_parsemethod(struct parsebuf_s * pbuf) +{ + int type, ret = XMLRPC_PARSE_ERROR; + + bzero((void *)&g_xmlcall, sizeof(struct xmlrpc_s)); + + /* Look for the methodName tag */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if ((type == TAG) && (!strncmp(g_data, "<methodName>", 12))) + { + /* Get the method name for the call */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if (type == VALUE) + { + /* Save the method name */ + + strcpy(g_xmlcall.name, g_data); + + /* Find the closing /methodCall */ + + type = xmlrpc_getelement(pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if ((type == TAG) && (!strncmp(g_data, "</methodName>", 13))) + { + /* Now, it's time to parse the parameters */ + + ret = xmlrpc_parseparams(pbuf); + } + } + } + + return ret; +} + +static void xmlrpc_sendfault(int fault) +{ + fault = -fault; + if (fault >= MAX_ERROR_CODE) + { + fault = 0; + } + + xmlrpc_buildresponse(&g_xmlcall, "{is}", + "faultCode", fault, "faultString", errorStrings[fault]); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int xmlrpc_parse(int sock, char *buffer) +{ + struct parsebuf_s pbuf; + int type; + int ret = XMLRPC_PARSE_ERROR; + + pbuf.buf = buffer; + pbuf.len = strlen(buffer); + pbuf.index = 0; + + /* Parse the xml header tag */ + + type = xmlrpc_getelement(&pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if ((type == TAG) && (!strncmp(g_data, "<?xml", 5))) + { + type = xmlrpc_getelement(&pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if ((type == TAG) && (!strncmp(g_data, "<methodCall>", 12))) + { + /* Parse the remaining tags within the methodCall tag */ + + xmlrpc_parsemethod(&pbuf); + + /* Check for the closing /methodCall */ + + type = xmlrpc_getelement(&pbuf, g_data, CONFIG_XMLRPC_STRINGSIZE); + if ((type == TAG) && (!strncmp(g_data, "</methodCall>", 13))) + { + /* Successful parse, try to call a user function */ + + ret = xmlrpc_call(&g_xmlcall); + } + } + } + + if (ret == 0) + { + write(sock, g_xmlcall.response, strlen(g_xmlcall.response)); + } + else + { + /* Send fault response */ + + g_xmlcall.error = 1; + xmlrpc_sendfault(ret); + write(sock, g_xmlcall.response, strlen(g_xmlcall.response)); + } + + return ret; +} + +void xmlrpc_register(struct xmlrpc_entry_s *entry) +{ + if (g_entries == NULL) + { + g_entries = entry; + entry->next = NULL; + } + else + { + entry->next = g_entries; + g_entries = entry; + } +} |