viruri.c 8.2 KB
Newer Older
M
Martin Kletzander 已提交
1 2 3 4 5
/*
 * viruri.c: URI parsing wrappers for libxml2 functions
 *
 * Copyright (C) 2012 Red Hat, Inc.
 *
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 25 26
 */

#include <config.h>

#include "viruri.h"

#include "memory.h"
#include "util.h"
27
#include "virterror_internal.h"
28
#include "buf.h"
29 30 31

#define VIR_FROM_THIS VIR_FROM_URI

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

    if (!(pname = strdup(name)))
        goto no_memory;
42
    if (!(pvalue = strdup(value)))
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
        goto no_memory;

    if (VIR_RESIZE_N(uri->params, uri->paramsAlloc, uri->paramsCount, 1) < 0)
        goto no_memory;

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

    return 0;

no_memory:
    VIR_FREE(pname);
    VIR_FREE(pvalue);
    virReportOOMError();
    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. */
76
        end = strchr(query, '&');
77
        if (!end)
78
            end = strchr(query, ';');
79
        if (!end)
80
            end = query + strlen(query);
81 82

        /* Find the first '=' character between here and end. */
83
        eq = strchr(query, '=');
84 85 86 87 88 89 90 91 92 93
        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) {
94
            name = xmlURIUnescapeString(query, end - query, NULL);
95 96 97 98 99 100
            if (!name) goto no_memory;
        }
        /* Or if we have "name=" here (works around annoying
         * problem when calling xmlURIUnescapeString with len = 0).
         */
        else if (eq+1 == end) {
101
            name = xmlURIUnescapeString(query, eq - query, NULL);
102 103 104 105 106 107 108 109 110 111
            if (!name) goto no_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 {
112
            name = xmlURIUnescapeString(query, eq - query, NULL);
113 114
            if (!name)
                goto no_memory;
115
            value = xmlURIUnescapeString(eq+1, end - (eq+1), NULL);
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
            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);
            goto no_memory;
        }
        VIR_FREE(name);
        VIR_FREE(value);

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

    return 0;

 no_memory:
    virReportOOMError();
    return -1;
}

M
Martin Kletzander 已提交
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
/**
 * 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)
{
158 159
    xmlURIPtr xmluri;
    virURIPtr ret = NULL;
M
Martin Kletzander 已提交
160

161 162 163
    xmluri = xmlParseURI(uri);

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

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    if (VIR_ALLOC(ret) < 0)
        goto no_memory;

    if (xmluri->scheme &&
        !(ret->scheme = strdup(xmluri->scheme)))
        goto no_memory;
    if (xmluri->server &&
        !(ret->server = strdup(xmluri->server)))
        goto no_memory;
    ret->port = xmluri->port;
    if (xmluri->path &&
        !(ret->path = strdup(xmluri->path)))
        goto no_memory;
#ifdef HAVE_XMLURI_QUERY_RAW
    if (xmluri->query_raw &&
        !(ret->query = strdup(xmluri->query_raw)))
        goto no_memory;
#else
    if (xmluri->query &&
        !(ret->query = strdup(xmluri->query)))
        goto no_memory;
#endif
    if (xmluri->fragment &&
        !(ret->fragment = strdup(xmluri->fragment)))
        goto no_memory;
195 196 197
    if (xmluri->user &&
        !(ret->user = strdup(xmluri->user)))
        goto no_memory;
198

M
Martin Kletzander 已提交
199
    /* First check: does it even make sense to jump inside */
200
    if (ret->server != NULL &&
M
Martin Kletzander 已提交
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
        ret->server[0] == '[') {
        size_t length = strlen(ret->server);

        /* We want to modify the server string only if there are
         * square brackets on both ends and inside there is IPv6
         * address. Otherwise we could make a mistake by modifying
         * something other than an IPv6 address. */
        if (ret->server[length - 1] == ']' && strchr(ret->server, ':')) {
            memmove(&ret->server[0], &ret->server[1], length - 2);
            ret->server[length - 2] = '\0';
        }
        /* Even after such modification, it is completely ok to free
         * the uri with xmlFreeURI() */
    }

216 217 218
    if (virURIParseParams(ret) < 0)
        goto error;

219 220
    xmlFreeURI(xmluri);

M
Martin Kletzander 已提交
221
    return ret;
222 223 224

no_memory:
    virReportOOMError();
225
error:
226 227 228
    xmlFreeURI(xmluri);
    virURIFree(ret);
    return NULL;
M
Martin Kletzander 已提交
229 230 231 232 233 234 235 236 237 238 239 240 241 242
}

/**
 * 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 *
243
virURIFormat(virURIPtr uri)
M
Martin Kletzander 已提交
244
{
245
    xmlURI xmluri;
M
Martin Kletzander 已提交
246 247 248
    char *tmpserver = NULL;
    char *ret;

249 250 251 252 253 254
    memset(&xmluri, 0, sizeof(xmluri));

    xmluri.scheme = uri->scheme;
    xmluri.server = uri->server;
    xmluri.port = uri->port;
    xmluri.path = uri->path;
255 256 257
#ifdef HAVE_XMLURI_QUERY_RAW
    xmluri.query_raw = uri->query;
#else
258
    xmluri.query = uri->query;
259
#endif
260
    xmluri.fragment = uri->fragment;
261
    xmluri.user = uri->user;
262

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

267 268
        if (virAsprintf(&tmpserver, "[%s]", xmluri.server) < 0) {
            virReportOOMError();
M
Martin Kletzander 已提交
269
            return NULL;
270
        }
M
Martin Kletzander 已提交
271

272
        xmluri.server = tmpserver;
M
Martin Kletzander 已提交
273 274
    }

275
    ret = (char *)xmlSaveUri(&xmluri);
276 277 278 279
    if (!ret) {
        virReportOOMError();
        goto cleanup;
    }
M
Martin Kletzander 已提交
280

281
cleanup:
282
    VIR_FREE(tmpserver);
M
Martin Kletzander 已提交
283 284 285

    return ret;
}
286 287


288 289 290 291 292 293 294
char *virURIFormatParams(virURIPtr uri)
{
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    int i, amp = 0;

    for (i = 0; i < uri->paramsCount; ++i) {
        if (!uri->params[i].ignore) {
295 296 297
            if (amp) virBufferAddChar(&buf, '&');
            virBufferStrcat(&buf, uri->params[i].name, "=", NULL);
            virBufferURIEncodeString(&buf, uri->params[i].value);
298 299 300 301 302 303 304 305 306 307 308 309 310
            amp = 1;
        }
    }

    if (virBufferError(&buf)) {
        virBufferFreeAndReset(&buf);
        virReportOOMError();
        return NULL;
    }

    return virBufferContentAndReset(&buf);
}

311 312 313 314 315 316 317 318
/**
 * virURIFree:
 * @uri: uri to free
 *
 * Frees the URI
 */
void virURIFree(virURIPtr uri)
{
319 320
    size_t i;

321 322 323 324 325
    if (!uri)
        return;

    VIR_FREE(uri->scheme);
    VIR_FREE(uri->server);
326
    VIR_FREE(uri->user);
327 328
    VIR_FREE(uri->path);
    VIR_FREE(uri->query);
329 330 331 332 333 334 335 336
    VIR_FREE(uri->fragment);

    for (i = 0 ; i < uri->paramsCount ; i++) {
        VIR_FREE(uri->params[i].name);
        VIR_FREE(uri->params[i].value);
    }
    VIR_FREE(uri->params);

337
    VIR_FREE(uri);
338
}