1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-url.c : utility functions to parse URLs
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5  *
6  * This library is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13  * for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * Authors: Dan Winship <danw@ximian.com>
19  *          Jeffrey Stedfast <fejj@ximian.com>
20  */
21 
22 #include "evolution-data-server-config.h"
23 
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include <glib/gi18n-lib.h>
30 
31 #include "camel-mime-utils.h"
32 #include "camel-object.h"
33 #include "camel-service.h"
34 #include "camel-string-utils.h"
35 #include "camel-url.h"
36 
37 static void copy_param (GQuark key_id, gpointer data, gpointer user_data);
38 static void output_param (GQuark key_id, gpointer data, gpointer user_data);
39 
40 static void append_url_encoded (GString *str, const gchar *in, const gchar *extra_enc_chars);
41 
42 GType
camel_url_get_type(void)43 camel_url_get_type (void)
44 {
45 	static GType type = G_TYPE_INVALID;
46 
47 	if (G_UNLIKELY (type == G_TYPE_INVALID))
48 		type = g_boxed_type_register_static (
49 			"CamelURL",
50 			(GBoxedCopyFunc) camel_url_copy,
51 			(GBoxedFreeFunc) camel_url_free);
52 
53 	return type;
54 }
55 
56 /**
57  * camel_url_new_with_base:
58  * @base: a base URL
59  * @url_string: the URL
60  *
61  * Parses @url_string relative to @base.
62  *
63  * Returns: a parsed #CamelURL
64  **/
65 CamelURL *
camel_url_new_with_base(CamelURL * base,const gchar * url_string)66 camel_url_new_with_base (CamelURL *base,
67                          const gchar *url_string)
68 {
69 	CamelURL *url;
70 	const gchar *end, *hash, *colon, *semi, *at, *slash, *question;
71 	const gchar *p;
72 
73 #ifdef G_OS_WIN32
74 	const gchar *start = url_string;
75 #endif
76 
77 	g_return_val_if_fail (url_string != NULL, NULL);
78 
79 	url = g_new0 (CamelURL, 1);
80 
81 	/* See RFC1808 for details. IF YOU CHANGE ANYTHING IN THIS
82 	 * FUNCTION, RUN tests/misc/url AFTERWARDS.
83 	 */
84 
85 	/* Find fragment.  RFC 1808 2.4.1 */
86 	end = hash = strchr (url_string, '#');
87 	if (hash) {
88 		if (hash[1]) {
89 			url->fragment = g_strdup (hash + 1);
90 			camel_url_decode (url->fragment);
91 		}
92 	} else
93 		end = url_string + strlen (url_string);
94 
95 	/* Find protocol: initial [a-z+.-]* substring until ":" */
96 	p = url_string;
97 	while (p < end && (isalnum ((guchar) * p) ||
98 			   *p == '.' || *p == '+' || *p == '-'))
99 		p++;
100 
101 	if (p > url_string && *p == ':') {
102 		url->protocol = g_strndup (url_string, p - url_string);
103 		camel_strdown (url->protocol);
104 		url_string = p + 1;
105 	}
106 
107 	if (!*url_string && !base)
108 		return url;
109 
110 #ifdef G_OS_WIN32
111 	if (url->protocol && !strcmp (url->protocol, "file")) {
112 		url->path = g_filename_from_uri (start, &url->host, NULL);
113 		return url;
114 	}
115 #endif
116 
117 	/* Check for authority */
118 	if (strncmp (url_string, "//", 2) == 0) {
119 		url_string += 2;
120 
121 		slash = url_string + strcspn (url_string, "/#");
122 		at = strchr (url_string, '@');
123 		if (at && at < slash) {
124 			colon = strchr (url_string, ':');
125 			if (colon && colon < at) {
126 				/* XXX We used to extract and store the
127 				 *     password here, now we just eat it. */
128 			} else {
129 				colon = at;
130 			}
131 
132 			semi = strchr (url_string, ';');
133 			if (semi && semi < colon &&
134 			    !g_ascii_strncasecmp (semi, ";auth=", 6)) {
135 				url->authmech = g_strndup (
136 					semi + 6, colon - semi - 6);
137 				camel_url_decode (url->authmech);
138 			} else {
139 				url->authmech = NULL;
140 				semi = colon;
141 			}
142 
143 			url->user = g_strndup (url_string, semi - url_string);
144 			camel_url_decode (url->user);
145 			url_string = at + 1;
146 		} else
147 			url->user = url->authmech = NULL;
148 
149 		/* Find host and port. */
150 		colon = strchr (url_string, ':');
151 		if (colon && colon < slash) {
152 			url->host = g_strndup (url_string, colon - url_string);
153 			url->port = strtoul (colon + 1, NULL, 10);
154 		} else {
155 			url->host = g_strndup (url_string, slash - url_string);
156 			camel_url_decode (url->host);
157 			url->port = 0;
158 		}
159 
160 		url_string = slash;
161 	}
162 
163 	/* Find query */
164 	question = memchr (url_string, '?', end - url_string);
165 	if (question) {
166 		if (question[1]) {
167 			url->query = g_strndup (
168 				question + 1, end - (question + 1));
169 			camel_url_decode (url->query);
170 		}
171 		end = question;
172 	}
173 
174 	/* Find parameters */
175 	semi = memchr (url_string, ';', end - url_string);
176 	if (semi) {
177 		if (semi[1]) {
178 			const gchar *cur, *p, *eq;
179 			gchar *name, *value;
180 
181 			for (cur = semi + 1; cur < end; cur = p + 1) {
182 				p = memchr (cur, ';', end - cur);
183 				if (!p)
184 					p = end;
185 				eq = memchr (cur, '=', p - cur);
186 				if (eq) {
187 					name = g_strndup (cur, eq - cur);
188 					value = g_strndup (eq + 1, p - (eq + 1));
189 					camel_url_decode (value);
190 				} else {
191 					name = g_strndup (cur, p - cur);
192 					value = g_strdup ("");
193 				}
194 				camel_url_decode (name);
195 				g_datalist_set_data_full (
196 					&url->params, name, value, g_free);
197 				g_free (name);
198 			}
199 		}
200 		end = semi;
201 	}
202 
203 	if (end != url_string) {
204 		url->path = g_strndup (url_string, end - url_string);
205 		camel_url_decode (url->path);
206 	}
207 
208 	/* Apply base URL. Again, this is spelled out in RFC 1808. */
209 	if (base && !url->protocol && url->host)
210 		url->protocol = g_strdup (base->protocol);
211 	else if (base && !url->protocol) {
212 		if (!url->user && !url->authmech &&
213 		    !url->host && !url->port && !url->path &&
214 		    !url->params && !url->query && !url->fragment)
215 			url->fragment = g_strdup (base->fragment);
216 
217 		url->protocol = g_strdup (base->protocol);
218 		url->user = g_strdup (base->user);
219 		url->authmech = g_strdup (base->authmech);
220 		url->host = g_strdup (base->host);
221 		url->port = base->port;
222 
223 		if (!url->path) {
224 			url->path = g_strdup (base->path);
225 			if (!url->params) {
226 				g_datalist_foreach (&base->params, copy_param,
227 						    &url->params);
228 				if (!url->query)
229 					url->query = g_strdup (base->query);
230 			}
231 		} else if (*url->path != '/') {
232 			gchar *newpath, *last, *p, *q;
233 
234 			/* the base->path is NULL if given Content-Base url was without last slash,
235 			 * i.e. like "http://example.com" (this expected only "http://example.com/") */
236 			last = base->path ? strrchr (base->path, '/') : NULL;
237 			if (last) {
238 				newpath = g_strdup_printf (
239 					"%.*s/%s",
240 					(gint)(last - base->path),
241 					base->path,
242 					url->path);
243 			} else
244 				newpath = g_strdup_printf ("/%s", url->path);
245 
246 			/* Remove "./" where "." is a complete segment. */
247 			for (p = newpath + 1; *p; ) {
248 				if (*(p - 1) == '/' &&
249 				    *p == '.' && *(p + 1) == '/')
250 					memmove (p, p + 2, strlen (p + 2) + 1);
251 				else
252 					p++;
253 			}
254 			/* Remove "." at end. */
255 			if (p > newpath + 2 &&
256 			    *(p - 1) == '.' && *(p - 2) == '/')
257 				*(p - 1) = '\0';
258 			/* Remove "<segment>/../" where <segment> != ".." */
259 			for (p = newpath + 1; *p; ) {
260 				if (!strncmp (p, "../", 3)) {
261 					p += 3;
262 					continue;
263 				}
264 				q = strchr (p + 1, '/');
265 				if (!q)
266 					break;
267 				if (strncmp (q, "/../", 4) != 0) {
268 					p = q + 1;
269 					continue;
270 				}
271 				memmove (p, q + 4, strlen (q + 4) + 1);
272 				p = newpath + 1;
273 			}
274 			/* Remove "<segment>/.." at end */
275 			q = strrchr (newpath, '/');
276 			if (q && !strcmp (q, "/..")) {
277 				p = q - 1;
278 				while (p > newpath && *p != '/')
279 					p--;
280 				if (strncmp (p, "/../", 4) != 0)
281 					*(p + 1) = 0;
282 			}
283 			g_free (url->path);
284 			url->path = newpath;
285 		}
286 	}
287 
288 	return url;
289 }
290 
291 static void
copy_param(GQuark key_id,gpointer data,gpointer user_data)292 copy_param (GQuark key_id,
293             gpointer data,
294             gpointer user_data)
295 {
296 	GData **copy = user_data;
297 
298 	g_datalist_id_set_data_full (copy, key_id, g_strdup (data), g_free);
299 }
300 
301 /**
302  * camel_url_new:
303  * @url_string: a URL string
304  * @error: return location for a #GError, or %NULL
305  *
306  * Parses an absolute URL.
307  *
308  * Returns: a #CamelURL if it can be parsed, or %NULL otherwise
309  **/
310 CamelURL *
camel_url_new(const gchar * url_string,GError ** error)311 camel_url_new (const gchar *url_string,
312                GError **error)
313 {
314 	CamelURL *url;
315 
316 	if (!url_string || !*url_string)
317 		return NULL;
318 
319 	url = camel_url_new_with_base (NULL, url_string);
320 
321 	if (!url->protocol) {
322 		camel_url_free (url);
323 		g_set_error (
324 			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
325 			_("Could not parse URL “%s”"), url_string);
326 		return NULL;
327 	}
328 	return url;
329 }
330 
331 /**
332  * camel_url_to_string:
333  * @url: a #CamelURL
334  * @flags: additional translation options
335  *
336  * Flatten a #CamelURL into a string.
337  *
338  * Returns: a string representing @url, which the caller must free
339  **/
340 gchar *
camel_url_to_string(CamelURL * url,CamelURLFlags flags)341 camel_url_to_string (CamelURL *url,
342                      CamelURLFlags flags)
343 {
344 	GString *str;
345 
346 	g_return_val_if_fail (url != NULL, NULL);
347 
348 	/* IF YOU CHANGE ANYTHING IN THIS FUNCTION, RUN
349 	 * tests/misc/url AFTERWARD.
350 	 */
351 
352 #ifdef G_OS_WIN32
353 	if (url->protocol && !strcmp (url->protocol, "file"))
354 		return g_filename_to_uri (url->path, url->host, NULL);
355 #endif /* G_OS_WIN32 */
356 
357 	str = g_string_sized_new (20);
358 
359 	if (url->protocol)
360 		g_string_append_printf (str, "%s:", url->protocol);
361 
362 	if (url->host) {
363 		g_string_append (str, "//");
364 		if (url->user) {
365 			append_url_encoded (str, url->user, ":;@/");
366 			if (url->authmech && *url->authmech && !(flags & CAMEL_URL_HIDE_AUTH)) {
367 				g_string_append (str, ";auth=");
368 				append_url_encoded (str, url->authmech, ":@/");
369 			}
370 			g_string_append_c (str, '@');
371 		}
372 		append_url_encoded (str, url->host, ":/");
373 		if (url->port)
374 			g_string_append_printf (str, ":%d", url->port);
375 		if (!url->path && (url->params || url->query || url->fragment))
376 			g_string_append_c (str, '/');
377 	}
378 
379 	if (url->path)
380 		append_url_encoded (str, url->path, ";?");
381 	if (url->params && !(flags & CAMEL_URL_HIDE_PARAMS))
382 		g_datalist_foreach (&url->params, output_param, str);
383 	if (url->query) {
384 		g_string_append_c (str, '?');
385 		append_url_encoded (str, url->query, NULL);
386 	}
387 	if (url->fragment) {
388 		g_string_append_c (str, '#');
389 		append_url_encoded (str, url->fragment, NULL);
390 	}
391 
392 	return g_string_free (str, FALSE);
393 }
394 
395 static void
output_param(GQuark key_id,gpointer data,gpointer user_data)396 output_param (GQuark key_id,
397               gpointer data,
398               gpointer user_data)
399 {
400 	GString *str = user_data;
401 
402 	g_string_append_c (str, ';');
403 	append_url_encoded (str, g_quark_to_string (key_id), "?=");
404 	if (*(gchar *) data) {
405 		g_string_append_c (str, '=');
406 		append_url_encoded (str, data, "?");
407 	}
408 }
409 
410 /**
411  * camel_url_free:
412  * @url: a #CamelURL
413  *
414  * Frees @url.
415  **/
416 void
camel_url_free(CamelURL * url)417 camel_url_free (CamelURL *url)
418 {
419 	if (url) {
420 		if (url->user)
421 			memset (url->user, 0, strlen (url->user));
422 		if (url->host)
423 			memset (url->host, 0, strlen (url->host));
424 		g_free (url->protocol);
425 		g_free (url->user);
426 		g_free (url->authmech);
427 		g_free (url->host);
428 		g_free (url->path);
429 		g_datalist_clear (&url->params);
430 		g_free (url->query);
431 		g_free (url->fragment);
432 
433 		g_free (url);
434 	}
435 }
436 
437 /**
438  * camel_url_set_protocol:
439  * @url: a #CamelURL
440  * @protocol: protocol schema
441  *
442  * Set the protocol of a #CamelURL.
443  **/
444 void
camel_url_set_protocol(CamelURL * url,const gchar * protocol)445 camel_url_set_protocol (CamelURL *url,
446                         const gchar *protocol)
447 {
448 	g_return_if_fail (url != NULL);
449 
450 	g_free (url->protocol);
451 	url->protocol = g_strdup (protocol);
452 }
453 
454 /**
455  * camel_url_set_user:
456  * @url: a #CamelURL
457  * @user: username
458  *
459  * Set the user of a #CamelURL.
460  **/
461 void
camel_url_set_user(CamelURL * url,const gchar * user)462 camel_url_set_user (CamelURL *url,
463                     const gchar *user)
464 {
465 	g_return_if_fail (url != NULL);
466 
467 	g_free (url->user);
468 	url->user = g_strdup (user);
469 }
470 
471 /**
472  * camel_url_set_authmech:
473  * @url: a #CamelURL
474  * @authmech: authentication mechanism
475  *
476  * Set the authmech of a #CamelURL.
477  **/
478 void
camel_url_set_authmech(CamelURL * url,const gchar * authmech)479 camel_url_set_authmech (CamelURL *url,
480                         const gchar *authmech)
481 {
482 	g_return_if_fail (url != NULL);
483 
484 	g_free (url->authmech);
485 	url->authmech = g_strdup (authmech);
486 }
487 
488 /**
489  * camel_url_set_host:
490  * @url: a #CamelURL
491  * @host: hostname
492  *
493  * Set the hostname of a #CamelURL.
494  **/
495 void
camel_url_set_host(CamelURL * url,const gchar * host)496 camel_url_set_host (CamelURL *url,
497                     const gchar *host)
498 {
499 	g_return_if_fail (url != NULL);
500 
501 	g_free (url->host);
502 	url->host = g_strdup (host);
503 }
504 
505 /**
506  * camel_url_set_path:
507  * @url: a #CamelURL
508  * @path: path
509  *
510  * Set the path component of a #CamelURL.
511  **/
512 void
camel_url_set_path(CamelURL * url,const gchar * path)513 camel_url_set_path (CamelURL *url,
514                     const gchar *path)
515 {
516 	g_return_if_fail (url != NULL);
517 
518 	g_free (url->path);
519 	url->path = g_strdup (path);
520 }
521 
522 /**
523  * camel_url_set_query:
524  * @url: a #CamelURL
525  * @query: url query
526  *
527  * Set the query of a #CamelURL.
528  **/
529 void
camel_url_set_query(CamelURL * url,const gchar * query)530 camel_url_set_query (CamelURL *url,
531                      const gchar *query)
532 {
533 	g_return_if_fail (url != NULL);
534 
535 	g_free (url->query);
536 	url->query = g_strdup (query);
537 }
538 
539 /**
540  * camel_url_set_fragment:
541  * @url: a #CamelURL
542  * @fragment: url fragment
543  *
544  * Set the fragment of a #CamelURL.
545  **/
546 void
camel_url_set_fragment(CamelURL * url,const gchar * fragment)547 camel_url_set_fragment (CamelURL *url,
548                         const gchar *fragment)
549 {
550 	g_return_if_fail (url != NULL);
551 
552 	g_free (url->fragment);
553 	url->fragment = g_strdup (fragment);
554 }
555 
556 /**
557  * camel_url_set_port:
558  * @url: a #CamelURL
559  * @port: port
560  *
561  * Set the port on a #CamelURL.
562  **/
563 void
camel_url_set_port(CamelURL * url,gint port)564 camel_url_set_port (CamelURL *url,
565                     gint port)
566 {
567 	g_return_if_fail (url != NULL);
568 
569 	url->port = port;
570 }
571 
572 /**
573  * camel_url_set_param:
574  * @url: a #CamelURL
575  * @name: name of the param to set
576  * @value: value of the param to set
577  *
578  * Set a param on the #CamelURL.
579  **/
580 void
camel_url_set_param(CamelURL * url,const gchar * name,const gchar * value)581 camel_url_set_param (CamelURL *url,
582                      const gchar *name,
583                      const gchar *value)
584 {
585 	g_return_if_fail (url != NULL);
586 
587 	if (value)
588 		g_datalist_set_data_full (&url->params, name, g_strdup (value), g_free);
589 	else
590 		g_datalist_remove_data (&url->params, name);
591 }
592 
593 /**
594  * camel_url_get_param:
595  * @url: a #CamelURL
596  * @name: name of the param
597  *
598  * Get the value of the specified param on the URL.
599  *
600  * Returns: the value of a param if found or %NULL otherwise
601  **/
602 const gchar *
camel_url_get_param(CamelURL * url,const gchar * name)603 camel_url_get_param (CamelURL *url,
604                      const gchar *name)
605 {
606 	g_return_val_if_fail (url != NULL, NULL);
607 
608 	return g_datalist_get_data (&url->params, name);
609 }
610 
611 /* From RFC 2396 2.4.3, the characters that should always be encoded */
612 static const gchar url_encoded_char[] = {
613 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x00 - 0x0f */
614 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x10 - 0x1f */
615 	1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  ' ' - '/'  */
616 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,  /*  '0' - '?'  */
617 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  '@' - 'O'  */
618 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,  /*  'P' - '_'  */
619 	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  '`' - 'o'  */
620 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1,  /*  'p' - 0x7f */
621 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
622 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
623 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
624 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
625 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
626 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
627 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
628 	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
629 };
630 
631 static void
append_url_encoded(GString * str,const gchar * in,const gchar * extra_enc_chars)632 append_url_encoded (GString *str,
633                     const gchar *in,
634                     const gchar *extra_enc_chars)
635 {
636 	const guchar *s = (const guchar *) in;
637 
638 	while (*s) {
639 		if (url_encoded_char[*s] ||
640 		    (extra_enc_chars && strchr (extra_enc_chars, *s)))
641 			g_string_append_printf (str, "%%%02x", (gint) * s++);
642 		else
643 			g_string_append_c (str, *s++);
644 	}
645 }
646 
647 /**
648  * camel_url_encode:
649  * @part: a URL part
650  * @escape_extra: additional characters beyond " \"%#<>{}|\^[]`"
651  * to escape (or %NULL)
652  *
653  * This &percnt;-encodes the given URL part and returns the escaped version
654  * in allocated memory, which the caller must free when it is done.
655  *
656  * Returns: the encoded string
657  **/
658 gchar *
camel_url_encode(const gchar * part,const gchar * escape_extra)659 camel_url_encode (const gchar *part,
660                   const gchar *escape_extra)
661 {
662 	GString *str;
663 
664 	g_return_val_if_fail (part != NULL, NULL);
665 
666 	str = g_string_new (NULL);
667 	append_url_encoded (str, part, escape_extra);
668 
669 	return g_string_free (str, FALSE);
670 }
671 
672 /**
673  * camel_url_decode:
674  * @part: a URL part
675  *
676  * &percnt;-decodes the passed-in URL *in place*. The decoded version is
677  * never longer than the encoded version, so there does not need to
678  * be any additional space at the end of the string.
679  */
680 void
camel_url_decode(gchar * part)681 camel_url_decode (gchar *part)
682 {
683 	guchar *s, *d;
684 
685 	g_return_if_fail (part != NULL);
686 
687 #define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
688 
689 	s = d = (guchar *) part;
690 	do {
691 		if (*s == '%' && isxdigit (s[1]) && isxdigit (s[2])) {
692 			*d++ = (XDIGIT (s[1]) << 4) + XDIGIT (s[2]);
693 			s += 2;
694 		} else
695 			*d++ = *s;
696 	} while (*s++);
697 }
698 
699 /**
700  * camel_url_hash:
701  * @u: the base URL
702  *
703  * Returns: the url hash
704  */
705 guint
camel_url_hash(const CamelURL * u)706 camel_url_hash (const CamelURL *u)
707 {
708 	guint hash = 0;
709 
710 #define ADD_HASH(s) if (s) hash ^= g_str_hash (s);
711 
712 	ADD_HASH (u->protocol);
713 	ADD_HASH (u->user);
714 	ADD_HASH (u->authmech);
715 	ADD_HASH (u->host);
716 	ADD_HASH (u->path);
717 	ADD_HASH (u->query);
718 	hash ^= u->port;
719 
720 	return hash;
721 }
722 
723 static gboolean
check_equal(gchar * s1,gchar * s2)724 check_equal (gchar *s1,
725              gchar *s2)
726 {
727 	if (s1 == NULL) {
728 		if (s2 == NULL)
729 			return TRUE;
730 		else
731 			return FALSE;
732 	}
733 
734 	if (s2 == NULL)
735 		return FALSE;
736 
737 	return strcmp (s1, s2) == 0;
738 }
739 
740 /**
741  * camel_url_equal:
742  * @u: the base URL
743  * @u2: the URL to compare
744  *
745  * Returns: return %TRUE if the two urls are equal
746  */
747 gboolean
camel_url_equal(const CamelURL * u,const CamelURL * u2)748 camel_url_equal (const CamelURL *u,
749                  const CamelURL *u2)
750 {
751 	return check_equal (u->protocol, u2->protocol)
752 		&& check_equal (u->user, u2->user)
753 		&& check_equal (u->authmech, u2->authmech)
754 		&& check_equal (u->host, u2->host)
755 		&& check_equal (u->path, u2->path)
756 		&& check_equal (u->query, u2->query)
757 		&& u->port == u2->port;
758 }
759 
760 /**
761  * camel_url_copy:
762  * @in: a #CamelURL to copy
763  *
764  * Copy a #CamelURL.
765  *
766  * Returns:(transfer full): a duplicate copy of @in
767  **/
768 CamelURL *
camel_url_copy(CamelURL * in)769 camel_url_copy (CamelURL *in)
770 {
771 	CamelURL *out;
772 
773 	g_return_val_if_fail (in != NULL, NULL);
774 
775 	out = g_malloc0 (sizeof (*out));
776 	out->protocol = g_strdup (in->protocol);
777 	out->user = g_strdup (in->user);
778 	out->authmech = g_strdup (in->authmech);
779 	out->host = g_strdup (in->host);
780 	out->port = in->port;
781 	out->path = g_strdup (in->path);
782 	out->params = NULL;
783 	if (in->params)
784 		g_datalist_foreach (&((CamelURL *) in)->params, copy_param, &out->params);
785 	out->query = g_strdup (in->query);
786 	out->fragment = g_strdup (in->fragment);
787 
788 	return out;
789 }
790 
791 gchar *
camel_url_decode_path(const gchar * path)792 camel_url_decode_path (const gchar *path)
793 {
794 	gchar **comps;
795 	GString *str;
796 	guint length, ii;
797 
798 	if (path == NULL || *path == '\0')
799 		return g_strdup ("");    /* ??? or NULL? */
800 
801 	str = g_string_new (NULL);
802 
803 	comps = g_strsplit (path, "/", -1);
804 	length = g_strv_length (comps);
805 
806 	for (ii = 0; ii < length; ii++) {
807 		if (ii > 0)
808 			g_string_append_c (str, '/');
809 		camel_url_decode (comps[ii]);
810 		g_string_append (str, comps[ii]);
811 	}
812 
813 	g_strfreev (comps);
814 
815 	return g_string_free (str, FALSE);
816 }
817 
818