Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / servers / exchange / lib / e2k-uri.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* Copyright (C) 1999-2004 Novell, Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU Lesser General Public
7  * License as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "e2k-uri.h"
30
31 /**
32  * e2k_uri_new:
33  * @uri_string: the URI
34  *
35  * Parses @uri_string.
36  *
37  * Return value: a parsed %E2kUri
38  **/
39 E2kUri *
40 e2k_uri_new (const char *uri_string)
41 {
42         E2kUri *uri;
43         const char *end, *hash, *colon, *semi, *at, *slash;
44         const char *question, *p;
45
46         uri = g_new0 (E2kUri, 1);
47
48         /* Find fragment. */
49         end = hash = strchr (uri_string, '#');
50         if (hash && hash[1]) {
51                 uri->fragment = g_strdup (hash + 1);
52                 e2k_uri_decode (uri->fragment);
53         } else
54                 end = uri_string + strlen (uri_string);
55
56         /* Find protocol: initial [a-z+.-]* substring until ":" */
57         p = uri_string;
58         while (p < end && (isalnum ((unsigned char)*p) ||
59                            *p == '.' || *p == '+' || *p == '-'))
60                 p++;
61
62         if (p > uri_string && *p == ':') {
63                 uri->protocol = g_ascii_strdown (uri_string, p - uri_string);
64                 uri_string = p + 1;
65         }
66
67         if (!*uri_string)
68                 return uri;
69
70         /* Check for authority */
71         if (strncmp (uri_string, "//", 2) == 0) {
72                 uri_string += 2;
73
74                 slash = uri_string + strcspn (uri_string, "/#");
75                 at = strchr (uri_string, '@');
76                 if (at && at < slash) {
77                         char *backslash;
78
79                         colon = strchr (uri_string, ':');
80                         if (colon && colon < at) {
81                                 uri->passwd = g_strndup (colon + 1,
82                                                          at - colon - 1);
83                                 e2k_uri_decode (uri->passwd);
84                         } else {
85                                 uri->passwd = NULL;
86                                 colon = at;
87                         }
88
89                         semi = strchr(uri_string, ';');
90                         if (semi && semi < colon &&
91                             !g_ascii_strncasecmp (semi, ";auth=", 6)) {
92                                 uri->authmech = g_strndup (semi + 6,
93                                                            colon - semi - 6);
94                                 e2k_uri_decode (uri->authmech);
95                         } else {
96                                 uri->authmech = NULL;
97                                 semi = colon;
98                         }
99
100                         uri->user = g_strndup (uri_string, semi - uri_string);
101                         e2k_uri_decode (uri->user);
102                         uri_string = at + 1;
103
104                         backslash = strchr (uri->user, '\\');
105                         if (!backslash)
106                                 backslash = strchr (uri->user, '/');
107                         if (backslash) {
108                                 uri->domain = uri->user;
109                                 *backslash = '\0';
110                                 uri->user = g_strdup (backslash + 1);
111                         }
112                 } else
113                         uri->user = uri->passwd = uri->domain = NULL;
114
115                 /* Find host and port. */
116                 colon = strchr (uri_string, ':');
117                 if (colon && colon < slash) {
118                         uri->host = g_strndup (uri_string, colon - uri_string);
119                         uri->port = strtoul (colon + 1, NULL, 10);
120                 } else {
121                         uri->host = g_strndup (uri_string, slash - uri_string);
122                         e2k_uri_decode (uri->host);
123                         uri->port = 0;
124                 }
125
126                 uri_string = slash;
127         }
128
129         /* Find query */
130         question = memchr (uri_string, '?', end - uri_string);
131         if (question) {
132                 if (question[1]) {
133                         uri->query = g_strndup (question + 1,
134                                                 end - (question + 1));
135                         e2k_uri_decode (uri->query);
136                 }
137                 end = question;
138         }
139
140         /* Find parameters */
141         semi = memchr (uri_string, ';', end - uri_string);
142         if (semi) {
143                 if (semi[1]) {
144                         const char *cur, *p, *eq;
145                         char *name, *value;
146
147                         for (cur = semi + 1; cur < end; cur = p + 1) {
148                                 p = memchr (cur, ';', end - cur);
149                                 if (!p)
150                                         p = end;
151                                 eq = memchr (cur, '=', p - cur);
152                                 if (eq) {
153                                         name = g_strndup (cur, eq - cur);
154                                         value = g_strndup (eq + 1, p - (eq + 1));
155                                         e2k_uri_decode (value);
156                                 } else {
157                                         name = g_strndup (cur, p - cur);
158                                         value = g_strdup ("");
159                                 }
160                                 e2k_uri_decode (name);
161                                 g_datalist_set_data_full (&uri->params, name,
162                                                           value, g_free);
163                                 g_free (name);
164                         }
165                 }
166                 end = semi;
167         }
168
169         if (end != uri_string) {
170                 uri->path = g_strndup (uri_string, end - uri_string);
171                 e2k_uri_decode (uri->path);
172         }
173
174         return uri;
175 }
176
177 /**
178  * e2k_uri_free:
179  * @uri: an %E2kUri
180  *
181  * Frees @uri
182  **/
183 void
184 e2k_uri_free (E2kUri *uri)
185 {
186         if (uri) {
187                 g_free (uri->protocol);
188                 g_free (uri->user);
189                 g_free (uri->domain);
190                 g_free (uri->authmech);
191                 g_free (uri->passwd);
192                 g_free (uri->host);
193                 g_free (uri->path);
194                 g_datalist_clear (&uri->params);
195                 g_free (uri->query);
196                 g_free (uri->fragment);
197                 
198                 g_free (uri);
199         }
200 }
201
202 /**
203  * e2k_uri_get_param:
204  * @uri: an %E2kUri
205  * @name: name of the parameter
206  *
207  * Fetches a parameter from @uri
208  *
209  * Return value: the value of @name, or %NULL if it is not set
210  **/
211 const char *
212 e2k_uri_get_param (E2kUri *uri, const char *name)
213 {
214         return g_datalist_get_data (&uri->params, name);
215 }
216
217
218 #define HEXVAL(c) (isdigit (c) ? (c) - '0' : g_ascii_tolower (c) - 'a' + 10)
219
220 /**
221  * e2k_uri_decode:
222  * @part: a piece of a URI
223  *
224  * Undoes URI-escaping in @part in-place.
225  **/
226 void
227 e2k_uri_decode (char *part)
228 {
229         guchar *s, *d;
230
231         s = d = (guchar *)part;
232         while (*s) {
233                 if (*s == '%') {
234                         if (isxdigit (s[1]) && isxdigit (s[2])) {
235                                 *d++ = HEXVAL (s[1]) * 16 + HEXVAL (s[2]);
236                                 s += 3;
237                         } else
238                                 *d++ = *s++;
239                 } else
240                         *d++ = *s++;
241         }
242         *d = '\0';
243 }
244
245 static const int uri_encoded_char[] = {
246         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x00 - 0x0f */
247         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 0x10 - 0x1f */
248         1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2,  /*  ' ' - '/'  */
249         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 2,  /*  '0' - '?'  */
250         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  '@' - 'O'  */
251         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 1, 0,  /*  'P' - '_'  */
252         1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /*  '`' - 'o'  */
253         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 1,  /*  'p' - 0x7f */
254         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
255         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
256         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
257         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
258         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
259         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
260         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
261         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
262 };
263
264 /**
265  * e2k_uri_append_encoded:
266  * @str: a %GString containing part of a URI
267  * @in: data to append to @str
268  * @wss_encode: whether or not to use the special Web Storage System
269  * encoding rules
270  * @extra_enc_chars: additional characters beyond the normal URI-reserved
271  * characters to encode when appending to @str
272  *
273  * Appends @in to @str, encoding URI-unsafe characters as needed
274  * (optionally including some Exchange-specific encodings).
275  *
276  * When appending a path, you must append each segment separately;
277  * e2k_uri_append_encoded() will encode any "/"s passed in.
278  **/
279 void
280 e2k_uri_append_encoded (GString *str, const char *in,
281                         gboolean wss_encode, const char *extra_enc_chars)
282 {
283         const unsigned char *s = (const unsigned char *)in;
284
285         while (*s) {
286                 if (extra_enc_chars && strchr (extra_enc_chars, *s))
287                         goto escape;
288                 switch (uri_encoded_char[*s]) {
289                 case 2:
290                         if (!wss_encode)
291                                 goto escape;
292                         switch (*s++) {
293                         case '/':
294                                 g_string_append (str, "_xF8FF_");
295                                 break;
296                         case '?':
297                                 g_string_append (str, "_x003F_");
298                                 break;
299                         case '\\':
300                                 g_string_append (str, "_xF8FE_");
301                                 break;
302                         case '~':
303                                 g_string_append (str, "_x007E_");
304                                 break;
305                         }
306                         break;
307                 case 1:
308                 escape:
309                         g_string_append_printf (str, "%%%02x", (int)*s++);
310                         break;
311                 default:
312                         g_string_append_c (str, *s++);
313                         break;
314                 }
315         }
316 }
317
318 /**
319  * e2k_uri_encode:
320  * @in: data to encode
321  * @wss_encode: whether or not to use the special Web Storage System
322  * encoding rules
323  * @extra_enc_chars: additional characters beyond the normal URI-reserved
324  * characters to encode when appending to @str
325  *
326  * Encodes URI-unsafe characters as in e2k_uri_append_encoded()
327  *
328  * Return value: the encoded string
329  **/
330 char *
331 e2k_uri_encode (const char *in, gboolean wss_encode,
332                 const char *extra_enc_chars)
333 {
334         GString *string;
335         char *out;
336
337         string = g_string_new (NULL);
338         e2k_uri_append_encoded (string, in, wss_encode, extra_enc_chars);
339         out = string->str;
340         g_string_free (string, FALSE);
341
342         return out;
343 }
344
345 /**
346  * e2k_uri_path:
347  * @uri_string: a well-formed absolute URI
348  *
349  * Returns the path component of @uri_string, including the initial
350  * "/". (The return value is actually a pointer into the passed-in
351  * string, meaning this will only really work if the URI has no
352  * query/fragment/etc.)
353  *
354  * Return value: the path component of @uri_string.
355  **/
356 const char *
357 e2k_uri_path (const char *uri_string)
358 {
359         const char *p;
360
361         p = strchr (uri_string, ':');
362         if (p++) {
363                 if (!strncmp (p, "//", 2)) {
364                         p = strchr (p + 2, '/');
365                         if (p)
366                                 return p;
367                 } else if (*p)
368                         return p;
369         }
370         return "";
371 }
372
373 /**
374  * e2k_uri_concat:
375  * @uri_prefix: an absolute URI
376  * @tail: a relative path
377  *
378  * Constructs a new URI consisting of the concatenation of
379  * @uri_prefix and @tail. If @uri_prefix does not end with a "/",
380  * one will be inserted between @uri_prefix and @tail.
381  *
382  * Return value: the new URI
383  **/
384 char *
385 e2k_uri_concat (const char *uri_prefix, const char *tail)
386 {
387         const char *p;
388
389         p = strrchr (uri_prefix, '/');
390         if (p && !p[1])
391                 return g_strdup_printf ("%s%s", uri_prefix, tail);
392         else
393                 return g_strdup_printf ("%s/%s", uri_prefix, tail);
394 }
395
396 /**
397  * e2k_uri_relative:
398  * @uri_prefix: an absolute URI
399  * @uri: another URI, presumably a child of @uri_prefix
400  *
401  * Returns a URI describing @uri's relation to @uri_prefix; either a
402  * relative URI consisting of the subpath of @uri underneath
403  * @uri_prefix, or all of @uri if it is not a sub-uri of @uri_prefix.
404  *
405  * Return value: the relative URI
406  **/
407 const char *
408 e2k_uri_relative (const char *uri_prefix, const char *uri)
409 {
410         int prefix_len = strlen (uri_prefix);
411
412         if (!strncmp (uri_prefix, uri, prefix_len)) {
413                 uri += prefix_len;
414                 while (*uri == '/')
415                         uri++;
416         }
417
418         return uri;
419 }