/***************************************************************************** * * FILE: sxmlrpc.c * DESCRIPTION: Skimpy XML-RPC library file * DATE: Sat, Mar 18 2006 * UPDATED: Tue, Sep 4 2007 * AUTHOR: Kouichi ABE (WALL) / °¤Éô¹¯°ì * E-MAIL: kouichi@MysticWALL.COM * URL: http://www.MysticWALL.COM/ * COPYRIGHT: (c) 2006-2007 °¤Éô¹¯°ì¡¿Kouichi ABE (WALL), All rights reserved. * LICENSE: * * Copyright (c) 2006-2007 Kouichi ABE (WALL) , * All rights reserved. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. * * $Id: sxmlrpc.c,v 1.10 2007/09/04 08:18:02 kouichi Exp $ * *****************************************************************************/ #if HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #if HAVE_STDLIB_H #include #endif /* HAVE_STDLIB_H */ #if HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #if HAVE_STRING_H #include #endif /* HAVE_STRING_H */ #if HAVE_FCNTL_H #include #endif /* HAVE_FCNTL_H */ #if HAVE_NETINET_IN_H #include #endif /* HAVE_NETINET_IN_H */ #if HAVE_SYS_TYPES_H #include #endif /* HAVE_SYS_TYPES_H */ #if HAVE_SYS_STAT_H #include #endif /* HAVE_SYS_STAT_H */ #include #if HAVE_SYS_SOCKET_H #include #endif /* HAVE_SYS_SOCKET_H */ #if HAVE_NETDB_H #include #endif /* HAVE_NETDB_H */ #if HAVE_NETINET_IN_H #include #endif /* HAVE_NETINET_IN_H */ #include #include #include #include "sxml.h" #include "sxmlrpc.h" /****************************************************************************** * * Macros and structures definition * *****************************************************************************/ #define EOL '\0' #define LF '\012' #define CR '\015' #define SPC '\040' #define CRLF "\015\012" /* easy network I/O manager */ typedef struct _netfd { #define netfd_fileno(x) ((x)->osfd) int osfd; /* underlying OS file descriptor */ #define NETFD_BUFSIZE (1024*4) struct { /* read buffer */ int total; /* initialize to 0 */ char * bufp; /* initialize to buf */ char buf[NETFD_BUFSIZE]; } rb; } netfd_t; #define NETFD_SEEK_SET SEEK_SET #define NETFD_SEEK_CUR SEEK_CUR #define NETFD_SEEK_END SEEK_END #define NETFD_DEF_BACKLOG (8) /* default encoding */ #if 1 #define SXMLRPC_ENCODING "us-ascii" #else #define SXMLRPC_ENCODING "utf-8" #endif /* default XML-RPC XML version */ #define SXMLRPC_XML_VERSION "1.0" /* default User-Agent of Request Header */ #define SXMLRPC_USER_AGENT SXMLRPC_VERSION /* default Server of Response Header */ #define SXMLRPC_SERVER SXMLRPC_VERSION /* default Content-Type of Request Header */ #define SXMLRPC_CONTENT_TYPE "text/xml" /* headers */ #define CONTENT_LENGTH "Content-Length" #define CONTENT_TYPE "Content-Type" /* mkstemp(3) */ #define SXMLRPC_TEMPLATE "/tmp/.sxmlrpc.XXXXXX" #define xfree(x) \ do { if ((x)!=NULL) { free((x)); (x)=NULL; } } while (0) /****************************************************************************** * * Lobal functions declaration * *****************************************************************************/ static void flush_params(sxmlrpc_params_t *); static int make_request_body(sxmlrpc_t *, const char *, sxmlrpc_param_t *, size_t); static sxml_node_t * graft_request_body(const char *, sxmlrpc_param_t *, size_t); static int make_request_head(sxmlrpc_t *); static int make_response_body(sxmlrpc_t *, sxmlrpc_param_t *); static sxml_node_t * graft_response_body(sxmlrpc_param_t *); static sxml_node_t * graft_fault_body(sxmlrpc_param_t *); static int make_response_head(sxmlrpc_t *); static int send_message(sxmlrpc_t *, netfd_t *, netfd_t *); static sxml_node_t * recv_message(sxmlrpc_t *, netfd_t *, netfd_t *); static int parse_start_line(sxmlrpc_t *, char *); static int make_new_value(sxml_node_t *, sxmlrpc_value_t *); static int get_call_params(sxmlrpc_t *, sxml_node_t *); static size_t set_call_params(sxmlrpc_t *, sxml_node_t *); static sxml_node_t * get_response_value(sxmlrpc_t *, sxml_node_t *); static int set_value(sxmlrpc_value_t *, sxml_node_t *); static int set_value_member(sxmlrpc_member_t *, sxml_node_t *); static int set_value_array(sxmlrpc_value_t *, sxml_node_t *); static size_t count_children(sxml_node_t *); static netfd_t * netfd_open(const char *, int, mode_t); static netfd_t * netfd_fdopen(int); static netfd_t * netfd_new(int); static int netfd_close(netfd_t *); static off_t netfd_seek(netfd_t *, off_t, int); static ssize_t netfd_read(netfd_t *, void *, size_t); static ssize_t netfd_write(netfd_t *, const void *, size_t); static ssize_t netfd_readline(netfd_t *, void *, size_t); static ssize_t one_read(netfd_t *, char *); static int netfd_readbuf(netfd_t *); static netfd_t * netfd_mkstemp(char *); #if HAVE_FREEBSD_SENDFILE static int netfd_sendfile(netfd_t *, netfd_t *, off_t, size_t, struct sf_hdtr *, off_t *, int); #elif HAVE_LINUX_SENDFILE static int netfd_sendfile(netfd_t *, netfd_t *, off_t, size_t, void *, off_t *, int); #else static int netfd_sendfile(netfd_t *, netfd_t *, off_t, size_t, void *, off_t *, int); #endif static void sigalrm(int signo); static netfd_t * netfd_tcp_client(const char *, const char *); static netfd_t * netfd_tcp_server(const char *, const char *, const int); static netfd_t * netfd_accept(netfd_t *, struct sockaddr *, socklen_t *); static int netfd_sock_ntop(const struct sockaddr * sa0, char ** ipaddr); static int my_close(int fd); /****************************************************************************** * * Lobal variable definition * *****************************************************************************/ static sig_atomic_t timedout = 0; /* flag for connect() */ /****************************************************************************** * * Functions definition * *****************************************************************************/ sxmlrpc_t * sxmlrpc_new(hostname, servname, path) const char * hostname; const char * servname; const char * path; { sxmlrpc_t * new; new = (sxmlrpc_t *)calloc(1, sizeof(sxmlrpc_t)); if (new != NULL) { new->hostname = strdup(hostname); new->servname = strdup(servname); new->path = strdup(path); new->encoding = NULL; } return new; } void sxmlrpc_free(sxRPC) sxmlrpc_t * sxRPC; { if (sxRPC != NULL) { xfree(sxRPC->hostname); xfree(sxRPC->servname); xfree(sxRPC->path); xfree(sxRPC->encoding); xfree(sxRPC); } } void sxmlrpc_flush(sxRPC) sxmlrpc_t * sxRPC; { if (sxRPC != NULL) { switch (sxRPC->role) { case SXMLRPC_ROLE_SERVER: xfree(sxRPC->call_method); flush_params(&sxRPC->call_params); break; case SXMLRPC_ROLE_CLIENT: sxmlrpc_flush_value(&sxRPC->response_value); default: break; } } } void sxmlrpc_flush_value(v) sxmlrpc_value_t * v; { if (v != NULL) { register size_t i; switch (v->type) { case SXMLRPC_VALUE_STRING: case SXMLRPC_VALUE_DATETIME: case SXMLRPC_VALUE_BASE64: xfree(v->u.sval); break; case SXMLRPC_VALUE_STRUCT: for (i = 0; i < v->u.tval.size; i++) { xfree(v->u.tval.member[i].name); sxmlrpc_flush_value(&v->u.tval.member[i].value); } xfree(v->u.tval.member); v->u.tval.size = 0; break; case SXMLRPC_VALUE_ARRAY: for (i = 0; i < v->u.aval.size; i++) { sxmlrpc_flush_value(&v->u.aval.value[i]); } xfree(v->u.aval.value); v->u.aval.size = 0; break; default: break; } } } static void flush_params(params) sxmlrpc_params_t * params; { if (params->param != NULL) { register size_t i; for (i = 0; i < params->size; i++) { sxmlrpc_flush_value(¶ms->param[i].value); } xfree(params->param); params->size = 0; } } /*****************************************************************************/ int sxmlrpc_call(sxRPC, method_name, param, param_num) sxmlrpc_t * sxRPC; const char * method_name; sxmlrpc_param_t * param; size_t param_num; { netfd_t * nfd; char template[] = SXMLRPC_TEMPLATE; int status = -1; if (sxRPC == NULL) { errno = EINVAL; return -1; } nfd = netfd_mkstemp(template); if (nfd != NULL) { sxRPC->msgbdy_fd = netfd_fileno(nfd); sxRPC->role = SXMLRPC_ROLE_CLIENT; sxRPC->method = SXMLRPC_METHOD_CALL; if (make_request_body(sxRPC, method_name, param, param_num) == 0) { if (make_request_head(sxRPC) == 0) { netfd_t * nsd; nsd = netfd_tcp_client(sxRPC->hostname, sxRPC->servname); if (nsd != NULL) { status = send_message(sxRPC, nfd, nsd); if (status == 0) { netfd_close(nfd); nfd = netfd_open(template, O_RDWR|O_TRUNC, 0600); if (nfd != NULL) { sxml_node_t * root; root = recv_message(sxRPC, nfd, nsd); if (root != NULL) { sxml_node_t * np; np = get_response_value(sxRPC, root); if (np != NULL) { status = set_value(&sxRPC->response_value, np); } sxml_delete_node(root); } } } netfd_close(nsd); } } } unlink(template); netfd_close(nfd); } return status; } static int make_request_body(sxRPC, method_name, param, param_num) sxmlrpc_t * sxRPC; const char * method_name; sxmlrpc_param_t * param; size_t param_num; { int fd; int status = -1; fd = dup(sxRPC->msgbdy_fd); if (fd != -1) { register FILE * fout; fout = fdopen(fd, "r+"); if (fout != NULL) { sxml_node_t * root; root = graft_request_body(method_name, param, param_num); if (root != NULL) { sxml_node_t * np; np = sxml_new_prolog(root, "xml"); if (np != NULL) { sxml_set_attribute(np, "encoding", sxRPC->encoding ? sxRPC->encoding : SXMLRPC_ENCODING); sxml_set_attribute(np, "version", SXMLRPC_XML_VERSION); } sxml_print_tree(root, fout); sxml_delete_node(root); if (fseek(fout, 0L, SEEK_SET) == 0) { struct stat sbuf; status = fstat(fd, &sbuf); if (status == 0) { sxRPC->msgbdy_size = (size_t)sbuf.st_size; } } } fclose(fout); } close(fd); } return status; } static sxml_node_t * graft_request_body(method_name, param, param_num) const char * method_name; sxmlrpc_param_t * param; size_t param_num; { sxml_node_t * root; root = sxml_new_vertex(); if (root != NULL) { sxml_node_t * mc; mc = sxml_new_element(root, "methodCall"); if (mc != NULL) { sxml_node_t * ps; sxml_set_node(mc, "methodName", method_name); ps = sxml_new_element(mc, "params"); if (ps != NULL) { register size_t i; for (i = 0; i < param_num; i++) { sxml_node_t * p; p = sxml_new_element(ps, "param"); if (p != NULL) { make_new_value(p, ¶m[i].value); } } } } } return root; } static int make_request_head(sxRPC) sxmlrpc_t * sxRPC; { sxRPC->msghdr_size = asprintf(&sxRPC->msghdr_text, "POST %s HTTP/1.0" CRLF "Host: %s:%s" CRLF "User-Agent: " SXMLRPC_USER_AGENT CRLF "Content-Type: " SXMLRPC_CONTENT_TYPE CRLF "Content-Length: %d" CRLF CRLF , sxRPC->path, sxRPC->hostname, sxRPC->servname, sxRPC->msgbdy_size); return (sxRPC->msghdr_text != NULL) ? 0 : -1; } /*****************************************************************************/ static sxmlrpc_boolean_t forever = true; static void trap(int signo) { forever = false; } int sxmlrpc_server(sxRPC, backlog, callback) sxmlrpc_t * sxRPC; const int backlog; sxmlrpc_callback_t callback; { netfd_t * listenfd; if (callback == NULL) { errno = EINVAL; return -1; } listenfd = netfd_tcp_server(sxRPC->hostname, sxRPC->servname, backlog); if (listenfd != NULL) { struct sigaction act; sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = (void *)trap; sigaction(SIGINT, &act, NULL); sigaction(SIGQUIT, &act, NULL); sigaction(SIGTERM, &act, NULL); sigaction(SIGBUS, &act, NULL); sigaction(SIGSEGV, &act, NULL); while (forever) { netfd_t * connfd; struct sockaddr addr; socklen_t addrlen; connfd = netfd_accept(listenfd, &addr, &addrlen); if (connfd != NULL) { netfd_t * nfd; char template[] = SXMLRPC_TEMPLATE; nfd = netfd_mkstemp(template); if (nfd != NULL) { sxml_node_t * root; sxRPC->msgbdy_fd = netfd_fileno(nfd); root = recv_message(sxRPC, nfd, connfd); if (root != NULL) { int status; status = get_call_params(sxRPC, root); if (status != -1) { sxmlrpc_param_t param; int status; char * client; status = netfd_sock_ntop(&addr, &client); if ((*callback)(client, sxmlrpc_method_name(sxRPC), &sxmlrpc_call_params(sxRPC), ¶m) == 0) { sxmlrpc_response_ok(sxRPC); } else { sxmlrpc_response_fault(sxRPC); } if (status == 0) { free(client); } netfd_close(nfd); nfd = netfd_open(template, O_RDWR|O_TRUNC, 0600); if (nfd != NULL) { if (make_response_body(sxRPC, ¶m) == 0) { if (make_response_head(sxRPC) == 0) { send_message(sxRPC, nfd, connfd); } } } sxmlrpc_flush(sxRPC); sxmlrpc_flush_value(¶m.value); } sxml_delete_node(root); } unlink(template); netfd_close(nfd); } netfd_close(connfd); } } netfd_close(listenfd); } return 0; } static int make_response_body(sxRPC, param) sxmlrpc_t * sxRPC; sxmlrpc_param_t * param; { int fd; int status = -1; fd = dup(sxRPC->msgbdy_fd); if (fd != -1) { register FILE * fout; fout = fdopen(fd, "r+"); if (fout != NULL) { sxml_node_t * root = NULL; switch (sxRPC->method) { case SXMLRPC_METHOD_RESPONSE: root = graft_response_body(param); break; case SXMLRPC_METHOD_RESPONSE_FAULT: root = graft_fault_body(param); break; default: break; } if (root != NULL) { sxml_node_t * np; np = sxml_new_prolog(root, "xml"); if (np != NULL) { sxml_set_attribute(np, "encoding", sxRPC->encoding ? sxRPC->encoding : SXMLRPC_ENCODING); sxml_set_attribute(np, "version", SXMLRPC_XML_VERSION); } sxml_print_tree(root, fout); sxml_delete_node(root); if (fseek(fout, 0L, SEEK_SET) == 0) { struct stat sbuf; status = fstat(fd, &sbuf); if (status == 0) { sxRPC->msgbdy_size = (size_t)sbuf.st_size; } } } fclose(fout); } close(fd); } return status; } static sxml_node_t * graft_response_body(param) sxmlrpc_param_t * param; { sxml_node_t * root; root = sxml_new_vertex(); if (root != NULL) { sxml_node_t * mr; mr = sxml_new_element(root, "methodResponse"); if (mr != NULL) { sxml_node_t * ps; ps = sxml_new_element(mr, "params"); if (ps != NULL) { sxml_node_t * p; p = sxml_new_element(ps, "param"); if (p != NULL) { make_new_value(p, ¶m->value); } } } } return root; } static sxml_node_t * graft_fault_body(param) sxmlrpc_param_t * param; { sxml_node_t * root; root = sxml_new_vertex(); if (root != NULL) { sxml_node_t * mr; mr = sxml_new_element(root, "methodResponse"); if (mr != NULL) { sxml_node_t * fp; fp = sxml_new_element(mr, "fault"); if (fp != NULL) { make_new_value(fp, ¶m->value); } } } return root; } static int make_response_head(sxRPC) sxmlrpc_t * sxRPC; { sxRPC->msghdr_size = asprintf(&sxRPC->msghdr_text, "HTTP/1.0 200 OK" CRLF "Connection: close" CRLF "Content-Type: " SXMLRPC_CONTENT_TYPE CRLF "Content-Length: %d" CRLF "Server: " SXMLRPC_SERVER CRLF CRLF , sxRPC->msgbdy_size); return (sxRPC->msghdr_text != NULL) ? 0 : -1; } /*****************************************************************************/ static int send_message(sxRPC, nfd, nsd) sxmlrpc_t * sxRPC; netfd_t * nfd; netfd_t * nsd; { int status = -1; ssize_t n; n = netfd_write(nsd, sxRPC->msghdr_text, (size_t)sxRPC->msghdr_size); if (n == (ssize_t)sxRPC->msghdr_size) { off_t sbytes; status = netfd_sendfile(nfd, nsd, 0, sxRPC->msgbdy_size, NULL, &sbytes, 0); } xfree(sxRPC->msghdr_text); return status; } static sxml_node_t * recv_message(sxRPC, nfd, nsd) sxmlrpc_t * sxRPC; netfd_t * nfd; netfd_t * nsd; { char buf[BUFSIZ]; ssize_t n; memset(buf, 0, sizeof(buf)); n = netfd_readline(nsd, buf, sizeof(buf)); if (n > 0) { size_t len = 0; /* Content-Length */ size_t l; if (parse_start_line(sxRPC, buf) == -1) { return NULL; } do { #define MAX_TOKEN (2) char ** ap; char * args[MAX_TOKEN]; char * p; memset(buf, 0, sizeof(buf)); n = netfd_readline(nsd, buf, sizeof(buf)); if (n <= 0) { return NULL; } for (n = 0, p = buf, ap = args; (*ap = strsep(&p, ": \r\n")) != NULL; ) { if (**ap != EOL) { n++; if (++ap >= &args[MAX_TOKEN]) { break; } } } if (n == MAX_TOKEN) { if (strncmp(args[0], CONTENT_LENGTH, sizeof(CONTENT_LENGTH) - 1) == 0) { len = (size_t)strtol(args[1], (char **)NULL, 10); } else if (strncmp(args[0], CONTENT_TYPE, sizeof(CONTENT_TYPE) - 1) == 0) { if (strncmp(args[1], SXMLRPC_CONTENT_TYPE, sizeof(SXMLRPC_CONTENT_TYPE) - 1) != 0) { return NULL; } } } } while (buf[0] != EOL); for (n = 0, l = 0; l < len; l += n) { memset(buf, 0, sizeof(buf)); n = netfd_read(nsd, buf, sizeof(buf)); if (n > 0) { if (netfd_write(nfd, buf, (size_t)n) != n) { return NULL; } } else { return NULL; } } netfd_seek(nfd, 0L, NETFD_SEEK_SET); return sxml_parse_file(nfd->osfd); } return NULL; } static int parse_start_line(sxRPC, buf) sxmlrpc_t * sxRPC; char * buf; { #define MAX_START_LINE_TOKEN (3) char ** ap; char * args[MAX_START_LINE_TOKEN]; char * p; int n; for (n = 0, p = buf, ap = args; (*ap = strsep(&p, " \r\n")) != NULL; ) { if (**ap != EOL) { n++; if (++ap >= &args[MAX_START_LINE_TOKEN]) { break; } } } if (n == MAX_START_LINE_TOKEN) { switch (sxRPC->role) { case SXMLRPC_ROLE_SERVER: return (strcmp(args[0], "POST") == 0) ? 0 : -1; case SXMLRPC_ROLE_CLIENT: return (strcmp(args[1], "200") == 0) ? 0 : -1; default: break; } } return -1; } /*****************************************************************************/ static int make_new_value(node, value) sxml_node_t * node; sxmlrpc_value_t * value; { sxml_node_t * ep; ep = sxml_new_element(node, "value"); if (ep != NULL) { sxml_node_t * np = NULL; char * s; switch (value->type) { case SXMLRPC_VALUE_INTEGER: asprintf(&s, "%d", value->u.ival); if (s != NULL) { np = sxml_set_node(ep, "int", s); free(s); } break; case SXMLRPC_VALUE_DOUBLE: asprintf(&s, "%f", value->u.dval); if (s != NULL) { np = sxml_set_node(ep, "double", s); free(s); } break; case SXMLRPC_VALUE_BOOLEAN: asprintf(&s, "%d", value->u.bval); if (s != NULL) { np = sxml_set_node(ep, "boolean", s); free(s); } break; case SXMLRPC_VALUE_STRING: np = sxml_set_node(ep, "string", value->u.sval); break; case SXMLRPC_VALUE_DATETIME: np = sxml_set_node(ep, "dateTime.iso8601", value->u.sval); break; case SXMLRPC_VALUE_BASE64: np = sxml_set_node(ep, "base64", value->u.sval); break; case SXMLRPC_VALUE_STRUCT: np = sxml_new_element(ep, "struct"); if (np != NULL) { register size_t i; for (i = 0; i < value->u.tval.size; i++) { sxml_node_t * mp; mp = sxml_new_element(np, "member"); if (mp != NULL) { sxml_set_node(mp, "name", value->u.tval.member[i].name); make_new_value(mp, &value->u.tval.member[i].value); } } } break; case SXMLRPC_VALUE_ARRAY: np = sxml_new_element(ep, "array"); if (np != NULL) { sxml_node_t * dp; dp = sxml_new_element(np, "data"); if (dp != NULL) { register size_t i; for (i = 0; i < value->u.aval.size; i++) { make_new_value(dp, &value->u.aval.value[i]); } } } break; default: break; } return np != NULL ? 0 : -1; } return -1; } static int get_call_params(sxRPC, root) sxmlrpc_t * sxRPC; sxml_node_t * root; { register sxml_node_t * np; sxRPC->role = SXMLRPC_ROLE_SERVER; sxRPC->method = SXMLRPC_METHOD_NONE; np = sxml_find_element(root, "methodCall", NULL, NULL); if (np != NULL) { for (np = np->child; np != NULL; np = np->next) { const char * content; if (np->type != SXML_ELEMENT) { continue; } if (np->value.element.name == NULL) { continue; } content = sxml_get_content(np->child); if (strcmp(np->value.element.name, "methodName") == 0) { sxRPC->call_method = strdup(content); } else if (strcmp(np->value.element.name, "params") == 0) { return set_call_params(sxRPC, np->child) > 0 ? 0 : -1; } } } return -1; } static size_t set_call_params(sxRPC, node) sxmlrpc_t * sxRPC; sxml_node_t * node; { size_t status; status = sxRPC->call_params.size = count_children(node); if (sxRPC->call_params.size > 0) { sxRPC->call_params.param = (sxmlrpc_param_t *)calloc(sxRPC->call_params.size, sizeof(sxmlrpc_param_t)); if (sxRPC->call_params.param != NULL) { register sxml_node_t * np; register size_t i; for (np = node, i = 0; np != NULL && i < sxRPC->call_params.size; np = np->next, i++) { if (np->child != NULL && strcmp(np->child->value.element.name, "value") == 0) { set_value(&sxRPC->call_params.param[i].value, np->child->child); } } } } return status; } static sxml_node_t * get_response_value(sxRPC, root) sxmlrpc_t * sxRPC; sxml_node_t * root; { register sxml_node_t * np; np = sxml_find_element(root, "methodResponse", NULL, NULL); if (np != NULL) { np = np->child; if (np != NULL) { if (strcmp(np->value.element.name, "params") == 0) { np = np->child; if (np != NULL && strcmp(np->value.element.name, "param") == 0) { np = np->child; if (np != NULL && strcmp(np->value.element.name, "value") == 0) { sxmlrpc_response_ok(sxRPC); return np->child; } } } else if (strcmp(np->value.element.name, "fault") == 0) { np = np->child; if (np != NULL && strcmp(np->value.element.name, "value") == 0) { sxmlrpc_response_fault(sxRPC); return np->child; } } } } return NULL; } static int set_value(v, node) sxmlrpc_value_t * v; sxml_node_t * node; { register sxml_node_t * np; for (np = node; np != NULL; np = np->next) { const char * content; if (np->type != SXML_ELEMENT) { continue; } if (np->value.element.name == NULL) { continue; } content = sxml_get_content(np->child); if (strcmp(np->value.element.name, "i4") == 0 || strcmp(np->value.element.name, "int") == 0) { v->type = SXMLRPC_VALUE_INTEGER; v->u.ival = (sxmlrpc_int_t)strtol(content, (char **)NULL, 10); } else if (strcmp(np->value.element.name, "double") == 0) { v->type = SXMLRPC_VALUE_DOUBLE; v->u.dval = (sxmlrpc_double_t)strtod(content, (char **)NULL); } else if (strcmp(np->value.element.name, "boolean") == 0) { v->type = SXMLRPC_VALUE_BOOLEAN; v->u.bval = (sxmlrpc_boolean_t)strtol(content, (char **)NULL, 10); } else if (strcmp(np->value.element.name, "string") == 0) { v->type = SXMLRPC_VALUE_STRING; v->u.sval = (sxmlrpc_string_t)strdup(content); } else if (strcmp(np->value.element.name, "dateTime.iso8601") == 0) { v->type = SXMLRPC_VALUE_DATETIME; v->u.sval = (sxmlrpc_string_t)strdup(content); } else if (strcmp(np->value.element.name, "base64") == 0) { v->type = SXMLRPC_VALUE_BASE64; v->u.sval = (sxmlrpc_string_t)strdup(content); } else if (strcmp(np->value.element.name, "struct") == 0) { sxmlrpc_struct_t tval; tval.size = count_children(np->child); if (tval.size > 0) { tval.member = (sxmlrpc_member_t *)calloc(tval.size, sizeof(sxmlrpc_member_t)); if (tval.member != NULL) { set_value_member(tval.member, np->child); } } v->type = SXMLRPC_VALUE_STRUCT; v->u.tval = tval; } else if (strcmp(np->value.element.name, "array") == 0) { np = np->child; if (np != NULL && strcmp(np->value.element.name, "data") == 0) { sxmlrpc_array_t aval; aval.size = count_children(np->child); if (aval.size > 0) { aval.value = (sxmlrpc_value_t *)calloc(aval.size, sizeof(sxmlrpc_value_t)); if (aval.value != NULL) { set_value_array(aval.value, np->child); } } v->type = SXMLRPC_VALUE_ARRAY; v->u.aval = aval; } } else { return -1; /* error */ } } return 0; } static int set_value_member(m, node) sxmlrpc_member_t * m; sxml_node_t * node; { register sxml_node_t * np; register int i = 0; for (np = node; np != NULL; np = np->next) { if (np->type != SXML_ELEMENT) { continue; } if (np->value.element.name == NULL) { continue; } if (strcmp(np->value.element.name, "member") == 0) { register sxml_node_t * mp; for (mp = np->child; mp != NULL; mp = mp->next) { const char * content; content = sxml_get_content(mp->child); if (strcmp(mp->value.element.name, "name") == 0 && content != NULL) { m[i].name = strdup(content); } else if (strcmp(mp->value.element.name, "value") == 0) { set_value(&m[i].value, mp->child); } } i++; } } return 0; } static int set_value_array(v, node) sxmlrpc_value_t * v; sxml_node_t * node; { register sxml_node_t * np; register int i = 0; for (np = node; np != NULL; np = np->next) { if (np->type != SXML_ELEMENT) { continue; } if (np->value.element.name == NULL) { continue; } if (strcmp(np->value.element.name, "value") == 0) { set_value(&v[i], np->child); i++; } } return 0; } static size_t count_children(node) sxml_node_t * node; { register sxml_node_t * np; register size_t c; for (c = 0, np = node; np != NULL; np = np->next, c++) { ; /* nothing to do */ } return c; } /*****************************************************************************/ int sxmlrpc_set_value_int(value, ival) sxmlrpc_value_t * value; sxmlrpc_int_t ival; { if (value == NULL) { errno = EINVAL; return -1; } value->type = SXMLRPC_VALUE_INTEGER; value->u.ival = ival; return 0; } int sxmlrpc_set_value_double(value, dval) sxmlrpc_value_t * value; sxmlrpc_double_t dval; { if (value == NULL) { errno = EINVAL; return -1; } value->type = SXMLRPC_VALUE_DOUBLE; value->u.dval = dval; return 0; } int sxmlrpc_set_value_boolean(value, bval) sxmlrpc_value_t * value; sxmlrpc_boolean_t bval; { if (value == NULL) { errno = EINVAL; return -1; } value->type = SXMLRPC_VALUE_BOOLEAN; value->u.bval = bval; return 0; } int sxmlrpc_set_value_string(value, sval) sxmlrpc_value_t * value; sxmlrpc_string_t sval; { if (value == NULL) { errno = EINVAL; return -1; } value->type = SXMLRPC_VALUE_STRING; value->u.sval = strdup(sval); return 0; } int sxmlrpc_set_value_datetime(value, sval) sxmlrpc_value_t * value; sxmlrpc_string_t sval; { if (value == NULL) { errno = EINVAL; return -1; } value->type = SXMLRPC_VALUE_DATETIME; value->u.sval = strdup(sval); return 0; } int sxmlrpc_set_value_base64(value, sval) sxmlrpc_value_t * value; sxmlrpc_string_t sval; { if (value == NULL) { errno = EINVAL; return -1; } value->type = SXMLRPC_VALUE_BASE64; value->u.sval = strdup(sval); return 0; } int sxmlrpc_set_value_struct(value, tval) sxmlrpc_value_t * value; sxmlrpc_struct_t tval; { if (value == NULL) { errno = EINVAL; return -1; } value->type = SXMLRPC_VALUE_STRUCT; value->u.tval = tval; return 0; } int sxmlrpc_set_value_array(value, aval) sxmlrpc_value_t * value; sxmlrpc_array_t aval; { if (value == NULL) { errno = EINVAL; return -1; } value->type = SXMLRPC_VALUE_ARRAY; value->u.aval = aval; return 0; } int sxmlrpc_set_param(param, value) sxmlrpc_param_t * param; sxmlrpc_value_t value; { if (param == NULL) { errno = EINVAL; return -1; } param->value = value; return 0; } int sxmlrpc_set_fault(param, code, string) sxmlrpc_param_t * param; int code; sxmlrpc_string_t string; { sxmlrpc_member_t * mval; mval = (sxmlrpc_member_t *)calloc(2, sizeof(sxmlrpc_member_t)); if (mval != NULL) { mval[0].name = strdup("faultCode"); sxmlrpc_set_value_int(&mval[0].value, code); mval[1].name = strdup("faultString"); sxmlrpc_set_value_string(&mval[1].value, string); param->value.u.tval.size = 2; param->value.u.tval.member = mval; } else { param->value.u.tval.size = 0; param->value.u.tval.member = NULL; } param->value.type = SXMLRPC_VALUE_STRUCT; return -1; /* fixed value returns */ } /*****************************************************************************/ int sxmlrpc_get_fault_code(sxRPC) sxmlrpc_t * sxRPC; { return (sxRPC->method == SXMLRPC_METHOD_RESPONSE_FAULT) ? sxRPC->fault_code : 0; } sxmlrpc_string_t sxmlrpc_get_fault_string(sxRPC) sxmlrpc_t * sxRPC; { return (sxRPC->method == SXMLRPC_METHOD_RESPONSE_FAULT) ? sxRPC->fault_string : NULL; } sxmlrpc_value_t * sxmlrpc_get_response_value(sxRPC) sxmlrpc_t * sxRPC; { return (sxRPC->method == SXMLRPC_METHOD_RESPONSE) ? &sxRPC->response_value : NULL; } void sxmlrpc_print_value(v, fout) sxmlrpc_value_t * v; register FILE * fout; { switch (sxmlrpc_get_value_type(v)) { case SXMLRPC_VALUE_INTEGER: fprintf(fout, "%d\n", sxmlrpc_get_value_int(v)); break; case SXMLRPC_VALUE_DOUBLE: fprintf(fout, "%f\n", sxmlrpc_get_value_double(v)); break; case SXMLRPC_VALUE_BOOLEAN: fprintf(fout, "%d\n", sxmlrpc_get_value_boolean(v)); break; case SXMLRPC_VALUE_STRING: fprintf(fout, "%s\n", sxmlrpc_get_value_string(v)); break; case SXMLRPC_VALUE_DATETIME: fprintf(fout, "%s\n", sxmlrpc_get_value_datetime(v)); break; case SXMLRPC_VALUE_BASE64: fprintf(fout, "%s\n", sxmlrpc_get_value_base64(v)); break; case SXMLRPC_VALUE_STRUCT: { sxmlrpc_member_t * mval; register size_t i; mval = sxmlrpc_get_struct_member(v); for (i = 0; i < sxmlrpc_get_struct_size(v); i++) { fprintf(fout, "%s:\t", mval[i].name); sxmlrpc_print_value(&mval[i].value, fout); } } break; case SXMLRPC_VALUE_ARRAY: { sxmlrpc_value_t * val; register size_t i; val = sxmlrpc_get_array_value(v); for (i = 0; i < sxmlrpc_get_array_size(v); i++) { sxmlrpc_print_value(&val[i], fout); } } default: break; } } void sxmlrpc_copy_value(dst, src) sxmlrpc_value_t * dst; sxmlrpc_value_t * src; { dst->type = src->type; switch (src->type) { case SXMLRPC_VALUE_INTEGER: dst->u.ival = src->u.ival; break; case SXMLRPC_VALUE_DOUBLE: dst->u.dval = src->u.dval; break; case SXMLRPC_VALUE_BOOLEAN: dst->u.bval = src->u.bval; break; case SXMLRPC_VALUE_STRING: case SXMLRPC_VALUE_DATETIME: case SXMLRPC_VALUE_BASE64: dst->u.sval = strdup(src->u.sval); break; case SXMLRPC_VALUE_STRUCT: dst->u.tval.size = src->u.tval.size; dst->u.tval.member = (sxmlrpc_member_t *)calloc(src->u.tval.size, sizeof(sxmlrpc_member_t)); if (dst->u.tval.member != NULL) { register size_t i; for (i = 0; i < src->u.tval.size; i++) { dst->u.tval.member[i].name = strdup(src->u.tval.member[i].name); sxmlrpc_copy_value(&dst->u.tval.member[i].value, &src->u.tval.member[i].value); } } break; case SXMLRPC_VALUE_ARRAY: dst->u.aval.size = src->u.aval.size; dst->u.aval.value = (sxmlrpc_value_t *)calloc(src->u.tval.size, sizeof(sxmlrpc_value_t)); if (dst->u.aval.value != NULL) { register size_t i; for (i = 0; i < src->u.aval.size; i++) { sxmlrpc_copy_value(&dst->u.aval.value[i], &src->u.aval.value[i]); } } break; default: break; } } /*****************************************************************************/ static netfd_t * netfd_open(path, flags, mode) const char * path; int flags; mode_t mode; { int fd; netfd_t * newfd; while ((fd = open(path, flags, mode)) < 0) { if (errno != EINTR) { return NULL; } } newfd = netfd_new(fd); if (newfd == NULL) { my_close(fd); } return newfd; } static netfd_t * netfd_fdopen(osfd) int osfd; { return (osfd > 0) ? netfd_new(osfd) : NULL; } static netfd_t * netfd_new(osfd) int osfd; { netfd_t * nfd; nfd = (netfd_t *)malloc(sizeof(netfd_t)); if (nfd != NULL) { nfd->osfd = osfd; nfd->rb.total = 0; nfd->rb.bufp = nfd->rb.buf; memset(nfd->rb.buf, 0, sizeof(nfd->rb.buf)); } return nfd; } static int netfd_close(nfd) register netfd_t * nfd; { int status = -1; if (nfd == NULL) { errno = EINVAL; } else { status = my_close(nfd->osfd); free(nfd); } return status; } static off_t netfd_seek(nfd, offset, whence) register netfd_t * nfd; off_t offset; int whence; { struct stat sbuf; if (fstat(nfd->osfd, &sbuf) == 0 && S_ISREG(sbuf.st_mode)) { off_t pos; pos = lseek(nfd->osfd, offset, whence); if (pos != -1) { return pos; } errno = EINVAL; } return -1; } static ssize_t netfd_read(nfd, buf, nbytes) netfd_t * nfd; void * buf; size_t nbytes; { register ssize_t n = 0; register char * ptr = (char *)buf; do { if (netfd_readbuf(nfd) < 0) { return -1; } if (nfd->rb.total == 0) { break; } *ptr++ = *nfd->rb.bufp++; nfd->rb.total--; } while ((size_t)n++ < nbytes && nfd->rb.total > 0); return n; } static ssize_t netfd_write(nfd, buf, nbyte) netfd_t * nfd; const void * buf; size_t nbyte; { size_t resid = nbyte; ssize_t n; while (resid > 0) { n = write(nfd->osfd, buf, resid); if (n == -1) { if (errno == EINTR) { continue; } return -1; } else { resid -= n; if (resid == 0) { break; } buf = (const void *)((const char *)buf + n); } } return (ssize_t)(nbyte - resid); } static ssize_t netfd_readline(nfd, buf, maxlen) netfd_t * nfd; void * buf; size_t maxlen; { register ssize_t n; register char * ptr = (char *)buf; for (n = 1; (size_t)n < maxlen; n++) { char c; int status; status = one_read(nfd, &c); if (status == 1) { *ptr++ = c; if (c == LF) { break; } if (c == EOL) { return 0; } /* EOF?, null data read */ } else if (status == 0) { if (n == 1) { return 0; } /* EOF, no data read */ else { break; } /* EOF, some data was read */ } else { return -1; /* error */ } } *ptr = EOL; return n; } static ssize_t one_read(nfd, ptr) netfd_t * nfd; char * ptr; { if (netfd_readbuf(nfd) < 0) { return -1; } if (nfd->rb.total == 0) { return 0; } nfd->rb.total--; *ptr = *nfd->rb.bufp++; return 1; } static int netfd_readbuf(nfd) netfd_t * nfd; { if (nfd->rb.total <= 0) { memset(nfd->rb.buf, 0, sizeof(nfd->rb.buf)); errno = 0; while ((nfd->rb.total = read(nfd->osfd, nfd->rb.buf, sizeof(nfd->rb.buf))) < 0) { if (errno == EINTR) { continue; } return -1; } nfd->rb.bufp = nfd->rb.buf; } return nfd->rb.total; } static netfd_t * netfd_mkstemp(template) char * template; { int fd; fd = mkstemp(template); return (fd == -1) ? NULL : netfd_new(fd); } #if HAVE_FREEBSD_SENDFILE static int netfd_sendfile(nfd, nsd, init_offset, nbytes, hdtr, ret_sbytes, flags) netfd_t * nfd; netfd_t * nsd; off_t init_offset; size_t nbytes; struct sf_hdtr * hdtr; off_t * ret_sbytes; int flags; { off_t offset; off_t sbytes; offset = init_offset; while (sendfile(nfd->osfd, nsd->osfd, offset, nbytes, hdtr, &sbytes, flags) < 0) { offset += sbytes; nbytes -= sbytes; if (errno == EINTR) { continue; } return -1; } if (ret_sbytes != NULL) { *ret_sbytes = offset + sbytes; } return 0; } #else #if HAVE_LINUX_SENDFILE /* * I have no linux environments. Is this routine ok? */ static int netfd_sendfile(nfd, nsd, init_offset, nbytes, hdtr, ret_sbytes, flags) netfd_t * nfd; netfd_t * nsd; off_t init_offset; size_t nbytes; void * hdtr; off_t * ret_sbytes; int flags; { off_t offset; off_t sbytes; offset = init_offset; while (sendfile(nfd->osfd, nsd->osfd, &sbytes, nbytes) < 0) { offset += sbytes; nbytes -= sbytes; if (errno == EINTR) { continue; } return -1; } if (ret_sbytes != NULL) { *ret_sbytes = offset + sbytes; } return 0; } #else /* No sendfile(2) */ static int netfd_sendfile(nfd, nsd, offset, nbytes, hdtr, sbytes, flags) netfd_t * nfd; netfd_t * nsd; off_t offset; size_t nbytes; void * hdtr __unused; off_t * sbytes; int flags __unused; { void * buf; size_t len; ssize_t n; int status = 0; if (nbytes == 0) { off_t off; /* get whole file size */ off = netfd_seek(nfd, 0, NETFD_SEEK_END); if (off == -1) { return -1; } len = (size_t)off; } else { len = nbytes; } buf = mmap(NULL, len, PROT_READ, MAP_PRIVATE, nfd->osfd, offset); if (buf == MAP_FAILED) { return -1; } n = netfd_write(nsd, buf, len); if (n == -1) { status = -1; } if (sbytes != NULL) { *sbytes = (size_t)n; } munmap(buf, len); return status; } #endif /* HAVE_LINUX_SENDFILE */ #endif /* HAVE_FREEBSD_SENDFILE */ static void sigalrm(signo) int signo __unused; { timedout = 1; /* set flag */ } static netfd_t * netfd_tcp_client(hostname, service) const char * hostname; const char * service; { struct addrinfo hints; struct addrinfo * res; memset((char *)&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = 0; if (getaddrinfo(hostname, service, &hints, &res) == 0) { struct itimerval value; /* new */ struct itimerval ovalue; /* old */ struct sigaction act; /* new */ struct sigaction oact; /* old */ struct addrinfo * res_save; int sd; #define DEF_CONNECT_TIMEOUT (10000) /* msec */ sigemptyset(&act.sa_mask); act.sa_flags = 0; act.sa_handler = (void *)sigalrm; sigaction(SIGALRM, &act, &oact); value.it_interval.tv_sec = value.it_interval.tv_usec = 0; value.it_value.tv_sec = DEF_CONNECT_TIMEOUT / 1000; value.it_value.tv_usec = (DEF_CONNECT_TIMEOUT % 1000) * 1000; setitimer(ITIMER_REAL, &value, &ovalue); timedout = 0; /* clear flag */ res_save = res; do { errno = 0; /* create a socket */ sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sd == -1) { continue; /*ignore this one */ } /* initiate a connection on a socket */ if (connect(sd, res->ai_addr, res->ai_addrlen) == 0) { break; } if (errno == EINTR) { if (timedout) { errno = ETIMEDOUT; } } my_close(sd); /* ignore this one */ } while ((res = res->ai_next) != NULL && errno == 0); freeaddrinfo(res_save); sigaction(SIGALRM, &oact, NULL); setitimer(ITIMER_REAL, &ovalue, NULL); if (res != NULL) { netfd_t * nfd; nfd = netfd_fdopen(sd); if (nfd != NULL) { return nfd; } my_close(sd); } } return NULL; } static netfd_t * netfd_tcp_server(hostname, service, backlog) const char * hostname; const char * service; const int backlog; { struct addrinfo hints; struct addrinfo * res; memset((char *)&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; if (getaddrinfo(hostname, service, &hints, &res) == 0) { struct addrinfo * res_save; int sd; res_save = res; do { static const int on = 1; /* create a socket */ sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sd < 0) { continue; /*error, try next one */ } /* allow reuse of local address */ (void)setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); /* bind a socket */ if (bind(sd, res->ai_addr, res->ai_addrlen) == 0) { break; /* success */ } my_close(sd); /* bind error, close and try next one */ } while ((res = res->ai_next) != NULL); freeaddrinfo(res_save); if (res != NULL) { /* listen for connection on the socket */ if (listen(sd, backlog > 0 ? backlog : NETFD_DEF_BACKLOG) == 0) { netfd_t * nfd; nfd = netfd_fdopen(sd); if (nfd != NULL) { return nfd; } my_close(sd); } } } return NULL; } static netfd_t * netfd_accept(nfd, addr, addrlen) netfd_t * nfd; struct sockaddr * addr; socklen_t * addrlen; { int sd; netfd_t * newfd; #if 0 while ((sd = accept(nfd->osfd, addr, (socklen_t *)addrlen)) < 0) { if (errno == EINTR) { continue; } return NULL; } #else if ((sd = accept(nfd->osfd, addr, addrlen)) < 0) { return NULL; } #endif newfd = netfd_fdopen(sd); if (newfd == NULL) { my_close(sd); } return newfd; } static int netfd_sock_ntop(sa0, ipaddr) const struct sockaddr * sa0; char ** ipaddr; { static char host[132]; /* Unix domain is largest */ int status = -1; memset(host, 0, sizeof(host)); switch (sa0->sa_family) { case AF_INET: { struct sockaddr_in * sa = (struct sockaddr_in *)sa0; if (inet_ntop(AF_INET, &sa->sin_addr, host, sizeof(host)) != NULL) { status = 0; } } break; case AF_INET6: { struct sockaddr_in6 * sa = (struct sockaddr_in6 *)sa0; if (inet_ntop(AF_INET6, &sa->sin6_addr, host, sizeof(host)) != NULL) { status = 0; } } break; default: break; } if (ipaddr != NULL) { if (status == 0) { *ipaddr = strdup(host); } else { *ipaddr = NULL; } } return status; } static int my_close(fd) int fd; { int status; int saved_errno = errno; while (((status = close(fd)) < 0) && (errno == EINTR)) { continue; } errno = saved_errno; return status; } /*****************************************************************************/ const char * sxmlrpc_get_datetime_iso8601(t) const time_t t; { static char iso8601[sizeof("20060401T16:55:31")]; struct tm * tp; tp = localtime(&t); if (tp == NULL) { return NULL; } memset(iso8601, 0, sizeof(iso8601)); snprintf(iso8601, sizeof(iso8601), "%04d%02d%02dT%02d:%02d:%02d", tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec); return iso8601; } const char * sxmlrpc_get_current_datetime_iso8601(void) { time_t now; time(&now); return sxmlrpc_get_datetime_iso8601(now); } /* * bit sequence for mask * * 0xfc = 11111100 * 0x03 = 00000011 * 0xf0 = 11110000 * 0x0f = 00001111 * 0xc0 = 11000000 * 0x3f = 00111111 * 0x30 = 00110000 * 0x3c = 00111100 */ /* * Base64 alphabet */ static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; int sxmlrpc_base64_encode(str, rbuf, rsize) const char * str; char * rbuf; /* return base64 encoded string */ size_t rsize; /* return buffer size */ { size_t len; register int i; register int n; len = strlen(str); if (rsize < (size_t)((double)len * 1.33) + 1) { return -1; } /* * encode 3-bytes (24-bits) at a time */ memset(rbuf, 0, rsize); for (i = n = 0; i < len - (len % 3); i += 3, n += 4) { rbuf[n] = base64[( str[i] & 0xfc) >> 2]; rbuf[n+1] = base64[((str[i] & 0x03) << 4) | ((str[i+1] & 0xf0) >> 4)]; rbuf[n+2] = base64[((str[i+1] & 0x0f) << 2) | ((str[i+2] & 0xc0) >> 6)]; rbuf[n+3] = base64[( str[i+2] & 0x3f)]; } i = len - (len % 3); /* rest size */ switch (len % 3) { case 2: /* one character padding */ rbuf[n] = base64[( str[i] & 0xfc) >> 2]; rbuf[n+1] = base64[((str[i] & 0x03) << 4) | ((str[i+1] & 0xf0) >> 4)]; rbuf[n+2] = base64[( str[i+1] & 0x0f) << 2]; rbuf[n+3] = base64[64]; /* Pad */ n += 4; break; case 1: /* two character padding */ rbuf[n] = base64[(str[i] & 0xfc) >> 2]; rbuf[n+1] = base64[(str[i] & 0x03) << 4]; rbuf[n+2] = base64[64]; /* Pad */ rbuf[n+3] = base64[64]; /* Pad */ n += 4; break; default: break; } rbuf[n] = '\0'; return n; } int sxmlrpc_base64_decode(str, rbuf, rsize) const char * str; char * rbuf; /* return base64 decoded string */ size_t rsize; /* return buffer size */ { #define VAL(x) (str[(x)] == '=' ? 0 : strchr(base64, str[(x)]) - base64) size_t len; register int i; register int n; len = strlen(str); if (rsize < (size_t)((double)len * 0.75) + 1) { return -1; } /* * work on 4-words (24-bits) at a time */ memset(rbuf, 0, rsize); for (i = n = 0; i < len; i += 4, n += 3) { rbuf[n] = (VAL(i) << 2) | ((VAL(i+1) & 0x30) >> 4); rbuf[n+1] = ((VAL(i+1) & 0x0f) << 4) | ((VAL(i+2) & 0x3c) >> 2); rbuf[n+2] = ((VAL(i+2) & 0x03) << 6) | (VAL(i+3) & 0x3f); } /* remove padding data */ if (str[len - 1] == '=') { n--; } if (str[len - 2] == '=') { n--; } return n; } /*****************************************************************************/ int sxmlrpc_get_call_params(sxRPC, infd) sxmlrpc_t * sxRPC; int infd; { sxml_node_t * root; int status = -1; root = sxml_parse_file(infd); if (root != NULL) { status = get_call_params(sxRPC, root); sxml_delete_node(root); } return status; } int sxmlrpc_set_response_param(sxRPC, param, outfd) sxmlrpc_t * sxRPC; sxmlrpc_param_t * param; int outfd; { int stkfd; int status; stkfd = sxRPC->msgbdy_fd; sxRPC->msgbdy_fd = outfd; status = make_response_body(sxRPC, param); sxRPC->msgbdy_fd = stkfd; return status; } void sxmlrpc_set_encoding(sxRPC, ctype) sxmlrpc_t * sxRPC; const char * ctype; { sxRPC->encoding = strdup(ctype); }