viruri.c 10.1 KB
Newer Older
M
Martin Kletzander 已提交
1 2 3
/*
 * viruri.c: URI parsing wrappers for libxml2 functions
 *
4
 * Copyright (C) 2012-2014 Red Hat, Inc.
M
Martin Kletzander 已提交
5
 *
O
Osier Yang 已提交
6 7 8 9 10 11 12 13 14 15 16
 * 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
17
 * License along with this library.  If not, see
O
Osier Yang 已提交
18
 * <http://www.gnu.org/licenses/>.
M
Martin Kletzander 已提交
19 20 21 22 23 24
 */

#include <config.h>

#include "viruri.h"

25
#include "viralloc.h"
26
#include "virerror.h"
27
#include "virbuffer.h"
28
#include "virlog.h"
29
#include "virstring.h"
30
#include "virutil.h"
31 32 33

#define VIR_FROM_THIS VIR_FROM_URI

34 35
VIR_LOG_INIT("util.uri");

36 37 38 39 40 41 42 43
static int
virURIParamAppend(virURIPtr uri,
                  const char *name,
                  const char *value)
{
    char *pname = NULL;
    char *pvalue = NULL;

44 45
    if (VIR_STRDUP(pname, name) < 0 || VIR_STRDUP(pvalue, value) < 0)
        goto error;
46

47
    if (VIR_RESIZE_N(uri->params, uri->paramsAlloc, uri->paramsCount, 1) < 0)
48
        goto error;
49 50 51 52 53 54 55 56

    uri->params[uri->paramsCount].name = pname;
    uri->params[uri->paramsCount].value = pvalue;
    uri->params[uri->paramsCount].ignore = 0;
    uri->paramsCount++;

    return 0;

57
 error:
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
    VIR_FREE(pname);
    VIR_FREE(pvalue);
    return -1;
}


static int
virURIParseParams(virURIPtr uri)
{
    const char *end, *eq;
    const char *query = uri->query;

    if (!query || query[0] == '\0')
        return 0;

    while (*query) {
        char *name = NULL, *value = NULL;

        /* Find the next separator, or end of the string. */
77
        end = strchr(query, '&');
78
        if (!end)
79
            end = strchr(query, ';');
80
        if (!end)
81
            end = query + strlen(query);
82 83

        /* Find the first '=' character between here and end. */
84
        eq = strchr(query, '=');
85 86
        if (eq && eq >= end) eq = NULL;

87 88
        if (end == query) {
            /* Empty section (eg. "&&"). */
89
            goto next;
90 91 92 93
        } else if (!eq) {
            /* If there is no '=' character, then we have just "name"
             * and consistent with CGI.pm we assume value is "".
             */
94
            name = xmlURIUnescapeString(query, end - query, NULL);
95
            if (!name) goto no_memory;
96 97 98 99
        } else if (eq+1 == end) {
            /* Or if we have "name=" here (works around annoying
             * problem when calling xmlURIUnescapeString with len = 0).
             */
100
            name = xmlURIUnescapeString(query, eq - query, NULL);
101
            if (!name) goto no_memory;
102 103 104 105
        } else if (query == eq) {
            /* If the '=' character is at the beginning then we have
             * "=value" and consistent with CGI.pm we _ignore_ this.
             */
106
            goto next;
107 108
        } else {
            /* Otherwise it's "name=value". */
109
            name = xmlURIUnescapeString(query, eq - query, NULL);
110 111
            if (!name)
                goto no_memory;
112
            value = xmlURIUnescapeString(eq+1, end - (eq+1), NULL);
113 114 115 116 117 118 119 120 121 122
            if (!value) {
                VIR_FREE(name);
                goto no_memory;
            }
        }

        /* Append to the parameter set. */
        if (virURIParamAppend(uri, name, value ? value : "") < 0) {
            VIR_FREE(name);
            VIR_FREE(value);
J
Ján Tomko 已提交
123
            return -1;
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
        }
        VIR_FREE(name);
        VIR_FREE(value);

    next:
        query = end;
        if (*query) query ++; /* skip '&' separator */
    }

    return 0;

 no_memory:
    virReportOOMError();
    return -1;
}

M
Martin Kletzander 已提交
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
/**
 * virURIParse:
 * @uri: URI to parse
 *
 * Wrapper for xmlParseURI
 *
 * Unfortunately there are few things that should be managed after
 * parsing the URI. Fortunately there is only one thing now and its
 * removing of square brackets around IPv6 addresses.
 *
 * @returns the parsed uri object with some fixes
 */
virURIPtr
virURIParse(const char *uri)
{
155 156
    xmlURIPtr xmluri;
    virURIPtr ret = NULL;
M
Martin Kletzander 已提交
157

158 159 160
    xmluri = xmlParseURI(uri);

    if (!xmluri) {
161
        /* libxml2 does not tell us what failed. Grr :-( */
162 163
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Unable to parse URI %s"), uri);
164 165 166
        return NULL;
    }

167
    if (VIR_ALLOC(ret) < 0)
168
        goto error;
169

170 171 172 173
    if (VIR_STRDUP(ret->scheme, xmluri->scheme) < 0)
        goto error;
    if (VIR_STRDUP(ret->server, xmluri->server) < 0)
        goto error;
174
    ret->port = xmluri->port;
175 176
    if (VIR_STRDUP(ret->path, xmluri->path) < 0)
        goto error;
177
#ifdef HAVE_XMLURI_QUERY_RAW
178 179
    if (VIR_STRDUP(ret->query, xmluri->query_raw) < 0)
        goto error;
180
#else
181 182
    if (VIR_STRDUP(ret->query, xmluri->query) < 0)
        goto error;
183
#endif
184 185 186 187
    if (VIR_STRDUP(ret->fragment, xmluri->fragment) < 0)
        goto error;
    if (VIR_STRDUP(ret->user, xmluri->user) < 0)
        goto error;
188

J
Ján Tomko 已提交
189 190 191 192
    /* Strip square bracket from an IPv6 address.
     * The function modifies the string in-place. Even after such
     * modification, it is OK to free the URI with xmlFreeURI. */
    virStringStripIPv6Brackets(ret->server);
M
Martin Kletzander 已提交
193

194 195 196
    if (virURIParseParams(ret) < 0)
        goto error;

197 198
    xmlFreeURI(xmluri);

M
Martin Kletzander 已提交
199
    return ret;
200

201
 error:
202 203 204
    xmlFreeURI(xmluri);
    virURIFree(ret);
    return NULL;
M
Martin Kletzander 已提交
205 206 207 208 209 210 211 212 213 214 215 216 217 218
}

/**
 * virURIFormat:
 * @uri: URI to format
 *
 * Wrapper for xmlSaveUri
 *
 * This function constructs back everything that @ref virURIParse
 * changes after parsing
 *
 * @returns the constructed uri as a string
 */
char *
219
virURIFormat(virURIPtr uri)
M
Martin Kletzander 已提交
220
{
221
    xmlURI xmluri;
M
Martin Kletzander 已提交
222 223 224
    char *tmpserver = NULL;
    char *ret;

225 226 227 228 229 230
    memset(&xmluri, 0, sizeof(xmluri));

    xmluri.scheme = uri->scheme;
    xmluri.server = uri->server;
    xmluri.port = uri->port;
    xmluri.path = uri->path;
231 232 233
#ifdef HAVE_XMLURI_QUERY_RAW
    xmluri.query_raw = uri->query;
#else
234
    xmluri.query = uri->query;
235
#endif
236
    xmluri.fragment = uri->fragment;
237
    xmluri.user = uri->user;
238

M
Martin Kletzander 已提交
239
    /* First check: does it make sense to do anything */
240 241
    if (xmluri.server != NULL &&
        strchr(xmluri.server, ':') != NULL) {
M
Martin Kletzander 已提交
242

243
        if (virAsprintf(&tmpserver, "[%s]", xmluri.server) < 0)
M
Martin Kletzander 已提交
244 245
            return NULL;

246
        xmluri.server = tmpserver;
M
Martin Kletzander 已提交
247 248
    }

249 250 251 252 253 254 255
    /*
     * This helps libxml2 deal with the difference
     * between uri:/absolute/path and uri:///absolute/path.
     */
    if (!xmluri.server && !xmluri.port)
        xmluri.port = -1;

256
    ret = (char *)xmlSaveUri(&xmluri);
257 258 259 260
    if (!ret) {
        virReportOOMError();
        goto cleanup;
    }
M
Martin Kletzander 已提交
261

262
 cleanup:
263
    VIR_FREE(tmpserver);
M
Martin Kletzander 已提交
264 265 266

    return ret;
}
267 268


269 270 271
char *virURIFormatParams(virURIPtr uri)
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;
272
    size_t i;
273
    bool amp = false;
274 275 276

    for (i = 0; i < uri->paramsCount; ++i) {
        if (!uri->params[i].ignore) {
277 278 279
            if (amp) virBufferAddChar(&buf, '&');
            virBufferStrcat(&buf, uri->params[i].name, "=", NULL);
            virBufferURIEncodeString(&buf, uri->params[i].value);
280
            amp = true;
281 282 283
        }
    }

284
    if (virBufferCheckError(&buf) < 0)
285 286 287 288 289
        return NULL;

    return virBufferContentAndReset(&buf);
}

290 291 292 293 294 295 296 297
/**
 * virURIFree:
 * @uri: uri to free
 *
 * Frees the URI
 */
void virURIFree(virURIPtr uri)
{
298 299
    size_t i;

300 301 302 303 304
    if (!uri)
        return;

    VIR_FREE(uri->scheme);
    VIR_FREE(uri->server);
305
    VIR_FREE(uri->user);
306 307
    VIR_FREE(uri->path);
    VIR_FREE(uri->query);
308 309
    VIR_FREE(uri->fragment);

310
    for (i = 0; i < uri->paramsCount; i++) {
311 312 313 314 315
        VIR_FREE(uri->params[i].name);
        VIR_FREE(uri->params[i].value);
    }
    VIR_FREE(uri->params);

316
    VIR_FREE(uri);
317
}
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405


#define URI_ALIAS_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"

static int
virURIFindAliasMatch(virConfValuePtr value, const char *alias,
                     char **uri)
{
    virConfValuePtr entry;
    size_t alias_len;

    if (value->type != VIR_CONF_LIST) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Expected a list for 'uri_aliases' config parameter"));
        return -1;
    }

    entry = value->list;
    alias_len = strlen(alias);
    while (entry) {
        char *offset;
        size_t safe;

        if (entry->type != VIR_CONF_STRING) {
            virReportError(VIR_ERR_CONF_SYNTAX, "%s",
                           _("Expected a string for 'uri_aliases' config "
                             "parameter list entry"));
            return -1;
        }

        if (!(offset = strchr(entry->str, '='))) {
            virReportError(VIR_ERR_CONF_SYNTAX,
                           _("Malformed 'uri_aliases' config entry '%s', "
                             "expected 'alias=uri://host/path'"), entry->str);
            return -1;
        }

        safe = strspn(entry->str, URI_ALIAS_CHARS);
        if (safe < (offset - entry->str)) {
            virReportError(VIR_ERR_CONF_SYNTAX,
                           _("Malformed 'uri_aliases' config entry '%s', "
                             "aliases may only contain 'a-Z, 0-9, _, -'"),
                            entry->str);
            return -1;
        }

        if (alias_len == (offset - entry->str) &&
            STREQLEN(entry->str, alias, alias_len)) {
            VIR_DEBUG("Resolved alias '%s' to '%s'",
                      alias, offset+1);
            return VIR_STRDUP(*uri, offset+1);
        }

        entry = entry->next;
    }

    VIR_DEBUG("No alias found for '%s', continuing...",
              alias);
    return 0;
}


/**
 * virURIResolveAlias:
 * @conf: configuration file handler
 * @alias: URI alias to be resolved
 * @uri: URI object reference where the resolved URI should be stored
 *
 * Resolves @alias to a canonical URI according to our configuration
 * file.
 *
 * Returns 0 on success, -1 on error.
 */
int
virURIResolveAlias(virConfPtr conf, const char *alias, char **uri)
{
    int ret = -1;
    virConfValuePtr value = NULL;

    *uri = NULL;

    if ((value = virConfGetValue(conf, "uri_aliases")))
        ret = virURIFindAliasMatch(value, alias, uri);
    else
        ret = 0;

    return ret;
}