878
|
1 /* GNet - Networking library
|
|
2 * Copyright (C) 2000-2003 David Helder, David Bolcsfoldi, Eric Williams
|
|
3 *
|
|
4 * This library is free software; you can redistribute it and/or
|
|
5 * modify it under the terms of the GNU Library General Public
|
|
6 * License as published by the Free Software Foundation; either
|
|
7 * version 2 of the License, or (at your option) any later version.
|
|
8 *
|
|
9 * This library 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 * Library General Public License for more details.
|
|
13 *
|
|
14 * You should have received a copy of the GNU Library General Public
|
|
15 * License along with this library; if not, write to the
|
|
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
17 * Boston, MA 02110-1301, USA.
|
|
18 */
|
|
19
|
|
20 /* FIXME: #include "gnet-private.h" */
|
|
21 #include <glib.h>
|
|
22 #include <string.h>
|
|
23 #include <stdlib.h>
|
|
24 #include <unistd.h>
|
|
25 #include <ctype.h>
|
|
26
|
|
27 #include "uri.h"
|
|
28 #include <string.h>
|
|
29
|
|
30 static void field_unescape (char *str);
|
|
31 static char* field_escape (char* str, unsigned char mask);
|
|
32
|
|
33 #define USERINFO_ESCAPE_MASK 0x01
|
|
34 #define PATH_ESCAPE_MASK 0x02
|
|
35 #define QUERY_ESCAPE_MASK 0x04
|
|
36 #define FRAGMENT_ESCAPE_MASK 0x08
|
|
37
|
|
38 /* #define FALSE 0 */
|
|
39 /* #define TRUE (!FALSE) */
|
|
40
|
|
41 static unsigned char neednt_escape_table[] =
|
|
42 {
|
|
43 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
44 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
45 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
46 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
47 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0x0f, 0x0f,
|
|
48 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e,
|
|
49 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
|
|
50 0x0f, 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x00, 0x0c,
|
|
51 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
|
|
52 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
|
|
53 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
|
|
54 0x0f, 0x0f, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x0f,
|
|
55 0x00, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
|
|
56 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
|
|
57 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
|
|
58 0x0f, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x0f, 0x00,
|
|
59 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
60 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
62 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
63 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
64 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
65 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
67 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
68 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
69 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
70 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
75 };
|
|
76
|
|
77
|
|
78 /*
|
|
79 Perl code to generate above table:
|
|
80
|
|
81 #!/usr/bin/perl
|
|
82
|
|
83 $ok = "abcdefghijklmnopqrstuvwxyz" .
|
|
84 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" .
|
|
85 "0123456789" .
|
|
86 "-_.!~*'()";
|
|
87 $userinfo_ok = ';:&=+\$,';
|
|
88 $path_ok = ':\@&=+\$,;/';
|
|
89 $query_ok = ';/?:\@&=+\$,';
|
|
90 $fragment_ok = ';/?:\@&=+\$,';
|
|
91
|
|
92 for ($i = 0; $i < 32; $i++)
|
|
93 {
|
|
94 print " ";
|
|
95 for ($j = 0; $j < 8; $j++)
|
|
96 {
|
|
97 $num = 0;
|
|
98 $letter = chr(($i * 8) + $j);
|
|
99
|
|
100 $num |= 0b0001 if (index($userinfo_ok, $letter) != -1);
|
|
101 $num |= 0b0010 if (index($path_ok, $letter) != -1);
|
|
102 $num |= 0b0100 if (index($query_ok, $letter) != -1);
|
|
103 $num |= 0b1000 if (index($fragment_ok, $letter) != -1);
|
|
104 $num |= 0b1111 if (index($ok, $letter) != -1);
|
|
105
|
|
106 printf "0x%02x, ", $num;
|
|
107 }
|
|
108 print "\n";
|
|
109 }
|
|
110 */
|
|
111
|
|
112
|
|
113 /* our own ISSPACE. ANSI isspace is local dependent */
|
|
114 #define ISSPACE(C) (((C) >= 9 && (C) <= 13) || (C) == ' ')
|
|
115
|
|
116
|
|
117 static int split_user_passwd(const char* in, char** user, char** passwd)
|
|
118 {
|
|
119 char *tmp = strdup(in);
|
|
120
|
|
121 if(!tmp)
|
|
122 return 0;
|
|
123 *passwd = strchr(tmp, ':');
|
|
124 if(!(*passwd))
|
|
125 {
|
|
126 free(tmp);
|
|
127 return 0;
|
|
128 }
|
|
129 *((*passwd)++) = '\0'; // don't you love C? :)
|
|
130
|
|
131 *user = strdup(tmp);
|
|
132 if(!*user)
|
|
133 return 0;
|
|
134 *passwd = strdup(*passwd);
|
|
135 if(!*passwd)
|
|
136 return 0;
|
|
137
|
|
138 free(tmp);
|
|
139 return 1;
|
|
140 }
|
|
141
|
|
142 /**
|
|
143 * gnet_uri_new
|
|
144 * @uri: URI string
|
|
145 *
|
|
146 * Creates a #GURI from a string. Empty fields are set to NULL. The
|
|
147 * parser does not validate the URI -- it will accept some malformed
|
|
148 * URI. URIs are usually in the form
|
|
149 * scheme://userinfo@hostname:port/path?query#fragment
|
|
150 *
|
|
151 * URIs created from user input are typically unescaped. URIs
|
|
152 * created from machine input (e.g. received over the internet) are
|
|
153 * typically escaped.
|
|
154 *
|
|
155 * Returns: a new #GURI, or NULL if there was a failure.
|
|
156 *
|
|
157 **/
|
|
158 GURI*
|
|
159 gnet_uri_new (const char* uri)
|
|
160 {
|
|
161 GURI* guri = NULL;
|
|
162 const char* p;
|
|
163 const char* temp;
|
|
164
|
|
165 g_return_val_if_fail (uri, NULL);
|
|
166
|
|
167 /* Skip initial whitespace */
|
|
168 p = uri;
|
|
169 while (*p && ISSPACE((int)*p))
|
|
170 ++p;
|
|
171 if (!*p) /* Error if it's just a string of space */
|
|
172 return NULL;
|
|
173
|
|
174 guri = g_new0 (GURI, 1);
|
|
175
|
|
176 /* Scheme */
|
|
177 temp = p;
|
|
178 while (*p && *p != ':' && *p != '/' && *p != '?' && *p != '#')
|
|
179 ++p;
|
|
180 if (*p == ':')
|
|
181 {
|
|
182 guri->scheme = g_strndup (temp, p - temp);
|
|
183 ++p;
|
|
184 }
|
|
185 else /* This char is NUL, /, ?, or # */
|
|
186 p = temp;
|
|
187
|
|
188 /* Authority */
|
|
189 if (*p == '/' && p[1] == '/')
|
|
190 {
|
|
191 char *userinfo;
|
|
192 p += 2;
|
|
193
|
|
194 /* Userinfo */
|
|
195 temp = p;
|
|
196 while (*p && *p != '@' && *p != '/' ) /* Look for @ or / */
|
|
197 ++p;
|
|
198 if (*p == '@') /* Found userinfo */
|
|
199 {
|
|
200 userinfo = g_strndup (temp, p - temp);
|
|
201 if(!split_user_passwd(userinfo, &guri->user, &guri->passwd))
|
|
202 {
|
|
203 free(userinfo);
|
|
204 goto error;
|
|
205 }
|
|
206 free(userinfo);
|
|
207 ++p;
|
|
208 }
|
|
209 else
|
|
210 p = temp;
|
|
211
|
|
212 /* Hostname */
|
|
213
|
|
214 /* Check for IPv6 canonical hostname in brackets */
|
|
215 if (*p == '[')
|
|
216 {
|
|
217 p++; /* Skip [ */
|
|
218 temp = p;
|
|
219 while (*p && *p != ']') ++p;
|
|
220 if ((p - temp) == 0)
|
|
221 goto error;
|
|
222 guri->hostname = g_strndup (temp, p - temp);
|
|
223 if (*p)
|
|
224 p++; /* Skip ] (if there) */
|
|
225 }
|
|
226 else
|
|
227 {
|
|
228 temp = p;
|
|
229 while (*p && *p != '/' && *p != '?' && *p != '#' && *p != ':') ++p;
|
|
230 if ((p - temp) == 0)
|
|
231 goto error;
|
|
232 guri->hostname = g_strndup (temp, p - temp);
|
|
233 }
|
|
234
|
|
235 /* Port */
|
|
236 if (*p == ':')
|
|
237 {
|
|
238 for (++p; isdigit((int)*p); ++p)
|
|
239 guri->port = guri->port * 10 + (*p - '0');
|
|
240 }
|
|
241
|
|
242 }
|
|
243
|
|
244 /* Path (we are liberal and won't check if it starts with /) */
|
|
245 temp = p;
|
|
246 while (*p && *p != '?' && *p != '#')
|
|
247 ++p;
|
|
248 if (p != temp)
|
|
249 guri->path = g_strndup(temp, p - temp);
|
|
250
|
|
251 /* Query */
|
|
252 if (*p == '?')
|
|
253 {
|
|
254 temp = p + 1;
|
|
255 while (*p && *p != '#')
|
|
256 ++p;
|
|
257 guri->query = g_strndup (temp, p - temp);
|
|
258 }
|
|
259
|
|
260 /* Fragment */
|
|
261 if (*p == '#')
|
|
262 {
|
|
263 ++p;
|
|
264 guri->fragment = g_strdup (p);
|
|
265 }
|
|
266
|
|
267 return guri;
|
|
268
|
|
269 error:
|
|
270 gnet_uri_delete (guri);
|
|
271 return NULL;
|
|
272 }
|
|
273
|
|
274
|
|
275 /**
|
|
276 * gnet_uri_new_fields
|
|
277 * @scheme: scheme
|
|
278 * @hostname: host name
|
|
279 * @port: port
|
|
280 * @path: path
|
|
281 *
|
|
282 * Creates a #GURI from the fields. This function uses the most
|
|
283 * common fields. Use gnet_uri_new_fields_all() to specify all
|
|
284 * fields.
|
|
285 *
|
|
286 * Returns: a new #GURI.
|
|
287 *
|
|
288 **/
|
|
289 GURI*
|
|
290 gnet_uri_new_fields (const char* scheme, const char* hostname,
|
|
291 const gint port, const char* path)
|
|
292 {
|
|
293 GURI* uri = NULL;
|
|
294
|
|
295 uri = g_new0 (GURI, 1);
|
|
296 if (scheme) uri->scheme = g_strdup (scheme);
|
|
297 if (hostname) uri->hostname = g_strdup (hostname);
|
|
298 uri->port = port;
|
|
299 if (path) uri->path = g_strdup (path);
|
|
300
|
|
301 return uri;
|
|
302 }
|
|
303
|
|
304
|
|
305 /**
|
|
306 * gnet_uri_new_fields_all
|
|
307 * @scheme: scheme
|
|
308 * @userinfo: user info
|
|
309 * @hostname: host name
|
|
310 * @port: port
|
|
311 * @path: path
|
|
312 * @query: query
|
|
313 * @fragment: fragment
|
|
314 *
|
|
315 * Creates a #GURI from all fields.
|
|
316 *
|
|
317 * Returns: a new #GURI.
|
|
318 *
|
|
319 **/
|
|
320 GURI*
|
|
321 gnet_uri_new_fields_all (const char* scheme, const char* user,
|
|
322 const char* passwd, const char* hostname,
|
|
323 const gint port, const char* path,
|
|
324 const char* query, const char* fragment)
|
|
325 {
|
|
326 GURI* uri = NULL;
|
|
327
|
|
328 uri = g_new0 (GURI, 1);
|
|
329 if (scheme) uri->scheme = g_strdup (scheme);
|
|
330 if (user) uri->user = g_strdup (user);
|
|
331 if (passwd) uri->passwd = g_strdup (passwd);
|
|
332 if (hostname) uri->hostname = g_strdup (hostname);
|
|
333 uri->port = port;
|
|
334 if (path) uri->path = g_strdup (path);
|
|
335 if (query) uri->query = g_strdup (query);
|
|
336 if (fragment) uri->fragment = g_strdup (fragment);
|
|
337
|
|
338 return uri;
|
|
339 }
|
|
340
|
|
341
|
|
342 /**
|
|
343 * gnet_uri_clone:
|
|
344 * @uri: a #GURI
|
|
345 *
|
|
346 * Copies a #GURI.
|
|
347 *
|
|
348 * Returns: a copy of @uri.
|
|
349 *
|
|
350 **/
|
|
351 GURI*
|
|
352 gnet_uri_clone (const GURI* uri)
|
|
353 {
|
|
354 GURI* uri2;
|
|
355
|
|
356 g_return_val_if_fail (uri, NULL);
|
|
357
|
|
358 uri2 = g_new0 (GURI, 1);
|
|
359 uri2->scheme = g_strdup (uri->scheme);
|
|
360 uri2->user = g_strdup (uri->user);
|
|
361 uri2->passwd = g_strdup (uri->passwd);
|
|
362 uri2->hostname = g_strdup (uri->hostname);
|
|
363 uri2->port = uri->port;
|
|
364 uri2->path = g_strdup (uri->path);
|
|
365 uri2->query = g_strdup (uri->query);
|
|
366 uri2->fragment = g_strdup (uri->fragment);
|
|
367
|
|
368 return uri2;
|
|
369 }
|
|
370
|
|
371
|
|
372 /**
|
|
373 * gnet_uri_delete:
|
|
374 * @uri: a #GURI
|
|
375 *
|
|
376 * Deletes a #GURI.
|
|
377 *
|
|
378 **/
|
|
379 void
|
|
380 gnet_uri_delete (GURI* uri)
|
|
381 {
|
|
382 if (uri)
|
|
383 {
|
|
384 g_free (uri->scheme);
|
|
385 g_free (uri->user);
|
|
386 g_free (uri->passwd);
|
|
387 g_free (uri->hostname);
|
|
388 g_free (uri->path);
|
|
389 g_free (uri->query);
|
|
390 g_free (uri->fragment);
|
|
391 g_free (uri);
|
|
392 }
|
|
393 }
|
|
394
|
|
395
|
|
396
|
|
397
|
|
398 #define SAFESTRCMP(A,B) (((A)&&(B))?(strcmp((A),(B))):((A)||(B)))
|
|
399
|
|
400 /**
|
|
401 * gnet_uri_equal
|
|
402 * @p1: a #GURI
|
|
403 * @p2: another #GURI
|
|
404 *
|
|
405 * Compares two #GURI's for equality.
|
|
406 *
|
|
407 * Returns: TRUE if they are equal; FALSE otherwise.
|
|
408 *
|
|
409 **/
|
|
410 int
|
|
411 gnet_uri_equal (gconstpointer p1, gconstpointer p2)
|
|
412 {
|
|
413 const GURI* uri1 = (const GURI*) p1;
|
|
414 const GURI* uri2 = (const GURI*) p2;
|
|
415
|
|
416 g_return_val_if_fail (uri1, FALSE);
|
|
417 g_return_val_if_fail (uri2, FALSE);
|
|
418
|
|
419 if (uri1->port == uri2->port &&
|
|
420 !SAFESTRCMP(uri1->scheme, uri2->scheme) &&
|
|
421 !SAFESTRCMP(uri1->user, uri2->user) &&
|
|
422 !SAFESTRCMP(uri1->passwd, uri2->passwd) &&
|
|
423 !SAFESTRCMP(uri1->hostname, uri2->hostname) &&
|
|
424 !SAFESTRCMP(uri1->path, uri2->path) &&
|
|
425 !SAFESTRCMP(uri1->query, uri2->query) &&
|
|
426 !SAFESTRCMP(uri1->fragment, uri2->fragment))
|
|
427 return TRUE;
|
|
428
|
|
429 return FALSE;
|
|
430 }
|
|
431
|
|
432
|
|
433 /**
|
|
434 * gnet_uri_hash
|
|
435 * @p: a #GURI
|
|
436 *
|
|
437 * Creates a hash code for @p for use with GHashTable.
|
|
438 *
|
|
439 * Returns: hash code for @p.
|
|
440 *
|
|
441 **/
|
|
442 guint
|
|
443 gnet_uri_hash (gconstpointer p)
|
|
444 {
|
|
445 const GURI* uri = (const GURI*) p;
|
|
446 guint h = 0;
|
|
447
|
|
448 g_return_val_if_fail (uri, 0);
|
|
449
|
|
450 if (uri->scheme) h = g_str_hash (uri->scheme);
|
|
451 if (uri->user) h ^= g_str_hash (uri->user);
|
|
452 if (uri->passwd) h ^= g_str_hash (uri->passwd);
|
|
453 if (uri->hostname) h ^= g_str_hash (uri->hostname);
|
|
454 h ^= uri->port;
|
|
455 if (uri->path) h ^= g_str_hash (uri->path);
|
|
456 if (uri->query) h ^= g_str_hash (uri->query);
|
|
457 if (uri->fragment) h ^= g_str_hash (uri->fragment);
|
|
458
|
|
459 return h;
|
|
460 }
|
|
461
|
|
462
|
|
463 /**
|
|
464 * gnet_uri_escape
|
|
465 * @uri: a #GURI
|
|
466 *
|
|
467 * Escapes the fields in a #GURI. Network protocols use escaped
|
|
468 * URIs. People use unescaped URIs.
|
|
469 *
|
|
470 **/
|
|
471 void
|
|
472 gnet_uri_escape (GURI* uri)
|
|
473 {
|
|
474 g_return_if_fail (uri);
|
|
475
|
|
476 uri->user = field_escape (uri->user, USERINFO_ESCAPE_MASK);
|
|
477 uri->passwd = field_escape (uri->passwd, USERINFO_ESCAPE_MASK);
|
|
478 uri->path = field_escape (uri->path, PATH_ESCAPE_MASK);
|
|
479 uri->query = field_escape (uri->query, QUERY_ESCAPE_MASK);
|
|
480 uri->fragment = field_escape (uri->fragment, FRAGMENT_ESCAPE_MASK);
|
|
481 }
|
|
482
|
|
483
|
|
484 /**
|
|
485 * gnet_uri_unescape
|
|
486 * @uri: a #GURI
|
|
487 *
|
|
488 * Unescapes the fields in the URI. Network protocols use escaped
|
|
489 * URIs. People use unescaped URIs.
|
|
490 *
|
|
491 **/
|
|
492 void
|
|
493 gnet_uri_unescape (GURI* uri)
|
|
494 {
|
|
495 g_return_if_fail (uri);
|
|
496
|
|
497 if (uri->user)
|
|
498 field_unescape (uri->user);
|
|
499 if (uri->passwd)
|
|
500 field_unescape (uri->passwd);
|
|
501 if (uri->path)
|
|
502 field_unescape (uri->path);
|
|
503 if (uri->query)
|
|
504 field_unescape (uri->query);
|
|
505 if (uri->fragment)
|
|
506 field_unescape (uri->fragment);
|
|
507 }
|
|
508
|
|
509
|
|
510 static char*
|
|
511 field_escape (char* str, unsigned char mask)
|
|
512 {
|
|
513 gint len;
|
|
514 gint i;
|
|
515 int must_escape = FALSE;
|
|
516 char* dst;
|
|
517 gint j;
|
|
518
|
|
519 if (str == NULL)
|
|
520 return NULL;
|
|
521
|
|
522 /* Roughly calculate buffer size */
|
|
523 len = 0;
|
|
524 for (i = 0; str[i]; i++)
|
|
525 {
|
|
526 if (neednt_escape_table[(guint) str[i]] & mask)
|
|
527 len++;
|
|
528 else
|
|
529 {
|
|
530 len += 3;
|
|
531 must_escape = TRUE;
|
|
532 }
|
|
533 }
|
|
534
|
|
535 /* Don't escape if unnecessary */
|
|
536 if (must_escape == FALSE)
|
|
537 return str;
|
|
538
|
|
539 /* Allocate buffer */
|
|
540 dst = (char*) g_malloc(len + 1);
|
|
541
|
|
542 /* Copy */
|
|
543 for (i = j = 0; str[i]; i++, j++)
|
|
544 {
|
|
545 /* Unescaped character */
|
|
546 if (neednt_escape_table[(guint) str[i]] & mask)
|
|
547 {
|
|
548 dst[j] = str[i];
|
|
549 }
|
|
550
|
|
551 /* Escaped character */
|
|
552 else
|
|
553 {
|
|
554 dst[j] = '%';
|
|
555
|
|
556 if (((str[i] & 0xf0) >> 4) < 10)
|
|
557 dst[j+1] = ((str[i] & 0xf0) >> 4) + '0';
|
|
558 else
|
|
559 dst[j+1] = ((str[i] & 0xf0) >> 4) + 'a' - 10;
|
|
560
|
|
561 if ((str[i] & 0x0f) < 10)
|
|
562 dst[j+2] = (str[i] & 0x0f) + '0';
|
|
563 else
|
|
564 dst[j+2] = (str[i] & 0x0f) + 'a' - 10;
|
|
565
|
|
566 j += 2; /* and j is incremented in loop too */
|
|
567 }
|
|
568 }
|
|
569 dst[j] = '\0';
|
|
570
|
|
571 g_free (str);
|
|
572 return dst;
|
|
573 }
|
|
574
|
|
575
|
|
576
|
|
577 static void
|
|
578 field_unescape (char* s)
|
|
579 {
|
|
580 char* src;
|
|
581 char* dst;
|
|
582
|
|
583 for (src = dst = s; *src; ++src, ++dst)
|
|
584 {
|
|
585 if (src[0] == '%' && src[1] != '\0' && src[2] != '\0')
|
|
586 {
|
|
587 gint high, low;
|
|
588
|
|
589 if ('a' <= src[1] && src[1] <= 'f')
|
|
590 high = src[1] - 'a' + 10;
|
|
591 else if ('A' <= src[1] && src[1] <= 'F')
|
|
592 high = src[1] - 'A' + 10;
|
|
593 else if ('0' <= src[1] && src[1] <= '9')
|
|
594 high = src[1] - '0';
|
|
595 else /* malformed */
|
|
596 goto regular_copy;
|
|
597
|
|
598 if ('a' <= src[2] && src[2] <= 'f')
|
|
599 low = src[2] - 'a' + 10;
|
|
600 else if ('A' <= src[2] && src[2] <= 'F')
|
|
601 low = src[2] - 'A' + 10;
|
|
602 else if ('0' <= src[2] && src[2] <= '9')
|
|
603 low = src[2] - '0';
|
|
604 else /* malformed */
|
|
605 goto regular_copy;
|
|
606
|
|
607 *dst = (char)((high << 4) + low);
|
|
608 src += 2;
|
|
609 }
|
|
610 else
|
|
611 {
|
|
612 regular_copy:
|
|
613 *dst = *src;
|
|
614 }
|
|
615 }
|
|
616
|
|
617 *dst = '\0';
|
|
618 }
|
|
619
|
|
620
|
|
621
|
|
622 /**
|
|
623 * gnet_uri_get_string
|
|
624 * @uri: a #GURI
|
|
625 *
|
|
626 * Gets a string representation of a #GURI. This function does not
|
|
627 * escape or unescape the fields first. Call gnet_uri_escape() or
|
|
628 * gnet_uri_unescape first if necessary.
|
|
629 *
|
|
630 * Returns: a string.
|
|
631 *
|
|
632 **/
|
|
633 char*
|
|
634 gnet_uri_get_string (const GURI* uri)
|
|
635 {
|
|
636 char* rv = NULL;
|
|
637 GString* buffer = NULL;
|
|
638
|
|
639 g_return_val_if_fail (uri, NULL);
|
|
640
|
|
641 buffer = g_string_sized_new (16);
|
|
642
|
|
643 if (uri->scheme)
|
|
644 g_string_sprintfa (buffer, "%s:", uri->scheme);
|
|
645
|
|
646 if (uri->user || uri->passwd || uri->hostname || uri->port)
|
|
647 g_string_append (buffer, "//");
|
|
648
|
|
649 if (uri->user)
|
|
650 {
|
|
651 buffer = g_string_append (buffer, uri->user);
|
|
652 buffer = g_string_append_c (buffer, '@');
|
|
653 }
|
|
654 if (uri->passwd)
|
|
655 {
|
|
656 buffer = g_string_append (buffer, uri->passwd);
|
|
657 buffer = g_string_append_c (buffer, '@');
|
|
658 }
|
|
659
|
|
660 /* Add brackets around the hostname if it's IPv6 */
|
|
661 if (uri->hostname)
|
|
662 {
|
|
663 if (strchr(uri->hostname, ':') == NULL)
|
|
664 buffer = g_string_append (buffer, uri->hostname);
|
|
665 else
|
|
666 g_string_sprintfa (buffer, "[%s]", uri->hostname);
|
|
667 }
|
|
668
|
|
669 if (uri->port)
|
|
670 g_string_sprintfa (buffer, ":%d", uri->port);
|
|
671
|
|
672 if (uri->path)
|
|
673 {
|
|
674 if (*uri->path == '/' ||
|
|
675 !(uri->user || uri->passwd || uri->hostname || uri->port))
|
|
676 g_string_append (buffer, uri->path);
|
|
677 else
|
|
678 g_string_sprintfa (buffer, "/%s", uri->path);
|
|
679 }
|
|
680
|
|
681 if (uri->query)
|
|
682 g_string_sprintfa (buffer, "?%s", uri->query);
|
|
683
|
|
684 if (uri->fragment)
|
|
685 g_string_sprintfa (buffer, "#%s", uri->fragment);
|
|
686
|
|
687 /* Free only GString not data contained, return the data instead */
|
|
688 rv = buffer->str;
|
|
689 g_string_free (buffer, FALSE);
|
|
690 return rv;
|
|
691 }
|
|
692
|
|
693
|
|
694 /**
|
|
695 * gnet_uri_set_scheme
|
|
696 * @uri: a #GURI
|
|
697 * @scheme: scheme
|
|
698 *
|
|
699 * Sets a #GURI's scheme.
|
|
700 *
|
|
701 **/
|
|
702 void
|
|
703 gnet_uri_set_scheme (GURI* uri, const char* scheme)
|
|
704 {
|
|
705 g_return_if_fail (uri);
|
|
706
|
|
707 if (uri->scheme)
|
|
708 {
|
|
709 g_free (uri->scheme);
|
|
710 uri->scheme = NULL;
|
|
711 }
|
|
712
|
|
713 if (scheme)
|
|
714 uri->scheme = g_strdup (scheme);
|
|
715 }
|
|
716
|
|
717
|
|
718 /**
|
|
719 * gnet_uri_set_userinfo
|
|
720 * @uri: a #GURI
|
|
721 * @userinfo: user info
|
|
722 *
|
|
723 * Sets a #GURI's user info.
|
|
724 *
|
|
725 **/
|
|
726 void
|
|
727 gnet_uri_set_userinfo (GURI* uri, const char* user, const char* passwd)
|
|
728 {
|
|
729 g_return_if_fail (uri);
|
|
730
|
|
731 if (uri->user)
|
|
732 {
|
|
733 g_free (uri->user);
|
|
734 uri->user = NULL;
|
|
735 }
|
|
736 if (uri->passwd)
|
|
737 {
|
|
738 g_free (uri->passwd);
|
|
739 uri->passwd = NULL;
|
|
740 }
|
|
741
|
|
742 if (user)
|
|
743 uri->user = g_strdup (user);
|
|
744 if (passwd)
|
|
745 uri->passwd = g_strdup (passwd);
|
|
746 }
|
|
747
|
|
748
|
|
749 /**
|
|
750 * gnet_uri_set_hostname
|
|
751 * @uri: a #GURI
|
|
752 * @hostname: host name
|
|
753 *
|
|
754 * Sets a #GURI's host name.
|
|
755 *
|
|
756 **/
|
|
757 void
|
|
758 gnet_uri_set_hostname (GURI* uri, const char* hostname)
|
|
759 {
|
|
760 g_return_if_fail (uri);
|
|
761
|
|
762 if (uri->hostname)
|
|
763 {
|
|
764 g_free (uri->hostname);
|
|
765 uri->hostname = NULL;
|
|
766 }
|
|
767
|
|
768 if (hostname)
|
|
769 uri->hostname = g_strdup (hostname);
|
|
770 }
|
|
771
|
|
772
|
|
773 /**
|
|
774 * gnet_uri_set_port
|
|
775 * @uri: a #GURI
|
|
776 * @port: port
|
|
777 *
|
|
778 * Set a #GURI's port.
|
|
779 *
|
|
780 **/
|
|
781 void
|
|
782 gnet_uri_set_port (GURI* uri, gint port)
|
|
783 {
|
|
784 uri->port = port;
|
|
785 }
|
|
786
|
|
787
|
|
788 /**
|
|
789 * gnet_uri_set_path
|
|
790 * @uri: a #GURI
|
|
791 * @path: path
|
|
792 *
|
|
793 * Set a #GURI's path.
|
|
794 *
|
|
795 **/
|
|
796 void
|
|
797 gnet_uri_set_path (GURI* uri, const char* path)
|
|
798 {
|
|
799 g_return_if_fail (uri);
|
|
800
|
|
801 if (uri->path)
|
|
802 {
|
|
803 g_free (uri->path);
|
|
804 uri->path = NULL;
|
|
805 }
|
|
806
|
|
807 if (path)
|
|
808 uri->path = g_strdup (path);
|
|
809 }
|
|
810
|
|
811
|
|
812
|
|
813 /**
|
|
814 * gnet_uri_set_query
|
|
815 * @uri: a #GURI
|
|
816 * @query: query
|
|
817 *
|
|
818 * Set a #GURI's query.
|
|
819 *
|
|
820 **/
|
|
821 void
|
|
822 gnet_uri_set_query (GURI* uri, const char* query)
|
|
823 {
|
|
824 g_return_if_fail (uri);
|
|
825
|
|
826 if (uri->query)
|
|
827 {
|
|
828 g_free (uri->query);
|
|
829 uri->query = NULL;
|
|
830 }
|
|
831
|
|
832 if (query)
|
|
833 uri->query = g_strdup (query);
|
|
834 }
|
|
835
|
|
836
|
|
837 /**
|
|
838 * gnet_uri_set_fragment
|
|
839 * @uri: a #GURI
|
|
840 * @fragment: fragment
|
|
841 *
|
|
842 * Set a #GURI's fragment.
|
|
843 *
|
|
844 **/
|
|
845 void
|
|
846 gnet_uri_set_fragment (GURI* uri, const char* fragment)
|
|
847 {
|
|
848 g_return_if_fail (uri);
|
|
849
|
|
850 if (uri->fragment)
|
|
851 {
|
|
852 g_free (uri->fragment);
|
|
853 uri->fragment = NULL;
|
|
854 }
|
|
855
|
|
856 if (fragment)
|
|
857 uri->fragment = g_strdup (fragment);
|
|
858 }
|