5fdf5a775e4e8c924f5368b08e4f8b81b308f4fd
[platform/upstream/libsoup.git] / libsoup / soup-uri.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* soup-uri.c : utility functions to parse URLs */
3
4 /* 
5  * Authors : 
6  *  Bertrand Guiheneuf <bertrand@helixcode.com>
7  *  Dan Winship <danw@helixcode.com>
8  *  Alex Graveley <alex@ximian.com>
9  *
10  * Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com)
11  *
12  * This program is free software; you can redistribute it and/or 
13  * modify it under the terms of the GNU General Public License as 
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
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 General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
25  * USA
26  */
27
28
29
30 /* 
31  * Here we deal with URLs following the general scheme:
32  *   protocol://user;AUTH=mech:password@host:port/name
33  * where name is a path-like string (ie dir1/dir2/....) See RFC 1738
34  * for the complete description of Uniform Resource Locators. The
35  * ";AUTH=mech" addition comes from RFC 2384, "POP URL Scheme".
36  */
37
38 /* XXX TODO:
39  * recover the words between #'s or ?'s after the path
40  * % escapes
41  */
42
43 #include <string.h>
44 #include <stdlib.h>
45
46 #include "soup-uri.h"
47 #include "soup-misc.h"
48
49 typedef struct {
50         SoupProtocol  proto;
51         gchar        *str;
52         gint          port;
53 } SoupKnownProtocols; 
54
55 SoupKnownProtocols known_protocols [] = {
56         { SOUP_PROTOCOL_HTTP,   "http://",   80 },
57         { SOUP_PROTOCOL_SHTTP,  "https://",  443 },
58         { SOUP_PROTOCOL_SMTP,   "mailto:",   25 },
59         { SOUP_PROTOCOL_SOCKS4, "socks4://", -1 },
60         { SOUP_PROTOCOL_SOCKS5, "socks5://", -1 },
61         { 0 }
62 };
63
64 static SoupProtocol
65 soup_uri_get_protocol (const gchar *proto, int *len)
66 {
67         SoupKnownProtocols *known = known_protocols;
68
69         while (known->proto) {
70                 if (!g_strncasecmp (proto, known->str, strlen (known->str))) {
71                         *len = strlen (known->str);
72                         return known->proto;
73                 }
74                 known++;
75         }
76
77         *len = 0;
78         return 0;
79 }
80
81 static gchar *
82 soup_uri_protocol_to_string (SoupProtocol proto)
83 {
84         SoupKnownProtocols *known = known_protocols;
85
86         while (known->proto) {
87                 if (known->proto == proto) return known->str;
88                 known++;
89         }
90
91         return "";
92 }
93
94 static gint
95 soup_uri_get_default_port (SoupProtocol proto)
96 {
97         SoupKnownProtocols *known = known_protocols;
98
99         while (known->proto) {
100                 if (known->proto == proto) return known->port;
101                 known++;
102         }
103
104         return -1;
105 }
106
107 /**
108  * soup_uri_new: create a SoupUri object from a string
109  *
110  * @uri_string: The string containing the URL to scan
111  * 
112  * This routine takes a gchar and parses it as a
113  * URL of the form:
114  *   protocol://user;AUTH=mech:password@host:port/path?querystring
115  * There is no test on the values. For example,
116  * "port" can be a string, not only a number!
117  * The SoupUri structure fields are filled with
118  * the scan results. When a member of the 
119  * general URL can not be found, the corresponding
120  * SoupUri member is NULL.
121  * Fields filled in the SoupUri structure are allocated
122  * and url_string is not modified. 
123  * 
124  * Return value: a SoupUri structure containing the URL items.
125  **/
126 SoupUri *
127 soup_uri_new (const gchar* uri_string)
128 {
129         SoupUri *g_uri;
130         char *semi, *colon, *at, *slash, *path, *query = NULL;
131         char **split;
132
133         g_uri = g_new0 (SoupUri,1);
134
135         /* Find protocol: initial substring until "://" */
136         colon = strchr (uri_string, ':');
137         if (colon) {
138                 gint protolen;
139                 g_uri->protocol = soup_uri_get_protocol (uri_string, &protolen);
140                 uri_string += protolen;
141         }
142
143         /* Must have a protocol */
144         if (!g_uri->protocol) return NULL;
145
146         /* If there is an @ sign, look for user, authmech, and
147          * password before it.
148          */
149         at = strchr (uri_string, '@');
150         if (at) {
151                 colon = strchr (uri_string, ':');
152                 if (colon && colon < at)
153                         g_uri->passwd = g_strndup (colon + 1, at - colon - 1);
154                 else {
155                         g_uri->passwd = NULL;
156                         colon = at;
157                 }
158
159                 semi = strchr(uri_string, ';');
160                 if (semi && semi < colon && !strncasecmp (semi, ";auth=", 6))
161                         g_uri->authmech = g_strndup (semi + 6, 
162                                                      colon - semi - 6);
163                 else {
164                         g_uri->authmech = NULL;
165                         semi = colon;
166                 }
167
168                 g_uri->user = g_strndup (uri_string, semi - uri_string);
169                 uri_string = at + 1;
170         } else
171                 g_uri->user = g_uri->passwd = g_uri->authmech = NULL;
172
173         /* Find host (required) and port. */
174         slash = strchr (uri_string, '/');
175         colon = strchr (uri_string, ':');
176         if (slash && colon > slash)
177                 colon = 0;
178
179         if (colon) {
180                 g_uri->host = g_strndup (uri_string, colon - uri_string);
181                 if (slash)
182                         g_uri->port = atoi(colon + 1);
183                 else
184                         g_uri->port = atoi(colon + 1);
185         } else if (slash) {
186                 g_uri->host = g_strndup (uri_string, slash - uri_string);
187                 g_uri->port = soup_uri_get_default_port (g_uri->protocol);
188         } else {
189                 g_uri->host = g_strdup (uri_string);
190                 g_uri->port = soup_uri_get_default_port (g_uri->protocol);
191         }
192
193         /* setup a fallback, if relative, then empty string, else
194            it will be from root */
195         if (slash == NULL) {
196                 slash = "/";
197         }
198         if (slash && *slash && !g_uri->protocol)
199                 slash++;
200
201         split = g_strsplit(slash, " ", 0);
202         path = g_strjoinv("%20", split);
203         g_strfreev(split);
204
205         if (path)
206                 query = strchr (path, '?');
207
208         if (path && query) {
209                 g_uri->path = g_strndup (path, query - path);
210                 g_uri->querystring = g_strdup (++query);
211                 g_uri->query_elems = g_strsplit (g_uri->querystring, "&", 0);
212                 g_free (path);
213         } else {
214                 g_uri->path = path;
215                 g_uri->querystring = NULL;
216         }
217
218         return g_uri;
219 }
220
221 /* Need to handle mailto which apparantly doesn't use the "//" after the : */
222 gchar *
223 soup_uri_to_string (const SoupUri *uri, gboolean show_passwd)
224 {
225         g_return_val_if_fail (uri != NULL, NULL);
226
227         if (uri->port != -1 && 
228             uri->port != soup_uri_get_default_port (uri->protocol))
229                 return g_strdup_printf(
230                         "%s%s%s%s%s%s%s%s:%d%s%s%s",
231                         soup_uri_protocol_to_string (uri->protocol),
232                         uri->user ? uri->user : "",
233                         uri->authmech ? ";auth=" : "",
234                         uri->authmech ? uri->authmech : "",
235                         uri->passwd && show_passwd ? ":" : "",
236                         uri->passwd && show_passwd ? uri->passwd : "",
237                         uri->user ? "@" : "",
238                         uri->host,
239                         uri->port,
240                         uri->path ? uri->path : "",
241                         uri->querystring ? "?" : "",
242                         uri->querystring ? uri->querystring : "");
243         else
244                 return g_strdup_printf(
245                         "%s%s%s%s%s%s%s%s%s%s%s",
246                         soup_uri_protocol_to_string (uri->protocol),
247                         uri->user ? uri->user : "",
248                         uri->authmech ? ";auth=" : "",
249                         uri->authmech ? uri->authmech : "",
250                         uri->passwd && show_passwd ? ":" : "",
251                         uri->passwd && show_passwd ? uri->passwd : "",
252                         uri->user ? "@" : "",
253                         uri->host,
254                         uri->path ? uri->path : "",
255                         uri->querystring ? "?" : "",
256                         uri->querystring ? uri->querystring : "");
257 }
258
259 SoupUri *
260 soup_uri_copy (const SoupUri* uri)
261 {
262         gchar *uri_str;
263         SoupUri *dup;
264
265         g_return_val_if_fail (uri != NULL, NULL);
266
267         uri_str = soup_uri_to_string (uri, TRUE);
268         dup = soup_uri_new (uri_str);
269         g_free (uri_str);
270
271         return dup;
272 }
273
274 void
275 soup_uri_free (SoupUri *uri)
276 {
277         g_assert (uri);
278
279         g_free (uri->user);
280         g_free (uri->authmech);
281         g_free (uri->passwd);
282         g_free (uri->host);
283         g_free (uri->path);
284         g_free (uri->querystring);
285         g_strfreev (uri->query_elems);
286
287         g_free (uri);
288 }
289
290 void
291 soup_debug_print_uri (SoupUri *uri)
292 {
293         g_return_if_fail (uri != NULL);
294
295         g_print ("Protocol: %s\n", soup_uri_protocol_to_string (uri->protocol));
296         g_print ("User:     %s\n", uri->user);
297         g_print ("Authmech: %s\n", uri->authmech);
298         g_print ("Password: %s\n", uri->passwd);
299         g_print ("Host:     %s\n", uri->host);
300         g_print ("Path:     %s\n", uri->path);
301         g_print ("Querystr: %s\n", uri->querystring);
302 }
303