1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* e-url.c
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: Jon Trowbridge <trow@ximian.com>
19  *          Rodrigo Moya <rodrigo@ximian.com>
20  */
21 
22 #include "evolution-data-server-config.h"
23 
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "e-url.h"
28 
29 /**
30  * e_url_shroud:
31  * @url: The url to shroud.
32  *
33  * Removes the moniker (i.e. mailto:) from a url.
34  *
35  * Returns: The newly-allocated shrouded url.
36  **/
37 gchar *
e_url_shroud(const gchar * url)38 e_url_shroud (const gchar *url)
39 {
40 	const gchar *first_colon = NULL;
41 	const gchar *last_at = NULL;
42 	const gchar *p;
43 	gchar *shrouded;
44 
45 	if (url == NULL)
46 		return NULL;
47 
48 	/* Skip past the moniker */
49 	for (p = url; *p && *p != ':'; ++p);
50 	if (*p)
51 		++p;
52 
53 	while (*p) {
54 		if (first_colon == NULL && *p == ':')
55 			first_colon = p;
56 		if (*p == '@')
57 			last_at = p;
58 		++p;
59 	}
60 
61 	if (first_colon && last_at && first_colon < last_at) {
62 		shrouded = g_malloc (first_colon - url + strlen (last_at) + 1);
63 		memcpy (shrouded, url, first_colon - url);
64 		strcpy (shrouded + (first_colon - url), last_at);
65 	} else {
66 		shrouded = g_strdup (url);
67 	}
68 
69 	return shrouded;
70 }
71 
72 /**
73  * e_url_equal:
74  * @url1: The first url to compare.
75  * @url2: The second url to compare.
76  *
77  * Checks two urls for equality, after first removing any monikers on
78  * the urls.
79  *
80  * Returns: %TRUE if the urls are equal, %FALSE if they are not.
81  **/
82 gboolean
e_url_equal(const gchar * url1,const gchar * url2)83 e_url_equal (const gchar *url1,
84              const gchar *url2)
85 {
86 	gchar *shroud1 = e_url_shroud (url1);
87 	gchar *shroud2 = e_url_shroud (url2);
88 	gint len1, len2;
89 	gboolean rv;
90 
91 	if (shroud1 == NULL || shroud2 == NULL) {
92 		rv = (shroud1 == shroud2);
93 	} else {
94 		len1 = strlen (shroud1);
95 		len2 = strlen (shroud2);
96 
97 		rv = !strncmp (shroud1, shroud2, MIN (len1, len2));
98 	}
99 
100 	g_free (shroud1);
101 	g_free (shroud2);
102 
103 	return rv;
104 }
105 
106 #define HEXVAL(c) (isdigit (c) ? (c) - '0' : tolower (c) - 'a' + 10)
107 
108 static void
uri_decode(gchar * part)109 uri_decode (gchar *part)
110 {
111 	guchar *s, *d;
112 
113 	s = d = (guchar *) part;
114 	while (*s) {
115 		if (*s == '%') {
116 			if (isxdigit (s[1]) && isxdigit (s[2])) {
117 				*d++ = HEXVAL (s[1]) * 16 + HEXVAL (s[2]);
118 				s += 3;
119 			} else
120 				*d++ = *s++;
121 		} else
122 			*d++ = *s++;
123 	}
124 	*d = '\0';
125 }
126 
127 /**
128  * e_uri_new:
129  * @uri_string: The uri to represent as an #EUri.
130  *
131  * Creates an #EUri representation of the uri given in @uri_string.
132  *
133  * Returns: The newly-allocated #EUri structure.
134  **/
135 EUri *
e_uri_new(const gchar * uri_string)136 e_uri_new (const gchar *uri_string)
137 {
138 	EUri *uri;
139 	const gchar *end, *hash, *colon, *semi, *at, *slash, *question;
140 	const gchar *p;
141 
142 	if (!uri_string)
143 		return NULL;
144 
145 	uri = g_new0 (EUri, 1);
146 
147 	/* find fragment */
148 	end = hash = strchr (uri_string, '#');
149 	if (hash && hash[1]) {
150 		uri->fragment = g_strdup (hash + 1);
151 		uri_decode (uri->fragment);
152 	}
153 	else
154 		end = uri_string + strlen (uri_string);
155 
156 	/* find protocol: initial [a-z+.-]* substring until ":" */
157 	p = uri_string;
158 	while (p < end && (isalnum ((guchar) *p) ||
159 			   *p == '.' || *p == '+' || *p == '-'))
160 		p++;
161 
162 	if (p > uri_string && *p == ':') {
163 		uri->protocol = g_ascii_strdown (uri_string, p - uri_string);
164 		uri_string = p + 1;
165 	}
166 	else
167 		uri->protocol = g_strdup ("file");
168 
169 	if (!*uri_string)
170 		return uri;
171 
172 	/* check for authority */
173 	if (strncmp (uri_string, "//", 2) == 0) {
174 		uri_string += 2;
175 
176 		slash = uri_string + strcspn (uri_string, "/#");
177 		at = strchr (uri_string, '@');
178 		if (at && at < slash) {
179 			const gchar *at2;
180 			/* this is for cases where username contains '@' at it, like:
181 			 * http://user@domain.com@server.addr.com/path
182 			 * We skip all at-s before the slash here. */
183 
184 			while (at2 = strchr (at + 1, '@'), at2 && at2 < slash) {
185 				at = at2;
186 			}
187 		}
188 		if (at && at < slash) {
189 			colon = strchr (uri_string, ':');
190 			if (colon && colon < at) {
191 				uri->passwd = g_strndup (colon + 1, at - colon - 1);
192 				uri_decode (uri->passwd);
193 			}
194 			else {
195 				uri->passwd = NULL;
196 				colon = at;
197 			}
198 
199 			semi = strchr (uri_string, ';');
200 			if (semi && semi < colon &&
201 			    !g_ascii_strncasecmp (semi, ";auth=", 6)) {
202 				uri->authmech = g_strndup (semi + 6, colon - semi - 6);
203 				uri_decode (uri->authmech);
204 			}
205 			else {
206 				uri->authmech = NULL;
207 				semi = colon;
208 			}
209 
210 			uri->user = g_strndup (uri_string, semi - uri_string);
211 			uri_decode (uri->user);
212 			uri_string = at + 1;
213 		}
214 		else
215 			uri->user = uri->passwd = uri->authmech = NULL;
216 
217 		/* find host and port */
218 		colon = strchr (uri_string, ':');
219 		if (colon && colon < slash) {
220 			uri->host = g_strndup (uri_string, colon - uri_string);
221 			uri->port = strtoul (colon + 1, NULL, 10);
222 		}
223 		else {
224 			uri->host = g_strndup (uri_string, slash - uri_string);
225 			uri_decode (uri->host);
226 			uri->port = 0;
227 		}
228 
229 		uri_string = slash;
230 	}
231 
232 	/* find query */
233 	question = memchr (uri_string, '?', end - uri_string);
234 	if (question) {
235 		if (question[1]) {
236 			uri->query = g_strndup (question + 1, end - (question + 1));
237 			uri_decode (uri->query);
238 		}
239 		end = question;
240 	}
241 
242 	/* find parameters */
243 	semi = memchr (uri_string, ';', end - uri_string);
244 	if (semi) {
245 		if (semi[1]) {
246 			const gchar *cur, *ptr, *eq;
247 			gchar *name, *value;
248 
249 			for (cur = semi + 1; cur < end; cur = ptr + 1) {
250 				ptr = memchr (cur, ';', end - cur);
251 				if (!ptr)
252 					ptr = end;
253 				eq = memchr (cur, '=', ptr - cur);
254 				if (eq) {
255 					name = g_strndup (cur, eq - cur);
256 					value = g_strndup (eq + 1, ptr - (eq + 1));
257 					uri_decode (value);
258 				} else {
259 					name = g_strndup (cur, ptr - cur);
260 					value = g_strdup ("");
261 				}
262 				uri_decode (name);
263 				g_datalist_set_data_full (
264 					&uri->params, name,
265 					value, g_free);
266 				g_free (name);
267 			}
268 		}
269 		end = semi;
270 	}
271 
272 	if (end != uri_string) {
273 		uri->path = g_strndup (uri_string, end - uri_string);
274 		uri_decode (uri->path);
275 	}
276 
277 	return uri;
278 }
279 
280 /**
281  * e_uri_free:
282  * @uri: A pointer to the #EUri to free.
283  *
284  * Frees the memory of an #EUri structure.
285  **/
286 void
e_uri_free(EUri * uri)287 e_uri_free (EUri *uri)
288 {
289 	if (uri) {
290 		g_free (uri->protocol);
291 		g_free (uri->user);
292 		g_free (uri->authmech);
293 		g_free (uri->passwd);
294 		g_free (uri->host);
295 		g_free (uri->path);
296 		g_datalist_clear (&uri->params);
297 		g_free (uri->query);
298 		g_free (uri->fragment);
299 
300 		g_free (uri);
301 	}
302 }
303 
304 /**
305  * e_uri_get_param:
306  * @uri: The #EUri to get the parameter from.
307  * @name: The name of the parameter to get.
308  *
309  * Retrieves the value of the parameter associated with @name in @uri.
310  *
311  * Returns: The value of the parameter.
312  **/
313 const gchar *
e_uri_get_param(EUri * uri,const gchar * name)314 e_uri_get_param (EUri *uri,
315                  const gchar *name)
316 {
317 	return g_datalist_get_data (&uri->params, name);
318 }
319 
320 static void
copy_param_cb(GQuark key_id,gpointer data,gpointer user_data)321 copy_param_cb (GQuark key_id,
322                gpointer data,
323                gpointer user_data)
324 {
325 	GData *params = (GData *) user_data;
326 
327 	g_datalist_id_set_data_full (&params, key_id, g_strdup (data), g_free);
328 }
329 
330 /**
331  * e_uri_copy:
332  * @uri: The #EUri to copy.
333  *
334  * Makes a copy of @uri.
335  *
336  * Returns: The newly-allocated copy of @uri.
337  **/
338 EUri *
e_uri_copy(EUri * uri)339 e_uri_copy (EUri *uri)
340 {
341 	EUri *uri_copy;
342 
343 	g_return_val_if_fail (uri != NULL, NULL);
344 
345 	uri_copy = g_new0 (EUri, 1);
346 	uri_copy->protocol = g_strdup (uri->protocol);
347 	uri_copy->user = g_strdup (uri->user);
348 	uri_copy->authmech = g_strdup (uri->authmech);
349 	uri_copy->passwd = g_strdup (uri->passwd);
350 	uri_copy->host = g_strdup (uri->host);
351 	uri_copy->port = uri->port;
352 	uri_copy->path = g_strdup (uri->path);
353 	uri_copy->query = g_strdup (uri->query);
354 	uri_copy->fragment = g_strdup (uri->fragment);
355 
356 	/* copy uri->params */
357 	g_datalist_foreach (&uri->params,
358 			    (GDataForeachFunc) copy_param_cb,
359 			    &uri_copy->params);
360 
361 	return uri_copy;
362 }
363 
364 /**
365  * e_uri_to_string:
366  * @uri: The #EUri to convert to a string.
367  * @show_password: Whether or not to show the password in the string.
368  *
369  * Creates a string representation of @uri. The password will only be
370  * included in the string if @show_password is set to %TRUE.
371  *
372  * Returns: The string representation of @uri.
373  **/
374 gchar *
e_uri_to_string(EUri * uri,gboolean show_password)375 e_uri_to_string (EUri *uri,
376                  gboolean show_password)
377 {
378 	gchar *str_uri = NULL;
379 
380 	g_return_val_if_fail (uri != NULL, NULL);
381 
382 	if (uri->port != 0)
383 		str_uri = g_strdup_printf (
384 			"%s://%s%s%s%s%s%s%s:%d%s%s%s",
385 			uri->protocol,
386 			uri->user ? uri->user : "",
387 			uri->authmech ? ";auth=" : "",
388 			uri->authmech ? uri->authmech : "",
389 			uri->passwd && show_password ? ":" : "",
390 			uri->passwd && show_password ? uri->passwd : "",
391 			uri->user ? "@" : "",
392 			uri->host ? uri->host : "",
393 			uri->port,
394 			uri->path ? uri->path : "",
395 			uri->query ? "?" : "",
396 			uri->query ? uri->query : "");
397 	else
398 		str_uri = g_strdup_printf (
399 			"%s://%s%s%s%s%s%s%s%s%s%s",
400 			uri->protocol,
401 			uri->user ? uri->user : "",
402 			uri->authmech ? ";auth=" : "",
403 			uri->authmech ? uri->authmech : "",
404 			uri->passwd && show_password ? ":" : "",
405 			uri->passwd && show_password ? uri->passwd : "",
406 			uri->user ? "@" : "",
407 			uri->host ? uri->host : "",
408 			uri->path ? uri->path : "",
409 			uri->query ? "?" : "",
410 			uri->query ? uri->query : "");
411 
412 	return str_uri;
413 }
414