1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-url.c : utility functions to parse URLs */
7 * Bertrand Guiheneuf <bertrand@helixcode.com>
8 * Dan Winship <danw@helixcode.com>
9 * Tiago Antà o <tiagoantao@bigfoot.com>
10 * Jeffrey Stedfast <fejj@helixcode.com>
12 * Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com)
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
36 #include "camel-url.h"
37 #include "camel-mime-utils.h"
38 #include "camel-exception.h"
39 #include "camel-object.h"
42 * camel_url_new: create a CamelURL object from a string
43 * @url_string: The string containing the URL to scan
45 * This routine takes a string and parses it as a URL of the form:
47 * protocol://user;AUTH=mech:password@host:port/path
49 * The protocol, followed by a ":" is required. If it is followed by * "//",
50 * there must be an "authority" containing at least a host,
51 * which ends at the end of the string or at the next "/". If there
52 * is an "@" in the authority, there must be a username before it,
53 * and the host comes after it. The authmech, password, and port are
54 * optional, and the punctuation that preceeds them is omitted if
55 * they are. Everything after the authority (or everything after the
56 * protocol if there was no authority) is the path. We consider the
57 * "/" between the authority and the path to be part of the path,
58 * although this is incorrect according to RFC 1738.
60 * The port, if present, must be numeric.
62 * Return value: a CamelURL structure containing the URL items.
65 camel_url_new (const char *url_string, CamelException *ex)
68 char *semi, *colon, *at, *slash;
71 g_assert (url_string);
73 /* Find protocol: initial substring until ":" */
74 colon = strchr (url_string, ':');
76 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
77 _("URL string `%s' contains no protocol"),
82 url = g_new0 (CamelURL, 1);
83 url->protocol = g_strndup (url_string, colon - url_string);
84 g_strdown (url->protocol);
89 if (!((*p >= 'a' && *p <= 'z') ||
90 (*p == '-') || (*p == '+') || (*p == '.'))) {
91 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
92 _("URL string `%s' contains an invalid protocol"),
99 if (strncmp (colon, "://", 3) != 0) {
101 url->path = g_strdup (colon + 1);
102 camel_url_decode (url->path);
107 url_string = colon + 3;
109 /* If there is an @ sign in the authority, look for user,
110 * authmech, and password before it.
112 slash = strchr (url_string, '/');
113 at = strchr (url_string, '@');
114 if (at && (!slash || at < slash)) {
115 colon = strchr (url_string, ':');
116 if (colon && colon < at) {
117 url->passwd = g_strndup (colon + 1, at - colon - 1);
118 camel_url_decode (url->passwd);
124 semi = strchr(url_string, ';');
125 if (semi && (semi < colon || (!colon && semi < at)) &&
126 !strncasecmp (semi, ";auth=", 6)) {
127 url->authmech = g_strndup (semi + 6,
129 camel_url_decode (url->authmech);
131 url->authmech = NULL;
135 url->user = g_strndup (url_string, semi - url_string);
136 camel_url_decode (url->user);
139 url->user = url->passwd = url->authmech = NULL;
141 /* Find host and port. */
142 slash = strchr (url_string, '/');
143 colon = strchr (url_string, ':');
144 if (slash && colon > slash)
148 url->host = g_strndup (url_string, colon - url_string);
149 url->port = strtoul (colon + 1, &colon, 10);
150 if (*colon && colon != slash) {
151 camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_URL_INVALID,
152 _("Port number in URL `%s' is non-"
153 "numeric"), url_string);
154 camel_url_free (url);
158 url->host = g_strndup (url_string, slash - url_string);
159 camel_url_decode (url->host);
162 url->host = g_strdup (url_string);
163 camel_url_decode (url->host);
169 url->path = g_strdup (slash);
170 camel_url_decode (url->path);
176 camel_url_to_string (CamelURL *url, gboolean show_passwd)
179 char *user = NULL, *authmech = NULL, *passwd = NULL;
180 char *host = NULL, *path = NULL;
184 user = camel_url_encode (url->user, TRUE, ":;@/");
186 if (url->authmech && *url->authmech)
187 authmech = camel_url_encode (url->authmech, TRUE, ":@/");
189 if (show_passwd && url->passwd)
190 passwd = camel_url_encode (url->passwd, TRUE, "@/");
193 host = camel_url_encode (url->host, TRUE, ":/");
196 g_snprintf (port, sizeof (port), "%d", url->port);
201 path = camel_url_encode (url->path, FALSE, NULL);
203 return_result = g_strdup_printf ("%s:%s%s%s%s%s%s%s%s%s%s%s%s",
207 authmech ? ";auth=" : "",
208 authmech ? authmech : "",
210 passwd ? passwd : "",
215 path && host && *path != '/' ? "/" : "",
223 return return_result;
227 camel_url_free (CamelURL *url)
231 g_free (url->protocol);
233 g_free (url->authmech);
234 g_free (url->passwd);
241 void camel_url_set_protocol(CamelURL *url, const char *p)
243 g_free(url->protocol);
244 url->protocol = g_strdup(p);
247 void camel_url_set_host(CamelURL *url, const char *h)
250 url->host = g_strdup(h);
253 void camel_url_set_port(CamelURL *url, int port)
257 void camel_url_set_path(CamelURL *url, const char *p)
260 url->path = g_strdup(p);
267 * @escape_unsafe: whether or not to %-escape "unsafe" characters.
269 * @escape_extra: additional characters to escape.
271 * This %-encodes the given URL part and returns the escaped version
272 * in allocated memory, which the caller must free when it is done.
275 camel_url_encode (char *part, gboolean escape_unsafe, char *escape_extra)
279 /* worst case scenario = 3 times the initial */
280 p = work = g_malloc (3 * strlen (part) + 1);
283 if (((guchar) *part >= 127) || ((guchar) *part <= ' ') ||
284 (escape_unsafe && strchr ("\"%#<>{}|\\^~[]`", *part)) ||
285 (escape_extra && strchr (escape_extra, *part))) {
286 sprintf (p, "%%%.02hX", (guchar) *part++);
296 #define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10)
302 * %-decodes the passed-in URL *in place*. The decoded version is
303 * never longer than the encoded version, so there does not need to
304 * be any additional space at the end of the string.
307 camel_url_decode (char *part)
311 s = d = (guchar *)part;
314 if (isxdigit (s[1]) && isxdigit (s[2])) {
315 *d++ = HEXVAL (s[1]) * 16 + HEXVAL (s[2]);
326 add_hash (guint *hash, char *s)
329 *hash ^= g_str_hash(s);
332 guint camel_url_hash (const void *v)
334 const CamelURL *u = v;
337 add_hash (&hash, u->protocol);
338 add_hash (&hash, u->user);
339 add_hash (&hash, u->authmech);
340 add_hash (&hash, u->host);
341 add_hash (&hash, u->path);
348 check_equal (char *s1, char *s2)
360 return strcmp (s1, s2) == 0;
363 int camel_url_equal(const void *v, const void *v2)
365 const CamelURL *u1 = v, *u2 = v2;
367 return check_equal(u1->protocol, u2->protocol)
368 && check_equal(u1->user, u2->user)
369 && check_equal(u1->authmech, u2->authmech)
370 && check_equal(u1->host, u2->host)
371 && check_equal(u1->path, u2->path)
372 && u1->port == u2->port;