diff options
Diffstat (limited to 'plugins/mms/libmms/uri.c')
-rw-r--r-- | plugins/mms/libmms/uri.c | 1033 |
1 files changed, 1033 insertions, 0 deletions
diff --git a/plugins/mms/libmms/uri.c b/plugins/mms/libmms/uri.c new file mode 100644 index 00000000..6b6475eb --- /dev/null +++ b/plugins/mms/libmms/uri.c @@ -0,0 +1,1033 @@ +/* GNet - Networking library + * Copyright (C) 2000-2003 David Helder, David Bolcsfoldi, Eric Williams + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + */ + +/* FIXME: #include "gnet-private.h" */ +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdio.h> + +#include "uri.h" +#include <string.h> + +static void field_unescape (char *str); +static char* field_escape (char* str, unsigned char mask); + +#define USERINFO_ESCAPE_MASK 0x01 +#define PATH_ESCAPE_MASK 0x02 +#define QUERY_ESCAPE_MASK 0x04 +#define FRAGMENT_ESCAPE_MASK 0x08 + +/* #define FALSE 0 */ +/* #define TRUE (!FALSE) */ + +static unsigned char neednt_escape_table[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x00, 0x0c, + 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x0f, + 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +/* +Perl code to generate above table: + +#!/usr/bin/perl + +$ok = "abcdefghijklmnopqrstuvwxyz" . + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" . + "0123456789" . + "-_.!~*'()"; +$userinfo_ok = ';:&=+\$,'; +$path_ok = ':\@&=+\$,;/'; +$query_ok = ';/?:\@&=+\$,'; +$fragment_ok = ';/?:\@&=+\$,'; + +for ($i = 0; $i < 32; $i++) +{ + print " "; + for ($j = 0; $j < 8; $j++) + { + $num = 0; + $letter = chr(($i * 8) + $j); + + $num |= 0b0001 if (index($userinfo_ok, $letter) != -1); + $num |= 0b0010 if (index($path_ok, $letter) != -1); + $num |= 0b0100 if (index($query_ok, $letter) != -1); + $num |= 0b1000 if (index($fragment_ok, $letter) != -1); + $num |= 0b1111 if (index($ok, $letter) != -1); + + printf "0x%02x, ", $num; + } + print "\n"; +} +*/ + + +/* our own ISSPACE. ANSI isspace is local dependent */ +#define ISSPACE(C) (((C) >= 9 && (C) <= 13) || (C) == ' ') + + +static int split_user_passwd(const char* in, char** user, char** passwd) +{ + char *tmp = strdup(in); + + if(!tmp) + return 0; + *passwd = strchr(tmp, ':'); + if(!(*passwd)) + { + free(tmp); + return 0; + } + *((*passwd)++) = '\0'; // don't you love C? :) + + *user = strdup(tmp); + if(!*user) + return 0; + *passwd = strdup(*passwd); + if(!*passwd) + return 0; + + free(tmp); + return 1; +} + +/** + * gnet_uri_new + * @uri: URI string + * + * Creates a #GURI from a string. Empty fields are set to NULL. The + * parser does not validate the URI -- it will accept some malformed + * URI. URIs are usually in the form + * scheme://userinfo@hostname:port/path?query#fragment + * + * URIs created from user input are typically unescaped. URIs + * created from machine input (e.g. received over the internet) are + * typically escaped. + * + * Returns: a new #GURI, or NULL if there was a failure. + * + **/ +GURI* +gnet_uri_new (const char* uri) +{ + GURI* guri = NULL; + const char* p; + const char* temp; + + if (!uri) { + return NULL; + } + + /* Skip initial whitespace */ + p = uri; + while (*p && ISSPACE((int)*p)) + ++p; + if (!*p) /* Error if it's just a string of space */ + return NULL; + + guri = malloc (sizeof (GURI)); + memset (guri, 0, sizeof (GURI)); + + /* Scheme */ + temp = p; + while (*p && *p != ':' && *p != '/' && *p != '?' && *p != '#') + ++p; + if (*p == ':') + { + guri->scheme = strndup (temp, p - temp); + ++p; + } + else /* This char is NUL, /, ?, or # */ + p = temp; + + /* Authority */ + if (*p == '/' && p[1] == '/') + { + char *userinfo; + p += 2; + + /* Userinfo */ + temp = p; + while (*p && *p != '@' && *p != '/' ) /* Look for @ or / */ + ++p; + if (*p == '@') /* Found userinfo */ + { + userinfo = strndup (temp, p - temp); + if(!split_user_passwd(userinfo, &guri->user, &guri->passwd)) + { + free(userinfo); + goto error; + } + free(userinfo); + ++p; + } + else + p = temp; + + /* Hostname */ + + /* Check for IPv6 canonical hostname in brackets */ + if (*p == '[') + { + p++; /* Skip [ */ + temp = p; + while (*p && *p != ']') ++p; + if ((p - temp) == 0) + goto error; + guri->hostname = strndup (temp, p - temp); + if (*p) + p++; /* Skip ] (if there) */ + } + else + { + temp = p; + while (*p && *p != '/' && *p != '?' && *p != '#' && *p != ':') ++p; + if ((p - temp) == 0) + goto error; + guri->hostname = strndup (temp, p - temp); + } + + /* Port */ + if (*p == ':') + { + for (++p; isdigit((int)*p); ++p) + guri->port = guri->port * 10 + (*p - '0'); + } + + } + + /* Path (we are liberal and won't check if it starts with /) */ + temp = p; + while (*p && *p != '?' && *p != '#') + ++p; + if (p != temp) + guri->path = strndup(temp, p - temp); + + /* Query */ + if (*p == '?') + { + temp = p + 1; + while (*p && *p != '#') + ++p; + guri->query = strndup (temp, p - temp); + } + + /* Fragment */ + if (*p == '#') + { + ++p; + guri->fragment = strdup (p); + } + + return guri; + + error: + gnet_uri_delete (guri); + return NULL; +} + + +/** + * gnet_uri_new_fields + * @scheme: scheme + * @hostname: host name + * @port: port + * @path: path + * + * Creates a #GURI from the fields. This function uses the most + * common fields. Use gnet_uri_new_fields_all() to specify all + * fields. + * + * Returns: a new #GURI. + * + **/ +GURI* +gnet_uri_new_fields (const char* scheme, const char* hostname, + const int port, const char* path) +{ + GURI* uri = NULL; + + uri = malloc (sizeof (GURI)); + memset (uri, 0, sizeof (GURI)); + if (scheme) uri->scheme = strdup (scheme); + if (hostname) uri->hostname = strdup (hostname); + uri->port = port; + if (path) uri->path = strdup (path); + + return uri; +} + + +/** + * gnet_uri_new_fields_all + * @scheme: scheme + * @userinfo: user info + * @hostname: host name + * @port: port + * @path: path + * @query: query + * @fragment: fragment + * + * Creates a #GURI from all fields. + * + * Returns: a new #GURI. + * + **/ +GURI* +gnet_uri_new_fields_all (const char* scheme, const char* user, + const char* passwd, const char* hostname, + const int port, const char* path, + const char* query, const char* fragment) +{ + GURI* uri = NULL; + + uri = malloc (sizeof (GURI)); + memset (uri, 0, sizeof (GURI)); + if (scheme) uri->scheme = strdup (scheme); + if (user) uri->user = strdup (user); + if (passwd) uri->passwd = strdup (passwd); + if (hostname) uri->hostname = strdup (hostname); + uri->port = port; + if (path) uri->path = strdup (path); + if (query) uri->query = strdup (query); + if (fragment) uri->fragment = strdup (fragment); + + return uri; +} + + +/** + * gnet_uri_clone: + * @uri: a #GURI + * + * Copies a #GURI. + * + * Returns: a copy of @uri. + * + **/ +GURI* +gnet_uri_clone (const GURI* uri) +{ + GURI* uri2; + + if (!uri) { + return NULL; + } + + uri2 = malloc (sizeof (GURI)); + memset (uri2, 0, sizeof (GURI)); + uri2->scheme = strdup (uri->scheme); + uri2->user = strdup (uri->user); + uri2->passwd = strdup (uri->passwd); + uri2->hostname = strdup (uri->hostname); + uri2->port = uri->port; + uri2->path = strdup (uri->path); + uri2->query = strdup (uri->query); + uri2->fragment = strdup (uri->fragment); + + return uri2; +} + + +/** + * gnet_uri_delete: + * @uri: a #GURI + * + * Deletes a #GURI. + * + **/ +void +gnet_uri_delete (GURI* uri) +{ + if (uri) + { + free (uri->scheme); + free (uri->user); + free (uri->passwd); + free (uri->hostname); + free (uri->path); + free (uri->query); + free (uri->fragment); + free (uri); + } +} + + + + +#define SAFESTRCMP(A,B) (((A)&&(B))?(strcmp((A),(B))):((A)||(B))) + +/** + * gnet_uri_equal + * @p1: a #GURI + * @p2: another #GURI + * + * Compares two #GURI's for equality. + * + * Returns: TRUE if they are equal; FALSE otherwise. + * + **/ +int +gnet_uri_equal (const char * p1, const char * p2) +{ + const GURI* uri1 = (const GURI*) p1; + const GURI* uri2 = (const GURI*) p2; + + if (!uri1) { + return 0; + } + if (!uri2) { + return 0; + } + + if (uri1->port == uri2->port && + !SAFESTRCMP(uri1->scheme, uri2->scheme) && + !SAFESTRCMP(uri1->user, uri2->user) && + !SAFESTRCMP(uri1->passwd, uri2->passwd) && + !SAFESTRCMP(uri1->hostname, uri2->hostname) && + !SAFESTRCMP(uri1->path, uri2->path) && + !SAFESTRCMP(uri1->query, uri2->query) && + !SAFESTRCMP(uri1->fragment, uri2->fragment)) + return 1; + + return 0; +} + + +/** + * gnet_uri_hash + * @p: a #GURI + * + * Creates a hash code for @p for use with GHashTable. + * + * Returns: hash code for @p. + * + **/ +#if 0 +unsigned int +gnet_uri_hash (const char * p) +{ + const GURI* uri = (const GURI*) p; + unsigned int h = 0; + + if (!uri) { + return 0; + } + + if (uri->scheme) h = g_str_hash (uri->scheme); + if (uri->user) h ^= g_str_hash (uri->user); + if (uri->passwd) h ^= g_str_hash (uri->passwd); + if (uri->hostname) h ^= g_str_hash (uri->hostname); + h ^= uri->port; + if (uri->path) h ^= g_str_hash (uri->path); + if (uri->query) h ^= g_str_hash (uri->query); + if (uri->fragment) h ^= g_str_hash (uri->fragment); + + return h; +} +#endif + + +/** + * gnet_uri_escape + * @uri: a #GURI + * + * Escapes the fields in a #GURI. Network protocols use escaped + * URIs. People use unescaped URIs. + * + **/ +void +gnet_uri_escape (GURI* uri) +{ + if (!uri) { + return; + } + + uri->user = field_escape (uri->user, USERINFO_ESCAPE_MASK); + uri->passwd = field_escape (uri->passwd, USERINFO_ESCAPE_MASK); + uri->path = field_escape (uri->path, PATH_ESCAPE_MASK); + uri->query = field_escape (uri->query, QUERY_ESCAPE_MASK); + uri->fragment = field_escape (uri->fragment, FRAGMENT_ESCAPE_MASK); +} + + +/** + * gnet_uri_unescape + * @uri: a #GURI + * + * Unescapes the fields in the URI. Network protocols use escaped + * URIs. People use unescaped URIs. + * + **/ +void +gnet_uri_unescape (GURI* uri) +{ + if (!uri) { + return; + } + + if (uri->user) + field_unescape (uri->user); + if (uri->passwd) + field_unescape (uri->passwd); + if (uri->path) + field_unescape (uri->path); + if (uri->query) + field_unescape (uri->query); + if (uri->fragment) + field_unescape (uri->fragment); +} + + +static char* +field_escape (char* str, unsigned char mask) +{ + int len; + int i; + int must_escape = 0; + char* dst; + int j; + + if (str == NULL) + return NULL; + + /* Roughly calculate buffer size */ + len = 0; + for (i = 0; str[i]; i++) + { + if (neednt_escape_table[(unsigned int) str[i]] & mask) + len++; + else + { + len += 3; + must_escape = 1; + } + } + + /* Don't escape if unnecessary */ + if (must_escape == 0) + return str; + + /* Allocate buffer */ + dst = (char*) malloc(len + 1); + memset (dst, 0, len+1); + + /* Copy */ + for (i = j = 0; str[i]; i++, j++) + { + /* Unescaped character */ + if (neednt_escape_table[(unsigned int) str[i]] & mask) + { + dst[j] = str[i]; + } + + /* Escaped character */ + else + { + dst[j] = '%'; + + if (((str[i] & 0xf0) >> 4) < 10) + dst[j+1] = ((str[i] & 0xf0) >> 4) + '0'; + else + dst[j+1] = ((str[i] & 0xf0) >> 4) + 'a' - 10; + + if ((str[i] & 0x0f) < 10) + dst[j+2] = (str[i] & 0x0f) + '0'; + else + dst[j+2] = (str[i] & 0x0f) + 'a' - 10; + + j += 2; /* and j is incremented in loop too */ + } + } + dst[j] = '\0'; + + free (str); + return dst; +} + + + +static void +field_unescape (char* s) +{ + char* src; + char* dst; + + for (src = dst = s; *src; ++src, ++dst) + { + if (src[0] == '%' && src[1] != '\0' && src[2] != '\0') + { + int high, low; + + if ('a' <= src[1] && src[1] <= 'f') + high = src[1] - 'a' + 10; + else if ('A' <= src[1] && src[1] <= 'F') + high = src[1] - 'A' + 10; + else if ('0' <= src[1] && src[1] <= '9') + high = src[1] - '0'; + else /* malformed */ + goto regular_copy; + + if ('a' <= src[2] && src[2] <= 'f') + low = src[2] - 'a' + 10; + else if ('A' <= src[2] && src[2] <= 'F') + low = src[2] - 'A' + 10; + else if ('0' <= src[2] && src[2] <= '9') + low = src[2] - '0'; + else /* malformed */ + goto regular_copy; + + *dst = (char)((high << 4) + low); + src += 2; + } + else + { + regular_copy: + *dst = *src; + } + } + + *dst = '\0'; +} + + + +/** + * gnet_uri_get_string + * @uri: a #GURI + * + * Gets a string representation of a #GURI. This function does not + * escape or unescape the fields first. Call gnet_uri_escape() or + * gnet_uri_unescape first if necessary. + * + * Returns: a string. + * + **/ +char* +gnet_uri_get_string (const GURI* uri) +{ + char* rv = NULL; + char *buffer = malloc (1024); + memset (buffer, 0, 1024); + char *b = buffer; + int remaining = 1024; + + if (!uri) { + return NULL; + } + + if (uri->scheme) { + int n = snprintf (buffer, n, "%s:", uri->scheme); + buffer += n; + remaining -= n; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + + if (uri->user || uri->passwd || uri->hostname || uri->port) { + strcpy (buffer, "//"); + buffer += 2; + remaining -= 2; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + + if (uri->user) + { + int n = strlen (uri->user); + memcpy (buffer, uri->user, n+1); + buffer += n; + remaining -= n; + if (remaining < 10) { + free (buffer); + return NULL; + } + strcpy (buffer, "@"); + buffer += 1; + remaining -= 1; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + if (uri->passwd) + { + int n = strlen (uri->passwd); + memcpy (buffer, uri->passwd, n+1); + buffer += n; + remaining -= n; + if (remaining < 10) { + free (buffer); + return NULL; + } + strcpy (buffer, "@"); + buffer += 1; + remaining -= 1; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + + /* Add brackets around the hostname if it's IPv6 */ + if (uri->hostname) + { + if (strchr(uri->hostname, ':') == NULL) { + int n = strlen (uri->hostname); + memcpy (buffer, uri->hostname, n+1); + buffer += n; + remaining -= n; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + else { + int n = snprintf (buffer, remaining, "[%s]", uri->hostname); + buffer += n; + remaining -= n; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + } + + if (uri->port) { + int n = snprintf (buffer, remaining, ":%d", uri->port); + buffer += n; + remaining -= n; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + + if (uri->path) + { + if (*uri->path == '/' || + !(uri->user || uri->passwd || uri->hostname || uri->port)) { + int n = strlen (uri->path); + memcpy (buffer, uri->path, n+1); + buffer += n; + remaining -= n; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + else { + int n = snprintf (buffer, remaining, "/%s", uri->path); + buffer += n; + remaining -= n; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + } + + if (uri->query) { + int n = snprintf (buffer, remaining, "?%s", uri->query); + buffer += n; + remaining -= n; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + + if (uri->fragment) { + int n = snprintf (buffer, remaining, "#%s", uri->fragment); + buffer += n; + remaining -= n; + if (remaining < 10) { + free (buffer); + return NULL; + } + } + + /* Free only GString not data contained, return the data instead */ + return b; +} + + +/** + * gnet_uri_set_scheme + * @uri: a #GURI + * @scheme: scheme + * + * Sets a #GURI's scheme. + * + **/ +void +gnet_uri_set_scheme (GURI* uri, const char* scheme) +{ + if (!uri) { + return; + } + + if (uri->scheme) + { + free (uri->scheme); + uri->scheme = NULL; + } + + if (scheme) + uri->scheme = strdup (scheme); +} + + +/** + * gnet_uri_set_userinfo + * @uri: a #GURI + * @userinfo: user info + * + * Sets a #GURI's user info. + * + **/ +void +gnet_uri_set_userinfo (GURI* uri, const char* user, const char* passwd) +{ + if (!uri) { + return; + } + + if (uri->user) + { + free (uri->user); + uri->user = NULL; + } + if (uri->passwd) + { + free (uri->passwd); + uri->passwd = NULL; + } + + if (user) + uri->user = strdup (user); + if (passwd) + uri->passwd = strdup (passwd); +} + + +/** + * gnet_uri_set_hostname + * @uri: a #GURI + * @hostname: host name + * + * Sets a #GURI's host name. + * + **/ +void +gnet_uri_set_hostname (GURI* uri, const char* hostname) +{ + if (!uri) { + return; + } + + if (uri->hostname) + { + free (uri->hostname); + uri->hostname = NULL; + } + + if (hostname) + uri->hostname = strdup (hostname); +} + + +/** + * gnet_uri_set_port + * @uri: a #GURI + * @port: port + * + * Set a #GURI's port. + * + **/ +void +gnet_uri_set_port (GURI* uri, int port) +{ + uri->port = port; +} + + +/** + * gnet_uri_set_path + * @uri: a #GURI + * @path: path + * + * Set a #GURI's path. + * + **/ +void +gnet_uri_set_path (GURI* uri, const char* path) +{ + if (!uri) { + return; + } + + if (uri->path) + { + free (uri->path); + uri->path = NULL; + } + + if (path) + uri->path = strdup (path); +} + + + +/** + * gnet_uri_set_query + * @uri: a #GURI + * @query: query + * + * Set a #GURI's query. + * + **/ +void +gnet_uri_set_query (GURI* uri, const char* query) +{ + if (!uri) { + return; + } + + if (uri->query) + { + free (uri->query); + uri->query = NULL; + } + + if (query) + uri->query = strdup (query); +} + + +/** + * gnet_uri_set_fragment + * @uri: a #GURI + * @fragment: fragment + * + * Set a #GURI's fragment. + * + **/ +void +gnet_uri_set_fragment (GURI* uri, const char* fragment) +{ + if (!uri) { + return; + } + + if (uri->fragment) + { + free (uri->fragment); + uri->fragment = NULL; + } + + if (fragment) + uri->fragment = strdup (fragment); +} + + +/** + * gnet_mms_helper + * @uri: a #GURI + * + * returns char* representation of an uri that is sutable for + * using in mms protocol. + * '/path?query' + * + **/ + +char* gnet_mms_helper(const GURI* uri, int make_absolute) +{ + size_t len = 0; + char *ret, *tmp = NULL; + + + /* Strip leading slashes and calculate the length of the path + * which might not be present in the URI */ + if (uri->path) { + tmp = uri->path; + while (*tmp == '/') + ++tmp; + len += strlen(tmp); + } + /* Append length of the query part */ + if (uri->query) + len += strlen(uri->query) + 1; /* + '?' */ + + if (!(ret = (char *) malloc(len + 2))) + return NULL; + memset (ret, 0, len + 2); + + if (make_absolute) + strcpy(ret, "/"); + else + ret[0] = 0; + + /* Copy the optional path */ + if (tmp) + strcat(ret, tmp); + + /* Copy the optional query */ + if (uri->query) { + strcat(ret, "?"); + strcat(ret, uri->query); + } + + return ret; +} |