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