From 3b0149354b2343be75aab9508822572b5ed3cae1 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Mon, 17 Dec 2007 10:07:56 +0000 Subject: [PATCH] Add separate qparams module for handling query parameters. * src/qparams.c, src/qparams.h, src/Makefile.am: Added a separate 'qparams' module for handling query parameters. * src/remote_internal.c: Factor out query parameter code so it uses the 'qparams' module. --- ChangeLog | 8 ++ src/Makefile.am | 1 + src/qparams.c | 245 ++++++++++++++++++++++++++++++++++++++++++ src/qparams.h | 70 ++++++++++++ src/remote_internal.c | 244 ++++++----------------------------------- 5 files changed, 354 insertions(+), 214 deletions(-) create mode 100644 src/qparams.c create mode 100644 src/qparams.h diff --git a/ChangeLog b/ChangeLog index 83612fdca8..748fb68a28 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Mon Dec 17 10:05:00 UTC 2007 Richard W.M. Jones + + Add separate qparams module for handling query parameters. + * src/qparams.c, src/qparams.h, src/Makefile.am: Added a + separate 'qparams' module for handling query parameters. + * src/remote_internal.c: Factor out query parameter code so + it uses the 'qparams' module. + Mon Dec 17 10:01:00 UTC 2007 Richard W.M. Jones Add extra utility functions to buf.c diff --git a/src/Makefile.am b/src/Makefile.am index 1979a19431..82194485ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -34,6 +34,7 @@ CLIENT_SOURCES = \ hash.c hash.h \ test.c test.h \ buf.c buf.h \ + qparams.c qparams.h \ xml.c xml.h \ event.c event.h \ xen_unified.c xen_unified.h \ diff --git a/src/qparams.c b/src/qparams.c new file mode 100644 index 0000000000..526c50e633 --- /dev/null +++ b/src/qparams.c @@ -0,0 +1,245 @@ +/* Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Richard W.M. Jones + * + * Utility functions to help parse and assemble query strings. + */ + +#include +#include +#include + +#include "buf.h" + +#include "qparams.h" + +struct qparam_set * +new_qparam_set (int init_alloc, ...) +{ + va_list args; + struct qparam_set *ps; + const char *pname, *pvalue; + + if (init_alloc <= 0) init_alloc = 1; + + ps = malloc (sizeof (*ps)); + if (!ps) return NULL; + ps->n = 0; + ps->alloc = init_alloc; + ps->p = malloc (init_alloc * sizeof (ps->p[0])); + if (!ps->p) { + free (ps); + return NULL; + } + + va_start (args, init_alloc); + while ((pname = va_arg (args, char *)) != NULL) { + pvalue = va_arg (args, char *); + + if (append_qparam (ps, pname, pvalue) == -1) { + free_qparam_set (ps); + return NULL; + } + } + va_end (args); + + return ps; +} + +int +append_qparams (struct qparam_set *ps, ...) +{ + va_list args; + const char *pname, *pvalue; + + va_start (args, ps); + while ((pname = va_arg (args, char *)) != NULL) { + pvalue = va_arg (args, char *); + + if (append_qparam (ps, pname, pvalue) == -1) + return -1; + } + va_end (args); + + return 0; +} + +/* Ensure there is space to store at least one more parameter + * at the end of the set. + */ +static int +grow_qparam_set (struct qparam_set *ps) +{ + struct qparam *old_p; + + if (ps->n >= ps->alloc) { + old_p = ps->p; + ps->p = realloc (ps->p, 2 * ps->alloc * sizeof (ps->p[0])); + if (!ps->p) { + ps->p = old_p; + perror ("realloc"); + return -1; + } + ps->alloc *= 2; + } + + return 0; +} + +int +append_qparam (struct qparam_set *ps, + const char *name, const char *value) +{ + char *pname, *pvalue; + + pname = strdup (name); + if (!pname) + return -1; + + pvalue = strdup (value); + if (!pvalue) { + free (pname); + return -1; + } + + if (grow_qparam_set (ps) == -1) { + free (pname); + free (pvalue); + return -1; + } + + ps->p[ps->n].name = pname; + ps->p[ps->n].value = pvalue; + ps->p[ps->n].ignore = 0; + ps->n++; + + return 0; +} + +char * +qparam_get_query (const struct qparam_set *ps) +{ + virBufferPtr buf; + int i, amp = 0; + + buf = virBufferNew (100); + for (i = 0; i < ps->n; ++i) { + if (!ps->p[i].ignore) { + if (amp) virBufferAddChar (buf, '&'); + virBufferStrcat (buf, ps->p[i].name, "=", NULL); + virBufferURIEncodeString (buf, ps->p[i].value); + amp = 1; + } + } + + return virBufferContentAndFree (buf); +} + +void +free_qparam_set (struct qparam_set *ps) +{ + int i; + + for (i = 0; i < ps->n; ++i) { + free (ps->p[i].name); + free (ps->p[i].value); + } + free (ps); +} + +struct qparam_set * +qparam_query_parse (const char *query) +{ + struct qparam_set *ps; + const char *name, *value, *end, *eq; + + ps = new_qparam_set (0, NULL); + if (!ps) return NULL; + + if (!query || query[0] == '\0') return ps; + + while (*query) { + /* Find the next separator, or end of the string. */ + end = strchr (query, '&'); + if (!end) end = query + strlen (query); + + /* Find the first '=' character between here and end. */ + eq = strchr (query, '='); + if (eq && eq >= end) eq = NULL; + + /* Empty section (eg. "&&"). */ + if (end == query) + goto next; + + /* If there is no '=' character, then we have just "name" + * and consistent with CGI.pm we assume value is "". + */ + else if (!eq) { + name = xmlURIUnescapeString (query, end - query, NULL); + value = ""; + if (!name) goto out_of_memory; + } + /* Or if we have "name=" here (works around annoying + * problem when calling xmlURIUnescapeString with len = 0). + */ + else if (eq+1 == end) { + name = xmlURIUnescapeString (query, eq - query, NULL); + value = ""; + if (!name) goto out_of_memory; + } + /* If the '=' character is at the beginning then we have + * "=value" and consistent with CGI.pm we _ignore_ this. + */ + else if (query == eq) + goto next; + + /* Otherwise it's "name=value". */ + else { + name = xmlURIUnescapeString (query, eq - query, NULL); + value = xmlURIUnescapeString (eq+1, end - (eq+1), NULL); + if (!name || !value) goto out_of_memory; + } + + /* Append to the parameter set. */ + if (append_qparam (ps, name, value) == -1) goto out_of_memory; + + next: + query = end; + if (*query) query ++; /* skip '&' separator */ + } + + return ps; + + out_of_memory: + free_qparam_set (ps); + return NULL; +} + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/src/qparams.h b/src/qparams.h new file mode 100644 index 0000000000..09b460a494 --- /dev/null +++ b/src/qparams.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: + * Richard W.M. Jones + * + * Utility functions to help parse and assemble query strings. + */ + +#ifndef _QPARAMS_H_ +#define _QPARAMS_H_ + +/* Single web service query parameter 'name=value'. */ +struct qparam { + char *name; /* Name (unescaped). */ + char *value; /* Value (unescaped). */ + int ignore; /* Ignore this field in qparam_get_query */ +}; + +/* Set of parameters. */ +struct qparam_set { + int n; /* number of parameters used */ + int alloc; /* allocated space */ + struct qparam *p; /* array of parameters */ +}; + +/* New parameter set. */ +extern struct qparam_set *new_qparam_set (int init_alloc, ...); + +/* Appending parameters. */ +extern int append_qparams (struct qparam_set *ps, ...); +extern int append_qparam (struct qparam_set *ps, + const char *name, const char *value); + +/* Get a query string ("name=value&name=value&...") */ +extern char *qparam_get_query (const struct qparam_set *ps); + +/* Parse a query string into a parameter set. */ +extern struct qparam_set *qparam_query_parse (const char *query); + +extern void free_qparam_set (struct qparam_set *ps); + +#endif /* _QPARAMS_H_ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/src/remote_internal.c b/src/remote_internal.c index e0e735338a..4f5ca1ed45 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -74,11 +74,11 @@ #include "internal.h" #include "driver.h" +#include "buf.h" +#include "qparams.h" #include "remote_internal.h" #include "remote_protocol.h" -#define DEBUG 0 /* Enable verbose messages on stderr. */ - /* Per-connection private data. */ #define MAGIC 999 /* private_data->magic if OK */ #define DEAD 998 /* private_data->magic if dead/closed */ @@ -152,22 +152,6 @@ static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr /* Helper functions for remoteOpen. */ static char *get_transport_from_scheme (char *scheme); -/* Parse query string. */ -struct query_fields { - struct query_fields *next; /* Linked list chain. */ - char *name; /* Field name (unescaped). */ - char *value; /* Field value (unescaped). */ - int ignore; /* Ignore field in query_create. */ -}; - -static int query_parse (const char *query, - const char *separator, - struct query_fields * *fields_out); -static int query_create (const struct query_fields *fields, - const char *separator, - char **query_out); -static void query_free (struct query_fields *fields); - /* GnuTLS functions used by remoteOpen. */ static int initialise_gnutls (virConnectPtr conn); static gnutls_session_t negotiate_gnutls_on_connection (virConnectPtr conn, struct private_data *priv, int no_verify); @@ -403,55 +387,59 @@ doRemoteOpen (virConnectPtr conn, * feasibly it might contain variables needed by the real driver, * although that won't be the case for now). */ - struct query_fields *vars, *var; + struct qparam_set *vars; + struct qparam *var; + int i; char *query; #ifdef HAVE_XMLURI_QUERY_RAW query = uri->query_raw; #else query = uri->query; #endif - if (query_parse (query, NULL, &vars) != 0) goto failed; + vars = qparam_query_parse (query); + if (vars == NULL) goto failed; - for (var = vars; var; var = var->next) { - if (strcasecmp (var->name, "name") == 0) { + for (i = 0; i < vars->n; i++) { + var = &vars->p[i]; + if (STRCASEEQ (var->name, "name")) { name = strdup (var->value); if (!name) goto out_of_memory; var->ignore = 1; - } else if (strcasecmp (var->name, "command") == 0) { + } else if (STRCASEEQ (var->name, "command")) { command = strdup (var->value); if (!command) goto out_of_memory; var->ignore = 1; - } else if (strcasecmp (var->name, "socket") == 0) { + } else if (STRCASEEQ (var->name, "socket")) { sockname = strdup (var->value); if (!sockname) goto out_of_memory; var->ignore = 1; - } else if (strcasecmp (var->name, "auth") == 0) { + } else if (STRCASEEQ (var->name, "auth")) { authtype = strdup (var->value); if (!authtype) goto out_of_memory; var->ignore = 1; - } else if (strcasecmp (var->name, "netcat") == 0) { + } else if (STRCASEEQ (var->name, "netcat")) { netcat = strdup (var->value); if (!netcat) goto out_of_memory; var->ignore = 1; - } else if (strcasecmp (var->name, "no_verify") == 0) { + } else if (STRCASEEQ (var->name, "no_verify")) { no_verify = atoi (var->value); var->ignore = 1; - } else if (strcasecmp (var->name, "no_tty") == 0) { + } else if (STRCASEEQ (var->name, "no_tty")) { no_tty = atoi (var->value); var->ignore = 1; - } else if (strcasecmp (var->name, "debug") == 0) { + } else if (STRCASEEQ (var->name, "debug")) { if (var->value && - strcasecmp(var->value, "stdout") == 0) + STRCASEEQ (var->value, "stdout")) priv->debugLog = stdout; else priv->debugLog = stderr; } -#if DEBUG +#ifdef ENABLE_DEBUG else fprintf (stderr, "remoteOpen: " - "passing through variable '%s' to remote end\n", - var->name); + "passing through variable '%s' ('%s') to remote end\n", + var->name, var->value); #endif } @@ -461,14 +449,15 @@ doRemoteOpen (virConnectPtr conn, if (uri->query) xmlFree (uri->query); #endif - if (query_create (vars, NULL, + if (( #ifdef HAVE_XMLURI_QUERY_RAW - &uri->query_raw + uri->query_raw = #else - &uri->query + uri->query = #endif - ) != 0) goto failed; - query_free (vars); + qparam_get_query (vars)) != 0) goto failed; + + free_qparam_set (vars); /* For ext transport, command is required. */ if (transport == trans_ext && !command) { @@ -496,7 +485,7 @@ doRemoteOpen (virConnectPtr conn, } assert (name); -#if DEBUG +#ifdef ENABLE_DEBUG fprintf (stderr, "remoteOpen: proceeding with name = %s\n", name); #endif @@ -882,179 +871,6 @@ get_transport_from_scheme (char *scheme) return p ? p+1 : 0; } -static int -query_create (const struct query_fields *fields, - const char *separator, - char **query_out) -{ - /* List of characters which are safe inside names or values, - * apart from '@', IS_MARK and IS_ALPHANUM. Best to escape - * as much as possible. Certainly '=', '&' and '#' must NEVER - * be added to this list. - */ - static const xmlChar *special_chars = BAD_CAST ""; - - int append_sep = 0, sep_len; - xmlBufferPtr buf; - xmlChar *str; - int rv; - - if (query_out) *query_out = NULL; - if (!fields) return 0; - - if (separator == NULL) { - separator = "&"; - sep_len = 1; - } else - sep_len = xmlStrlen (BAD_CAST separator); - - buf = xmlBufferCreate (); - if (!buf) return -1; - - rv = 0; - while (fields) { - if (!fields->ignore) { - if (append_sep) { - rv = xmlBufferAdd (buf, BAD_CAST separator, sep_len); - if (rv != 0) goto error; - } - append_sep = 1; - - str = xmlURIEscapeStr (BAD_CAST fields->name, special_chars); - if (!str) { rv = XML_ERR_NO_MEMORY; goto error; } - rv = xmlBufferAdd (buf, str, xmlStrlen (str)); - xmlFree (str); - if (rv != 0) goto error; - - rv = xmlBufferAdd (buf, BAD_CAST "=", 1); - if (rv != 0) goto error; - str = xmlURIEscapeStr (BAD_CAST fields->value, special_chars); - if (!str) { rv = XML_ERR_NO_MEMORY; goto error; } - rv = xmlBufferAdd (buf, str, xmlStrlen (str)); - xmlFree (str); - if (rv != 0) goto error; - } - - fields = fields->next; - } - - if (query_out && buf->content) { - *query_out = (char *) xmlStrdup (buf->content); - if (!*query_out) { - rv = XML_ERR_NO_MEMORY; - goto error; - } - } - - error: - if (buf) - xmlBufferFree (buf); - return rv; -} - -static int -query_parse (const char *query_, - const char *separator, - struct query_fields * *fields_out) -{ - struct query_fields *fields, *field, **prev; - int sep_len; - const xmlChar *query = BAD_CAST query_, *end, *eq; - char *name, *value; - - if (fields_out) *fields_out = NULL; - if (!query || query[0] == '\0') return 0; - - if (separator == NULL) { - separator = "&"; - sep_len = 1; - } else - sep_len = xmlStrlen (BAD_CAST separator); - - fields = NULL; - prev = &fields; - - while (*query) { - /* Find the next separator, or end of the string. */ - end = xmlStrstr (query, BAD_CAST separator); - if (!end) end = query + xmlStrlen (query); - - /* Find the first '=' character between here and end. */ - eq = xmlStrchr (query, '='); - if (eq && eq >= end) eq = NULL; - - /* Empty section (eg. "?&"). */ - if (end == query) - goto next; - /* If there is no '=' character, then we have just "name" - * and consistent with CGI.pm we assume value is "". - */ - else if (!eq) { - name = xmlURIUnescapeString ((const char *) query, - end - query, NULL); - value = (char *) xmlStrdup (BAD_CAST ""); - if (!name || !value) goto out_of_memory; - } - /* Or if we have "name=" here (works around annoying - * problem when calling xmlURIUnescapeString with len = 0). - */ - else if (eq+1 == end) { - name = xmlURIUnescapeString ((const char *) query, - eq - query, NULL); - value = (char *) xmlStrdup (BAD_CAST ""); - if (!name || !value) goto out_of_memory; - } - /* If the '=' character is at the beginning then we have - * "=value" and consistent with CGI.pm we _ignore_ this. - */ - else if (query == eq) - goto next; - /* Otherwise it's "name=value". */ - else { - name = xmlURIUnescapeString ((const char *) query, - eq - query, NULL); - value = xmlURIUnescapeString ((const char *) eq+1, - end - (eq+1), NULL); - if (!name || !value) goto out_of_memory; - } - - /* Allocate this field and append to the list. */ - field = xmlMalloc (sizeof *field); - if (!field) goto out_of_memory; - field->next = NULL; - field->name = name; - field->value = value; - field->ignore = 0; - *prev = field; - prev = &field->next; - - next: - query = end; - if (*query) query += sep_len; /* skip separator */ - } - - if (fields_out) *fields_out = fields; - return 0; - - out_of_memory: - query_free (fields); - return XML_ERR_NO_MEMORY; -} - -static void -query_free (struct query_fields *fields) -{ - struct query_fields *t; - - while (fields) { - if (fields->name) xmlFree (fields->name); - if (fields->value) xmlFree (fields->value); - t = fields; - fields = fields->next; - xmlFree (t); - } -} - /* GnuTLS functions used by remoteOpen. */ static gnutls_certificate_credentials_t x509_cred; @@ -1100,7 +916,7 @@ initialise_gnutls (virConnectPtr conn) return -1; /* Set the trusted CA cert. */ -#if DEBUG +#ifdef ENABLE_DEBUG fprintf (stderr, "loading CA file %s\n", LIBVIRT_CACERT); #endif err = @@ -1112,7 +928,7 @@ initialise_gnutls (virConnectPtr conn) } /* Set the client certificate and private key. */ -#if DEBUG +#ifdef ENABLE_DEBUG fprintf (stderr, "loading client cert and key from files %s and %s\n", LIBVIRT_CLIENTCERT, LIBVIRT_CLIENTKEY); #endif