~ubuntu-branches/ubuntu/karmic/gtk-gnutella/karmic

« back to all changes in this revision

Viewing changes to src/url.c

  • Committer: Bazaar Package Importer
  • Author(s): Anand Kumria
  • Date: 2005-08-04 11:32:05 UTC
  • mfrom: (1.2.1 upstream) (2.1.1 sarge)
  • Revision ID: james.westby@ubuntu.com-20050804113205-q746i4lgo3rtlegn
Tags: 0.95.4-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * $Id: url.c,v 1.21 2004/01/14 20:52:33 rmanfredi Exp $
3
 
 *
4
 
 * Copyright (c) 2002-2003, Raphael Manfredi
5
 
 *
6
 
 *----------------------------------------------------------------------
7
 
 * This file is part of gtk-gnutella.
8
 
 *
9
 
 *  gtk-gnutella is free software; you can redistribute it and/or modify
10
 
 *  it under the terms of the GNU General Public License as published by
11
 
 *  the Free Software Foundation; either version 2 of the License, or
12
 
 *  (at your option) any later version.
13
 
 *
14
 
 *  gtk-gnutella is distributed in the hope that it will be useful,
15
 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 
 *  GNU General Public License for more details.
18
 
 *
19
 
 *  You should have received a copy of the GNU General Public License
20
 
 *  along with gtk-gnutella; if not, write to the Free Software
21
 
 *  Foundation, Inc.:
22
 
 *      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23
 
 *----------------------------------------------------------------------
24
 
 */
25
 
 
26
 
#include <ctype.h>
27
 
 
28
 
#include "common.h"
29
 
#include "url.h"
30
 
#include "override.h"           /* Must be the last header included */
31
 
 
32
 
RCSID("$Id: url.c,v 1.21 2004/01/14 20:52:33 rmanfredi Exp $");
33
 
 
34
 
#define ESCAPE_CHAR             '%'
35
 
#define TRANSPARENT_CHAR(x,m) \
36
 
        ((x) >= 32 && (x) < 128 && (is_transparent[(x)-32] & (m)))
37
 
 
38
 
/*
39
 
 * Reserved chars: ";", "/", "?", ":", "@", "=" and "&"
40
 
 * Unsafe chars  : " ", '"', "<", ">", "#", and "%"
41
 
 * Misc chars    : "{", "}", "|", "\", "^", "~", "[", "]" and "`"
42
 
 *
43
 
 * Bit 0 encodes regular transparent set (pathnames, '/' is transparent).
44
 
 * Bit 1 encodes regular transparent set minus '+' (query string).
45
 
 */
46
 
static const guint8 is_transparent[96] = {
47
 
/*  0 1 2 3 4 5 6 7 8 9 a b c d e f */  /* 0123456789abcdef -            */
48
 
    0,3,0,0,3,0,0,3,3,3,3,1,3,3,3,3,    /*  !"#$%&'()*+,-./ -  32 -> 47  */
49
 
    3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,    /* 0123456789:;<=>? -  48 -> 63  */
50
 
    0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,    /* @ABCDEFGHIJKLMNO -  64 -> 79  */
51
 
    3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,3,    /* PQRSTUVWXYZ[\]^_ -  80 -> 95  */
52
 
    0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,    /* `abcdefghijklmno -  96 -> 111 */
53
 
    3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,    /* pqrstuvwxyz{|}~  - 112 -> 127 */
54
 
};
55
 
 
56
 
#define PATH_MASK               0x1
57
 
#define QUERY_MASK              0x2
58
 
 
59
 
static const char hex_alphabet[] = "0123456789ABCDEF";
60
 
 
61
 
/*
62
 
 * url_escape_mask
63
 
 *
64
 
 * Escape undesirable characters using %xx, where xx is an hex code.
65
 
 * `mask' tells us whether we're escaping an URL path or a query string.
66
 
 *
67
 
 * Returns argument if no escaping is necessary, or a new string otherwise.
68
 
 */
69
 
static gchar *url_escape_mask(gchar *url, guint8 mask)
70
 
{
71
 
        gchar *p;
72
 
        gchar *q;
73
 
        int need_escape = 0;
74
 
        guchar c;
75
 
        gchar *new;
76
 
 
77
 
        for (p = url, c = *p++; c; c = *p++)
78
 
                if (!TRANSPARENT_CHAR(c, mask))
79
 
                        need_escape++;
80
 
 
81
 
        if (need_escape == 0)
82
 
                return url;
83
 
 
84
 
        new = g_malloc(p - url + (need_escape << 1));
85
 
 
86
 
        for (p = url, q = new, c = *p++; c; c = *p++) {
87
 
                if (TRANSPARENT_CHAR(c, mask))
88
 
                        *q++ = c;
89
 
                else {
90
 
                        *q++ = ESCAPE_CHAR;
91
 
                        *q++ = hex_alphabet[c >> 4];
92
 
                        *q++ = hex_alphabet[c & 0xf];
93
 
                }
94
 
        }
95
 
        *q = '\0';
96
 
 
97
 
        return new;
98
 
}
99
 
 
100
 
/*
101
 
 * url_escape_mask_into
102
 
 *
103
 
 * Escape undesirable characters using %xx, where xx is an hex code.
104
 
 * This is done in the `target' buffer, whose size is `len'.
105
 
 * `mask' tells us whether we're escaping an URL path or a query string.
106
 
 *
107
 
 * Returns amount of characters written into buffer (not counting trailing
108
 
 * NUL), or -1 if the buffer was too small.
109
 
 */
110
 
static gint url_escape_mask_into(
111
 
        const gchar *url, gchar *target, gint len, guint8 mask)
112
 
{
113
 
        const gchar *p = url;
114
 
        gchar *q;
115
 
        guchar c;
116
 
        gchar *end = target + len;
117
 
 
118
 
        for (q = target, c = *p++; c && q < end; c = *p++) {
119
 
                if (TRANSPARENT_CHAR(c, mask))
120
 
                        *q++ = c;
121
 
                else if (end - q >= 3) {
122
 
                        *q++ = ESCAPE_CHAR;
123
 
                        *q++ = hex_alphabet[c >> 4];
124
 
                        *q++ = hex_alphabet[c & 0xf];
125
 
                } else
126
 
                        break;
127
 
        }
128
 
 
129
 
        g_assert(q <= end);
130
 
 
131
 
        if (q == end)
132
 
                return -1;
133
 
 
134
 
        *q = '\0';
135
 
 
136
 
        return q - target;
137
 
}
138
 
 
139
 
/*
140
 
 * url_escape
141
 
 *
142
 
 * Escape undesirable characters using %xx, where xx is an hex code.
143
 
 * Returns argument if no escaping is necessary, or a new string otherwise.
144
 
 */
145
 
gchar *url_escape(gchar *url)
146
 
{
147
 
        return url_escape_mask(url, PATH_MASK);
148
 
}
149
 
 
150
 
/*
151
 
 * url_escape_query
152
 
 *
153
 
 * Same as url_escape(), but '+' are also escaped for the query string.
154
 
 * Returns argument if no escaping is necessary, or a new string otherwise.
155
 
 */
156
 
gchar *url_escape_query(gchar *url)
157
 
{
158
 
        return url_escape_mask(url, QUERY_MASK);
159
 
}
160
 
 
161
 
/*
162
 
 * url_escape_into
163
 
 *
164
 
 * Escape undesirable characters using %xx, where xx is an hex code.
165
 
 * This is done in the `target' buffer, whose size is `len'.
166
 
 *
167
 
 * Returns amount of characters written into buffer (not counting trailing
168
 
 * NUL), or -1 if the buffer was too small.
169
 
 */
170
 
gint url_escape_into(const gchar *url, gchar *target, gint len)
171
 
{
172
 
        return url_escape_mask_into(url, target, len, PATH_MASK);
173
 
}
174
 
 
175
 
/*
176
 
 * url_escape_cntrl
177
 
 *
178
 
 * Escape control characters using %xx, where xx is an hex code.
179
 
 *
180
 
 * Returns argument if no escaping is necessary, or a new string otherwise.
181
 
 */
182
 
gchar *url_escape_cntrl(gchar *url)
183
 
{
184
 
        gchar *p;
185
 
        gchar *q;
186
 
        int need_escape = 0;
187
 
        guchar c;
188
 
        gchar *new;
189
 
 
190
 
        for (p = url, c = *p++; c; c = *p++)
191
 
                if (iscntrl(c) || c == ESCAPE_CHAR)
192
 
                        need_escape++;
193
 
 
194
 
        if (need_escape == 0)
195
 
                return url;
196
 
 
197
 
        new = g_malloc(p - url + (need_escape << 1));
198
 
 
199
 
        for (p = url, q = new, c = *p++; c; c = *p++) {
200
 
                if (!iscntrl(c) && c != ESCAPE_CHAR)
201
 
                        *q++ = c;
202
 
                else {
203
 
                        *q++ = ESCAPE_CHAR;
204
 
                        *q++ = hex_alphabet[c >> 4];
205
 
                        *q++ = hex_alphabet[c & 0xf];
206
 
                }
207
 
        }
208
 
        *q = '\0';
209
 
 
210
 
        return new;
211
 
}
212
 
 
213
 
/*
214
 
 * url_unescape
215
 
 *
216
 
 * Unescape string, in-place if `inplace' is TRUE.
217
 
 *
218
 
 * Returns the argument if un-escaping is NOT necessary, a new string
219
 
 * otherwise unless in-place decoding was requested.
220
 
 */
221
 
gchar *url_unescape(gchar *url, gboolean inplace)
222
 
{
223
 
        gchar *p;
224
 
        gchar *q;
225
 
        gint need_unescape = 0;
226
 
        guchar c;
227
 
        gchar *new;
228
 
 
229
 
        for (p = url, c = *p++; c; c = *p++)
230
 
                if (c == ESCAPE_CHAR)
231
 
                        need_unescape++;
232
 
 
233
 
        if (need_unescape == 0)
234
 
                return url;
235
 
 
236
 
        /*
237
 
         * The "+ 1" in the g_malloc() call below is for the rare cases where
238
 
         * the string would finish on a truncated escape sequence.  In that
239
 
         * case, we would not have enough room for the final trailing NUL.
240
 
         */
241
 
 
242
 
        if (inplace)
243
 
                new = url;
244
 
        else
245
 
                new = g_malloc(p - url - (need_unescape << 1) + 1);
246
 
 
247
 
        for (p = url, q = new, c = *p++; c; c = *p++) {
248
 
                if (c != ESCAPE_CHAR)
249
 
                        *q++ = c;
250
 
                else {
251
 
                        if ((c = *p++)) {
252
 
                                gint v = (hex2dec(c) << 4) & 0xf0;
253
 
                                if ((c = *p++))
254
 
                                        v += hex2dec(c) & 0x0f;
255
 
                                else
256
 
                                        break;          /* String ending in the middle of escape */
257
 
                                *q++ = v;
258
 
                        } else
259
 
                                break;
260
 
                }
261
 
        }
262
 
        *q = '\0';
263
 
 
264
 
        g_assert(!inplace || new == url);
265
 
 
266
 
        return new;
267
 
}
268
 
 
269
 
/*
270
 
 * url_params_parse
271
 
 *
272
 
 * Parse all the parameters in the URL query string.  All parameter values are
273
 
 * stored in their URL-unescaped form, but parameter names are NOT un-escaped.
274
 
 *
275
 
 * Returns an url_params_t object that can be queried for later...
276
 
 */
277
 
url_params_t *url_params_parse(gchar *query)
278
 
{
279
 
        url_params_t *up;
280
 
        gchar *q;
281
 
        gchar *start;
282
 
        gchar *name = NULL;
283
 
        gchar *value = NULL;
284
 
        gboolean in_value = FALSE;
285
 
 
286
 
        up = walloc(sizeof(*up));
287
 
        up->params = g_hash_table_new(g_str_hash, g_str_equal);
288
 
        up->count = 0;
289
 
 
290
 
        for (q = start = query; /* empty */; q++) {
291
 
                gchar c = *q;
292
 
 
293
 
                if (in_value) {
294
 
                        if (c == '&' || c == '\0') {            /* End of value */
295
 
                                *q = '\0';
296
 
                                value = url_unescape(start, FALSE);
297
 
                                if (value == start)                             /* No unescaping took place */
298
 
                                        value = g_strdup(start);
299
 
                                *q = c;
300
 
                                g_hash_table_insert(up->params, name, value);
301
 
                                up->count++;
302
 
                                in_value = FALSE;
303
 
                                name = NULL;
304
 
                                value = NULL;
305
 
                                start = q + 1;                                  /* Name will start there */
306
 
                        }
307
 
                } else {
308
 
                        if (c == '=') {                                         /* End of parameter name */
309
 
                                *q = '\0';
310
 
                                name = g_strdup(start);
311
 
                                *q = c;
312
 
                                in_value = TRUE;
313
 
                                start = q + 1;                                  /* Value will start there */
314
 
                        }
315
 
                }
316
 
 
317
 
                if (c == '\0')
318
 
                        break;
319
 
        }
320
 
 
321
 
        g_assert(name == NULL);
322
 
        g_assert(value == NULL);
323
 
 
324
 
        return up;
325
 
}
326
 
 
327
 
/*
328
 
 * url_params_get
329
 
 *
330
 
 * Get the value of a parameter, or NULL if the parameter is not present.
331
 
 * The value returned has already been URL-unescaped.
332
 
 */
333
 
gchar *url_params_get(url_params_t *up, gchar *name)
334
 
{
335
 
        g_assert(up != NULL);
336
 
        g_assert(up->params != NULL);
337
 
 
338
 
        return g_hash_table_lookup(up->params, name);
339
 
}
340
 
 
341
 
static void free_params_kv(gpointer key, gpointer value, gpointer udata)
342
 
{
343
 
        g_free(key);
344
 
        g_free(value);
345
 
}
346
 
 
347
 
/*
348
 
 * url_params_free
349
 
 *
350
 
 * Dispose of the url_params_t structure.
351
 
 */
352
 
void url_params_free(url_params_t *up)
353
 
{
354
 
        g_assert(up != NULL);
355
 
 
356
 
        g_hash_table_foreach(up->params, free_params_kv, NULL);
357
 
        g_hash_table_destroy(up->params);
358
 
 
359
 
        wfree(up, sizeof(*up));
360
 
}
361